+++ /dev/null
----
-name: Diagnostic issue
-about: Create a bug report or feature request for a change to `rustc`'s error output
-labels: A-diagnostics, T-compiler
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
-
-If you cannot produce a minimal reproduction case (something that would work in
-isolation), please provide the steps or even link to a repository that causes
-the problematic output to occur.
--->
-
-Given the following code: <!-- Please provide a link to play.rust-lang.org -->
-
-```rust
-<code>
-```
-
-The current output is:
-
-```
-<rustc output>
-```
-
-<!-- The following is not always necessary. -->
-Ideally the output should look like:
-
-```
-<proposed output>
-```
-
-<!--
-If the problem is not self-explanatory, please provide a rationale for the
-change.
--->
-
-<!--
-If dramatically different output is caused by small changes, consider also
-adding them here.
-
-If you're using the stable version of the compiler, you should also check if the
-bug also exists in the beta or nightly versions. The output might also be
-different depending on the Edition.
--->
--- /dev/null
+name: Diagnostic issue
+description: Create a bug report or feature request for a change to `rustc`'s error output
+labels: ["A-diagnostics", "T-compiler"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for filing a diagnostics bug report! 🐛
+
+ Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug.
+
+ If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur.
+ - type: textarea
+ id: code
+ attributes:
+ label: Code
+ description: Please provide code that can reproduce the problem
+ placeholder: code
+ render: Rust
+ validations:
+ required: true
+ - type: textarea
+ id: output
+ attributes:
+ label: Current output
+ description: Please provide the `rustc` output you see
+ placeholder: rustc output
+ render: Shell
+ validations:
+ required: true
+ - type: textarea
+ id: desired-output
+ attributes:
+ label: Desired output
+ description: Please provide what the output *should* be
+ placeholder: proposed output
+ render: Shell
+ validations:
+ required: false
+ - type: textarea
+ id: rationale
+ attributes:
+ label: Rationale and extra context
+ description: If the problem is not self-explanatory, please provide a rationale for the change.
+ validations:
+ required: false
+ - type: textarea
+ id: other-output
+ attributes:
+ label: Other cases
+ description: If dramatically different output is caused by small changes, consider also adding them here.
+ render: Rust
+ validations:
+ required: false
+ - type: markdown
+ attributes:
+ value: |
+ If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. The output might also be different depending on the Edition.
+ - type: textarea
+ id: extra
+ attributes:
+ label: Anything else?
+ description: If you have more details you want to give us to reproduce this issue, please add it here
+ validations:
+ required: false
\ No newline at end of file
+++ /dev/null
----
-name: Documentation problem
-about: Create a report for a documentation problem.
-labels: A-docs
----
-<!--
-
-Thank you for finding a documentation problem! 📚
-
-Documentation problems might be grammatical issues, typos, or unclear wording, please provide details regarding the documentation including where it is present.
-
-Note: If your issue is for one of these, please use their dedicated issue tracker instead:
-
-- The Rust Book: https://github.com/rust-lang/book/issues
-- Rust by Example: https://github.com/rust-lang/rust-by-example/issues
-- The Edition Guide: https://github.com/rust-lang/edition-guide/issues
-- The Cargo Book: https://github.com/rust-lang/cargo/issues
-- The Clippy Book: https://github.com/rust-lang/rust-clippy/issues
-- The Reference: https://github.com/rust-lang/reference/issues
-- The Rustonomicon: https://github.com/rust-lang/nomicon/issues
-- The Embedded Book: https://github.com/rust-embedded/book/issues
-
-All other documentation issues should be filed here.
-
-Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
-
--->
-
-### Location
-
-### Summary
--- /dev/null
+name: Documentation problem
+description: Create a report for a documentation problem.
+labels: ["A-docs"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for finding a documentation problem! 📚
+
+ Documentation problems might be grammatical issues, typos, or unclear wording, please provide details regarding the documentation including where it is present.
+
+ Note: If your issue is for one of these, please use their dedicated issue tracker instead:
+ - [The Rust Book](https://github.com/rust-lang/book/issues)
+ - [Rust by Example](https://github.com/rust-lang/rust-by-example/issues)
+ - [The Edition Guide](https://github.com/rust-lang/edition-guide/issues)
+ - [The Cargo Book](https://github.com/rust-lang/cargo/issues)
+ - [The Clippy Book](https://github.com/rust-lang/rust-clippy/issues)
+ - [The Reference](https://github.com/rust-lang/reference/issues)
+ - [The Rustonomicon](https://github.com/rust-lang/nomicon/issues)
+ - [The Embedded Book](https://github.com/rust-embedded/book/issues)
+
+ All other documentation issues should be filed here.
+
+ Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
+
+ - type: textarea
+ id: location
+ attributes:
+ label: Location
+ validations:
+ required: true
+
+ - type: textarea
+ id: summary
+ attributes:
+ label: Summary
+ validations:
+ required: true
\ No newline at end of file
+++ /dev/null
----
-name: Internal Compiler Error
-about: Create a report for an internal compiler error in rustc.
-labels: C-bug, I-ICE, T-compiler
----
-<!--
-Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide
-a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
-how to create smaller examples.
-
-http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
--->
-
-### Code
-
-```Rust
-<code>
-```
-
-
-### Meta
-<!--
-If you're using the stable version of the compiler, you should also check if the
-bug also exists in the beta or nightly versions.
--->
-
-`rustc --version --verbose`:
-```
-<version>
-```
-
-### Error output
-
-```
-<output>
-```
-
-<!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo build`.
--->
-<details><summary><strong>Backtrace</strong></summary>
-<p>
-
-```
-<backtrace>
-```
-
-</p>
-</details>
-
--- /dev/null
+name: Internal Compiler Error
+description: Create a report for an internal compiler error in `rustc`
+labels: ["C-bug", "I-ICE", "T-compiler"]
+title: "[ICE]: "
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for finding an Internal Compiler Error! 🧊
+
+ If possible, try to provide a minimal verifiable example.
+
+ You can read "[Rust Bug Minimization Patterns](http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/)" for how to create smaller examples.
+
+ - type: textarea
+ id: code
+ attributes:
+ label: Code
+ description: Please provide code or a link to a repository that can reproduce the problem
+ placeholder: code
+ render: Rust
+ validations:
+ required: false
+
+ - type: checkboxes
+ attributes:
+ label: Affected release channels
+ description: If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions
+ options:
+ - label: Previous Stable
+ required: false
+ - label: Current Stable
+ required: false
+ - label: Current Beta
+ required: false
+ - label: Current Nightly
+ required: false
+
+ - type: textarea
+ id: version
+ attributes:
+ label: Rust Version
+ description: Please provide the `rustc` version, `rustc --version --verbose`
+ placeholder: |
+ $ rustc --version --verbose
+ rustc 1.XX.Y (SHORTHASH DATE)
+ binary: rustc
+ commit-hash: LONGHASHVALUE
+ commit-date: DATE
+ host: PLATFORMTRIPLE
+ release: 1.XX.Y
+ LLVM version: XX.YY.ZZ
+ render: Shell
+ validations:
+ required: true
+
+ - type: textarea
+ id: output
+ attributes:
+ label: Current error output
+ description: Please provide the `rustc` output you see
+ placeholder: output
+ render: Shell
+ validations:
+ required: false
+
+ - type: textarea
+ id: backtrace
+ attributes:
+ label: Backtrace
+ description: Include a backtrace in the code block by setting `RUST_BACKTRACE=full` in your environment, e.g. `RUST_BACKTRACE=full cargo build`
+ render: Shell
+ validations:
+ required: true
+
+ - type: textarea
+ id: extra
+ attributes:
+ label: Anything else?
+ description: If you have more details you want to give us to reproduce this issue, please add it here
+ validations:
+ required: false
\ No newline at end of file
- name: x86_64-gnu-distcheck
os: ubuntu-20.04-xl
env: {}
+ - name: x86_64-gnu-llvm-15
+ env:
+ RUST_BACKTRACE: 1
+ os: ubuntu-20.04-xl
+ - name: x86_64-gnu-llvm-14
+ env:
+ RUST_BACKTRACE: 1
+ os: ubuntu-20.04-xl
- name: x86_64-gnu-llvm-13
env:
RUST_BACKTRACE: 1
os: windows-latest-xl
- name: i686-mingw-1
env:
- RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu --set llvm.allow-old-toolchain"
+ RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu"
SCRIPT: make ci-mingw-subset-1
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
- name: i686-mingw-2
env:
- RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu --set llvm.allow-old-toolchain"
+ RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu"
SCRIPT: make ci-mingw-subset-2
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
- name: x86_64-mingw-1
env:
SCRIPT: make ci-mingw-subset-1
- RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler --set llvm.allow-old-toolchain"
+ RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler"
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
- name: x86_64-mingw-2
env:
SCRIPT: make ci-mingw-subset-2
- RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler --set llvm.allow-old-toolchain"
+ RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-profiler"
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
os: windows-latest-xl
os: windows-latest-xl
- name: dist-i686-mingw
env:
- RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu --enable-full-tools --enable-profiler --set llvm.allow-old-toolchain"
+ RUST_CONFIGURE_ARGS: "--build=i686-pc-windows-gnu --enable-full-tools --enable-profiler"
NO_DOWNLOAD_CI_LLVM: 1
SCRIPT: python x.py dist bootstrap --include-default-paths
CUSTOM_MINGW: 1
- name: dist-x86_64-mingw
env:
SCRIPT: python x.py dist bootstrap --include-default-paths
- RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler --set llvm.allow-old-toolchain"
+ RUST_CONFIGURE_ARGS: "--build=x86_64-pc-windows-gnu --enable-full-tools --enable-profiler"
NO_DOWNLOAD_CI_LLVM: 1
CUSTOM_MINGW: 1
DIST_REQUIRE_ALL_TOOLS: 1
Ahmed Charles <ahmedcharles@gmail.com> <acharles@outlook.com>
Alan Egerton <eggyal@gmail.com>
Alan Stoate <alan.stoate@gmail.com>
+Albert Larsan <albert.larsan@gmail.com> Albert Larsan <74931857+albertlarsan68@users.noreply.github.com>
Alessandro Decina <alessandro.d@gmail.com>
Alex Burka <durka42+github@gmail.com> Alex Burka <aburka@seas.upenn.edu>
Alex Hansen <ahansen2@trinity.edu>
Léo Lanteri Thauvin <leseulartichaut@gmail.com>
Léo Lanteri Thauvin <leseulartichaut@gmail.com> <38361244+LeSeulArtichaut@users.noreply.github.com>
Léo Testard <leo.testard@gmail.com>
+León Orell Valerian Liehr <me@fmease.dev> <liehr.exchange@gmx.net>
Leonardo Yvens <leoyvens@gmail.com>
Liigo Zhuang <liigo@qq.com>
Lily Ballard <lily@ballards.net> <kevin@sb.org>
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
- "clap 4.0.32",
+ "clap 4.1.4",
"crates-io",
"curl",
"curl-sys",
[[package]]
name = "cc"
-version = "1.0.76"
+version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
dependencies = [
"jobserver",
]
[[package]]
name = "clap"
-version = "4.0.32"
+version = "4.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39"
+checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
dependencies = [
"bitflags",
- "clap_derive 4.0.21",
+ "clap_derive 4.1.0",
"clap_lex 0.3.0",
"is-terminal",
"once_cell",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
dependencies = [
- "clap 4.0.32",
+ "clap 4.1.4",
]
[[package]]
[[package]]
name = "clap_derive"
-version = "4.0.21"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014"
+checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
dependencies = [
"heck",
"proc-macro-error",
[[package]]
name = "clippy"
-version = "0.1.68"
+version = "0.1.69"
dependencies = [
"clippy_lints",
"clippy_utils",
[[package]]
name = "clippy_lints"
-version = "0.1.68"
+version = "0.1.69"
dependencies = [
"cargo_metadata 0.14.0",
"clippy_utils",
[[package]]
name = "clippy_utils"
-version = "0.1.68"
+version = "0.1.69"
dependencies = [
"arrayvec",
"if_chain",
[[package]]
name = "declare_clippy_lint"
-version = "0.1.68"
+version = "0.1.69"
dependencies = [
"itertools",
"quote",
version = "0.1.0"
dependencies = [
"anyhow",
- "clap 4.0.32",
+ "clap 4.1.4",
"fs-err",
"rustdoc-json-types",
"serde",
"ammonia",
"anyhow",
"chrono",
- "clap 4.0.32",
+ "clap 4.1.4",
"clap_complete",
"elasticlunr-rs",
"env_logger 0.10.0",
name = "rustbook"
version = "0.1.0"
dependencies = [
- "clap 4.0.32",
+ "clap 4.1.4",
"env_logger 0.7.1",
"mdbook",
]
version = "0.0.0"
dependencies = [
"bitflags",
+ "memchr",
"rustc_data_structures",
"rustc_index",
"rustc_lexer",
version = "0.0.0"
dependencies = [
"rustc_ast",
+ "rustc_parse_format",
"rustc_span",
]
name = "rustc_metadata"
version = "0.0.0"
dependencies = [
+ "bitflags",
"libloading",
"odht",
"rustc_ast",
"rustc_middle",
"rustc_parse_format",
"rustc_query_system",
+ "rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_target",
[[package]]
name = "rustfmt-config_proc_macro"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"proc-macro2",
"quote",
[[package]]
name = "rustfmt-nightly"
-version = "1.5.1"
+version = "1.5.2"
dependencies = [
"annotate-snippets",
"anyhow",
[[package]]
name = "snap"
-version = "1.0.1"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da73c8f77aebc0e40c300b93f0a5f1bece7a248a36eee287d4e095f35c7b7d6e"
+checksum = "5e9f0ab6ef7eb7353d9119c170a436d1bf248eea575ac42d19d12f4e34130831"
[[package]]
name = "snapbox"
name = "tidy"
version = "0.1.0"
dependencies = [
+ "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cargo_metadata 0.14.0",
"ignore",
"lazy_static",
"miropt-test-tools",
"regex",
+ "semver",
"termcolor",
"walkdir",
]
This is the main source code repository for [Rust]. It contains the compiler,
standard library, and documentation.
-[Rust]: https://www.rust-lang.org
+[Rust]: https://www.rust-lang.org/
**Note: this README is for _users_ rather than _contributors_.**
-If you wish to _contribute_ to the compiler, you should read [CONTRIBUTING.md](CONTRIBUTING.md) instead.
+If you wish to _contribute_ to the compiler, you should read
+[CONTRIBUTING.md](CONTRIBUTING.md) instead.
## Quick Start
The Rust build system uses a Python script called `x.py` to build the compiler,
which manages the bootstrapping process. It lives at the root of the project.
-The `x.py` command can be run directly on most Unix systems in the following format:
+The `x.py` command can be run directly on most Unix systems in the following
+format:
```sh
./x.py <subcommand> [flags]
```
-This is how the documentation and examples assume you are running `x.py`. Some alternative ways are:
+This is how the documentation and examples assume you are running `x.py`.
+Some alternative ways are:
```sh
# On a Unix shell if you don't have the necessary `python3` command
python x.py <subcommand> [flags]
```
-More information about `x.py` can be found
-by running it with the `--help` flag or reading the [rustc dev guide][rustcguidebuild].
+More information about `x.py` can be found by running it with the `--help` flag
+or reading the [rustc dev guide][rustcguidebuild].
[gettingstarted]: https://rustc-dev-guide.rust-lang.org/getting-started.html
[rustcguidebuild]: https://rustc-dev-guide.rust-lang.org/building/how-to-build-and-run.html
Make sure you have installed the dependencies:
- * `python` 3 or 2.7
- * `git`
- * A C compiler (when building for the host, `cc` is enough; cross-compiling may need additional compilers)
- * `curl` (not needed on Windows)
- * `pkg-config` if you are compiling on Linux and targeting Linux
- * `libiconv` (already included with glibc on Debian-based distros)
+* `python` 3 or 2.7
+* `git`
+* A C compiler (when building for the host, `cc` is enough; cross-compiling may
+ need additional compilers)
+* `curl` (not needed on Windows)
+* `pkg-config` if you are compiling on Linux and targeting Linux
+* `libiconv` (already included with glibc on Debian-based distros)
-To build cargo, you'll also need OpenSSL (`libssl-dev` or `openssl-devel` on most Unix distros).
+To build Cargo, you'll also need OpenSSL (`libssl-dev` or `openssl-devel` on
+most Unix distros).
If building LLVM from source, you'll need additional tools:
* `g++`, `clang++`, or MSVC with versions listed on
[LLVM's documentation](https://llvm.org/docs/GettingStarted.html#host-c-toolchain-both-compiler-and-standard-library)
-* `ninja`, or GNU `make` 3.81 or later (ninja is recommended, especially on Windows)
+* `ninja`, or GNU `make` 3.81 or later (Ninja is recommended, especially on
+ Windows)
* `cmake` 3.13.4 or later
-* `libstdc++-static` may be required on some Linux distributions such as Fedora and Ubuntu
+* `libstdc++-static` may be required on some Linux distributions such as Fedora
+ and Ubuntu
-On tier 1 or tier 2 with host tools platforms, you can also choose to download LLVM by setting `llvm.download-ci-llvm = true`.
+On tier 1 or tier 2 with host tools platforms, you can also choose to download
+LLVM by setting `llvm.download-ci-llvm = true`.
Otherwise, you'll need LLVM installed and `llvm-config` in your path.
See [the rustc-dev-guide for more info][sysllvm].
2. Configure the build settings:
- The Rust build system uses a file named `config.toml` in the root of the
- source tree to determine various configuration settings for the build.
- Set up the defaults intended for distros to get started. You can see a full list of options
- in `config.toml.example`.
+ The Rust build system uses a file named `config.toml` in the root of the
+ source tree to determine various configuration settings for the build.
+ Set up the defaults intended for distros to get started. You can see a full
+ list of options in `config.toml.example`.
- ```sh
- printf 'profile = "user" \nchangelog-seen = 2 \n' > config.toml
- ```
+ ```sh
+ printf 'profile = "user" \nchangelog-seen = 2 \n' > config.toml
+ ```
- If you plan to use `x.py install` to create an installation, it is recommended
- that you set the `prefix` value in the `[install]` section to a directory.
+ If you plan to use `x.py install` to create an installation, it is
+ recommended that you set the `prefix` value in the `[install]` section to a
+ directory.
3. Build and install:
- ```sh
- ./x.py build && ./x.py install
- ```
+ ```sh
+ ./x.py build && ./x.py install
+ ```
- When complete, `./x.py install` will place several programs into
- `$PREFIX/bin`: `rustc`, the Rust compiler, and `rustdoc`, the
- API-documentation tool. If you've set `profile = "user"` or `build.extended = true`, it will
- also include [Cargo], Rust's package manager.
+ When complete, `./x.py install` will place several programs into
+ `$PREFIX/bin`: `rustc`, the Rust compiler, and `rustdoc`, the
+ API-documentation tool. If you've set `profile = "user"` or
+ `build.extended = true`, it will also include [Cargo], Rust's package
+ manager.
[Cargo]: https://github.com/rust-lang/cargo
### Building on Windows
-On Windows, we suggest using [winget] to install dependencies by running the following in a terminal:
+On Windows, we suggest using [winget] to install dependencies by running the
+following in a terminal:
```powershell
winget install -e Python.Python.3
winget install -e Git.Git
```
-Then edit your system's `PATH` variable and add: `C:\Program Files\CMake\bin`. See
-[this guide on editing the system `PATH`](https://www.java.com/en/download/help/path.html) from the
-Java documentation.
+Then edit your system's `PATH` variable and add: `C:\Program Files\CMake\bin`.
+See
+[this guide on editing the system `PATH`](https://www.java.com/en/download/help/path.html)
+from the Java documentation.
[winget]: https://github.com/microsoft/winget-cli
There are two prominent ABIs in use on Windows: the native (MSVC) ABI used by
Visual Studio and the GNU ABI used by the GCC toolchain. Which version of Rust
you need depends largely on what C/C++ libraries you want to interoperate with.
-Use the MSVC build of Rust to interop with software produced by Visual Studio and
-the GNU build to interop with GNU software built using the MinGW/MSYS2 toolchain.
+Use the MSVC build of Rust to interop with software produced by Visual Studio
+and the GNU build to interop with GNU software built using the MinGW/MSYS2
+toolchain.
#### MinGW
2. Run `mingw32_shell.bat` or `mingw64_shell.bat` from the MSYS2 installation
directory (e.g. `C:\msys64`), depending on whether you want 32-bit or 64-bit
Rust. (As of the latest version of MSYS2 you have to run `msys2_shell.cmd
- -mingw32` or `msys2_shell.cmd -mingw64` from the command line instead)
+ -mingw32` or `msys2_shell.cmd -mingw64` from the command line instead.)
3. From this terminal, install the required tools:
pacman -Sy pacman-mirrors
# Install build tools needed for Rust. If you're building a 32-bit compiler,
- # then replace "x86_64" below with "i686". If you've already got git, python,
- # or CMake installed and in PATH you can remove them from this list. Note
- # that it is important that you do **not** use the 'python2', 'cmake' and 'ninja'
- # packages from the 'msys2' subsystem. The build has historically been known
- # to fail with these packages.
+ # then replace "x86_64" below with "i686". If you've already got Git, Python,
+ # or CMake installed and in PATH you can remove them from this list.
+ # Note that it is important that you do **not** use the 'python2', 'cmake',
+ # and 'ninja' packages from the 'msys2' subsystem.
+ # The build has historically been known to fail with these packages.
pacman -S git \
make \
diffutils \
MSVC builds of Rust additionally require an installation of Visual Studio 2017
(or later) so `rustc` can use its linker. The simplest way is to get
-[Visual Studio], check the “C++ build tools” and “Windows 10 SDK” workload.
+[Visual Studio], check the "C++ build tools" and "Windows 10 SDK" workload.
[Visual Studio]: https://visualstudio.microsoft.com/downloads/
-(If you're installing cmake yourself, be careful that “C++ CMake tools for
-Windows” doesn't get included under “Individual components”.)
+(If you're installing CMake yourself, be careful that "C++ CMake tools for
+Windows" doesn't get included under "Individual components".)
With these dependencies installed, you can build the compiler in a `cmd.exe`
shell with:
python x.py build
```
-Right now, building Rust only works with some known versions of Visual Studio. If
-you have a more recent version installed and the build system doesn't understand,
-you may need to force rustbuild to use an older version. This can be done
-by manually calling the appropriate vcvars file before running the bootstrap.
+Right now, building Rust only works with some known versions of Visual Studio.
+If you have a more recent version installed and the build system doesn't
+understand, you may need to force rustbuild to use an older version.
+This can be done by manually calling the appropriate vcvars file before running
+the bootstrap.
```batch
CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat"
- `x86_64-pc-windows-msvc`
The build triple can be specified by either specifying `--build=<triple>` when
-invoking `x.py` commands, or by creating a `config.toml` file (as described
-in [Installing From Source](#installing-from-source)), and modifying the
-`build` option under the `[build]` section.
+invoking `x.py` commands, or by creating a `config.toml` file (as described in
+[Installing from Source](#installing-from-source)), and modifying the `build`
+option under the `[build]` section.
### Configure and Make
make && sudo make install
```
-`configure` generates a `config.toml` which can also be used with normal `x.py` invocations.
+`configure` generates a `config.toml` which can also be used with normal `x.py`
+invocations.
## Building Documentation
-If you’d like to build the documentation, it’s almost the same:
+If you'd like to build the documentation, it's almost the same:
```sh
./x.py doc
```
The generated documentation will appear under `doc` in the `build` directory for
-the ABI used. I.e., if the ABI was `x86_64-pc-windows-msvc`, the directory will be
-`build\x86_64-pc-windows-msvc\doc`.
+the ABI used. That is, if the ABI was `x86_64-pc-windows-msvc`, the directory
+will be `build\x86_64-pc-windows-msvc\doc`.
## Notes
-Since the Rust compiler is written in Rust, it must be built by a
-precompiled "snapshot" version of itself (made in an earlier stage of
-development). As such, source builds require an Internet connection to
-fetch snapshots, and an OS that can execute the available snapshot binaries.
+Since the Rust compiler is written in Rust, it must be built by a precompiled
+"snapshot" version of itself (made in an earlier stage of development).
+As such, source builds require an Internet connection to fetch snapshots, and an
+OS that can execute the available snapshot binaries.
-See https://doc.rust-lang.org/nightly/rustc/platform-support.html for a list of supported platforms.
-Only "host tools" platforms have a pre-compiled snapshot binary available; to compile for a platform
-without host tools you must cross-compile.
+See https://doc.rust-lang.org/nightly/rustc/platform-support.html for a list of
+supported platforms.
+Only "host tools" platforms have a pre-compiled snapshot binary available; to
+compile for a platform without host tools you must cross-compile.
-You may find that other platforms work, but these are our officially
-supported build environments that are most likely to work.
+You may find that other platforms work, but these are our officially supported
+build environments that are most likely to work.
## Getting Help
## License
-Rust is primarily distributed under the terms of both the MIT license
-and the Apache License (Version 2.0), with portions covered by various
-BSD-like licenses.
+Rust is primarily distributed under the terms of both the MIT license and the
+Apache License (Version 2.0), with portions covered by various BSD-like
+licenses.
See [LICENSE-APACHE](LICENSE-APACHE), [LICENSE-MIT](LICENSE-MIT), and
[COPYRIGHT](COPYRIGHT) for details.
## Trademark
[The Rust Foundation][rust-foundation] owns and protects the Rust and Cargo
-trademarks and logos (the “Rust Trademarks”).
+trademarks and logos (the "Rust Trademarks").
-If you want to use these names or brands, please read the [media guide][media-guide].
+If you want to use these names or brands, please read the
+[media guide][media-guide].
Third-party logos may be subject to third-party copyrights and trademarks. See
[Licenses][policies-licenses] for details.
[rust-foundation]: https://foundation.rust-lang.org/
-[media-guide]: https://www.rust-lang.org/policies/media-guide
+[media-guide]: https://foundation.rust-lang.org/policies/logo-policy-and-media-guide/
[policies-licenses]: https://www.rust-lang.org/policies/licenses
+Version 1.67.0 (2023-01-26)
+==========================
+
+<a id="1.67.0-Language"></a>
+
+Language
+--------
+
+- [Make `Sized` predicates coinductive, allowing cycles.](https://github.com/rust-lang/rust/pull/100386/)
+- [`#[must_use]` annotations on `async fn` also affect the `Future::Output`.](https://github.com/rust-lang/rust/pull/100633/)
+- [Elaborate supertrait obligations when deducing closure signatures.](https://github.com/rust-lang/rust/pull/101834/)
+- [Invalid literals are no longer an error under `cfg(FALSE)`.](https://github.com/rust-lang/rust/pull/102944/)
+- [Unreserve braced enum variants in value namespace.](https://github.com/rust-lang/rust/pull/103578/)
+
+<a id="1.67.0-Compiler"></a>
+
+Compiler
+--------
+
+- [Enable varargs support for calling conventions other than `C` or `cdecl`.](https://github.com/rust-lang/rust/pull/97971/)
+- [Add new MIR constant propagation based on dataflow analysis.](https://github.com/rust-lang/rust/pull/101168/)
+- [Optimize field ordering by grouping m\*2^n-sized fields with equivalently aligned ones.](https://github.com/rust-lang/rust/pull/102750/)
+- [Stabilize native library modifier `verbatim`.](https://github.com/rust-lang/rust/pull/104360/)
+
+Added and removed targets:
+
+- [Add a tier 3 target for PowerPC on AIX](https://github.com/rust-lang/rust/pull/102293/), `powerpc64-ibm-aix`.
+- [Add a tier 3 target for the Sony PlayStation 1](https://github.com/rust-lang/rust/pull/102689/), `mipsel-sony-psx`.
+- [Add tier 3 `no_std` targets for the QNX Neutrino RTOS](https://github.com/rust-lang/rust/pull/102701/),
+ `aarch64-unknown-nto-qnx710` and `x86_64-pc-nto-qnx710`.
+- [Remove tier 3 `linuxkernel` targets](https://github.com/rust-lang/rust/pull/104015/) (not used by the actual kernel).
+
+Refer to Rust's [platform support page][platform-support-doc]
+for more information on Rust's tiered platform support.
+
+<a id="1.67.0-Libraries"></a>
+
+Libraries
+---------
+
+- [Merge `crossbeam-channel` into `std::sync::mpsc`.](https://github.com/rust-lang/rust/pull/93563/)
+- [Fix inconsistent rounding of 0.5 when formatted to 0 decimal places.](https://github.com/rust-lang/rust/pull/102935/)
+- [Derive `Eq` and `Hash` for `ControlFlow`.](https://github.com/rust-lang/rust/pull/103084/)
+- [Don't build `compiler_builtins` with `-C panic=abort`.](https://github.com/rust-lang/rust/pull/103786/)
+
+<a id="1.67.0-Stabilized-APIs"></a>
+
+Stabilized APIs
+---------------
+
+- [`{integer}::checked_ilog`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog)
+- [`{integer}::checked_ilog2`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog2)
+- [`{integer}::checked_ilog10`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.checked_ilog10)
+- [`{integer}::ilog`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog)
+- [`{integer}::ilog2`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog2)
+- [`{integer}::ilog10`](https://doc.rust-lang.org/stable/std/primitive.i32.html#method.ilog10)
+- [`NonZeroU*::ilog2`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog2)
+- [`NonZeroU*::ilog10`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#method.ilog10)
+- [`NonZero*::BITS`](https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html#associatedconstant.BITS)
+
+These APIs are now stable in const contexts:
+
+- [`char::from_u32`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_u32)
+- [`char::from_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.from_digit)
+- [`char::to_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.to_digit)
+- [`core::char::from_u32`](https://doc.rust-lang.org/stable/core/char/fn.from_u32.html)
+- [`core::char::from_digit`](https://doc.rust-lang.org/stable/core/char/fn.from_digit.html)
+
+<a id="1.67.0-Compatibility-Notes"></a>
+
+Compatibility Notes
+-------------------
+
+- [The layout of `repr(Rust)` types now groups m\*2^n-sized fields with
+ equivalently aligned ones.](https://github.com/rust-lang/rust/pull/102750/)
+ This is intended to be an optimization, but it is also known to increase type
+ sizes in a few cases for the placement of enum tags. As a reminder, the layout
+ of `repr(Rust)` types is an implementation detail, subject to change.
+- [0.5 now rounds to 0 when formatted to 0 decimal places.](https://github.com/rust-lang/rust/pull/102935/)
+ This makes it consistent with the rest of floating point formatting that
+ rounds ties toward even digits.
+- [Chains of `&&` and `||` will now drop temporaries from their sub-expressions in
+ evaluation order, left-to-right.](https://github.com/rust-lang/rust/pull/103293/)
+ Previously, it was "twisted" such that the _first_ expression dropped its
+ temporaries _last_, after all of the other expressions dropped in order.
+- [Underscore suffixes on string literals are now a hard error.](https://github.com/rust-lang/rust/pull/103914/)
+ This has been a future-compatibility warning since 1.20.0.
+- [Stop passing `-export-dynamic` to `wasm-ld`.](https://github.com/rust-lang/rust/pull/105405/)
+- [`main` is now mangled as `__main_void` on `wasm32-wasi`.](https://github.com/rust-lang/rust/pull/105468/)
+- [Cargo now emits an error if there are multiple registries in the configuration
+ with the same index URL.](https://github.com/rust-lang/cargo/pull/10592)
+
+<a id="1.67.0-Internal-Changes"></a>
+
+Internal Changes
+----------------
+
+These changes do not affect any public interfaces of Rust, but they represent
+significant improvements to the performance or internals of rustc and related
+tools.
+
+- [Rewrite LLVM's archive writer in Rust.](https://github.com/rust-lang/rust/pull/97485/)
+
Version 1.66.1 (2023-01-10)
===========================
["a", ref a @ ..] => dl.aggregate_align = align(a, "a")?,
["f32", ref a @ ..] => dl.f32_align = align(a, "f32")?,
["f64", ref a @ ..] => dl.f64_align = align(a, "f64")?,
+ // FIXME(erikdesjardins): we should be parsing nonzero address spaces
+ // this will require replacing TargetDataLayout::{pointer_size,pointer_align}
+ // with e.g. `fn pointer_size_in(AddressSpace)`
[p @ "p", s, ref a @ ..] | [p @ "p0", s, ref a @ ..] => {
dl.pointer_size = size(s, p)?;
dl.pointer_align = align(a, p)?;
Int(Integer, bool),
F32,
F64,
- Pointer,
+ Pointer(AddressSpace),
}
impl Primitive {
Int(i, _) => i.size(),
F32 => Size::from_bits(32),
F64 => Size::from_bits(64),
- Pointer => dl.pointer_size,
+ // FIXME(erikdesjardins): ignoring address space is technically wrong, pointers in
+ // different address spaces can have different sizes
+ // (but TargetDataLayout doesn't currently parse that part of the DL string)
+ Pointer(_) => dl.pointer_size,
}
}
Int(i, _) => i.align(dl),
F32 => dl.f32_align,
F64 => dl.f64_align,
- Pointer => dl.pointer_align,
+ // FIXME(erikdesjardins): ignoring address space is technically wrong, pointers in
+ // different address spaces can have different alignments
+ // (but TargetDataLayout doesn't currently parse that part of the DL string)
+ Pointer(_) => dl.pointer_align,
}
}
-
- // FIXME(eddyb) remove, it's trivial thanks to `matches!`.
- #[inline]
- pub fn is_float(self) -> bool {
- matches!(self, F32 | F64)
- }
-
- // FIXME(eddyb) remove, it's completely unused.
- #[inline]
- pub fn is_int(self) -> bool {
- matches!(self, Int(..))
- }
-
- #[inline]
- pub fn is_ptr(self) -> bool {
- matches!(self, Pointer)
- }
}
/// Inclusive wrap-around range of valid values, that is, if
/// named `inverse_memory_index`.
///
// FIXME(eddyb) build a better abstraction for permutations, if possible.
- // FIXME(camlorn) also consider small vector optimization here.
+ // FIXME(camlorn) also consider small vector optimization here.
memory_index: Vec<u32>,
},
}
/// An identifier that specifies the address space that some operation
/// should operate on. Special address spaces have an effect on code generation,
/// depending on the target and the address spaces it implements.
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub struct AddressSpace(pub u32);
impl AddressSpace {
/// Enum-likes with more than one inhabited variant: each variant comes with
/// a *discriminant* (usually the same as the variant index but the user can
- /// assign explicit discriminant values). That discriminant is encoded
- /// as a *tag* on the machine. The layout of each variant is
+ /// assign explicit discriminant values). That discriminant is encoded
+ /// as a *tag* on the machine. The layout of each variant is
/// a struct, and they all have space reserved for the tag.
/// For enums, the tag is the sole field of the layout.
Multiple {
pub size: Size,
pub align: Align,
pub safe: Option<PointerKind>,
- pub address_space: AddressSpace,
}
/// Used in `might_permit_raw_init` to indicate the kind of initialisation
[dependencies]
bitflags = "1.2.1"
+memchr = "2.5.0"
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" }
rustc_lexer = { path = "../rustc_lexer" }
//! - [`Attribute`]: Metadata associated with item.
//! - [`UnOp`], [`BinOp`], and [`BinOpKind`]: Unary and binary operators.
+pub use crate::format::*;
pub use crate::util::parser::ExprPrecedence;
pub use GenericArgs::*;
pub use UnsafeSource::*;
ExprKind::Try(..) => ExprPrecedence::Try,
ExprKind::Yield(..) => ExprPrecedence::Yield,
ExprKind::Yeet(..) => ExprPrecedence::Yeet,
+ ExprKind::FormatArgs(..) => ExprPrecedence::FormatArgs,
ExprKind::Err => ExprPrecedence::Err,
}
}
/// with a `ByteStr` literal.
IncludedBytes(Lrc<[u8]>),
+ /// A `format_args!()` expression.
+ FormatArgs(P<FormatArgs>),
+
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err,
}
impl Ty {
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
- while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind {
+ while let TyKind::Ref(_, MutTy { ty, .. }) | TyKind::Ptr(MutTy { ty, .. }) = &final_ty.kind
+ {
final_ty = ty;
}
final_ty
--- /dev/null
+use crate::ptr::P;
+use crate::Expr;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::Span;
+
+// Definitions:
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └──────────────────────────────────────────────┘
+// FormatArgs
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └─────────┘
+// argument
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └───────────────────┘
+// template
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └────┘└─────────┘└┘
+// pieces
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └────┘ └┘
+// literal pieces
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └─────────┘
+// placeholder
+//
+// format_args!("hello {abc:.xyz$}!!", abc="world");
+// └─┘ └─┘
+// positions (could be names, numbers, empty, or `*`)
+
+/// (Parsed) format args.
+///
+/// Basically the "AST" for a complete `format_args!()`.
+///
+/// E.g., `format_args!("hello {name}");`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FormatArgs {
+ pub span: Span,
+ pub template: Vec<FormatArgsPiece>,
+ pub arguments: FormatArguments,
+}
+
+/// A piece of a format template string.
+///
+/// E.g. "hello" or "{name}".
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FormatArgsPiece {
+ Literal(Symbol),
+ Placeholder(FormatPlaceholder),
+}
+
+/// The arguments to format_args!().
+///
+/// E.g. `1, 2, name="ferris", n=3`,
+/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FormatArguments {
+ arguments: Vec<FormatArgument>,
+ num_unnamed_args: usize,
+ num_explicit_args: usize,
+ names: FxHashMap<Symbol, usize>,
+}
+
+// FIXME: Rustdoc has trouble proving Send/Sync for this. See #106930.
+#[cfg(parallel_compiler)]
+unsafe impl Sync for FormatArguments {}
+#[cfg(parallel_compiler)]
+unsafe impl Send for FormatArguments {}
+
+impl FormatArguments {
+ pub fn new() -> Self {
+ Self {
+ arguments: Vec::new(),
+ names: FxHashMap::default(),
+ num_unnamed_args: 0,
+ num_explicit_args: 0,
+ }
+ }
+
+ pub fn add(&mut self, arg: FormatArgument) -> usize {
+ let index = self.arguments.len();
+ if let Some(name) = arg.kind.ident() {
+ self.names.insert(name.name, index);
+ } else if self.names.is_empty() {
+ // Only count the unnamed args before the first named arg.
+ // (Any later ones are errors.)
+ self.num_unnamed_args += 1;
+ }
+ if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
+ // This is an explicit argument.
+ // Make sure that all arguments so far are explcit.
+ assert_eq!(
+ self.num_explicit_args,
+ self.arguments.len(),
+ "captured arguments must be added last"
+ );
+ self.num_explicit_args += 1;
+ }
+ self.arguments.push(arg);
+ index
+ }
+
+ pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {
+ let i = *self.names.get(&name)?;
+ Some((i, &self.arguments[i]))
+ }
+
+ pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
+ (i < self.num_explicit_args).then(|| &self.arguments[i])
+ }
+
+ pub fn unnamed_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_unnamed_args]
+ }
+
+ pub fn named_args(&self) -> &[FormatArgument] {
+ &self.arguments[self.num_unnamed_args..self.num_explicit_args]
+ }
+
+ pub fn explicit_args(&self) -> &[FormatArgument] {
+ &self.arguments[..self.num_explicit_args]
+ }
+
+ pub fn all_args(&self) -> &[FormatArgument] {
+ &self.arguments[..]
+ }
+
+ pub fn all_args_mut(&mut self) -> &mut [FormatArgument] {
+ &mut self.arguments[..]
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct FormatArgument {
+ pub kind: FormatArgumentKind,
+ pub expr: P<Expr>,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FormatArgumentKind {
+ /// `format_args(…, arg)`
+ Normal,
+ /// `format_args(…, arg = 1)`
+ Named(Ident),
+ /// `format_args("… {arg} …")`
+ Captured(Ident),
+}
+
+impl FormatArgumentKind {
+ pub fn ident(&self) -> Option<Ident> {
+ match self {
+ &Self::Normal => None,
+ &Self::Named(id) => Some(id),
+ &Self::Captured(id) => Some(id),
+ }
+ }
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub struct FormatPlaceholder {
+ /// Index into [`FormatArgs::arguments`].
+ pub argument: FormatArgPosition,
+ /// The span inside the format string for the full `{…}` placeholder.
+ pub span: Option<Span>,
+ /// `{}`, `{:?}`, or `{:x}`, etc.
+ pub format_trait: FormatTrait,
+ /// `{}` or `{:.5}` or `{:-^20}`, etc.
+ pub format_options: FormatOptions,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub struct FormatArgPosition {
+ /// Which argument this position refers to (Ok),
+ /// or would've referred to if it existed (Err).
+ pub index: Result<usize, usize>,
+ /// What kind of position this is. See [`FormatArgPositionKind`].
+ pub kind: FormatArgPositionKind,
+ /// The span of the name or number.
+ pub span: Option<Span>,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatArgPositionKind {
+ /// `{}` or `{:.*}`
+ Implicit,
+ /// `{1}` or `{:1$}` or `{:.1$}`
+ Number,
+ /// `{a}` or `{:a$}` or `{:.a$}`
+ Named,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq, Hash)]
+pub enum FormatTrait {
+ /// `{}`
+ Display,
+ /// `{:?}`
+ Debug,
+ /// `{:e}`
+ LowerExp,
+ /// `{:E}`
+ UpperExp,
+ /// `{:o}`
+ Octal,
+ /// `{:p}`
+ Pointer,
+ /// `{:b}`
+ Binary,
+ /// `{:x}`
+ LowerHex,
+ /// `{:X}`
+ UpperHex,
+}
+
+#[derive(Clone, Encodable, Decodable, Default, Debug, PartialEq, Eq)]
+pub struct FormatOptions {
+ /// The width. E.g. `{:5}` or `{:width$}`.
+ pub width: Option<FormatCount>,
+ /// The precision. E.g. `{:.5}` or `{:.precision$}`.
+ pub precision: Option<FormatCount>,
+ /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
+ pub alignment: Option<FormatAlignment>,
+ /// The fill character. E.g. the `.` in `{:.>10}`.
+ pub fill: Option<char>,
+ /// The `+` or `-` flag.
+ pub sign: Option<FormatSign>,
+ /// The `#` flag.
+ pub alternate: bool,
+ /// The `0` flag. E.g. the `0` in `{:02x}`.
+ pub zero_pad: bool,
+ /// The `x` or `X` flag (for `Debug` only). E.g. the `x` in `{:x?}`.
+ pub debug_hex: Option<FormatDebugHex>,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatSign {
+ /// The `+` flag.
+ Plus,
+ /// The `-` flag.
+ Minus,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatDebugHex {
+ /// The `x` flag in `{:x?}`.
+ Lower,
+ /// The `X` flag in `{:X?}`.
+ Upper,
+}
+
+#[derive(Copy, Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatAlignment {
+ /// `{:<}`
+ Left,
+ /// `{:>}`
+ Right,
+ /// `{:^}`
+ Center,
+}
+
+#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
+pub enum FormatCount {
+ /// `{:5}` or `{:.5}`
+ Literal(usize),
+ /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
+ Argument(FormatArgPosition),
+}
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(negative_impls)]
-#![feature(slice_internals)]
#![feature(stmt_expr_attributes)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
pub mod attr;
pub mod entry;
pub mod expand;
+pub mod format;
pub mod mut_visit;
pub mod node_id;
pub mod ptr;
pub use self::ast::*;
pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
+pub use self::format::*;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
fn visit_inline_asm_sym(&mut self, sym: &mut InlineAsmSym) {
noop_visit_inline_asm_sym(sym, self)
}
+
+ fn visit_format_args(&mut self, fmt: &mut FormatArgs) {
+ noop_visit_format_args(fmt, self)
+ }
}
/// Use a map-style function (`FnOnce(T) -> T`) to overwrite a `&mut T`. Useful
vis.visit_path(path);
}
+pub fn noop_visit_format_args<T: MutVisitor>(fmt: &mut FormatArgs, vis: &mut T) {
+ for arg in fmt.arguments.all_args_mut() {
+ if let FormatArgumentKind::Named(name) = &mut arg.kind {
+ vis.visit_ident(name);
+ }
+ vis.visit_expr(&mut arg.expr);
+ }
+}
+
pub fn noop_visit_expr<T: MutVisitor>(
Expr { kind, id, span, attrs, tokens }: &mut Expr,
vis: &mut T,
visit_opt(expr, |expr| vis.visit_expr(expr));
}
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
+ ExprKind::FormatArgs(fmt) => vis.visit_format_args(fmt),
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(se) => {
let StructExpr { qself, path, fields, rest } = se.deref_mut();
Try,
InlineAsm,
Mac,
+ FormatArgs,
Array,
Repeat,
| ExprPrecedence::Yeet => PREC_JUMP,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
- // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
+ // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
// ensures that `pprust` will add parentheses in the right places to get the desired
// parse.
ExprPrecedence::Range => PREC_RANGE,
| ExprPrecedence::Index
| ExprPrecedence::Try
| ExprPrecedence::InlineAsm
- | ExprPrecedence::Mac => PREC_POSTFIX,
+ | ExprPrecedence::Mac
+ | ExprPrecedence::FormatArgs => PREC_POSTFIX,
// Never need parens
ExprPrecedence::Array
// U+2069 - E2 81 A9
let mut bytes = s.as_bytes();
loop {
- match core::slice::memchr::memchr(0xE2, bytes) {
+ match memchr::memchr(0xE2, bytes) {
Some(idx) => {
// bytes are valid UTF-8 -> E2 must be followed by two bytes
let ch = &bytes[idx..idx + 3];
fn visit_inline_asm(&mut self, asm: &'ast InlineAsm) {
walk_inline_asm(self, asm)
}
+ fn visit_format_args(&mut self, fmt: &'ast FormatArgs) {
+ walk_format_args(self, fmt)
+ }
fn visit_inline_asm_sym(&mut self, sym: &'ast InlineAsmSym) {
walk_inline_asm_sym(self, sym)
}
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
visitor.visit_ty(&mutable_type.ty)
}
- TyKind::Tup(tuple_element_types) => {
- walk_list!(visitor, visit_ty, tuple_element_types);
+ TyKind::Tup(tys) => {
+ walk_list!(visitor, visit_ty, tys);
}
TyKind::BareFn(function_declaration) => {
walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);
visitor.visit_path(&sym.path, sym.id);
}
+pub fn walk_format_args<'a, V: Visitor<'a>>(visitor: &mut V, fmt: &'a FormatArgs) {
+ for arg in fmt.arguments.all_args() {
+ if let FormatArgumentKind::Named(name) = arg.kind {
+ visitor.visit_ident(name);
+ }
+ visitor.visit_expr(&arg.expr);
+ }
+}
+
pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
walk_list!(visitor, visit_attribute, expression.attrs.iter());
ExprKind::MacCall(mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(asm) => visitor.visit_inline_asm(asm),
+ ExprKind::FormatArgs(f) => visitor.visit_format_args(f),
ExprKind::Yield(optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
use rustc_hir::definitions::DefPathData;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
-use rustc_span::symbol::{sym, Ident};
+use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::DUMMY_SP;
use thin_vec::thin_vec;
ExprKind::InlineAsm(asm) => {
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
}
+ ExprKind::FormatArgs(fmt) => self.lower_format_args(e.span, fmt),
ExprKind::Struct(se) => {
let rest = match &se.rest {
StructRest::Base(e) => Some(self.lower_expr(e)),
self.expr(span, hir::ExprKind::DropTemps(expr))
}
- fn expr_match(
+ pub(super) fn expr_match(
&mut self,
span: Span,
arg: &'hir hir::Expr<'hir>,
self.arena.alloc(self.expr(sp, hir::ExprKind::Tup(&[])))
}
- fn expr_call_mut(
+ pub(super) fn expr_usize(&mut self, sp: Span, value: usize) -> hir::Expr<'hir> {
+ self.expr(
+ sp,
+ hir::ExprKind::Lit(hir::Lit {
+ span: sp,
+ node: ast::LitKind::Int(
+ value as u128,
+ ast::LitIntType::Unsigned(ast::UintTy::Usize),
+ ),
+ }),
+ )
+ }
+
+ pub(super) fn expr_u32(&mut self, sp: Span, value: u32) -> hir::Expr<'hir> {
+ self.expr(
+ sp,
+ hir::ExprKind::Lit(hir::Lit {
+ span: sp,
+ node: ast::LitKind::Int(value.into(), ast::LitIntType::Unsigned(ast::UintTy::U32)),
+ }),
+ )
+ }
+
+ pub(super) fn expr_char(&mut self, sp: Span, value: char) -> hir::Expr<'hir> {
+ self.expr(sp, hir::ExprKind::Lit(hir::Lit { span: sp, node: ast::LitKind::Char(value) }))
+ }
+
+ pub(super) fn expr_str(&mut self, sp: Span, value: Symbol) -> hir::Expr<'hir> {
+ self.expr(
+ sp,
+ hir::ExprKind::Lit(hir::Lit {
+ span: sp,
+ node: ast::LitKind::Str(value, ast::StrStyle::Cooked),
+ }),
+ )
+ }
+
+ pub(super) fn expr_call_mut(
&mut self,
span: Span,
e: &'hir hir::Expr<'hir>,
self.expr(span, hir::ExprKind::Call(e, args))
}
- fn expr_call(
+ pub(super) fn expr_call(
&mut self,
span: Span,
e: &'hir hir::Expr<'hir>,
)
}
+ /// `<LangItem>::name`
+ pub(super) fn expr_lang_item_type_relative(
+ &mut self,
+ span: Span,
+ lang_item: hir::LangItem,
+ name: Symbol,
+ ) -> hir::Expr<'hir> {
+ let path = hir::ExprKind::Path(hir::QPath::TypeRelative(
+ self.arena.alloc(self.ty(
+ span,
+ hir::TyKind::Path(hir::QPath::LangItem(lang_item, self.lower_span(span), None)),
+ )),
+ self.arena.alloc(hir::PathSegment::new(
+ Ident::new(name, span),
+ self.next_id(),
+ Res::Err,
+ )),
+ ));
+ self.expr(span, path)
+ }
+
pub(super) fn expr_ident(
&mut self,
sp: Span,
self.expr(b.span, hir::ExprKind::Block(b, None))
}
+ pub(super) fn expr_array_ref(
+ &mut self,
+ span: Span,
+ elements: &'hir [hir::Expr<'hir>],
+ ) -> hir::Expr<'hir> {
+ let addrof = hir::ExprKind::AddrOf(
+ hir::BorrowKind::Ref,
+ hir::Mutability::Not,
+ self.arena.alloc(self.expr(span, hir::ExprKind::Array(elements))),
+ );
+ self.expr(span, addrof)
+ }
+
pub(super) fn expr(&mut self, span: Span, kind: hir::ExprKind<'hir>) -> hir::Expr<'hir> {
let hir_id = self.next_id();
hir::Expr { hir_id, kind, span: self.lower_span(span) }
}
- fn expr_field(
+ pub(super) fn expr_field(
&mut self,
ident: Ident,
expr: &'hir hir::Expr<'hir>,
}
}
- fn arm(&mut self, pat: &'hir hir::Pat<'hir>, expr: &'hir hir::Expr<'hir>) -> hir::Arm<'hir> {
+ pub(super) fn arm(
+ &mut self,
+ pat: &'hir hir::Pat<'hir>,
+ expr: &'hir hir::Expr<'hir>,
+ ) -> hir::Arm<'hir> {
hir::Arm {
hir_id: self.next_id(),
pat,
--- /dev/null
+use super::LoweringContext;
+use rustc_ast as ast;
+use rustc_ast::visit::{self, Visitor};
+use rustc_ast::*;
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_hir as hir;
+use rustc_span::{
+ sym,
+ symbol::{kw, Ident},
+ Span,
+};
+
+impl<'hir> LoweringContext<'_, 'hir> {
+ pub(crate) fn lower_format_args(&mut self, sp: Span, fmt: &FormatArgs) -> hir::ExprKind<'hir> {
+ expand_format_args(self, sp, fmt)
+ }
+}
+
+#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
+enum ArgumentType {
+ Format(FormatTrait),
+ Usize,
+}
+
+/// Generate a hir expression representing an argument to a format_args invocation.
+///
+/// Generates:
+///
+/// ```text
+/// <core::fmt::ArgumentV1>::new_…(arg)
+/// ```
+fn make_argument<'hir>(
+ ctx: &mut LoweringContext<'_, 'hir>,
+ sp: Span,
+ arg: &'hir hir::Expr<'hir>,
+ ty: ArgumentType,
+) -> hir::Expr<'hir> {
+ use ArgumentType::*;
+ use FormatTrait::*;
+ let new_fn = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ sp,
+ hir::LangItem::FormatArgument,
+ match ty {
+ Format(Display) => sym::new_display,
+ Format(Debug) => sym::new_debug,
+ Format(LowerExp) => sym::new_lower_exp,
+ Format(UpperExp) => sym::new_upper_exp,
+ Format(Octal) => sym::new_octal,
+ Format(Pointer) => sym::new_pointer,
+ Format(Binary) => sym::new_binary,
+ Format(LowerHex) => sym::new_lower_hex,
+ Format(UpperHex) => sym::new_upper_hex,
+ Usize => sym::from_usize,
+ },
+ ));
+ ctx.expr_call_mut(sp, new_fn, std::slice::from_ref(arg))
+}
+
+/// Generate a hir expression for a format_args Count.
+///
+/// Generates:
+///
+/// ```text
+/// <core::fmt::rt::v1::Count>::Is(…)
+/// ```
+///
+/// or
+///
+/// ```text
+/// <core::fmt::rt::v1::Count>::Param(…)
+/// ```
+///
+/// or
+///
+/// ```text
+/// <core::fmt::rt::v1::Count>::Implied
+/// ```
+fn make_count<'hir>(
+ ctx: &mut LoweringContext<'_, 'hir>,
+ sp: Span,
+ count: &Option<FormatCount>,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+) -> hir::Expr<'hir> {
+ match count {
+ Some(FormatCount::Literal(n)) => {
+ let count_is = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ sp,
+ hir::LangItem::FormatCount,
+ sym::Is,
+ ));
+ let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, *n)]);
+ ctx.expr_call_mut(sp, count_is, value)
+ }
+ Some(FormatCount::Argument(arg)) => {
+ if let Ok(arg_index) = arg.index {
+ let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
+ let count_param = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ sp,
+ hir::LangItem::FormatCount,
+ sym::Param,
+ ));
+ let value = ctx.arena.alloc_from_iter([ctx.expr_usize(sp, i)]);
+ ctx.expr_call_mut(sp, count_param, value)
+ } else {
+ ctx.expr(sp, hir::ExprKind::Err)
+ }
+ }
+ None => ctx.expr_lang_item_type_relative(sp, hir::LangItem::FormatCount, sym::Implied),
+ }
+}
+
+/// Generate a hir expression for a format_args placeholder specification.
+///
+/// Generates
+///
+/// ```text
+/// <core::fmt::rt::v1::Argument::new(
+/// …usize, // position
+/// '…', // fill
+/// <core::fmt::rt::v1::Alignment>::…, // alignment
+/// …u32, // flags
+/// <core::fmt::rt::v1::Count::…>, // width
+/// <core::fmt::rt::v1::Count::…>, // precision
+/// )
+/// ```
+fn make_format_spec<'hir>(
+ ctx: &mut LoweringContext<'_, 'hir>,
+ sp: Span,
+ placeholder: &FormatPlaceholder,
+ argmap: &mut FxIndexSet<(usize, ArgumentType)>,
+) -> hir::Expr<'hir> {
+ let position = match placeholder.argument.index {
+ Ok(arg_index) => {
+ let (i, _) =
+ argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
+ ctx.expr_usize(sp, i)
+ }
+ Err(_) => ctx.expr(sp, hir::ExprKind::Err),
+ };
+ let &FormatOptions {
+ ref width,
+ ref precision,
+ alignment,
+ fill,
+ sign,
+ alternate,
+ zero_pad,
+ debug_hex,
+ } = &placeholder.format_options;
+ let fill = ctx.expr_char(sp, fill.unwrap_or(' '));
+ let align = ctx.expr_lang_item_type_relative(
+ sp,
+ hir::LangItem::FormatAlignment,
+ match alignment {
+ Some(FormatAlignment::Left) => sym::Left,
+ Some(FormatAlignment::Right) => sym::Right,
+ Some(FormatAlignment::Center) => sym::Center,
+ None => sym::Unknown,
+ },
+ );
+ // This needs to match `FlagV1` in library/core/src/fmt/mod.rs.
+ let flags: u32 = ((sign == Some(FormatSign::Plus)) as u32)
+ | ((sign == Some(FormatSign::Minus)) as u32) << 1
+ | (alternate as u32) << 2
+ | (zero_pad as u32) << 3
+ | ((debug_hex == Some(FormatDebugHex::Lower)) as u32) << 4
+ | ((debug_hex == Some(FormatDebugHex::Upper)) as u32) << 5;
+ let flags = ctx.expr_u32(sp, flags);
+ let precision = make_count(ctx, sp, &precision, argmap);
+ let width = make_count(ctx, sp, &width, argmap);
+ let format_placeholder_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ sp,
+ hir::LangItem::FormatPlaceholder,
+ sym::new,
+ ));
+ let args = ctx.arena.alloc_from_iter([position, fill, align, flags, precision, width]);
+ ctx.expr_call_mut(sp, format_placeholder_new, args)
+}
+
+fn expand_format_args<'hir>(
+ ctx: &mut LoweringContext<'_, 'hir>,
+ macsp: Span,
+ fmt: &FormatArgs,
+) -> hir::ExprKind<'hir> {
+ let lit_pieces =
+ ctx.arena.alloc_from_iter(fmt.template.iter().enumerate().filter_map(|(i, piece)| {
+ match piece {
+ &FormatArgsPiece::Literal(s) => Some(ctx.expr_str(fmt.span, s)),
+ &FormatArgsPiece::Placeholder(_) => {
+ // Inject empty string before placeholders when not already preceded by a literal piece.
+ if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
+ Some(ctx.expr_str(fmt.span, kw::Empty))
+ } else {
+ None
+ }
+ }
+ }
+ }));
+ let lit_pieces = ctx.expr_array_ref(fmt.span, lit_pieces);
+
+ // Whether we'll use the `Arguments::new_v1_formatted` form (true),
+ // or the `Arguments::new_v1` form (false).
+ let mut use_format_options = false;
+
+ // Create a list of all _unique_ (argument, format trait) combinations.
+ // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
+ let mut argmap = FxIndexSet::default();
+ for piece in &fmt.template {
+ let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
+ if placeholder.format_options != Default::default() {
+ // Can't use basic form if there's any formatting options.
+ use_format_options = true;
+ }
+ if let Ok(index) = placeholder.argument.index {
+ if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
+ // Duplicate (argument, format trait) combination,
+ // which we'll only put once in the args array.
+ use_format_options = true;
+ }
+ }
+ }
+
+ let format_options = use_format_options.then(|| {
+ // Generate:
+ // &[format_spec_0, format_spec_1, format_spec_2]
+ let elements: Vec<_> = fmt
+ .template
+ .iter()
+ .filter_map(|piece| {
+ let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
+ Some(make_format_spec(ctx, macsp, placeholder, &mut argmap))
+ })
+ .collect();
+ ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
+ });
+
+ let arguments = fmt.arguments.all_args();
+
+ // If the args array contains exactly all the original arguments once,
+ // in order, we can use a simple array instead of a `match` construction.
+ // However, if there's a yield point in any argument except the first one,
+ // we don't do this, because an ArgumentV1 cannot be kept across yield points.
+ //
+ // This is an optimization, speeding up compilation about 1-2% in some cases.
+ // See https://github.com/rust-lang/rust/pull/106770#issuecomment-1380790609
+ let use_simple_array = argmap.len() == arguments.len()
+ && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
+ && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
+
+ let args = if use_simple_array {
+ // Generate:
+ // &[
+ // <core::fmt::ArgumentV1>::new_display(&arg0),
+ // <core::fmt::ArgumentV1>::new_lower_hex(&arg1),
+ // <core::fmt::ArgumentV1>::new_debug(&arg2),
+ // …
+ // ]
+ let elements: Vec<_> = arguments
+ .iter()
+ .zip(argmap)
+ .map(|(arg, (_, ty))| {
+ let sp = arg.expr.span.with_ctxt(macsp.ctxt());
+ let arg = ctx.lower_expr(&arg.expr);
+ let ref_arg = ctx.arena.alloc(ctx.expr(
+ sp,
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg),
+ ));
+ make_argument(ctx, sp, ref_arg, ty)
+ })
+ .collect();
+ ctx.expr_array_ref(macsp, ctx.arena.alloc_from_iter(elements))
+ } else {
+ // Generate:
+ // &match (&arg0, &arg1, &…) {
+ // args => [
+ // <core::fmt::ArgumentV1>::new_display(args.0),
+ // <core::fmt::ArgumentV1>::new_lower_hex(args.1),
+ // <core::fmt::ArgumentV1>::new_debug(args.0),
+ // …
+ // ]
+ // }
+ let args_ident = Ident::new(sym::args, macsp);
+ let (args_pat, args_hir_id) = ctx.pat_ident(macsp, args_ident);
+ let args = ctx.arena.alloc_from_iter(argmap.iter().map(|&(arg_index, ty)| {
+ if let Some(arg) = arguments.get(arg_index) {
+ let sp = arg.expr.span.with_ctxt(macsp.ctxt());
+ let args_ident_expr = ctx.expr_ident(macsp, args_ident, args_hir_id);
+ let arg = ctx.arena.alloc(ctx.expr(
+ sp,
+ hir::ExprKind::Field(
+ args_ident_expr,
+ Ident::new(sym::integer(arg_index), macsp),
+ ),
+ ));
+ make_argument(ctx, sp, arg, ty)
+ } else {
+ ctx.expr(macsp, hir::ExprKind::Err)
+ }
+ }));
+ let elements: Vec<_> = arguments
+ .iter()
+ .map(|arg| {
+ let arg_expr = ctx.lower_expr(&arg.expr);
+ ctx.expr(
+ arg.expr.span.with_ctxt(macsp.ctxt()),
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, arg_expr),
+ )
+ })
+ .collect();
+ let args_tuple = ctx
+ .arena
+ .alloc(ctx.expr(macsp, hir::ExprKind::Tup(ctx.arena.alloc_from_iter(elements))));
+ let array = ctx.arena.alloc(ctx.expr(macsp, hir::ExprKind::Array(args)));
+ let match_arms = ctx.arena.alloc_from_iter([ctx.arm(args_pat, array)]);
+ let match_expr = ctx.arena.alloc(ctx.expr_match(
+ macsp,
+ args_tuple,
+ match_arms,
+ hir::MatchSource::FormatArgs,
+ ));
+ ctx.expr(
+ macsp,
+ hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, match_expr),
+ )
+ };
+
+ if let Some(format_options) = format_options {
+ // Generate:
+ // <core::fmt::Arguments>::new_v1_formatted(
+ // lit_pieces,
+ // args,
+ // format_options,
+ // unsafe { ::core::fmt::UnsafeArg::new() }
+ // )
+ let new_v1_formatted = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ macsp,
+ hir::LangItem::FormatArguments,
+ sym::new_v1_formatted,
+ ));
+ let unsafe_arg_new = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ macsp,
+ hir::LangItem::FormatUnsafeArg,
+ sym::new,
+ ));
+ let unsafe_arg_new_call = ctx.expr_call(macsp, unsafe_arg_new, &[]);
+ let hir_id = ctx.next_id();
+ let unsafe_arg = ctx.expr_block(ctx.arena.alloc(hir::Block {
+ stmts: &[],
+ expr: Some(unsafe_arg_new_call),
+ hir_id,
+ rules: hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::CompilerGenerated),
+ span: macsp,
+ targeted_by_break: false,
+ }));
+ let args = ctx.arena.alloc_from_iter([lit_pieces, args, format_options, unsafe_arg]);
+ hir::ExprKind::Call(new_v1_formatted, args)
+ } else {
+ // Generate:
+ // <core::fmt::Arguments>::new_v1(
+ // lit_pieces,
+ // args,
+ // )
+ let new_v1 = ctx.arena.alloc(ctx.expr_lang_item_type_relative(
+ macsp,
+ hir::LangItem::FormatArguments,
+ sym::new_v1,
+ ));
+ let new_args = ctx.arena.alloc_from_iter([lit_pieces, args]);
+ hir::ExprKind::Call(new_v1, new_args)
+ }
+}
+
+fn may_contain_yield_point(e: &ast::Expr) -> bool {
+ struct MayContainYieldPoint(bool);
+
+ impl Visitor<'_> for MayContainYieldPoint {
+ fn visit_expr(&mut self, e: &ast::Expr) {
+ if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
+ self.0 = true;
+ } else {
+ visit::walk_expr(self, e);
+ }
+ }
+
+ fn visit_mac_call(&mut self, _: &ast::MacCall) {
+ // Macros should be expanded at this point.
+ unreachable!("unexpanded macro in ast lowering");
+ }
+
+ fn visit_item(&mut self, _: &ast::Item) {
+ // Do not recurse into nested items.
+ }
+ }
+
+ let mut visitor = MayContainYieldPoint(false);
+ visitor.visit_expr(e);
+ visitor.0
+}
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
let mut nodes = IndexVec::new();
// This node's parent should never be accessed: the owner's parent is computed by the
- // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
+ // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
// used.
nodes.push(Some(ParentedNode { parent: ItemLocalId::INVALID, node: item.into() }));
let mut collector = NodeCollector {
});
}
- fn visit_fn(
- &mut self,
- fk: intravisit::FnKind<'hir>,
- fd: &'hir FnDecl<'hir>,
- b: BodyId,
- _: Span,
- id: HirId,
- ) {
- assert_eq!(self.owner, id.owner);
- assert_eq!(self.parent_node, id.local_id);
- intravisit::walk_fn(self, fk, fd, b, id);
- }
-
fn visit_block(&mut self, block: &'hir Block<'hir>) {
self.insert(block.span, block.hir_id, Node::Block(block));
self.with_parent(block.hir_id, |this| {
current_hir_id_owner: hir::CRATE_OWNER_ID,
item_local_id_counter: hir::ItemLocalId::new(0),
node_id_to_local_id: Default::default(),
- local_id_to_def_id: SortedMap::new(),
trait_map: Default::default(),
// Lowering state.
//
// The first two are produced by recursively invoking
// `lower_use_tree` (and indeed there may be things
- // like `use foo::{a::{b, c}}` and so forth). They
+ // like `use foo::{a::{b, c}}` and so forth). They
// wind up being directly added to
// `self.items`. However, the structure of this
// function also requires us to return one item, and
mod block;
mod errors;
mod expr;
+mod format;
mod index;
mod item;
mod lifetime_collector;
current_hir_id_owner: hir::OwnerId,
item_local_id_counter: hir::ItemLocalId,
- local_id_to_def_id: SortedMap<ItemLocalId, LocalDefId>,
trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
impl_trait_defs: Vec<hir::GenericParam<'hir>>,
pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
let sess = tcx.sess;
+ tcx.ensure().output_filenames(());
let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal();
let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
let current_attrs = std::mem::take(&mut self.attrs);
let current_bodies = std::mem::take(&mut self.bodies);
let current_node_ids = std::mem::take(&mut self.node_id_to_local_id);
- let current_id_to_def_id = std::mem::take(&mut self.local_id_to_def_id);
let current_trait_map = std::mem::take(&mut self.trait_map);
let current_owner =
std::mem::replace(&mut self.current_hir_id_owner, hir::OwnerId { def_id });
self.attrs = current_attrs;
self.bodies = current_bodies;
self.node_id_to_local_id = current_node_ids;
- self.local_id_to_def_id = current_id_to_def_id;
self.trait_map = current_trait_map;
self.current_hir_id_owner = current_owner;
self.item_local_id_counter = current_local_counter;
fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInfo<'hir> {
let attrs = std::mem::take(&mut self.attrs);
let mut bodies = std::mem::take(&mut self.bodies);
- let local_id_to_def_id = std::mem::take(&mut self.local_id_to_def_id);
let trait_map = std::mem::take(&mut self.trait_map);
#[cfg(debug_assertions)]
let (hash_including_bodies, hash_without_bodies) = self.hash_owner(node, &bodies);
let (nodes, parenting) =
index::index_hir(self.tcx.sess, &*self.tcx.definitions_untracked(), node, &bodies);
- let nodes = hir::OwnerNodes {
- hash_including_bodies,
- hash_without_bodies,
- nodes,
- bodies,
- local_id_to_def_id,
- };
+ let nodes = hir::OwnerNodes { hash_including_bodies, hash_without_bodies, nodes, bodies };
let attrs = {
let hash = self.tcx.with_stable_hashing_context(|mut hcx| {
let mut stable_hasher = StableHasher::new();
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
}
- /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
+ /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
/// queries which depend on the full HIR tree and those which only depend on the item signature.
fn hash_owner(
&mut self,
assert_ne!(local_id, hir::ItemLocalId::new(0));
if let Some(def_id) = self.opt_local_def_id(ast_node_id) {
self.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
- self.local_id_to_def_id.insert(local_id, def_id);
}
if let Some(traits) = self.resolver.trait_map.remove(&ast_node_id) {
itctx: &ImplTraitContext,
) -> hir::Ty<'hir> {
// Check whether we should interpret this as a bare trait object.
- // This check mirrors the one in late resolution. We only introduce this special case in
+ // This check mirrors the one in late resolution. We only introduce this special case in
// the rare occurrence we need to lower `Fresh` anonymous lifetimes.
// The other cases when a qpath should be opportunistically made a trait object are handled
// by `ty_path`.
this.with_remapping(new_remapping, |this| {
// We have to be careful to get elision right here. The
// idea is that we create a lifetime parameter for each
- // lifetime in the return type. So, given a return type
+ // lifetime in the return type. So, given a return type
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
// Future<Output = &'1 [ &'2 u32 ]>`.
//
// Create the `Foo<...>` reference itself. Note that the `type
// Foo = impl Trait` is, internally, created as a child of the
- // async fn, so the *type parameters* are inherited. It's
+ // async fn, so the *type parameters* are inherited. It's
// only the lifetime parameters that we must supply.
let opaque_ty_ref = hir::TyKind::OpaqueDef(
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
replace_span: self.ending_semi_or_hi(item.span),
extern_block_suggestion: match sig.header.ext {
Extern::None => None,
- Extern::Implicit(start_span) => Some(ExternBlockSuggestion {
+ Extern::Implicit(start_span) => Some(ExternBlockSuggestion::Implicit {
start_span,
end_span: item.span.shrink_to_hi(),
- abi: None,
- }),
- Extern::Explicit(abi, start_span) => Some(ExternBlockSuggestion {
- start_span,
- end_span: item.span.shrink_to_hi(),
- abi: Some(abi.symbol_unescaped),
}),
+ Extern::Explicit(abi, start_span) => {
+ Some(ExternBlockSuggestion::Explicit {
+ start_span,
+ end_span: item.span.shrink_to_hi(),
+ abi: abi.symbol_unescaped,
+ })
+ }
},
});
}
//! Errors emitted by ast_passes.
-use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
pub extern_block_suggestion: Option<ExternBlockSuggestion>,
}
-pub struct ExternBlockSuggestion {
- pub start_span: Span,
- pub end_span: Span,
- pub abi: Option<Symbol>,
-}
-
-impl AddToDiagnostic for ExternBlockSuggestion {
- fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
- where
- F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
- {
- let start_suggestion = if let Some(abi) = self.abi {
- format!("extern \"{}\" {{", abi)
- } else {
- "extern {".to_owned()
- };
- let end_suggestion = " }".to_owned();
-
- diag.multipart_suggestion(
- fluent::extern_block_suggestion,
- vec![(self.start_span, start_suggestion), (self.end_span, end_suggestion)],
- Applicability::MaybeIncorrect,
- );
- }
+#[derive(Subdiagnostic)]
+pub enum ExternBlockSuggestion {
+ #[multipart_suggestion(ast_passes_extern_block_suggestion, applicability = "maybe-incorrect")]
+ Implicit {
+ #[suggestion_part(code = "extern {{")]
+ start_span: Span,
+ #[suggestion_part(code = " }}")]
+ end_span: Span,
+ },
+ #[multipart_suggestion(ast_passes_extern_block_suggestion, applicability = "maybe-incorrect")]
+ Explicit {
+ #[suggestion_part(code = "extern \"{abi}\" {{")]
+ start_span: Span,
+ #[suggestion_part(code = " }}")]
+ end_span: Span,
+ abi: Symbol,
+ },
}
[lib]
[dependencies]
-rustc_span = { path = "../rustc_span" }
rustc_ast = { path = "../rustc_ast" }
+rustc_parse_format = { path = "../rustc_parse_format" }
+rustc_span = { path = "../rustc_span" }
use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
use rustc_ast::{self as ast, BlockCheckMode};
+use rustc_ast::{
+ FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, FormatDebugHex, FormatSign,
+ FormatTrait,
+};
+use std::fmt::Write;
impl<'a> State<'a> {
fn print_else(&mut self, els: Option<&ast::Expr>) {
self.word("]");
}
ast::ExprKind::Range(start, end, limits) => {
- // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
+ // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
// than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
// Here we use a fake precedence value so that any child with lower precedence than
- // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
+ // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
let fake_prec = AssocOp::LOr.precedence() as i8;
if let Some(e) = start {
self.print_expr_maybe_paren(e, fake_prec);
}
}
ast::ExprKind::InlineAsm(a) => {
+ // FIXME: This should have its own syntax, distinct from a macro invocation.
self.word("asm!");
self.print_inline_asm(a);
}
+ ast::ExprKind::FormatArgs(fmt) => {
+ // FIXME: This should have its own syntax, distinct from a macro invocation.
+ self.word("format_args!");
+ self.popen();
+ self.rbox(0, Inconsistent);
+ self.word(reconstruct_format_args_template_string(&fmt.template));
+ for arg in fmt.arguments.all_args() {
+ self.word_space(",");
+ self.print_expr(&arg.expr);
+ }
+ self.end();
+ self.pclose();
+ }
ast::ExprKind::MacCall(m) => self.print_mac(m),
ast::ExprKind::Paren(e) => {
self.popen();
}
}
}
+
+pub fn reconstruct_format_args_template_string(pieces: &[FormatArgsPiece]) -> String {
+ let mut template = "\"".to_string();
+ for piece in pieces {
+ match piece {
+ FormatArgsPiece::Literal(s) => {
+ for c in s.as_str().escape_debug() {
+ template.push(c);
+ if let '{' | '}' = c {
+ template.push(c);
+ }
+ }
+ }
+ FormatArgsPiece::Placeholder(p) => {
+ template.push('{');
+ let (Ok(n) | Err(n)) = p.argument.index;
+ write!(template, "{n}").unwrap();
+ if p.format_options != Default::default() || p.format_trait != FormatTrait::Display
+ {
+ template.push_str(":");
+ }
+ if let Some(fill) = p.format_options.fill {
+ template.push(fill);
+ }
+ match p.format_options.alignment {
+ Some(FormatAlignment::Left) => template.push_str("<"),
+ Some(FormatAlignment::Right) => template.push_str(">"),
+ Some(FormatAlignment::Center) => template.push_str("^"),
+ None => {}
+ }
+ match p.format_options.sign {
+ Some(FormatSign::Plus) => template.push('+'),
+ Some(FormatSign::Minus) => template.push('-'),
+ None => {}
+ }
+ if p.format_options.alternate {
+ template.push('#');
+ }
+ if p.format_options.zero_pad {
+ template.push('0');
+ }
+ if let Some(width) = &p.format_options.width {
+ match width {
+ FormatCount::Literal(n) => write!(template, "{n}").unwrap(),
+ FormatCount::Argument(FormatArgPosition {
+ index: Ok(n) | Err(n), ..
+ }) => {
+ write!(template, "{n}$").unwrap();
+ }
+ }
+ }
+ if let Some(precision) = &p.format_options.precision {
+ template.push('.');
+ match precision {
+ FormatCount::Literal(n) => write!(template, "{n}").unwrap(),
+ FormatCount::Argument(FormatArgPosition {
+ index: Ok(n) | Err(n), ..
+ }) => {
+ write!(template, "{n}$").unwrap();
+ }
+ }
+ }
+ match p.format_options.debug_hex {
+ Some(FormatDebugHex::Lower) => template.push('x'),
+ Some(FormatDebugHex::Upper) => template.push('X'),
+ None => {}
+ }
+ template.push_str(match p.format_trait {
+ FormatTrait::Display => "",
+ FormatTrait::Debug => "?",
+ FormatTrait::LowerExp => "e",
+ FormatTrait::UpperExp => "E",
+ FormatTrait::Octal => "o",
+ FormatTrait::Pointer => "p",
+ FormatTrait::Binary => "b",
+ FormatTrait::LowerHex => "x",
+ FormatTrait::UpperHex => "X",
+ });
+ template.push('}');
+ }
+ }
+ }
+ template.push('"');
+ template
+}
desc,
);
- err.span_label(borrow_span, format!("borrow of {} occurs here", borrow_desc));
+ err.span_label(borrow_span, format!("{} is borrowed here", borrow_desc));
err.span_label(span, format!("use of borrowed {}", borrow_desc));
err
}
desc,
);
- err.span_label(borrow_span, format!("borrow of {} occurs here", desc));
- err.span_label(span, format!("assignment to borrowed {} occurs here", desc));
+ err.span_label(borrow_span, format!("{} is borrowed here", desc));
+ err.span_label(span, format!("{} is assigned to here but it was already borrowed", desc));
err
}
| mir::StatementKind::AscribeUserType(..)
| mir::StatementKind::Coverage(..)
| mir::StatementKind::Intrinsic(..)
+ | mir::StatementKind::ConstEvalCounter
| mir::StatementKind::Nop => {}
}
}
let tcx = self.infcx.tcx;
// Find out if the predicates show that the type is a Fn or FnMut
- let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
- &[(ty::Predicate<'tcx>, Span)],
- >,
- substs| {
- predicates.0.iter().find_map(|(pred, _)| {
- let pred = if let Some(substs) = substs {
- predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
- } else {
- pred.kind().skip_binder()
- };
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
- if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
- return Some(hir::Mutability::Not);
- } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
- return Some(hir::Mutability::Mut);
- }
+ let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| {
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder()
+ && pred.self_ty() == ty
+ {
+ if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
+ return Some(hir::Mutability::Not);
+ } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
+ return Some(hir::Mutability::Mut);
}
- None
- })
+ }
+ None
};
// If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
// borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
// These types seem reasonably opaque enough that they could be substituted with their
// borrowed variants in a function body when we see a move error.
- let borrow_level = match ty.kind() {
- ty::Param(_) => find_fn_kind_from_did(
- tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
- .map_bound(|p| p.predicates),
- None,
- ),
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*def_id), Some(*substs))
- }
+ let borrow_level = match *ty.kind() {
+ ty::Param(_) => tcx
+ .explicit_predicates_of(self.mir_def_id().to_def_id())
+ .predicates
+ .iter()
+ .copied()
+ .find_map(find_fn_kind_from_did),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
+ .bound_explicit_item_bounds(def_id)
+ .subst_iter_copied(tcx, substs)
+ .find_map(find_fn_kind_from_did),
ty::Closure(_, substs) => match substs.as_closure().kind() {
ty::ClosureKind::Fn => Some(hir::Mutability::Not),
ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
let cause = ObligationCause::new(
span,
- self.mir_hir_id(),
+ self.mir_def_id(),
rustc_infer::traits::ObligationCauseCode::MiscObligation,
);
let errors = rustc_trait_selection::traits::fully_solve_bound(
&self.local_names,
&mut err,
"",
- None,
+ Some(borrow_span),
None,
);
}
let mut back_edge_stack = Vec::new();
predecessor_locations(self.body, location).for_each(|predecessor| {
- if location.dominates(predecessor, &self.dominators) {
+ if location.dominates(predecessor, self.dominators()) {
back_edge_stack.push(predecessor)
} else {
stack.push(predecessor);
let mut has_predecessor = false;
predecessor_locations(self.body, location).for_each(|predecessor| {
- if location.dominates(predecessor, &self.dominators) {
+ if location.dominates(predecessor, self.dominators()) {
back_edge_stack.push(predecessor)
} else {
stack.push(predecessor);
match ty.kind() {
ty::FnDef(_, _) | ty::FnPtr(_) => self.annotate_fn_sig(
self.mir_def_id(),
- self.infcx.tcx.fn_sig(self.mir_def_id()),
+ self.infcx.tcx.fn_sig(self.mir_def_id()).subst_identity(),
),
_ => None,
}
//! Print diagnostics to explain why values are borrowed.
use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir as hir;
+use rustc_hir::intravisit::Visitor;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::NllRegionVariableOrigin;
use rustc_middle::mir::{
use rustc_middle::ty::{self, RegionVid, TyCtxt};
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, DesugaringKind, Span};
+use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
use crate::{
borrow_span: Option<Span>,
multiple_borrow_span: Option<(Span, Span)>,
) {
+ if let Some(span) = borrow_span {
+ let def_id = body.source.def_id();
+ if let Some(node) = tcx.hir().get_if_local(def_id)
+ && let Some(body_id) = node.body_id()
+ {
+ let body = tcx.hir().body(body_id);
+ let mut expr_finder = FindExprBySpan::new(span);
+ expr_finder.visit_expr(body.value);
+ if let Some(mut expr) = expr_finder.result {
+ while let hir::ExprKind::AddrOf(_, _, inner)
+ | hir::ExprKind::Unary(hir::UnOp::Deref, inner)
+ | hir::ExprKind::Field(inner, _)
+ | hir::ExprKind::MethodCall(_, inner, _, _)
+ | hir::ExprKind::Index(inner, _) = &expr.kind
+ {
+ expr = inner;
+ }
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind
+ && let [hir::PathSegment { ident, args: None, .. }] = p.segments
+ && let hir::def::Res::Local(hir_id) = p.res
+ && let Some(hir::Node::Pat(pat)) = tcx.hir().find(hir_id)
+ {
+ err.span_label(
+ pat.span,
+ &format!("binding `{ident}` declared here"),
+ );
+ }
+ }
+ }
+ }
match *self {
BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => {
let message = match later_use_kind {
);
}
}
- CallKind::Normal { self_arg, desugaring, method_did } => {
+ CallKind::Normal { self_arg, desugaring, method_did, method_substs } => {
let self_arg = self_arg.unwrap();
let tcx = self.infcx.tcx;
if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
"{place_name} {partially_str}moved due to this method call{loop_message}",
),
);
+
let infcx = tcx.infer_ctxt().build();
+ // Erase and shadow everything that could be passed to the new infcx.
let ty = tcx.erase_regions(moved_place.ty(self.body, tcx).ty);
+ let method_substs = tcx.erase_regions(method_substs);
+
if let ty::Adt(def, substs) = ty.kind()
&& Some(def.did()) == tcx.lang_items().pin_type()
&& let ty::Ref(_, _, hir::Mutability::Mut) = substs.type_at(0).kind()
&& let self_ty = infcx.replace_bound_vars_with_fresh_vars(
fn_call_span,
LateBoundRegionConversionTime::FnCall,
- tcx.fn_sig(method_did).input(0),
+ tcx.fn_sig(method_did).subst(tcx, method_substs).input(0),
)
&& infcx.can_eq(self.param_env, ty, self_ty).is_ok()
{
};
self.note_type_does_not_implement_copy(err, &place_desc, place_ty, Some(span), "");
- use_spans.args_span_label(err, format!("move out of {place_desc} occurs here"));
+ use_spans.args_span_label(err, format!("{place_desc} is moved here"));
}
}
}
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
+use rustc_hir::def::Res::Def;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::Visitor;
+use rustc_hir::GenericBound::Trait;
+use rustc_hir::QPath::Resolved;
+use rustc_hir::WherePredicate::BoundPredicate;
+use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
use rustc_infer::infer::{
error_reporting::nice_region_error::{
self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
false
}
+ // For generic associated types (GATs) which implied 'static requirement
+ // from higher-ranked trait bounds (HRTB). Try to locate span of the trait
+ // and the span which bounded to the trait for adding 'static lifetime suggestion
+ fn suggest_static_lifetime_for_gat_from_hrtb(
+ &self,
+ diag: &mut DiagnosticBuilder<'_, ErrorGuaranteed>,
+ lower_bound: RegionVid,
+ ) {
+ let mut suggestions = vec![];
+ let hir = self.infcx.tcx.hir();
+
+ // find generic associated types in the given region 'lower_bound'
+ let gat_id_and_generics = self
+ .regioncx
+ .placeholders_contained_in(lower_bound)
+ .map(|placeholder| {
+ if let Some(id) = placeholder.name.get_id()
+ && let Some(placeholder_id) = id.as_local()
+ && let gat_hir_id = hir.local_def_id_to_hir_id(placeholder_id)
+ && let Some(generics_impl) = hir.get_parent(gat_hir_id).generics()
+ {
+ Some((gat_hir_id, generics_impl))
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ debug!(?gat_id_and_generics);
+
+ // find higher-ranked trait bounds bounded to the generic associated types
+ let mut hrtb_bounds = vec![];
+ gat_id_and_generics.iter().flatten().for_each(|(gat_hir_id, generics)| {
+ for pred in generics.predicates {
+ let BoundPredicate(
+ WhereBoundPredicate {
+ bound_generic_params,
+ bounds,
+ ..
+ }) = pred else { continue; };
+ if bound_generic_params
+ .iter()
+ .rfind(|bgp| hir.local_def_id_to_hir_id(bgp.def_id) == *gat_hir_id)
+ .is_some()
+ {
+ for bound in *bounds {
+ hrtb_bounds.push(bound);
+ }
+ }
+ }
+ });
+ debug!(?hrtb_bounds);
+
+ hrtb_bounds.iter().for_each(|bound| {
+ let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; };
+ diag.span_note(
+ *trait_span,
+ format!("due to current limitations in the borrow checker, this implies a `'static` lifetime")
+ );
+ let Some(generics_fn) = hir.get_generics(self.body.source.def_id().expect_local()) else { return; };
+ let Def(_, trait_res_defid) = trait_ref.path.res else { return; };
+ debug!(?generics_fn);
+ generics_fn.predicates.iter().for_each(|predicate| {
+ let BoundPredicate(
+ WhereBoundPredicate {
+ span: bounded_span,
+ bounded_ty,
+ bounds,
+ ..
+ }
+ ) = predicate else { return; };
+ bounds.iter().for_each(|bd| {
+ if let Trait(PolyTraitRef { trait_ref: tr_ref, .. }, _) = bd
+ && let Def(_, res_defid) = tr_ref.path.res
+ && res_defid == trait_res_defid // trait id matches
+ && let TyKind::Path(Resolved(_, path)) = bounded_ty.kind
+ && let Def(_, defid) = path.res
+ && generics_fn.params
+ .iter()
+ .rfind(|param| param.def_id.to_def_id() == defid)
+ .is_some() {
+ suggestions.push((bounded_span.shrink_to_hi(), format!(" + 'static")));
+ }
+ });
+ });
+ });
+ if suggestions.len() > 0 {
+ suggestions.dedup();
+ diag.multipart_suggestion_verbose(
+ format!("consider restricting the type parameter to the `'static` lifetime"),
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
/// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`.
pub(crate) fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) {
// Iterate through all the errors, producing a diagnostic for each one. The diagnostics are
// to report it; we could probably handle it by
// iterating over the universal regions and reporting
// an error that multiple bounds are required.
- self.buffer_error(self.infcx.tcx.sess.create_err(
- GenericDoesNotLiveLongEnough {
+ let mut diag =
+ self.infcx.tcx.sess.create_err(GenericDoesNotLiveLongEnough {
kind: type_test.generic_kind.to_string(),
span: type_test_span,
- },
- ));
+ });
+
+ // Add notes and suggestions for the case of 'static lifetime
+ // implied but not specified when a generic associated types
+ // are from higher-ranked trait bounds
+ self.suggest_static_lifetime_for_gat_from_hrtb(
+ &mut diag,
+ type_test.lower_bound,
+ );
+
+ self.buffer_error(diag);
}
}
if *outlived_f != ty::ReStatic {
return;
}
+ let suitable_region = self.infcx.tcx.is_suitable_region(f);
+ let Some(suitable_region) = suitable_region else { return; };
- let fn_returns = self
- .infcx
- .tcx
- .is_suitable_region(f)
- .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
- .unwrap_or_default();
-
- if fn_returns.is_empty() {
- return;
- }
+ let fn_returns = self.infcx.tcx.return_type_impl_or_dyn_traits(suitable_region.def_id);
let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
param
};
let captures = format!("captures data from {arg}");
- return nice_region_error::suggest_new_region_bound(
- self.infcx.tcx,
- diag,
- fn_returns,
- lifetime.to_string(),
- Some(arg),
- captures,
- Some((param.param_ty_span, param.param_ty.to_string())),
- self.infcx.tcx.is_suitable_region(f).map(|r| r.def_id),
+ if !fn_returns.is_empty() {
+ nice_region_error::suggest_new_region_bound(
+ self.infcx.tcx,
+ diag,
+ fn_returns,
+ lifetime.to_string(),
+ Some(arg),
+ captures,
+ Some((param.param_ty_span, param.param_ty.to_string())),
+ Some(suitable_region.def_id),
+ );
+ return;
+ }
+
+ let Some((alias_tys, alias_span)) = self
+ .infcx
+ .tcx
+ .return_type_impl_or_dyn_traits_with_type_alias(suitable_region.def_id) else { return; };
+
+ // in case the return type of the method is a type alias
+ let mut spans_suggs: Vec<_> = Vec::new();
+ for alias_ty in alias_tys {
+ if alias_ty.span.desugaring_kind().is_some() {
+ // Skip `async` desugaring `impl Future`.
+ ()
+ }
+ if let TyKind::TraitObject(_, lt, _) = alias_ty.kind {
+ spans_suggs.push((lt.ident.span.shrink_to_hi(), " + 'a".to_string()));
+ }
+ }
+ spans_suggs.push((alias_span.shrink_to_hi(), "<'a>".to_string()));
+ diag.multipart_suggestion_verbose(
+ &format!(
+ "to declare that the trait object {captures}, you can add a lifetime parameter `'a` in the type alias"
+ ),
+ spans_suggs,
+ Applicability::MaybeIncorrect,
);
}
}
LocalMutationIsAllowed::Yes,
);
}
- StatementKind::Nop
+ StatementKind::ConstEvalCounter
+ | StatementKind::Nop
| StatementKind::Retag { .. }
| StatementKind::Deinit(..)
| StatementKind::SetDiscriminant { .. } => {
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(never_type)]
+#![feature(once_cell)]
#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
#![feature(trusted_step)]
use either::Either;
use smallvec::SmallVec;
+use std::cell::OnceCell;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
borrow_set: Rc::clone(&borrow_set),
- dominators: Dominators::dummy(), // not used
+ dominators: Default::default(),
upvars: Vec::new(),
local_names: IndexVec::from_elem(None, &promoted_body.local_decls),
region_names: RefCell::default(),
};
}
- let dominators = body.basic_blocks.dominators();
-
let mut mbcx = MirBorrowckCtxt {
infcx,
param_env,
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
borrow_set: Rc::clone(&borrow_set),
- dominators,
+ dominators: Default::default(),
upvars,
local_names,
region_names: RefCell::default(),
borrow_set: Rc<BorrowSet<'tcx>>,
/// Dominators for MIR
- dominators: Dominators<BasicBlock>,
+ dominators: OnceCell<Dominators<BasicBlock>>,
/// Information about upvars not necessarily preserved in types or MIR
upvars: Vec<Upvar<'tcx>>,
StatementKind::AscribeUserType(..)
// Doesn't have any language semantics
| StatementKind::Coverage(..)
- // Does not actually affect borrowck
+ // These do not actually affect borrowck
+ | StatementKind::ConstEvalCounter
| StatementKind::StorageLive(..) => {}
StatementKind::StorageDead(local) => {
self.access_place(
(Read(kind), BorrowKind::Unique | BorrowKind::Mut { .. }) => {
// Reading from mere reservations of mutable-borrows is OK.
- if !is_active(&this.dominators, borrow, location) {
+ if !is_active(this.dominators(), borrow, location) {
assert!(allow_two_phase_borrow(borrow.kind));
return Control::Continue;
}
// `self.foo` -- we want to double
// check that the location `*self`
// is mutable (i.e., this is not a
- // `Fn` closure). But if that
+ // `Fn` closure). But if that
// check succeeds, we want to
// *blame* the mutability on
// `place` (that is,
fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<Field> {
path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body())
}
+
+ fn dominators(&self) -> &Dominators<BasicBlock> {
+ self.dominators.get_or_init(|| self.body.basic_blocks.dominators())
+ }
}
mod error {
R1: Copy + Hash + Eq,
{
/// Remap the "member region" key using `map_fn`, producing a new
- /// member constraint set. This is used in the NLL code to map from
+ /// member constraint set. This is used in the NLL code to map from
/// the original `RegionVid` to an scc index. In some cases, we
/// may have multiple `R1` values mapping to the same `R2` key -- that
/// is ok, the two sets will be merged.
}
/// Iterate down the constraint indices associated with a given
- /// peek-region. You can then use `choice_regions` and other
+ /// peek-region. You can then use `choice_regions` and other
/// methods to access data.
pub(crate) fn indices(
&self,
// When the enclosing function is tagged with `#[rustc_regions]`,
// we dump out various bits of state as warnings. This is useful
- // for verifying that the compiler is behaving as expected. These
+ // for verifying that the compiler is behaving as expected. These
// warnings focus on the closure region requirements -- for
// viewing the intraprocedural state, the -Zdump-mir output is
// better.
ty::RawPtr(..) | ty::Ref(_, _, hir::Mutability::Not) => {
// For both derefs of raw pointers and `&T`
// references, the original path is `Copy` and
- // therefore not significant. In particular,
+ // therefore not significant. In particular,
// there is nothing the user can do to the
// original path that would invalidate the
// newly created reference -- and if there
match (elem, &base_ty.kind(), access) {
(_, _, Shallow(Some(ArtificialField::ArrayLength)))
| (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
- // The array length is like additional fields on the
+ // The array length is like additional fields on the
// type; it does not overlap any existing data there.
// Furthermore, if cannot actually be a prefix of any
// borrowed place (at least in MIR as it is currently.)
use rustc_data_structures::graph::scc::Sccs;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::CRATE_DEF_ID;
-use rustc_hir::CRATE_HIR_ID;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
self.scc_values.region_value_str(scc)
}
+ pub(crate) fn placeholders_contained_in<'a>(
+ &'a self,
+ r: RegionVid,
+ ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
+ let scc = self.constraint_sccs.scc(r.to_region_vid());
+ self.scc_values.placeholders_contained_in(scc)
+ }
+
/// Returns access to the value of `r` for debugging purposes.
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
let scc = self.constraint_sccs.scc(r.to_region_vid());
/// enforce the constraint).
///
/// The current value of `scc` at the time the method is invoked
- /// is considered a *lower bound*. If possible, we will modify
+ /// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
#[instrument(skip(self, member_constraint_index), level = "debug")]
//
// This is needed because -- particularly in the case
// where `ur` is a local bound -- we are sometimes in a
- // position to prove things that our caller cannot. See
+ // position to prove things that our caller cannot. See
// #53570 for an example.
if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) {
continue;
.map(|constraint| BlameConstraint {
category: constraint.category,
from_closure: constraint.from_closure,
- cause: ObligationCause::new(constraint.span, CRATE_HIR_ID, cause_code.clone()),
+ cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()),
variance_info: constraint.variance_info,
outlives_constraint: *constraint,
})
// '5: '6 ('6 is the target)
//
// Some of those regions are unified with `'6` (in the same
- // SCC). We want to screen those out. After that point, the
+ // SCC). We want to screen those out. After that point, the
// "closest" constraint we have to the end is going to be the
// most likely to be the point where the value escapes -- but
// we still want to screen for an "interesting" point to
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::ObligationCtxt;
+use crate::session_diagnostics::NonGenericOpaqueTypeParam;
+
use super::RegionInferenceContext;
impl<'tcx> RegionInferenceContext<'tcx> {
/// # Parameters
///
/// - `def_id`, the `impl Trait` type
- /// - `substs`, the substs used to instantiate this opaque type
+ /// - `substs`, the substs used to instantiate this opaque type
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
/// `opaque_defn.concrete_ty`
#[instrument(level = "debug", skip(self))]
}
let definition_ty = instantiated_ty
- .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false, origin)
+ .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
.ty;
if !check_opaque_type_parameter_valid(
// This logic duplicates most of `check_opaque_meets_bounds`.
// FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
let param_env = self.tcx.param_env(def_id);
- let body_id = self.tcx.local_def_id_to_hir_id(def_id);
// HACK This bubble is required for this tests to pass:
// type-alias-impl-trait/issue-67844-nested-opaque.rs
let infcx =
// the bounds that the function supplies.
let opaque_ty = self.tcx.mk_opaque(def_id.to_def_id(), id_substs);
if let Err(err) = ocx.eq(
- &ObligationCause::misc(instantiated_ty.span, body_id),
+ &ObligationCause::misc(instantiated_ty.span, def_id),
param_env,
opaque_ty,
definition_ty,
infcx
.err_ctxt()
.report_mismatched_types(
- &ObligationCause::misc(instantiated_ty.span, body_id),
+ &ObligationCause::misc(instantiated_ty.span, def_id),
opaque_ty,
definition_ty,
err,
ocx.register_obligation(Obligation::misc(
infcx.tcx,
instantiated_ty.span,
- body_id,
+ def_id,
param_env,
predicate,
));
for (i, arg) in opaque_type_key.substs.iter().enumerate() {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
- GenericArgKind::Lifetime(lt) if lt.is_static() => {
- tcx.sess
- .struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_label(
- tcx.def_span(opaque_generics.param_at(i, tcx).def_id),
- "cannot use static lifetime; use a bound lifetime \
- instead or remove the lifetime parameter from the \
- opaque type",
- )
- .emit();
- return false;
- }
GenericArgKind::Lifetime(lt) => {
matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
}
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx);
- tcx.sess
- .struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_note(
- tcx.def_span(opaque_param.def_id),
- &format!(
- "used non-generic {} `{}` for generic parameter",
- opaque_param.kind.descr(),
- arg,
- ),
- )
- .emit();
+ let kind = opaque_param.kind.descr();
+ tcx.sess.emit_err(NonGenericOpaqueTypeParam {
+ ty: arg,
+ kind,
+ span,
+ param_span: tcx.def_span(opaque_param.def_id),
+ });
return false;
}
}
use rustc_errors::{IntoDiagnosticArg, MultiSpan};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{GenericArg, Ty};
use rustc_span::Span;
use crate::diagnostics::RegionName;
#[label]
pub borrow_span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(borrowck_opaque_type_non_generic_param, code = "E0792")]
+pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
+ pub ty: GenericArg<'tcx>,
+ pub kind: &'a str,
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub param_span: Span,
+}
instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
locations: Locations,
) {
- for (predicate, span) in instantiated_predicates
- .predicates
- .into_iter()
- .zip(instantiated_predicates.spans.into_iter())
- {
+ for (predicate, span) in instantiated_predicates {
debug!(?predicate);
let category = ConstraintCategory::Predicate(span);
let predicate = self.normalize_with_category(predicate, locations, category);
closure_substs: ty::SubstsRef<'tcx>,
) {
// Extract the values of the free regions in `closure_substs`
- // into a vector. These are the regions that we will be
+ // into a vector. These are the regions that we will be
// relating to one another.
let closure_mapping = &UniversalRegions::closure_mapping(
self.tcx,
let upper_bounds = self.non_local_upper_bounds(fr);
// In case we find more than one, reduce to one for
- // convenience. This is to prevent us from generating more
+ // convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds);
let lower_bounds = self.non_local_bounds(&self.outlives, fr);
// In case we find more than one, reduce to one for
- // convenience. This is to prevent us from generating more
+ // convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
- }
-
- OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
- self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
+ .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
}
}
debug_assert!(self.drop_live_at.contains(term_point));
// Otherwise, scan backwards through the statements in the
- // block. One of them may be either a definition or use
+ // block. One of them may be either a definition or use
// live point.
let term_location = self.cx.elements.to_location(term_point);
debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
| StatementKind::StorageDead(..)
| StatementKind::Retag { .. }
| StatementKind::Coverage(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
StatementKind::Deinit(..) | StatementKind::SetDiscriminant { .. } => {
bug!("Statement not allowed in this MIR phase")
fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
let tcx = self.tcx();
- // Erase the regions from `ty` to get a global type. The
+ // Erase the regions from `ty` to get a global type. The
// `Sized` bound in no way depends on precise regions, so this
// shouldn't affect `is_sized`.
let erased_ty = tcx.erase_regions(ty);
/// `ty::Region` to the internal `RegionVid` we are using. This is
/// used because trait matching and type-checking will feed us
/// region constraints that reference those regions and we need to
- /// be able to map them our internal `RegionVid`. This is
+ /// be able to map them to our internal `RegionVid`. This is
/// basically equivalent to an `InternalSubsts`, except that it also
/// contains an entry for `ReStatic` -- it might be nice to just
/// use a substs, and then handle `ReStatic` another way.
// C-variadic fns also have a `VaList` input that's not listed in the signature
// (as it's created inside the body itself, not passed in from outside).
if let DefiningTy::FnDef(def_id, _) = defining_ty {
- if self.infcx.tcx.fn_sig(def_id).c_variadic() {
+ if self.infcx.tcx.fn_sig(def_id).skip_binder().c_variadic() {
let va_list_did = self.infcx.tcx.require_lang_item(
LangItem::VaList,
Some(self.infcx.tcx.def_span(self.mir_def.did)),
let closure_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
// The "inputs" of the closure in the
- // signature appear as a tuple. The MIR side
+ // signature appear as a tuple. The MIR side
// flattens this tuple.
let (&output, tuplized_inputs) =
inputs_and_output.skip_binder().split_last().unwrap();
}
DefiningTy::FnDef(def_id, _) => {
- let sig = tcx.fn_sig(def_id);
+ let sig = tcx.fn_sig(def_id).subst_identity();
let sig = indices.fold_to_region_vids(tcx, sig);
sig.inputs_and_output()
}
| ExprKind::Continue(_)
| ExprKind::Err
| ExprKind::Field(_, _)
+ | ExprKind::FormatArgs(_)
| ExprKind::ForLoop(_, _, _, _)
| ExprKind::If(_, _, _)
| ExprKind::IncludedBytes(..)
// some additional `AssertParamIsClone` assertions.
//
// We can use the simple form if either of the following are true.
- // - The type derives Copy and there are no generic parameters. (If we
+ // - The type derives Copy and there are no generic parameters. (If we
// used the simple form with generics, we'd have to bound the generics
// with Clone + Copy, and then there'd be no Clone impl at all if the
// user fills in something that is Clone but not Copy. After
nonself_args: Vec::new(),
ret_ty: Self_,
attributes: attrs,
- unify_fieldless_variants: false,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: substructure,
}],
associated_types: Vec::new(),
all_fields = af;
vdata = &variant.data;
}
- EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)),
+ EnumTag(..) | AllFieldlessEnum(..) => {
+ cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,))
+ }
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
}
nonself_args: vec![],
ret_ty: Unit,
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
cs_total_eq_assert(a, b, c)
})),
nonself_args: vec![(self_ref(), sym::other)],
ret_ty: Path(path_std!(cmp::Ordering)),
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))),
}],
associated_types: Vec::new(),
nonself_args: vec![(self_ref(), sym::other)],
ret_ty: Path(path_local!(bool)),
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
}];
use crate::deriving::generic::ty::*;
use crate::deriving::generic::*;
use crate::deriving::{path_std, pathvec_std};
-use rustc_ast::MetaItem;
+use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
let attrs = thin_vec![cx.attr_word(sym::inline, span)];
+ // Order in which to perform matching
+ let tag_then_data = if let Annotatable::Item(item) = item
+ && let ItemKind::Enum(def, _) = &item.kind {
+ let dataful: Vec<bool> = def.variants.iter().map(|v| !v.data.fields().is_empty()).collect();
+ match dataful.iter().filter(|&&b| b).count() {
+ // No data, placing the tag check first makes codegen simpler
+ 0 => true,
+ 1..=2 => false,
+ _ => {
+ (0..dataful.len()-1).any(|i| {
+ if dataful[i] && let Some(idx) = dataful[i+1..].iter().position(|v| *v) {
+ idx >= 2
+ } else {
+ false
+ }
+ })
+ }
+ }
+ } else {
+ true
+ };
let partial_cmp_def = MethodDef {
name: sym::partial_cmp,
generics: Bounds::empty(),
nonself_args: vec![(self_ref(), sym::other)],
ret_ty,
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
- cs_partial_cmp(cx, span, substr)
+ cs_partial_cmp(cx, span, substr, tag_then_data)
})),
};
trait_def.expand(cx, mitem, item, push)
}
-pub fn cs_partial_cmp(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
+fn cs_partial_cmp(
+ cx: &mut ExtCtxt<'_>,
+ span: Span,
+ substr: &Substructure<'_>,
+ tag_then_data: bool,
+) -> BlockOrExpr {
let test_id = Ident::new(sym::cmp, span);
let equal_path = cx.path_global(span, cx.std_path(&[sym::cmp, sym::Ordering, sym::Equal]));
let partial_cmp_path = cx.std_path(&[sym::cmp, sym::PartialOrd, sym::partial_cmp]);
let args = vec![field.self_expr.clone(), other_expr.clone()];
cx.expr_call_global(field.span, partial_cmp_path.clone(), args)
}
- CsFold::Combine(span, expr1, expr2) => {
- let eq_arm =
- cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1);
- let neq_arm =
- cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
- cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
+ CsFold::Combine(span, mut expr1, expr2) => {
+ // When the item is an enum, this expands to
+ // ```
+ // match (expr2) {
+ // Some(Ordering::Equal) => expr1,
+ // cmp => cmp
+ // }
+ // ```
+ // where `expr2` is `partial_cmp(self_tag, other_tag)`, and `expr1` is a `match`
+ // against the enum variants. This means that we begin by comparing the enum tags,
+ // before either inspecting their contents (if they match), or returning
+ // the `cmp::Ordering` of comparing the enum tags.
+ // ```
+ // match partial_cmp(self_tag, other_tag) {
+ // Some(Ordering::Equal) => match (self, other) {
+ // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
+ // (Self::B(self_0), Self::B(other_0)) => partial_cmp(self_0, other_0),
+ // _ => Some(Ordering::Equal)
+ // }
+ // cmp => cmp
+ // }
+ // ```
+ // If we have any certain enum layouts, flipping this results in better codegen
+ // ```
+ // match (self, other) {
+ // (Self::A(self_0), Self::A(other_0)) => partial_cmp(self_0, other_0),
+ // _ => partial_cmp(self_tag, other_tag)
+ // }
+ // ```
+ // Reference: https://github.com/rust-lang/rust/pull/103659#issuecomment-1328126354
+
+ if !tag_then_data
+ && let ExprKind::Match(_, arms) = &mut expr1.kind
+ && let Some(last) = arms.last_mut()
+ && let PatKind::Wild = last.pat.kind {
+ last.body = expr2;
+ expr1
+ } else {
+ let eq_arm =
+ cx.arm(span, cx.pat_some(span, cx.pat_path(span, equal_path.clone())), expr1);
+ let neq_arm =
+ cx.arm(span, cx.pat_ident(span, test_id), cx.expr_ident(span, test_id));
+ cx.expr_match(span, expr2, vec![eq_arm, neq_arm])
+ }
}
CsFold::Fieldless => cx.expr_some(span, cx.expr_path(equal_path.clone())),
},
use crate::deriving::generic::*;
use crate::deriving::path_std;
+use ast::EnumDef;
use rustc_ast::{self as ast, MetaItem};
use rustc_expand::base::{Annotatable, ExtCtxt};
use rustc_span::symbol::{sym, Ident, Symbol};
nonself_args: vec![(fmtr, sym::f)],
ret_ty: Path(path_std!(fmt::Result)),
attributes: ast::AttrVec::new(),
- unify_fieldless_variants: false,
+ fieldless_variants_strategy:
+ FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
show_substructure(a, b, c)
})),
}
fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
+ // We want to make sure we have the ctxt set so that we can use unstable methods
+ let span = cx.with_def_site_ctxt(span);
+
let (ident, vdata, fields) = match substr.fields {
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
+ AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
}
};
- // We want to make sure we have the ctxt set so that we can use unstable methods
- let span = cx.with_def_site_ctxt(span);
let name = cx.expr_str(span, ident.name);
let fmt = substr.nonselflike_args[0].clone();
BlockOrExpr::new_mixed(stmts, Some(expr))
}
}
+
+/// Special case for enums with no fields. Builds:
+/// ```text
+/// impl ::core::fmt::Debug for A {
+/// fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
+/// ::core::fmt::Formatter::write_str(f,
+/// match self {
+/// A::A => "A",
+/// A::B() => "B",
+/// A::C {} => "C",
+/// })
+/// }
+/// }
+/// ```
+fn show_fieldless_enum(
+ cx: &mut ExtCtxt<'_>,
+ span: Span,
+ def: &EnumDef,
+ substr: &Substructure<'_>,
+) -> BlockOrExpr {
+ let fmt = substr.nonselflike_args[0].clone();
+ let arms = def
+ .variants
+ .iter()
+ .map(|v| {
+ let variant_path = cx.path(span, vec![substr.type_ident, v.ident]);
+ let pat = match &v.data {
+ ast::VariantData::Tuple(fields, _) => {
+ debug_assert!(fields.is_empty());
+ cx.pat_tuple_struct(span, variant_path, vec![])
+ }
+ ast::VariantData::Struct(fields, _) => {
+ debug_assert!(fields.is_empty());
+ cx.pat_struct(span, variant_path, vec![])
+ }
+ ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
+ };
+ cx.arm(span, pat, cx.expr_str(span, v.ident.name))
+ })
+ .collect::<Vec<_>>();
+ let name = cx.expr_match(span, cx.expr_self(span), arms);
+ let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
+ BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]))
+}
PathKind::Std,
)),
attributes: ast::AttrVec::new(),
- unify_fieldless_variants: false,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
decodable_substructure(a, b, c, krate)
})),
nonself_args: Vec::new(),
ret_ty: Self_,
attributes: attrs,
- unify_fieldless_variants: false,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
match substr.fields {
StaticStruct(_, fields) => {
PathKind::Std,
)),
attributes: AttrVec::new(),
- unify_fieldless_variants: false,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
encodable_substructure(a, b, c, krate)
})),
pub attributes: ast::AttrVec,
- /// Can we combine fieldless variants for enums into a single match arm?
- /// If true, indicates that the trait operation uses the enum tag in some
- /// way.
- pub unify_fieldless_variants: bool,
+ pub fieldless_variants_strategy: FieldlessVariantsStrategy,
pub combine_substructure: RefCell<CombineSubstructureFunc<'a>>,
}
+/// How to handle fieldless enum variants.
+#[derive(PartialEq)]
+pub enum FieldlessVariantsStrategy {
+ /// Combine fieldless variants into a single match arm.
+ /// This assumes that relevant information has been handled
+ /// by looking at the enum's discriminant.
+ Unify,
+ /// Don't do anything special about fieldless variants. They are
+ /// handled like any other variant.
+ Default,
+ /// If all variants of the enum are fieldless, expand the special
+ /// `AllFieldLessEnum` substructure, so that the entire enum can be handled
+ /// at once.
+ SpecializeIfAllVariantsFieldless,
+}
+
/// All the data about the data structure/method being derived upon.
pub struct Substructure<'a> {
/// ident of self
/// A summary of the possible sets of fields.
pub enum SubstructureFields<'a> {
- /// A non-static method with `Self` is a struct.
+ /// A non-static method where `Self` is a struct.
Struct(&'a ast::VariantData, Vec<FieldInfo>),
+ /// A non-static method handling the entire enum at once
+ /// (after it has been determined that none of the enum
+ /// variants has any fields).
+ AllFieldlessEnum(&'a ast::EnumDef),
+
/// Matching variants of the enum: variant index, variant count, ast::Variant,
/// fields: the field name is only non-`None` in the case of a struct
/// variant.
/// ```
/// Creates a tag check combined with a match for a tuple of all
/// `selflike_args`, with an arm for each variant with fields, possibly an
- /// arm for each fieldless variant (if `!unify_fieldless_variants` is not
- /// true), and possibly a default arm.
+ /// arm for each fieldless variant (if `unify_fieldless_variants` is not
+ /// `Unify`), and possibly a default arm.
fn expand_enum_method_body<'b>(
&self,
cx: &mut ExtCtxt<'_>,
let variants = &enum_def.variants;
// Traits that unify fieldless variants always use the tag(s).
- let uses_tags = self.unify_fieldless_variants;
+ let unify_fieldless_variants =
+ self.fieldless_variants_strategy == FieldlessVariantsStrategy::Unify;
// There is no sensible code to be generated for *any* deriving on a
// zero-variant enum. So we just generate a failing expression.
// match is necessary.
let all_fieldless = variants.iter().all(|v| v.data.fields().is_empty());
if all_fieldless {
- if uses_tags && variants.len() > 1 {
- // If the type is fieldless and the trait uses the tag and
- // there are multiple variants, we need just an operation on
- // the tag(s).
- let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
- let mut tag_check = self.call_substructure_method(
- cx,
- trait_,
- type_ident,
- nonselflike_args,
- &EnumTag(tag_field, None),
- );
- tag_let_stmts.append(&mut tag_check.0);
- return BlockOrExpr(tag_let_stmts, tag_check.1);
- }
-
- if variants.len() == 1 {
+ if variants.len() > 1 {
+ match self.fieldless_variants_strategy {
+ FieldlessVariantsStrategy::Unify => {
+ // If the type is fieldless and the trait uses the tag and
+ // there are multiple variants, we need just an operation on
+ // the tag(s).
+ let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
+ let mut tag_check = self.call_substructure_method(
+ cx,
+ trait_,
+ type_ident,
+ nonselflike_args,
+ &EnumTag(tag_field, None),
+ );
+ tag_let_stmts.append(&mut tag_check.0);
+ return BlockOrExpr(tag_let_stmts, tag_check.1);
+ }
+ FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless => {
+ return self.call_substructure_method(
+ cx,
+ trait_,
+ type_ident,
+ nonselflike_args,
+ &AllFieldlessEnum(enum_def),
+ );
+ }
+ FieldlessVariantsStrategy::Default => (),
+ }
+ } else if variants.len() == 1 {
// If there is a single variant, we don't need an operation on
// the tag(s). Just use the most degenerate result.
return self.call_substructure_method(
nonselflike_args,
&EnumMatching(0, 1, &variants[0], Vec::new()),
);
- };
+ }
}
// These arms are of the form:
let mut match_arms: Vec<ast::Arm> = variants
.iter()
.enumerate()
- .filter(|&(_, v)| !(self.unify_fieldless_variants && v.data.fields().is_empty()))
+ .filter(|&(_, v)| !(unify_fieldless_variants && v.data.fields().is_empty()))
.map(|(index, variant)| {
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
// (see "Final wrinkle" note below for why.)
// Add a default arm to the match, if necessary.
let first_fieldless = variants.iter().find(|v| v.data.fields().is_empty());
let default = match first_fieldless {
- Some(v) if self.unify_fieldless_variants => {
+ Some(v) if unify_fieldless_variants => {
// We need a default case that handles all the fieldless
// variants. The index and actual variant aren't meaningful in
// this case, so just use dummy values.
// If the trait uses the tag and there are multiple variants, we need
// to add a tag check operation before the match. Otherwise, the match
// is enough.
- if uses_tags && variants.len() > 1 {
+ if unify_fieldless_variants && variants.len() > 1 {
let (tag_field, mut tag_let_stmts) = get_tag_pieces(cx);
// Combine a tag check with the match.
}
}
StaticEnum(..) | StaticStruct(..) => cx.span_bug(trait_span, "static function in `derive`"),
+ AllFieldlessEnum(..) => cx.span_bug(trait_span, "fieldless enum in `derive`"),
}
}
nonself_args: vec![(Ref(Box::new(Path(arg)), Mutability::Mut), sym::state)],
ret_ty: Unit,
attributes: AttrVec::new(),
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
hash_substructure(a, b, c)
})),
-// The compiler code necessary to support the env! extension. Eventually this
+// The compiler code necessary to support the env! extension. Eventually this
// should all get sucked into either the compiler syntax extension plugin
// interface.
//
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::Expr;
+use rustc_ast::{
+ Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs,
+ FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount,
+ FormatDebugHex, FormatOptions, FormatPlaceholder, FormatSign, FormatTrait,
+};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, Applicability, MultiSpan, PResult};
use rustc_expand::base::{self, *};
use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiagnostics, LintId};
-mod ast;
-use ast::*;
-
-mod expand;
-use expand::expand_parsed_format_args;
-
// The format_args!() macro is expanded in three steps:
// 1. First, `parse_args` will parse the `(literal, arg, arg, name=arg, name=arg)` syntax,
// but doesn't parse the template (the literal) itself.
// 2. Second, `make_format_args` will parse the template, the format options, resolve argument references,
-// produce diagnostics, and turn the whole thing into a `FormatArgs` structure.
-// 3. Finally, `expand_parsed_format_args` will turn that `FormatArgs` structure
-// into the expression that the macro expands to.
+// produce diagnostics, and turn the whole thing into a `FormatArgs` AST node.
+// 3. Much later, in AST lowering (rustc_ast_lowering), that `FormatArgs` structure will be turned
+// into the expression of type `core::fmt::Arguments`.
-// See format/ast.rs for the FormatArgs structure and glossary.
+// See rustc_ast/src/format.rs for the FormatArgs structure and glossary.
// Only used in parse_args and report_invalid_references,
// to indicate how a referred argument was used.
format_options: FormatOptions {
fill: format.fill,
alignment,
- flags: format.flags,
+ sign: format.sign.map(|s| match s {
+ parse::Sign::Plus => FormatSign::Plus,
+ parse::Sign::Minus => FormatSign::Minus,
+ }),
+ alternate: format.alternate,
+ zero_pad: format.zero_pad,
+ debug_hex: format.debug_hex.map(|s| match s {
+ parse::DebugHex::Lower => FormatDebugHex::Lower,
+ parse::DebugHex::Upper => FormatDebugHex::Upper,
+ }),
precision,
width,
},
if detect_foreign_fmt {
use super::format_foreign as foreign;
- // The set of foreign substitutions we've explained. This prevents spamming the user
+ // The set of foreign substitutions we've explained. This prevents spamming the user
// with `%d should be written as {}` over and over again.
let mut explained = FxHashSet::default();
match parse_args(ecx, sp, tts) {
Ok((efmt, args)) => {
if let Ok(format_args) = make_format_args(ecx, efmt, args, nl) {
- MacEager::expr(expand_parsed_format_args(ecx, format_args))
+ MacEager::expr(ecx.expr(sp, ExprKind::FormatArgs(P(format_args))))
} else {
MacEager::expr(DummyResult::raw_expr(sp, true))
}
+++ /dev/null
-use rustc_ast::ptr::P;
-use rustc_ast::Expr;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::Span;
-
-// Definitions:
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └──────────────────────────────────────────────┘
-// FormatArgs
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └─────────┘
-// argument
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └───────────────────┘
-// template
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └────┘└─────────┘└┘
-// pieces
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └────┘ └┘
-// literal pieces
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └─────────┘
-// placeholder
-//
-// format_args!("hello {abc:.xyz$}!!", abc="world");
-// └─┘ └─┘
-// positions (could be names, numbers, empty, or `*`)
-
-/// (Parsed) format args.
-///
-/// Basically the "AST" for a complete `format_args!()`.
-///
-/// E.g., `format_args!("hello {name}");`.
-#[derive(Clone, Debug)]
-pub struct FormatArgs {
- pub span: Span,
- pub template: Vec<FormatArgsPiece>,
- pub arguments: FormatArguments,
-}
-
-/// A piece of a format template string.
-///
-/// E.g. "hello" or "{name}".
-#[derive(Clone, Debug)]
-pub enum FormatArgsPiece {
- Literal(Symbol),
- Placeholder(FormatPlaceholder),
-}
-
-/// The arguments to format_args!().
-///
-/// E.g. `1, 2, name="ferris", n=3`,
-/// but also implicit captured arguments like `x` in `format_args!("{x}")`.
-#[derive(Clone, Debug)]
-pub struct FormatArguments {
- arguments: Vec<FormatArgument>,
- num_unnamed_args: usize,
- num_explicit_args: usize,
- names: FxHashMap<Symbol, usize>,
-}
-
-impl FormatArguments {
- pub fn new() -> Self {
- Self {
- arguments: Vec::new(),
- names: FxHashMap::default(),
- num_unnamed_args: 0,
- num_explicit_args: 0,
- }
- }
-
- pub fn add(&mut self, arg: FormatArgument) -> usize {
- let index = self.arguments.len();
- if let Some(name) = arg.kind.ident() {
- self.names.insert(name.name, index);
- } else if self.names.is_empty() {
- // Only count the unnamed args before the first named arg.
- // (Any later ones are errors.)
- self.num_unnamed_args += 1;
- }
- if !matches!(arg.kind, FormatArgumentKind::Captured(..)) {
- // This is an explicit argument.
- // Make sure that all arguments so far are explcit.
- assert_eq!(
- self.num_explicit_args,
- self.arguments.len(),
- "captured arguments must be added last"
- );
- self.num_explicit_args += 1;
- }
- self.arguments.push(arg);
- index
- }
-
- pub fn by_name(&self, name: Symbol) -> Option<(usize, &FormatArgument)> {
- let i = *self.names.get(&name)?;
- Some((i, &self.arguments[i]))
- }
-
- pub fn by_index(&self, i: usize) -> Option<&FormatArgument> {
- (i < self.num_explicit_args).then(|| &self.arguments[i])
- }
-
- pub fn unnamed_args(&self) -> &[FormatArgument] {
- &self.arguments[..self.num_unnamed_args]
- }
-
- pub fn named_args(&self) -> &[FormatArgument] {
- &self.arguments[self.num_unnamed_args..self.num_explicit_args]
- }
-
- pub fn explicit_args(&self) -> &[FormatArgument] {
- &self.arguments[..self.num_explicit_args]
- }
-
- pub fn into_vec(self) -> Vec<FormatArgument> {
- self.arguments
- }
-}
-
-#[derive(Clone, Debug)]
-pub struct FormatArgument {
- pub kind: FormatArgumentKind,
- pub expr: P<Expr>,
-}
-
-#[derive(Clone, Debug)]
-pub enum FormatArgumentKind {
- /// `format_args(…, arg)`
- Normal,
- /// `format_args(…, arg = 1)`
- Named(Ident),
- /// `format_args("… {arg} …")`
- Captured(Ident),
-}
-
-impl FormatArgumentKind {
- pub fn ident(&self) -> Option<Ident> {
- match self {
- &Self::Normal => None,
- &Self::Named(id) => Some(id),
- &Self::Captured(id) => Some(id),
- }
- }
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct FormatPlaceholder {
- /// Index into [`FormatArgs::arguments`].
- pub argument: FormatArgPosition,
- /// The span inside the format string for the full `{…}` placeholder.
- pub span: Option<Span>,
- /// `{}`, `{:?}`, or `{:x}`, etc.
- pub format_trait: FormatTrait,
- /// `{}` or `{:.5}` or `{:-^20}`, etc.
- pub format_options: FormatOptions,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct FormatArgPosition {
- /// Which argument this position refers to (Ok),
- /// or would've referred to if it existed (Err).
- pub index: Result<usize, usize>,
- /// What kind of position this is. See [`FormatArgPositionKind`].
- pub kind: FormatArgPositionKind,
- /// The span of the name or number.
- pub span: Option<Span>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum FormatArgPositionKind {
- /// `{}` or `{:.*}`
- Implicit,
- /// `{1}` or `{:1$}` or `{:.1$}`
- Number,
- /// `{a}` or `{:a$}` or `{:.a$}`
- Named,
-}
-
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-pub enum FormatTrait {
- /// `{}`
- Display,
- /// `{:?}`
- Debug,
- /// `{:e}`
- LowerExp,
- /// `{:E}`
- UpperExp,
- /// `{:o}`
- Octal,
- /// `{:p}`
- Pointer,
- /// `{:b}`
- Binary,
- /// `{:x}`
- LowerHex,
- /// `{:X}`
- UpperHex,
-}
-
-#[derive(Clone, Debug, Default, PartialEq, Eq)]
-pub struct FormatOptions {
- /// The width. E.g. `{:5}` or `{:width$}`.
- pub width: Option<FormatCount>,
- /// The precision. E.g. `{:.5}` or `{:.precision$}`.
- pub precision: Option<FormatCount>,
- /// The alignment. E.g. `{:>}` or `{:<}` or `{:^}`.
- pub alignment: Option<FormatAlignment>,
- /// The fill character. E.g. the `.` in `{:.>10}`.
- pub fill: Option<char>,
- /// The `+`, `-`, `0`, `#`, `x?` and `X?` flags.
- pub flags: u32,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum FormatAlignment {
- /// `{:<}`
- Left,
- /// `{:>}`
- Right,
- /// `{:^}`
- Center,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum FormatCount {
- /// `{:5}` or `{:.5}`
- Literal(usize),
- /// `{:.*}`, `{:.5$}`, or `{:a$}`, etc.
- Argument(FormatArgPosition),
-}
+++ /dev/null
-use super::*;
-use rustc_ast as ast;
-use rustc_ast::visit::{self, Visitor};
-use rustc_ast::{BlockCheckMode, UnsafeSource};
-use rustc_data_structures::fx::FxIndexSet;
-use rustc_span::{sym, symbol::kw};
-
-#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
-enum ArgumentType {
- Format(FormatTrait),
- Usize,
-}
-
-fn make_argument(ecx: &ExtCtxt<'_>, sp: Span, arg: P<ast::Expr>, ty: ArgumentType) -> P<ast::Expr> {
- // Generate:
- // ::core::fmt::ArgumentV1::new_…(arg)
- use ArgumentType::*;
- use FormatTrait::*;
- ecx.expr_call_global(
- sp,
- ecx.std_path(&[
- sym::fmt,
- sym::ArgumentV1,
- match ty {
- Format(Display) => sym::new_display,
- Format(Debug) => sym::new_debug,
- Format(LowerExp) => sym::new_lower_exp,
- Format(UpperExp) => sym::new_upper_exp,
- Format(Octal) => sym::new_octal,
- Format(Pointer) => sym::new_pointer,
- Format(Binary) => sym::new_binary,
- Format(LowerHex) => sym::new_lower_hex,
- Format(UpperHex) => sym::new_upper_hex,
- Usize => sym::from_usize,
- },
- ]),
- vec![arg],
- )
-}
-
-fn make_count(
- ecx: &ExtCtxt<'_>,
- sp: Span,
- count: &Option<FormatCount>,
- argmap: &mut FxIndexSet<(usize, ArgumentType)>,
-) -> P<ast::Expr> {
- // Generate:
- // ::core::fmt::rt::v1::Count::…(…)
- match count {
- Some(FormatCount::Literal(n)) => ecx.expr_call_global(
- sp,
- ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Is]),
- vec![ecx.expr_usize(sp, *n)],
- ),
- Some(FormatCount::Argument(arg)) => {
- if let Ok(arg_index) = arg.index {
- let (i, _) = argmap.insert_full((arg_index, ArgumentType::Usize));
- ecx.expr_call_global(
- sp,
- ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Param]),
- vec![ecx.expr_usize(sp, i)],
- )
- } else {
- DummyResult::raw_expr(sp, true)
- }
- }
- None => ecx.expr_path(ecx.path_global(
- sp,
- ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Count, sym::Implied]),
- )),
- }
-}
-
-fn make_format_spec(
- ecx: &ExtCtxt<'_>,
- sp: Span,
- placeholder: &FormatPlaceholder,
- argmap: &mut FxIndexSet<(usize, ArgumentType)>,
-) -> P<ast::Expr> {
- // Generate:
- // ::core::fmt::rt::v1::Argument {
- // position: 0usize,
- // format: ::core::fmt::rt::v1::FormatSpec {
- // fill: ' ',
- // align: ::core::fmt::rt::v1::Alignment::Unknown,
- // flags: 0u32,
- // precision: ::core::fmt::rt::v1::Count::Implied,
- // width: ::core::fmt::rt::v1::Count::Implied,
- // },
- // }
- let position = match placeholder.argument.index {
- Ok(arg_index) => {
- let (i, _) =
- argmap.insert_full((arg_index, ArgumentType::Format(placeholder.format_trait)));
- ecx.expr_usize(sp, i)
- }
- Err(_) => DummyResult::raw_expr(sp, true),
- };
- let fill = ecx.expr_char(sp, placeholder.format_options.fill.unwrap_or(' '));
- let align = ecx.expr_path(ecx.path_global(
- sp,
- ecx.std_path(&[
- sym::fmt,
- sym::rt,
- sym::v1,
- sym::Alignment,
- match placeholder.format_options.alignment {
- Some(FormatAlignment::Left) => sym::Left,
- Some(FormatAlignment::Right) => sym::Right,
- Some(FormatAlignment::Center) => sym::Center,
- None => sym::Unknown,
- },
- ]),
- ));
- let flags = ecx.expr_u32(sp, placeholder.format_options.flags);
- let prec = make_count(ecx, sp, &placeholder.format_options.precision, argmap);
- let width = make_count(ecx, sp, &placeholder.format_options.width, argmap);
- ecx.expr_struct(
- sp,
- ecx.path_global(sp, ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::Argument])),
- vec![
- ecx.field_imm(sp, Ident::new(sym::position, sp), position),
- ecx.field_imm(
- sp,
- Ident::new(sym::format, sp),
- ecx.expr_struct(
- sp,
- ecx.path_global(
- sp,
- ecx.std_path(&[sym::fmt, sym::rt, sym::v1, sym::FormatSpec]),
- ),
- vec![
- ecx.field_imm(sp, Ident::new(sym::fill, sp), fill),
- ecx.field_imm(sp, Ident::new(sym::align, sp), align),
- ecx.field_imm(sp, Ident::new(sym::flags, sp), flags),
- ecx.field_imm(sp, Ident::new(sym::precision, sp), prec),
- ecx.field_imm(sp, Ident::new(sym::width, sp), width),
- ],
- ),
- ),
- ],
- )
-}
-
-pub fn expand_parsed_format_args(ecx: &mut ExtCtxt<'_>, fmt: FormatArgs) -> P<ast::Expr> {
- let macsp = ecx.with_def_site_ctxt(ecx.call_site());
-
- let lit_pieces = ecx.expr_array_ref(
- fmt.span,
- fmt.template
- .iter()
- .enumerate()
- .filter_map(|(i, piece)| match piece {
- &FormatArgsPiece::Literal(s) => Some(ecx.expr_str(fmt.span, s)),
- &FormatArgsPiece::Placeholder(_) => {
- // Inject empty string before placeholders when not already preceded by a literal piece.
- if i == 0 || matches!(fmt.template[i - 1], FormatArgsPiece::Placeholder(_)) {
- Some(ecx.expr_str(fmt.span, kw::Empty))
- } else {
- None
- }
- }
- })
- .collect(),
- );
-
- // Whether we'll use the `Arguments::new_v1_formatted` form (true),
- // or the `Arguments::new_v1` form (false).
- let mut use_format_options = false;
-
- // Create a list of all _unique_ (argument, format trait) combinations.
- // E.g. "{0} {0:x} {0} {1}" -> [(0, Display), (0, LowerHex), (1, Display)]
- let mut argmap = FxIndexSet::default();
- for piece in &fmt.template {
- let FormatArgsPiece::Placeholder(placeholder) = piece else { continue };
- if placeholder.format_options != Default::default() {
- // Can't use basic form if there's any formatting options.
- use_format_options = true;
- }
- if let Ok(index) = placeholder.argument.index {
- if !argmap.insert((index, ArgumentType::Format(placeholder.format_trait))) {
- // Duplicate (argument, format trait) combination,
- // which we'll only put once in the args array.
- use_format_options = true;
- }
- }
- }
-
- let format_options = use_format_options.then(|| {
- // Generate:
- // &[format_spec_0, format_spec_1, format_spec_2]
- ecx.expr_array_ref(
- macsp,
- fmt.template
- .iter()
- .filter_map(|piece| {
- let FormatArgsPiece::Placeholder(placeholder) = piece else { return None };
- Some(make_format_spec(ecx, macsp, placeholder, &mut argmap))
- })
- .collect(),
- )
- });
-
- let arguments = fmt.arguments.into_vec();
-
- // If the args array contains exactly all the original arguments once,
- // in order, we can use a simple array instead of a `match` construction.
- // However, if there's a yield point in any argument except the first one,
- // we don't do this, because an ArgumentV1 cannot be kept across yield points.
- let use_simple_array = argmap.len() == arguments.len()
- && argmap.iter().enumerate().all(|(i, &(j, _))| i == j)
- && arguments.iter().skip(1).all(|arg| !may_contain_yield_point(&arg.expr));
-
- let args = if use_simple_array {
- // Generate:
- // &[
- // ::core::fmt::ArgumentV1::new_display(&arg0),
- // ::core::fmt::ArgumentV1::new_lower_hex(&arg1),
- // ::core::fmt::ArgumentV1::new_debug(&arg2),
- // ]
- ecx.expr_array_ref(
- macsp,
- arguments
- .into_iter()
- .zip(argmap)
- .map(|(arg, (_, ty))| {
- let sp = arg.expr.span.with_ctxt(macsp.ctxt());
- make_argument(ecx, sp, ecx.expr_addr_of(sp, arg.expr), ty)
- })
- .collect(),
- )
- } else {
- // Generate:
- // match (&arg0, &arg1, &arg2) {
- // args => &[
- // ::core::fmt::ArgumentV1::new_display(args.0),
- // ::core::fmt::ArgumentV1::new_lower_hex(args.1),
- // ::core::fmt::ArgumentV1::new_debug(args.0),
- // ]
- // }
- let args_ident = Ident::new(sym::args, macsp);
- let args = argmap
- .iter()
- .map(|&(arg_index, ty)| {
- if let Some(arg) = arguments.get(arg_index) {
- let sp = arg.expr.span.with_ctxt(macsp.ctxt());
- make_argument(
- ecx,
- sp,
- ecx.expr_field(
- sp,
- ecx.expr_ident(macsp, args_ident),
- Ident::new(sym::integer(arg_index), macsp),
- ),
- ty,
- )
- } else {
- DummyResult::raw_expr(macsp, true)
- }
- })
- .collect();
- ecx.expr_addr_of(
- macsp,
- ecx.expr_match(
- macsp,
- ecx.expr_tuple(
- macsp,
- arguments
- .into_iter()
- .map(|arg| {
- ecx.expr_addr_of(arg.expr.span.with_ctxt(macsp.ctxt()), arg.expr)
- })
- .collect(),
- ),
- vec![ecx.arm(macsp, ecx.pat_ident(macsp, args_ident), ecx.expr_array(macsp, args))],
- ),
- )
- };
-
- if let Some(format_options) = format_options {
- // Generate:
- // ::core::fmt::Arguments::new_v1_formatted(
- // lit_pieces,
- // args,
- // format_options,
- // unsafe { ::core::fmt::UnsafeArg::new() }
- // )
- ecx.expr_call_global(
- macsp,
- ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1_formatted]),
- vec![
- lit_pieces,
- args,
- format_options,
- ecx.expr_block(P(ast::Block {
- stmts: vec![ecx.stmt_expr(ecx.expr_call_global(
- macsp,
- ecx.std_path(&[sym::fmt, sym::UnsafeArg, sym::new]),
- Vec::new(),
- ))],
- id: ast::DUMMY_NODE_ID,
- rules: BlockCheckMode::Unsafe(UnsafeSource::CompilerGenerated),
- span: macsp,
- tokens: None,
- could_be_bare_literal: false,
- })),
- ],
- )
- } else {
- // Generate:
- // ::core::fmt::Arguments::new_v1(
- // lit_pieces,
- // args,
- // )
- ecx.expr_call_global(
- macsp,
- ecx.std_path(&[sym::fmt, sym::Arguments, sym::new_v1]),
- vec![lit_pieces, args],
- )
- }
-}
-
-fn may_contain_yield_point(e: &ast::Expr) -> bool {
- struct MayContainYieldPoint(bool);
-
- impl Visitor<'_> for MayContainYieldPoint {
- fn visit_expr(&mut self, e: &ast::Expr) {
- if let ast::ExprKind::Await(_) | ast::ExprKind::Yield(_) = e.kind {
- self.0 = true;
- } else {
- visit::walk_expr(self, e);
- }
- }
-
- fn visit_mac_call(&mut self, _: &ast::MacCall) {
- self.0 = true;
- }
-
- fn visit_attribute(&mut self, _: &ast::Attribute) {
- // Conservatively assume this may be a proc macro attribute in
- // expression position.
- self.0 = true;
- }
-
- fn visit_item(&mut self, _: &ast::Item) {
- // Do not recurse into nested items.
- }
- }
-
- let mut visitor = MayContainYieldPoint(false);
- visitor.visit_expr(e);
- visitor.0
-}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Num {
// The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
- // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
+ // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
// is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
// with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
// on a screen.
task:
name: freebsd
freebsd_instance:
- image: freebsd-12-1-release-amd64
+ image: freebsd-13-1-release-amd64
setup_rust_script:
- pkg install -y curl git bash
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh --default-toolchain none -y --profile=minimal
- cargo_bin_cache:
- folder: ~/.cargo/bin
target_cache:
folder: target
prepare_script:
- ./y.rs prepare
test_script:
- . $HOME/.cargo/env
- - # Enable backtraces for easier debugging
- - export RUST_BACKTRACE=1
- - # Reduce amount of benchmark runs as they are slow
- - export COMPILE_RUNS=2
- - export RUN_RUNS=2
- ./y.rs test
runs-on: ${{ matrix.os }}
timeout-minutes: 60
+ defaults:
+ run:
+ shell: bash
+
strategy:
fail-fast: false
matrix:
- os: ubuntu-latest
env:
TARGET_TRIPLE: s390x-unknown-linux-gnu
+ - os: windows-latest
+ env:
+ TARGET_TRIPLE: x86_64-pc-windows-msvc
+ - os: windows-latest
+ env:
+ TARGET_TRIPLE: x86_64-pc-windows-gnu
steps:
- uses: actions/checkout@v3
- - name: Cache cargo installed crates
- uses: actions/cache@v3
- with:
- path: ~/.cargo/bin
- key: ${{ runner.os }}-cargo-installed-crates
-
- - name: Cache cargo registry and index
- uses: actions/cache@v3
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }}
-
- name: Cache cargo target dir
uses: actions/cache@v3
with:
path: build/cg_clif
- key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
+ key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
+
+ - name: Set MinGW as the default toolchain
+ if: matrix.os == 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
+ run: rustup set default-host x86_64-pc-windows-gnu
- name: Install MinGW toolchain and wine
if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
run: |
sudo apt-get update
sudo apt-get install -y gcc-mingw-w64-x86-64 wine-stable
- rustup target add x86_64-pc-windows-gnu
- name: Install AArch64 toolchain and qemu
if: matrix.os == 'ubuntu-latest' && matrix.env.TARGET_TRIPLE == 'aarch64-unknown-linux-gnu'
sudo apt-get update
sudo apt-get install -y gcc-s390x-linux-gnu qemu-user
+ - name: Use sparse cargo registry
+ run: |
+ cat >> ~/.cargo/config.toml <<EOF
+ [unstable]
+ sparse-registry = true
+ EOF
+
- name: Prepare dependencies
run: ./y.rs prepare
- name: Test
env:
TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
- run: |
- # Enable backtraces for easier debugging
- export RUST_BACKTRACE=1
-
- # Reduce amount of benchmark runs as they are slow
- export COMPILE_RUNS=2
- export RUN_RUNS=2
-
- # Enable extra checks
- export CG_CLIF_ENABLE_VERIFIER=1
-
- ./y.rs test
+ run: ./y.rs test
- name: Package prebuilt cg_clif
run: tar cvfJ cg_clif.tar.xz dist
- name: Upload prebuilt cg_clif
- if: matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu'
- uses: actions/upload-artifact@v2
+ if: matrix.os == 'windows-latest' || matrix.env.TARGET_TRIPLE != 'x86_64-pc-windows-gnu'
+ uses: actions/upload-artifact@v3
with:
name: cg_clif-${{ matrix.env.TARGET_TRIPLE }}
path: cg_clif.tar.xz
- name: Upload prebuilt cg_clif (cross compile)
- if: matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
+ if: matrix.os != 'windows-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
uses: actions/upload-artifact@v3
with:
name: cg_clif-${{ runner.os }}-cross-x86_64-mingw
path: cg_clif.tar.xz
- windows:
+
+ abi_cafe:
runs-on: ${{ matrix.os }}
timeout-minutes: 60
+ defaults:
+ run:
+ shell: bash
+
strategy:
- fail-fast: false
+ fail-fast: true
matrix:
include:
- # Native Windows build with MSVC
+ - os: ubuntu-latest
+ env:
+ TARGET_TRIPLE: x86_64-unknown-linux-gnu
+ - os: macos-latest
+ env:
+ TARGET_TRIPLE: x86_64-apple-darwin
- os: windows-latest
env:
TARGET_TRIPLE: x86_64-pc-windows-msvc
- # cross-compile from Windows to Windows MinGW
- os: windows-latest
env:
TARGET_TRIPLE: x86_64-pc-windows-gnu
steps:
- uses: actions/checkout@v3
- - name: Cache cargo installed crates
- uses: actions/cache@v3
- with:
- path: ~/.cargo/bin
- key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-installed-crates
-
- - name: Cache cargo registry and index
- uses: actions/cache@v3
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- key: ${{ runner.os }}-${{ matrix.env.TARGET_TRIPLE }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }}
-
- name: Cache cargo target dir
uses: actions/cache@v3
with:
if: matrix.env.TARGET_TRIPLE == 'x86_64-pc-windows-gnu'
run: rustup set default-host x86_64-pc-windows-gnu
- - name: Prepare dependencies
+ - name: Use sparse cargo registry
run: |
- git config --global core.autocrlf false
- rustc y.rs -o y.exe -g
- ./y.exe prepare
+ cat >> ~/.cargo/config.toml <<EOF
+ [unstable]
+ sparse-registry = true
+ EOF
- - name: Build without unstable features
- env:
- TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
- # This is the config rust-lang/rust uses for builds
- run: ./y.rs build --no-unstable-features
+ - name: Prepare dependencies
+ run: ./y.rs prepare
- name: Build
run: ./y.rs build --sysroot none
- - name: Test
- run: |
- # Enable backtraces for easier debugging
- $Env:RUST_BACKTRACE=1
-
- # Reduce amount of benchmark runs as they are slow
- $Env:COMPILE_RUNS=2
- $Env:RUN_RUNS=2
-
- # Enable extra checks
- $Env:CG_CLIF_ENABLE_VERIFIER=1
-
- # WIP Disable some tests
-
- # This fails due to some weird argument handling by hyperfine, not an actual regression
- # more of a build system issue
- (Get-Content config.txt) -replace '(bench.simple-raytracer)', '# $1' | Out-File config.txt
-
- # This fails with a different output than expected
- (Get-Content config.txt) -replace '(test.regex-shootout-regex-dna)', '# $1' | Out-File config.txt
-
- ./y.exe test
-
- - name: Package prebuilt cg_clif
- # don't use compression as xzip isn't supported by tar on windows and bzip2 hangs
- run: tar cvf cg_clif.tar dist
-
- - name: Upload prebuilt cg_clif
- uses: actions/upload-artifact@v3
- with:
- name: cg_clif-${{ matrix.env.TARGET_TRIPLE }}
- path: cg_clif.tar
+ - name: Test abi-cafe
+ env:
+ TARGET_TRIPLE: ${{ matrix.env.TARGET_TRIPLE }}
+ run: ./y.rs abi-cafe
+++ /dev/null
-name: Test nightly Cranelift
-
-on:
- push:
- schedule:
- - cron: '17 1 * * *' # At 01:17 UTC every day.
-
-jobs:
- build:
- runs-on: ubuntu-latest
- timeout-minutes: 60
-
- steps:
- - uses: actions/checkout@v3
-
- - name: Cache cargo installed crates
- uses: actions/cache@v3
- with:
- path: ~/.cargo/bin
- key: ubuntu-latest-cargo-installed-crates
-
- - name: Prepare dependencies
- run: |
- git config --global user.email "user@example.com"
- git config --global user.name "User"
- ./y.rs prepare
-
- - name: Patch Cranelift
- run: |
- sed -i 's/cranelift-codegen = { version = "\w*.\w*.\w*", features = \["unwind", "all-arch"\] }/cranelift-codegen = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git", features = ["unwind", "all-arch"] }/' Cargo.toml
- sed -i 's/cranelift-frontend = "\w*.\w*.\w*"/cranelift-frontend = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git" }/' Cargo.toml
- sed -i 's/cranelift-module = "\w*.\w*.\w*"/cranelift-module = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git" }/' Cargo.toml
- sed -i 's/cranelift-native = "\w*.\w*.\w*"/cranelift-native = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git" }/' Cargo.toml
- sed -i 's/cranelift-jit = { version = "\w*.\w*.\w*", optional = true }/cranelift-jit = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git", optional = true }/' Cargo.toml
- sed -i 's/cranelift-object = "\w*.\w*.\w*"/cranelift-object = { git = "https:\/\/github.com\/bytecodealliance\/wasmtime.git" }/' Cargo.toml
-
- sed -i 's/object = { version = "0.27.0"/object = { version = "0.28.0"/' Cargo.toml
-
- cat Cargo.toml
-
- - name: Build without unstable features
- # This is the config rust-lang/rust uses for builds
- run: ./y.rs build --no-unstable-features
-
- - name: Build
- run: ./y.rs build --sysroot none
- - name: Test
- run: |
- # Enable backtraces for easier debugging
- export RUST_BACKTRACE=1
-
- # Reduce amount of benchmark runs as they are slow
- export COMPILE_RUNS=2
- export RUN_RUNS=2
-
- # Enable extra checks
- export CG_CLIF_ENABLE_VERIFIER=1
-
- ./test.sh
steps:
- uses: actions/checkout@v3
- - name: Cache cargo installed crates
- uses: actions/cache@v3
- with:
- path: ~/.cargo/bin
- key: ${{ runner.os }}-cargo-installed-crates
-
- - name: Cache cargo registry and index
- uses: actions/cache@v3
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }}
-
- name: Cache cargo target dir
uses: actions/cache@v3
with:
path: build/cg_clif
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
- - name: Prepare dependencies
+ - name: Use sparse cargo registry
run: |
- git config --global user.email "user@example.com"
- git config --global user.name "User"
- ./y.rs prepare
+ cat >> ~/.cargo/config.toml <<EOF
+ [unstable]
+ sparse-registry = true
+ EOF
- - name: Test
- run: |
- # Enable backtraces for easier debugging
- export RUST_BACKTRACE=1
+ - name: Prepare dependencies
+ run: ./y.rs prepare
- ./scripts/test_bootstrap.sh
+ - name: Test
+ run: ./scripts/test_bootstrap.sh
rustc_test_suite:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- - name: Cache cargo installed crates
- uses: actions/cache@v3
- with:
- path: ~/.cargo/bin
- key: ${{ runner.os }}-cargo-installed-crates
-
- - name: Cache cargo registry and index
- uses: actions/cache@v3
- with:
- path: |
- ~/.cargo/registry
- ~/.cargo/git
- key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }}
-
- name: Cache cargo target dir
uses: actions/cache@v3
with:
path: build/cg_clif
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
- - name: Prepare dependencies
+ - name: Use sparse cargo registry
run: |
- git config --global user.email "user@example.com"
- git config --global user.name "User"
- ./y.rs prepare
+ cat >> ~/.cargo/config.toml <<EOF
+ [unstable]
+ sparse-registry = true
+ EOF
- - name: Test
- run: |
- # Enable backtraces for easier debugging
- export RUST_BACKTRACE=1
+ - name: Prepare dependencies
+ run: ./y.rs prepare
- ./scripts/test_rustc_tests.sh
+ - name: Test
+ run: ./scripts/test_rustc_tests.sh
-target
+/target
**/*.rs.bk
*.rlib
*.o
/y.exe
/y.pdb
/build
-/build_sysroot/sysroot_src
-/build_sysroot/compiler-builtins
-/build_sysroot/rustc_version
/dist
/rust
/download
{
+ "editor.formatOnSave": true,
+
// source for rustc_* is not included in the rust-src component; disable the errors about this
"rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate", "unresolved-macro-call"],
"rust-analyzer.imports.granularity.enforce": true,
]
},
{
- "sysroot_src": "./build_sysroot/sysroot_src/library",
+ "sysroot_src": "./download/sysroot/sysroot_src/library",
"crates": [
{
"root_module": "./example/std_example.rs",
[[package]]
name = "cranelift-bforest"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b62c772976416112fa4484cbd688cb6fb35fd430005c1c586224fc014018abad"
+checksum = "2f3d54eab028f5805ae3b26fd60eca3f3a9cfb76b989d9bab173be3f61356cc3"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b40ed2dd13c2ac7e24f88a3090c68ad3414eb1d066a95f8f1f7b3b819cb4e46"
+checksum = "2be1d5f2c3cca1efb691844bc1988b89c77291f13f778499a3f3c0cf49c0ed61"
dependencies = [
"arrayvec",
"bumpalo",
"cranelift-bforest",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
- "cranelift-egraph",
"cranelift-entity",
"cranelift-isle",
"gimli",
+ "hashbrown",
"log",
"regalloc2",
"smallvec",
[[package]]
name = "cranelift-codegen-meta"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb927a8f1c27c34ee3759b6b0ffa528d2330405d5cc4511f0cab33fe2279f4b5"
+checksum = "3f9b1b1089750ce4005893af7ee00bb08a2cf1c9779999c0f7164cbc8ad2e0d2"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43dfa417b884a9ab488d95fd6b93b25e959321fe7bfd7a0a960ba5d7fb7ab927"
-
-[[package]]
-name = "cranelift-egraph"
-version = "0.90.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a66b39785efd8513d2cca967ede56d6cc57c8d7986a595c7c47d0c78de8dce"
-dependencies = [
- "cranelift-entity",
- "fxhash",
- "hashbrown",
- "indexmap",
- "log",
- "smallvec",
-]
+checksum = "cc5fbaec51de47297fd7304986fd53c8c0030abbe69728a60d72e1c63559318d"
[[package]]
name = "cranelift-entity"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0637ffde963cb5d759bc4d454cfa364b6509e6c74cdaa21298add0ed9276f346"
+checksum = "dab984c94593f876090fae92e984bdcc74d9b1acf740ab5f79036001c65cba13"
[[package]]
name = "cranelift-frontend"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb72b8342685e850cb037350418f62cc4fc55d6c2eb9c7ca01b82f9f1a6f3d56"
+checksum = "6e0cb3102d21a2fe5f3210af608748ddd0cd09825ac12d42dc56ed5ed8725fe0"
dependencies = [
"cranelift-codegen",
"log",
[[package]]
name = "cranelift-isle"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "850579cb9e4b448f7c301f1e6e6cbad99abe3f1f1d878a4994cb66e33c6db8cd"
+checksum = "72101dd1f441d629735143c41e00b3428f9267738176983ef588ff43382af0a0"
[[package]]
name = "cranelift-jit"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9add822ad66dcbe152b5ab57de10240a2df4505099f2f6c27159acb711890bd4"
+checksum = "6557f8ce44d498777f2495aa58d9692a4a37d6f84aa445750d666cef770b6a5c"
dependencies = [
"anyhow",
"cranelift-codegen",
[[package]]
name = "cranelift-module"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "406b772626fc2664864cf947f3895a23b619895c7fff635f3622e2d857f4492f"
+checksum = "88807e1c0c47ec02fe433333ccbe56b480425418b1470e333205e11650697d72"
dependencies = [
"anyhow",
"cranelift-codegen",
[[package]]
name = "cranelift-native"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d0a279e5bcba3e0466c734d8d8eb6bfc1ad29e95c37f3e4955b492b5616335e"
+checksum = "c22b0d9fcbe3fc5a1af9e7021b44ce42b930bcefac446ce22e02e8f9a0d67120"
dependencies = [
"cranelift-codegen",
"libc",
[[package]]
name = "cranelift-object"
-version = "0.90.1"
+version = "0.92.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39793c550f0c1d7db96c2fc1324583670c8143befe6edbfbaf1c68aba53be983"
+checksum = "341375758d7c3fedc0b5315f552e6f0feac46baf87c450a15e9455ef47c2b261"
dependencies = [
"anyhow",
"cranelift-codegen",
[[package]]
name = "regalloc2"
-version = "0.4.2"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91b2eab54204ea0117fe9a060537e0b07a4e72f7c7d182361ecc346cab2240e5"
+checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c"
dependencies = [
"fxhash",
"log",
"cranelift-frontend",
"cranelift-jit",
"cranelift-module",
- "cranelift-native",
"cranelift-object",
"gimli",
"indexmap",
[[package]]
name = "wasmtime-jit-icache-coherence"
-version = "2.0.1"
+version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6bbabb309c06cc238ee91b1455b748c45f0bdcab0dda2c2db85b0a1e69fcb66"
+checksum = "08fcba5ebd96da2a9f0747ab6337fe9788adfb3f63fa2c180520d665562d257e"
dependencies = [
"cfg-if",
"libc",
[[package]]
name = "windows-sys"
-version = "0.36.1"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
+checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
+ "windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
+
[[package]]
name = "windows_aarch64_msvc"
-version = "0.36.1"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
+checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
-version = "0.36.1"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
+checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
-version = "0.36.1"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
+checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.36.1"
+version = "0.42.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
+checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.36.1"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[dependencies]
# These have to be in sync with each other
-cranelift-codegen = { version = "0.90.1", features = ["unwind", "all-arch"] }
-cranelift-frontend = "0.90.1"
-cranelift-module = "0.90.1"
-cranelift-native = "0.90.1"
-cranelift-jit = { version = "0.90.1", optional = true }
-cranelift-object = "0.90.1"
+cranelift-codegen = { version = "0.92", features = ["unwind", "all-arch"] }
+cranelift-frontend = { version = "0.92" }
+cranelift-module = { version = "0.92" }
+# NOTE vendored as src/cranelift_native.rs
+# FIXME revert back to the external crate with Cranelift 0.93
+#cranelift-native = { version = "0.92" }
+cranelift-jit = { version = "0.92", optional = true }
+cranelift-object = { version = "0.92" }
target-lexicon = "0.12.0"
gimli = { version = "0.26.0", default-features = false, features = ["write"]}
object = { version = "0.29.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
## Building and testing
```bash
-$ git clone https://github.com/bjorn3/rustc_codegen_cranelift.git
+$ git clone https://github.com/bjorn3/rustc_codegen_cranelift
$ cd rustc_codegen_cranelift
-$ ./y.rs prepare # download and patch sysroot src and install hyperfine for benchmarking
+$ ./y.rs prepare
$ ./y.rs build
```
$ ./test.sh
```
-This will implicitly build cg_clif too. Both `y.rs build` and `test.sh` accept a `--debug` argument to
-build in debug mode.
+For more docs on how to build and test see [build_system/usage.txt](build_system/usage.txt) or the help message of `./y.rs`.
-Alternatively you can download a pre built version from [GHA]. It is listed in the artifacts section
+Alternatively you can download a pre built version from [Github Actions]. It is listed in the artifacts section
of workflow runs. Unfortunately due to GHA restrictions you need to be logged in to access it.
-[GHA]: https://github.com/bjorn3/rustc_codegen_cranelift/actions?query=branch%3Amaster+event%3Apush+is%3Asuccess
+[Github Actions]: https://github.com/bjorn3/rustc_codegen_cranelift/actions?query=branch%3Amaster+event%3Apush+is%3Asuccess
## Usage
* Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041))
* On UNIX there is support for invoking an external assembler for `global_asm!` and `asm!`.
-* SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), some basic things work)
+* SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), `std::simd` fully works, `std::arch` is partially supported)
+* Unwinding on panics ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1677), `-Cpanic=abort` is enabled by default)
## License
[[package]]
name = "cc"
-version = "1.0.77"
+version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
+checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]]
name = "cfg-if"
[[package]]
name = "compiler_builtins"
-version = "0.1.85"
+version = "0.1.86"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e81c6cd7ab79f51a0c927d22858d61ad12bd0b3865f0b13ece02a4486aeabb"
+checksum = "5dae98c88e576098d7ab13ebcb40cc43e5114b2beafe61a87cda9200649ff205"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "libc"
-version = "0.2.138"
+version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
dependencies = [
"rustc-std-workspace-core",
]
use std::path::Path;
use super::build_sysroot;
-use super::config;
use super::path::Dirs;
use super::prepare::GitRepo;
use super::utils::{spawn_and_wait, CargoProject, Compiler};
pub(crate) static ABI_CAFE_REPO: GitRepo =
GitRepo::github("Gankra", "abi-cafe", "4c6dc8c9c687e2b3a760ff2176ce236872b37212", "abi-cafe");
-static ABI_CAFE: CargoProject = CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe");
+pub(crate) static ABI_CAFE: CargoProject =
+ CargoProject::new(&ABI_CAFE_REPO.source_dir(), "abi_cafe");
pub(crate) fn run(
channel: &str,
sysroot_kind: SysrootKind,
dirs: &Dirs,
cg_clif_dylib: &Path,
- host_triple: &str,
- target_triple: &str,
+ bootstrap_host_compiler: &Compiler,
) {
- if !config::get_bool("testsuite.abi-cafe") {
- eprintln!("[SKIP] abi-cafe");
- return;
- }
-
- if host_triple != target_triple {
- eprintln!("[SKIP] abi-cafe (cross-compilation not supported)");
- return;
- }
-
eprintln!("Building sysroot for abi-cafe");
build_sysroot::build_sysroot(
dirs,
channel,
sysroot_kind,
cg_clif_dylib,
- host_triple,
- target_triple,
+ bootstrap_host_compiler,
+ bootstrap_host_compiler.triple.clone(),
);
eprintln!("Running abi-cafe");
let pairs = ["rustc_calls_cgclif", "cgclif_calls_rustc", "cgclif_calls_cc", "cc_calls_cgclif"];
- let mut cmd = ABI_CAFE.run(&Compiler::host(), dirs);
+ let mut cmd = ABI_CAFE.run(bootstrap_host_compiler, dirs);
cmd.arg("--");
cmd.arg("--pairs");
cmd.args(pairs);
--- /dev/null
+use std::env;
+use std::fs;
+use std::path::Path;
+
+use super::path::{Dirs, RelPath};
+use super::prepare::GitRepo;
+use super::rustc_info::get_file_name;
+use super::utils::{hyperfine_command, is_ci, spawn_and_wait, CargoProject, Compiler};
+
+pub(crate) static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github(
+ "ebobby",
+ "simple-raytracer",
+ "804a7a21b9e673a482797aa289a18ed480e4d813",
+ "<none>",
+);
+
+// Use a separate target dir for the initial LLVM build to reduce unnecessary recompiles
+pub(crate) static SIMPLE_RAYTRACER_LLVM: CargoProject =
+ CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer_llvm");
+
+pub(crate) static SIMPLE_RAYTRACER: CargoProject =
+ CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer");
+
+pub(crate) fn benchmark(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
+ benchmark_simple_raytracer(dirs, bootstrap_host_compiler);
+}
+
+fn benchmark_simple_raytracer(dirs: &Dirs, bootstrap_host_compiler: &Compiler) {
+ if std::process::Command::new("hyperfine").output().is_err() {
+ eprintln!("Hyperfine not installed");
+ eprintln!("Hint: Try `cargo install hyperfine` to install hyperfine");
+ std::process::exit(1);
+ }
+
+ eprintln!("[LLVM BUILD] simple-raytracer");
+ let build_cmd = SIMPLE_RAYTRACER_LLVM.build(bootstrap_host_compiler, dirs);
+ spawn_and_wait(build_cmd);
+ fs::copy(
+ SIMPLE_RAYTRACER_LLVM
+ .target_dir(dirs)
+ .join(&bootstrap_host_compiler.triple)
+ .join("debug")
+ .join(get_file_name("main", "bin")),
+ RelPath::BUILD.to_path(dirs).join(get_file_name("raytracer_cg_llvm", "bin")),
+ )
+ .unwrap();
+
+ let run_runs = env::var("RUN_RUNS")
+ .unwrap_or(if is_ci() { "2" } else { "10" }.to_string())
+ .parse()
+ .unwrap();
+
+ eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
+ let cargo_clif =
+ RelPath::DIST.to_path(dirs).join(get_file_name("cargo_clif", "bin").replace('_', "-"));
+ let manifest_path = SIMPLE_RAYTRACER.manifest_path(dirs);
+ let target_dir = SIMPLE_RAYTRACER.target_dir(dirs);
+
+ let clean_cmd = format!(
+ "cargo clean --manifest-path {manifest_path} --target-dir {target_dir}",
+ manifest_path = manifest_path.display(),
+ target_dir = target_dir.display(),
+ );
+ let llvm_build_cmd = format!(
+ "cargo build --manifest-path {manifest_path} --target-dir {target_dir}",
+ manifest_path = manifest_path.display(),
+ target_dir = target_dir.display(),
+ );
+ let clif_build_cmd = format!(
+ "{cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir}",
+ cargo_clif = cargo_clif.display(),
+ manifest_path = manifest_path.display(),
+ target_dir = target_dir.display(),
+ );
+
+ let bench_compile =
+ hyperfine_command(1, run_runs, Some(&clean_cmd), &llvm_build_cmd, &clif_build_cmd);
+
+ spawn_and_wait(bench_compile);
+
+ eprintln!("[BENCH RUN] ebobby/simple-raytracer");
+ fs::copy(
+ target_dir.join("debug").join(get_file_name("main", "bin")),
+ RelPath::BUILD.to_path(dirs).join(get_file_name("raytracer_cg_clif", "bin")),
+ )
+ .unwrap();
+
+ let mut bench_run = hyperfine_command(
+ 0,
+ run_runs,
+ None,
+ Path::new(".").join(get_file_name("raytracer_cg_llvm", "bin")).to_str().unwrap(),
+ Path::new(".").join(get_file_name("raytracer_cg_clif", "bin")).to_str().unwrap(),
+ );
+ bench_run.current_dir(RelPath::BUILD.to_path(dirs));
+ spawn_and_wait(bench_run);
+}
use super::rustc_info::get_file_name;
use super::utils::{is_ci, CargoProject, Compiler};
-static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif");
+pub(crate) static CG_CLIF: CargoProject = CargoProject::new(&RelPath::SOURCE, "cg_clif");
pub(crate) fn build_backend(
dirs: &Dirs,
channel: &str,
- host_triple: &str,
+ bootstrap_host_compiler: &Compiler,
use_unstable_features: bool,
) -> PathBuf {
- let mut cmd = CG_CLIF.build(&Compiler::host(), dirs);
+ let mut cmd = CG_CLIF.build(&bootstrap_host_compiler, dirs);
cmd.env("CARGO_BUILD_INCREMENTAL", "true"); // Force incr comp even in release mode
// Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway
cmd.env("CARGO_BUILD_INCREMENTAL", "false");
+
+ cmd.env("CARGO_PROFILE_RELEASE_DEBUG_ASSERTIONS", "true");
}
if use_unstable_features {
CG_CLIF
.target_dir(dirs)
- .join(host_triple)
+ .join(&bootstrap_host_compiler.triple)
.join(channel)
.join(get_file_name("rustc_codegen_cranelift", "dylib"))
}
use std::fs;
-use std::path::Path;
+use std::path::{Path, PathBuf};
use std::process::{self, Command};
use super::path::{Dirs, RelPath};
-use super::rustc_info::{get_file_name, get_rustc_version, get_wrapper_file_name};
-use super::utils::{spawn_and_wait, try_hard_link, CargoProject, Compiler};
+use super::rustc_info::{get_file_name, get_rustc_version, get_toolchain_name};
+use super::utils::{remove_dir_if_exists, spawn_and_wait, try_hard_link, CargoProject, Compiler};
use super::SysrootKind;
static DIST_DIR: RelPath = RelPath::DIST;
static BIN_DIR: RelPath = RelPath::DIST.join("bin");
static LIB_DIR: RelPath = RelPath::DIST.join("lib");
-static RUSTLIB_DIR: RelPath = LIB_DIR.join("rustlib");
pub(crate) fn build_sysroot(
dirs: &Dirs,
channel: &str,
sysroot_kind: SysrootKind,
cg_clif_dylib_src: &Path,
- host_triple: &str,
- target_triple: &str,
-) {
+ bootstrap_host_compiler: &Compiler,
+ target_triple: String,
+) -> Compiler {
eprintln!("[BUILD] sysroot {:?}", sysroot_kind);
DIST_DIR.ensure_fresh(dirs);
BIN_DIR.ensure_exists(dirs);
LIB_DIR.ensure_exists(dirs);
+ let is_native = bootstrap_host_compiler.triple == target_triple;
+
// Copy the backend
let cg_clif_dylib_path = if cfg!(windows) {
// Windows doesn't have rpath support, so the cg_clif dylib needs to be next to the
LIB_DIR
}
.to_path(dirs)
- .join(get_file_name("rustc_codegen_cranelift", "dylib"));
+ .join(cg_clif_dylib_src.file_name().unwrap());
try_hard_link(cg_clif_dylib_src, &cg_clif_dylib_path);
// Build and copy rustc and cargo wrappers
+ let wrapper_base_name = get_file_name("____", "bin");
+ let toolchain_name = get_toolchain_name();
for wrapper in ["rustc-clif", "rustdoc-clif", "cargo-clif"] {
- let wrapper_name = get_wrapper_file_name(wrapper, "bin");
+ let wrapper_name = wrapper_base_name.replace("____", wrapper);
- let mut build_cargo_wrapper_cmd = Command::new("rustc");
+ let mut build_cargo_wrapper_cmd = Command::new(&bootstrap_host_compiler.rustc);
build_cargo_wrapper_cmd
+ .env("TOOLCHAIN_NAME", toolchain_name.clone())
.arg(RelPath::SCRIPTS.to_path(dirs).join(&format!("{wrapper}.rs")))
.arg("-o")
.arg(DIST_DIR.to_path(dirs).join(wrapper_name))
- .arg("-g");
+ .arg("-Cstrip=debuginfo");
spawn_and_wait(build_cargo_wrapper_cmd);
}
- let default_sysroot = super::rustc_info::get_default_sysroot();
+ let host = build_sysroot_for_triple(
+ dirs,
+ channel,
+ bootstrap_host_compiler.clone(),
+ &cg_clif_dylib_path,
+ sysroot_kind,
+ );
+ host.install_into_sysroot(&DIST_DIR.to_path(dirs));
- let host_rustlib_lib = RUSTLIB_DIR.to_path(dirs).join(host_triple).join("lib");
- let target_rustlib_lib = RUSTLIB_DIR.to_path(dirs).join(target_triple).join("lib");
- fs::create_dir_all(&host_rustlib_lib).unwrap();
- fs::create_dir_all(&target_rustlib_lib).unwrap();
+ if !is_native {
+ build_sysroot_for_triple(
+ dirs,
+ channel,
+ {
+ let mut bootstrap_target_compiler = bootstrap_host_compiler.clone();
+ bootstrap_target_compiler.triple = target_triple.clone();
+ bootstrap_target_compiler.set_cross_linker_and_runner();
+ bootstrap_target_compiler
+ },
+ &cg_clif_dylib_path,
+ sysroot_kind,
+ )
+ .install_into_sysroot(&DIST_DIR.to_path(dirs));
+ }
- if target_triple == "x86_64-pc-windows-gnu" {
- if !default_sysroot.join("lib").join("rustlib").join(target_triple).join("lib").exists() {
- eprintln!(
- "The x86_64-pc-windows-gnu target needs to be installed first before it is possible \
- to compile a sysroot for it.",
- );
- process::exit(1);
+ // Copy std for the host to the lib dir. This is necessary for the jit mode to find
+ // libstd.
+ for lib in host.libs {
+ let filename = lib.file_name().unwrap().to_str().unwrap();
+ if filename.contains("std-") && !filename.contains(".rlib") {
+ try_hard_link(&lib, LIB_DIR.to_path(dirs).join(lib.file_name().unwrap()));
}
- for file in fs::read_dir(
- default_sysroot.join("lib").join("rustlib").join(target_triple).join("lib"),
- )
- .unwrap()
- {
- let file = file.unwrap().path();
- if file.extension().map_or(true, |ext| ext.to_str().unwrap() != "o") {
- continue; // only copy object files
- }
- try_hard_link(&file, target_rustlib_lib.join(file.file_name().unwrap()));
+ }
+
+ let mut target_compiler = {
+ let dirs: &Dirs = &dirs;
+ let rustc_clif =
+ RelPath::DIST.to_path(&dirs).join(wrapper_base_name.replace("____", "rustc-clif"));
+ let rustdoc_clif =
+ RelPath::DIST.to_path(&dirs).join(wrapper_base_name.replace("____", "rustdoc-clif"));
+
+ Compiler {
+ cargo: bootstrap_host_compiler.cargo.clone(),
+ rustc: rustc_clif.clone(),
+ rustdoc: rustdoc_clif.clone(),
+ rustflags: String::new(),
+ rustdocflags: String::new(),
+ triple: target_triple,
+ runner: vec![],
}
+ };
+ if !is_native {
+ target_compiler.set_cross_linker_and_runner();
}
+ target_compiler
+}
- match sysroot_kind {
- SysrootKind::None => {} // Nothing to do
- SysrootKind::Llvm => {
- for file in fs::read_dir(
- default_sysroot.join("lib").join("rustlib").join(host_triple).join("lib"),
- )
- .unwrap()
- {
- let file = file.unwrap().path();
- let file_name_str = file.file_name().unwrap().to_str().unwrap();
- if (file_name_str.contains("rustc_")
- && !file_name_str.contains("rustc_std_workspace_")
- && !file_name_str.contains("rustc_demangle"))
- || file_name_str.contains("chalk")
- || file_name_str.contains("tracing")
- || file_name_str.contains("regex")
- {
- // These are large crates that are part of the rustc-dev component and are not
- // necessary to run regular programs.
- continue;
- }
- try_hard_link(&file, host_rustlib_lib.join(file.file_name().unwrap()));
- }
+struct SysrootTarget {
+ triple: String,
+ libs: Vec<PathBuf>,
+}
- if target_triple != host_triple {
- for file in fs::read_dir(
- default_sysroot.join("lib").join("rustlib").join(target_triple).join("lib"),
- )
- .unwrap()
- {
- let file = file.unwrap().path();
- try_hard_link(&file, target_rustlib_lib.join(file.file_name().unwrap()));
- }
- }
+impl SysrootTarget {
+ fn install_into_sysroot(&self, sysroot: &Path) {
+ if self.libs.is_empty() {
+ return;
}
- SysrootKind::Clif => {
- build_clif_sysroot_for_triple(dirs, channel, host_triple, &cg_clif_dylib_path, None);
-
- if host_triple != target_triple {
- // When cross-compiling it is often necessary to manually pick the right linker
- let linker = match target_triple {
- "aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu-gcc"),
- "s390x-unknown-linux-gnu" => Some("s390x-linux-gnu-gcc"),
- _ => None,
- };
- build_clif_sysroot_for_triple(
- dirs,
- channel,
- target_triple,
- &cg_clif_dylib_path,
- linker,
- );
- }
- // Copy std for the host to the lib dir. This is necessary for the jit mode to find
- // libstd.
- for file in fs::read_dir(host_rustlib_lib).unwrap() {
- let file = file.unwrap().path();
- let filename = file.file_name().unwrap().to_str().unwrap();
- if filename.contains("std-") && !filename.contains(".rlib") {
- try_hard_link(&file, LIB_DIR.to_path(dirs).join(file.file_name().unwrap()));
- }
- }
+ let target_rustlib_lib = sysroot.join("lib").join("rustlib").join(&self.triple).join("lib");
+ fs::create_dir_all(&target_rustlib_lib).unwrap();
+
+ for lib in &self.libs {
+ try_hard_link(lib, target_rustlib_lib.join(lib.file_name().unwrap()));
}
}
}
-// FIXME move to download/ or dist/
-pub(crate) static SYSROOT_RUSTC_VERSION: RelPath = RelPath::BUILD_SYSROOT.join("rustc_version");
-pub(crate) static SYSROOT_SRC: RelPath = RelPath::BUILD_SYSROOT.join("sysroot_src");
-static STANDARD_LIBRARY: CargoProject = CargoProject::new(&RelPath::BUILD_SYSROOT, "build_sysroot");
+pub(crate) static ORIG_BUILD_SYSROOT: RelPath = RelPath::SOURCE.join("build_sysroot");
+pub(crate) static BUILD_SYSROOT: RelPath = RelPath::DOWNLOAD.join("sysroot");
+pub(crate) static SYSROOT_RUSTC_VERSION: RelPath = BUILD_SYSROOT.join("rustc_version");
+pub(crate) static SYSROOT_SRC: RelPath = BUILD_SYSROOT.join("sysroot_src");
+pub(crate) static STANDARD_LIBRARY: CargoProject =
+ CargoProject::new(&BUILD_SYSROOT, "build_sysroot");
+pub(crate) static RTSTARTUP_SYSROOT: RelPath = RelPath::BUILD.join("rtstartup");
+#[must_use]
+fn build_sysroot_for_triple(
+ dirs: &Dirs,
+ channel: &str,
+ compiler: Compiler,
+ cg_clif_dylib_path: &Path,
+ sysroot_kind: SysrootKind,
+) -> SysrootTarget {
+ match sysroot_kind {
+ SysrootKind::None => build_rtstartup(dirs, &compiler)
+ .unwrap_or(SysrootTarget { triple: compiler.triple, libs: vec![] }),
+ SysrootKind::Llvm => build_llvm_sysroot_for_triple(compiler),
+ SysrootKind::Clif => {
+ build_clif_sysroot_for_triple(dirs, channel, compiler, &cg_clif_dylib_path)
+ }
+ }
+}
+
+#[must_use]
+fn build_llvm_sysroot_for_triple(compiler: Compiler) -> SysrootTarget {
+ let default_sysroot = super::rustc_info::get_default_sysroot(&compiler.rustc);
+
+ let mut target_libs = SysrootTarget { triple: compiler.triple, libs: vec![] };
+
+ for entry in fs::read_dir(
+ default_sysroot.join("lib").join("rustlib").join(&target_libs.triple).join("lib"),
+ )
+ .unwrap()
+ {
+ let entry = entry.unwrap();
+ if entry.file_type().unwrap().is_dir() {
+ continue;
+ }
+ let file = entry.path();
+ let file_name_str = file.file_name().unwrap().to_str().unwrap();
+ if (file_name_str.contains("rustc_")
+ && !file_name_str.contains("rustc_std_workspace_")
+ && !file_name_str.contains("rustc_demangle"))
+ || file_name_str.contains("chalk")
+ || file_name_str.contains("tracing")
+ || file_name_str.contains("regex")
+ {
+ // These are large crates that are part of the rustc-dev component and are not
+ // necessary to run regular programs.
+ continue;
+ }
+ target_libs.libs.push(file);
+ }
+
+ target_libs
+}
+
+#[must_use]
fn build_clif_sysroot_for_triple(
dirs: &Dirs,
channel: &str,
- triple: &str,
+ mut compiler: Compiler,
cg_clif_dylib_path: &Path,
- linker: Option<&str>,
-) {
+) -> SysrootTarget {
match fs::read_to_string(SYSROOT_RUSTC_VERSION.to_path(dirs)) {
Err(e) => {
eprintln!("Failed to get rustc version for patched sysroot source: {}", e);
process::exit(1);
}
Ok(source_version) => {
- let rustc_version = get_rustc_version();
+ let rustc_version = get_rustc_version(&compiler.rustc);
if source_version != rustc_version {
eprintln!("The patched sysroot source is outdated");
eprintln!("Source version: {}", source_version.trim());
}
}
- let build_dir = STANDARD_LIBRARY.target_dir(dirs).join(triple).join(channel);
+ let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] };
+
+ if let Some(rtstartup_target_libs) = build_rtstartup(dirs, &compiler) {
+ rtstartup_target_libs.install_into_sysroot(&RTSTARTUP_SYSROOT.to_path(dirs));
+
+ target_libs.libs.extend(rtstartup_target_libs.libs);
+ }
+
+ let build_dir = STANDARD_LIBRARY.target_dir(dirs).join(&compiler.triple).join(channel);
if !super::config::get_bool("keep_sysroot") {
// Cleanup the deps dir, but keep build scripts and the incremental cache for faster
// recompilation as they are not affected by changes in cg_clif.
- if build_dir.join("deps").exists() {
- fs::remove_dir_all(build_dir.join("deps")).unwrap();
- }
+ remove_dir_if_exists(&build_dir.join("deps"));
}
// Build sysroot
- let mut rustflags = "-Zforce-unstable-if-unmarked -Cpanic=abort".to_string();
+ let mut rustflags = " -Zforce-unstable-if-unmarked -Cpanic=abort".to_string();
rustflags.push_str(&format!(" -Zcodegen-backend={}", cg_clif_dylib_path.to_str().unwrap()));
- rustflags.push_str(&format!(" --sysroot={}", DIST_DIR.to_path(dirs).to_str().unwrap()));
+ // Necessary for MinGW to find rsbegin.o and rsend.o
+ rustflags
+ .push_str(&format!(" --sysroot={}", RTSTARTUP_SYSROOT.to_path(dirs).to_str().unwrap()));
if channel == "release" {
rustflags.push_str(" -Zmir-opt-level=3");
}
- if let Some(linker) = linker {
- use std::fmt::Write;
- write!(rustflags, " -Clinker={}", linker).unwrap();
- }
- let mut compiler = Compiler::with_triple(triple.to_owned());
- compiler.rustflags = rustflags;
+ compiler.rustflags += &rustflags;
let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
if channel == "release" {
build_cmd.arg("--release");
build_cmd.env("__CARGO_DEFAULT_LIB_METADATA", "cg_clif");
spawn_and_wait(build_cmd);
- // Copy all relevant files to the sysroot
for entry in fs::read_dir(build_dir.join("deps")).unwrap() {
let entry = entry.unwrap();
if let Some(ext) = entry.path().extension() {
} else {
continue;
};
- try_hard_link(
- entry.path(),
- RUSTLIB_DIR.to_path(dirs).join(triple).join("lib").join(entry.file_name()),
- );
+ target_libs.libs.push(entry.path());
+ }
+
+ target_libs
+}
+
+fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> {
+ if !compiler.triple.ends_with("windows-gnu") {
+ return None;
}
+
+ RTSTARTUP_SYSROOT.ensure_fresh(dirs);
+
+ let rtstartup_src = SYSROOT_SRC.to_path(dirs).join("library").join("rtstartup");
+ let mut target_libs = SysrootTarget { triple: compiler.triple.clone(), libs: vec![] };
+
+ for file in ["rsbegin", "rsend"] {
+ let obj = RTSTARTUP_SYSROOT.to_path(dirs).join(format!("{file}.o"));
+ let mut build_rtstartup_cmd = Command::new(&compiler.rustc);
+ build_rtstartup_cmd
+ .arg("--target")
+ .arg(&compiler.triple)
+ .arg("--emit=obj")
+ .arg("-o")
+ .arg(&obj)
+ .arg(rtstartup_src.join(format!("{file}.rs")));
+ spawn_and_wait(build_rtstartup_cmd);
+ target_libs.libs.push(obj.clone());
+ }
+
+ Some(target_libs)
}
use std::path::PathBuf;
use std::process;
-use self::utils::is_ci;
+use self::utils::{is_ci, Compiler};
mod abi_cafe;
+mod bench;
mod build_backend;
mod build_sysroot;
mod config;
mod tests;
mod utils;
-const USAGE: &str = r#"The build system of cg_clif.
-
-USAGE:
- ./y.rs prepare [--out-dir DIR]
- ./y.rs build [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
- ./y.rs test [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
-
-OPTIONS:
- --sysroot none|clif|llvm
- Which sysroot libraries to use:
- `none` will not include any standard library in the sysroot.
- `clif` will build the standard library using Cranelift.
- `llvm` will use the pre-compiled standard library of rustc which is compiled with LLVM.
-
- --out-dir DIR
- Specify the directory in which the download, build and dist directories are stored.
- By default this is the working directory.
-
- --no-unstable-features
- fSome features are not yet ready for production usage. This option will disable these
- features. This includes the JIT mode and inline assembly support.
-"#;
-
fn usage() {
- eprintln!("{USAGE}");
+ eprintln!("{}", include_str!("usage.txt"));
}
macro_rules! arg_error {
Prepare,
Build,
Test,
+ AbiCafe,
+ Bench,
}
#[derive(Copy, Clone, Debug)]
}
pub fn main() {
- env::set_var("CG_CLIF_DISPLAY_CG_TIME", "1");
+ if env::var("RUST_BACKTRACE").is_err() {
+ env::set_var("RUST_BACKTRACE", "1");
+ }
env::set_var("CG_CLIF_DISABLE_INCR_CACHE", "1");
if is_ci() {
// Disabling incr comp reduces cache size and incr comp doesn't save as much on CI anyway
env::set_var("CARGO_BUILD_INCREMENTAL", "false");
+
+ // Enable the Cranelift verifier
+ env::set_var("CG_CLIF_ENABLE_VERIFIER", "1");
}
let mut args = env::args().skip(1);
Some("prepare") => Command::Prepare,
Some("build") => Command::Build,
Some("test") => Command::Test,
+ Some("abi-cafe") => Command::AbiCafe,
+ Some("bench") => Command::Bench,
Some(flag) if flag.starts_with('-') => arg_error!("Expected command found flag {}", flag),
Some(command) => arg_error!("Unknown command {}", command),
None => {
}
}
- let host_triple = if let Ok(host_triple) = std::env::var("HOST_TRIPLE") {
- host_triple
- } else if let Some(host_triple) = config::get_value("host") {
- host_triple
- } else {
- rustc_info::get_host_triple()
- };
- let target_triple = if let Ok(target_triple) = std::env::var("TARGET_TRIPLE") {
- if target_triple != "" {
- target_triple
- } else {
- host_triple.clone() // Empty target triple can happen on GHA
- }
- } else if let Some(target_triple) = config::get_value("target") {
- target_triple
- } else {
- host_triple.clone()
- };
+ let bootstrap_host_compiler = Compiler::bootstrap_with_triple(
+ std::env::var("HOST_TRIPLE")
+ .ok()
+ .or_else(|| config::get_value("host"))
+ .unwrap_or_else(|| rustc_info::get_host_triple()),
+ );
+ let target_triple = std::env::var("TARGET_TRIPLE")
+ .ok()
+ .or_else(|| config::get_value("target"))
+ .unwrap_or_else(|| bootstrap_host_compiler.triple.clone());
// FIXME allow changing the location of these dirs using cli arguments
let current_dir = std::env::current_dir().unwrap();
process::exit(0);
}
- let cg_clif_dylib =
- build_backend::build_backend(&dirs, channel, &host_triple, use_unstable_features);
+ env::set_var("RUSTC", "rustc_should_be_set_explicitly");
+ env::set_var("RUSTDOC", "rustdoc_should_be_set_explicitly");
+
+ let cg_clif_dylib = build_backend::build_backend(
+ &dirs,
+ channel,
+ &bootstrap_host_compiler,
+ use_unstable_features,
+ );
match command {
Command::Prepare => {
// Handled above
channel,
sysroot_kind,
&cg_clif_dylib,
- &host_triple,
- &target_triple,
+ &bootstrap_host_compiler,
+ target_triple.clone(),
);
-
- abi_cafe::run(
+ }
+ Command::AbiCafe => {
+ if bootstrap_host_compiler.triple != target_triple {
+ eprintln!("Abi-cafe doesn't support cross-compilation");
+ process::exit(1);
+ }
+ abi_cafe::run(channel, sysroot_kind, &dirs, &cg_clif_dylib, &bootstrap_host_compiler);
+ }
+ Command::Build => {
+ build_sysroot::build_sysroot(
+ &dirs,
channel,
sysroot_kind,
- &dirs,
&cg_clif_dylib,
- &host_triple,
- &target_triple,
+ &bootstrap_host_compiler,
+ target_triple,
);
}
- Command::Build => {
+ Command::Bench => {
build_sysroot::build_sysroot(
&dirs,
channel,
sysroot_kind,
&cg_clif_dylib,
- &host_triple,
- &target_triple,
+ &bootstrap_host_compiler,
+ target_triple,
);
+ bench::benchmark(&dirs, &bootstrap_host_compiler);
}
}
}
use std::fs;
use std::path::PathBuf;
+use super::utils::remove_dir_if_exists;
+
#[derive(Debug, Clone)]
pub(crate) struct Dirs {
pub(crate) source_dir: PathBuf,
pub(crate) const DIST: RelPath = RelPath::Base(PathBase::Dist);
pub(crate) const SCRIPTS: RelPath = RelPath::SOURCE.join("scripts");
- pub(crate) const BUILD_SYSROOT: RelPath = RelPath::SOURCE.join("build_sysroot");
pub(crate) const PATCHES: RelPath = RelPath::SOURCE.join("patches");
pub(crate) const fn join(&'static self, suffix: &'static str) -> RelPath {
pub(crate) fn ensure_fresh(&self, dirs: &Dirs) {
let path = self.to_path(dirs);
- if path.exists() {
- fs::remove_dir_all(&path).unwrap();
- }
+ remove_dir_if_exists(&path);
fs::create_dir_all(path).unwrap();
}
}
use std::path::{Path, PathBuf};
use std::process::Command;
-use super::build_sysroot::{SYSROOT_RUSTC_VERSION, SYSROOT_SRC};
+use super::build_sysroot::{BUILD_SYSROOT, ORIG_BUILD_SYSROOT, SYSROOT_RUSTC_VERSION, SYSROOT_SRC};
use super::path::{Dirs, RelPath};
-use super::rustc_info::{get_file_name, get_rustc_path, get_rustc_version};
-use super::utils::{copy_dir_recursively, spawn_and_wait, Compiler};
+use super::rustc_info::{get_default_sysroot, get_rustc_version};
+use super::utils::{copy_dir_recursively, git_command, retry_spawn_and_wait, spawn_and_wait};
pub(crate) fn prepare(dirs: &Dirs) {
- if RelPath::DOWNLOAD.to_path(dirs).exists() {
- std::fs::remove_dir_all(RelPath::DOWNLOAD.to_path(dirs)).unwrap();
- }
- std::fs::create_dir_all(RelPath::DOWNLOAD.to_path(dirs)).unwrap();
+ RelPath::DOWNLOAD.ensure_fresh(dirs);
- prepare_sysroot(dirs);
+ spawn_and_wait(super::build_backend::CG_CLIF.fetch("cargo", dirs));
- // FIXME maybe install this only locally?
- eprintln!("[INSTALL] hyperfine");
- Command::new("cargo")
- .arg("install")
- .arg("hyperfine")
- .env_remove("CARGO_TARGET_DIR")
- .spawn()
- .unwrap()
- .wait()
- .unwrap();
+ prepare_sysroot(dirs);
+ spawn_and_wait(super::build_sysroot::STANDARD_LIBRARY.fetch("cargo", dirs));
+ spawn_and_wait(super::tests::LIBCORE_TESTS.fetch("cargo", dirs));
super::abi_cafe::ABI_CAFE_REPO.fetch(dirs);
+ spawn_and_wait(super::abi_cafe::ABI_CAFE.fetch("cargo", dirs));
super::tests::RAND_REPO.fetch(dirs);
+ spawn_and_wait(super::tests::RAND.fetch("cargo", dirs));
super::tests::REGEX_REPO.fetch(dirs);
+ spawn_and_wait(super::tests::REGEX.fetch("cargo", dirs));
super::tests::PORTABLE_SIMD_REPO.fetch(dirs);
- super::tests::SIMPLE_RAYTRACER_REPO.fetch(dirs);
-
- eprintln!("[LLVM BUILD] simple-raytracer");
- let host_compiler = Compiler::host();
- let build_cmd = super::tests::SIMPLE_RAYTRACER.build(&host_compiler, dirs);
- spawn_and_wait(build_cmd);
- fs::copy(
- super::tests::SIMPLE_RAYTRACER
- .target_dir(dirs)
- .join(&host_compiler.triple)
- .join("debug")
- .join(get_file_name("main", "bin")),
- RelPath::BUILD.to_path(dirs).join(get_file_name("raytracer_cg_llvm", "bin")),
- )
- .unwrap();
+ spawn_and_wait(super::tests::PORTABLE_SIMD.fetch("cargo", dirs));
+ super::bench::SIMPLE_RAYTRACER_REPO.fetch(dirs);
+ spawn_and_wait(super::bench::SIMPLE_RAYTRACER.fetch("cargo", dirs));
}
fn prepare_sysroot(dirs: &Dirs) {
- let rustc_path = get_rustc_path();
- let sysroot_src_orig = rustc_path.parent().unwrap().join("../lib/rustlib/src/rust");
- let sysroot_src = SYSROOT_SRC;
-
+ let sysroot_src_orig = get_default_sysroot(Path::new("rustc")).join("lib/rustlib/src/rust");
assert!(sysroot_src_orig.exists());
- sysroot_src.ensure_fresh(dirs);
- fs::create_dir_all(sysroot_src.to_path(dirs).join("library")).unwrap();
eprintln!("[COPY] sysroot src");
+
+ // FIXME ensure builds error out or update the copy if any of the files copied here change
+ BUILD_SYSROOT.ensure_fresh(dirs);
+ copy_dir_recursively(&ORIG_BUILD_SYSROOT.to_path(dirs), &BUILD_SYSROOT.to_path(dirs));
+
+ fs::create_dir_all(SYSROOT_SRC.to_path(dirs).join("library")).unwrap();
copy_dir_recursively(
&sysroot_src_orig.join("library"),
- &sysroot_src.to_path(dirs).join("library"),
+ &SYSROOT_SRC.to_path(dirs).join("library"),
);
- let rustc_version = get_rustc_version();
+ let rustc_version = get_rustc_version(Path::new("rustc"));
fs::write(SYSROOT_RUSTC_VERSION.to_path(dirs), &rustc_version).unwrap();
eprintln!("[GIT] init");
- init_git_repo(&sysroot_src.to_path(dirs));
+ init_git_repo(&SYSROOT_SRC.to_path(dirs));
- apply_patches(dirs, "sysroot", &sysroot_src.to_path(dirs));
+ apply_patches(dirs, "sysroot", &SYSROOT_SRC.to_path(dirs));
}
pub(crate) struct GitRepo {
fn clone_repo(download_dir: &Path, repo: &str, rev: &str) {
eprintln!("[CLONE] {}", repo);
// Ignore exit code as the repo may already have been checked out
- Command::new("git").arg("clone").arg(repo).arg(&download_dir).spawn().unwrap().wait().unwrap();
+ git_command(None, "clone").arg(repo).arg(download_dir).spawn().unwrap().wait().unwrap();
- let mut clean_cmd = Command::new("git");
- clean_cmd.arg("checkout").arg("--").arg(".").current_dir(&download_dir);
+ let mut clean_cmd = git_command(download_dir, "checkout");
+ clean_cmd.arg("--").arg(".");
spawn_and_wait(clean_cmd);
- let mut checkout_cmd = Command::new("git");
- checkout_cmd.arg("checkout").arg("-q").arg(rev).current_dir(download_dir);
+ let mut checkout_cmd = git_command(download_dir, "checkout");
+ checkout_cmd.arg("-q").arg(rev);
spawn_and_wait(checkout_cmd);
}
// Download zip archive
let mut download_cmd = Command::new("curl");
- download_cmd.arg("--location").arg("--output").arg(&archive_file).arg(archive_url);
- spawn_and_wait(download_cmd);
+ download_cmd
+ .arg("--max-time")
+ .arg("600")
+ .arg("-y")
+ .arg("30")
+ .arg("-Y")
+ .arg("10")
+ .arg("--connect-timeout")
+ .arg("30")
+ .arg("--continue-at")
+ .arg("-")
+ .arg("--location")
+ .arg("--output")
+ .arg(&archive_file)
+ .arg(archive_url);
+ retry_spawn_and_wait(5, download_cmd);
// Unpack tar archive
let mut unpack_cmd = Command::new("tar");
}
fn init_git_repo(repo_dir: &Path) {
- let mut git_init_cmd = Command::new("git");
- git_init_cmd.arg("init").arg("-q").current_dir(repo_dir);
+ let mut git_init_cmd = git_command(repo_dir, "init");
+ git_init_cmd.arg("-q");
spawn_and_wait(git_init_cmd);
- let mut git_add_cmd = Command::new("git");
- git_add_cmd.arg("add").arg(".").current_dir(repo_dir);
+ let mut git_add_cmd = git_command(repo_dir, "add");
+ git_add_cmd.arg(".");
spawn_and_wait(git_add_cmd);
- let mut git_commit_cmd = Command::new("git");
- git_commit_cmd
- .arg("-c")
- .arg("user.name=Dummy")
- .arg("-c")
- .arg("user.email=dummy@example.com")
- .arg("commit")
- .arg("-m")
- .arg("Initial commit")
- .arg("-q")
- .current_dir(repo_dir);
+ let mut git_commit_cmd = git_command(repo_dir, "commit");
+ git_commit_cmd.arg("-m").arg("Initial commit").arg("-q");
spawn_and_wait(git_commit_cmd);
}
target_dir.file_name().unwrap(),
patch.file_name().unwrap()
);
- let mut apply_patch_cmd = Command::new("git");
- apply_patch_cmd
- .arg("-c")
- .arg("user.name=Dummy")
- .arg("-c")
- .arg("user.email=dummy@example.com")
- .arg("am")
- .arg(patch)
- .arg("-q")
- .current_dir(target_dir);
+ let mut apply_patch_cmd = git_command(target_dir, "am");
+ apply_patch_cmd.arg(patch).arg("-q");
spawn_and_wait(apply_patch_cmd);
}
}
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
-pub(crate) fn get_rustc_version() -> String {
+pub(crate) fn get_rustc_version(rustc: &Path) -> String {
let version_info =
- Command::new("rustc").stderr(Stdio::inherit()).args(&["-V"]).output().unwrap().stdout;
+ Command::new(rustc).stderr(Stdio::inherit()).args(&["-V"]).output().unwrap().stdout;
String::from_utf8(version_info).unwrap()
}
.to_owned()
}
+pub(crate) fn get_toolchain_name() -> String {
+ let active_toolchain = Command::new("rustup")
+ .stderr(Stdio::inherit())
+ .args(&["show", "active-toolchain"])
+ .output()
+ .unwrap()
+ .stdout;
+ String::from_utf8(active_toolchain).unwrap().trim().split_once(' ').unwrap().0.to_owned()
+}
+
pub(crate) fn get_cargo_path() -> PathBuf {
let cargo_path = Command::new("rustup")
.stderr(Stdio::inherit())
Path::new(String::from_utf8(rustc_path).unwrap().trim()).to_owned()
}
-pub(crate) fn get_default_sysroot() -> PathBuf {
- let default_sysroot = Command::new("rustc")
+pub(crate) fn get_default_sysroot(rustc: &Path) -> PathBuf {
+ let default_sysroot = Command::new(rustc)
.stderr(Stdio::inherit())
.args(&["--print", "sysroot"])
.output()
assert!(file_name.contains(crate_name));
file_name
}
-
-/// Similar to `get_file_name`, but converts any dashes (`-`) in the `crate_name` to
-/// underscores (`_`). This is specially made for the rustc and cargo wrappers
-/// which have a dash in the name, and that is not allowed in a crate name.
-pub(crate) fn get_wrapper_file_name(crate_name: &str, crate_type: &str) -> String {
- let crate_name = crate_name.replace('-', "_");
- let wrapper_name = get_file_name(&crate_name, crate_type);
- wrapper_name.replace('_', "-")
-}
-use super::build_sysroot;
+use super::bench::SIMPLE_RAYTRACER;
+use super::build_sysroot::{self, SYSROOT_SRC};
use super::config;
use super::path::{Dirs, RelPath};
use super::prepare::GitRepo;
-use super::rustc_info::{get_cargo_path, get_wrapper_file_name};
-use super::utils::{
- hyperfine_command, spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler,
-};
+use super::rustc_info::get_host_triple;
+use super::utils::{spawn_and_wait, spawn_and_wait_with_input, CargoProject, Compiler};
use super::SysrootKind;
use std::env;
use std::ffi::OsStr;
struct TestCase {
config: &'static str,
- func: &'static dyn Fn(&TestRunner),
+ cmd: TestCaseCmd,
+}
+
+enum TestCaseCmd {
+ Custom { func: &'static dyn Fn(&TestRunner) },
+ BuildLib { source: &'static str, crate_types: &'static str },
+ BuildBinAndRun { source: &'static str, args: &'static [&'static str] },
+ JitBin { source: &'static str, args: &'static str },
}
impl TestCase {
- const fn new(config: &'static str, func: &'static dyn Fn(&TestRunner)) -> Self {
- Self { config, func }
+ // FIXME reduce usage of custom test case commands
+ const fn custom(config: &'static str, func: &'static dyn Fn(&TestRunner)) -> Self {
+ Self { config, cmd: TestCaseCmd::Custom { func } }
+ }
+
+ const fn build_lib(
+ config: &'static str,
+ source: &'static str,
+ crate_types: &'static str,
+ ) -> Self {
+ Self { config, cmd: TestCaseCmd::BuildLib { source, crate_types } }
+ }
+
+ const fn build_bin_and_run(
+ config: &'static str,
+ source: &'static str,
+ args: &'static [&'static str],
+ ) -> Self {
+ Self { config, cmd: TestCaseCmd::BuildBinAndRun { source, args } }
+ }
+
+ const fn jit_bin(config: &'static str, source: &'static str, args: &'static str) -> Self {
+ Self { config, cmd: TestCaseCmd::JitBin { source, args } }
}
}
const NO_SYSROOT_SUITE: &[TestCase] = &[
- TestCase::new("build.mini_core", &|runner| {
- runner.run_rustc([
- "example/mini_core.rs",
- "--crate-name",
- "mini_core",
- "--crate-type",
- "lib,dylib",
- "--target",
- &runner.target_compiler.triple,
- ]);
- }),
- TestCase::new("build.example", &|runner| {
- runner.run_rustc([
- "example/example.rs",
- "--crate-type",
- "lib",
- "--target",
- &runner.target_compiler.triple,
- ]);
- }),
- TestCase::new("jit.mini_core_hello_world", &|runner| {
- let mut jit_cmd = runner.rustc_command([
- "-Zunstable-options",
- "-Cllvm-args=mode=jit",
- "-Cprefer-dynamic",
- "example/mini_core_hello_world.rs",
- "--cfg",
- "jit",
- "--target",
- &runner.target_compiler.triple,
- ]);
- jit_cmd.env("CG_CLIF_JIT_ARGS", "abc bcd");
- spawn_and_wait(jit_cmd);
-
- eprintln!("[JIT-lazy] mini_core_hello_world");
- let mut jit_cmd = runner.rustc_command([
- "-Zunstable-options",
- "-Cllvm-args=mode=jit-lazy",
- "-Cprefer-dynamic",
- "example/mini_core_hello_world.rs",
- "--cfg",
- "jit",
- "--target",
- &runner.target_compiler.triple,
- ]);
- jit_cmd.env("CG_CLIF_JIT_ARGS", "abc bcd");
- spawn_and_wait(jit_cmd);
- }),
- TestCase::new("aot.mini_core_hello_world", &|runner| {
- runner.run_rustc([
- "example/mini_core_hello_world.rs",
- "--crate-name",
- "mini_core_hello_world",
- "--crate-type",
- "bin",
- "-g",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("mini_core_hello_world", ["abc", "bcd"]);
- }),
+ TestCase::build_lib("build.mini_core", "example/mini_core.rs", "lib,dylib"),
+ TestCase::build_lib("build.example", "example/example.rs", "lib"),
+ TestCase::jit_bin("jit.mini_core_hello_world", "example/mini_core_hello_world.rs", "abc bcd"),
+ TestCase::build_bin_and_run(
+ "aot.mini_core_hello_world",
+ "example/mini_core_hello_world.rs",
+ &["abc", "bcd"],
+ ),
];
const BASE_SYSROOT_SUITE: &[TestCase] = &[
- TestCase::new("aot.arbitrary_self_types_pointers_and_wrappers", &|runner| {
- runner.run_rustc([
- "example/arbitrary_self_types_pointers_and_wrappers.rs",
- "--crate-name",
- "arbitrary_self_types_pointers_and_wrappers",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("arbitrary_self_types_pointers_and_wrappers", []);
- }),
- TestCase::new("aot.issue_91827_extern_types", &|runner| {
- runner.run_rustc([
- "example/issue-91827-extern-types.rs",
- "--crate-name",
- "issue_91827_extern_types",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("issue_91827_extern_types", []);
- }),
- TestCase::new("build.alloc_system", &|runner| {
- runner.run_rustc([
- "example/alloc_system.rs",
- "--crate-type",
- "lib",
- "--target",
- &runner.target_compiler.triple,
- ]);
- }),
- TestCase::new("aot.alloc_example", &|runner| {
- runner.run_rustc([
- "example/alloc_example.rs",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("alloc_example", []);
- }),
- TestCase::new("jit.std_example", &|runner| {
- runner.run_rustc([
- "-Zunstable-options",
- "-Cllvm-args=mode=jit",
- "-Cprefer-dynamic",
- "example/std_example.rs",
- "--target",
- &runner.target_compiler.triple,
- ]);
-
- eprintln!("[JIT-lazy] std_example");
- runner.run_rustc([
- "-Zunstable-options",
- "-Cllvm-args=mode=jit-lazy",
- "-Cprefer-dynamic",
- "example/std_example.rs",
- "--target",
- &runner.target_compiler.triple,
- ]);
- }),
- TestCase::new("aot.std_example", &|runner| {
- runner.run_rustc([
- "example/std_example.rs",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("std_example", ["arg"]);
- }),
- TestCase::new("aot.dst_field_align", &|runner| {
- runner.run_rustc([
- "example/dst-field-align.rs",
- "--crate-name",
- "dst_field_align",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("dst_field_align", []);
- }),
- TestCase::new("aot.subslice-patterns-const-eval", &|runner| {
- runner.run_rustc([
- "example/subslice-patterns-const-eval.rs",
- "--crate-type",
- "bin",
- "-Cpanic=abort",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("subslice-patterns-const-eval", []);
- }),
- TestCase::new("aot.track-caller-attribute", &|runner| {
- runner.run_rustc([
- "example/track-caller-attribute.rs",
- "--crate-type",
- "bin",
- "-Cpanic=abort",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("track-caller-attribute", []);
- }),
- TestCase::new("aot.float-minmax-pass", &|runner| {
- runner.run_rustc([
- "example/float-minmax-pass.rs",
- "--crate-type",
- "bin",
- "-Cpanic=abort",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("float-minmax-pass", []);
- }),
- TestCase::new("aot.mod_bench", &|runner| {
- runner.run_rustc([
- "example/mod_bench.rs",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("mod_bench", []);
- }),
- TestCase::new("aot.issue-72793", &|runner| {
- runner.run_rustc([
- "example/issue-72793.rs",
- "--crate-type",
- "bin",
- "--target",
- &runner.target_compiler.triple,
- ]);
- runner.run_out_command("issue-72793", []);
- }),
+ TestCase::build_bin_and_run(
+ "aot.arbitrary_self_types_pointers_and_wrappers",
+ "example/arbitrary_self_types_pointers_and_wrappers.rs",
+ &[],
+ ),
+ TestCase::build_bin_and_run(
+ "aot.issue_91827_extern_types",
+ "example/issue-91827-extern-types.rs",
+ &[],
+ ),
+ TestCase::build_lib("build.alloc_system", "example/alloc_system.rs", "lib"),
+ TestCase::build_bin_and_run("aot.alloc_example", "example/alloc_example.rs", &[]),
+ TestCase::jit_bin("jit.std_example", "example/std_example.rs", ""),
+ TestCase::build_bin_and_run("aot.std_example", "example/std_example.rs", &["arg"]),
+ TestCase::build_bin_and_run("aot.dst_field_align", "example/dst-field-align.rs", &[]),
+ TestCase::build_bin_and_run(
+ "aot.subslice-patterns-const-eval",
+ "example/subslice-patterns-const-eval.rs",
+ &[],
+ ),
+ TestCase::build_bin_and_run(
+ "aot.track-caller-attribute",
+ "example/track-caller-attribute.rs",
+ &[],
+ ),
+ TestCase::build_bin_and_run("aot.float-minmax-pass", "example/float-minmax-pass.rs", &[]),
+ TestCase::build_bin_and_run("aot.mod_bench", "example/mod_bench.rs", &[]),
+ TestCase::build_bin_and_run("aot.issue-72793", "example/issue-72793.rs", &[]),
];
pub(crate) static RAND_REPO: GitRepo =
GitRepo::github("rust-random", "rand", "0f933f9c7176e53b2a3c7952ded484e1783f0bf1", "rand");
-static RAND: CargoProject = CargoProject::new(&RAND_REPO.source_dir(), "rand");
+pub(crate) static RAND: CargoProject = CargoProject::new(&RAND_REPO.source_dir(), "rand");
pub(crate) static REGEX_REPO: GitRepo =
GitRepo::github("rust-lang", "regex", "341f207c1071f7290e3f228c710817c280c8dca1", "regex");
-static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir(), "regex");
+pub(crate) static REGEX: CargoProject = CargoProject::new(®EX_REPO.source_dir(), "regex");
pub(crate) static PORTABLE_SIMD_REPO: GitRepo = GitRepo::github(
"rust-lang",
"portable-simd",
- "d5cd4a8112d958bd3a252327e0d069a6363249bd",
+ "582239ac3b32007613df04d7ffa78dc30f4c5645",
"portable-simd",
);
-static PORTABLE_SIMD: CargoProject =
+pub(crate) static PORTABLE_SIMD: CargoProject =
CargoProject::new(&PORTABLE_SIMD_REPO.source_dir(), "portable_simd");
-pub(crate) static SIMPLE_RAYTRACER_REPO: GitRepo = GitRepo::github(
- "ebobby",
- "simple-raytracer",
- "804a7a21b9e673a482797aa289a18ed480e4d813",
- "<none>",
-);
-
-pub(crate) static SIMPLE_RAYTRACER: CargoProject =
- CargoProject::new(&SIMPLE_RAYTRACER_REPO.source_dir(), "simple_raytracer");
-
-static LIBCORE_TESTS: CargoProject =
- CargoProject::new(&RelPath::BUILD_SYSROOT.join("sysroot_src/library/core/tests"), "core_tests");
+pub(crate) static LIBCORE_TESTS: CargoProject =
+ CargoProject::new(&SYSROOT_SRC.join("library/core/tests"), "core_tests");
const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
- TestCase::new("test.rust-random/rand", &|runner| {
- spawn_and_wait(RAND.clean(&runner.target_compiler.cargo, &runner.dirs));
+ TestCase::custom("test.rust-random/rand", &|runner| {
+ RAND.clean(&runner.dirs);
if runner.is_native {
eprintln!("[TEST] rust-random/rand");
spawn_and_wait(build_cmd);
}
}),
- TestCase::new("bench.simple-raytracer", &|runner| {
- let run_runs = env::var("RUN_RUNS").unwrap_or("10".to_string()).parse().unwrap();
-
- if runner.is_native {
- eprintln!("[BENCH COMPILE] ebobby/simple-raytracer");
- let cargo_clif = RelPath::DIST
- .to_path(&runner.dirs)
- .join(get_wrapper_file_name("cargo-clif", "bin"));
- let manifest_path = SIMPLE_RAYTRACER.manifest_path(&runner.dirs);
- let target_dir = SIMPLE_RAYTRACER.target_dir(&runner.dirs);
-
- let clean_cmd = format!(
- "cargo clean --manifest-path {manifest_path} --target-dir {target_dir}",
- manifest_path = manifest_path.display(),
- target_dir = target_dir.display(),
- );
- let llvm_build_cmd = format!(
- "cargo build --manifest-path {manifest_path} --target-dir {target_dir}",
- manifest_path = manifest_path.display(),
- target_dir = target_dir.display(),
- );
- let clif_build_cmd = format!(
- "{cargo_clif} build --manifest-path {manifest_path} --target-dir {target_dir}",
- cargo_clif = cargo_clif.display(),
- manifest_path = manifest_path.display(),
- target_dir = target_dir.display(),
- );
-
- let bench_compile =
- hyperfine_command(1, run_runs, Some(&clean_cmd), &llvm_build_cmd, &clif_build_cmd);
-
- spawn_and_wait(bench_compile);
-
- eprintln!("[BENCH RUN] ebobby/simple-raytracer");
- fs::copy(
- target_dir.join("debug").join("main"),
- RelPath::BUILD.to_path(&runner.dirs).join("raytracer_cg_clif"),
- )
- .unwrap();
-
- let mut bench_run =
- hyperfine_command(0, run_runs, None, "./raytracer_cg_llvm", "./raytracer_cg_clif");
- bench_run.current_dir(RelPath::BUILD.to_path(&runner.dirs));
- spawn_and_wait(bench_run);
- } else {
- spawn_and_wait(SIMPLE_RAYTRACER.clean(&runner.target_compiler.cargo, &runner.dirs));
- eprintln!("[BENCH COMPILE] ebobby/simple-raytracer (skipped)");
- eprintln!("[COMPILE] ebobby/simple-raytracer");
- spawn_and_wait(SIMPLE_RAYTRACER.build(&runner.target_compiler, &runner.dirs));
- eprintln!("[BENCH RUN] ebobby/simple-raytracer (skipped)");
- }
+ TestCase::custom("test.simple-raytracer", &|runner| {
+ SIMPLE_RAYTRACER.clean(&runner.dirs);
+ spawn_and_wait(SIMPLE_RAYTRACER.build(&runner.target_compiler, &runner.dirs));
}),
- TestCase::new("test.libcore", &|runner| {
- spawn_and_wait(LIBCORE_TESTS.clean(&runner.host_compiler.cargo, &runner.dirs));
+ TestCase::custom("test.libcore", &|runner| {
+ LIBCORE_TESTS.clean(&runner.dirs);
if runner.is_native {
spawn_and_wait(LIBCORE_TESTS.test(&runner.target_compiler, &runner.dirs));
spawn_and_wait(build_cmd);
}
}),
- TestCase::new("test.regex-shootout-regex-dna", &|runner| {
- spawn_and_wait(REGEX.clean(&runner.target_compiler.cargo, &runner.dirs));
+ TestCase::custom("test.regex-shootout-regex-dna", &|runner| {
+ REGEX.clean(&runner.dirs);
// newer aho_corasick versions throw a deprecation warning
let lint_rust_flags = format!("{} --cap-lints warn", runner.target_compiler.rustflags);
REGEX.source_dir(&runner.dirs).join("examples").join("regexdna-input.txt"),
)
.unwrap();
- let expected_path =
- REGEX.source_dir(&runner.dirs).join("examples").join("regexdna-output.txt");
- let expected = fs::read_to_string(&expected_path).unwrap();
+ let expected = fs::read_to_string(
+ REGEX.source_dir(&runner.dirs).join("examples").join("regexdna-output.txt"),
+ )
+ .unwrap();
let output = spawn_and_wait_with_input(run_cmd, input);
// Make sure `[codegen mono items] start` doesn't poison the diff
let output_matches = expected.lines().eq(output.lines());
if !output_matches {
- let res_path = REGEX.source_dir(&runner.dirs).join("res.txt");
- fs::write(&res_path, &output).unwrap();
-
- if cfg!(windows) {
- println!("Output files don't match!");
- println!("Expected Output:\n{}", expected);
- println!("Actual Output:\n{}", output);
- } else {
- let mut diff = Command::new("diff");
- diff.arg("-u");
- diff.arg(res_path);
- diff.arg(expected_path);
- spawn_and_wait(diff);
- }
+ println!("Output files don't match!");
+ println!("Expected Output:\n{}", expected);
+ println!("Actual Output:\n{}", output);
std::process::exit(1);
}
}
}),
- TestCase::new("test.regex", &|runner| {
- spawn_and_wait(REGEX.clean(&runner.host_compiler.cargo, &runner.dirs));
+ TestCase::custom("test.regex", &|runner| {
+ REGEX.clean(&runner.dirs);
// newer aho_corasick versions throw a deprecation warning
let lint_rust_flags = format!("{} --cap-lints warn", runner.target_compiler.rustflags);
spawn_and_wait(build_cmd);
}
}),
- TestCase::new("test.portable-simd", &|runner| {
- spawn_and_wait(PORTABLE_SIMD.clean(&runner.host_compiler.cargo, &runner.dirs));
+ TestCase::custom("test.portable-simd", &|runner| {
+ PORTABLE_SIMD.clean(&runner.dirs);
let mut build_cmd = PORTABLE_SIMD.build(&runner.target_compiler, &runner.dirs);
build_cmd.arg("--all-targets");
channel: &str,
sysroot_kind: SysrootKind,
cg_clif_dylib: &Path,
- host_triple: &str,
- target_triple: &str,
+ bootstrap_host_compiler: &Compiler,
+ target_triple: String,
) {
- let runner = TestRunner::new(dirs.clone(), host_triple.to_string(), target_triple.to_string());
-
if config::get_bool("testsuite.no_sysroot") {
- build_sysroot::build_sysroot(
+ let target_compiler = build_sysroot::build_sysroot(
dirs,
channel,
SysrootKind::None,
cg_clif_dylib,
- &host_triple,
- &target_triple,
+ bootstrap_host_compiler,
+ target_triple.clone(),
);
+ let runner =
+ TestRunner::new(dirs.clone(), target_compiler, get_host_triple() == target_triple);
+
BUILD_EXAMPLE_OUT_DIR.ensure_fresh(dirs);
runner.run_testsuite(NO_SYSROOT_SUITE);
} else {
let run_extended_sysroot = config::get_bool("testsuite.extended_sysroot");
if run_base_sysroot || run_extended_sysroot {
- build_sysroot::build_sysroot(
+ let target_compiler = build_sysroot::build_sysroot(
dirs,
channel,
sysroot_kind,
cg_clif_dylib,
- &host_triple,
- &target_triple,
+ bootstrap_host_compiler,
+ target_triple.clone(),
);
- }
- if run_base_sysroot {
- runner.run_testsuite(BASE_SYSROOT_SUITE);
- } else {
- eprintln!("[SKIP] base_sysroot tests");
- }
+ let runner =
+ TestRunner::new(dirs.clone(), target_compiler, get_host_triple() == target_triple);
- if run_extended_sysroot {
- runner.run_testsuite(EXTENDED_SYSROOT_SUITE);
- } else {
- eprintln!("[SKIP] extended_sysroot tests");
+ if run_base_sysroot {
+ runner.run_testsuite(BASE_SYSROOT_SUITE);
+ } else {
+ eprintln!("[SKIP] base_sysroot tests");
+ }
+
+ if run_extended_sysroot {
+ runner.run_testsuite(EXTENDED_SYSROOT_SUITE);
+ } else {
+ eprintln!("[SKIP] extended_sysroot tests");
+ }
}
}
is_native: bool,
jit_supported: bool,
dirs: Dirs,
- host_compiler: Compiler,
target_compiler: Compiler,
}
impl TestRunner {
- pub fn new(dirs: Dirs, host_triple: String, target_triple: String) -> Self {
- let is_native = host_triple == target_triple;
- let jit_supported =
- target_triple.contains("x86_64") && is_native && !host_triple.contains("windows");
-
- let rustc_clif =
- RelPath::DIST.to_path(&dirs).join(get_wrapper_file_name("rustc-clif", "bin"));
- let rustdoc_clif =
- RelPath::DIST.to_path(&dirs).join(get_wrapper_file_name("rustdoc-clif", "bin"));
-
- let mut rustflags = env::var("RUSTFLAGS").ok().unwrap_or("".to_string());
- let mut runner = vec![];
-
- if !is_native {
- match target_triple.as_str() {
- "aarch64-unknown-linux-gnu" => {
- // We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
- rustflags = format!("-Clinker=aarch64-linux-gnu-gcc{}", rustflags);
- runner = vec![
- "qemu-aarch64".to_owned(),
- "-L".to_owned(),
- "/usr/aarch64-linux-gnu".to_owned(),
- ];
- }
- "s390x-unknown-linux-gnu" => {
- // We are cross-compiling for s390x. Use the correct linker and run tests in qemu.
- rustflags = format!("-Clinker=s390x-linux-gnu-gcc{}", rustflags);
- runner = vec![
- "qemu-s390x".to_owned(),
- "-L".to_owned(),
- "/usr/s390x-linux-gnu".to_owned(),
- ];
- }
- "x86_64-pc-windows-gnu" => {
- // We are cross-compiling for Windows. Run tests in wine.
- runner = vec!["wine".to_owned()];
- }
- _ => {
- println!("Unknown non-native platform");
- }
- }
+ pub fn new(dirs: Dirs, mut target_compiler: Compiler, is_native: bool) -> Self {
+ if let Ok(rustflags) = env::var("RUSTFLAGS") {
+ target_compiler.rustflags.push(' ');
+ target_compiler.rustflags.push_str(&rustflags);
+ }
+ if let Ok(rustdocflags) = env::var("RUSTDOCFLAGS") {
+ target_compiler.rustdocflags.push(' ');
+ target_compiler.rustdocflags.push_str(&rustdocflags);
}
// FIXME fix `#[linkage = "extern_weak"]` without this
- if target_triple.contains("darwin") {
- rustflags = format!("{} -Clink-arg=-undefined -Clink-arg=dynamic_lookup", rustflags);
+ if target_compiler.triple.contains("darwin") {
+ target_compiler.rustflags.push_str(" -Clink-arg=-undefined -Clink-arg=dynamic_lookup");
}
- let host_compiler = Compiler {
- cargo: get_cargo_path(),
- rustc: rustc_clif.clone(),
- rustdoc: rustdoc_clif.clone(),
- rustflags: String::new(),
- rustdocflags: String::new(),
- triple: host_triple,
- runner: vec![],
- };
-
- let target_compiler = Compiler {
- cargo: get_cargo_path(),
- rustc: rustc_clif,
- rustdoc: rustdoc_clif,
- rustflags: rustflags.clone(),
- rustdocflags: rustflags,
- triple: target_triple,
- runner,
- };
-
- Self { is_native, jit_supported, dirs, host_compiler, target_compiler }
+ let jit_supported = is_native
+ && target_compiler.triple.contains("x86_64")
+ && !target_compiler.triple.contains("windows");
+
+ Self { is_native, jit_supported, dirs, target_compiler }
}
pub fn run_testsuite(&self, tests: &[TestCase]) {
- for &TestCase { config, func } in tests {
+ for TestCase { config, cmd } in tests {
let (tag, testname) = config.split_once('.').unwrap();
let tag = tag.to_uppercase();
let is_jit_test = tag == "JIT";
eprintln!("[{tag}] {testname}");
}
- func(self);
+ match *cmd {
+ TestCaseCmd::Custom { func } => func(self),
+ TestCaseCmd::BuildLib { source, crate_types } => {
+ self.run_rustc([source, "--crate-type", crate_types]);
+ }
+ TestCaseCmd::BuildBinAndRun { source, args } => {
+ self.run_rustc([source]);
+ self.run_out_command(
+ source.split('/').last().unwrap().split('.').next().unwrap(),
+ args,
+ );
+ }
+ TestCaseCmd::JitBin { source, args } => {
+ let mut jit_cmd = self.rustc_command([
+ "-Zunstable-options",
+ "-Cllvm-args=mode=jit",
+ "-Cprefer-dynamic",
+ source,
+ "--cfg",
+ "jit",
+ ]);
+ if !args.is_empty() {
+ jit_cmd.env("CG_CLIF_JIT_ARGS", args);
+ }
+ spawn_and_wait(jit_cmd);
+
+ eprintln!("[JIT-lazy] {testname}");
+ let mut jit_cmd = self.rustc_command([
+ "-Zunstable-options",
+ "-Cllvm-args=mode=jit-lazy",
+ "-Cprefer-dynamic",
+ source,
+ "--cfg",
+ "jit",
+ ]);
+ if !args.is_empty() {
+ jit_cmd.env("CG_CLIF_JIT_ARGS", args);
+ }
+ spawn_and_wait(jit_cmd);
+ }
+ }
}
}
cmd.arg("--out-dir");
cmd.arg(format!("{}", BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs).display()));
cmd.arg("-Cdebuginfo=2");
+ cmd.arg("--target");
+ cmd.arg(&self.target_compiler.triple);
+ cmd.arg("-Cpanic=abort");
cmd.args(args);
cmd
}
spawn_and_wait(self.rustc_command(args));
}
- fn run_out_command<'a, I>(&self, name: &str, args: I)
- where
- I: IntoIterator<Item = &'a str>,
- {
+ fn run_out_command<'a>(&self, name: &str, args: &[&str]) {
let mut full_cmd = vec![];
// Prepend the RUN_WRAPPER's
BUILD_EXAMPLE_OUT_DIR.to_path(&self.dirs).join(name).to_str().unwrap().to_string(),
);
- for arg in args.into_iter() {
+ for arg in args {
full_cmd.push(arg.to_string());
}
--- /dev/null
+The build system of cg_clif.
+
+USAGE:
+ ./y.rs prepare [--out-dir DIR]
+ ./y.rs build [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
+ ./y.rs test [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
+ ./y.rs abi-cafe [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
+ ./y.rs bench [--debug] [--sysroot none|clif|llvm] [--out-dir DIR] [--no-unstable-features]
+
+OPTIONS:
+ --debug
+ Build cg_clif and the standard library in debug mode rather than release mode.
+ Warning: An unoptimized cg_clif is very slow.
+
+ --sysroot none|clif|llvm
+ Which sysroot libraries to use:
+ `none` will not include any standard library in the sysroot.
+ `clif` will build the standard library using Cranelift.
+ `llvm` will use the pre-compiled standard library of rustc which is compiled with LLVM.
+
+ --out-dir DIR
+ Specify the directory in which the download, build and dist directories are stored.
+ By default this is the working directory.
+
+ --no-unstable-features
+ Some features are not yet ready for production usage. This option will disable these
+ features. This includes the JIT mode and inline assembly support.
+
+REQUIREMENTS:
+ * Rustup: The build system has a hard coded dependency on rustup to install the right nightly
+ version and make sure it is used where necessary.
+ * Git: `./y.rs prepare` uses git for applying patches and on Windows for downloading test repos.
+ * Curl and tar (non-Windows only): Used by `./y.rs prepare` to download a single commit for
+ repos. Git will be used to clone the whole repo when using Windows.
+ * [Hyperfine](https://github.com/sharkdp/hyperfine/): Used for benchmarking with `./y.rs bench`.
use std::env;
use std::fs;
-use std::io::Write;
+use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Command, Stdio};
use super::path::{Dirs, RelPath};
-use super::rustc_info::{get_cargo_path, get_host_triple, get_rustc_path, get_rustdoc_path};
+use super::rustc_info::{get_cargo_path, get_rustc_path, get_rustdoc_path};
+#[derive(Clone, Debug)]
pub(crate) struct Compiler {
pub(crate) cargo: PathBuf,
pub(crate) rustc: PathBuf,
}
impl Compiler {
- pub(crate) fn host() -> Compiler {
+ pub(crate) fn bootstrap_with_triple(triple: String) -> Compiler {
Compiler {
cargo: get_cargo_path(),
rustc: get_rustc_path(),
rustdoc: get_rustdoc_path(),
rustflags: String::new(),
rustdocflags: String::new(),
- triple: get_host_triple(),
+ triple,
runner: vec![],
}
}
- pub(crate) fn with_triple(triple: String) -> Compiler {
- Compiler {
- cargo: get_cargo_path(),
- rustc: get_rustc_path(),
- rustdoc: get_rustdoc_path(),
- rustflags: String::new(),
- rustdocflags: String::new(),
- triple,
- runner: vec![],
+ pub(crate) fn set_cross_linker_and_runner(&mut self) {
+ match self.triple.as_str() {
+ "aarch64-unknown-linux-gnu" => {
+ // We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
+ self.rustflags += " -Clinker=aarch64-linux-gnu-gcc";
+ self.rustdocflags += " -Clinker=aarch64-linux-gnu-gcc";
+ self.runner = vec![
+ "qemu-aarch64".to_owned(),
+ "-L".to_owned(),
+ "/usr/aarch64-linux-gnu".to_owned(),
+ ];
+ }
+ "s390x-unknown-linux-gnu" => {
+ // We are cross-compiling for s390x. Use the correct linker and run tests in qemu.
+ self.rustflags += " -Clinker=s390x-linux-gnu-gcc";
+ self.rustdocflags += " -Clinker=s390x-linux-gnu-gcc";
+ self.runner = vec![
+ "qemu-s390x".to_owned(),
+ "-L".to_owned(),
+ "/usr/s390x-linux-gnu".to_owned(),
+ ];
+ }
+ "x86_64-pc-windows-gnu" => {
+ // We are cross-compiling for Windows. Run tests in wine.
+ self.runner = vec!["wine".to_owned()];
+ }
+ _ => {
+ println!("Unknown non-native platform");
+ }
}
}
}
RelPath::BUILD.join(self.target).to_path(dirs)
}
+ #[must_use]
fn base_cmd(&self, command: &str, cargo: &Path, dirs: &Dirs) -> Command {
let mut cmd = Command::new(cargo);
.arg("--manifest-path")
.arg(self.manifest_path(dirs))
.arg("--target-dir")
- .arg(self.target_dir(dirs));
+ .arg(self.target_dir(dirs))
+ .arg("--frozen");
cmd
}
+ #[must_use]
fn build_cmd(&self, command: &str, compiler: &Compiler, dirs: &Dirs) -> Command {
let mut cmd = self.base_cmd(command, &compiler.cargo, dirs);
cmd
}
- #[must_use]
- pub(crate) fn clean(&self, cargo: &Path, dirs: &Dirs) -> Command {
- self.base_cmd("clean", cargo, dirs)
+ pub(crate) fn clean(&self, dirs: &Dirs) {
+ let _ = fs::remove_dir_all(self.target_dir(dirs));
}
#[must_use]
bench
}
+#[must_use]
+pub(crate) fn git_command<'a>(repo_dir: impl Into<Option<&'a Path>>, cmd: &str) -> Command {
+ let mut git_cmd = Command::new("git");
+ git_cmd
+ .arg("-c")
+ .arg("user.name=Dummy")
+ .arg("-c")
+ .arg("user.email=dummy@example.com")
+ .arg("-c")
+ .arg("core.autocrlf=false")
+ .arg(cmd);
+ if let Some(repo_dir) = repo_dir.into() {
+ git_cmd.current_dir(repo_dir);
+ }
+ git_cmd
+}
+
#[track_caller]
pub(crate) fn try_hard_link(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
let src = src.as_ref();
}
}
+// Based on the retry function in rust's src/ci/shared.sh
+#[track_caller]
+pub(crate) fn retry_spawn_and_wait(tries: u64, mut cmd: Command) {
+ for i in 1..tries + 1 {
+ if i != 1 {
+ println!("Command failed. Attempt {i}/{tries}:");
+ }
+ if cmd.spawn().unwrap().wait().unwrap().success() {
+ return;
+ }
+ std::thread::sleep(std::time::Duration::from_secs(i * 5));
+ }
+ println!("The command has failed after {tries} attempts.");
+ process::exit(1);
+}
+
#[track_caller]
pub(crate) fn spawn_and_wait_with_input(mut cmd: Command, input: String) -> String {
let mut child = cmd
String::from_utf8(output.stdout).unwrap()
}
+pub(crate) fn remove_dir_if_exists(path: &Path) {
+ match fs::remove_dir_all(&path) {
+ Ok(()) => {}
+ Err(err) if err.kind() == io::ErrorKind::NotFound => {}
+ Err(err) => panic!("Failed to remove {path}: {err}", path = path.display()),
+ }
+}
+
pub(crate) fn copy_dir_recursively(from: &Path, to: &Path) {
for entry in fs::read_dir(from).unwrap() {
let entry = entry.unwrap();
#!/usr/bin/env bash
set -e
-rm -rf build_sysroot/{sysroot_src/,target/,compiler-builtins/,rustc_version}
-rm -rf target/ build/ dist/ perf.data{,.old} y.bin
-rm -rf download/
+rm -rf target/ download/ build/ dist/ y.bin y.bin.dSYM y.exe y.pdb
# Kept for now in case someone updates their checkout of cg_clif before running clean_all.sh
# FIXME remove at some point in the future
rm -rf rand/ regex/ simple-raytracer/ portable-simd/ abi-checker/ abi-cafe/
+rm -rf build_sysroot/{sysroot_src/,target/,compiler-builtins/,rustc_version}
testsuite.extended_sysroot
test.rust-random/rand
-bench.simple-raytracer
+test.simple-raytracer
test.libcore
test.regex-shootout-regex-dna
test.regex
test.portable-simd
-
-testsuite.abi-cafe
+++ /dev/null
-From b742f03694b920cc14400727d54424e8e1b60928 Mon Sep 17 00:00:00 2001
-From: bjorn3 <bjorn3@users.noreply.github.com>
-Date: Thu, 18 Nov 2021 19:28:40 +0100
-Subject: [PATCH] Disable unsupported tests
-
----
- crates/core_simd/src/elements/int.rs | 8 ++++++++
- crates/core_simd/src/elements/uint.rs | 4 ++++
- crates/core_simd/src/masks/full_masks.rs | 6 ++++++
- crates/core_simd/src/vector.rs | 2 ++
- crates/core_simd/tests/masks.rs | 3 ---
- 5 files changed, 20 insertions(+), 3 deletions(-)
-
-diff --git a/crates/core_simd/src/vector.rs b/crates/core_simd/src/vector.rs
-index e8e8f68..7173c24 100644
---- a/crates/core_simd/src/vector.rs
-+++ b/crates/core_simd/src/vector.rs
-@@ -250,6 +250,7 @@ where
- unsafe { intrinsics::simd_cast(self) }
- }
-
-+ /*
- /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector.
- /// If an index is out-of-bounds, the lane is instead selected from the `or` vector.
- ///
-@@ -473,6 +474,7 @@ where
- // Cleared ☢️ *mut T Zone
- }
- }
-+ */
- }
-
- impl<T, const LANES: usize> Copy for Simd<T, LANES>
---
-2.25.1
index 0000000..46fd999
--- /dev/null
+++ b/library/core/tests/Cargo.toml
-@@ -0,0 +1,11 @@
+@@ -0,0 +1,12 @@
+[package]
+name = "core"
+version = "0.0.0"
+path = "lib.rs"
+
+[dependencies]
-+rand = "0.7"
++rand = { version = "0.8.5", default-features = false }
++rand_xorshift = { version = "0.3.0", default-features = false }
--
2.21.0 (Apple Git-122)
[toolchain]
-channel = "nightly-2022-12-13"
+channel = "nightly-2023-01-20"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
env::set_var("RUSTDOCFLAGS", env::var("RUSTDOCFLAGS").unwrap_or(String::new()) + &rustflags);
// Ensure that the right toolchain is used
- env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));
+ env::set_var("RUSTUP_TOOLCHAIN", env!("TOOLCHAIN_NAME"));
let args: Vec<_> = match env::args().nth(1).as_deref() {
Some("jit") => {
}
// Ensure that the right toolchain is used
- env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));
+ env::set_var("RUSTUP_TOOLCHAIN", env!("TOOLCHAIN_NAME"));
#[cfg(unix)]
Command::new("rustc").args(args).exec();
}
// Ensure that the right toolchain is used
- env::set_var("RUSTUP_TOOLCHAIN", env!("RUSTUP_TOOLCHAIN"));
+ env::set_var("RUSTUP_TOOLCHAIN", env!("TOOLCHAIN_NAME"));
#[cfg(unix)]
Command::new("rustdoc").args(args).exec();
done
./clean_all.sh
- ./y.rs prepare
- (cd build_sysroot && cargo update)
+ ./y.rs prepare
+ (cd download/sysroot && cargo update && cargo fetch && cp Cargo.lock ../../build_sysroot/)
;;
"commit")
git add rust-toolchain build_sysroot/Cargo.lock
git checkout -- .
git checkout "$(rustc -V | cut -d' ' -f3 | tr -d '(')"
-git am ../patches/*-sysroot-*.patch
+git -c user.name=Dummy -c user.email=dummy@example.com am ../patches/*-sysroot-*.patch
git apply - <<EOF
diff --git a/library/alloc/Cargo.toml b/library/alloc/Cargo.toml
+compiler_builtins = { version = "0.1.66", features = ['rustc-dep-of-std', 'no-asm'] }
[dev-dependencies]
- rand = "0.7"
- rand_xorshift = "0.2"
+ rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
+ rand_xorshift = "0.3.0"
EOF
cat > config.toml <<EOF
# FIXME remove once inline asm is fully supported
export RUSTFLAGS="$RUSTFLAGS --cfg=rustix_use_libc"
-export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd build_sysroot/sysroot_src; pwd)"
+export CFG_VIRTUAL_RUST_SOURCE_BASE_DIR="$(cd download/sysroot/sysroot_src; pwd)"
# Allow the testsuite to use llvm tools
host_triple=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
command -v rg >/dev/null 2>&1 || cargo install ripgrep
rm -r tests/ui/{extern/,unsized-locals/,lto/,linkage*} || true
-for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" tests/{ui,incremental}); do
+for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" tests/{codegen-units,ui,incremental}); do
rm $test
done
# requires compiling with -Cpanic=unwind
rm -r tests/ui/macros/rfc-2011-nicer-assert-messages/
rm -r tests/run-make/test-benches
+rm tests/ui/test-attrs/test-type.rs
# vendor intrinsics
rm tests/ui/sse2.rs # cpuid not supported, so sse2 not detected
rm tests/ui/intrinsics/const-eval-select-x86_64.rs # requires x86_64 vendor intrinsics
rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant"
-rm tests/ui/simd/intrinsic/generic-bitmask-pass.rs # simd_bitmask unimplemented
-rm tests/ui/simd/intrinsic/generic-as.rs # simd_as unimplemented
-rm tests/ui/simd/intrinsic/generic-arithmetic-saturating-pass.rs # simd_saturating_add unimplemented
rm tests/ui/simd/intrinsic/float-math-pass.rs # simd_fcos unimplemented
-rm tests/ui/simd/intrinsic/generic-gather-pass.rs # simd_gather unimplemented
-rm tests/ui/simd/intrinsic/generic-select-pass.rs # simd_select_bitmask unimplemented
-rm tests/ui/simd/issue-85915-simd-ptrs.rs # simd_gather unimplemented
-rm tests/ui/simd/issue-89193.rs # simd_gather unimplemented
-rm tests/ui/simd/simd-bitmask.rs # simd_bitmask unimplemented
# exotic linkages
rm tests/ui/issues/issue-33992.rs # unsupported linkages
rm tests/ui/target-feature/missing-plusminus.rs # error not implemented
rm tests/ui/fn/dyn-fn-alignment.rs # wants a 256 byte alignment
rm -r tests/run-make/emit-named-files # requires full --emit support
-rm tests/ui/abi/stack-probes.rs # stack probes not yet implemented
-rm tests/ui/simd/intrinsic/ptr-cast.rs # simd_expose_addr intrinsic unimplemented
rm -r tests/run-make/repr128-dwarf # debuginfo test
-rm tests/codegen-units/item-collection/asm-sym.rs # requires support for sym in asm!()
# optimization tests
# ==================
rm tests/ui/consts/issue-33537.rs # same
rm tests/ui/layout/valid_range_oob.rs # different ICE message
+rm tests/ui/consts/issue-miri-1910.rs # different error message
+rm tests/ui/consts/offset_ub.rs # same
+rm tests/ui/intrinsics/panic-uninitialized-zeroed.rs # same
+rm tests/ui/lint/lint-const-item-mutation.rs # same
+rm tests/ui/pattern/usefulness/doc-hidden-non-exhaustive.rs # same
+rm tests/ui/suggestions/derive-trait-for-method-call.rs # same
+rm tests/ui/typeck/issue-46112.rs # same
+
+rm tests/ui/proc-macro/crt-static.rs # extra warning about -Cpanic=abort for proc macros
+rm tests/ui/proc-macro/proc-macro-deprecated-attr.rs # same
+rm tests/ui/proc-macro/quote-debug.rs # same
+rm tests/ui/proc-macro/no-missing-docs.rs # same
+rm tests/ui/rust-2018/proc-macro-crate-in-paths.rs # same
+
# doesn't work due to the way the rustc test suite is invoked.
# should work when using ./x.py test the way it is intended
# ============================================================
# ============
rm tests/incremental/spike-neg1.rs # errors out for some reason
rm tests/incremental/spike-neg2.rs # same
-rm tests/ui/issues/issue-74564-if-expr-stack-overflow.rs # gives a stackoverflow before the backend runs
-rm tests/ui/mir/ssa-analysis-regression-50041.rs # produces ICE
-rm tests/ui/type-alias-impl-trait/assoc-projection-ice.rs # produces ICE
rm tests/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors
-rm tests/ui/runtime/out-of-stack.rs # SIGSEGV instead of SIGABRT for some reason (#1301)
+rm tests/ui/simd/intrinsic/generic-as.rs # crash when accessing vector type filed (#1318)
+rm tests/ui/simd/simd-bitmask.rs # crash
+
+rm tests/ui/dyn-star/dyn-star-to-dyn.rs
+rm tests/ui/dyn-star/dispatch-on-pin-mut.rs
# bugs in the test suite
# ======================
rm tests/ui/backtrace.rs # TODO warning
rm tests/ui/simple_global_asm.rs # TODO add needs-asm-support
-rm tests/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout
-# not sure if this is actually a bug in the test suite, but the symbol list shows the function without leading _ for some reason
-rm -r tests/run-make/native-link-modifier-bundle
rm tests/ui/process/nofile-limit.rs # TODO some AArch64 linking issue
-rm tests/ui/dyn-star/dispatch-on-pin-mut.rs # TODO failed assertion in vtable::get_ptr_and_method_ref
rm tests/ui/stdio-is-blocking.rs # really slow with unoptimized libstd
use cranelift_module::ModuleError;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::ty::layout::FnAbiOf;
+use rustc_session::Session;
use rustc_target::abi::call::{Conv, FnAbi};
use rustc_target::spec::abi::Abi;
default_call_conv: CallConv,
fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
) -> Signature {
- let call_conv = conv_to_call_conv(fn_abi.conv, default_call_conv);
+ let call_conv = conv_to_call_conv(tcx.sess, fn_abi.conv, default_call_conv);
let inputs = fn_abi.args.iter().map(|arg_abi| arg_abi.get_abi_param(tcx).into_iter()).flatten();
Signature { params, returns, call_conv }
}
-pub(crate) fn conv_to_call_conv(c: Conv, default_call_conv: CallConv) -> CallConv {
+pub(crate) fn conv_to_call_conv(sess: &Session, c: Conv, default_call_conv: CallConv) -> CallConv {
match c {
Conv::Rust | Conv::C => default_call_conv,
Conv::RustCold => CallConv::Cold,
Conv::X86_64SysV => CallConv::SystemV,
Conv::X86_64Win64 => CallConv::WindowsFastcall,
- Conv::ArmAapcs
- | Conv::CCmseNonSecureCall
- | Conv::Msp430Intr
+
+ // Should already get a back compat warning
+ Conv::X86Fastcall | Conv::X86Stdcall | Conv::X86ThisCall | Conv::X86VectorCall => {
+ default_call_conv
+ }
+
+ Conv::X86Intr => sess.fatal("x86-interrupt call conv not yet implemented"),
+
+ Conv::ArmAapcs => sess.fatal("aapcs call conv not yet implemented"),
+ Conv::CCmseNonSecureCall => {
+ sess.fatal("C-cmse-nonsecure-call call conv is not yet implemented");
+ }
+
+ Conv::Msp430Intr
| Conv::PtxKernel
- | Conv::X86Fastcall
- | Conv::X86Intr
- | Conv::X86Stdcall
- | Conv::X86ThisCall
- | Conv::X86VectorCall
| Conv::AmdGpuKernel
| Conv::AvrInterrupt
- | Conv::AvrNonBlockingInterrupt => todo!("{:?}", c),
+ | Conv::AvrNonBlockingInterrupt => {
+ unreachable!("tried to use {c:?} call conv which only exists on an unsupported target");
+ }
}
}
layout: TyAndLayout<'tcx>,
is_ssa: bool,
) -> CPlace<'tcx> {
+ if layout.is_unsized() {
+ fx.tcx.sess.span_fatal(
+ fx.mir.local_decls[local].source_info.span,
+ "unsized locals are not yet supported",
+ );
+ }
let place = if is_ssa {
if let rustc_target::abi::Abi::ScalarPair(_, _) = layout.abi {
CPlace::new_var_pair(fx, local, layout)
};
tcx.sess.time("codegen clif ir", || codegen_fn_body(&mut fx, start_block));
+ fx.bcx.seal_all_blocks();
+ fx.bcx.finalize();
// Recover all necessary data from fx, before accessing func will prevent future access to it.
let symbol_name = fx.symbol_name;
let source_info = bb_data.terminator().source_info;
fx.set_debug_loc(source_info);
+ let _print_guard =
+ crate::PrintOnPanic(|| format!("terminator {:?}", bb_data.terminator().kind));
+
match &bb_data.terminator().kind {
TerminatorKind::Goto { target } => {
if let TerminatorKind::Return = fx.mir[*target].terminator().kind {
*destination,
);
}
- TerminatorKind::Resume | TerminatorKind::Abort => {
+ TerminatorKind::Abort => {
+ codegen_panic_cannot_unwind(fx, source_info);
+ }
+ TerminatorKind::Resume => {
// FIXME implement unwinding
fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
}
}
};
}
-
- fx.bcx.seal_all_blocks();
- fx.bcx.finalize();
}
fn codegen_stmt<'tcx>(
StatementKind::StorageLive(_)
| StatementKind::StorageDead(_)
| StatementKind::Deinit(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop
| StatementKind::FakeRead(..)
| StatementKind::Retag { .. }
codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, source_info.span);
}
-pub(crate) fn codegen_panic_inner<'tcx>(
+pub(crate) fn codegen_panic_nounwind<'tcx>(
+ fx: &mut FunctionCx<'_, '_, 'tcx>,
+ msg_str: &str,
+ source_info: mir::SourceInfo,
+) {
+ let msg_ptr = fx.anonymous_str(msg_str);
+ let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
+ let args = [msg_ptr, msg_len];
+
+ codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, source_info.span);
+}
+
+pub(crate) fn codegen_panic_cannot_unwind<'tcx>(
+ fx: &mut FunctionCx<'_, '_, 'tcx>,
+ source_info: mir::SourceInfo,
+) {
+ let args = [];
+
+ codegen_panic_inner(fx, rustc_hir::LangItem::PanicCannotUnwind, &args, source_info.span);
+}
+
+fn codegen_panic_inner<'tcx>(
fx: &mut FunctionCx<'_, '_, 'tcx>,
lang_item: rustc_hir::LangItem,
args: &[Value],
fx.lib_call(
&*symbol_name,
- vec![
- AbiParam::new(fx.pointer_type),
- AbiParam::new(fx.pointer_type),
- AbiParam::new(fx.pointer_type),
- ],
+ args.iter().map(|&arg| AbiParam::new(fx.bcx.func.dfg.value_type(arg))).collect(),
vec![],
args,
);
},
Primitive::F32 => types::F32,
Primitive::F64 => types::F64,
- Primitive::Pointer => pointer_ty(tcx),
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ Primitive::Pointer(_) => pointer_ty(tcx),
}
}
}
}
+pub(crate) fn codegen_bitcast(fx: &mut FunctionCx<'_, '_, '_>, dst_ty: Type, val: Value) -> Value {
+ let mut flags = MemFlags::new();
+ flags.set_endianness(match fx.tcx.data_layout.endian {
+ rustc_target::abi::Endian::Big => cranelift_codegen::ir::Endianness::Big,
+ rustc_target::abi::Endian::Little => cranelift_codegen::ir::Endianness::Little,
+ });
+ fx.bcx.ins().bitcast(dst_ty, flags, val)
+}
+
pub(crate) fn type_zero_value(bcx: &mut FunctionBuilder<'_>, ty: Type) -> Value {
if ty == types::I128 {
let zero = bcx.ins().iconst(types::I64, 0);
// Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141
// Declare an internal global `extern_with_linkage_foo` which
- // is initialized with the address of `foo`. If `foo` is
+ // is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
| StatementKind::Retag(_, _)
| StatementKind::AscribeUserType(_, _)
| StatementKind::Coverage(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
}
--- /dev/null
+// Vendored from https://github.com/bytecodealliance/wasmtime/blob/b58a197d33f044193c3d608010f5e6ec394ac07e/cranelift/native/src/lib.rs
+// which is licensed as
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+// unlike rustc_codegen_cranelift itself. Also applies a small change to remove #![cfg_attr] that
+// rust's CI complains about and to fix formatting to match rustc.
+// FIXME revert back to the external crate with Cranelift 0.93
+#![allow(warnings)]
+
+//! Performs autodetection of the host for the purposes of running
+//! Cranelift to generate code to run on the same machine.
+
+#![deny(missing_docs, trivial_numeric_casts, unused_extern_crates, unstable_features)]
+#![warn(unused_import_braces)]
+
+use cranelift_codegen::isa;
+use target_lexicon::Triple;
+
+/// Return an `isa` builder configured for the current host
+/// machine, or `Err(())` if the host machine is not supported
+/// in the current configuration.
+pub fn builder() -> Result<isa::Builder, &'static str> {
+ builder_with_options(true)
+}
+
+/// Return an `isa` builder configured for the current host
+/// machine, or `Err(())` if the host machine is not supported
+/// in the current configuration.
+///
+/// Selects the given backend variant specifically; this is
+/// useful when more than oen backend exists for a given target
+/// (e.g., on x86-64).
+pub fn builder_with_options(infer_native_flags: bool) -> Result<isa::Builder, &'static str> {
+ let mut isa_builder = isa::lookup(Triple::host()).map_err(|err| match err {
+ isa::LookupError::SupportDisabled => "support for architecture disabled at compile time",
+ isa::LookupError::Unsupported => "unsupported architecture",
+ })?;
+
+ #[cfg(target_arch = "x86_64")]
+ {
+ use cranelift_codegen::settings::Configurable;
+
+ if !std::is_x86_feature_detected!("sse2") {
+ return Err("x86 support requires SSE2");
+ }
+
+ if !infer_native_flags {
+ return Ok(isa_builder);
+ }
+
+ // These are temporarily enabled by default (see #3810 for
+ // more) so that a default-constructed `Flags` can work with
+ // default Wasmtime features. Otherwise, the user must
+ // explicitly use native flags or turn these on when on x86-64
+ // platforms to avoid a configuration panic. In order for the
+ // "enable if detected" logic below to work, we must turn them
+ // *off* (differing from the default) and then re-enable below
+ // if present.
+ isa_builder.set("has_sse3", "false").unwrap();
+ isa_builder.set("has_ssse3", "false").unwrap();
+ isa_builder.set("has_sse41", "false").unwrap();
+ isa_builder.set("has_sse42", "false").unwrap();
+
+ if std::is_x86_feature_detected!("sse3") {
+ isa_builder.enable("has_sse3").unwrap();
+ }
+ if std::is_x86_feature_detected!("ssse3") {
+ isa_builder.enable("has_ssse3").unwrap();
+ }
+ if std::is_x86_feature_detected!("sse4.1") {
+ isa_builder.enable("has_sse41").unwrap();
+ }
+ if std::is_x86_feature_detected!("sse4.2") {
+ isa_builder.enable("has_sse42").unwrap();
+ }
+ if std::is_x86_feature_detected!("popcnt") {
+ isa_builder.enable("has_popcnt").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx") {
+ isa_builder.enable("has_avx").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx2") {
+ isa_builder.enable("has_avx2").unwrap();
+ }
+ if std::is_x86_feature_detected!("fma") {
+ isa_builder.enable("has_fma").unwrap();
+ }
+ if std::is_x86_feature_detected!("bmi1") {
+ isa_builder.enable("has_bmi1").unwrap();
+ }
+ if std::is_x86_feature_detected!("bmi2") {
+ isa_builder.enable("has_bmi2").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx512bitalg") {
+ isa_builder.enable("has_avx512bitalg").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx512dq") {
+ isa_builder.enable("has_avx512dq").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx512f") {
+ isa_builder.enable("has_avx512f").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx512vl") {
+ isa_builder.enable("has_avx512vl").unwrap();
+ }
+ if std::is_x86_feature_detected!("avx512vbmi") {
+ isa_builder.enable("has_avx512vbmi").unwrap();
+ }
+ if std::is_x86_feature_detected!("lzcnt") {
+ isa_builder.enable("has_lzcnt").unwrap();
+ }
+ }
+
+ #[cfg(target_arch = "aarch64")]
+ {
+ use cranelift_codegen::settings::Configurable;
+
+ if !infer_native_flags {
+ return Ok(isa_builder);
+ }
+
+ if std::arch::is_aarch64_feature_detected!("lse") {
+ isa_builder.enable("has_lse").unwrap();
+ }
+
+ if std::arch::is_aarch64_feature_detected!("paca") {
+ isa_builder.enable("has_pauth").unwrap();
+ }
+
+ if cfg!(target_os = "macos") {
+ // Pointer authentication is always available on Apple Silicon.
+ isa_builder.enable("sign_return_address").unwrap();
+ // macOS enforces the use of the B key for return addresses.
+ isa_builder.enable("sign_return_address_with_bkey").unwrap();
+ }
+ }
+
+ // There is no is_s390x_feature_detected macro yet, so for now
+ // we use getauxval from the libc crate directly.
+ #[cfg(all(target_arch = "s390x", target_os = "linux"))]
+ {
+ use cranelift_codegen::settings::Configurable;
+
+ if !infer_native_flags {
+ return Ok(isa_builder);
+ }
+
+ let v = unsafe { libc::getauxval(libc::AT_HWCAP) };
+ const HWCAP_S390X_VXRS_EXT2: libc::c_ulong = 32768;
+ if (v & HWCAP_S390X_VXRS_EXT2) != 0 {
+ isa_builder.enable("has_vxrs_ext2").unwrap();
+ // There is no separate HWCAP bit for mie2, so assume
+ // that any machine with vxrs_ext2 also has mie2.
+ isa_builder.enable("has_mie2").unwrap();
+ }
+ }
+
+ // `is_riscv_feature_detected` is nightly only for now, use
+ // getauxval from the libc crate directly as a temporary measure.
+ #[cfg(all(target_arch = "riscv64", target_os = "linux"))]
+ {
+ use cranelift_codegen::settings::Configurable;
+
+ if !infer_native_flags {
+ return Ok(isa_builder);
+ }
+
+ let v = unsafe { libc::getauxval(libc::AT_HWCAP) };
+
+ const HWCAP_RISCV_EXT_A: libc::c_ulong = 1 << (b'a' - b'a');
+ const HWCAP_RISCV_EXT_C: libc::c_ulong = 1 << (b'c' - b'a');
+ const HWCAP_RISCV_EXT_D: libc::c_ulong = 1 << (b'd' - b'a');
+ const HWCAP_RISCV_EXT_F: libc::c_ulong = 1 << (b'f' - b'a');
+ const HWCAP_RISCV_EXT_M: libc::c_ulong = 1 << (b'm' - b'a');
+ const HWCAP_RISCV_EXT_V: libc::c_ulong = 1 << (b'v' - b'a');
+
+ if (v & HWCAP_RISCV_EXT_A) != 0 {
+ isa_builder.enable("has_a").unwrap();
+ }
+
+ if (v & HWCAP_RISCV_EXT_C) != 0 {
+ isa_builder.enable("has_c").unwrap();
+ }
+
+ if (v & HWCAP_RISCV_EXT_D) != 0 {
+ isa_builder.enable("has_d").unwrap();
+ }
+
+ if (v & HWCAP_RISCV_EXT_F) != 0 {
+ isa_builder.enable("has_f").unwrap();
+
+ // TODO: There doesn't seem to be a bit associated with this extension
+ // rust enables it with the `f` extension:
+ // https://github.com/rust-lang/stdarch/blob/790411f93c4b5eada3c23abb4c9a063fb0b24d99/crates/std_detect/src/detect/os/linux/riscv.rs#L43
+ isa_builder.enable("has_zicsr").unwrap();
+ }
+
+ if (v & HWCAP_RISCV_EXT_M) != 0 {
+ isa_builder.enable("has_m").unwrap();
+ }
+
+ if (v & HWCAP_RISCV_EXT_V) != 0 {
+ isa_builder.enable("has_v").unwrap();
+ }
+
+ // TODO: ZiFencei does not have a bit associated with it
+ // TODO: Zbkb does not have a bit associated with it
+ }
+
+ // squelch warnings about unused mut/variables on some platforms.
+ drop(&mut isa_builder);
+ drop(infer_native_flags);
+
+ Ok(isa_builder)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::builder;
+ use cranelift_codegen::isa::CallConv;
+ use cranelift_codegen::settings;
+
+ #[test]
+ fn test() {
+ if let Ok(isa_builder) = builder() {
+ let flag_builder = settings::builder();
+ let isa = isa_builder.finish(settings::Flags::new(flag_builder)).unwrap();
+
+ if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
+ assert_eq!(isa.default_call_conv(), CallConv::AppleAarch64);
+ } else if cfg!(any(unix, target_os = "nebulet")) {
+ assert_eq!(isa.default_call_conv(), CallConv::SystemV);
+ } else if cfg!(windows) {
+ assert_eq!(isa.default_call_conv(), CallConv::WindowsFastcall);
+ }
+
+ if cfg!(target_pointer_width = "64") {
+ assert_eq!(isa.pointer_bits(), 64);
+ } else if cfg!(target_pointer_width = "32") {
+ assert_eq!(isa.pointer_bits(), 32);
+ } else if cfg!(target_pointer_width = "16") {
+ assert_eq!(isa.pointer_bits(), 16);
+ }
+ }
+ }
+}
+
+/// Version number of this crate.
+pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub(crate) use emit::{DebugReloc, DebugRelocName};
pub(crate) use unwind::UnwindContext;
+pub(crate) fn producer() -> String {
+ format!(
+ "cg_clif (rustc {}, cranelift {})",
+ rustc_interface::util::rustc_version_str().unwrap_or("unknown version"),
+ cranelift_codegen::VERSION,
+ )
+}
+
pub(crate) struct DebugContext {
endian: RunTimeEndian,
let mut dwarf = DwarfUnit::new(encoding);
- let producer = format!(
- "cg_clif (rustc {}, cranelift {})",
- rustc_interface::util::rustc_version_str().unwrap_or("unknown version"),
- cranelift_codegen::VERSION,
- );
+ let producer = producer();
let comp_dir = tcx
.sess
.opts
.working_dir
.to_string_lossy(FileNameDisplayPreference::Remapped)
.into_owned();
- let (name, file_info) = match tcx.sess.local_crate_source_file.clone() {
+ let (name, file_info) = match tcx.sess.local_crate_source_file() {
Some(path) => {
let name = path.to_string_lossy().into_owned();
(name, None)
self.concurrency_limiter.finished();
+ sess.abort_if_errors();
+
(
CodegenResults {
modules,
fn emit_module(
output_filenames: &OutputFilenames,
prof: &SelfProfilerRef,
- object: cranelift_object::object::write::Object<'_>,
+ mut object: cranelift_object::object::write::Object<'_>,
kind: ModuleKind,
name: String,
) -> Result<CompiledModule, String> {
+ if object.format() == cranelift_object::object::BinaryFormat::Elf {
+ let comment_section = object.add_section(
+ Vec::new(),
+ b".comment".to_vec(),
+ cranelift_object::object::SectionKind::OtherString,
+ );
+ let mut producer = vec![0];
+ producer.extend(crate::debuginfo::producer().as_bytes());
+ producer.push(0);
+ object.set_section_data(comment_section, producer, 1);
+ }
+
let tmp_file = output_filenames.temp_path(OutputType::Object, Some(&name));
let mut file = match File::create(&tmp_file) {
Ok(file) => file,
.collect::<Vec<_>>()
});
- tcx.sess.abort_if_errors();
-
let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string());
let mut allocator_unwind_context = UnwindContext::new(allocator_module.isa(), true);
let created_alloc_shim =
// cast float to int
let a_lane = match lane_ty {
- types::F32 => fx.bcx.ins().bitcast(types::I32, a_lane),
- types::F64 => fx.bcx.ins().bitcast(types::I64, a_lane),
+ types::F32 => codegen_bitcast(fx, types::I32, a_lane),
+ types::F64 => codegen_bitcast(fx, types::I64, a_lane),
_ => a_lane,
};
pub(crate) use cpuid::codegen_cpuid_call;
pub(crate) use llvm::codegen_llvm_intrinsic_call;
+use rustc_middle::ty::layout::HasParamEnv;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::SubstsRef;
use rustc_span::symbol::{kw, sym, Symbol};
let mut res = fx.bcx.ins().bmask(int_ty, val);
if ty.is_float() {
- res = fx.bcx.ins().bitcast(ty, res);
+ res = codegen_bitcast(fx, ty, res);
}
res
substs,
args,
destination,
+ target,
source_info.span,
);
- let ret_block = fx.get_block(target);
- fx.bcx.ins().jump(ret_block, &[]);
} else if codegen_float_intrinsic_call(fx, intrinsic, args, destination) {
let ret_block = fx.get_block(target);
fx.bcx.ins().jump(ret_block, &[]);
let layout = fx.layout_of(substs.type_at(0));
if layout.abi.is_uninhabited() {
with_no_trimmed_paths!({
- crate::base::codegen_panic(
+ crate::base::codegen_panic_nounwind(
fx,
&format!("attempted to instantiate uninhabited type `{}`", layout.ty),
source_info,
return;
}
- if intrinsic == sym::assert_zero_valid && !fx.tcx.permits_zero_init(layout) {
+ if intrinsic == sym::assert_zero_valid
+ && !fx.tcx.permits_zero_init(fx.param_env().and(layout))
+ {
with_no_trimmed_paths!({
- crate::base::codegen_panic(
+ crate::base::codegen_panic_nounwind(
fx,
&format!(
"attempted to zero-initialize type `{}`, which is invalid",
}
if intrinsic == sym::assert_mem_uninitialized_valid
- && !fx.tcx.permits_uninit_init(layout)
+ && !fx.tcx.permits_uninit_init(fx.param_env().and(layout))
{
with_no_trimmed_paths!({
- crate::base::codegen_panic(
+ crate::base::codegen_panic_nounwind(
fx,
&format!(
"attempted to leave type `{}` uninitialized, which is invalid",
_substs: SubstsRef<'tcx>,
args: &[mir::Operand<'tcx>],
ret: CPlace<'tcx>,
+ target: BasicBlock,
span: Span,
) {
match intrinsic {
} else {
fx.tcx.sess.span_warn(span, "Index argument for `simd_extract` is not a constant");
let trap_block = fx.bcx.create_block();
- let dummy_block = fx.bcx.create_block();
let true_ = fx.bcx.ins().iconst(types::I8, 1);
fx.bcx.ins().brnz(true_, trap_block, &[]);
- fx.bcx.ins().jump(dummy_block, &[]);
+ let ret_block = fx.get_block(target);
+ fx.bcx.ins().jump(ret_block, &[]);
fx.bcx.switch_to_block(trap_block);
crate::trap::trap_unimplemented(
fx,
"Index argument for `simd_extract` is not a constant",
);
- fx.bcx.switch_to_block(dummy_block);
return;
};
});
}
- // simd_arith_offset
- // simd_scatter
- // simd_gather
+ sym::simd_expose_addr | sym::simd_from_exposed_addr | sym::simd_cast_ptr => {
+ intrinsic_args!(fx, args => (arg); intrinsic);
+ ret.write_cvalue_transmute(fx, arg);
+ }
+
+ sym::simd_arith_offset => {
+ intrinsic_args!(fx, args => (ptr, offset); intrinsic);
+
+ let (lane_count, ptr_lane_ty) = ptr.layout().ty.simd_size_and_type(fx.tcx);
+ let pointee_ty = ptr_lane_ty.builtin_deref(true).unwrap().ty;
+ let pointee_size = fx.layout_of(pointee_ty).size.bytes();
+ let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
+ let ret_lane_layout = fx.layout_of(ret_lane_ty);
+ assert_eq!(lane_count, ret_lane_count);
+
+ for lane_idx in 0..lane_count {
+ let ptr_lane = ptr.value_lane(fx, lane_idx).load_scalar(fx);
+ let offset_lane = offset.value_lane(fx, lane_idx).load_scalar(fx);
+
+ let ptr_diff = if pointee_size != 1 {
+ fx.bcx.ins().imul_imm(offset_lane, pointee_size as i64)
+ } else {
+ offset_lane
+ };
+ let res_lane = fx.bcx.ins().iadd(ptr_lane, ptr_diff);
+ let res_lane = CValue::by_val(res_lane, ret_lane_layout);
+
+ ret.place_lane(fx, lane_idx).write_cvalue(fx, res_lane);
+ }
+ }
+
+ sym::simd_gather => {
+ intrinsic_args!(fx, args => (val, ptr, mask); intrinsic);
+
+ let (val_lane_count, val_lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx);
+ let (ptr_lane_count, _ptr_lane_ty) = ptr.layout().ty.simd_size_and_type(fx.tcx);
+ let (mask_lane_count, _mask_lane_ty) = mask.layout().ty.simd_size_and_type(fx.tcx);
+ let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
+ assert_eq!(val_lane_count, ptr_lane_count);
+ assert_eq!(val_lane_count, mask_lane_count);
+ assert_eq!(val_lane_count, ret_lane_count);
+
+ let lane_clif_ty = fx.clif_type(val_lane_ty).unwrap();
+ let ret_lane_layout = fx.layout_of(ret_lane_ty);
+
+ for lane_idx in 0..ptr_lane_count {
+ let val_lane = val.value_lane(fx, lane_idx).load_scalar(fx);
+ let ptr_lane = ptr.value_lane(fx, lane_idx).load_scalar(fx);
+ let mask_lane = mask.value_lane(fx, lane_idx).load_scalar(fx);
+
+ let if_enabled = fx.bcx.create_block();
+ let if_disabled = fx.bcx.create_block();
+ let next = fx.bcx.create_block();
+ let res_lane = fx.bcx.append_block_param(next, lane_clif_ty);
+
+ fx.bcx.ins().brnz(mask_lane, if_enabled, &[]);
+ fx.bcx.ins().jump(if_disabled, &[]);
+ fx.bcx.seal_block(if_enabled);
+ fx.bcx.seal_block(if_disabled);
+
+ fx.bcx.switch_to_block(if_enabled);
+ let res = fx.bcx.ins().load(lane_clif_ty, MemFlags::trusted(), ptr_lane, 0);
+ fx.bcx.ins().jump(next, &[res]);
+
+ fx.bcx.switch_to_block(if_disabled);
+ fx.bcx.ins().jump(next, &[val_lane]);
+
+ fx.bcx.seal_block(next);
+ fx.bcx.switch_to_block(next);
+
+ fx.bcx.ins().nop();
+
+ ret.place_lane(fx, lane_idx)
+ .write_cvalue(fx, CValue::by_val(res_lane, ret_lane_layout));
+ }
+ }
+
+ sym::simd_scatter => {
+ intrinsic_args!(fx, args => (val, ptr, mask); intrinsic);
+
+ let (val_lane_count, _val_lane_ty) = val.layout().ty.simd_size_and_type(fx.tcx);
+ let (ptr_lane_count, _ptr_lane_ty) = ptr.layout().ty.simd_size_and_type(fx.tcx);
+ let (mask_lane_count, _mask_lane_ty) = mask.layout().ty.simd_size_and_type(fx.tcx);
+ assert_eq!(val_lane_count, ptr_lane_count);
+ assert_eq!(val_lane_count, mask_lane_count);
+
+ for lane_idx in 0..ptr_lane_count {
+ let val_lane = val.value_lane(fx, lane_idx).load_scalar(fx);
+ let ptr_lane = ptr.value_lane(fx, lane_idx).load_scalar(fx);
+ let mask_lane = mask.value_lane(fx, lane_idx).load_scalar(fx);
+
+ let if_enabled = fx.bcx.create_block();
+ let next = fx.bcx.create_block();
+
+ fx.bcx.ins().brnz(mask_lane, if_enabled, &[]);
+ fx.bcx.ins().jump(next, &[]);
+ fx.bcx.seal_block(if_enabled);
+
+ fx.bcx.switch_to_block(if_enabled);
+ fx.bcx.ins().store(MemFlags::trusted(), val_lane, ptr_lane, 0);
+ fx.bcx.ins().jump(next, &[]);
+
+ fx.bcx.seal_block(next);
+ fx.bcx.switch_to_block(next);
+ }
+ }
+
_ => {
- fx.tcx.sess.span_fatal(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
+ fx.tcx.sess.span_err(span, &format!("Unknown SIMD intrinsic {}", intrinsic));
+ // Prevent verifier error
+ fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
}
}
+ let ret_block = fx.get_block(target);
+ fx.bcx.ins().jump(ret_block, &[]);
}
mod concurrency_limiter;
mod config;
mod constant;
+// FIXME revert back to the external crate with Cranelift 0.93
+mod cranelift_native;
mod debuginfo;
mod discriminant;
mod driver;
}
}
- if target_triple.architecture == target_lexicon::Architecture::X86_64 {
+ if let target_lexicon::Architecture::Aarch64(_) | target_lexicon::Architecture::X86_64 =
+ target_triple.architecture
+ {
// Windows depends on stack probes to grow the committed part of the stack
flags_builder.enable("enable_probestack").unwrap();
flags_builder.set("probestack_strategy", "inline").unwrap();
} else {
- // __cranelift_probestack is not provided and inline stack probes are only supported on x86_64
+ // __cranelift_probestack is not provided and inline stack probes are only supported on AArch64 and x86_64
flags_builder.set("enable_probestack", "false").unwrap();
}
is_main_fn: bool,
sigpipe: u8,
) {
- let main_ret_ty = tcx.fn_sig(rust_main_def_id).output();
+ let main_ret_ty = tcx.fn_sig(rust_main_def_id).no_bound_vars().unwrap().output();
// Given that `main()` has no arguments,
// then its return type cannot have
// late-bound regions, since late-bound
],
returns: vec![AbiParam::new(m.target_config().pointer_type() /*isize*/)],
call_conv: crate::conv_to_call_conv(
+ tcx.sess,
tcx.sess.target.options.entry_abi,
m.target_config().default_call_conv,
),
};
let entry_name = tcx.sess.target.options.entry_name.as_ref();
- let cmain_func_id = m.declare_function(entry_name, Linkage::Export, &cmain_sig).unwrap();
+ let cmain_func_id = match m.declare_function(entry_name, Linkage::Export, &cmain_sig) {
+ Ok(func_id) => func_id,
+ Err(err) => {
+ tcx.sess
+ .fatal(&format!("entry symbol `{entry_name}` declared multiple times: {err}"));
+ }
+ };
let instance = Instance::mono(tcx, rust_main_def_id).polymorphize(tcx);
bcx.seal_all_blocks();
bcx.finalize();
}
- m.define_function(cmain_func_id, &mut ctx).unwrap();
+
+ if let Err(err) = m.define_function(cmain_func_id, &mut ctx) {
+ tcx.sess.fatal(&format!("entry symbol `{entry_name}` defined multiple times: {err}"));
+ }
+
unwind_context.add_function(cmain_func_id, &ctx, m.isa());
}
}
/// otherwise return the given value and false.
pub(crate) fn maybe_unwrap_bool_not(bcx: &mut FunctionBuilder<'_>, arg: Value) -> (Value, bool) {
if let ValueDef::Result(arg_inst, 0) = bcx.func.dfg.value_def(arg) {
- match bcx.func.dfg[arg_inst] {
+ match bcx.func.dfg.insts[arg_inst] {
// This is the lowering of `Rvalue::Not`
InstructionData::IntCompareImm {
opcode: Opcode::IcmpImm,
return None;
};
- match bcx.func.dfg[arg_inst] {
+ match bcx.func.dfg.insts[arg_inst] {
InstructionData::UnaryImm { opcode: Opcode::Iconst, imm } => {
if test_zero {
Some(imm.bits() == 0)
(types::I32, types::F32)
| (types::F32, types::I32)
| (types::I64, types::F64)
- | (types::F64, types::I64) => fx.bcx.ins().bitcast(dst_ty, data),
- _ if src_ty.is_vector() && dst_ty.is_vector() => fx.bcx.ins().bitcast(dst_ty, data),
+ | (types::F64, types::I64) => codegen_bitcast(fx, dst_ty, data),
+ _ if src_ty.is_vector() && dst_ty.is_vector() => codegen_bitcast(fx, dst_ty, data),
_ if src_ty.is_vector() || dst_ty.is_vector() => {
// FIXME do something more efficient for transmutes between vectors and integers.
let stack_slot = fx.bcx.create_sized_stack_slot(StackSlotData {
# This block is ignored by rustc
set -e
echo "[BUILD] y.rs" 1>&2
-rustc $0 -o ${0/.rs/.bin} -Cdebuginfo=1 --edition 2021
+rustc $0 -o ${0/.rs/.bin} -Cdebuginfo=1 --edition 2021 -Cpanic=abort
exec ${0/.rs/.bin} $@
*/
bx.range_metadata(load, vr);
}
}
- abi::Pointer if vr.start < vr.end && !vr.contains(0) => {
+ abi::Pointer(_) if vr.start < vr.end && !vr.contains(0) => {
bx.nonnull_metadata(load);
}
_ => {}
let base_addr = self.const_bitcast(base_addr, self.usize_type);
let offset = self.context.new_rvalue_from_long(self.usize_type, offset.bytes() as i64);
let ptr = self.const_bitcast(base_addr + offset, ptr_type);
- if layout.primitive() != Pointer {
+ if !matches!(layout.primitive(), Pointer(_)) {
self.const_bitcast(ptr.dereference(None).to_rvalue(), ty)
}
else {
)
.expect("const_alloc_to_llvm: could not read relocation pointer")
as u64;
+
+ let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
+
llvals.push(cx.scalar_to_backend(
InterpScalar::from_pointer(
interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)),
&cx.tcx,
),
- abi::Scalar::Initialized { value: Primitive::Pointer, valid_range: WrappingRange::full(dl.pointer_size) },
- cx.type_i8p(),
+ abi::Scalar::Initialized { value: Primitive::Pointer(address_space), valid_range: WrappingRange::full(dl.pointer_size) },
+ cx.type_i8p_ext(address_space),
));
next_offset = offset + pointer_size;
}
Int(i, false) => cx.type_from_unsigned_integer(i),
F32 => cx.type_f32(),
F64 => cx.type_f64(),
- Pointer => {
+ Pointer(address_space) => {
// If we know the alignment, pick something better than i8.
let pointee =
if let Some(pointee) = self.pointee_info_at(cx, offset) {
else {
cx.type_i8()
};
- cx.type_ptr_to(pointee)
+ cx.type_ptr_to_ext(pointee, address_space)
}
}
}
bx.store(val, cast_dst, self.layout.align.abi);
} else {
// The actual return type is a struct, but the ABI
- // adaptation code has cast it into some scalar type. The
+ // adaptation code has cast it into some scalar type. The
// code that follows is the only reliable way I have
// found to do a transform like i64 -> {i32,i32}.
// Basically we dump the data onto the stack then memcpy it.
};
// Store mark in a metadata node so we can map LLVM errors
- // back to source locations. See #17552.
+ // back to source locations. See #17552.
let key = "srcloc";
let kind = llvm::LLVMGetMDKindIDInContext(
bx.llcx,
/// Helper function to get the LLVM type for a Scalar. Pointers are returned as
/// the equivalent integer type.
fn llvm_asm_scalar_type<'ll>(cx: &CodegenCx<'ll, '_>, scalar: Scalar) -> &'ll Type {
+ let dl = &cx.tcx.data_layout;
match scalar.primitive() {
Primitive::Int(Integer::I8, _) => cx.type_i8(),
Primitive::Int(Integer::I16, _) => cx.type_i16(),
Primitive::Int(Integer::I64, _) => cx.type_i64(),
Primitive::F32 => cx.type_f32(),
Primitive::F64 => cx.type_f64(),
- Primitive::Pointer => cx.type_isize(),
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ Primitive::Pointer(_) => cx.type_from_integer(dl.ptr_sized_integer()),
_ => unreachable!(),
}
}
reg: InlineAsmRegClass,
layout: &TyAndLayout<'tcx>,
) -> &'ll Value {
+ let dl = &bx.tcx.data_layout;
match (reg, layout.abi) {
(InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg), Abi::Scalar(s)) => {
if let Primitive::Int(Integer::I8, _) = s.primitive() {
let elem_ty = llvm_asm_scalar_type(bx.cx, s);
let count = 16 / layout.size.bytes();
let vec_ty = bx.cx.type_vector(elem_ty, count);
- if let Primitive::Pointer = s.primitive() {
- value = bx.ptrtoint(value, bx.cx.type_isize());
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ if let Primitive::Pointer(_) = s.primitive() {
+ let t = bx.type_from_integer(dl.ptr_sized_integer());
+ value = bx.ptrtoint(value, t);
}
bx.insert_element(bx.const_undef(vec_ty), value, bx.const_i32(0))
}
}
(InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::vreg_low16), Abi::Scalar(s)) => {
value = bx.extract_element(value, bx.const_i32(0));
- if let Primitive::Pointer = s.primitive() {
+ if let Primitive::Pointer(_) = s.primitive() {
value = bx.inttoptr(value, layout.llvm_type(bx.cx));
}
value
// the WebAssembly specification, which has this feature. This won't be
// needed when LLVM enables this `multivalue` feature by default.
if !cx.tcx.is_closure(instance.def_id()) {
- let abi = cx.tcx.fn_sig(instance.def_id()).abi();
+ let abi = cx.tcx.fn_sig(instance.def_id()).skip_binder().abi();
if abi == Abi::Wasm {
function_features.push("+multivalue".to_string());
}
use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
use rustc_codegen_ssa::back::archive::{
- get_native_object_symbols, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder,
- ArchiveBuilderBuilder, UnknownArchiveKind,
+ get_native_object_symbols, try_extract_macho_fat_archive, ArArchiveBuilder,
+ ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, UnknownArchiveKind,
};
use rustc_session::cstore::DllImport;
archive: &Path,
skip: Box<dyn FnMut(&str) -> bool + 'static>,
) -> io::Result<()> {
- let archive_ro = match ArchiveRO::open(archive) {
+ let mut archive = archive.to_path_buf();
+ if self.sess.target.llvm_target.contains("-apple-macosx") {
+ if let Some(new_archive) = try_extract_macho_fat_archive(&self.sess, &archive)? {
+ archive = new_archive
+ }
+ }
+ let archive_ro = match ArchiveRO::open(&archive) {
Ok(ar) => ar,
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
};
return Ok(());
}
self.additions.push(Addition::Archive {
- path: archive.to_path_buf(),
+ path: archive,
archive: archive_ro,
skip: Box::new(skip),
});
impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a> {
- if sess.target.arch == "wasm32" || sess.target.arch == "wasm64" {
+ // FIXME use ArArchiveBuilder on most targets again once reading thin archives is
+ // implemented
+ if true || sess.target.arch == "wasm32" || sess.target.arch == "wasm64" {
Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
} else {
Box::new(ArArchiveBuilder::new(sess, get_llvm_object_symbols))
// The binutils linker used on -windows-gnu targets cannot read the import
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
// that loaded but crashed with an AV upon calling one of the imported
- // functions. Therefore, use binutils to create the import library instead,
+ // functions. Therefore, use binutils to create the import library instead,
// by writing a .DEF file to the temp dir and calling binutils's dlltool.
let def_file_path =
tmpdir.join(format!("{}{}", lib_name, name_suffix)).with_extension("def");
// All import names are Rust identifiers and therefore cannot contain \0 characters.
// FIXME: when support for #[link_name] is implemented, ensure that the import names
- // still don't contain any \0 characters. Also need to check that the names don't
+ // still don't contain any \0 characters. Also need to check that the names don't
// contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
// in definition files.
let cstring_import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> =
}
// The user didn't specify the location of the dlltool binary, and we weren't able
- // to find the appropriate one on the PATH. Just return the name of the tool
+ // to find the appropriate one on the PATH. Just return the name of the tool
// and let the invocation fail with a hopefully useful error message.
tool_name
}
// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
// This is required to satisfy `dllimport` references to static data in .rlibs
-// when using MSVC linker. We do this only for data, as linker can fix up
+// when using MSVC linker. We do this only for data, as linker can fix up
// code references on its own.
// See #26591, #27438
fn create_msvc_imps(
layout: TyAndLayout<'tcx>,
offset: Size,
) {
- if !scalar.is_always_valid(bx) {
+ if !scalar.is_uninit_valid() {
bx.noundef_metadata(load);
}
bx.range_metadata(load, scalar.valid_range(bx));
}
}
- abi::Pointer => {
+ abi::Pointer(_) => {
if !scalar.valid_range(bx).contains(0) {
bx.nonnull_metadata(load);
}
let llptrty = fn_abi.ptr_to_llvm_type(cx);
// This is subtle and surprising, but sometimes we have to bitcast
- // the resulting fn pointer. The reason has to do with external
- // functions. If you have two crates that both bind the same C
+ // the resulting fn pointer. The reason has to do with external
+ // functions. If you have two crates that both bind the same C
// library, they may not use precisely the same types: for
// example, they will probably each declare their own structs,
// which are distinct types from LLVM's point of view (nominal
Scalar::Int(int) => {
let data = int.assert_bits(layout.size(self));
let llval = self.const_uint_big(self.type_ix(bitsize), data);
- if layout.primitive() == Pointer {
+ if matches!(layout.primitive(), Pointer(_)) {
unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
} else {
self.const_bitcast(llval, llty)
1,
)
};
- if layout.primitive() != Pointer {
+ if !matches!(layout.primitive(), Pointer(_)) {
unsafe { llvm::LLVMConstPtrToInt(llval, llty) }
} else {
self.const_bitcast(llval, llty)
use rustc_hir::def_id::DefId;
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::interpret::{
- read_target_uint, Allocation, ConstAllocation, ErrorHandled, GlobalAlloc, InitChunk, Pointer,
+ read_target_uint, Allocation, ConstAllocation, ErrorHandled, InitChunk, Pointer,
Scalar as InterpScalar,
};
use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_middle::{bug, span_bug};
use rustc_session::config::Lto;
-use rustc_target::abi::{
- AddressSpace, Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange,
-};
+use rustc_target::abi::{Align, HasDataLayout, Primitive, Scalar, Size, WrappingRange};
use std::ops::Range;
pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<'_>) -> &'ll Value {
.expect("const_alloc_to_llvm: could not read relocation pointer")
as u64;
- let address_space = match cx.tcx.global_alloc(alloc_id) {
- GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
- GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
- AddressSpace::DATA
- }
- };
+ let address_space = cx.tcx.global_alloc(alloc_id).address_space(cx);
llvals.push(cx.scalar_to_backend(
InterpScalar::from_pointer(
&cx.tcx,
),
Scalar::Initialized {
- value: Primitive::Pointer,
+ value: Primitive::Pointer(address_space),
valid_range: WrappingRange::full(dl.pointer_size),
},
cx.type_i8p_ext(address_space),
fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
// The target may require greater alignment for globals than the type does.
// Note: GCC and Clang also allow `__attribute__((aligned))` on variables,
- // which can force it to be smaller. Rust doesn't support this yet.
+ // which can force it to be smaller. Rust doesn't support this yet.
if let Some(min) = cx.sess().target.min_global_align {
match Align::from_bits(min) {
Ok(min) => align = align.max(min),
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
// Declare an internal global `extern_with_linkage_foo` which
- // is initialized with the address of `foo`. If `foo` is
+ // is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
//
// FIXME(#34960)
let cfg_llvm_root = option_env!("CFG_LLVM_ROOT").unwrap_or("");
- let custom_llvm_used = cfg_llvm_root.trim() != "";
+ let custom_llvm_used = !cfg_llvm_root.trim().is_empty();
if !custom_llvm_used && target_data_layout != llvm_data_layout {
bug!(
use crate::common::CodegenCx;
use crate::coverageinfo;
-use crate::errors::InstrumentCoverageRequiresLLVM12;
use crate::llvm;
use llvm::coverageinfo::CounterMappingRegion;
use rustc_codegen_ssa::traits::{ConstMethods, CoverageInfoMethods};
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def::DefKind;
-use rustc_hir::def_id::DefIdSet;
+use rustc_hir::def_id::DefId;
use rustc_llvm::RustString;
use rustc_middle::bug;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
/// Generates and exports the Coverage Map.
///
-/// Rust Coverage Map generation supports LLVM Coverage Mapping Format versions
-/// 5 (LLVM 12, only) and 6 (zero-based encoded as 4 and 5, respectively), as defined at
+/// Rust Coverage Map generation supports LLVM Coverage Mapping Format version
+/// 6 (zero-based encoded as 5), as defined at
/// [LLVM Code Coverage Mapping Format](https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#llvm-code-coverage-mapping-format).
/// These versions are supported by the LLVM coverage tools (`llvm-profdata` and `llvm-cov`)
/// bundled with Rust's fork of LLVM.
pub fn finalize(cx: &CodegenCx<'_, '_>) {
let tcx = cx.tcx;
- // Ensure the installed version of LLVM supports at least Coverage Map
- // Version 5 (encoded as a zero-based value: 4), which was introduced with
- // LLVM 12.
+ // Ensure the installed version of LLVM supports Coverage Map Version 6
+ // (encoded as a zero-based value: 5), which was introduced with LLVM 13.
let version = coverageinfo::mapping_version();
- if version < 4 {
- tcx.sess.emit_fatal(InstrumentCoverageRequiresLLVM12);
- }
+ assert_eq!(version, 5, "The `CoverageMappingVersion` exposed by `llvm-wrapper` is out of sync");
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
return;
}
- let mut mapgen = CoverageMapGenerator::new(tcx, version);
+ let mut mapgen = CoverageMapGenerator::new(tcx);
// Encode coverage mappings and generate function records
let mut function_data = Vec::new();
}
impl CoverageMapGenerator {
- fn new(tcx: TyCtxt<'_>, version: u32) -> Self {
+ fn new(tcx: TyCtxt<'_>) -> Self {
let mut filenames = FxIndexSet::default();
- if version >= 5 {
- // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
- // requires setting the first filename to the compilation directory.
- // Since rustc generates coverage maps with relative paths, the
- // compilation directory can be combined with the relative paths
- // to get absolute paths, if needed.
- let working_dir = tcx
- .sess
- .opts
- .working_dir
- .remapped_path_if_available()
- .to_string_lossy()
- .to_string();
- let c_filename =
- CString::new(working_dir).expect("null error converting filename to C string");
- filenames.insert(c_filename);
- }
+ // LLVM Coverage Mapping Format version 6 (zero-based encoded as 5)
+ // requires setting the first filename to the compilation directory.
+ // Since rustc generates coverage maps with relative paths, the
+ // compilation directory can be combined with the relative paths
+ // to get absolute paths, if needed.
+ let working_dir =
+ tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy().to_string();
+ let c_filename =
+ CString::new(working_dir).expect("null error converting filename to C string");
+ filenames.insert(c_filename);
Self { filenames }
}
let ignore_unused_generics = tcx.sess.instrument_coverage_except_unused_generics();
- let eligible_def_ids: DefIdSet = tcx
+ let eligible_def_ids: Vec<DefId> = tcx
.mir_keys(())
.iter()
.filter_map(|local_def_id| {
DefKind::Fn | DefKind::AssocFn | DefKind::Closure | DefKind::Generator
) {
return None;
- } else if ignore_unused_generics
- && tcx.generics_of(def_id).requires_monomorphization(tcx)
- {
+ }
+ if ignore_unused_generics && tcx.generics_of(def_id).requires_monomorphization(tcx) {
return None;
}
Some(local_def_id.to_def_id())
let codegenned_def_ids = tcx.codegened_and_inlined_items(());
- for &non_codegenned_def_id in eligible_def_ids.difference(codegenned_def_ids) {
+ for non_codegenned_def_id in
+ eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id))
+ {
let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
// If a function is marked `#[no_coverage]`, then skip generating a
codegen_unit_name: &str,
debug_context: &CodegenUnitDebugContext<'ll, 'tcx>,
) -> &'ll DIDescriptor {
- let mut name_in_debuginfo = match tcx.sess.local_crate_source_file {
- Some(ref path) => path.clone(),
- None => PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str()),
- };
+ let mut name_in_debuginfo = tcx
+ .sess
+ .local_crate_source_file()
+ .unwrap_or_else(|| PathBuf::from(tcx.crate_name(LOCAL_CRATE).as_str()));
// To avoid breaking split DWARF, we need to ensure that each codegen unit
// has a unique `DW_AT_name`. This is because there's a remote chance that
return;
}
+ // When full debuginfo is enabled, we want to try and prevent vtables from being
+ // merged. Otherwise debuggers will have a hard time mapping from dyn pointer
+ // to concrete type.
+ llvm::SetUnnamedAddress(vtable, llvm::UnnamedAddr::No);
+
let vtable_name =
compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable);
let vtable_type_di_node = build_vtable_type_di_node(cx, ty, poly_trait_ref);
Primitive::Int(t, _) => t,
Primitive::F32 => Integer::I32,
Primitive::F64 => Integer::I64,
- Primitive::Pointer => {
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ Primitive::Pointer(_) => {
// If the niche is the NULL value of a reference, then `discr_enum_ty` will be
// a RawPtr. CodeView doesn't know what to do with enums whose base type is a
// pointer so we fix this up to just be `usize`.
pub error: String,
}
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_instrument_coverage_requires_llvm_12)]
-pub(crate) struct InstrumentCoverageRequiresLLVM12;
-
#[derive(Diagnostic)]
#[diag(codegen_llvm_symbol_already_defined)]
pub(crate) struct SymbolAlreadyDefined<'a> {
emit_va_arg(self, args[0], ret_ty)
}
}
- Primitive::F64 | Primitive::Pointer => {
+ Primitive::F64 | Primitive::Pointer(_) => {
emit_va_arg(self, args[0], ret_ty)
}
// `va_arg` should never be used with the return type f32.
// Type indicator for the exception being thrown.
//
// The first value in this tuple is a pointer to the exception object
- // being thrown. The second value is a "selector" indicating which of
+ // being thrown. The second value is a "selector" indicating which of
// the landing pad clauses the exception's type had been matched to.
// rust_try ignores the selector.
bx.switch_to_block(catch);
// Type indicator for the exception being thrown.
//
// The first value in this tuple is a pointer to the exception object
- // being thrown. The second value is a "selector" indicating which of
+ // being thrown. The second value is a "selector" indicating which of
// the landing pad clauses the exception's type had been matched to.
bx.switch_to_block(catch);
let tydesc = bx.eh_catch_typeinfo();
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Ty, TypeVisitable};
-use rustc_target::abi::{Abi, AddressSpace, Align, FieldsShape};
+use rustc_target::abi::{Abi, Align, FieldsShape};
use rustc_target::abi::{Int, Pointer, F32, F64};
use rustc_target::abi::{PointeeInfo, Scalar, Size, TyAbiInterface, Variants};
use smallvec::{smallvec, SmallVec};
Int(i, _) => cx.type_from_integer(i),
F32 => cx.type_f32(),
F64 => cx.type_f64(),
- Pointer => {
+ Pointer(address_space) => {
// If we know the alignment, pick something better than i8.
- let (pointee, address_space) =
- if let Some(pointee) = self.pointee_info_at(cx, offset) {
- (cx.type_pointee_for_align(pointee.align), pointee.address_space)
- } else {
- (cx.type_i8(), AddressSpace::DATA)
- };
+ let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) {
+ cx.type_pointee_for_align(pointee.align)
+ } else {
+ cx.type_i8()
+ };
cx.type_ptr_to_ext(pointee, address_space)
}
}
let scalar = [a, b][index];
// Make sure to return the same type `immediate_llvm_type` would when
- // dealing with an immediate pair. This means that `(bool, bool)` is
+ // dealing with an immediate pair. This means that `(bool, bool)` is
// effectively represented as `{i8, i8}` in memory and two `i1`s as an
// immediate, just like `bool` is typically `i8` in memory and only `i1`
- // when immediate. We need to load/store `bool` as `i8` to avoid
+ // when immediate. We need to load/store `bool` as `i8` to avoid
// crippling LLVM optimizations or triggering other LLVM bugs with `i1`.
if immediate && scalar.is_bool() {
return cx.type_i1();
use std::error::Error;
use std::fs::File;
-use std::io;
+use std::io::{self, Write};
use std::path::{Path, PathBuf};
// Re-exporting for rustc_codegen_llvm::back::archive
}
}
-fn try_filter_fat_archs<'a>(
+fn try_filter_fat_archs(
archs: object::read::Result<&[impl FatArch]>,
target_arch: object::Architecture,
- archive_map_data: &'a [u8],
-) -> io::Result<Option<(&'a [u8], u64)>> {
+ archive_path: &Path,
+ archive_map_data: &[u8],
+) -> io::Result<Option<PathBuf>> {
let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let desired = match archs.iter().find(|a| a.architecture() == target_arch) {
None => return Ok(None),
};
- Ok(Some((
+ let (mut new_f, extracted_path) = tempfile::Builder::new()
+ .suffix(archive_path.file_name().unwrap())
+ .tempfile()?
+ .keep()
+ .unwrap();
+
+ new_f.write_all(
desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
- desired.offset().into(),
- )))
+ )?;
+
+ Ok(Some(extracted_path))
}
-pub fn try_extract_macho_fat_archive<'a>(
+pub fn try_extract_macho_fat_archive(
sess: &Session,
- archive_bytes: &'a [u8],
-) -> io::Result<Option<(&'a [u8], u64)>> {
+ archive_path: &Path,
+) -> io::Result<Option<PathBuf>> {
+ let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
let target_arch = match sess.target.arch.as_ref() {
"aarch64" => object::Architecture::Aarch64,
"x86_64" => object::Architecture::X86_64,
_ => return Ok(None),
};
- match object::macho::FatHeader::parse(archive_bytes) {
+ match object::macho::FatHeader::parse(&*archive_map) {
Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
- let archs = object::macho::FatHeader::parse_arch32(archive_bytes);
- try_filter_fat_archs(archs, target_arch, archive_bytes)
+ let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
}
Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
- let archs = object::macho::FatHeader::parse_arch64(archive_bytes);
- try_filter_fat_archs(archs, target_arch, archive_bytes)
+ let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
+ try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
}
// Not a FatHeader at all, just return None.
_ => Ok(None),
archive_path: &Path,
mut skip: Box<dyn FnMut(&str) -> bool + 'static>,
) -> io::Result<()> {
- let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
+ let mut archive_path = archive_path.to_path_buf();
+ if self.sess.target.llvm_target.contains("-apple-macosx") {
+ if let Some(new_archive_path) =
+ try_extract_macho_fat_archive(&self.sess, &archive_path)?
+ {
+ archive_path = new_archive_path
+ }
+ }
+
if self.src_archives.iter().any(|archive| archive.0 == archive_path) {
return Ok(());
}
- let (archive_bytes, offset) = if self.sess.target.llvm_target.contains("-apple-macosx") {
- if let Some((sub_archive, archive_offset)) =
- try_extract_macho_fat_archive(&self.sess, &*archive_map)?
- {
- (sub_archive, Some(archive_offset))
- } else {
- (&*archive_map, None)
- }
- } else {
- (&*archive_map, None)
- };
-
- let archive = ArchiveFile::parse(&*archive_bytes)
+ let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? };
+ let archive = ArchiveFile::parse(&*archive_map)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
let archive_index = self.src_archives.len();
let file_name = String::from_utf8(entry.name().to_vec())
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?;
if !skip(&file_name) {
- let mut range = entry.file_range();
- if let Some(offset) = offset {
- range.0 += offset;
- }
self.entries.push((
file_name.into_bytes(),
- ArchiveEntry::FromArchive { archive_index, file_range: range },
+ ArchiveEntry::FromArchive { archive_index, file_range: entry.file_range() },
));
}
}
/// Extract all symbols defined in raw-dylib libraries, collated by library name.
///
/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
-/// then the CodegenResults value contains one NativeLib instance for each block. However, the
+/// then the CodegenResults value contains one NativeLib instance for each block. However, the
/// linker appears to expect only a single import library for each library used, so we need to
/// collate the symbols together by library name before generating the import libraries.
fn collate_raw_dylibs<'a, 'b>(
cg_results: &CodegenResults,
executable_out_filename: &Path,
) {
- let dwp_out_filename = executable_out_filename.with_extension("dwp");
+ let mut dwp_out_filename = executable_out_filename.to_path_buf().into_os_string();
+ dwp_out_filename.push(".dwp");
debug!(?dwp_out_filename, ?executable_out_filename);
#[derive(Default)]
if cfg!(any(target_os = "solaris", target_os = "illumos")) {
// On historical Solaris systems, "cc" may have
// been Sun Studio, which is not flag-compatible
- // with "gcc". This history casts a long shadow,
+ // with "gcc". This history casts a long shadow,
// and many modern illumos distributions today
// ship GCC as "gcc" without also making it
// available as "cc".
return (false, false);
}
- // If we're only producing artifacts that are archives, no need to preserve
- // the objects as they're losslessly contained inside the archives.
- if sess.crate_types().iter().all(|&x| x.is_archive()) {
- return (false, false);
- }
-
match (sess.split_debuginfo(), sess.opts.unstable_opts.split_dwarf_kind) {
// If there is no split debuginfo then do not preserve objects.
(SplitDebuginfo::Off, _) => (false, false),
// link times negatively.
//
// -dead_strip can't be part of the pre_link_args because it's also used
- // for partial linking when using multiple codegen units (-r). So we
+ // for partial linking when using multiple codegen units (-r). So we
// insert it here.
if self.sess.target.is_like_osx {
self.linker_arg("-dead_strip");
return &[];
}
- let mut symbols: Vec<_> = tcx
- .reachable_non_generics(LOCAL_CRATE)
- .iter()
- .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
- .collect();
+ // FIXME: Sorting this is unnecessary since we are sorting later anyway.
+ // Can we skip the later sorting?
+ let mut symbols: Vec<_> = tcx.with_stable_hashing_context(|hcx| {
+ tcx.reachable_non_generics(LOCAL_CRATE)
+ .to_sorted(&hcx, true)
+ .into_iter()
+ .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
+ .collect()
+ });
if tcx.entry_fn(()).is_some() {
let exported_symbol =
pub emit_thin_lto: bool,
pub bc_cmdline: String,
- // Miscellaneous flags. These are mostly copied from command-line
+ // Miscellaneous flags. These are mostly copied from command-line
// options.
pub verify_llvm_ir: bool,
pub no_prepopulate_passes: bool,
let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| {
if compiled_modules.modules.len() == 1 {
- // 1) Only one codegen unit. In this case it's no difficulty
+ // 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
let module_name = Some(&compiled_modules.modules[0].name[..]);
let path = crate_output.temp_path(output_type, module_name);
.to_owned();
if crate_output.outputs.contains_key(&output_type) {
- // 2) Multiple codegen units, with `--emit foo=some_name`. We have
+ // 2) Multiple codegen units, with `--emit foo=some_name`. We have
// no good solution for this case, so warn the user.
sess.emit_warning(errors::IgnoringEmitPath { extension });
} else if crate_output.single_output_file.is_some() {
- // 3) Multiple codegen units, with `-o some_name`. We have
+ // 3) Multiple codegen units, with `-o some_name`. We have
// no good solution for this case, so warn the user.
sess.emit_warning(errors::IgnoringOutput { extension });
} else {
- // 4) Multiple codegen units, but no explicit name. We
+ // 4) Multiple codegen units, but no explicit name. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
}
match *output_type {
OutputType::Bitcode => {
user_wants_bitcode = true;
- // Copy to .bc, but always keep the .0.bc. There is a later
+ // Copy to .bc, but always keep the .0.bc. There is a later
// check to figure out if we should delete .0.bc files, or keep
// them for making an rlib.
copy_if_one_unit(OutputType::Bitcode, true);
// `-C save-temps` or `--emit=` flags).
if !sess.opts.cg.save_temps {
- // Remove the temporary .#module-name#.o objects. If the user didn't
+ // Remove the temporary .#module-name#.o objects. If the user didn't
// explicitly request bitcode (with --emit=bc), and the bitcode is not
// needed for building an rlib, then we must remove .#module-name#.bc as
// well.
cx.type_func(&[], cx.type_int())
};
- let main_ret_ty = cx.tcx().fn_sig(rust_main_def_id).output();
+ let main_ret_ty = cx.tcx().fn_sig(rust_main_def_id).no_bound_vars().unwrap().output();
// Given that `main()` has no arguments,
// then its return type cannot have
// late-bound regions, since late-bound
};
let (defids, _) = tcx.collect_and_partition_mono_items(cratenum);
- for id in &*defids {
+
+ let any_for_speed = defids.items().any(|id| {
let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
match optimize {
- attr::OptimizeAttr::None => continue,
- attr::OptimizeAttr::Size => continue,
- attr::OptimizeAttr::Speed => {
- return for_speed;
- }
+ attr::OptimizeAttr::None | attr::OptimizeAttr::Size => false,
+ attr::OptimizeAttr::Speed => true,
}
+ });
+
+ if any_for_speed {
+ return for_speed;
}
+
tcx.sess.opts.optimize
};
}
}
} else if attr.has_name(sym::cmse_nonsecure_entry) {
if validate_fn_only_attr(attr.span)
- && !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. })
+ && !matches!(tcx.fn_sig(did).skip_binder().abi(), abi::Abi::C { .. })
{
struct_span_err!(
tcx.sess,
} else if attr.has_name(sym::track_caller) {
if !tcx.is_closure(did.to_def_id())
&& validate_fn_only_attr(attr.span)
- && tcx.fn_sig(did).abi() != abi::Abi::Rust
+ && tcx.fn_sig(did).skip_binder().abi() != abi::Abi::Rust
{
struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
.emit();
}
} else if attr.has_name(sym::target_feature) {
if !tcx.is_closure(did.to_def_id())
- && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal
+ && tcx.fn_sig(did).skip_binder().unsafety() == hir::Unsafety::Normal
{
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
// The `#[target_feature]` attribute is allowed on
sole_meta_list
{
// According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
- // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
+ // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
// to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
//
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
// both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
- // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
+ // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
// for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
// library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
// if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
| ty::Placeholder(..)
| ty::Alias(..)
| ty::Bound(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::GeneratorWitness(..) => {
bug!(
"debuginfo: Trying to create type name for \
// Arguments get assigned to by means of the function being called
for arg in mir.args_iter() {
- analyzer.assign(arg, mir::START_BLOCK.start_location());
+ analyzer.assign(arg, DefLocation::Argument);
}
// If there exists a local definition that dominates all uses of that local,
/// A scalar or a scalar pair local that is neither defined nor used.
Unused,
/// A scalar or a scalar pair local with a single definition that dominates all uses.
- SSA(mir::Location),
+ SSA(DefLocation),
+}
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+enum DefLocation {
+ Argument,
+ Body(Location),
+}
+
+impl DefLocation {
+ fn dominates(self, location: Location, dominators: &Dominators<mir::BasicBlock>) -> bool {
+ match self {
+ DefLocation::Argument => true,
+ DefLocation::Body(def) => def.successor_within_block().dominates(location, dominators),
+ }
+ }
}
struct LocalAnalyzer<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
}
impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> LocalAnalyzer<'mir, 'a, 'tcx, Bx> {
- fn assign(&mut self, local: mir::Local, location: Location) {
+ fn assign(&mut self, local: mir::Local, location: DefLocation) {
let kind = &mut self.locals[local];
match *kind {
LocalKind::ZST => {}
LocalKind::Memory => {}
- LocalKind::Unused => {
- *kind = LocalKind::SSA(location);
- }
- LocalKind::SSA(_) => {
- *kind = LocalKind::Memory;
- }
+ LocalKind::Unused => *kind = LocalKind::SSA(location),
+ LocalKind::SSA(_) => *kind = LocalKind::Memory,
}
}
debug!("visit_assign(place={:?}, rvalue={:?})", place, rvalue);
if let Some(local) = place.as_local() {
- self.assign(local, location);
+ self.assign(local, DefLocation::Body(location));
if self.locals[local] != LocalKind::Memory {
let decl_span = self.fx.mir.local_decls[local].source_info.span;
if !self.fx.rvalue_creates_operand(rvalue, decl_span) {
match context {
PlaceContext::MutatingUse(MutatingUseContext::Call)
| PlaceContext::MutatingUse(MutatingUseContext::Yield) => {
- self.assign(local, location);
+ self.assign(local, DefLocation::Body(location));
}
PlaceContext::NonUse(_) | PlaceContext::MutatingUse(MutatingUseContext::Retag) => {}
struct TerminatorCodegenHelper<'tcx> {
bb: mir::BasicBlock,
terminator: &'tcx mir::Terminator<'tcx>,
- funclet_bb: Option<mir::BasicBlock>,
}
impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
&self,
fx: &'b mut FunctionCx<'a, 'tcx, Bx>,
) -> Option<&'b Bx::Funclet> {
- let funclet_bb = self.funclet_bb?;
- if base::wants_msvc_seh(fx.cx.tcx().sess) {
- // If `landing_pad_for` hasn't been called yet to create the `Funclet`,
- // it has to be now. This may not seem necessary, as RPO should lead
- // to all the unwind edges being visited (and so to `landing_pad_for`
- // getting called for them), before building any of the blocks inside
- // the funclet itself - however, if MIR contains edges that end up not
- // being needed in the LLVM IR after monomorphization, the funclet may
- // be unreachable, and we don't have yet a way to skip building it in
- // such an eventuality (which may be a better solution than this).
- if fx.funclets[funclet_bb].is_none() {
- fx.landing_pad_for(funclet_bb);
- }
-
- Some(
- fx.funclets[funclet_bb]
- .as_ref()
- .expect("landing_pad_for didn't also create funclets entry"),
- )
- } else {
- None
+ let cleanup_kinds = (&fx.cleanup_kinds).as_ref()?;
+ let funclet_bb = cleanup_kinds[self.bb].funclet_bb(self.bb)?;
+ // If `landing_pad_for` hasn't been called yet to create the `Funclet`,
+ // it has to be now. This may not seem necessary, as RPO should lead
+ // to all the unwind edges being visited (and so to `landing_pad_for`
+ // getting called for them), before building any of the blocks inside
+ // the funclet itself - however, if MIR contains edges that end up not
+ // being needed in the LLVM IR after monomorphization, the funclet may
+ // be unreachable, and we don't have yet a way to skip building it in
+ // such an eventuality (which may be a better solution than this).
+ if fx.funclets[funclet_bb].is_none() {
+ fx.landing_pad_for(funclet_bb);
}
+ Some(
+ fx.funclets[funclet_bb]
+ .as_ref()
+ .expect("landing_pad_for didn't also create funclets entry"),
+ )
}
/// Get a basic block (creating it if necessary), possibly with cleanup
fx: &mut FunctionCx<'a, 'tcx, Bx>,
target: mir::BasicBlock,
) -> (bool, bool) {
- let target_funclet = fx.cleanup_kinds[target].funclet_bb(target);
- let (needs_landing_pad, is_cleanupret) = match (self.funclet_bb, target_funclet) {
- (None, None) => (false, false),
- (None, Some(_)) => (true, false),
- (Some(_), None) => {
- let span = self.terminator.source_info.span;
- span_bug!(span, "{:?} - jump out of cleanup?", self.terminator);
- }
- (Some(f), Some(t_f)) => {
- if f == t_f || !base::wants_msvc_seh(fx.cx.tcx().sess) {
- (false, false)
- } else {
- (true, true)
+ if let Some(ref cleanup_kinds) = fx.cleanup_kinds {
+ let funclet_bb = cleanup_kinds[self.bb].funclet_bb(self.bb);
+ let target_funclet = cleanup_kinds[target].funclet_bb(target);
+ let (needs_landing_pad, is_cleanupret) = match (funclet_bb, target_funclet) {
+ (None, None) => (false, false),
+ (None, Some(_)) => (true, false),
+ (Some(f), Some(t_f)) => (f != t_f, f != t_f),
+ (Some(_), None) => {
+ let span = self.terminator.source_info.span;
+ span_bug!(span, "{:?} - jump out of cleanup?", self.terminator);
}
- }
- };
- (needs_landing_pad, is_cleanupret)
+ };
+ (needs_landing_pad, is_cleanupret)
+ } else {
+ let needs_landing_pad = !fx.mir[self.bb].is_cleanup && fx.mir[target].is_cleanup;
+ let is_cleanupret = false;
+ (needs_landing_pad, is_cleanupret)
+ }
}
fn funclet_br<Bx: BuilderMethods<'a, 'tcx>>(
let layout = bx.layout_of(ty);
let do_panic = match intrinsic {
Inhabited => layout.abi.is_uninhabited(),
- ZeroValid => !bx.tcx().permits_zero_init(layout),
- MemUninitializedValid => !bx.tcx().permits_uninit_init(layout),
+ ZeroValid => !bx.tcx().permits_zero_init(bx.param_env().and(layout)),
+ MemUninitializedValid => !bx.tcx().permits_uninit_init(bx.param_env().and(layout)),
};
Some(if do_panic {
let msg_str = with_no_visible_paths!({
) -> MergingSucc {
debug!("codegen_terminator: {:?}", terminator);
- // Create the cleanup bundle, if needed.
- let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
- let helper = TerminatorCodegenHelper { bb, terminator, funclet_bb };
+ let helper = TerminatorCodegenHelper { bb, terminator };
let mergeable_succ = || {
// Note: any call to `switch_to_block` will invalidate a `true` value
match (src.layout.abi, dst.layout.abi) {
(abi::Abi::Scalar(src_scalar), abi::Abi::Scalar(dst_scalar)) => {
// HACK(eddyb) LLVM doesn't like `bitcast`s between pointers and non-pointers.
- let src_is_ptr = src_scalar.primitive() == abi::Pointer;
- let dst_is_ptr = dst_scalar.primitive() == abi::Pointer;
+ let src_is_ptr = matches!(src_scalar.primitive(), abi::Pointer(_));
+ let dst_is_ptr = matches!(dst_scalar.primitive(), abi::Pointer(_));
if src_is_ptr == dst_is_ptr {
assert_eq!(src.layout.size, dst.layout.size);
}
impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
- /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
+ /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
/// it may so happen that the current span belongs to a different file than the DIScope
- /// corresponding to span's containing source scope. If so, we need to create a DIScope
+ /// corresponding to span's containing source scope. If so, we need to create a DIScope
/// "extension" into that file.
pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
&self,
+use crate::base;
use crate::traits::*;
use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>>,
/// The funclet status of each basic block
- cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
+ cleanup_kinds: Option<IndexVec<mir::BasicBlock, analyze::CleanupKind>>,
/// When targeting MSVC, this stores the cleanup info for each funclet BB.
/// This is initialized at the same time as the `landing_pads` entry for the
start_bx.set_personality_fn(cx.eh_personality());
}
- let cleanup_kinds = analyze::cleanup_kinds(&mir);
+ let cleanup_kinds =
+ if base::wants_msvc_seh(cx.tcx().sess) { Some(analyze::cleanup_kinds(&mir)) } else { None };
+
let cached_llbbs: IndexVec<mir::BasicBlock, CachedLlbb<Bx::BasicBlock>> =
mir.basic_blocks
.indices()
use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, Ty};
-use rustc_target::abi::{Abi, Align, FieldsShape, Int, TagEncoding};
+use rustc_target::abi::{Abi, Align, FieldsShape, Int, Pointer, TagEncoding};
use rustc_target::abi::{VariantIdx, Variants};
#[derive(Copy, Clone, Debug)]
bx: &mut Bx,
cast_to: Ty<'tcx>,
) -> V {
+ let dl = &bx.tcx().data_layout;
let cast_to_layout = bx.cx().layout_of(cast_to);
let cast_to_size = cast_to_layout.layout.size();
let cast_to = bx.cx().immediate_backend_type(cast_to_layout);
TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
// Cast to an integer so we don't have to treat a pointer as a
// special case.
- let (tag, tag_llty) = if tag_scalar.primitive().is_ptr() {
- let t = bx.type_isize();
- let tag = bx.ptrtoint(tag_imm, t);
- (tag, t)
- } else {
- (tag_imm, bx.cx().immediate_backend_type(tag_op.layout))
+ let (tag, tag_llty) = match tag_scalar.primitive() {
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ Pointer(_) => {
+ let t = bx.type_from_integer(dl.ptr_sized_integer());
+ let tag = bx.ptrtoint(tag_imm, t);
+ (tag, t)
+ }
+ _ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
};
let tag_size = tag_scalar.size(bx.cx());
mir::StatementKind::FakeRead(..)
| mir::StatementKind::Retag { .. }
| mir::StatementKind::AscribeUserType(..)
+ | mir::StatementKind::ConstEvalCounter
| mir::StatementKind::Nop => {}
}
}
impl fmt::Display for ConstEvalErrKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use self::ConstEvalErrKind::*;
- match *self {
+ match self {
ConstAccessesStatic => write!(f, "constant accesses static"),
ModifiedGlobal => {
write!(f, "modifying a static's initial value from another static's initializer")
}
- AssertFailure(ref msg) => write!(f, "{:?}", msg),
+ AssertFailure(msg) => write!(f, "{:?}", msg),
Panic { msg, line, col, file } => {
write!(f, "the evaluated program panicked at '{}', {}:{}:{}", msg, file, line, col)
}
- Abort(ref msg) => write!(f, "{}", msg),
+ Abort(msg) => write!(f, "{}", msg),
}
}
}
if cfg!(debug_assertions) && stab.promotable {
let sig = tcx.fn_sig(def_id);
assert_eq!(
- sig.unsafety(),
+ sig.skip_binder().unsafety(),
hir::Unsafety::Normal,
"don't mark const unsafe fns as promotable",
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
/// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
/// may not have an address.
///
- /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should
+ /// If `ptr` does have a known address, then we return `Continue(())` and the function call should
/// proceed as normal.
///
/// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most
ret,
StackPopUnwind::NotAllowed,
)?;
- Ok(ControlFlow::BREAK)
+ Ok(ControlFlow::Break(()))
} else {
// Not alignable in const, return `usize::MAX`.
let usize_max = Scalar::from_machine_usize(self.machine_usize_max(), self);
self.write_scalar(usize_max, dest)?;
self.return_to_block(ret)?;
- Ok(ControlFlow::BREAK)
+ Ok(ControlFlow::Break(()))
}
}
Err(_addr) => {
// The pointer has an address, continue with function call.
- Ok(ControlFlow::CONTINUE)
+ Ok(ControlFlow::Continue(()))
}
}
}
// Only check non-glue functions
if let ty::InstanceDef::Item(def) = instance.def {
// Execution might have wandered off into other crates, so we cannot do a stability-
- // sensitive check here. But we can at least rule out functions that are not const
+ // sensitive check here. But we can at least rule out functions that are not const
// at all.
if !ecx.tcx.is_const_fn_raw(def.did) {
// allow calling functions inside a trait marked with #[const_trait].
let eval_to_int =
|op| ecx.read_immediate(&ecx.eval_operand(op, None)?).map(|x| x.to_const_int());
let err = match msg {
- BoundsCheck { ref len, ref index } => {
+ BoundsCheck { len, index } => {
let len = eval_to_int(len)?;
let index = eval_to_int(index)?;
BoundsCheck { len, index }
throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time");
}
- fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
- // The step limit has already been hit in a previous call to `before_terminator`.
+ fn increment_const_eval_counter(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
+ // The step limit has already been hit in a previous call to `increment_const_eval_counter`.
if ecx.machine.steps_remaining == 0 {
return Ok(());
}
// FIXME(oli-obk): we can probably encode closures just like structs
| ty::Closure(..)
| ty::Generator(..)
- | ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType),
+ | ty::GeneratorWitness(..) |ty::GeneratorWitnessMIR(..)=> Err(ValTreeCreationError::NonSupportedType),
}
}
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::FnPtr(_)
| ty::RawPtr(_)
| ty::Str
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
}
- (_, &ty::Dynamic(ref data, _, ty::Dyn)) => {
+ (_, &ty::Dynamic(data, _, ty::Dyn)) => {
// Initial cast from sized to dyn trait
let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
let ptr = self.read_scalar(src)?;
}
}
- /// Overwrite the local. If the local can be overwritten in place, return a reference
+ /// Overwrite the local. If the local can be overwritten in place, return a reference
/// to do so; otherwise return the `MemPlace` to consult instead.
///
/// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from
);
// Recurse to get the size of the dynamically sized field (must be
- // the last field). Can't have foreign types here, how would we
+ // the last field). Can't have foreign types here, how would we
// adjust alignment and size for them?
let field = layout.field(self, layout.fields.count() - 1);
let Some((unsized_size, mut unsized_align)) = self.size_and_align_of(metadata, &field)? else {
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
- /// A static and its current mutability. Below shared references inside a `static mut`,
+ /// A static and its current mutability. Below shared references inside a `static mut`,
/// this is *immutable*, and below mutable references inside an `UnsafeCell`, this
/// is *mutable*.
Static(hir::Mutability),
}
}
InternMode::Const => {
- // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity
+ // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity
// checking for mutable references that we encounter -- they must all be
// ZST.
InternMode::Const
/// Intern `ret` and everything it references.
///
-/// This *cannot raise an interpreter error*. Doing so is left to validation, which
+/// This *cannot raise an interpreter error*. Doing so is left to validation, which
/// tracks where in the value we are and thus can show much better error messages.
#[instrument(level = "debug", skip(ecx))]
pub fn intern_const_alloc_recursive<
inside_unsafe_cell: false,
}
.visit_value(&mplace);
- // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining
+ // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining
// references are "leftover"-interned, and later validation will show a proper error
// and point at the right part of the value causing the problem.
match res {
return Err(reported);
} else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
// We have hit an `AllocId` that is neither in local or global memory and isn't
- // marked as dangling by local memory. That should be impossible.
+ // marked as dangling by local memory. That should be impossible.
span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);
}
}
}
sym::variant_count => match tp_ty.kind() {
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
- ty::Adt(ref adt, _) => {
- ConstValue::from_machine_usize(adt.variants().len() as u64, &tcx)
- }
+ ty::Adt(adt, _) => ConstValue::from_machine_usize(adt.variants().len() as u64, &tcx),
ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => {
throw_inval!(TooGeneric)
}
| ty::Closure(_, _)
| ty::Generator(_, _, _)
| ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(_, _)
| ty::Never
| ty::Tuple(_)
| ty::Error(_) => ConstValue::from_machine_usize(0u64, &tcx),
}
if intrinsic_name == sym::assert_zero_valid {
- let should_panic = !self.tcx.permits_zero_init(layout);
+ let should_panic = !self.tcx.permits_zero_init(self.param_env.and(layout));
if should_panic {
M::abort(
}
if intrinsic_name == sym::assert_mem_uninitialized_valid {
- let should_panic = !self.tcx.permits_uninit_init(layout);
+ let should_panic = !self.tcx.permits_uninit_init(self.param_env.and(layout));
if should_panic {
M::abort(
unwind: StackPopUnwind,
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
- /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
+ /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
/// pointer as appropriate.
fn call_extra_fn(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
}
/// Called before a basic block terminator is executed.
- /// You can use this to detect endlessly running programs.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
Ok(())
}
+ /// Called when the interpreter encounters a `StatementKind::ConstEvalCounter` instruction.
+ /// You can use this to detect long or endlessly running programs.
+ #[inline]
+ fn increment_const_eval_counter(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
+ Ok(())
+ }
+
/// Called before a global allocation is accessed.
/// `def_id` is `Some` if this is the "lazy" allocation of a static.
#[inline]
}
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
-/// (CTFE and ConstProp) use the same instance. Here, we share that code.
+/// (CTFE and ConstProp) use the same instance. Here, we share that code.
pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
type Provenance = AllocId;
type ProvenanceExtra = ();
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
- /// the machine pointer to the allocation. Must never be used
+ /// the machine pointer to the allocation. Must never be used
/// for any other pointers, nor for TLS statics.
///
/// Using the resulting pointer represents a *direct* access to that memory
&self,
id: AllocId,
) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> {
- // The error type of the inner closure here is somewhat funny. We have two
+ // The error type of the inner closure here is somewhat funny. We have two
// ways of "erroring": An actual error, or because we got a reference from
// `get_global_alloc` that we can actually use directly without inserting anything anywhere.
// So the error type is `InterpResult<'tcx, &Allocation<M::Provenance>>`.
write!(fmt, "{id:?}")?;
match self.ecx.memory.alloc_map.get(id) {
- Some(&(kind, ref alloc)) => {
+ Some((kind, alloc)) => {
// normal alloc
write!(fmt, " ({}, ", kind)?;
write_allocation_track_relocs(
assert_eq!(size, mplace.layout.size, "abi::Scalar size does not match layout size");
let scalar = alloc.read_scalar(
alloc_range(Size::ZERO, size),
- /*read_provenance*/ s.is_ptr(),
+ /*read_provenance*/ matches!(s, abi::Pointer(_)),
)?;
Some(ImmTy { imm: scalar.into(), layout: mplace.layout })
}
assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields
let a_val = alloc.read_scalar(
alloc_range(Size::ZERO, a_size),
- /*read_provenance*/ a.is_ptr(),
+ /*read_provenance*/ matches!(a, abi::Pointer(_)),
)?;
let b_val = alloc.read_scalar(
alloc_range(b_offset, b_size),
- /*read_provenance*/ b.is_ptr(),
+ /*read_provenance*/ matches!(b, abi::Pointer(_)),
)?;
Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })
}
Ok(OpTy { op, layout: place.layout, align: Some(place.align) })
}
- /// Evaluate a place with the goal of reading from it. This lets us sometimes
+ /// Evaluate a place with the goal of reading from it. This lets us sometimes
/// avoid allocations.
pub fn eval_place_to_op(
&self,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
use rustc_middle::mir::Operand::*;
- let op = match *mir_op {
+ let op = match mir_op {
// FIXME: do some more logic on `move` to invalidate the old location
- Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?,
+ &Copy(place) | &Move(place) => self.eval_place_to_op(place, layout)?,
- Constant(ref constant) => {
+ Constant(constant) => {
let c =
self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
_ => bug!("len not supported on unsized type {:?}", self.layout.ty),
}
} else {
- // Go through the layout. There are lots of types that support a length,
+ // Go through the layout. There are lots of types that support a length,
// e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
match self.layout.fields {
abi::FieldsShape::Array { count, .. } => Ok(count),
M: Machine<'mir, 'tcx, Provenance = Prov>,
{
/// Take a value, which represents a (thin or wide) reference, and make it a place.
- /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
+ /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
///
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
/// want to ever use the place for memory access!
&mut Operand::Immediate(local_val) => {
// We need to make an allocation.
- // We need the layout of the local. We can NOT use the layout we got,
+ // We need the layout of the local. We can NOT use the layout we got,
// that might e.g., be an inner field of a struct with `Scalar` layout,
// that has different alignment than the outer field.
let local_layout =
M::retag_place_contents(self, *kind, &dest)?;
}
- Intrinsic(box ref intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
+ Intrinsic(box intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
// Statements we do not track.
AscribeUserType(..) => {}
// FIXME(#73156): Handle source code coverage in const eval
Coverage(..) => {}
+ ConstEvalCounter => {
+ M::increment_const_eval_counter(self)?;
+ }
+
// Defined to do nothing. These are added by optimization passes, to avoid changing the
// size of MIR constantly.
Nop => {}
self.copy_op(&op, &dest, /*allow_transmute*/ false)?;
}
- CopyForDeref(ref place) => {
- let op = self.eval_place_to_op(*place, Some(dest.layout))?;
+ CopyForDeref(place) => {
+ let op = self.eval_place_to_op(place, Some(dest.layout))?;
self.copy_op(&op, &dest, /* allow_transmute*/ false)?;
}
// they go to.
// For where they come from: If the ABI is RustCall, we untuple the
- // last incoming argument. These two iterators do not have the same type,
+ // last incoming argument. These two iterators do not have the same type,
// so to keep the code paths uniform we accept an allocation
// (for RustCall ABI only).
let caller_args: Cow<'_, [OpTy<'tcx, M::Provenance>]> =
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
// Now we have to spread them out across the callee's locals,
- // taking into account the `spread_arg`. If we could write
+ // taking into account the `spread_arg`. If we could write
// this is a single iterator (that handles `spread_arg`), then
// `pass_argument` would be the loop body. It takes care to
// not advance `caller_iter` for ZSTs.
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
- // We take the address of the object. This may well be unaligned, which is fine
- // for us here. However, unaligned accesses will probably make the actual drop
+ // We take the address of the object. This may well be unaligned, which is fine
+ // for us here. However, unaligned accesses will probably make the actual drop
// implementation fail -- a problem shared by rustc.
let place = self.force_allocation(place)?;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.needs_subst() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match *ty.kind() {
return subst.visit_with(self);
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => ty.super_visit_with(self),
}
TupleElem(idx) => write!(out, ".{}", idx),
ArrayElem(idx) => write!(out, "[{}]", idx),
// `.<deref>` does not match Rust syntax, but it is more readable for long paths -- and
- // some of the other items here also are not Rust syntax. Actually we can't
+ // some of the other items here also are not Rust syntax. Actually we can't
// even use the usual syntax because we are just showing the projections,
// not the root.
Deref => write!(out, ".<deref>"),
)
}
// Recursive checking
- if let Some(ref mut ref_tracking) = self.ref_tracking {
+ if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
// Proceed recursively even for ZST, no reason to skip them!
// `!` is a ZST and we want to validate it.
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
}
/// Check if this is a value of primitive type, and if yes check the validity of the value
- /// at that type. Return `true` if the type is indeed primitive.
+ /// at that type. Return `true` if the type is indeed primitive.
fn try_visit_primitive(
&mut self,
value: &OpTy<'tcx, M::Provenance>,
| ty::Bound(..)
| ty::Param(..)
| ty::Alias(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty),
}
}
// Can only happen during CTFE.
// We support 2 kinds of ranges here: full range, and excluding zero.
if start == 1 && end == max_value {
- // Only null is the niche. So make sure the ptr is NOT null.
+ // Only null is the niche. So make sure the ptr is NOT null.
if self.ecx.scalar_may_be_null(scalar)? {
throw_validation_failure!(self.path,
{ "a potentially null pointer" }
// Recursively walk the value at its type.
self.walk_value(op)?;
- // *After* all of this, check the ABI. We need to check the ABI to handle
+ // *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
// the fields say (`rustc_layout_scalar_valid_range_start`).
// But in most cases, this will just propagate what the fields say,
// Optimization: we just check the entire range at once.
// NOTE: Keep this in sync with the handling of integer and float
// types above, in `visit_primitive`.
- // In run-time mode, we accept pointers in here. This is actually more
+ // In run-time mode, we accept pointers in here. This is actually more
// permissive than a per-element check would be, e.g., we accept
// a &[u8] that contains a pointer even though bytewise checking would
- // reject it. However, that's good: We don't inherently want
+ // reject it. However, that's good: We don't inherently want
// to reject those pointers, we just do not have the machinery to
// talk about parts of a pointer.
// We also accept uninit, for consistency with the slow path.
};
// Visit the fields of this value.
- match v.layout().fields {
+ match &v.layout().fields {
FieldsShape::Primitive => {}
- FieldsShape::Union(fields) => {
+ &FieldsShape::Union(fields) => {
self.visit_union(v, fields)?;
}
- FieldsShape::Arbitrary { ref offsets, .. } => {
+ FieldsShape::Arbitrary { offsets, .. } => {
// FIXME: We collect in a vec because otherwise there are lifetime
// errors: Projecting to a field needs access to `ecx`.
let fields: Vec<InterpResult<'tcx, Self::V>> =
#![feature(assert_matches)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(decl_macro)]
#![feature(exact_size_is_empty)]
#![feature(let_chains)]
let (param_env, value) = param_env_and_value.into_parts();
const_eval::deref_mir_constant(tcx, param_env, value)
};
- providers.permits_uninit_init =
- |tcx, ty| util::might_permit_raw_init(tcx, ty, InitKind::UninitMitigated0x01Fill);
- providers.permits_zero_init = |tcx, ty| util::might_permit_raw_init(tcx, ty, InitKind::Zero);
+ providers.permits_uninit_init = |tcx, param_env_and_ty| {
+ let (param_env, ty) = param_env_and_ty.into_parts();
+ util::might_permit_raw_init(tcx, param_env, ty, InitKind::UninitMitigated0x01Fill)
+ };
+ providers.permits_zero_init = |tcx, param_env_and_ty| {
+ let (param_env, ty) = param_env_and_ty.into_parts();
+ util::might_permit_raw_init(tcx, param_env, ty, InitKind::Zero)
+ };
}
self.super_rvalue(rvalue, location);
- match *rvalue {
+ match rvalue {
Rvalue::ThreadLocalRef(_) => self.check_op(ops::ThreadLocalAccess),
Rvalue::Use(_)
| Rvalue::Discriminant(..)
| Rvalue::Len(_) => {}
- Rvalue::Aggregate(ref kind, ..) => {
- if let AggregateKind::Generator(def_id, ..) = kind.as_ref() {
- if let Some(generator_kind) = self.tcx.generator_kind(def_id.to_def_id()) {
- if matches!(generator_kind, hir::GeneratorKind::Async(..)) {
- self.check_op(ops::Generator(generator_kind));
- }
- }
+ Rvalue::Aggregate(kind, ..) => {
+ if let AggregateKind::Generator(def_id, ..) = kind.as_ref()
+ && let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id.to_def_id())
+ {
+ self.check_op(ops::Generator(generator_kind));
}
}
- Rvalue::Ref(_, kind @ BorrowKind::Mut { .. }, ref place)
- | Rvalue::Ref(_, kind @ BorrowKind::Unique, ref place) => {
+ Rvalue::Ref(_, kind @ (BorrowKind::Mut { .. } | BorrowKind::Unique), place) => {
let ty = place.ty(self.body, self.tcx).ty;
let is_allowed = match ty.kind() {
// Inside a `static mut`, `&mut [...]` is allowed.
}
}
- Rvalue::AddressOf(Mutability::Mut, ref place) => {
+ Rvalue::AddressOf(Mutability::Mut, place) => {
self.check_mut_borrow(place.local, hir::BorrowKind::Raw)
}
- Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, ref place)
- | Rvalue::AddressOf(Mutability::Not, ref place) => {
+ Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Shallow, place)
+ | Rvalue::AddressOf(Mutability::Not, place) => {
let borrowed_place_has_mut_interior = qualifs::in_place::<HasMutInterior, _>(
&self.ccx,
&mut |local| self.qualifs.has_mut_interior(self.ccx, local, location),
Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) => {}
Rvalue::ShallowInitBox(_, _) => {}
- Rvalue::UnaryOp(_, ref operand) => {
+ Rvalue::UnaryOp(_, operand) => {
let ty = operand.ty(self.body, self.tcx);
if is_int_bool_or_char(ty) {
// Int, bool, and char operations are fine.
}
}
- Rvalue::BinaryOp(op, box (ref lhs, ref rhs))
- | Rvalue::CheckedBinaryOp(op, box (ref lhs, ref rhs)) => {
+ Rvalue::BinaryOp(op, box (lhs, rhs))
+ | Rvalue::CheckedBinaryOp(op, box (lhs, rhs)) => {
let lhs_ty = lhs.ty(self.body, self.tcx);
let rhs_ty = rhs.ty(self.body, self.tcx);
} else if lhs_ty.is_fn_ptr() || lhs_ty.is_unsafe_ptr() {
assert_eq!(lhs_ty, rhs_ty);
assert!(
- op == BinOp::Eq
- || op == BinOp::Ne
- || op == BinOp::Le
- || op == BinOp::Lt
- || op == BinOp::Ge
- || op == BinOp::Gt
- || op == BinOp::Offset
+ matches!(
+ op,
+ BinOp::Eq
+ | BinOp::Ne
+ | BinOp::Le
+ | BinOp::Lt
+ | BinOp::Ge
+ | BinOp::Gt
+ | BinOp::Offset
+ )
);
self.check_op(ops::RawPtrComparison);
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
}
let ocx = ObligationCtxt::new(&infcx);
let predicates = tcx.predicates_of(callee).instantiate(tcx, substs);
- let hir_id = tcx
- .hir()
- .local_def_id_to_hir_id(self.body.source.def_id().expect_local());
let cause = ObligationCause::new(
terminator.source_info.span,
- hir_id,
+ self.body.source.def_id().expect_local(),
ObligationCauseCode::ItemObligation(callee),
);
let normalized_predicates = ocx.normalize(&cause, param_env, predicates);
let ty::Closure(_, substs) = ty.kind() else { bug!("type_of closure not ty::Closure") };
substs.as_closure().sig()
} else {
- self.tcx.fn_sig(did)
+ self.tcx.fn_sig(did).subst_identity()
}
}
}
}
_ => { /* mark as unpromotable below */ }
}
- } else if let TempState::Defined { ref mut uses, .. } = *temp {
+ } else if let TempState::Defined { uses, .. } = temp {
// We always allow borrows, even mutable ones, as we need
// to promote mutable borrows of some ZSTs e.g., `&mut []`.
let allowed_use = match context {
if loc.statement_index < num_stmts {
let (mut rvalue, source_info) = {
let statement = &mut self.source[loc.block].statements[loc.statement_index];
- let StatementKind::Assign(box (_, ref mut rhs)) = statement.kind else {
+ let StatementKind::Assign(box (_, rhs)) = &mut statement.kind else {
span_bug!(
statement.source_info.span,
"{:?} is not an assignment",
self.source[loc.block].terminator().clone()
} else {
let terminator = self.source[loc.block].terminator_mut();
- let target = match terminator.kind {
- TerminatorKind::Call { target: Some(target), .. } => target,
- ref kind => {
+ let target = match &terminator.kind {
+ TerminatorKind::Call { target: Some(target), .. } => *target,
+ kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
..terminator
};
}
- ref kind => {
+ kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
let local_decls = &mut self.source.local_decls;
let loc = candidate.location;
let statement = &mut blocks[loc.block].statements[loc.statement_index];
- match statement.kind {
- StatementKind::Assign(box (
- _,
- Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
- )) => {
- // Use the underlying local for this (necessarily interior) borrow.
- let ty = local_decls[place.local].ty;
- let span = statement.source_info.span;
-
- let ref_ty = tcx.mk_ref(
- tcx.lifetimes.re_erased,
- ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
- );
+ let StatementKind::Assign(box (_, Rvalue::Ref(region, borrow_kind, place))) = &mut statement.kind else {
+ bug!()
+ };
- *region = tcx.lifetimes.re_erased;
-
- let mut projection = vec![PlaceElem::Deref];
- projection.extend(place.projection);
- place.projection = tcx.intern_place_elems(&projection);
-
- // Create a temp to hold the promoted reference.
- // This is because `*r` requires `r` to be a local,
- // otherwise we would use the `promoted` directly.
- let mut promoted_ref = LocalDecl::new(ref_ty, span);
- promoted_ref.source_info = statement.source_info;
- let promoted_ref = local_decls.push(promoted_ref);
- assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
-
- let promoted_ref_statement = Statement {
- source_info: statement.source_info,
- kind: StatementKind::Assign(Box::new((
- Place::from(promoted_ref),
- Rvalue::Use(promoted_operand(ref_ty, span)),
- ))),
- };
- self.extra_statements.push((loc, promoted_ref_statement));
-
- Rvalue::Ref(
- tcx.lifetimes.re_erased,
- borrow_kind,
- Place {
- local: mem::replace(&mut place.local, promoted_ref),
- projection: List::empty(),
- },
- )
- }
- _ => bug!(),
- }
+ // Use the underlying local for this (necessarily interior) borrow.
+ let ty = local_decls[place.local].ty;
+ let span = statement.source_info.span;
+
+ let ref_ty = tcx.mk_ref(
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
+ );
+
+ *region = tcx.lifetimes.re_erased;
+
+ let mut projection = vec![PlaceElem::Deref];
+ projection.extend(place.projection);
+ place.projection = tcx.intern_place_elems(&projection);
+
+ // Create a temp to hold the promoted reference.
+ // This is because `*r` requires `r` to be a local,
+ // otherwise we would use the `promoted` directly.
+ let mut promoted_ref = LocalDecl::new(ref_ty, span);
+ promoted_ref.source_info = statement.source_info;
+ let promoted_ref = local_decls.push(promoted_ref);
+ assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
+
+ let promoted_ref_statement = Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(Box::new((
+ Place::from(promoted_ref),
+ Rvalue::Use(promoted_operand(ref_ty, span)),
+ ))),
+ };
+ self.extra_statements.push((loc, promoted_ref_statement));
+
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ *borrow_kind,
+ Place {
+ local: mem::replace(&mut place.local, promoted_ref),
+ projection: List::empty(),
+ },
+ )
};
assert_eq!(self.new_block(), START_BLOCK);
//! Validates the MIR to ensure that invariants are upheld.
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
use rustc_infer::traits::Reveal;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
Terminator, TerminatorKind, UnOp, START_BLOCK,
};
-use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_target::abi::{Size, VariantIdx};
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind {
Unwind,
Normal,
.iterate_to_fixpoint()
.into_results_cursor(body);
- TypeChecker {
+ let mut checker = TypeChecker {
when: &self.when,
body,
tcx,
param_env,
mir_phase,
+ unwind_edge_count: 0,
reachable_blocks: traversal::reachable_as_bitset(body),
storage_liveness,
place_cache: Vec::new(),
value_cache: Vec::new(),
- }
- .visit_body(body);
+ };
+ checker.visit_body(body);
+ checker.check_cleanup_control_flow();
}
}
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
mir_phase: MirPhase,
+ unwind_edge_count: usize,
reachable_blocks: BitSet<BasicBlock>,
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: Vec<PlaceRef<'tcx>>,
);
}
- fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
+ fn check_edge(&mut self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
if bb == START_BLOCK {
self.fail(location, "start block must not have predecessors")
}
match (src.is_cleanup, bb.is_cleanup, edge_kind) {
// Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
(false, false, EdgeKind::Normal)
- // Non-cleanup blocks can jump to cleanup blocks along unwind edges
- | (false, true, EdgeKind::Unwind)
// Cleanup blocks can jump to cleanup blocks along non-unwind edges
| (true, true, EdgeKind::Normal) => {}
+ // Non-cleanup blocks can jump to cleanup blocks along unwind edges
+ (false, true, EdgeKind::Unwind) => {
+ self.unwind_edge_count += 1;
+ }
// All other jumps are invalid
_ => {
self.fail(
}
}
+ fn check_cleanup_control_flow(&self) {
+ if self.unwind_edge_count <= 1 {
+ return;
+ }
+ let doms = self.body.basic_blocks.dominators();
+ let mut post_contract_node = FxHashMap::default();
+ // Reusing the allocation across invocations of the closure
+ let mut dom_path = vec![];
+ let mut get_post_contract_node = |mut bb| {
+ let root = loop {
+ if let Some(root) = post_contract_node.get(&bb) {
+ break *root;
+ }
+ let parent = doms.immediate_dominator(bb);
+ dom_path.push(bb);
+ if !self.body.basic_blocks[parent].is_cleanup {
+ break bb;
+ }
+ bb = parent;
+ };
+ for bb in dom_path.drain(..) {
+ post_contract_node.insert(bb, root);
+ }
+ root
+ };
+
+ let mut parent = IndexVec::from_elem(None, &self.body.basic_blocks);
+ for (bb, bb_data) in self.body.basic_blocks.iter_enumerated() {
+ if !bb_data.is_cleanup || !self.reachable_blocks.contains(bb) {
+ continue;
+ }
+ let bb = get_post_contract_node(bb);
+ for s in bb_data.terminator().successors() {
+ let s = get_post_contract_node(s);
+ if s == bb {
+ continue;
+ }
+ let parent = &mut parent[bb];
+ match parent {
+ None => {
+ *parent = Some(s);
+ }
+ Some(e) if *e == s => (),
+ Some(e) => self.fail(
+ Location { block: bb, statement_index: 0 },
+ format!(
+ "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}",
+ bb,
+ s,
+ *e
+ )
+ ),
+ }
+ }
+ }
+
+ // Check for cycles
+ let mut stack = FxHashSet::default();
+ for i in 0..parent.len() {
+ let mut bb = BasicBlock::from_usize(i);
+ stack.clear();
+ stack.insert(bb);
+ loop {
+ let Some(parent)= parent[bb].take() else {
+ break
+ };
+ let no_cycle = stack.insert(parent);
+ if !no_cycle {
+ self.fail(
+ Location { block: bb, statement_index: 0 },
+ format!(
+ "Cleanup control flow violation: Cycle involving edge {:?} -> {:?}",
+ bb, parent,
+ ),
+ );
+ break;
+ }
+ bb = parent;
+ }
+ }
+ }
+
/// Check if src can be assigned into dest.
/// This is not precise, it will accept some incorrect assignments.
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
// Equal types, all is good.
return true;
}
- // Normalization reveals opaque types, but we may be validating MIR while computing
- // said opaque types, causing cycles.
- if (src, dest).has_opaque_types() {
- return true;
- }
crate::util::is_subtype(self.tcx, self.param_env, src, dest)
}
return;
};
- let Some(&f_ty) = layout.field_tys.get(local) else {
+ let Some(f_ty) = layout.field_tys.get(local) else {
self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty));
return;
};
- f_ty
+ f_ty.ty
} else {
let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
fail_out_of_bounds(self, location);
StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
| StatementKind::Coverage(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
self_arg: Option<Ident>,
desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
method_did: DefId,
+ method_substs: SubstsRef<'tcx>,
},
/// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
} else {
None
};
- CallKind::Normal { self_arg, desugaring, method_did }
+ CallKind::Normal { self_arg, desugaring, method_did, method_substs }
})
}
/// to the full uninit check).
pub fn might_permit_raw_init<'tcx>(
tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
ty: TyAndLayout<'tcx>,
kind: InitKind,
) -> bool {
if tcx.sess.opts.unstable_opts.strict_init_checks {
might_permit_raw_init_strict(ty, tcx, kind)
} else {
- let layout_cx = LayoutCx { tcx, param_env: ParamEnv::reveal_all() };
+ let layout_cx = LayoutCx { tcx, param_env };
might_permit_raw_init_lax(ty, &layout_cx, kind)
}
}
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
ty::GeneratorWitness(_) => bug!("type_name: unexpected `GeneratorWitness`"),
+ ty::GeneratorWitnessMIR(..) => bug!("type_name: unexpected `GeneratorWitnessMIR`"),
}
}
//! ```
//!
//! `Frozen` impls `Deref`, so we can ergonomically call methods on `Bar`, but it doesn't `impl
-//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that
+//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that
//! `mutate` requires a mutable reference but we don't have one.
//!
//! # Caveats
#[macro_export]
macro_rules! define_id_collections {
($map_name:ident, $set_name:ident, $entry_name:ident, $key:ty) => {
- pub type $map_name<T> = $crate::fx::FxHashMap<$key, T>;
- pub type $set_name = $crate::fx::FxHashSet<$key>;
+ pub type $map_name<T> = $crate::unord::UnordMap<$key, T>;
+ pub type $set_name = $crate::unord::UnordSet<$key>;
pub type $entry_name<'a, T> = $crate::fx::StdEntry<'a, $key, T>;
};
}
// This loop computes the semi[w] for w.
semi[w] = w;
for v in graph.predecessors(pre_order_to_real[w]) {
- let v = real_to_pre_order[v].unwrap();
+ // TL;DR: Reachable vertices may have unreachable predecessors, so ignore any of them.
+ //
+ // Ignore blocks which are not connected to the entry block.
+ //
+ // The algorithm that was used to traverse the graph and build the
+ // `pre_order_to_real` and `real_to_pre_order` vectors does so by
+ // starting from the entry block and following the successors.
+ // Therefore, any blocks not reachable from the entry block will be
+ // set to `None` in the `pre_order_to_real` vector.
+ //
+ // For example, in this graph, A and B should be skipped:
+ //
+ // ┌─────┐
+ // │ │
+ // └──┬──┘
+ // │
+ // ┌──▼──┐ ┌─────┐
+ // │ │ │ A │
+ // └──┬──┘ └──┬──┘
+ // │ │
+ // ┌───────┴───────┐ │
+ // │ │ │
+ // ┌──▼──┐ ┌──▼──┐ ┌──▼──┐
+ // │ │ │ │ │ B │
+ // └──┬──┘ └──┬──┘ └──┬──┘
+ // │ └──────┬─────┘
+ // ┌──▼──┐ │
+ // │ │ │
+ // └──┬──┘ ┌──▼──┐
+ // │ │ │
+ // │ └─────┘
+ // ┌──▼──┐
+ // │ │
+ // └──┬──┘
+ // │
+ // ┌──▼──┐
+ // │ │
+ // └─────┘
+ //
+ // ...this may be the case if a MirPass modifies the CFG to remove
+ // or rearrange certain blocks/edges.
+ let Some(v) = real_to_pre_order[v] else {
+ continue
+ };
// eval returns a vertex x from which semi[x] is minimum among
// vertices semi[v] +> x *> v.
}
}
+/// Tracks the list of dominators for each node.
#[derive(Clone, Debug)]
pub struct Dominators<N: Idx> {
post_order_rank: IndexVec<N, usize>,
+ // Even though we track only the immediate dominator of each node, it's
+ // possible to get its full list of dominators by looking up the dominator
+ // of each dominator. (See the `impl Iterator for Iter` definition).
immediate_dominators: IndexVec<N, Option<N>>,
}
impl<Node: Idx> Dominators<Node> {
- pub fn dummy() -> Self {
- Self { post_order_rank: IndexVec::new(), immediate_dominators: IndexVec::new() }
- }
-
+ /// Whether the given Node has an immediate dominator.
pub fn is_reachable(&self, node: Node) -> bool {
self.immediate_dominators[node].is_some()
}
self.immediate_dominators[node].unwrap()
}
+ /// Provides an iterator over each dominator up the CFG, for the given Node.
+ /// See the `impl Iterator for Iter` definition to understand how this works.
pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
assert!(self.is_reachable(node), "node {node:?} is not reachable");
Iter { dominators: self, node: Some(node) }
}
- pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
+ pub fn dominates(&self, dom: Node, node: Node) -> bool {
// FIXME -- could be optimized by using post-order-rank
self.dominators(node).any(|n| n == dom)
}
/// of two unrelated nodes will also be consistent, but otherwise the order has no
/// meaning.) This method cannot be used to determine if either Node dominates the other.
pub fn rank_partial_cmp(&self, lhs: Node, rhs: Node) -> Option<Ordering> {
- self.post_order_rank[lhs].partial_cmp(&self.post_order_rank[rhs])
+ self.post_order_rank[rhs].partial_cmp(&self.post_order_rank[lhs])
}
}
"counter={:?} expected={:?} edge_index={:?} edge={:?}",
counter, expected_incoming[counter], edge_index, edge
);
- match expected_incoming[counter] {
- (ref e, ref n) => {
+ match &expected_incoming[counter] {
+ (e, n) => {
assert!(e == &edge.data);
assert!(n == graph.node_data(edge.source()));
assert!(start_index == edge.target);
"counter={:?} expected={:?} edge_index={:?} edge={:?}",
counter, expected_outgoing[counter], edge_index, edge
);
- match expected_outgoing[counter] {
- (ref e, ref n) => {
+ match &expected_outgoing[counter] {
+ (e, n) => {
assert!(e == &edge.data);
assert!(start_index == edge.source);
assert!(n == graph.node_data(edge.target));
_node: G::Node,
_prior_status: Option<NodeStatus>,
) -> ControlFlow<Self::BreakVal> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
/// Called after all nodes reachable from this one have been examined.
fn node_settled(&mut self, _node: G::Node) -> ControlFlow<Self::BreakVal> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
/// Behave as if no edges exist from `source` to `target`.
prior_status: Option<NodeStatus>,
) -> ControlFlow<Self::BreakVal> {
match prior_status {
- Some(NodeStatus::Visited) => ControlFlow::BREAK,
- _ => ControlFlow::CONTINUE,
+ Some(NodeStatus::Visited) => ControlFlow::Break(()),
+ _ => ControlFlow::Continue(()),
}
}
}
// 0 -> 1 -> 2 -> 1
//
// and at this point detect a cycle. The state of 2 will thus be
- // `InCycleWith { 1 }`. We will then visit the 1 -> 3 edge, which
+ // `InCycleWith { 1 }`. We will then visit the 1 -> 3 edge, which
// will attempt to visit 0 as well, thus going to the state
// `InCycleWith { 0 }`. Finally, node 1 will complete; the lowest
// depth of any successor was 3 which had depth 0, and thus it
#![feature(associated_type_bounds)]
#![feature(auto_traits)]
#![feature(cell_leak)]
-#![feature(control_flow_enum)]
#![feature(extend_one)]
#![feature(hash_raw_entry)]
#![feature(hasher_prefixfree_extras)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
struct ObligationTreeId(usize);
-type ObligationTreeIdGenerator =
- std::iter::Map<std::ops::RangeFrom<usize>, fn(usize) -> ObligationTreeId>;
+type ObligationTreeIdGenerator = impl Iterator<Item = ObligationTreeId>;
pub struct ObligationForest<O: ForestObligation> {
/// The list of obligations. In between calls to [Self::process_obligations],
use crate::stable_hasher::{HashStable, StableHasher, StableOrd};
use std::borrow::Borrow;
-use std::cmp::Ordering;
use std::fmt::Debug;
use std::mem;
use std::ops::{Bound, Index, IndexMut, RangeBounds};
where
F: Fn(&mut K),
{
- self.data.iter_mut().map(|&mut (ref mut k, _)| k).for_each(f);
+ self.data.iter_mut().map(|(k, _)| k).for_each(f);
}
/// Inserts a presorted range of elements into the map. If the range can be
R: RangeBounds<K>,
{
let start = match range.start_bound() {
- Bound::Included(ref k) => match self.lookup_index_for(k) {
+ Bound::Included(k) => match self.lookup_index_for(k) {
Ok(index) | Err(index) => index,
},
- Bound::Excluded(ref k) => match self.lookup_index_for(k) {
+ Bound::Excluded(k) => match self.lookup_index_for(k) {
Ok(index) => index + 1,
Err(index) => index,
},
};
let end = match range.end_bound() {
- Bound::Included(ref k) => match self.lookup_index_for(k) {
+ Bound::Included(k) => match self.lookup_index_for(k) {
Ok(index) => index + 1,
Err(index) => index,
},
- Bound::Excluded(ref k) => match self.lookup_index_for(k) {
+ Bound::Excluded(k) => match self.lookup_index_for(k) {
Ok(index) | Err(index) => index,
},
Bound::Unbounded => self.data.len(),
let mut data: Vec<(K, V)> = iter.into_iter().collect();
data.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
- data.dedup_by(|&mut (ref k1, _), &mut (ref k2, _)| k1.cmp(k2) == Ordering::Equal);
+ data.dedup_by(|(k1, _), (k2, _)| k1 == k2);
SortedMap { data }
}
/// Returns an iterator over the items in the map in insertion order.
#[inline]
pub fn iter(&self) -> impl '_ + DoubleEndedIterator<Item = (&K, &V)> {
- self.items.iter().map(|(ref k, ref v)| (k, v))
+ self.items.iter().map(|(k, v)| (k, v))
}
/// Returns an iterator over the items in the map in insertion order along with their indices.
#[inline]
pub fn iter_enumerated(&self) -> impl '_ + DoubleEndedIterator<Item = (I, (&K, &V))> {
- self.items.iter_enumerated().map(|(i, (ref k, ref v))| (i, (k, v)))
+ self.items.iter_enumerated().map(|(i, (k, v))| (i, (k, v)))
}
/// Returns the item in the map with the given index.
let set: SortedIndexMultiMap<usize, _, _> = entries.iter().copied().collect();
// Insertion order is preserved.
- assert!(entries.iter().map(|(ref k, ref v)| (k, v)).eq(set.iter()));
+ assert!(entries.iter().map(|(k, v)| (k, v)).eq(set.iter()));
// Indexing
for (i, expect) in entries.iter().enumerate() {
#[inline]
pub fn remove(&mut self, data: &T) -> bool {
- self.head = match self.head {
- Some(ref mut head) if head.data == *data => head.next.take().map(|x| *x),
- Some(ref mut head) => return head.remove_next(data),
+ self.head = match &mut self.head {
+ Some(head) if head.data == *data => head.next.take().map(|x| *x),
+ Some(head) => return head.remove_next(data),
None => return false,
};
true
#[inline]
pub fn contains(&self, data: &T) -> bool {
let mut elem = self.head.as_ref();
- while let Some(ref e) = elem {
+ while let Some(e) = elem {
if &e.data == data {
return true;
}
}
impl<T: PartialEq> Element<T> {
- fn remove_next(&mut self, data: &T) -> bool {
- let mut n = self;
+ fn remove_next(mut self: &mut Self, data: &T) -> bool {
loop {
- match n.next {
+ match self.next {
Some(ref mut next) if next.data == *data => {
- n.next = next.next.take();
+ self.next = next.next.take();
return true;
}
- Some(ref mut next) => n = next,
+ Some(ref mut next) => self = next,
None => return false,
}
}
impl<T> TinyList<T> {
fn len(&self) -> usize {
let (mut elem, mut count) = (self.head.as_ref(), 0);
- while let Some(ref e) = elem {
+ while let Some(e) = elem {
count += 1;
elem = e.next.as_deref();
}
// values. So here is what we do:
//
// 1. Find the vector `[X | a < X && b < X]` of all values
- // `X` where `a < X` and `b < X`. In terms of the
+ // `X` where `a < X` and `b < X`. In terms of the
// graph, this means all values reachable from both `a`
// and `b`. Note that this vector is also a set, but we
// use the term vector because the order matters
use smallvec::SmallVec;
use std::{
borrow::Borrow,
+ collections::hash_map::Entry,
hash::Hash,
iter::{Product, Sum},
+ ops::Index,
};
use crate::{
fingerprint::Fingerprint,
- stable_hasher::{HashStable, StableHasher, ToStableHashKey},
+ stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey},
};
/// `UnordItems` is the order-less version of `Iterator`. It only contains methods
}
#[inline]
- pub fn all<U, F: Fn(T) -> bool>(mut self, f: F) -> bool {
+ pub fn all<F: Fn(T) -> bool>(mut self, f: F) -> bool {
self.0.all(f)
}
#[inline]
- pub fn any<U, F: Fn(T) -> bool>(mut self, f: F) -> bool {
+ pub fn any<F: Fn(T) -> bool>(mut self, f: F) -> bool {
self.0.any(f)
}
#[inline]
- pub fn filter<U, F: Fn(&T) -> bool>(self, f: F) -> UnordItems<T, impl Iterator<Item = T>> {
+ pub fn filter<F: Fn(&T) -> bool>(self, f: F) -> UnordItems<T, impl Iterator<Item = T>> {
UnordItems(self.0.filter(f))
}
pub fn count(self) -> usize {
self.0.count()
}
+
+ #[inline]
+ pub fn flat_map<U, F, O>(self, f: F) -> UnordItems<O, impl Iterator<Item = O>>
+ where
+ U: IntoIterator<Item = O>,
+ F: Fn(T) -> U,
+ {
+ UnordItems(self.0.flat_map(f))
+ }
}
impl<'a, T: Clone + 'a, I: Iterator<Item = &'a T>> UnordItems<&'a T, I> {
}
impl<V: Eq + Hash> Default for UnordSet<V> {
+ #[inline]
fn default() -> Self {
Self { inner: FxHashSet::default() }
}
}
#[inline]
- pub fn items(&self) -> UnordItems<&V, impl Iterator<Item = &V>> {
+ pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> bool
+ where
+ V: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.remove(k)
+ }
+
+ #[inline]
+ pub fn items<'a>(&'a self) -> UnordItems<&'a V, impl Iterator<Item = &'a V>> {
UnordItems(self.inner.iter())
}
UnordItems(self.inner.into_iter())
}
+ /// Returns the items of this set in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `V` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn to_sorted<HCX>(&self, hcx: &HCX, cache_sort_key: bool) -> Vec<&V>
+ where
+ V: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&x| x)
+ }
+
+ /// Returns the items of this set in stable sort order (as defined by
+ /// `StableOrd`). This method is much more efficient than
+ /// `into_sorted` because it does not need to transform keys to their
+ /// `ToStableHashKey` equivalent.
+ #[inline]
+ pub fn to_sorted_stable_ord(&self) -> Vec<V>
+ where
+ V: Ord + StableOrd + Copy,
+ {
+ let mut items: Vec<V> = self.inner.iter().copied().collect();
+ items.sort_unstable();
+ items
+ }
+
+ /// Returns the items of this set in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `V` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn into_sorted<HCX>(self, hcx: &HCX, cache_sort_key: bool) -> Vec<V>
+ where
+ V: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |x| x)
+ }
+
// We can safely extend this UnordSet from a set of unordered values because that
// won't expose the internal ordering anywhere.
#[inline]
pub fn extend<I: Iterator<Item = V>>(&mut self, items: UnordItems<V, I>) {
self.inner.extend(items.0)
}
+
+ #[inline]
+ pub fn clear(&mut self) {
+ self.inner.clear();
+ }
}
impl<V: Hash + Eq> Extend<V> for UnordSet<V> {
+ #[inline]
fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
self.inner.extend(iter)
}
}
+impl<V: Hash + Eq> FromIterator<V> for UnordSet<V> {
+ #[inline]
+ fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
+ UnordSet { inner: FxHashSet::from_iter(iter) }
+ }
+}
+
impl<HCX, V: Hash + Eq + HashStable<HCX>> HashStable<HCX> for UnordSet<V> {
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
}
impl<K: Eq + Hash, V> Default for UnordMap<K, V> {
+ #[inline]
fn default() -> Self {
Self { inner: FxHashMap::default() }
}
}
impl<K: Hash + Eq, V> Extend<(K, V)> for UnordMap<K, V> {
+ #[inline]
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
self.inner.extend(iter)
}
}
+impl<K: Hash + Eq, V> FromIterator<(K, V)> for UnordMap<K, V> {
+ #[inline]
+ fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
+ UnordMap { inner: FxHashMap::from_iter(iter) }
+ }
+}
+
+impl<K: Hash + Eq, V, I: Iterator<Item = (K, V)>> From<UnordItems<(K, V), I>> for UnordMap<K, V> {
+ #[inline]
+ fn from(items: UnordItems<(K, V), I>) -> Self {
+ UnordMap { inner: FxHashMap::from_iter(items.0) }
+ }
+}
+
impl<K: Eq + Hash, V> UnordMap<K, V> {
#[inline]
pub fn len(&self) -> usize {
}
#[inline]
- pub fn items(&self) -> UnordItems<(&K, &V), impl Iterator<Item = (&K, &V)>> {
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ #[inline]
+ pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
+ self.inner.entry(key)
+ }
+
+ #[inline]
+ pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
+ where
+ K: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.get(k)
+ }
+
+ #[inline]
+ pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
+ where
+ K: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.get_mut(k)
+ }
+
+ #[inline]
+ pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
+ where
+ K: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.remove(k)
+ }
+
+ #[inline]
+ pub fn items<'a>(&'a self) -> UnordItems<(&'a K, &'a V), impl Iterator<Item = (&'a K, &'a V)>> {
UnordItems(self.inner.iter())
}
pub fn extend<I: Iterator<Item = (K, V)>>(&mut self, items: UnordItems<(K, V), I>) {
self.inner.extend(items.0)
}
+
+ /// Returns the entries of this map in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `K` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn to_sorted<HCX>(&self, hcx: &HCX, cache_sort_key: bool) -> Vec<(&K, &V)>
+ where
+ K: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k)
+ }
+
+ /// Returns the entries of this map in stable sort order (as defined by `StableOrd`).
+ /// This method can be much more efficient than `into_sorted` because it does not need
+ /// to transform keys to their `ToStableHashKey` equivalent.
+ #[inline]
+ pub fn to_sorted_stable_ord(&self) -> Vec<(K, &V)>
+ where
+ K: Ord + StableOrd + Copy,
+ {
+ let mut items: Vec<(K, &V)> = self.inner.iter().map(|(&k, v)| (k, v)).collect();
+ items.sort_unstable_by_key(|&(k, _)| k);
+ items
+ }
+
+ /// Returns the entries of this map in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `K` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn into_sorted<HCX>(self, hcx: &HCX, cache_sort_key: bool) -> Vec<(K, V)>
+ where
+ K: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |(k, _)| k)
+ }
+
+ /// Returns the values of this map in stable sort order (as defined by K's
+ /// `ToStableHashKey` implementation).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `K` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn values_sorted<HCX>(&self, hcx: &HCX, cache_sort_key: bool) -> impl Iterator<Item = &V>
+ where
+ K: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k)
+ .into_iter()
+ .map(|(_, v)| v)
+ }
+}
+
+impl<K, Q: ?Sized, V> Index<&Q> for UnordMap<K, V>
+where
+ K: Eq + Hash + Borrow<Q>,
+ Q: Eq + Hash,
+{
+ type Output = V;
+
+ #[inline]
+ fn index(&self, key: &Q) -> &V {
+ &self.inner[key]
+ }
}
impl<HCX, K: Hash + Eq + HashStable<HCX>, V: HashStable<HCX>> HashStable<HCX> for UnordMap<K, V> {
}
}
+impl<T, I: Iterator<Item = T>> From<UnordItems<T, I>> for UnordBag<T> {
+ fn from(value: UnordItems<T, I>) -> Self {
+ UnordBag { inner: Vec::from_iter(value.0) }
+ }
+}
+
impl<HCX, V: Hash + Eq + HashStable<HCX>> HashStable<HCX> for UnordBag<V> {
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
}
}
+#[inline]
+fn to_sorted_vec<HCX, T, K, I>(
+ hcx: &HCX,
+ iter: I,
+ cache_sort_key: bool,
+ extract_key: fn(&T) -> &K,
+) -> Vec<T>
+where
+ I: Iterator<Item = T>,
+ K: ToStableHashKey<HCX>,
+{
+ let mut items: Vec<T> = iter.collect();
+ if cache_sort_key {
+ items.sort_by_cached_key(|x| extract_key(x).to_stable_hash_key(hcx));
+ } else {
+ items.sort_unstable_by_key(|x| extract_key(x).to_stable_hash_key(hcx));
+ }
+
+ items
+}
+
fn hash_iter_order_independent<
HCX,
T: HashStable<HCX>,
The `driver` crate is effectively the "main" function for the rust
-compiler. It orchestrates the compilation process and "knits together"
+compiler. It orchestrates the compilation process and "knits together"
the code from the other crates within rustc. This crate itself does
not contain any of the "main logic" of the compiler (though it does
have some code related to pretty printing or other minor compiler
crate_cfg: cfg,
crate_check_cfg: check_cfg,
input: Input::File(PathBuf::new()),
- input_path: None,
output_file: ofile,
output_dir: odir,
file_loader,
match make_input(config.opts.error_format, &matches.free) {
Err(reported) => return Err(reported),
- Ok(Some((input, input_file_path))) => {
+ Ok(Some(input)) => {
config.input = input;
- config.input_path = input_file_path;
callbacks.config(&mut config);
}
describe_lints(compiler.session(), &lint_store, registered_lints);
return;
}
- let should_stop = print_crate_info(
- &***compiler.codegen_backend(),
- compiler.session(),
- None,
- compiler.output_dir(),
- compiler.output_file(),
- compiler.temps_dir(),
- );
+ let should_stop =
+ print_crate_info(&***compiler.codegen_backend(), compiler.session(), false);
if should_stop == Compilation::Stop {
return;
interface::run_compiler(config, |compiler| {
let sess = compiler.session();
- let should_stop = print_crate_info(
- &***compiler.codegen_backend(),
- sess,
- Some(compiler.input()),
- compiler.output_dir(),
- compiler.output_file(),
- compiler.temps_dir(),
- )
- .and_then(|| {
- list_metadata(sess, &*compiler.codegen_backend().metadata_loader(), compiler.input())
- })
- .and_then(|| try_process_rlink(sess, compiler));
+ let should_stop = print_crate_info(&***compiler.codegen_backend(), sess, true)
+ .and_then(|| list_metadata(sess, &*compiler.codegen_backend().metadata_loader()))
+ .and_then(|| try_process_rlink(sess, compiler));
if should_stop == Compilation::Stop {
return sess.compile_status();
if let Some(ppm) = &sess.opts.pretty {
if ppm.needs_ast_map() {
- let expanded_crate = queries.expansion()?.borrow().0.clone();
queries.global_ctxt()?.enter(|tcx| {
- pretty::print_after_hir_lowering(
- tcx,
- compiler.input(),
- &*expanded_crate,
- *ppm,
- compiler.output_file().as_deref(),
- );
+ pretty::print_after_hir_lowering(tcx, *ppm);
Ok(())
})?;
} else {
let krate = queries.parse()?.steal();
- pretty::print_after_parsing(
- sess,
- compiler.input(),
- &krate,
- *ppm,
- compiler.output_file().as_deref(),
- );
+ pretty::print_after_parsing(sess, &krate, *ppm);
}
trace!("finished pretty-printing");
return early_exit();
}
}
- queries.expansion()?;
+ let mut gctxt = queries.global_ctxt()?;
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
return early_exit();
}
- queries.prepare_outputs()?;
+ // Make sure the `output_filenames` query is run for its side
+ // effects of writing the dep-info and reporting errors.
+ gctxt.enter(|tcx| tcx.output_filenames(()));
if sess.opts.output_types.contains_key(&OutputType::DepInfo)
&& sess.opts.output_types.len() == 1
return early_exit();
}
- queries.global_ctxt()?;
-
if sess.opts.unstable_opts.no_analysis {
return early_exit();
}
- queries.global_ctxt()?.enter(|tcx| {
+ gctxt.enter(|tcx| {
let result = tcx.analysis(());
if sess.opts.unstable_opts.save_analysis {
let crate_name = tcx.crate_name(LOCAL_CRATE);
save::process_crate(
tcx,
crate_name,
- compiler.input(),
+ &sess.io.input,
None,
- DumpHandler::new(compiler.output_dir().as_deref(), crate_name),
+ DumpHandler::new(sess.io.output_dir.as_deref(), crate_name),
)
});
}
result
})?;
+ drop(gctxt);
+
if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
return early_exit();
}
fn make_input(
error_format: ErrorOutputType,
free_matches: &[String],
-) -> Result<Option<(Input, Option<PathBuf>)>, ErrorGuaranteed> {
+) -> Result<Option<Input>, ErrorGuaranteed> {
if free_matches.len() == 1 {
let ifile = &free_matches[0];
if ifile == "-" {
let line = isize::from_str_radix(&line, 10)
.expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
- Ok(Some((Input::Str { name: file_name, input: src }, None)))
+ Ok(Some(Input::Str { name: file_name, input: src }))
} else {
- Ok(Some((Input::Str { name: FileName::anon_source_code(&src), input: src }, None)))
+ Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src }))
}
} else {
- Ok(Some((Input::File(PathBuf::from(ifile)), Some(PathBuf::from(ifile)))))
+ Ok(Some(Input::File(PathBuf::from(ifile))))
}
} else {
Ok(None)
pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
if sess.opts.unstable_opts.link_only {
- if let Input::File(file) = compiler.input() {
+ if let Input::File(file) = &sess.io.input {
// FIXME: #![crate_type] and #![crate_name] support not implemented yet
sess.init_crate_types(collect_crate_types(sess, &[]));
let outputs = compiler.build_output_filenames(sess, &[]);
}
}
-pub fn list_metadata(
- sess: &Session,
- metadata_loader: &dyn MetadataLoader,
- input: &Input,
-) -> Compilation {
+pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Compilation {
if sess.opts.unstable_opts.ls {
- match *input {
+ match sess.io.input {
Input::File(ref ifile) => {
let path = &(*ifile);
let mut v = Vec::new();
fn print_crate_info(
codegen_backend: &dyn CodegenBackend,
sess: &Session,
- input: Option<&Input>,
- odir: &Option<PathBuf>,
- ofile: &Option<PathBuf>,
- temps_dir: &Option<PathBuf>,
+ parse_attrs: bool,
) -> Compilation {
use rustc_session::config::PrintRequest::*;
// NativeStaticLibs and LinkArgs are special - printed during linking
return Compilation::Continue;
}
- let attrs = match input {
- None => None,
- Some(input) => {
- let result = parse_crate_attrs(sess, input);
- match result {
- Ok(attrs) => Some(attrs),
- Err(mut parse_error) => {
- parse_error.emit();
- return Compilation::Stop;
- }
+ let attrs = if parse_attrs {
+ let result = parse_crate_attrs(sess);
+ match result {
+ Ok(attrs) => Some(attrs),
+ Err(mut parse_error) => {
+ parse_error.emit();
+ return Compilation::Stop;
}
}
+ } else {
+ None
};
for req in &sess.opts.prints {
match *req {
println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
}
FileNames | CrateName => {
- let input = input.unwrap_or_else(|| {
- early_error(ErrorOutputType::default(), "no input file provided")
- });
let attrs = attrs.as_ref().unwrap();
- let t_outputs = rustc_interface::util::build_output_filenames(
- input, odir, ofile, temps_dir, attrs, sess,
- );
- let id = rustc_session::output::find_crate_name(sess, attrs, input);
+ let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
+ let id = rustc_session::output::find_crate_name(sess, attrs);
if *req == PrintRequest::CrateName {
println!("{id}");
continue;
Some(matches)
}
-fn parse_crate_attrs<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::AttrVec> {
- match input {
+fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
+ match &sess.io.input {
Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess),
Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str(
name.clone(),
use rustc_middle::hir::map as hir_map;
use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::config::{Input, PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
+use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
use rustc_session::Session;
use rustc_span::symbol::Ident;
use rustc_span::FileName;
use std::cell::Cell;
use std::fmt::Write;
-use std::path::Path;
pub use self::PpMode::*;
pub use self::PpSourceMode::*;
}
}
-fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
- let src_name = input.source_name();
+fn get_source(sess: &Session) -> (String, FileName) {
+ let src_name = sess.io.input.source_name();
let src = String::clone(
sess.source_map()
.get_source_file(&src_name)
(src, src_name)
}
-fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
- match ofile {
+fn write_or_print(out: &str, sess: &Session) {
+ match &sess.io.output_file {
None => print!("{out}"),
Some(p) => {
if let Err(e) = std::fs::write(p, out) {
}
}
-pub fn print_after_parsing(
- sess: &Session,
- input: &Input,
- krate: &ast::Crate,
- ppm: PpMode,
- ofile: Option<&Path>,
-) {
- let (src, src_name) = get_source(input, sess);
+pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) {
+ let (src, src_name) = get_source(sess);
let out = match ppm {
Source(s) => {
_ => unreachable!(),
};
- write_or_print(&out, ofile, sess);
+ write_or_print(&out, sess);
}
-pub fn print_after_hir_lowering<'tcx>(
- tcx: TyCtxt<'tcx>,
- input: &Input,
- krate: &ast::Crate,
- ppm: PpMode,
- ofile: Option<&Path>,
-) {
+pub fn print_after_hir_lowering<'tcx>(tcx: TyCtxt<'tcx>, ppm: PpMode) {
if ppm.needs_analysis() {
- abort_on_err(print_with_analysis(tcx, ppm, ofile), tcx.sess);
+ abort_on_err(print_with_analysis(tcx, ppm), tcx.sess);
return;
}
- let (src, src_name) = get_source(input, tcx.sess);
+ let (src, src_name) = get_source(tcx.sess);
let out = match ppm {
Source(s) => {
let parse = &sess.parse_sess;
pprust::print_crate(
sess.source_map(),
- krate,
+ &tcx.resolver_for_lowering(()).borrow().1,
src_name,
src,
annotation.pp_ann(),
AstTree(PpAstTreeMode::Expanded) => {
debug!("pretty-printing expanded AST");
- format!("{krate:#?}")
+ format!("{:#?}", tcx.resolver_for_lowering(()).borrow().1)
}
Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| {
_ => unreachable!(),
};
- write_or_print(&out, ofile, tcx.sess);
+ write_or_print(&out, tcx.sess);
}
// In an ideal world, this would be a public function called by the driver after
// analysis is performed. However, we want to call `phase_3_run_analysis_passes`
// with a different callback than the standard driver, so that isn't easy.
// Instead, we call that function ourselves.
-fn print_with_analysis(
- tcx: TyCtxt<'_>,
- ppm: PpMode,
- ofile: Option<&Path>,
-) -> Result<(), ErrorGuaranteed> {
+fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuaranteed> {
tcx.analysis(())?;
let out = match ppm {
Mir => {
out
}
+ ThirFlat => {
+ let mut out = String::new();
+ abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
+ debug!("pretty printing THIR flat");
+ for did in tcx.hir().body_owners() {
+ let _ = writeln!(
+ out,
+ "{:?}:\n{}\n",
+ did,
+ tcx.thir_flat(ty::WithOptConstParam::unknown(did))
+ );
+ }
+ out
+ }
+
_ => unreachable!(),
};
- write_or_print(&out, ofile, tcx.sess);
+ write_or_print(&out, tcx.sess);
Ok(())
}
-// Error messages for EXXXX errors. Each message should start and end with a
-// new line, and be wrapped to 80 characters. In vim you can `:set tw=80` and
+// Error messages for EXXXX errors. Each message should start and end with a
+// new line, and be wrapped to 80 characters. In vim you can `:set tw=80` and
// use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
//
// /!\ IMPORTANT /!\
E0786: include_str!("./error_codes/E0786.md"),
E0787: include_str!("./error_codes/E0787.md"),
E0788: include_str!("./error_codes/E0788.md"),
+E0789: include_str!("./error_codes/E0789.md"),
E0790: include_str!("./error_codes/E0790.md"),
E0791: include_str!("./error_codes/E0791.md"),
+E0792: include_str!("./error_codes/E0792.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
// E0721, // `await` keyword
// E0723, // unstable feature in `const` context
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
- E0789, // rustc_allowed_through_unstable_modules without stability attribute
}
#### This error code is internal to the compiler and will not be emitted with normal Rust code.
+#### Note: this error code is no longer emitted by the compiler.
+
+This error code shows the variance of a type's generic parameters.
+
+Erroneous code example:
+
+```compile_fail
+// NOTE: this feature is perma-unstable and should *only* be used for
+// testing purposes.
+#![feature(rustc_attrs)]
+
+#[rustc_variance]
+struct Foo<'a, T> { // error: deliberate error to display type's variance
+ t: &'a mut T,
+}
+```
+
+which produces the following error:
+
+```text
+error: [-, o]
+ --> <anon>:4:1
+ |
+4 | struct Foo<'a, T> {
+ | ^^^^^^^^^^^^^^^^^
+```
+
+*Note that while `#[rustc_variance]` still exists and is used within the*
+*compiler, it no longer is marked as `E0208` and instead has no error code.*
+
+This error is deliberately triggered with the `#[rustc_variance]` attribute
+(`#![feature(rustc_attrs)]` must be enabled) and helps to show you the variance
+of the type's generic parameters. You can read more about variance and
+subtyping in [this section of the Rustnomicon]. For a more in depth look at
+variance (including a more complete list of common variances) see
+[this section of the Reference]. For information on how variance is implemented
+in the compiler, see [this section of `rustc-dev-guide`].
+
+This error can be easily fixed by removing the `#[rustc_variance]` attribute,
+the compiler's suggestion to comment it out can be applied automatically with
+`rustfix`.
+
+[this section of the Rustnomicon]: https://doc.rust-lang.org/nomicon/subtyping.html
+[this section of the Reference]: https://doc.rust-lang.org/reference/subtyping.html#variance
+[this section of `rustc-dev-guide`]: https://rustc-dev-guide.rust-lang.org/variance.html
foo(|| x = 2);
}
-// Attempts to take a mutable reference to closed-over data. Error message
+// Attempts to take a mutable reference to closed-over data. Error message
// reads: `cannot borrow data mutably in a captured outer variable...`
fn mut_addr() {
let mut x = 0u32;
type to a given size, you should provide a size to packed:
```
-#[repr(packed)] // ok!
+#[repr(packed(8))] // ok!
struct Umbrella(i32);
```
access to the fields of the struct when it runs.
This means that when `s` reaches the end of `demo`, its destructor
-gets exclusive access to its `&mut`-borrowed string data. allowing
+gets exclusive access to its `&mut`-borrowed string data. allowing
another borrow of that string data (`p`), to exist across the drop of
`s` would be a violation of the principle that `&mut`-borrows have
exclusive, unaliased access to their referenced data.
```
The items of marker traits cannot be overridden, so there's no need to have them
-when they cannot be changed per-type anyway. If you wanted them for ergonomic
+when they cannot be changed per-type anyway. If you wanted them for ergonomic
reasons, consider making an extension trait instead.
--- /dev/null
+#### This error code is internal to the compiler and will not be emitted with normal Rust code.
+
+The internal `rustc_allowed_through_unstable_modules` attribute must be used
+on an item with a `stable` attribute.
+
+Erroneous code example:
+
+```compile_fail,E0789
+// NOTE: both of these attributes are perma-unstable and should *never* be
+// used outside of the compiler and standard library.
+#![feature(rustc_attrs)]
+#![feature(staged_api)]
+
+#![unstable(feature = "foo_module", reason = "...", issue = "123")]
+
+#[rustc_allowed_through_unstable_modules]
+// #[stable(feature = "foo", since = "1.0")]
+struct Foo;
+// ^^^ error: `rustc_allowed_through_unstable_modules` attribute must be
+// paired with a `stable` attribute
+```
+
+Typically when an item is marked with a `stable` attribute, the modules that
+enclose the item must also be marked with `stable` attributes, otherwise the
+item becomes *de facto* unstable. `#[rustc_allowed_through_unstable_modules]`
+is a workaround which allows an item to "escape" its unstable parent modules.
+This error occurs when an item is marked with
+`#[rustc_allowed_through_unstable_modules]` but no supplementary `stable`
+attribute exists. See [#99288](https://github.com/rust-lang/rust/pull/99288)
+for an example of `#[rustc_allowed_through_unstable_modules]` in use.
--- /dev/null
+A type alias impl trait can only have its hidden type assigned
+when used fully generically (and within their defining scope).
+This means
+
+```compile_fail,E0792
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl std::fmt::Debug;
+
+fn foo() -> Foo<u32> {
+ 5u32
+}
+```
+
+is not accepted. If it were accepted, one could create unsound situations like
+
+```compile_fail,E0792
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl Default;
+
+fn foo() -> Foo<u32> {
+ 5u32
+}
+
+fn main() {
+ let x = Foo::<&'static mut String>::default();
+}
+```
+
+
+Instead you need to make the function generic:
+
+```
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl std::fmt::Debug;
+
+fn foo<U>() -> Foo<U> {
+ 5u32
+}
+```
+
+This means that no matter the generic parameter to `foo`,
+the hidden type will always be `u32`.
+If you want to link the generic parameter to the hidden type,
+you can do that, too:
+
+
+```
+#![feature(type_alias_impl_trait)]
+
+use std::fmt::Debug;
+
+type Foo<T: Debug> = impl Debug;
+
+fn foo<U: Debug>() -> Foo<U> {
+ Vec::<U>::new()
+}
+```
ast_passes_fn_without_body =
free function without a body
.suggestion = provide a definition for the function
- .extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
+
+ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
[value] value
*[other] {$value_place}
} occurs here
+
+borrowck_opaque_type_non_generic_param =
+ expected generic {$kind} parameter, found `{$ty}`
+ .label = {STREQ($ty, "'static") ->
+ [true] cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
+ *[other] this generic parameter must be used with a generic {$kind} parameter
+ }
invalid monomorphization of `{$name}` intrinsic: unsupported {$name} from `{$in_ty}` with element `{$elem_ty}` to `{$ret_ty}`
codegen_gcc_invalid_monomorphization_invalid_bitmask =
- invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
+ invalid monomorphization of `{$name}` intrinsic: invalid bitmask `{$ty}`, expected `u{$expected_int_bits}` or `[u8; {$expected_bytes}]`
codegen_gcc_invalid_monomorphization_simd_shuffle =
invalid monomorphization of `{$name}` intrinsic: simd_shuffle index must be an array of `u32`, got `{$ty}`
codegen_llvm_error_creating_import_library =
Error creating import library for {$lib_name}: {$error}
-codegen_llvm_instrument_coverage_requires_llvm_12 =
- rustc option `-C instrument-coverage` requires LLVM 12 or higher.
-
codegen_llvm_symbol_already_defined =
symbol `{$symbol_name}` is already defined
codegen_ssa_unsupported_arch = unsupported arch `{$arch}` for os `{$os}`
-codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {error}
+codegen_ssa_apple_sdk_error_sdk_path = failed to get {$sdk_name} SDK path: {$error}
-codegen_ssa_read_file = failed to read file: {message}
+codegen_ssa_read_file = failed to read file: {$message}
codegen_ssa_unsupported_link_self_contained = option `-C link-self-contained` is not supported on this target
}{$desc_kind ->
*[should_not_happen] [{$desc_kind}]
[restatic] the static lifetime
- [reempty] the empty lifetime
- [reemptyuni] the empty lifetime in universe {$desc_arg}
[revar] lifetime {$desc_arg}
[as_defined] the lifetime `{$desc_arg}` as defined here
infer_actual_impl_expl_expected_signature_some = {$leading_ellipsis ->
[true] ...
*[false] {""}
-}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{lifetime_1}`...
+}closure with signature `{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...
infer_actual_impl_expl_expected_signature_nothing = {$leading_ellipsis ->
[true] ...
*[false] {""}
infer_actual_impl_expl_expected_passive_some = {$leading_ellipsis ->
[true] ...
*[false] {""}
-}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{lifetime_1}`...
+}`{$trait_path}` would have to be implemented for the type `{$ty_or_sig}`, for some specific lifetime `'{$lifetime_1}`...
infer_actual_impl_expl_expected_passive_nothing = {$leading_ellipsis ->
[true] ...
*[false] {""}
infer_actual_impl_expl_expected_other_some = {$leading_ellipsis ->
[true] ...
*[false] {""}
-}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{lifetime_1}`...
+}`{$ty_or_sig}` must implement `{$trait_path}`, for some specific lifetime `'{$lifetime_1}`...
infer_actual_impl_expl_expected_other_nothing = {$leading_ellipsis ->
[true] ...
*[false] {""}
[true] `{$param_name}`
*[false] `fn` parameter
} has {$lifetime_kind ->
- [named] lifetime `{lifetime}`
- *[anon] an anonymous lifetime `'_`
-} but calling `{assoc_item}` introduces an implicit `'static` lifetime requirement
+ [true] lifetime `{$lifetime}`
+ *[false] an anonymous lifetime `'_`
+} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement
.label1 = {$has_lifetime ->
- [named] lifetime `{lifetime}`
- *[anon] an anonymous lifetime `'_`
+ [true] lifetime `{$lifetime}`
+ *[false] an anonymous lifetime `'_`
}
.label2 = ...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path ->
- [named] `impl` of `{$impl_path}`
- *[anon] inherent `impl`
+ [true] `impl` of `{$impl_path}`
+ *[false] inherent `impl`
}
infer_but_needs_to_satisfy = {$has_param_name ->
[true] `{$param_name}`
*[false] `fn` parameter
} has {$has_lifetime ->
- [named] lifetime `{lifetime}`
- *[anon] an anonymous lifetime `'_`
+ [true] lifetime `{$lifetime}`
+ *[false] an anonymous lifetime `'_`
} but it needs to satisfy a `'static` lifetime requirement
.influencer = this data with {$has_lifetime ->
- [named] lifetime `{lifetime}`
- *[anon] an anonymous lifetime `'_`
+ [true] lifetime `{$lifetime}`
+ *[false] an anonymous lifetime `'_`
}...
.require = {$spans_empty ->
*[true] ...is used and required to live as long as `'static` here
[true] `{$param_name}`
*[false] `fn` parameter
} has {$has_lifetime ->
- [named] lifetime `{lifetime}`
- *[anon] an anonymous lifetime `'_`
+ [true] lifetime `{$lifetime}`
+ *[false] an anonymous lifetime `'_`
} but calling `{$ident}` introduces an implicit `'static` lifetime requirement
infer_ril_introduced_here = `'static` requirement introduced here
.note = pointers do not have a lifetime; when calling `as_ptr` the `CString` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
.help = for more information, see https://doc.rust-lang.org/reference/destructors.html
+lint_multple_supertrait_upcastable = `{$ident}` is object-safe and has multiple supertraits
+
lint_identifier_non_ascii_char = identifier contains non-ASCII characters
lint_identifier_uncommon_codepoints = identifier contains uncommon Unicode codepoints
.suggestion = borrow this binding in the pattern to avoid moving the value
mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once at a time
- .label = first mutable borrow, by `{$name}`, occurs here
- .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here
- .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here
- .moved = also moved into `{$name_moved}` here
+
+mir_build_already_borrowed = cannot borrow value as mutable because it is also borrowed as immutable
+
+mir_build_already_mut_borrowed = cannot borrow value as immutable because it is also borrowed as mutable
+
+mir_build_moved_while_borrowed = cannot move out of value because it is borrowed
+
+mir_build_mutable_borrow = value is mutably borrowed by `{$name}` here
+
+mir_build_borrow = value is borrowed by `{$name}` here
+
+mir_build_moved = value is moved into `{$name}` here
mir_build_union_pattern = cannot use unions in constant patterns
[one] variant that isn't
*[other] variants that aren't
} matched
+
+mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
} with a body
.suggestion_use_comma_not_semicolon = use a comma to end a `match` arm expression
+parse_inclusive_range_extra_equals = unexpected `=` after inclusive range
+ .suggestion_remove_eq = use `..=` instead
+ .note = inclusive ranges end with a single equals sign (`..=`)
+
+parse_inclusive_range_match_arrow = unexpected `=>` after open range
+ .suggestion_add_space = add a space between the pattern and `=>`
+
+parse_inclusive_range_no_end = inclusive range with no end
+ .suggestion_open_range = use `..` instead
+ .note = inclusive ranges must be bounded at the end (`..=b` or `a..=b`)
+
parse_struct_literal_not_allowed_here = struct literals are not allowed here
.suggestion = surround the struct literal with parentheses
parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
parse_invalid_curly_in_let_else = right curly brace `{"}"}` before `else` in a `let...else` statement not allowed
+parse_extra_if_in_let_else = remove the `if` if you meant to write a `let...else` statement
parse_compound_assignment_expression_in_let = can't reassign to an uninitialized variable
.suggestion = initialize the variable
[one] trait {$trait_list}, but this is
*[other] traits {$trait_list}, but these are
} intentionally ignored during dead code analysis
+
+passes_proc_macro_typeerror = mismatched {$kind} signature
+ .label = found {$found}, expected type `proc_macro::TokenStream`
+ .note = {$kind}s must have a signature of `{$expected_signature}`
+
+passes_proc_macro_diff_arg_count = mismatched {$kind} signature
+ .label = found unexpected {$count ->
+ [one] argument
+ *[other] arguments
+ }
+ .note = {$kind}s must have a signature of `{$expected_signature}`
+
+passes_proc_macro_missing_args = mismatched {$kind} signature
+ .label = {$kind} must have {$expected_input_count ->
+ [one] one argument
+ *[other] two arguments
+ } of type `proc_macro::TokenStream`
+
+passes_proc_macro_invalid_abi = proc macro functions may not be `extern "{$abi}"`
+
+passes_proc_macro_unsafe = proc macro functions may not be `unsafe`
ty_utils_array_not_supported = array construction is not supported in generic constants
-ty_utils_block_not_supported = blocks are not supported in generic constant
+ty_utils_block_not_supported = blocks are not supported in generic constants
-ty_utils_never_to_any_not_supported = converting nevers to any is not supported in generic constant
+ty_utils_never_to_any_not_supported = converting nevers to any is not supported in generic constants
ty_utils_tuple_not_supported = tuple construction is not supported in generic constants
-ty_utils_index_not_supported = indexing is not supported in generic constant
+ty_utils_index_not_supported = indexing is not supported in generic constants
-ty_utils_field_not_supported = field access is not supported in generic constant
+ty_utils_field_not_supported = field access is not supported in generic constants
-ty_utils_const_block_not_supported = const blocks are not supported in generic constant
+ty_utils_const_block_not_supported = const blocks are not supported in generic constants
ty_utils_adt_not_supported = struct/enum construction is not supported in generic constants
ty_utils_inline_asm_not_supported = assembly is not supported in generic constants
-ty_utils_operation_not_supported = unsupported operation in generic constant
+ty_utils_operation_not_supported = unsupported operation in generic constants
trace!(?locale);
let mut bundle = new_bundle(vec![locale]);
+ // Add convenience functions available to ftl authors.
+ register_functions(&mut bundle);
+
// Fluent diagnostics can insert directionality isolation markers around interpolated variables
// indicating that there may be a shift from right-to-left to left-to-right text (or
// vice-versa). These are disabled because they are sometimes visible in the error output, but
Ok(Some(bundle))
}
+fn register_functions(bundle: &mut FluentBundle) {
+ bundle
+ .add_function("STREQ", |positional, _named| match positional {
+ [FluentValue::String(a), FluentValue::String(b)] => format!("{}", (a == b)).into(),
+ _ => FluentValue::Error,
+ })
+ .expect("Failed to add a function to the bundle.");
+}
+
/// Type alias for the result of `fallback_fluent_bundle` - a reference-counted pointer to a lazily
/// evaluated fluent bundle.
pub type LazyFallbackBundle = Lrc<Lazy<FluentBundle, impl FnOnce() -> FluentBundle>>;
) -> LazyFallbackBundle {
Lrc::new(Lazy::new(move || {
let mut fallback_bundle = new_bundle(vec![langid!("en-US")]);
+
+ register_functions(&mut fallback_bundle);
+
// See comment in `fluent_bundle`.
fallback_bundle.set_use_isolating(with_directionality_markers);
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
- /// This is not used for highlighting or rendering any error message. Rather, it can be used
- /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
- /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
+ /// This is not used for highlighting or rendering any error message. Rather, it can be used
+ /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
+ /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
pub sort_span: Span,
/// If diagnostic is from Lint, custom hash function ignores notes
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
- assert!(!suggestion.is_empty());
- debug_assert!(
- !(suggestion.iter().any(|(sp, text)| sp.is_empty() && text.is_empty())),
- "Span must not be empty and have no suggestion"
+ let mut parts = suggestion
+ .into_iter()
+ .map(|(span, snippet)| SubstitutionPart { snippet, span })
+ .collect::<Vec<_>>();
+
+ parts.sort_unstable_by_key(|part| part.span);
+
+ assert!(!parts.is_empty());
+ debug_assert_eq!(
+ parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
+ None,
+ "Span must not be empty and have no suggestion",
+ );
+ debug_assert_eq!(
+ parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
+ None,
+ "suggestion must not have overlapping parts",
);
self.push_suggestion(CodeSuggestion {
- substitutions: vec![Substitution {
- parts: suggestion
- .into_iter()
- .map(|(span, snippet)| SubstitutionPart { snippet, span })
- .collect(),
- }],
+ substitutions: vec![Substitution { parts }],
msg: self.subdiagnostic_message_to_diagnostic_message(msg),
style,
applicability,
suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
applicability: Applicability,
) -> &mut Self {
- let suggestions: Vec<_> = suggestions.into_iter().collect();
- debug_assert!(
- !(suggestions
- .iter()
- .flatten()
- .any(|(sp, suggestion)| sp.is_empty() && suggestion.is_empty())),
- "Span must not be empty and have no suggestion"
- );
+ let substitutions = suggestions
+ .into_iter()
+ .map(|sugg| {
+ let mut parts = sugg
+ .into_iter()
+ .map(|(span, snippet)| SubstitutionPart { snippet, span })
+ .collect::<Vec<_>>();
+
+ parts.sort_unstable_by_key(|part| part.span);
+
+ assert!(!parts.is_empty());
+ debug_assert_eq!(
+ parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
+ None,
+ "Span must not be empty and have no suggestion",
+ );
+ debug_assert_eq!(
+ parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
+ None,
+ "suggestion must not have overlapping parts",
+ );
+
+ Substitution { parts }
+ })
+ .collect();
self.push_suggestion(CodeSuggestion {
- substitutions: suggestions
- .into_iter()
- .map(|sugg| Substitution {
- parts: sugg
- .into_iter()
- .map(|(span, snippet)| SubstitutionPart { snippet, span })
- .collect(),
- })
- .collect(),
+ substitutions,
msg: self.subdiagnostic_message_to_diagnostic_message(msg),
style: SuggestionStyle::ShowCode,
applicability,
if let Some(span) = span.primary_span() {
// Compare the primary span of the diagnostic with the span of the suggestion
- // being emitted. If they belong to the same file, we don't *need* to show the
+ // being emitted. If they belong to the same file, we don't *need* to show the
// file name, saving in verbosity, but if it *isn't* we do need it, otherwise we're
// telling users to make a change but not clarifying *where*.
let loc = sm.lookup_char_pos(parts[0].span.lo());
//
// On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
// the .flush() is called we take the buffer created from the buffered writes and write it at
- // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
+ // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
// scheme, this buffered approach works and maintains the styling.
//
// On Windows, styling happens through calls to a terminal API. This prevents us from using the
- // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
+ // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
// enough to output the full error message, then we release.
let _buffer_lock = lock::acquire_global_lock("rustc_errors");
for (pos, line) in rendered_buffer.iter().enumerate() {
//! This module contains the code for creating and emitting diagnostics.
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(array_windows)]
#![feature(drain_filter)]
#![feature(if_let_guard)]
#![feature(is_terminal)]
impl Annotatable {
pub fn span(&self) -> Span {
- match *self {
- Annotatable::Item(ref item) => item.span,
- Annotatable::TraitItem(ref trait_item) => trait_item.span,
- Annotatable::ImplItem(ref impl_item) => impl_item.span,
- Annotatable::ForeignItem(ref foreign_item) => foreign_item.span,
- Annotatable::Stmt(ref stmt) => stmt.span,
- Annotatable::Expr(ref expr) => expr.span,
- Annotatable::Arm(ref arm) => arm.span,
- Annotatable::ExprField(ref field) => field.span,
- Annotatable::PatField(ref fp) => fp.pat.span,
- Annotatable::GenericParam(ref gp) => gp.ident.span,
- Annotatable::Param(ref p) => p.span,
- Annotatable::FieldDef(ref sf) => sf.span,
- Annotatable::Variant(ref v) => v.span,
- Annotatable::Crate(ref c) => c.spans.inner_span,
+ match self {
+ Annotatable::Item(item) => item.span,
+ Annotatable::TraitItem(trait_item) => trait_item.span,
+ Annotatable::ImplItem(impl_item) => impl_item.span,
+ Annotatable::ForeignItem(foreign_item) => foreign_item.span,
+ Annotatable::Stmt(stmt) => stmt.span,
+ Annotatable::Expr(expr) => expr.span,
+ Annotatable::Arm(arm) => arm.span,
+ Annotatable::ExprField(field) => field.span,
+ Annotatable::PatField(fp) => fp.pat.span,
+ Annotatable::GenericParam(gp) => gp.ident.span,
+ Annotatable::Param(p) => p.span,
+ Annotatable::FieldDef(sf) => sf.span,
+ Annotatable::Variant(v) => v.span,
+ Annotatable::Crate(c) => c.spans.inner_span,
}
}
Some(AttrTokenTree::Delimited(sp, delim, inner))
.into_iter()
}
- AttrTokenTree::Token(ref token, _) if let TokenKind::Interpolated(ref nt) = token.kind => {
+ AttrTokenTree::Token(ref token, _) if let TokenKind::Interpolated(nt) = &token.kind => {
panic!(
"Nonterminal should have been flattened at {:?}: {:?}",
token.span, nt
}
pub fn visit_with<'a, V: Visitor<'a>>(&'a self, visitor: &mut V) {
- match *self {
- AstFragment::OptExpr(Some(ref expr)) => visitor.visit_expr(expr),
+ match self {
+ AstFragment::OptExpr(Some(expr)) => visitor.visit_expr(expr),
AstFragment::OptExpr(None) => {}
- AstFragment::MethodReceiverExpr(ref expr) => visitor.visit_method_receiver_expr(expr),
- $($(AstFragment::$Kind(ref ast) => visitor.$visit_ast(ast),)?)*
- $($(AstFragment::$Kind(ref ast) => for ast_elt in &ast[..] {
+ AstFragment::MethodReceiverExpr(expr) => visitor.visit_method_receiver_expr(expr),
+ $($(AstFragment::$Kind(ast) => visitor.$visit_ast(ast),)?)*
+ $($(AstFragment::$Kind(ast) => for ast_elt in &ast[..] {
visitor.$visit_ast_elt(ast_elt, $($args)*);
})?)*
}
let expn_id = invoc.expansion_data.id;
let parent_def = self.cx.resolver.invocation_parent(expn_id);
let span = match &mut invoc.kind {
- InvocationKind::Bang { ref mut span, .. } => span,
+ InvocationKind::Bang { span, .. } => span,
InvocationKind::Attr { attr, .. } => &mut attr.span,
InvocationKind::Derive { path, .. } => &mut path.span,
};
let def_site_span = parser.token.span.with_ctxt(SyntaxContext::root());
let semi_span = parser.sess.source_map().next_point(span);
- let add_semicolon = match parser.sess.source_map().span_to_snippet(semi_span) {
- Ok(ref snippet) if &snippet[..] != ";" && kind_name == "expression" => {
+ let add_semicolon = match &parser.sess.source_map().span_to_snippet(semi_span) {
+ Ok(snippet) if &snippet[..] != ";" && kind_name == "expression" => {
Some(span.shrink_to_hi())
}
_ => None,
// Iterates from top to bottom of the stack.
fn next(&mut self) -> Option<&'a T> {
- match *self {
+ match self {
Stack::Empty => None,
- Stack::Push { ref top, ref prev } => {
+ Stack::Push { top, prev } => {
*self = prev;
Some(top)
}
// We check that the meta-variable is correctly used.
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
}
- (NestedMacroState::MacroRulesNotName, &TokenTree::Delimited(_, ref del))
- | (NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del))
+ (NestedMacroState::MacroRulesNotName, TokenTree::Delimited(_, del))
+ | (NestedMacroState::MacroName, TokenTree::Delimited(_, del))
if del.delim == Delimiter::Brace =>
{
let macro_rules = state == NestedMacroState::MacroRulesNotName;
valid,
);
}
- (_, ref tt) => {
+ (_, tt) => {
state = NestedMacroState::Empty;
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
}
use rustc_ast as ast;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
-use rustc_ast::tokenstream::{DelimSpan, TokenStream};
+use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_attr::{self as attr, TransparencyError};
};
let arm_span = rhses[i].span();
- let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>();
// rhs has holes ( `$id` and `$(...)` that need filled)
let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) {
Ok(tts) => tts,
// Replace all the tokens for the corresponding positions in the macro, to maintain
// proper positions in error reporting, while maintaining the macro_backtrace.
- if rhs_spans.len() == tts.len() {
+ if tts.len() == rhs.tts.len() {
tts = tts.map_enumerated(|i, tt| {
let mut tt = tt.clone();
- let mut sp = rhs_spans[i];
- sp = sp.with_ctxt(tt.span().ctxt());
- tt.set_span(sp);
+ let rhs_tt = &rhs.tts[i];
+ let ctxt = tt.span().ctxt();
+ match (&mut tt, rhs_tt) {
+ // preserve the delim spans if able
+ (
+ TokenTree::Delimited(target_sp, ..),
+ mbe::TokenTree::Delimited(source_sp, ..),
+ ) => {
+ target_sp.open = source_sp.open.with_ctxt(ctxt);
+ target_sp.close = source_sp.close.with_ctxt(ctxt);
+ }
+ _ => {
+ let sp = rhs_tt.span().with_ctxt(ctxt);
+ tt.set_span(sp);
+ }
+ }
tt
});
}
let mut valid = true;
// Extract the arguments:
- let lhses = match argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] {
- MatchedSeq(ref s) => s
+ let lhses = match &argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] {
+ MatchedSeq(s) => s
.iter()
.map(|m| {
- if let MatchedTokenTree(ref tt) = *m {
+ if let MatchedTokenTree(tt) = m {
let tt = mbe::quoted::parse(
TokenStream::new(vec![tt.clone()]),
true,
_ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
};
- let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
- MatchedSeq(ref s) => s
+ let rhses = match &argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
+ MatchedSeq(s) => s
.iter()
.map(|m| {
- if let MatchedTokenTree(ref tt) = *m {
+ if let MatchedTokenTree(tt) = m {
return mbe::quoted::parse(
TokenStream::new(vec![tt.clone()]),
false,
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
use mbe::TokenTree;
for tt in tts {
- match *tt {
+ match tt {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
| TokenTree::MetaVarExpr(..) => (),
- TokenTree::Delimited(_, ref del) => {
+ TokenTree::Delimited(_, del) => {
if !check_lhs_no_empty_seq(sess, &del.tts) {
return false;
}
}
- TokenTree::Sequence(span, ref seq) => {
+ TokenTree::Sequence(span, seq) => {
if seq.separator.is_none()
- && seq.tts.iter().all(|seq_tt| match *seq_tt {
+ && seq.tts.iter().all(|seq_tt| match seq_tt {
TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true,
- TokenTree::Sequence(_, ref sub_seq) => {
+ TokenTree::Sequence(_, sub_seq) => {
sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
|| sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
}
fn build_recur<'tt>(sets: &mut FirstSets<'tt>, tts: &'tt [TokenTree]) -> TokenSet<'tt> {
let mut first = TokenSet::empty();
for tt in tts.iter().rev() {
- match *tt {
+ match tt {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
| TokenTree::MetaVarExpr(..) => {
first.replace_with(TtHandle::TtRef(tt));
}
- TokenTree::Delimited(span, ref delimited) => {
+ TokenTree::Delimited(span, delimited) => {
build_recur(sets, &delimited.tts);
first.replace_with(TtHandle::from_token_kind(
token::OpenDelim(delimited.delim),
span.open,
));
}
- TokenTree::Sequence(sp, ref seq_rep) => {
+ TokenTree::Sequence(sp, seq_rep) => {
let subfirst = build_recur(sets, &seq_rep.tts);
match sets.first.entry(sp.entire()) {
let mut first = TokenSet::empty();
for tt in tts.iter() {
assert!(first.maybe_empty);
- match *tt {
+ match tt {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
first.add_one(TtHandle::TtRef(tt));
return first;
}
- TokenTree::Delimited(span, ref delimited) => {
+ TokenTree::Delimited(span, delimited) => {
first.add_one(TtHandle::from_token_kind(
token::OpenDelim(delimited.delim),
span.open,
));
return first;
}
- TokenTree::Sequence(sp, ref seq_rep) => {
+ TokenTree::Sequence(sp, seq_rep) => {
let subfirst_owned;
let subfirst = match self.first.get(&sp.entire()) {
Some(Some(subfirst)) => subfirst,
// First, update `last` so that it corresponds to the set
// of NT tokens that might end the sequence `... token`.
- match *token {
+ match token {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
suffix_first = build_suffix_first();
}
}
- TokenTree::Delimited(span, ref d) => {
+ TokenTree::Delimited(span, d) => {
let my_suffix = TokenSet::singleton(TtHandle::from_token_kind(
token::CloseDelim(d.delim),
span.close,
// against SUFFIX
continue 'each_token;
}
- TokenTree::Sequence(_, ref seq_rep) => {
+ TokenTree::Sequence(_, seq_rep) => {
suffix_first = build_suffix_first();
// The trick here: when we check the interior, we want
// to include the separator (if any) as a potential
}
fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
- match *tt {
- mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token).into(),
+ match tt {
+ mbe::TokenTree::Token(token) => pprust::token_to_string(&token).into(),
mbe::TokenTree::MetaVar(_, name) => format!("${}", name),
mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind),
mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name),
} else {
match delim {
Delimiter::Brace => {
- // The delimiter is `{`. This indicates the beginning
+ // The delimiter is `{`. This indicates the beginning
// of a meta-variable expression (e.g. `${count(ident)}`).
// Try to parse the meta-variable expression.
match MetaVarExpr::parse(&tts, delim_span.entire(), sess) {
}
}
// If we didn't find a metavar expression above, then we must have a
- // repetition sequence in the macro (e.g. `$(pat)*`). Parse the
+ // repetition sequence in the macro (e.g. `$(pat)*`). Parse the
// contents of the sequence itself
let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition);
// Get the Kleene operator and optional separator
fn next(&mut self) -> Option<&'a mbe::TokenTree> {
match self {
- Frame::Delimited { tts, ref mut idx, .. }
- | Frame::Sequence { tts, ref mut idx, .. } => {
+ Frame::Delimited { tts, idx, .. } | Frame::Sequence { tts, idx, .. } => {
let res = tts.get(*idx);
*idx += 1;
res
let ident = MacroRulesNormalizedIdent::new(original_ident);
if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
match cur_matched {
- MatchedTokenTree(ref tt) => {
+ MatchedTokenTree(tt) => {
// `tt`s are emitted into the output stream directly as "raw tokens",
// without wrapping them into groups.
let token = tt.clone();
result.push(token);
}
- MatchedNonterminal(ref nt) => {
+ MatchedNonterminal(nt) => {
// Other variables are emitted into the output stream as groups with
// `Delimiter::Invisible` to maintain parsing priorities.
// `Interpolated` is currently used for such groups in rustc parser.
interpolations: &'a FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
repeats: &[(usize, usize)],
) -> Option<&'a NamedMatch> {
- interpolations.get(&ident).map(|matched| {
- let mut matched = matched;
+ interpolations.get(&ident).map(|mut matched| {
for &(idx, _) in repeats {
match matched {
MatchedTokenTree(_) | MatchedNonterminal(_) => break,
- MatchedSeq(ref ads) => matched = ads.get(idx).unwrap(),
+ MatchedSeq(ads) => matched = ads.get(idx).unwrap(),
}
}
match self {
LockstepIterSize::Unconstrained => other,
LockstepIterSize::Contradiction(_) => self,
- LockstepIterSize::Constraint(l_len, ref l_id) => match other {
+ LockstepIterSize::Constraint(l_len, l_id) => match other {
LockstepIterSize::Unconstrained => self,
LockstepIterSize::Contradiction(_) => other,
LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
repeats: &[(usize, usize)],
) -> LockstepIterSize {
use mbe::TokenTree;
- match *tree {
- TokenTree::Delimited(_, ref delimited) => {
+ match tree {
+ TokenTree::Delimited(_, delimited) => {
delimited.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
size.with(lockstep_iter_size(tt, interpolations, repeats))
})
}
- TokenTree::Sequence(_, ref seq) => {
+ TokenTree::Sequence(_, seq) => {
seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
size.with(lockstep_iter_size(tt, interpolations, repeats))
})
}
TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
- let name = MacroRulesNormalizedIdent::new(name);
+ let name = MacroRulesNormalizedIdent::new(*name);
match lookup_cur_matched(name, interpolations, repeats) {
Some(matched) => match matched {
MatchedTokenTree(_) | MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
- MatchedSeq(ref ads) => LockstepIterSize::Constraint(ads.len(), name),
+ MatchedSeq(ads) => LockstepIterSize::Constraint(ads.len(), name),
},
_ => LockstepIterSize::Unconstrained,
}
}
- TokenTree::MetaVarExpr(_, ref expr) => {
+ TokenTree::MetaVarExpr(_, expr) => {
let default_rslt = LockstepIterSize::Unconstrained;
let Some(ident) = expr.ident() else { return default_rslt; };
let name = MacroRulesNormalizedIdent::new(ident);
match lookup_cur_matched(name, interpolations, repeats) {
- Some(MatchedSeq(ref ads)) => {
+ Some(MatchedSeq(ads)) => {
default_rslt.with(LockstepIterSize::Constraint(ads.len(), name))
}
_ => default_rslt,
Some(_) => Err(out_of_bounds_err(cx, declared_lhs_depth, sp.entire(), "count")),
}
}
- MatchedSeq(ref named_matches) => {
+ MatchedSeq(named_matches) => {
let new_declared_lhs_depth = declared_lhs_depth + 1;
match depth_opt {
None => named_matches
// before we start counting. `matched` contains the various levels of the
// tree as we descend, and its final value is the subtree we are currently at.
for &(idx, _) in repeats {
- if let MatchedSeq(ref ads) = matched {
+ if let MatchedSeq(ads) = matched {
matched = &ads[idx];
}
}
}
impl<'a> visit::Visitor<'a> for PatIdentVisitor {
fn visit_pat(&mut self, p: &'a ast::Pat) {
- match p.kind {
- PatKind::Ident(_, ref ident, _) => {
- self.spans.push(ident.span.clone());
+ match &p.kind {
+ PatKind::Ident(_, ident, _) => {
+ self.spans.push(ident.span);
}
_ => {
visit::walk_pat(self, p);
)
.unwrap();
- let tts: Vec<_> = match expr.kind {
- ast::ExprKind::MacCall(ref mac) => mac.args.tokens.clone().into_trees().collect(),
- _ => panic!("not a macro"),
- };
+ let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") };
+ let tts: Vec<_> = mac.args.tokens.clone().into_trees().collect();
let span = tts.iter().rev().next().unwrap().span();
.unwrap()
.unwrap();
- if let ast::ItemKind::Mod(_, ref mod_kind) = item.kind {
- assert!(matches!(mod_kind, ast::ModKind::Loaded(items, ..) if items.len() == 2));
- } else {
- panic!();
- }
+ let ast::ItemKind::Mod(_, mod_kind) = &item.kind else { panic!() };
+ assert!(matches!(mod_kind, ast::ModKind::Loaded(items, ..) if items.len() == 2));
});
}
let stream = TokenStream::from_nonterminal_ast(&nt);
// A hack used to pass AST fragments to attribute and derive
// macros as a single nonterminal token instead of a token
- // stream. Such token needs to be "unwrapped" and not
+ // stream. Such token needs to be "unwrapped" and not
// represented as a delimited group.
// FIXME: It needs to be removed, but there are some
// compatibility issues (see #73345).
}
fn path(&mut self, file: &Self::SourceFile) -> String {
- match file.name {
- FileName::Real(ref name) => name
+ match &file.name {
+ FileName::Real(name) => name
.local_path()
.expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`")
.to_str()
(active, intrinsics, "1.0.0", None, None),
/// Allows using `#[lang = ".."]` attribute for linking items to special compiler logic.
(active, lang_items, "1.0.0", None, None),
+ /// Allows the `multiple_supertrait_upcastable` lint.
+ (active, multiple_supertrait_upcastable, "CURRENT_RUSTC_VERSION", None, None),
/// Allows using `#[omit_gdb_pretty_printer_section]`.
(active, omit_gdb_pretty_printer_section, "1.5.0", None, None),
/// Allows using `#[prelude_import]` on glob `use` items.
/// Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
pub fn from_environment(krate: Option<&str>) -> Self {
// `true` if this is a feature-staged build, i.e., on the beta or stable channel.
- let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+ let disable_unstable_features =
+ option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
// Returns whether `krate` should be counted as unstable
let is_unstable_crate = |var: &str| {
krate.map_or(false, |name| var.split(',').any(|new_krate| new_krate == name))
binder: NodeId,
},
/// This variant is used for anonymous lifetimes that we did not resolve during
- /// late resolution. Those lifetimes will be inferred by typechecking.
+ /// late resolution. Those lifetimes will be inferred by typechecking.
Infer,
/// Explicit `'static` lifetime.
Static,
/// Implicit lifetime in a context like `dyn Foo`. This is
/// distinguished from implicit lifetimes elsewhere because the
/// lifetime that they default to must appear elsewhere within the
- /// enclosing type. This means that, in an `impl Trait` context, we
+ /// enclosing type. This means that, in an `impl Trait` context, we
/// don't have to create a parameter for them. That is, `impl
/// Trait<Item = &u32>` expands to an opaque type like `type
/// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
pub hash_without_bodies: Fingerprint,
/// Full HIR for the current owner.
// The zeroth node's parent should never be accessed: the owner's parent is computed by the
- // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally
+ // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally
// used.
pub nodes: IndexVec<ItemLocalId, Option<ParentedNode<'tcx>>>,
/// Content of local bodies.
pub bodies: SortedMap<ItemLocalId, &'tcx Body<'tcx>>,
- /// Non-owning definitions contained in this owner.
- pub local_id_to_def_id: SortedMap<ItemLocalId, LocalDefId>,
}
impl<'tcx> OwnerNodes<'tcx> {
.collect::<Vec<_>>(),
)
.field("bodies", &self.bodies)
- .field("local_id_to_def_id", &self.local_id_to_def_id)
.field("hash_without_bodies", &self.hash_without_bodies)
.field("hash_including_bodies", &self.hash_including_bodies)
.finish()
expr
}
+ pub fn peel_borrows(&self) -> &Self {
+ let mut expr = self;
+ while let ExprKind::AddrOf(.., inner) = &expr.kind {
+ expr = inner;
+ }
+ expr
+ }
+
pub fn can_have_side_effects(&self) -> bool {
match self.peel_drop_temps().kind {
ExprKind::Path(_) | ExprKind::Lit(_) => false,
}
/// Hints at the original code for a `match _ { .. }`.
-#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)]
-#[derive(HashStable_Generic)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(HashStable_Generic, Encodable, Decodable)]
pub enum MatchSource {
/// A `match _ { .. }`.
Normal,
TryDesugar,
/// A desugared `<expr>.await`.
AwaitDesugar,
+ /// A desugared `format_args!()`.
+ FormatArgs,
}
impl MatchSource {
ForLoopDesugar => "for",
TryDesugar => "?",
AwaitDesugar => ".await",
+ FormatArgs => "format_args!()",
}
}
}
}
}
+ pub fn alias_ty(self) -> Option<&'hir Ty<'hir>> {
+ match self {
+ Node::Item(Item { kind: ItemKind::TyAlias(ty, ..), .. }) => Some(ty),
+ _ => None,
+ }
+ }
+
pub fn body_id(&self) -> Option<BodyId> {
match self {
Node::TraitItem(TraitItem {
use crate::hir::*;
use rustc_ast::walk_list;
use rustc_ast::{Attribute, Label};
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
fn visit_fn_decl(&mut self, fd: &'v FnDecl<'v>) {
walk_fn_decl(self, fd)
}
- fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, _: Span, id: HirId) {
+ fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl<'v>, b: BodyId, _: Span, id: LocalDefId) {
walk_fn(self, fk, fd, b, id)
}
fn visit_use(&mut self, path: &'v UsePath<'v>, hir_id: HirId) {
visitor.visit_ty(typ);
visitor.visit_nested_body(body);
}
- ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
- FnKind::ItemFn(item.ident, generics, sig.header),
- sig.decl,
- body_id,
- item.span,
- item.hir_id(),
- ),
+ ItemKind::Fn(ref sig, ref generics, body_id) => {
+ visitor.visit_id(item.hir_id());
+ visitor.visit_fn(
+ FnKind::ItemFn(item.ident, generics, sig.header),
+ sig.decl,
+ body_id,
+ item.span,
+ item.owner_id.def_id,
+ )
+ }
ItemKind::Macro(..) => {
visitor.visit_id(item.hir_id());
}
walk_list!(visitor, visit_arm, arms);
}
ExprKind::Closure(&Closure {
- def_id: _,
+ def_id,
binder: _,
bound_generic_params,
fn_decl,
constness: _,
}) => {
walk_list!(visitor, visit_generic_param, bound_generic_params);
- visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id)
+ visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, def_id)
}
ExprKind::Block(ref block, ref opt_label) => {
walk_list!(visitor, visit_label, opt_label);
function_kind: FnKind<'v>,
function_declaration: &'v FnDecl<'v>,
body_id: BodyId,
- id: HirId,
+ _: LocalDefId,
) {
- visitor.visit_id(id);
visitor.visit_fn_decl(function_declaration);
walk_fn_kind(visitor, function_kind);
visitor.visit_nested_body(body_id)
let TraitItem { ident, generics, ref defaultness, ref kind, span, owner_id: _ } = *trait_item;
let hir_id = trait_item.hir_id();
visitor.visit_ident(ident);
- visitor.visit_generics(generics);
- visitor.visit_defaultness(defaultness);
+ visitor.visit_generics(&generics);
+ visitor.visit_defaultness(&defaultness);
+ visitor.visit_id(hir_id);
match *kind {
TraitItemKind::Const(ref ty, default) => {
- visitor.visit_id(hir_id);
visitor.visit_ty(ty);
walk_list!(visitor, visit_nested_body, default);
}
TraitItemKind::Fn(ref sig, TraitFn::Required(param_names)) => {
- visitor.visit_id(hir_id);
visitor.visit_fn_decl(sig.decl);
for ¶m_name in param_names {
visitor.visit_ident(param_name);
}
}
TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => {
- visitor.visit_fn(FnKind::Method(ident, sig), sig.decl, body_id, span, hir_id);
+ visitor.visit_fn(
+ FnKind::Method(ident, sig),
+ sig.decl,
+ body_id,
+ span,
+ trait_item.owner_id.def_id,
+ );
}
TraitItemKind::Type(bounds, ref default) => {
- visitor.visit_id(hir_id);
walk_list!(visitor, visit_param_bound, bounds);
walk_list!(visitor, visit_ty, default);
}
visitor.visit_ident(ident);
visitor.visit_generics(generics);
visitor.visit_defaultness(defaultness);
+ visitor.visit_id(impl_item.hir_id());
match *kind {
ImplItemKind::Const(ref ty, body) => {
- visitor.visit_id(impl_item.hir_id());
visitor.visit_ty(ty);
visitor.visit_nested_body(body);
}
sig.decl,
body_id,
impl_item.span,
- impl_item.hir_id(),
+ impl_item.owner_id.def_id,
);
}
ImplItemKind::Type(ref ty) => {
- visitor.visit_id(impl_item.hir_id());
visitor.visit_ty(ty);
}
}
/// libstd panic entry point. Necessary for const eval to be able to catch it
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
+ // Lang items needed for `format_args!()`.
+ FormatAlignment, sym::format_alignment, format_alignment, Target::Enum, GenericRequirement::None;
+ FormatArgument, sym::format_argument, format_argument, Target::Struct, GenericRequirement::None;
+ FormatArguments, sym::format_arguments, format_arguments, Target::Struct, GenericRequirement::None;
+ FormatCount, sym::format_count, format_count, Target::Enum, GenericRequirement::None;
+ FormatPlaceholder, sym::format_placeholder, format_placeholder, Target::Struct, GenericRequirement::None;
+ FormatUnsafeArg, sym::format_unsafe_arg, format_unsafe_arg, Target::Struct, GenericRequirement::None;
+
ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None;
BoxFree, sym::box_free, box_free_fn, Target::Fn, GenericRequirement::Minimum(1);
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None;
GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
+ Context, sym::Context, context, Target::Struct, GenericRequirement::None;
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
FromFrom, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
// `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing
// the body satisfies the condition of two nodes being different have different
// `hash_stable` results.
- let OwnerNodes {
- hash_including_bodies,
- hash_without_bodies: _,
- nodes: _,
- bodies: _,
- local_id_to_def_id: _,
- } = *self;
+ let OwnerNodes { hash_including_bodies, hash_without_bodies: _, nodes: _, bodies: _ } =
+ *self;
hash_including_bodies.hash_stable(hcx, hasher);
}
}
}
if potential_assoc_types.len() == assoc_items.len() {
// When the amount of missing associated types equals the number of
- // extra type arguments present. A suggesting to replace the generic args with
+ // extra type arguments present. A suggesting to replace the generic args with
// associated types is already emitted.
already_has_generics_args_suggestion = true;
} else if let (Ok(snippet), false) =
// We should never be able to reach this point with well-formed input.
// There are three situations in which we can encounter this issue.
//
- // 1. The number of arguments is incorrect. In this case, an error
- // will already have been emitted, and we can ignore it.
- // 2. There are late-bound lifetime parameters present, yet the
- // lifetime arguments have also been explicitly specified by the
- // user.
- // 3. We've inferred some lifetimes, which have been provided later (i.e.
- // after a type or const). We want to throw an error in this case.
+ // 1. The number of arguments is incorrect. In this case, an error
+ // will already have been emitted, and we can ignore it.
+ // 2. There are late-bound lifetime parameters present, yet the
+ // lifetime arguments have also been explicitly specified by the
+ // user.
+ // 3. We've inferred some lifetimes, which have been provided later (i.e.
+ // after a type or const). We want to throw an error in this case.
if arg_count.correct.is_ok()
&& arg_count.explicit_late_bound == ExplicitLateBound::No
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{walk_generics, Visitor as _};
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
use rustc_span::edition::Edition;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span};
+use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::spec::abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::astconv_object_safety_violations;
pub struct PathSeg(pub DefId, pub usize);
pub trait AstConv<'tcx> {
- fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
+ fn tcx(&self) -> TyCtxt<'tcx>;
fn item_def_id(&self) -> DefId;
{
self
}
+
+ fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
}
#[derive(Debug)]
return tcx.const_error(ty).into();
}
if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
+ tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
if infer_args {
self.astconv.ct_infer(ty, Some(param), self.span).into()
.bindings
.iter()
.map(|binding| {
- let kind = match binding.kind {
- hir::TypeBindingKind::Equality { ref term } => match term {
- hir::Term::Ty(ref ty) => {
+ let kind = match &binding.kind {
+ hir::TypeBindingKind::Equality { term } => match term {
+ hir::Term::Ty(ty) => {
ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
}
- hir::Term::Const(ref c) => {
+ hir::Term::Const(c) => {
let c = Const::from_anon_const(self.tcx(), c.def_id);
ConvertedBindingKind::Equality(c.into())
}
},
- hir::TypeBindingKind::Constraint { ref bounds } => {
+ hir::TypeBindingKind::Constraint { bounds } => {
ConvertedBindingKind::Constraint(bounds)
}
};
/// ```
///
/// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
- /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
+ /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
/// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
///
/// `span` should be the declaration size of the parameter.
i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
assert_eq!(trait_ref.self_ty(), dummy_self);
- // Verify that `dummy_self` did not leak inside default type parameters. This
+ // Verify that `dummy_self` did not leak inside default type parameters. This
// could not be done at path creation, since we need to see through trait aliases.
let mut missing_type_params = vec![];
let mut references_self = false;
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
let tcx = self.tcx();
let assoc_ident = assoc_segment.ident;
- let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind {
+ let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
return;
};
let (qself_sugg_span, is_self) = if let hir::TyKind::Path(
- hir::QPath::Resolved(_, ref path)
- ) = qself.kind {
+ hir::QPath::Resolved(_, path)
+ ) = &qself.kind {
// If the path segment already has type params, we want to overwrite
// them.
match &path.segments[..] {
};
self.one_bound_for_assoc_type(
- || traits::supertraits(tcx, ty::Binder::dummy(trait_ref)),
+ || traits::supertraits(tcx, ty::Binder::dummy(trait_ref.subst_identity())),
|| "Self".to_string(),
assoc_ident,
span,
)
.emit() // Already reported in an earlier stage.
} else {
- // Find all the `impl`s that `qself_ty` has for any trait that has the
- // associated type, so that we suggest the right one.
- let infcx = tcx.infer_ctxt().build();
- // We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
- // to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
- let param_env = ty::ParamEnv::empty();
- let traits: Vec<_> = self
- .tcx()
- .all_traits()
- .filter(|trait_def_id| {
- // Consider only traits with the associated type
- tcx.associated_items(*trait_def_id)
- .in_definition_order()
- .any(|i| {
- i.kind.namespace() == Namespace::TypeNS
- && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
- && matches!(i.kind, ty::AssocKind::Type)
- })
- // Consider only accessible traits
- && tcx.visibility(*trait_def_id)
- .is_accessible_from(self.item_def_id(), tcx)
- && tcx.all_impls(*trait_def_id)
- .any(|impl_def_id| {
- let trait_ref = tcx.bound_impl_trait_ref(impl_def_id);
- trait_ref.map_or(false, |trait_ref| {
- let impl_ = trait_ref.subst(
- tcx,
- infcx.fresh_substs_for_item(span, impl_def_id),
- );
- infcx
- .can_eq(
- param_env,
- tcx.erase_regions(impl_.self_ty()),
- tcx.erase_regions(qself_ty),
- )
- .is_ok()
- })
- && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
- })
- })
- .map(|trait_def_id| tcx.def_path_str(trait_def_id))
- .collect();
+ let traits: Vec<_> =
+ self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
// Don't print `TyErr` to the user.
self.report_ambiguous_associated_type(
Ok((ty, DefKind::AssocTy, assoc_ty_did))
}
+ fn probe_traits_that_match_assoc_ty(
+ &self,
+ qself_ty: Ty<'tcx>,
+ assoc_ident: Ident,
+ ) -> Vec<String> {
+ let tcx = self.tcx();
+
+ // In contexts that have no inference context, just make a new one.
+ // We do need a local variable to store it, though.
+ let infcx_;
+ let infcx = if let Some(infcx) = self.infcx() {
+ infcx
+ } else {
+ assert!(!qself_ty.needs_infer());
+ infcx_ = tcx.infer_ctxt().build();
+ &infcx_
+ };
+
+ tcx.all_traits()
+ .filter(|trait_def_id| {
+ // Consider only traits with the associated type
+ tcx.associated_items(*trait_def_id)
+ .in_definition_order()
+ .any(|i| {
+ i.kind.namespace() == Namespace::TypeNS
+ && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
+ && matches!(i.kind, ty::AssocKind::Type)
+ })
+ // Consider only accessible traits
+ && tcx.visibility(*trait_def_id)
+ .is_accessible_from(self.item_def_id(), tcx)
+ && tcx.all_impls(*trait_def_id)
+ .any(|impl_def_id| {
+ let trait_ref = tcx.impl_trait_ref(impl_def_id);
+ trait_ref.map_or(false, |trait_ref| {
+ let impl_ = trait_ref.subst(
+ tcx,
+ infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id),
+ );
+ infcx
+ .can_eq(
+ ty::ParamEnv::empty(),
+ tcx.erase_regions(impl_.self_ty()),
+ tcx.erase_regions(qself_ty),
+ )
+ .is_ok()
+ })
+ && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
+ })
+ })
+ .map(|trait_def_id| tcx.def_path_str(trait_def_id))
+ .collect()
+ }
+
fn lookup_assoc_ty(
&self,
ident: Ident,
&& tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
})
.filter_map(|impl_def_id| tcx.impl_trait_ref(impl_def_id))
- .map(|impl_| impl_.self_ty())
+ .map(|impl_| impl_.subst_identity().self_ty())
// We don't care about blanket impls.
.filter(|self_ty| !self_ty.has_non_region_param())
.map(|self_ty| tcx.erase_regions(self_ty).to_string())
match path.res {
Res::Def(DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder, did) => {
// Check for desugared `impl Trait`.
- assert!(ty::is_impl_trait_defn(tcx, did).is_none());
+ assert!(tcx.is_type_alias_impl_trait(did));
let item_segment = path.segments.split_last().unwrap();
self.prohibit_generics(item_segment.1.iter(), |err| {
err.note("`impl Trait` types can't have type parameters");
"generic `Self` types are currently not permitted in anonymous constants",
);
if let Some(hir::Node::Item(&hir::Item {
- kind: hir::ItemKind::Impl(ref impl_),
+ kind: hir::ItemKind::Impl(impl_),
..
})) = tcx.hir().get_if_local(def_id)
{
}
/// Parses the programmer's textual representation of a type into our
- /// internal notion of a type. This is meant to be used within a path.
+ /// internal notion of a type. This is meant to be used within a path.
pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
self.ast_ty_to_ty_inner(ast_ty, false, true)
}
fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> {
let tcx = self.tcx();
- let result_ty = match ast_ty.kind {
- hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
- hir::TyKind::Ptr(ref mt) => {
+ let result_ty = match &ast_ty.kind {
+ hir::TyKind::Slice(ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
+ hir::TyKind::Ptr(mt) => {
tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
}
- hir::TyKind::Ref(ref region, ref mt) => {
+ hir::TyKind::Ref(region, mt) => {
let r = self.ast_region_to_region(region, None);
debug!(?r);
let t = self.ast_ty_to_ty_inner(mt.ty, true, false);
Some(ast_ty),
))
}
- hir::TyKind::TraitObject(bounds, ref lifetime, repr) => {
+ hir::TyKind::TraitObject(bounds, lifetime, repr) => {
self.maybe_lint_bare_trait(ast_ty, in_path);
let repr = match repr {
TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
};
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
}
- hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => {
+ hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
self.res_to_ty(opt_self_ty, path, false)
}
- hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
+ &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
let opaque_ty = tcx.hir().item(item_id);
let def_id = item_id.owner_id.to_def_id();
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}
}
- hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => {
+ hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment);
let ty = self.ast_ty_to_ty_inner(qself, false, true);
self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
.map(|(ty, _, _)| ty)
.unwrap_or_else(|_| tcx.ty_error())
}
- hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
+ &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
let def_id = tcx.require_lang_item(lang_item, Some(span));
let (substs, _) = self.create_substs_for_ast_path(
span,
);
EarlyBinder(tcx.at(span).type_of(def_id)).subst(tcx, substs)
}
- hir::TyKind::Array(ref ty, ref length) => {
+ hir::TyKind::Array(ty, length) => {
let length = match length {
&hir::ArrayLen::Infer(_, span) => self.ct_infer(tcx.types.usize, None, span),
hir::ArrayLen::Body(constant) => {
tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length))
}
- hir::TyKind::Typeof(ref e) => {
+ hir::TyKind::Typeof(e) => {
let ty_erased = tcx.type_of(e.def_id);
let ty = tcx.fold_regions(ty_erased, |r, _| {
if r.is_erased() { tcx.lifetimes.re_static } else { r }
trait_ref.def_id,
)?;
- let fn_sig = tcx.bound_fn_sig(assoc.def_id).subst(
+ let fn_sig = tcx.fn_sig(assoc.def_id).subst(
tcx,
trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
);
let label = "add `dyn` keyword before this trait";
let mut diag =
rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg);
- diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
+ if self_ty.span.can_be_used_for_suggestions() {
+ diag.multipart_suggestion_verbose(
+ label,
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
// check if the impl trait that we are considering is a impl of a local trait
self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
diag.emit();
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::NormalizeExt;
use crate::traits::{self, TraitEngine, TraitEngineExt};
-use rustc_hir as hir;
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::Limit;
+use rustc_span::def_id::LocalDefId;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::Span;
// Meta infos:
infcx: &'a InferCtxt<'tcx>,
span: Span,
- body_id: hir::HirId,
+ body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
// Current state:
pub fn new(
infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_def_id: LocalDefId,
span: Span,
base_ty: Ty<'tcx>,
) -> Autoderef<'a, 'tcx> {
Autoderef {
infcx,
span,
- body_id,
+ body_id: body_def_id,
param_env,
state: AutoderefSnapshot {
steps: vec![],
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
-use rustc_infer::traits::Obligation;
+use rustc_infer::traits::{Obligation, TraitEngineExt as _};
use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::stability::EvalResult;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
-use rustc_trait_selection::traits::{self, ObligationCtxt};
+use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
use std::ops::ControlFlow;
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!(?t, "root_visit_ty");
if t == self.opaque_identity_ty {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
t.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
if self.references_parent_regions {
ControlFlow::Break(t)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
span: Span,
origin: &hir::OpaqueTyOrigin,
) {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
hir::OpaqueTyOrigin::TyAlias => def_id,
_ => re,
});
- let misc_cause = traits::ObligationCause::misc(span, hir_id);
+ let misc_cause = traits::ObligationCause::misc(span, def_id);
match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) {
Ok(()) => {}
DefKind::Fn => {} // entirely within check_item_body
DefKind::Impl => {
let it = tcx.hir().item(id);
- let hir::ItemKind::Impl(ref impl_) = it.kind else {
- return;
- };
+ let hir::ItemKind::Impl(impl_) = it.kind else { return };
debug!("ItemKind::Impl {} with id {:?}", it.ident, it.owner_id);
if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.owner_id) {
check_impl_items_against_trait(
tcx,
it.span,
it.owner_id.def_id,
- impl_trait_ref,
+ impl_trait_ref.subst_identity(),
&impl_.items,
);
check_on_unimplemented(tcx, it);
}
DefKind::Trait => {
let it = tcx.hir().item(id);
- let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else {
+ let hir::ItemKind::Trait(_, _, _, _, items) = it.kind else {
return;
};
check_on_unimplemented(tcx, it);
for item in items.iter() {
let item = tcx.hir().trait_item(item.id);
- match item.kind {
- hir::TraitItemKind::Fn(ref sig, _) => {
+ match &item.kind {
+ hir::TraitItemKind::Fn(sig, _) => {
let abi = sig.header.abi;
fn_maybe_err(tcx, item.ident.span, abi);
}
}
let item = tcx.hir().foreign_item(item.id);
- match item.kind {
- hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
+ match &item.kind {
+ hir::ForeignItemKind::Fn(fn_decl, _, _) => {
require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
}
hir::ForeignItemKind::Static(..) => {
DefKind::GlobalAsm => {
let it = tcx.hir().item(id);
let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) };
- InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id());
+ InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.owner_id.def_id);
}
_ => {}
}
///
/// If all the return expressions evaluate to `!`, then we explain that the error will go away
/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
-fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed {
+fn opaque_type_cycle_error(
+ tcx: TyCtxt<'_>,
+ opaque_def_id: LocalDefId,
+ span: Span,
+) -> ErrorGuaranteed {
let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
let mut label = false;
- if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) {
+ if let Some((def_id, visitor)) = get_owner_return_paths(tcx, opaque_def_id) {
let typeck_results = tcx.typeck(def_id);
if visitor
.returns
.filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t)))
.filter(|(_, ty)| !matches!(ty.kind(), ty::Never))
{
- struct OpaqueTypeCollector(Vec<DefId>);
+ #[derive(Default)]
+ struct OpaqueTypeCollector {
+ opaques: Vec<DefId>,
+ closures: Vec<DefId>,
+ }
impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match *t.kind() {
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
- self.0.push(def);
- ControlFlow::CONTINUE
+ self.opaques.push(def);
+ ControlFlow::Continue(())
+ }
+ ty::Closure(def_id, ..) | ty::Generator(def_id, ..) => {
+ self.closures.push(def_id);
+ t.super_visit_with(self)
}
_ => t.super_visit_with(self),
}
}
}
- let mut visitor = OpaqueTypeCollector(vec![]);
+
+ let mut visitor = OpaqueTypeCollector::default();
ty.visit_with(&mut visitor);
- for def_id in visitor.0 {
+ for def_id in visitor.opaques {
let ty_span = tcx.def_span(def_id);
if !seen.contains(&ty_span) {
- err.span_label(ty_span, &format!("returning this opaque type `{ty}`"));
+ let descr = if ty.is_impl_trait() { "opaque " } else { "" };
+ err.span_label(ty_span, &format!("returning this {descr}type `{ty}`"));
seen.insert(ty_span);
}
err.span_label(sp, &format!("returning here with type `{ty}`"));
}
+
+ for closure_def_id in visitor.closures {
+ let Some(closure_local_did) = closure_def_id.as_local() else { continue; };
+ let typeck_results = tcx.typeck(closure_local_did);
+
+ let mut label_match = |ty: Ty<'_>, span| {
+ for arg in ty.walk() {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: captured_def_id, .. }) = *ty.kind()
+ && captured_def_id == opaque_def_id.to_def_id()
+ {
+ err.span_label(
+ span,
+ format!(
+ "{} captures itself here",
+ tcx.def_kind(closure_def_id).descr(closure_def_id)
+ ),
+ );
+ }
+ }
+ };
+
+ // Label any closure upvars that capture the opaque
+ for capture in typeck_results.closure_min_captures_flattened(closure_local_did)
+ {
+ label_match(capture.place.ty(), capture.get_path_span(tcx));
+ }
+ // Label any generator locals that capture the opaque
+ for interior_ty in
+ typeck_results.generator_interior_types.as_ref().skip_binder()
+ {
+ label_match(interior_ty.ty, interior_ty.span);
+ }
+ }
}
}
}
}
err.emit()
}
+
+pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+ debug_assert!(tcx.sess.opts.unstable_opts.drop_tracking_mir);
+ debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Generator));
+
+ let typeck = tcx.typeck(def_id);
+ let param_env = tcx.param_env(def_id);
+
+ let generator_interior_predicates = &typeck.generator_interior_predicates[&def_id];
+ debug!(?generator_interior_predicates);
+
+ let infcx = tcx
+ .infer_ctxt()
+ // typeck writeback gives us predicates with their regions erased.
+ // As borrowck already has checked lifetimes, we do not need to do it again.
+ .ignoring_regions()
+ // Bind opaque types to `def_id` as they should have been checked by borrowck.
+ .with_opaque_type_inference(DefiningAnchor::Bind(def_id))
+ .build();
+
+ let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+ for (predicate, cause) in generator_interior_predicates {
+ let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate);
+ fulfillment_cx.register_predicate_obligation(&infcx, obligation);
+ }
+ let errors = fulfillment_cx.select_all_or_error(&infcx);
+ debug!(?errors);
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+ }
+}
let impl_m_span = tcx.def_span(impl_m.def_id);
- if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) {
- return;
- }
-
- if let Err(_) = compare_number_of_generics(tcx, impl_m, trait_m, trait_item_span, false) {
- return;
- }
-
- if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m, false) {
- return;
- }
-
- if let Err(_) =
- compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)
- {
- return;
- }
-
- if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) {
- return;
- }
-
- if let Err(_) = compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
- return;
- }
-
- if let Err(_) = compare_method_predicate_entailment(
- tcx,
- impl_m,
- impl_m_span,
- trait_m,
- impl_trait_ref,
- CheckImpliedWfMode::Check,
- ) {
- return;
- }
+ let _: Result<_, ErrorGuaranteed> = try {
+ compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)?;
+ compare_number_of_generics(tcx, impl_m, trait_m, trait_item_span, false)?;
+ compare_generic_param_kinds(tcx, impl_m, trait_m, false)?;
+ compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)?;
+ compare_synthetic_generics(tcx, impl_m, trait_m)?;
+ compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span)?;
+ compare_method_predicate_entailment(
+ tcx,
+ impl_m,
+ impl_m_span,
+ trait_m,
+ impl_trait_ref,
+ CheckImpliedWfMode::Check,
+ )?;
+ };
}
/// This function is best explained by example. Consider a trait:
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
///
/// This type is also the same but the name of the bound region (`'a`
-/// vs `'b`). However, the normal subtyping rules on fn types handle
+/// vs `'b`). However, the normal subtyping rules on fn types handle
/// this kind of equivalency just fine.
///
/// We now use these substitutions to ensure that all declared bounds are
//
// FIXME(@lcnr): remove that after removing `cause.body_id` from
// obligations.
- let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ let impl_m_def_id = impl_m.def_id.expect_local();
let cause = ObligationCause::new(
impl_m_span,
- impl_m_hir_id,
+ impl_m_def_id,
ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
+ impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
},
//
// We then register the obligations from the impl_m and check to see
// if all constraints hold.
- hybrid_preds
- .predicates
- .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates);
+ hybrid_preds.predicates.extend(
+ trait_m_predicates
+ .instantiate_own(tcx, trait_to_placeholder_substs)
+ .map(|(predicate, _)| predicate),
+ );
// Construct trait parameter environment and then shift it into the placeholder viewpoint.
// The key step here is to update the caller_bounds's predicates to be
// the new hybrid bounds we computed.
- let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
let param_env = ty::ParamEnv::new(
tcx.intern_predicates(&hybrid_preds.predicates),
Reveal::UserFacing,
debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
- for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
- let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
+ for (predicate, span) in impl_m_own_bounds {
+ let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
let predicate = ocx.normalize(&normalize_cause, param_env, predicate);
let cause = ObligationCause::new(
span,
- impl_m_hir_id,
+ impl_m_def_id,
ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
+ impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
},
let unnormalized_impl_sig = infcx.replace_bound_vars_with_fresh_vars(
impl_m_span,
infer::HigherRankedType,
- tcx.fn_sig(impl_m.def_id),
+ tcx.fn_sig(impl_m.def_id).subst_identity(),
);
let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig));
- let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id);
let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig);
debug!("compare_impl_method: impl_fty={:?}", impl_sig);
- let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
// Next, add all inputs and output as well-formed tys. Importantly,
if !errors.is_empty() {
match check_implied_wf {
CheckImpliedWfMode::Check => {
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
return compare_method_predicate_entailment(
tcx,
impl_m,
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
Some(infcx),
- infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
+ infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys.clone()),
);
infcx.process_registered_region_obligations(
outlives_env.region_bound_pairs(),
if !errors.is_empty() {
// FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
// becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors`
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
match check_implied_wf {
CheckImpliedWfMode::Check => {
return compare_method_predicate_entailment(
}
CheckImpliedWfMode::Skip => {
if infcx.tainted_by_errors().is_none() {
- infcx.err_ctxt().report_region_errors(impl_m.def_id.expect_local(), &errors);
+ infcx.err_ctxt().report_region_errors(impl_m_def_id, &errors);
}
return Err(tcx
.sess
// Map late-bound regions from trait to impl, so the names are right.
let mapping = std::iter::zip(
- tcx.fn_sig(trait_m.def_id).bound_vars(),
- tcx.fn_sig(impl_m.def_id).bound_vars(),
+ tcx.fn_sig(trait_m.def_id).skip_binder().bound_vars(),
+ tcx.fn_sig(impl_m.def_id).skip_binder().bound_vars(),
)
.filter_map(|(impl_bv, trait_bv)| {
if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
trait_item_span: Option<Span>,
) -> Result<(), ErrorGuaranteed> {
if tcx.asyncness(trait_m.def_id) == hir::IsAsync::Async {
- match tcx.fn_sig(impl_m.def_id).skip_binder().output().kind() {
+ match tcx.fn_sig(impl_m.def_id).skip_binder().skip_binder().output().kind() {
ty::Alias(ty::Opaque, ..) => {
// allow both `async fn foo()` and `fn foo() -> impl Future`
}
) -> Result<&'tcx FxHashMap<DefId, Ty<'tcx>>, ErrorGuaranteed> {
let impl_m = tcx.opt_associated_item(def_id).unwrap();
let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
- let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap();
+ let impl_trait_ref =
+ tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap().subst_identity();
let param_env = tcx.param_env(def_id);
// First, check a few of the same things as `compare_impl_method`,
let trait_to_impl_substs = impl_trait_ref.substs;
- let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ let impl_m_def_id = impl_m.def_id.expect_local();
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span();
let cause = ObligationCause::new(
return_span,
- impl_m_hir_id,
+ impl_m_def_id,
ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
+ impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
},
let ocx = ObligationCtxt::new(infcx);
// Normalize the impl signature with fresh variables for lifetime inference.
- let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
+ let norm_cause = ObligationCause::misc(return_span, impl_m_def_id);
let impl_sig = ocx.normalize(
&norm_cause,
param_env,
infcx.replace_bound_vars_with_fresh_vars(
return_span,
infer::HigherRankedType,
- tcx.fn_sig(impl_m.def_id),
+ tcx.fn_sig(impl_m.def_id).subst_identity(),
),
);
impl_sig.error_reported()?;
// the ImplTraitInTraitCollector, which gathers all of the RPITITs and replaces
// them with inference variables.
// We will use these inference variables to collect the hidden types of RPITITs.
- let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id);
+ let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_def_id);
let unnormalized_trait_sig = tcx
.liberate_late_bound_regions(
impl_m.def_id,
- tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
+ tcx.fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
)
.fold_with(&mut collector);
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
let outlives_environment = OutlivesEnvironment::with_bounds(
param_env,
Some(infcx),
- infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
);
- infcx.err_ctxt().check_region_obligations_and_report_errors(
- impl_m.def_id.expect_local(),
- &outlives_environment,
- )?;
+ infcx
+ .err_ctxt()
+ .check_region_obligations_and_report_errors(impl_m_def_id, &outlives_environment)?;
let mut collected_tys = FxHashMap::default();
for (def_id, (ty, substs)) in collector.types {
match infcx.fully_resolve(ty) {
Ok(ty) => {
// `ty` contains free regions that we created earlier while liberating the
- // trait fn signature. However, projection normalization expects `ty` to
+ // trait fn signature. However, projection normalization expects `ty` to
// contains `def_id`'s early-bound regions.
let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
debug!(?id_substs, ?substs);
types: FxHashMap<DefId, (Ty<'tcx>, ty::SubstsRef<'tcx>)>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
}
impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> {
ocx: &'a ObligationCtxt<'a, 'tcx>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
) -> Self {
ImplTraitInTraitCollector { ocx, types: FxHashMap::default(), span, param_env, body_id }
}
// When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
// span points only at the type `Box<Self`>, but we want to cover the whole
// argument pattern and type.
- let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, body) => tcx
- .hir()
- .body_param_names(body)
- .zip(sig.decl.inputs.iter())
- .map(|(param, ty)| param.span.to(ty.span))
- .next()
- .unwrap_or(impl_err_span),
- _ => bug!("{:?} is not a method", impl_m),
- };
+ let ImplItemKind::Fn(ref sig, body) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{impl_m:?} is not a method") };
+ let span = tcx
+ .hir()
+ .body_param_names(body)
+ .zip(sig.decl.inputs.iter())
+ .map(|(param, ty)| param.span.to(ty.span))
+ .next()
+ .unwrap_or(impl_err_span);
diag.span_suggestion(
span,
if trait_sig.inputs().len() == *i {
// Suggestion to change output type. We do not suggest in `async` functions
// to avoid complex logic or incorrect output.
- match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, _) if !sig.header.asyncness.is_async() => {
- let msg = "change the output type to match the trait";
- let ap = Applicability::MachineApplicable;
- match sig.decl.output {
- hir::FnRetTy::DefaultReturn(sp) => {
- let sugg = format!("-> {} ", trait_sig.output());
- diag.span_suggestion_verbose(sp, msg, sugg, ap);
- }
- hir::FnRetTy::Return(hir_ty) => {
- let sugg = trait_sig.output();
- diag.span_suggestion(hir_ty.span, msg, sugg, ap);
- }
- };
- }
- _ => {}
+ if let ImplItemKind::Fn(sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind
+ && !sig.header.asyncness.is_async()
+ {
+ let msg = "change the output type to match the trait";
+ let ap = Applicability::MachineApplicable;
+ match sig.decl.output {
+ hir::FnRetTy::DefaultReturn(sp) => {
+ let sugg = format!("-> {} ", trait_sig.output());
+ diag.span_suggestion_verbose(sp, msg, sugg, ap);
+ }
+ hir::FnRetTy::Return(hir_ty) => {
+ let sugg = trait_sig.output();
+ diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+ }
+ };
};
} else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
diag.span_suggestion(
// Must have same number of early-bound lifetime parameters.
// Unfortunately, if the user screws up the bounds, then this
- // will change classification between early and late. E.g.,
+ // will change classification between early and late. E.g.,
// if in trait we have `<'a,'b:'a>`, and in impl we just have
// `<'a,'b>`, then we have 2 early-bound lifetime parameters
// in trait but 0 in the impl. But if we report "expected 2
trait_m: &ty::AssocItem,
) -> (Span, Option<Span>) {
let tcx = infcx.tcx;
- let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, _) => {
- sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
- }
- _ => bug!("{:?} is not a method", impl_m),
+ let mut impl_args = {
+ let ImplItemKind::Fn(sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{:?} is not a method", impl_m) };
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
};
- let trait_args =
- trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind {
- TraitItemKind::Fn(ref sig, _) => {
- sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
- }
- _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
- });
+
+ let trait_args = trait_m.def_id.as_local().map(|def_id| {
+ let TraitItemKind::Fn(sig, _) = &tcx.hir().expect_trait_item(def_id).kind else { bug!("{:?} is not a TraitItemKind::Fn", trait_m) };
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
+ });
match terr {
- TypeError::ArgumentMutability(i) => {
- (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
- }
- TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
+ TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
(impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
}
_ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
// Try to give more informative error messages about self typing
- // mismatches. Note that any mismatch will also be detected
+ // mismatches. Note that any mismatch will also be detected
// below, where we construct a canonical function type that
- // includes the self parameter as a normal parameter. It's just
+ // includes the self parameter as a normal parameter. It's just
// that the error messages you get out of this code are a bit more
// inscrutable, particularly for cases where one method has no
// self.
ty::ImplContainer => impl_trait_ref.self_ty(),
ty::TraitContainer => tcx.types.self_param,
};
- let self_arg_ty = tcx.fn_sig(method.def_id).input(0);
+ let self_arg_ty = tcx.fn_sig(method.def_id).subst_identity().input(0);
let param_env = ty::ParamEnv::reveal_all();
let infcx = tcx.infer_ctxt().build();
} else {
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
}
- let reported = err.emit();
- return Err(reported);
+ return Err(err.emit());
}
(true, false) => {
} else {
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
}
- let reported = err.emit();
- return Err(reported);
+
+ return Err(err.emit());
}
}
) -> Result<(), ErrorGuaranteed> {
let impl_m_fty = tcx.fn_sig(impl_m.def_id);
let trait_m_fty = tcx.fn_sig(trait_m.def_id);
- let trait_number_args = trait_m_fty.inputs().skip_binder().len();
- let impl_number_args = impl_m_fty.inputs().skip_binder().len();
+ let trait_number_args = trait_m_fty.skip_binder().inputs().skip_binder().len();
+ let impl_number_args = impl_m_fty.skip_binder().inputs().skip_binder().len();
+
if trait_number_args != impl_number_args {
- let trait_span = if let Some(def_id) = trait_m.def_id.as_local() {
- match tcx.hir().expect_trait_item(def_id).kind {
- TraitItemKind::Fn(ref trait_m_sig, _) => {
- let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 };
- if let Some(arg) = trait_m_sig.decl.inputs.get(pos) {
- Some(if pos == 0 {
- arg.span
- } else {
- arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
- })
- } else {
- trait_item_span
- }
- }
- _ => bug!("{:?} is not a method", impl_m),
- }
- } else {
- trait_item_span
- };
- let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref impl_m_sig, _) => {
- let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 };
- if let Some(arg) = impl_m_sig.decl.inputs.get(pos) {
+ let trait_span = trait_m
+ .def_id
+ .as_local()
+ .and_then(|def_id| {
+ let TraitItemKind::Fn(trait_m_sig, _) = &tcx.hir().expect_trait_item(def_id).kind else { bug!("{:?} is not a method", impl_m) };
+ let pos = trait_number_args.saturating_sub(1);
+ trait_m_sig.decl.inputs.get(pos).map(|arg| {
if pos == 0 {
arg.span
} else {
- arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
+ arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
}
+ })
+ })
+ .or(trait_item_span);
+
+ let ImplItemKind::Fn(impl_m_sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{:?} is not a method", impl_m) };
+ let pos = impl_number_args.saturating_sub(1);
+ let impl_span = impl_m_sig
+ .decl
+ .inputs
+ .get(pos)
+ .map(|arg| {
+ if pos == 0 {
+ arg.span
} else {
- impl_m_span
+ arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
}
- }
- _ => bug!("{:?} is not a method", impl_m),
- };
+ })
+ .unwrap_or(impl_m_span);
+
let mut err = struct_span_err!(
tcx.sess,
impl_span,
tcx.def_path_str(trait_m.def_id),
trait_number_args
);
+
if let Some(trait_span) = trait_span {
err.span_label(
trait_span,
} else {
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
}
+
err.span_label(
impl_span,
format!(
impl_number_args
),
);
- let reported = err.emit();
- return Err(reported);
+
+ return Err(err.emit());
}
Ok(())
// explicit generics
(true, false) => {
err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
- (|| {
+ let _: Option<_> = try {
// try taking the name from the trait impl
// FIXME: this is obviously suboptimal since the name can already be used
// as another generic argument
],
Applicability::MaybeIncorrect,
);
- Some(())
- })();
+ };
}
// The case where the trait method uses `impl Trait`, but the impl method uses
// explicit generics.
(false, true) => {
err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
- (|| {
+ let _: Option<_> = try {
let impl_m = impl_m.def_id.as_local()?;
let impl_m = tcx.hir().expect_impl_item(impl_m);
- let input_tys = match impl_m.kind {
- hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
- _ => unreachable!(),
- };
+ let hir::ImplItemKind::Fn(sig, _) = &impl_m.kind else { unreachable!() };
+ let input_tys = sig.decl.inputs;
+
struct Visitor(Option<Span>, hir::def_id::LocalDefId);
impl<'v> intravisit::Visitor<'v> for Visitor {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
intravisit::walk_ty(self, ty);
- if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
- ty.kind
+ if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
&& let Res::Def(DefKind::TyParam, def_id) = path.res
&& def_id == self.1.to_def_id()
{
}
}
}
+
let mut visitor = Visitor(None, impl_def_id);
for ty in input_tys {
intravisit::Visitor::visit_ty(&mut visitor, ty);
],
Applicability::MaybeIncorrect,
);
- Some(())
- })();
+ };
}
_ => unreachable!(),
}
- let reported = err.emit();
- error_found = Some(reported);
+ error_found = Some(err.emit());
}
}
if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
) -> Result<(), ErrorGuaranteed> {
let impl_const_item = tcx.associated_item(impl_const_item_def);
let trait_const_item = tcx.associated_item(trait_const_item_def);
- let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap();
+ let impl_trait_ref =
+ tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().subst_identity();
debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
// Create a parameter environment that represents the implementation's
// method.
- let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def);
-
// Compute placeholder form of impl and trait const tys.
let impl_ty = tcx.type_of(impl_const_item_def.to_def_id());
let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs);
let mut cause = ObligationCause::new(
impl_c_span,
- impl_c_hir_id,
+ impl_const_item_def,
ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id: impl_const_item_def,
trait_item_def_id: trait_const_item_def,
);
// Locate the Span containing just the type of the offending impl
- match tcx.hir().expect_impl_item(impl_const_item_def).kind {
- ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
- _ => bug!("{:?} is not a impl const", impl_const_item),
- }
+ let ImplItemKind::Const(ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).kind else { bug!("{impl_const_item:?} is not a impl const") };
+ cause.span = ty.span;
let mut diag = struct_span_err!(
tcx.sess,
let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
// Add a label to the Span containing just the type of the const
- match tcx.hir().expect_trait_item(trait_c_def_id).kind {
- TraitItemKind::Const(ref ty, _) => ty.span,
- _ => bug!("{:?} is not a trait const", trait_const_item),
- }
+ let TraitItemKind::Const(ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).kind else { bug!("{trait_const_item:?} is not a trait const") };
+ ty.span
});
infcx.err_ctxt().note_type_err(
) {
debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
- let _: Result<(), ErrorGuaranteed> = (|| {
+ let _: Result<(), ErrorGuaranteed> = try {
compare_number_of_generics(tcx, impl_ty, trait_ty, trait_item_span, false)?;
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
let sp = tcx.def_span(impl_ty.def_id);
compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
- check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
- })();
+ check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)?;
+ };
}
/// The equivalent of [compare_method_predicate_entailment], but for associated types
check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
-
- if impl_ty_own_bounds.is_empty() {
+ if impl_ty_own_bounds.len() == 0 {
// Nothing to check.
return Ok(());
}
// This `HirId` should be used for the `body_id` field on each
// `ObligationCause` (and the `FnCtxt`). This is what
// `regionck_item` expects.
- let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ let impl_ty_def_id = impl_ty.def_id.expect_local();
debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
// The predicates declared by the impl definition, the trait and the
// associated type in the trait are assumed.
let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
- hybrid_preds
- .predicates
- .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates);
+ hybrid_preds.predicates.extend(
+ trait_ty_predicates
+ .instantiate_own(tcx, trait_to_impl_substs)
+ .map(|(predicate, _)| predicate),
+ );
debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
- let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
+ let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let param_env = ty::ParamEnv::new(
tcx.intern_predicates(&hybrid_preds.predicates),
Reveal::UserFacing,
debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
- assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
- for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
- {
- let cause = ObligationCause::misc(span, impl_ty_hir_id);
+ for (predicate, span) in impl_ty_own_bounds {
+ let cause = ObligationCause::misc(span, impl_ty_def_id);
let predicate = ocx.normalize(&cause, param_env, predicate);
let cause = ObligationCause::new(
span,
- impl_ty_hir_id,
+ impl_ty_def_id,
ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id: impl_ty.def_id.expect_local(),
trait_item_def_id: trait_ty.def_id,
};
debug!(?normalize_param_env);
- let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ let impl_ty_def_id = impl_ty.def_id.expect_local();
let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
let normalize_cause = ObligationCause::new(
impl_ty_span,
- impl_ty_hir_id,
+ impl_ty_def_id,
ObligationCauseCode::CheckAssociatedTypeBounds {
impl_item_def_id: impl_ty.def_id.expect_local(),
trait_item_def_id: trait_ty.def_id,
} else {
traits::BindingObligation(trait_ty.def_id, span)
};
- ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
+ ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
};
let obligations = tcx
// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
- let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types);
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
let outlives_environment =
OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds);
)
}
_ => {
- // Destructors only work on nominal types. This was
+ // Destructors only work on nominal types. This was
// already checked by coherence, but compilation may
// not have been terminated.
let span = tcx.def_span(drop_impl_did);
&& gen_count_ok(own_counts.consts, 0, "const")
{
let fty = tcx.mk_fn_ptr(sig);
- let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType);
- require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty);
+ let it_def_id = it.owner_id.def_id;
+ let cause = ObligationCause::new(it.span, it_def_id, ObligationCauseCode::IntrinsicType);
+ require_same_types(
+ tcx,
+ &cause,
+ tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id).subst_identity()),
+ fty,
+ );
}
}
use rustc_hir as hir;
use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy};
use rustc_session::lint;
+use rustc_span::def_id::LocalDefId;
use rustc_span::{Symbol, DUMMY_SP};
use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType};
Some(asm_ty)
}
- pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: hir::HirId) {
- let hir = self.tcx.hir();
- let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id();
- let target_features = self.tcx.asm_target_features(enclosing_def_id);
+ pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: LocalDefId) {
+ let target_features = self.tcx.asm_target_features(enclosing_id.to_def_id());
let Some(asm_arch) = self.tcx.sess.asm_arch else {
self.tcx.sess.delay_span_bug(DUMMY_SP, "target architecture does not support asm");
return;
}
match *op {
- hir::InlineAsmOperand::In { reg, ref expr } => {
+ hir::InlineAsmOperand::In { reg, expr } => {
self.check_asm_operand_type(
idx,
reg,
&target_features,
);
}
- hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
+ hir::InlineAsmOperand::Out { reg, late: _, expr } => {
if let Some(expr) = expr {
self.check_asm_operand_type(
idx,
);
}
}
- hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
+ hir::InlineAsmOperand::InOut { reg, late: _, expr } => {
self.check_asm_operand_type(
idx,
reg,
&target_features,
);
}
- hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
+ hir::InlineAsmOperand::SplitInOut { reg, late: _, in_expr, out_expr } => {
let in_ty = self.check_asm_operand_type(
idx,
reg,
- main: the main pass does the lion's share of the work: it
determines the types of all expressions, resolves
- methods, checks for most invalid conditions, and so forth. In
+ methods, checks for most invalid conditions, and so forth. In
some cases, where a type is unknown, it may create a type or region
variable and use that as the type of an expression.
In the process of checking, various constraints will be placed on
these type variables through the subtyping relationships requested
- through the `demand` module. The `infer` module is in charge
+ through the `demand` module. The `infer` module is in charge
of resolving those constraints.
- regionck: after main is complete, the regionck pass goes over all
types looking for regions and making sure that they did not escape
- into places where they are not in scope. This may also influence the
+ into places where they are not in scope. This may also influence the
final assignments of the various region variables if there is some
flexibility.
- writeback: writes the final types within a function body, replacing
- type variables with their final inferred types. These final types
+ type variables with their final inferred types. These final types
are written into the `tcx.node_types` table, which should *never* contain
any reference to a type variable.
While type checking a function, the intermediate types for the
expressions, blocks, and so forth contained within the function are
-stored in `fcx.node_types` and `fcx.node_substs`. These types
-may contain unresolved type variables. After type checking is
+stored in `fcx.node_types` and `fcx.node_substs`. These types
+may contain unresolved type variables. After type checking is
complete, the functions in the writeback module are used to take the
types from this table, resolve them, and then write them into their
permanent home in the type context `tcx`.
The types of top-level items, which never contain unbound type
variables, are stored directly into the `tcx` typeck_results.
-N.B., a type variable is not the same thing as a type parameter. A
+N.B., a type variable is not the same thing as a type parameter. A
type variable is an instance of a type parameter. That is,
given a generic function `fn foo<T>(t: T)`, while checking the
function `foo`, the type `ty_param(0)` refers to the type `T`, which
is treated in abstract. However, when `foo()` is called, `T` will be
-substituted for a fresh type variable `N`. This variable will
+substituted for a fresh type variable `N`. This variable will
eventually be resolved to some concrete type (which might itself be
a type parameter).
region_scope_tree,
collect_return_position_impl_trait_in_trait_tys,
compare_impl_const: compare_impl_item::compare_impl_const_raw,
+ check_generator_obligations: check::check_generator_obligations,
..*providers
};
}
ty::AssocKind::Fn => {
// We skip the binder here because the binder would deanonymize all
// late-bound regions, and we don't want method signatures to show up
- // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
+ // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
fn_sig_suggestion(
tcx,
- tcx.fn_sig(assoc.def_id).skip_binder(),
+ tcx.fn_sig(assoc.def_id).subst_identity().skip_binder(),
assoc.ident(tcx),
tcx.predicates_of(assoc.def_id),
assoc,
visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
- if let Some(hir::Guard::If(ref expr)) = arm.guard {
+ if let Some(hir::Guard::If(expr)) = arm.guard {
visitor.terminating_scopes.insert(expr.hir_id.local_id);
}
// This ensures fixed size stacks.
hir::ExprKind::Binary(
source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
- ref l,
- ref r,
+ l,
+ r,
) => {
// expr is a short circuiting operator (|| or &&). As its
// functionality can't be overridden by traits, it always
terminating(r.hir_id.local_id);
}
}
- hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
+ hir::ExprKind::If(_, then, Some(otherwise)) => {
terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id);
}
- hir::ExprKind::If(_, ref then, None) => {
+ hir::ExprKind::If(_, then, None) => {
terminating(then.hir_id.local_id);
}
- hir::ExprKind::Loop(ref body, _, _, _) => {
+ hir::ExprKind::Loop(body, _, _, _) => {
terminating(body.hir_id.local_id);
}
- hir::ExprKind::DropTemps(ref expr) => {
+ hir::ExprKind::DropTemps(expr) => {
// `DropTemps(expr)` does not denote a conditional scope.
// Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.
terminating(expr.hir_id.local_id);
// The idea is that call.callee_id represents *the time when
// the invoked function is actually running* and call.id
// represents *the time to prepare the arguments and make the
- // call*. See the section "Borrows in Calls" borrowck/README.md
+ // call*. See the section "Borrows in Calls" borrowck/README.md
// for an extended explanation of why this distinction is
// important.
//
let body = visitor.tcx.hir().body(body);
visitor.visit_body(body);
}
- hir::ExprKind::AssignOp(_, ref left_expr, ref right_expr) => {
+ hir::ExprKind::AssignOp(_, left_expr, right_expr) => {
debug!(
"resolve_expr - enabling pessimistic_yield, was previously {}",
prev_pessimistic
}
}
- hir::ExprKind::If(ref cond, ref then, Some(ref otherwise)) => {
+ hir::ExprKind::If(cond, then, Some(otherwise)) => {
let expr_cx = visitor.cx;
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
visitor.cx.var_parent = visitor.cx.parent;
visitor.visit_expr(otherwise);
}
- hir::ExprKind::If(ref cond, ref then, None) => {
+ hir::ExprKind::If(cond, then, None) => {
let expr_cx = visitor.cx;
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
visitor.cx.var_parent = visitor.cx.parent;
match pat.kind {
PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true,
- PatKind::Struct(_, ref field_pats, _) => {
+ PatKind::Struct(_, field_pats, _) => {
field_pats.iter().any(|fp| is_binding_pat(&fp.pat))
}
- PatKind::Slice(ref pats1, ref pats2, ref pats3) => {
+ PatKind::Slice(pats1, pats2, pats3) => {
pats1.iter().any(|p| is_binding_pat(&p))
|| pats2.iter().any(|p| is_binding_pat(&p))
|| pats3.iter().any(|p| is_binding_pat(&p))
}
- PatKind::Or(ref subpats)
- | PatKind::TupleStruct(_, ref subpats, _)
- | PatKind::Tuple(ref subpats, _) => subpats.iter().any(|p| is_binding_pat(&p)),
+ PatKind::Or(subpats)
+ | PatKind::TupleStruct(_, subpats, _)
+ | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(&p)),
- PatKind::Box(ref subpat) => is_binding_pat(&subpat),
+ PatKind::Box(subpat) => is_binding_pat(&subpat),
PatKind::Ref(_, _)
| PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id);
}
}
- hir::ExprKind::Cast(ref subexpr, _) => {
+ hir::ExprKind::Cast(subexpr, _) => {
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id)
}
- hir::ExprKind::Block(ref block, _) => {
- if let Some(ref subexpr) = block.expr {
+ hir::ExprKind::Block(block, _) => {
+ if let Some(subexpr) = block.expr {
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id);
}
}
};
use std::cell::LazyCell;
-use std::iter;
use std::ops::{ControlFlow, Deref};
pub(super) struct WfCheckingCtxt<'a, 'tcx> {
pub(super) ocx: ObligationCtxt<'a, 'tcx>,
span: Span,
- body_id: hir::HirId,
+ body_def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> {
T: TypeFoldable<'tcx>,
{
self.ocx.normalize(
- &ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)),
+ &ObligationCause::new(span, self.body_def_id, ObligationCauseCode::WellFormed(loc)),
self.param_env,
value,
)
loc: Option<WellFormedLoc>,
arg: ty::GenericArg<'tcx>,
) {
- let cause =
- traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc));
+ let cause = traits::ObligationCause::new(
+ span,
+ self.body_def_id,
+ ObligationCauseCode::WellFormed(loc),
+ );
// for a type to be WF, we do not need to check if const trait predicates satisfy.
let param_env = self.param_env.without_const();
self.ocx.register_obligation(traits::Obligation::new(
F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>),
{
let param_env = tcx.param_env(body_def_id);
- let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
- let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+ let mut wfcx = WfCheckingCtxt { ocx, span, body_def_id, param_env };
if !tcx.features().trivial_bounds {
wfcx.check_false_global_bounds()
f(&mut wfcx);
let assumed_wf_types = wfcx.ocx.assumed_wf_types(param_env, span, body_def_id);
- let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
+ let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
let errors = wfcx.select_all_or_error();
if !errors.is_empty() {
//
// won't be allowed unless there's an *explicit* implementation of `Send`
// for `T`
- hir::ItemKind::Impl(ref impl_) => {
+ hir::ItemKind::Impl(impl_) => {
let is_auto = tcx
.impl_trait_ref(def_id)
- .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
+ .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.skip_binder().def_id));
if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
let mut err =
hir::ItemKind::Const(ty, ..) => {
check_item_type(tcx, def_id, ty.span, false);
}
- hir::ItemKind::Struct(_, ref ast_generics) => {
+ hir::ItemKind::Struct(_, ast_generics) => {
check_type_defn(tcx, item, false);
check_variances_for_type_defn(tcx, item, ast_generics);
}
- hir::ItemKind::Union(_, ref ast_generics) => {
+ hir::ItemKind::Union(_, ast_generics) => {
check_type_defn(tcx, item, true);
check_variances_for_type_defn(tcx, item, ast_generics);
}
- hir::ItemKind::Enum(_, ref ast_generics) => {
+ hir::ItemKind::Enum(_, ast_generics) => {
check_type_defn(tcx, item, true);
check_variances_for_type_defn(tcx, item, ast_generics);
}
continue;
}
- let item_hir_id = item.id.hir_id();
let param_env = tcx.param_env(item_def_id);
let item_required_bounds = match item.kind {
// `Self::Iter<'a>` is a GAT we want to gather any potential missing bounds from.
let sig: ty::FnSig<'_> = tcx.liberate_late_bound_regions(
item_def_id.to_def_id(),
- tcx.fn_sig(item_def_id),
+ tcx.fn_sig(item_def_id).subst_identity(),
);
gather_gat_bounds(
tcx,
param_env,
- item_hir_id,
+ item_def_id,
sig.inputs_and_output,
// We also assume that all of the function signature's parameter types
// are well formed.
gather_gat_bounds(
tcx,
param_env,
- item_hir_id,
+ item_def_id,
tcx.explicit_item_bounds(item_def_id).to_vec(),
&FxIndexSet::default(),
gat_def_id.def_id,
let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id);
debug!(?required_bounds);
let param_env = tcx.param_env(gat_def_id);
- let gat_hir = gat_item_hir.hir_id();
let mut unsatisfied_bounds: Vec<_> = required_bounds
.into_iter()
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
a,
b,
- ))) => {
- !region_known_to_outlive(tcx, gat_hir, param_env, &FxIndexSet::default(), a, b)
- }
+ ))) => !region_known_to_outlive(
+ tcx,
+ gat_def_id.def_id,
+ param_env,
+ &FxIndexSet::default(),
+ a,
+ b,
+ ),
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
a,
b,
- ))) => !ty_known_to_outlive(tcx, gat_hir, param_env, &FxIndexSet::default(), a, b),
+ ))) => !ty_known_to_outlive(
+ tcx,
+ gat_def_id.def_id,
+ param_env,
+ &FxIndexSet::default(),
+ a,
+ b,
+ ),
_ => bug!("Unexpected PredicateKind"),
})
.map(|clause| clause.to_string())
fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- item_hir: hir::HirId,
+ item_def_id: hir::OwnerId,
to_check: T,
wf_tys: &FxIndexSet<Ty<'tcx>>,
gat_def_id: LocalDefId,
// reflected in a where clause on the GAT itself.
for (ty, ty_idx) in &types {
// In our example, requires that `Self: 'a`
- if ty_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *ty, *region_a) {
+ if ty_known_to_outlive(tcx, item_def_id.def_id, param_env, &wf_tys, *ty, *region_a) {
debug!(?ty_idx, ?region_a_idx);
debug!("required clause: {ty} must outlive {region_a}");
// Translate into the generic parameters of the GAT. In
if ty::ReStatic == **region_b || region_a == region_b {
continue;
}
- if region_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *region_a, *region_b) {
+ if region_known_to_outlive(
+ tcx,
+ item_def_id.def_id,
+ param_env,
+ &wf_tys,
+ *region_a,
+ *region_b,
+ ) {
debug!(?region_a_idx, ?region_b_idx);
debug!("required clause: {region_a} must outlive {region_b}");
// Translate into the generic parameters of the GAT.
/// `ty` outlives `region`.
fn ty_known_to_outlive<'tcx>(
tcx: TyCtxt<'tcx>,
- id: hir::HirId,
+ id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
ty: Ty<'tcx>,
/// `region_a` outlives `region_b`
fn region_known_to_outlive<'tcx>(
tcx: TyCtxt<'tcx>,
- id: hir::HirId,
+ id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
region_a: ty::Region<'tcx>,
/// to be tested), then resolve region and return errors
fn resolve_regions_with_wf_tys<'tcx>(
tcx: TyCtxt<'tcx>,
- id: hir::HirId,
+ id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxIndexSet<Ty<'tcx>>,
add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
_ => {}
}
if !trait_should_be_self.is_empty() {
- if tcx.object_safety_violations(trait_def_id).is_empty() {
+ if tcx.check_is_object_safe(trait_def_id) {
return;
}
let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect();
wfcx.register_wf_obligation(span, loc, ty.into());
}
ty::AssocKind::Fn => {
- let sig = tcx.fn_sig(item.def_id);
+ let sig = tcx.fn_sig(item.def_id).subst_identity();
let hir_sig = sig_if_method.expect("bad signature for method");
check_fn_or_method(
wfcx,
wfcx.register_bound(
traits::ObligationCause::new(
hir_ty.span,
- wfcx.body_id,
+ wfcx.body_def_id,
traits::FieldSized {
adt_kind: match item_adt_kind(&item.kind) {
Some(i) => i,
if let ty::VariantDiscr::Explicit(discr_def_id) = variant.discr {
let cause = traits::ObligationCause::new(
tcx.def_span(discr_def_id),
- wfcx.body_id,
+ wfcx.body_def_id,
traits::MiscObligation,
);
wfcx.register_obligation(traits::Obligation::new(
traits::wf::predicate_obligations(
wfcx.infcx,
wfcx.param_env,
- wfcx.body_id,
+ wfcx.body_def_id,
normalized_bound,
bound_span,
)
decl: &hir::FnDecl<'_>,
) {
enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
- let sig = tcx.fn_sig(def_id);
+ let sig = tcx.fn_sig(def_id).subst_identity();
check_fn_or_method(wfcx, ident.span, sig, decl, def_id);
})
}
wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(item_id)), item_ty.into());
if forbid_unsized {
wfcx.register_bound(
- traits::ObligationCause::new(ty_span, wfcx.body_id, traits::WellFormed(None)),
+ traits::ObligationCause::new(ty_span, wfcx.body_def_id, traits::WellFormed(None)),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::Sized, None),
if should_check_for_sync {
wfcx.register_bound(
- traits::ObligationCause::new(ty_span, wfcx.body_id, traits::SharedStatic),
+ traits::ObligationCause::new(ty_span, wfcx.body_def_id, traits::SharedStatic),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
constness: hir::Constness,
) {
enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
- match *ast_trait_ref {
- Some(ref ast_trait_ref) => {
+ match ast_trait_ref {
+ Some(ast_trait_ref) => {
// `#[rustc_reservation_impl]` impls are not real impls and
// therefore don't need to be WF (the trait's `Self: Trait` predicate
// won't hold).
- let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap().subst_identity();
let trait_ref = wfcx.normalize(
ast_trait_ref.path.span,
Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
let mut obligations = traits::wf::trait_obligations(
wfcx.infcx,
wfcx.param_env,
- wfcx.body_id,
+ wfcx.body_def_id,
&trait_pred,
ast_trait_ref.path.span,
item,
let infcx = wfcx.infcx;
let tcx = wfcx.tcx();
- let predicates = tcx.bound_predicates_of(def_id.to_def_id());
+ let predicates = tcx.predicates_of(def_id.to_def_id());
let generics = tcx.generics_of(def_id);
let is_our_default = |def: &ty::GenericParamDef| match def.kind {
// is incorrect when dealing with unused substs, for example
// for `struct Foo<const N: usize, const M: usize = { 1 - 2 }>`
// we should eagerly error.
- let default_ct = tcx.const_param_default(param.def_id);
+ let default_ct = tcx.const_param_default(param.def_id).subst_identity();
if !default_ct.needs_subst() {
wfcx.register_wf_obligation(
tcx.def_span(param.def_id),
GenericParamDefKind::Const { .. } => {
// If the param has a default, ...
if is_our_default(param) {
- let default_ct = tcx.const_param_default(param.def_id);
+ let default_ct = tcx.const_param_default(param.def_id).subst_identity();
// ... and it's not a dependent default, ...
if !default_ct.needs_subst() {
// ... then substitute it with the default.
// Now we build the substituted predicates.
let default_obligations = predicates
- .0
.predicates
.iter()
.flat_map(|&(pred, sp)| {
}
fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::BREAK
+ ControlFlow::Break(())
}
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
}
let mut param_count = CountParams::default();
let has_region = pred.visit_with(&mut param_count).is_break();
- let substituted_pred = predicates.rebind(pred).subst(tcx, substs);
+ let substituted_pred = ty::EarlyBinder(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region
{
None
- } else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) {
+ } else if predicates.predicates.iter().any(|&(p, _)| p == substituted_pred) {
// Avoid duplication of predicates that contain no parameters, for example.
None
} else {
let pred = wfcx.normalize(sp, None, pred);
let cause = traits::ObligationCause::new(
sp,
- wfcx.body_id,
+ wfcx.body_def_id,
traits::ItemObligation(def_id.to_def_id()),
);
traits::Obligation::new(tcx, cause, wfcx.param_env, pred)
});
- let predicates = predicates.0.instantiate_identity(tcx);
+ let predicates = predicates.instantiate_identity(tcx);
let predicates = wfcx.normalize(span, None, predicates);
debug!(?predicates.predicates);
assert_eq!(predicates.predicates.len(), predicates.spans.len());
- let wf_obligations =
- iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| {
- traits::wf::predicate_obligations(
- infcx,
- wfcx.param_env.without_const(),
- wfcx.body_id,
- p,
- sp,
- )
- });
-
+ let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| {
+ traits::wf::predicate_obligations(
+ infcx,
+ wfcx.param_env.without_const(),
+ wfcx.body_def_id,
+ p,
+ sp,
+ )
+ });
let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
wfcx.register_obligations(obligations);
}
// Check that the argument is a tuple
if let Some(ty) = inputs.next() {
wfcx.register_bound(
- ObligationCause::new(span, wfcx.body_id, ObligationCauseCode::RustCall),
+ ObligationCause::new(span, wfcx.body_def_id, ObligationCauseCode::RustCall),
wfcx.param_env,
*ty,
tcx.require_lang_item(hir::LangItem::Tuple, Some(span)),
traits::wf::predicate_obligations(
wfcx.infcx,
wfcx.param_env,
- wfcx.body_id,
+ wfcx.body_def_id,
normalized_bound,
bound_span,
)
let span = fn_sig.decl.inputs[0].span;
- let sig = tcx.fn_sig(method.def_id);
+ let sig = tcx.fn_sig(method.def_id).subst_identity();
let sig = tcx.liberate_late_bound_regions(method.def_id, sig);
let sig = wfcx.normalize(span, None, sig);
let infcx = wfcx.infcx;
let tcx = wfcx.tcx();
let cause =
- ObligationCause::new(span, wfcx.body_id, traits::ObligationCauseCode::MethodReceiver);
+ ObligationCause::new(span, wfcx.body_def_id, traits::ObligationCauseCode::MethodReceiver);
let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty).is_ok();
return true;
}
- let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty);
+ let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
// The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`.
if arbitrary_self_types_enabled {
let mut span = self.span;
let empty_env = ty::ParamEnv::empty();
- let def_id = tcx.hir().local_def_id(self.body_id);
- let predicates_with_span = tcx.predicates_of(def_id).predicates.iter().copied();
+ let predicates_with_span = tcx.predicates_of(self.body_def_id).predicates.iter().copied();
// Check elaborated bounds.
let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span);
// Match the existing behavior.
if pred.is_global() && !pred.has_late_bound_vars() {
let pred = self.normalize(span, None, pred);
- let hir_node = tcx.hir().find(self.body_id);
+ let hir_node = tcx.hir().find_by_def_id(self.body_def_id);
// only use the span of the predicate clause (#90869)
let obligation = traits::Obligation::new(
tcx,
- traits::ObligationCause::new(span, self.body_id, traits::TrivialBound),
+ traits::ObligationCause::new(span, self.body_def_id, traits::TrivialBound),
empty_env,
pred,
);
fn unused_crates_lint(tcx: TyCtxt<'_>) {
let lint = lint::builtin::UNUSED_EXTERN_CRATES;
- // Collect first the crates that are completely unused. These we
+ // Collect first the crates that are completely unused. These we
// can always suggest removing (no matter which edition we are
// in).
let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::ItemKind;
-use rustc_infer::infer;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{self, RegionResolutionError};
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
-use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
+use rustc_trait_selection::traits::misc::{
+ type_allowed_to_implement_copy, CopyImplementationError, InfringingFieldsReason,
+};
use rustc_trait_selection::traits::predicate_for_trait_def;
use rustc_trait_selection::traits::{self, ObligationCause};
use std::collections::BTreeMap;
_ => {}
}
- let sp = match tcx.hir().expect_item(impl_did).kind {
- ItemKind::Impl(ref impl_) => impl_.self_ty.span,
- _ => bug!("expected Drop impl item"),
- };
+ let ItemKind::Impl(impl_) = tcx.hir().expect_item(impl_did).kind else { bug!("expected Drop impl item") };
- tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
+ tcx.sess.emit_err(DropImplOnWrongItem { span: impl_.self_ty.span });
}
fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
-
let self_type = tcx.type_of(impl_did);
debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
_ => bug!("expected Copy impl item"),
};
- let cause = traits::ObligationCause::misc(span, impl_hir_id);
- match can_type_implement_copy(tcx, param_env, self_type, cause) {
+ let cause = traits::ObligationCause::misc(span, impl_did);
+ match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
Ok(()) => {}
Err(CopyImplementationError::InfrigingFields(fields)) => {
let mut err = struct_span_err!(
let mut errors: BTreeMap<_, Vec<_>> = Default::default();
let mut bounds = vec![];
- for (field, ty) in fields {
+ for (field, ty, reason) in fields {
let field_span = tcx.def_span(field.did);
- let field_ty_span = match tcx.hir().get_if_local(field.did) {
- Some(hir::Node::Field(field_def)) => field_def.ty.span,
- _ => field_span,
- };
err.span_label(field_span, "this field does not implement `Copy`");
- // Spin up a new FulfillmentContext, so we can get the _precise_ reason
- // why this field does not implement Copy. This is useful because sometimes
- // it is not immediately clear why Copy is not implemented for a field, since
- // all we point at is the field itself.
- let infcx = tcx.infer_ctxt().ignoring_regions().build();
- for error in traits::fully_solve_bound(
- &infcx,
- traits::ObligationCause::dummy_with_span(field_ty_span),
- param_env,
- ty,
- tcx.require_lang_item(LangItem::Copy, Some(span)),
- ) {
- let error_predicate = error.obligation.predicate;
- // Only note if it's not the root obligation, otherwise it's trivial and
- // should be self-explanatory (i.e. a field literally doesn't implement Copy).
-
- // FIXME: This error could be more descriptive, especially if the error_predicate
- // contains a foreign type or if it's a deeply nested type...
- if error_predicate != error.root_obligation.predicate {
- errors
- .entry((ty.to_string(), error_predicate.to_string()))
- .or_default()
- .push(error.obligation.cause.span);
+
+ match reason {
+ InfringingFieldsReason::Fulfill(fulfillment_errors) => {
+ for error in fulfillment_errors {
+ let error_predicate = error.obligation.predicate;
+ // Only note if it's not the root obligation, otherwise it's trivial and
+ // should be self-explanatory (i.e. a field literally doesn't implement Copy).
+
+ // FIXME: This error could be more descriptive, especially if the error_predicate
+ // contains a foreign type or if it's a deeply nested type...
+ if error_predicate != error.root_obligation.predicate {
+ errors
+ .entry((ty.to_string(), error_predicate.to_string()))
+ .or_default()
+ .push(error.obligation.cause.span);
+ }
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(
+ ty::TraitPredicate {
+ trait_ref,
+ polarity: ty::ImplPolarity::Positive,
+ ..
+ },
+ )) = error_predicate.kind().skip_binder()
+ {
+ let ty = trait_ref.self_ty();
+ if let ty::Param(_) = ty.kind() {
+ bounds.push((
+ format!("{ty}"),
+ trait_ref.print_only_trait_path().to_string(),
+ Some(trait_ref.def_id),
+ ));
+ }
+ }
+ }
}
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
- trait_ref,
- polarity: ty::ImplPolarity::Positive,
- ..
- })) = error_predicate.kind().skip_binder()
- {
- let ty = trait_ref.self_ty();
- if let ty::Param(_) = ty.kind() {
- bounds.push((
- format!("{ty}"),
- trait_ref.print_only_trait_path().to_string(),
- Some(trait_ref.def_id),
- ));
+ InfringingFieldsReason::Regions(region_errors) => {
+ for error in region_errors {
+ let ty = ty.to_string();
+ match error {
+ RegionResolutionError::ConcreteFailure(origin, a, b) => {
+ let predicate = format!("{b}: {a}");
+ errors
+ .entry((ty.clone(), predicate.clone()))
+ .or_default()
+ .push(origin.span());
+ if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() {
+ bounds.push((b.to_string(), a.to_string(), None));
+ }
+ }
+ RegionResolutionError::GenericBoundFailure(origin, a, b) => {
+ let predicate = format!("{a}: {b}");
+ errors
+ .entry((ty.clone(), predicate.clone()))
+ .or_default()
+ .push(origin.span());
+ if let infer::region_constraints::GenericKind::Param(_) = a {
+ bounds.push((a.to_string(), b.to_string(), None));
+ }
+ }
+ _ => continue,
+ }
}
}
}
let source = tcx.type_of(impl_did);
assert!(!source.has_escaping_bound_vars());
let target = {
- let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity();
assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
trait_ref.substs.type_at(1)
let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
let infcx = tcx.infer_ctxt().build();
- let cause = ObligationCause::misc(span, impl_hir_id);
+ let cause = ObligationCause::misc(span, impl_did);
use rustc_type_ir::sty::TyKind::*;
match (source.kind(), target.kind()) {
});
let source = tcx.type_of(impl_did);
- let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity();
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
let target = trait_ref.substs.type_at(1);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
let infcx = tcx.infer_ctxt().build();
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
- let cause = ObligationCause::misc(span, impl_hir_id);
+ let cause = ObligationCause::misc(span, impl_did);
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
mt_b: ty::TypeAndMut<'tcx>,
mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
// when this coercion occurs, we would be changing the
// field `ptr` from a thin pointer of type `*mut [i32;
// 3]` to a fat pointer of type `*mut [i32]` (with
- // extra data `3`). **The purpose of this check is to
+ // extra data `3`). **The purpose of this check is to
// make sure that we know how to do this conversion.**
//
// To check if this impl is legal, we would walk down
return err_info;
} else if diff_fields.len() > 1 {
let item = tcx.hir().expect_item(impl_did);
- let span =
- if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = item.kind {
- t.path.span
- } else {
- tcx.def_span(impl_did)
- };
+ let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind {
+ t.path.span
+ } else {
+ tcx.def_span(impl_did)
+ };
struct_span_err!(
tcx.sess,
};
// Register an obligation for `A: Trait<B>`.
- let cause = traits::ObligationCause::misc(span, impl_hir_id);
+ let cause = traits::ObligationCause::misc(span, impl_did);
let predicate =
predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]);
let errors = traits::fully_solve_obligation(&infcx, predicate);
}
let item = self.tcx.hir().item(id);
- let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else {
+ let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, items, .. }) = item.kind else {
return;
};
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(_) => {
let impls = tcx.hir().trait_impls(def_id);
for &impl_def_id in impls {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
check_impl(tcx, impl_def_id, trait_ref);
check_object_overlap(tcx, impl_def_id, trait_ref);
});
for component_def_id in component_def_ids {
- if !tcx.is_object_safe(component_def_id) {
+ if !tcx.check_is_object_safe(component_def_id) {
// Without the 'object_safe_for_dispatch' feature this is an error
- // which will be reported by wfcheck. Ignore it here.
+ // which will be reported by wfcheck. Ignore it here.
// This is tested by `coherence-impl-trait-for-trait-object-safe.rs`.
// With the feature enabled, the trait is not implemented automatically,
// so this is valid.
tcx: TyCtxt<'_>,
impl_def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
trait_ref.error_reported()?;
let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
let trait_def_id = trait_ref.def_id;
let item = tcx.hir().expect_item(def_id);
- let hir::ItemKind::Impl(ref impl_) = item.kind else {
+ let hir::ItemKind::Impl(impl_) = item.kind else {
bug!("{:?} is not an impl: {:?}", def_id, item);
};
let sp = tcx.def_span(def_id);
if t != self.self_ty_root {
for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) {
match tcx.impl_polarity(impl_def_id) {
- ImplPolarity::Negative => return ControlFlow::BREAK,
+ ImplPolarity::Negative => return ControlFlow::Break(()),
ImplPolarity::Reservation => {}
// FIXME(@lcnr): That's probably not good enough, idk
//
// We might just want to take the rustdoc code and somehow avoid
// explicit impls for `Self`.
- ImplPolarity::Positive => return ControlFlow::CONTINUE,
+ ImplPolarity::Positive => return ControlFlow::Continue(()),
}
}
}
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => t.super_visit_with(self),
}
pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
let item = tcx.hir().expect_item(def_id);
- let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() };
+ let hir::ItemKind::Impl(impl_) = item.kind else { bug!() };
if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) {
+ let trait_ref = trait_ref.subst_identity();
let trait_def = tcx.trait_def(trait_ref.def_id);
let unsafe_attr =
impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{GenericParamKind, Node};
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::ObligationCause;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::query::Providers;
is_foreign_item,
generator_kind,
collect_mod_item_types,
+ is_type_alias_impl_trait,
..*providers
};
}
fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) {
// There's no place to record types from signatures?
}
+
+ fn infcx(&self) -> Option<&InferCtxt<'tcx>> {
+ None
+ }
}
/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
debug!("convert: item {} with id {}", it.ident, it.hir_id());
let def_id = item_id.owner_id.def_id;
- match it.kind {
+ match &it.kind {
// These don't define types.
hir::ItemKind::ExternCrate(_)
| hir::ItemKind::Use(..)
| hir::ItemKind::Mod(_)
| hir::ItemKind::GlobalAsm(_) => {}
hir::ItemKind::ForeignMod { items, .. } => {
- for item in items {
+ for item in *items {
let item = tcx.hir().foreign_item(item.id);
tcx.ensure().generics_of(item.owner_id);
tcx.ensure().type_of(item.owner_id);
tcx.at(it.span).super_predicates_of(def_id);
tcx.ensure().predicates_of(def_id);
}
- hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
+ hir::ItemKind::Struct(struct_def, _) | hir::ItemKind::Union(struct_def, _) => {
tcx.ensure().generics_of(def_id);
tcx.ensure().type_of(def_id);
tcx.ensure().predicates_of(def_id);
};
let repr = tcx.repr_options_of_def(def_id.to_def_id());
- let (kind, variants) = match item.kind {
- ItemKind::Enum(ref def, _) => {
+ let (kind, variants) = match &item.kind {
+ ItemKind::Enum(def, _) => {
let mut distance_from_explicit = 0;
let variants = def
.variants
.iter()
.map(|v| {
- let discr = if let Some(ref e) = v.disr_expr {
+ let discr = if let Some(e) = &v.disr_expr {
distance_from_explicit = 0;
ty::VariantDiscr::Explicit(e.def_id.to_def_id())
} else {
(AdtKind::Enum, variants)
}
- ItemKind::Struct(ref def, _) | ItemKind::Union(ref def, _) => {
+ ItemKind::Struct(def, _) | ItemKind::Union(def, _) => {
let adt_kind = match item.kind {
ItemKind::Struct(..) => AdtKind::Struct,
_ => AdtKind::Union,
}
#[instrument(level = "debug", skip(tcx))]
-fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
+fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'_>> {
use rustc_hir::Node::*;
use rustc_hir::*;
let icx = ItemCtxt::new(tcx, def_id.to_def_id());
- match tcx.hir().get(hir_id) {
+ let output = match tcx.hir().get(hir_id) {
TraitItem(hir::TraitItem {
kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
generics,
x => {
bug!("unexpected sort of node in fn_sig(): {:?}", x);
}
- }
+ };
+ ty::EarlyBinder(output)
}
fn infer_return_ty_for_fn_sig<'tcx>(
}
}
+// FIXME(vincenzopalazzo): remove the hir item when the refactoring is stable
fn suggest_impl_trait<'tcx>(
tcx: TyCtxt<'tcx>,
ret_ty: Ty<'tcx>,
span: Span,
- hir_id: hir::HirId,
+ _hir_id: hir::HirId,
def_id: LocalDefId,
) -> Option<String> {
let format_as_assoc: fn(_, _, _, _, _) -> _ =
}
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
let item_ty = ocx.normalize(
- &ObligationCause::misc(span, hir_id),
+ &ObligationCause::misc(span, def_id),
param_env,
tcx.mk_projection(assoc_item_def_id, substs),
);
None
}
-fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
+fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::EarlyBinder<ty::TraitRef<'_>>> {
let icx = ItemCtxt::new(tcx, def_id);
let item = tcx.hir().expect_item(def_id.expect_local());
- match item.kind {
- hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
+ let hir::ItemKind::Impl(impl_) = item.kind else { bug!() };
+ impl_
+ .of_trait
+ .as_ref()
+ .map(|ast_trait_ref| {
let selfty = tcx.type_of(def_id);
icx.astconv().instantiate_mono_trait_ref(
ast_trait_ref,
selfty,
check_impl_constness(tcx, impl_.constness, ast_trait_ref),
)
- }),
- _ => bug!(),
- }
+ })
+ .map(ty::EarlyBinder)
}
fn check_impl_constness(
for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
check(input, *ty)
}
- if let hir::FnRetTy::Return(ref ty) = decl.output {
+ if let hir::FnRetTy::Return(ty) = decl.output {
check(ty, fty.output().skip_binder())
}
}
_ => bug!("generator_kind applied to non-local def-id {:?}", def_id),
}
}
+
+fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+ match tcx.hir().get_if_local(def_id) {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. })) => {
+ matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias)
+ }
+ Some(_) => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id),
+ _ => bug!("tried getting opaque_ty_origin for non-local def-id {:?}", def_id),
+ }
+}
// expressions' count (i.e. `N` in `[x; N]`), and explicit
// `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
// as they shouldn't be able to cause query cycle errors.
- Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
+ Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. })
if constant.hir_id() == hir_id =>
{
Some(parent_def_id.to_def_id())
}
- Node::Variant(Variant { disr_expr: Some(ref constant), .. })
+ Node::Variant(Variant { disr_expr: Some(constant), .. })
if constant.hir_id == hir_id =>
{
Some(parent_def_id.to_def_id())
params.extend(ast_generics.params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => None,
- GenericParamKind::Type { ref default, synthetic, .. } => {
+ GenericParamKind::Type { default, synthetic, .. } => {
if default.is_some() {
match allow_defaults {
Defaults::Allowed => {}
}
match node {
- Node::TraitItem(item) => match item.kind {
- hir::TraitItemKind::Fn(ref sig, _) => {
- has_late_bound_regions(tcx, &item.generics, sig.decl)
- }
+ Node::TraitItem(item) => match &item.kind {
+ hir::TraitItemKind::Fn(sig, _) => has_late_bound_regions(tcx, &item.generics, sig.decl),
_ => None,
},
- Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::Fn(ref sig, _) => {
- has_late_bound_regions(tcx, &item.generics, sig.decl)
- }
+ Node::ImplItem(item) => match &item.kind {
+ hir::ImplItemKind::Fn(sig, _) => has_late_bound_regions(tcx, &item.generics, sig.decl),
_ => None,
},
Node::ForeignItem(item) => match item.kind {
- hir::ForeignItemKind::Fn(fn_decl, _, ref generics) => {
+ hir::ForeignItemKind::Fn(fn_decl, _, generics) => {
has_late_bound_regions(tcx, generics, fn_decl)
}
_ => None,
},
- Node::Item(item) => match item.kind {
- hir::ItemKind::Fn(ref sig, .., ref generics, _) => {
+ Node::Item(item) => match &item.kind {
+ hir::ItemKind::Fn(sig, .., generics, _) => {
has_late_bound_regions(tcx, generics, sig.decl)
}
_ => None,
}
}
-pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List<ty::Predicate<'_>> {
- tcx.mk_predicates(
+pub(super) fn item_bounds(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
+ let bounds = tcx.mk_predicates(
util::elaborate_predicates(
tcx,
tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
)
.map(|obligation| obligation.predicate),
- )
+ );
+ ty::EarlyBinder(bounds)
}
//! Resolution of early vs late bound lifetimes.
//!
-//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this
+//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this
//! information, typechecking needs to transform the lifetime parameters into bound lifetimes.
-//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit
-//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
+//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit
+//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
use rustc_ast::walk_list;
/// that it corresponds to.
///
/// FIXME. This struct gets converted to a `ResolveLifetimes` for
-/// actual use. It has the same data, but indexed by `LocalDefId`. This
+/// actual use. It has the same data, but indexed by `LocalDefId`. This
/// is silly.
#[derive(Debug, Default)]
struct NamedRegionMap {
_ => {}
}
match item.kind {
- hir::ItemKind::Fn(_, ref generics, _) => {
+ hir::ItemKind::Fn(_, generics, _) => {
self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_item(this, item);
});
this.with(scope, |this| intravisit::walk_item(this, item))
});
}
- hir::ItemKind::TyAlias(_, ref generics)
- | hir::ItemKind::Enum(_, ref generics)
- | hir::ItemKind::Struct(_, ref generics)
- | hir::ItemKind::Union(_, ref generics)
- | hir::ItemKind::Trait(_, _, ref generics, ..)
- | hir::ItemKind::TraitAlias(ref generics, ..)
- | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
+ hir::ItemKind::TyAlias(_, generics)
+ | hir::ItemKind::Enum(_, generics)
+ | hir::ItemKind::Struct(_, generics)
+ | hir::ItemKind::Union(_, generics)
+ | hir::ItemKind::Trait(_, _, generics, ..)
+ | hir::ItemKind::TraitAlias(generics, ..)
+ | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => {
// These kinds of items have only early-bound lifetime parameters.
let lifetimes = generics
.params
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
match item.kind {
- hir::ForeignItemKind::Fn(_, _, ref generics) => {
+ hir::ForeignItemKind::Fn(_, _, generics) => {
self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_foreign_item(this, item);
})
#[instrument(level = "debug", skip(self))]
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
match ty.kind {
- hir::TyKind::BareFn(ref c) => {
+ hir::TyKind::BareFn(c) => {
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
.generic_params
.iter()
intravisit::walk_ty(this, ty);
});
}
- hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
+ hir::TyKind::TraitObject(bounds, lifetime, _) => {
debug!(?bounds, ?lifetime, "TraitObject");
let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |this| {
LifetimeName::Error => {}
}
}
- hir::TyKind::Ref(ref lifetime_ref, ref mt) => {
+ hir::TyKind::Ref(lifetime_ref, ref mt) => {
self.visit_lifetime(lifetime_ref);
let scope = Scope::ObjectLifetimeDefault {
lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(),
// ^ ^ this gets resolved in the scope of
// the opaque_ty generics
let opaque_ty = self.tcx.hir().item(item_id);
- match opaque_ty.kind {
+ match &opaque_ty.kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias,
..
origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
..
}) => {}
- ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
+ i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
};
// Resolve the lifetimes that are applied to the opaque type.
intravisit::walk_trait_item(this, trait_item)
});
}
- Type(bounds, ref ty) => {
+ Type(bounds, ty) => {
let generics = &trait_item.generics;
let lifetimes = generics
.params
Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
intravisit::walk_impl_item(this, impl_item)
}),
- Type(ref ty) => {
+ Type(ty) => {
let generics = &impl_item.generics;
let lifetimes: FxIndexMap<LocalDefId, Region> = generics
.params
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
for (i, segment) in path.segments.iter().enumerate() {
let depth = path.segments.len() - i - 1;
- if let Some(ref args) = segment.args {
+ if let Some(args) = segment.args {
self.visit_segment_args(path.res, depth, args);
}
}
fd: &'tcx hir::FnDecl<'tcx>,
body_id: hir::BodyId,
_: Span,
- _: hir::HirId,
+ _: LocalDefId,
) {
let output = match fd.output {
hir::FnRetTy::DefaultReturn(_) => None,
- hir::FnRetTy::Return(ref ty) => Some(&**ty),
+ hir::FnRetTy::Return(ty) => Some(ty),
};
self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure));
intravisit::walk_fn_kind(self, fk);
for param in generics.params {
match param.kind {
GenericParamKind::Lifetime { .. } => {}
- GenericParamKind::Type { ref default, .. } => {
- if let Some(ref ty) = default {
- this.visit_ty(&ty);
+ GenericParamKind::Type { default, .. } => {
+ if let Some(ty) = default {
+ this.visit_ty(ty);
}
}
- GenericParamKind::Const { ref ty, default } => {
- this.visit_ty(&ty);
+ GenericParamKind::Const { ty, default } => {
+ this.visit_ty(ty);
if let Some(default) = default {
this.visit_body(this.tcx.hir().body(default.body));
}
match predicate {
&hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
hir_id,
- ref bounded_ty,
+ bounded_ty,
bounds,
- ref bound_generic_params,
+ bound_generic_params,
origin,
..
}) => {
})
}
&hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
- ref lifetime,
+ lifetime,
bounds,
..
}) => {
if lifetime.res != hir::LifetimeName::Static {
for bound in bounds {
- let hir::GenericBound::Outlives(ref lt) = bound else {
+ let hir::GenericBound::Outlives(lt) = bound else {
continue;
};
if lt.res != hir::LifetimeName::Static {
}
}
&hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
- ref lhs_ty,
- ref rhs_ty,
+ lhs_ty,
+ rhs_ty,
..
}) => {
this.visit_ty(lhs_ty);
}
for bound in bound.bounds {
- if let hir::GenericBound::Outlives(ref lifetime) = *bound {
+ if let hir::GenericBound::Outlives(lifetime) = bound {
set.insert(lifetime.res);
}
}
} else if let Some(body_id) = outermost_body {
let fn_id = self.tcx.hir().body_owner(body_id);
match self.tcx.hir().get(fn_id) {
- Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
+ Node::Item(hir::Item { owner_id, kind: hir::ItemKind::Fn(..), .. })
| Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(..), ..
+ owner_id,
+ kind: hir::TraitItemKind::Fn(..),
+ ..
})
- | Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. })
- | Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {
- let scope = self.tcx.hir().local_def_id(fn_id);
- def = Region::Free(scope.to_def_id(), def.id().unwrap());
+ | Node::ImplItem(hir::ImplItem {
+ owner_id,
+ kind: hir::ImplItemKind::Fn(..),
+ ..
+ }) => {
+ def = Region::Free(owner_id.to_def_id(), def.id().unwrap());
+ }
+ Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) => {
+ def = Region::Free(closure.def_id.to_def_id(), def.id().unwrap());
}
_ => {}
}
// We may fail to resolve higher-ranked lifetimes that are mentioned by APIT.
// AST-based resolution does not care for impl-trait desugaring, which are the
- // responibility of lowering. This may create a mismatch between the resolution
+ // responibility of lowering. This may create a mismatch between the resolution
// AST found (`region_def_id`) which points to HRTB, and what HIR allows.
// ```
// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty),
DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)),
// We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter
- // works. Ignore it because it can't have a meaningful lifetime default.
+ // works. Ignore it because it can't have a meaningful lifetime default.
DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None,
dk => bug!("unexpected def_kind {:?}", dk),
}
/// "Constrained" basically means that it appears in any type but
/// not amongst the inputs to a projection. In other words, `<&'a
/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`.
-fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet<LocalDefId>> {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let decl = tcx.hir().fn_decl_by_hir_id(hir_id)?;
- let generics = tcx.hir().get_generics(def_id)?;
+fn is_late_bound_map(
+ tcx: TyCtxt<'_>,
+ owner_id: hir::OwnerId,
+) -> Option<&FxIndexSet<hir::ItemLocalId>> {
+ let decl = tcx.hir().fn_decl_by_hir_id(owner_id.into())?;
+ let generics = tcx.hir().get_generics(owner_id.def_id)?;
let mut late_bound = FxIndexSet::default();
hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => continue,
}
- let param_def_id = tcx.hir().local_def_id(param.hir_id);
-
// appears in the where clauses? early-bound.
- if appears_in_where_clause.regions.contains(¶m_def_id) {
+ if appears_in_where_clause.regions.contains(¶m.def_id) {
continue;
}
// does not appear in the inputs, but appears in the return type? early-bound.
- if !constrained_by_input.regions.contains(¶m_def_id)
- && appears_in_output.regions.contains(¶m_def_id)
+ if !constrained_by_input.regions.contains(¶m.def_id)
+ && appears_in_output.regions.contains(¶m.def_id)
{
continue;
}
- debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.hir_id);
+ debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.def_id);
- let inserted = late_bound.insert(param_def_id);
- assert!(inserted, "visited lifetime {:?} twice", param.hir_id);
+ let inserted = late_bound.insert(param.hir_id.local_id);
+ assert!(inserted, "visited lifetime {:?} twice", param.def_id);
}
debug!(?late_bound);
}
}
- hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
// consider only the lifetimes on the final
// segment; I am not sure it's even currently
// valid to have them elsewhere, but even if it
Node::ImplItem(item) => item.generics,
Node::Item(item) => match item.kind {
- ItemKind::Impl(ref impl_) => {
+ ItemKind::Impl(impl_) => {
if impl_.defaultness.is_default() {
- is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy);
+ is_default_impl_trait =
+ tcx.impl_trait_ref(def_id).map(|t| ty::Binder::dummy(t.subst_identity()));
}
- &impl_.generics
+ impl_.generics
}
- ItemKind::Fn(.., ref generics, _)
- | ItemKind::TyAlias(_, ref generics)
- | ItemKind::Enum(_, ref generics)
- | ItemKind::Struct(_, ref generics)
- | ItemKind::Union(_, ref generics) => *generics,
+ ItemKind::Fn(.., generics, _)
+ | ItemKind::TyAlias(_, generics)
+ | ItemKind::Enum(_, generics)
+ | ItemKind::Struct(_, generics)
+ | ItemKind::Union(_, generics) => generics,
- ItemKind::Trait(_, _, ref generics, ..) | ItemKind::TraitAlias(ref generics, _) => {
+ ItemKind::Trait(_, _, generics, ..) | ItemKind::TraitAlias(generics, _) => {
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
- *generics
+ generics
}
- ItemKind::OpaqueTy(OpaqueTy { ref generics, .. }) => generics,
+ ItemKind::OpaqueTy(OpaqueTy { generics, .. }) => generics,
_ => NO_GENERICS,
},
Node::ForeignItem(item) => match item.kind {
ForeignItemKind::Static(..) => NO_GENERICS,
- ForeignItemKind::Fn(_, _, ref generics) => *generics,
+ ForeignItemKind::Fn(_, _, generics) => generics,
ForeignItemKind::Type => NO_GENERICS,
},
// Subtle: before we store the predicates into the tcx, we
// sort them so that predicates like `T: Foo<Item=U>` come
- // before uses of `U`. This avoids false ambiguity errors
+ // before uses of `U`. This avoids false ambiguity errors
// in trait checking. See `setup_constraining_predicates`
// for details.
if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node {
let self_ty = tcx.type_of(def_id);
- let trait_ref = tcx.impl_trait_ref(def_id);
+ let trait_ref = tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::subst_identity);
cgp::setup_constraining_predicates(
tcx,
&mut predicates,
}
let hir::GenericParamKind::Lifetime { .. } = duplicate.kind else { continue };
- let dup_def = tcx.hir().local_def_id(duplicate.hir_id).to_def_id();
+ let dup_def = duplicate.def_id.to_def_id();
let Some(dup_index) = generics.param_def_id_to_index(tcx, dup_def) else { bug!() };
let node = tcx.hir().get(hir_id);
let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
- if let hir::Node::Item(item) = node && let hir::ItemKind::Impl(ref impl_) = item.kind {
+ if let hir::Node::Item(item) = node && let hir::ItemKind::Impl(impl_) = item.kind {
if let Some(of_trait) = &impl_.of_trait {
debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id);
collector.visit_trait_ref(of_trait);
};
let (generics, bounds) = match item.kind {
- hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits),
- hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits),
+ hir::ItemKind::Trait(.., generics, supertraits, _) => (generics, supertraits),
+ hir::ItemKind::TraitAlias(generics, supertraits) => (generics, supertraits),
_ => span_bug!(item.span, "super_predicates invoked on non-trait"),
};
Node::Item(item) => {
match item.kind {
- ItemKind::Fn(.., ref generics, _)
- | ItemKind::Impl(hir::Impl { ref generics, .. })
- | ItemKind::TyAlias(_, ref generics)
+ ItemKind::Fn(.., generics, _)
+ | ItemKind::Impl(&hir::Impl { generics, .. })
+ | ItemKind::TyAlias(_, generics)
| ItemKind::OpaqueTy(OpaqueTy {
- ref generics,
+ generics,
origin: hir::OpaqueTyOrigin::TyAlias,
..
})
- | ItemKind::Enum(_, ref generics)
- | ItemKind::Struct(_, ref generics)
- | ItemKind::Union(_, ref generics) => generics,
- ItemKind::Trait(_, _, ref generics, ..) => {
+ | ItemKind::Enum(_, generics)
+ | ItemKind::Struct(_, generics)
+ | ItemKind::Union(_, generics) => generics,
+ ItemKind::Trait(_, _, generics, ..) => {
// Implied `Self: Trait` and supertrait bounds.
if param_id == item_hir_id {
let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id);
}
Node::ForeignItem(item) => match item.kind {
- ForeignItemKind::Fn(_, _, ref generics) => generics,
+ ForeignItemKind::Fn(_, _, generics) => generics,
_ => return result,
},
ast_generics
.predicates
.iter()
- .filter_map(|wp| match *wp {
- hir::WherePredicate::BoundPredicate(ref bp) => Some(bp),
+ .filter_map(|wp| match wp {
+ hir::WherePredicate::BoundPredicate(bp) => Some(bp),
_ => None,
})
.flat_map(|bp| {
// ty which is a fully resolved projection.
// For the code example above, this would mean converting Self::Assoc<3>
// into a ty::Alias(ty::Projection, <Self as Foo>::Assoc<3>)
- let item_hir_id = tcx
+ let item_def_id = tcx
.hir()
- .parent_iter(hir_id)
- .filter(|(_, node)| matches!(node, Node::Item(_)))
- .map(|(id, _)| id)
- .next()
- .unwrap();
- let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id();
- let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>;
+ .parent_owner_iter(hir_id)
+ .find(|(_, node)| matches!(node, OwnerNode::Item(_)))
+ .unwrap()
+ .0
+ .to_def_id();
+ let item_ctxt = &ItemCtxt::new(tcx, item_def_id) as &dyn crate::astconv::AstConv<'_>;
let ty = item_ctxt.ast_ty_to_ty(hir_ty);
// Iterate through the generics of the projection to find the one that corresponds to
ForeignItemKind::Type => tcx.mk_foreign(def_id.to_def_id()),
},
- Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def {
+ Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def {
VariantData::Unit(..) | VariantData::Struct(..) => {
tcx.type_of(tcx.hir().get_parent_item(hir_id))
}
Node::AnonConst(_) => {
let parent_node = tcx.hir().get_parent(hir_id);
match parent_node {
- Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. })
- | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
+ Node::Ty(Ty { kind: TyKind::Array(_, constant), .. })
+ | Node::Expr(Expr { kind: ExprKind::Repeat(_, constant), .. })
if constant.hir_id() == hir_id =>
{
tcx.types.usize
}
- Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => {
+ Node::Ty(Ty { kind: TyKind::Typeof(e), .. }) if e.hir_id == hir_id => {
tcx.typeck(def_id).node_type(e.hir_id)
}
- Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. })
+ Node::Expr(Expr { kind: ExprKind::ConstBlock(anon_const), .. })
if anon_const.hir_id == hir_id =>
{
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
tcx.typeck(def_id).node_type(hir_id)
}
- Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => {
+ Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
}
Node::TypeBinding(
- binding @ &TypeBinding {
+ TypeBinding {
hir_id: binding_id,
- kind: TypeBindingKind::Equality { term: Term::Const(ref e) },
+ kind: TypeBindingKind::Equality { term: Term::Const(e) },
+ ident,
..
},
) if let Node::TraitRef(trait_ref) =
- tcx.hir().get_parent(binding_id)
+ tcx.hir().get_parent(*binding_id)
&& e.hir_id == hir_id =>
{
let Some(trait_def_id) = trait_ref.trait_def_id() else {
let assoc_items = tcx.associated_items(trait_def_id);
let assoc_item = assoc_items.find_by_name_and_kind(
tcx,
- binding.ident,
+ *ident,
ty::AssocKind::Const,
def_id.to_def_id(),
);
}
Node::TypeBinding(
- binding @ &TypeBinding { hir_id: binding_id, gen_args, ref kind, .. },
+ TypeBinding { hir_id: binding_id, gen_args, kind, ident, .. },
) if let Node::TraitRef(trait_ref) =
- tcx.hir().get_parent(binding_id)
+ tcx.hir().get_parent(*binding_id)
&& let Some((idx, _)) =
gen_args.args.iter().enumerate().find(|(_, arg)| {
if let GenericArg::Const(ct) = arg {
let assoc_items = tcx.associated_items(trait_def_id);
let assoc_item = assoc_items.find_by_name_and_kind(
tcx,
- binding.ident,
+ *ident,
match kind {
// I think `<A: T>` type bindings requires that `A` is a type
TypeBindingKind::Constraint { .. }
}
match ty.kind() {
- ty::FnDef(def_id, _) => self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id)),
+ ty::FnDef(def_id, substs) => {
+ self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id).subst(self.tcx, substs))
+ }
// FIXME: non-capturing closures should also suggest a function pointer
ty::Closure(..) | ty::Generator(..) => {
self.success = false;
match *t.kind() {
ty::Alias(ty::Projection, ..) if !self.include_nonconstraining => {
// projections are not injective
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::Param(data) => {
self.parameters.push(Parameter::from(data));
if let ty::ReEarlyBound(data) = *r {
self.parameters.push(Parameter::from(data));
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
use crate::collect::ItemCtxt;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{ForeignItem, ForeignItemKind, HirId};
+use rustc_hir::{ForeignItem, ForeignItemKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::{ObligationCause, WellFormedLoc};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Region, TyCtxt, TypeFoldable, TypeFolder};
+use rustc_span::def_id::LocalDefId;
use rustc_trait_selection::traits;
pub fn provide(providers: &mut Providers) {
cause: Option<ObligationCause<'tcx>>,
cause_depth: usize,
icx: ItemCtxt<'tcx>,
- hir_id: HirId,
+ def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
depth: usize,
}
let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
let cause = traits::ObligationCause::new(
ty.span,
- self.hir_id,
+ self.def_id,
traits::ObligationCauseCode::WellFormed(None),
);
let errors = traits::fully_solve_obligation(
cause: None,
cause_depth: 0,
icx,
- hir_id,
+ def_id,
param_env: tcx.param_env(def_id.to_def_id()),
depth: 0,
};
},
hir::Node::Item(item) => match item.kind {
hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => vec![ty],
- hir::ItemKind::Impl(ref impl_) => match &impl_.of_trait {
+ hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
Some(t) => t
.path
.segments
}
let impl_generics = tcx.generics_of(impl_def_id);
let impl_predicates = tcx.predicates_of(impl_def_id);
- let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::subst_identity);
let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref);
cgp::identify_constrained_generic_params(
fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Option<Node> {
let trait_ref = tcx.impl_trait_ref(impl1_def_id)?;
- let trait_def = tcx.trait_def(trait_ref.def_id);
+ let trait_def = tcx.trait_def(trait_ref.skip_binder().def_id);
let impl2_node = trait_def.ancestors(tcx, impl1_def_id.to_def_id()).ok()?.nth(1)?;
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);
- let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id);
let assumed_wf_types =
ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
return None;
}
- let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types);
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
let _ =
infcx.err_ctxt().check_region_obligations_and_report_errors(impl1_def_id, &outlives_env);
let impl_generic_predicates = tcx.predicates_of(impl_def_id);
let mut unconstrained_parameters = FxHashSet::default();
let mut constrained_params = FxHashSet::default();
- let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::subst_identity);
// Unfortunately the functions in `constrained_generic_parameters` don't do
// what we want here. We want only a list of constrained parameters while
});
// Include the well-formed predicates of the type parameters of the impl.
- for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
+ for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().subst_identity().substs {
let infcx = &tcx.infer_ctxt().build();
- let obligations = wf::obligations(
- infcx,
- tcx.param_env(impl1_def_id),
- tcx.hir().local_def_id_to_hir_id(impl1_def_id),
- 0,
- arg,
- span,
- )
- .unwrap();
+ let obligations =
+ wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, arg, span)
+ .unwrap();
assert!(!obligations.needs_infer());
impl2_predicates.extend(
4. Finally, the check phase then checks function bodies and so forth.
Within the check phase, we check each function body one at a time
(bodies of function expressions are checked as part of the
- containing function). Inference is used to supply types wherever
+ containing function). Inference is used to supply types wherever
they are unknown. The actual checking of a function itself has
several phases (check, regionck, writeback), as discussed in the
documentation for the [`check`] module.
local variables, type parameters, etc as necessary.
- infer: finds the types to use for each type variable such that
- all subtyping and assignment constraints are met. In essence, the check
+ all subtyping and assignment constraints are met. In essence, the check
module specifies the constraints, and the infer module solves them.
## Note
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_hir::Node;
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
use rustc_middle::middle;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
use rustc_session::{config::EntryFnType, parse::feature_err};
+use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_span::{symbol::sym, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use std::iter;
+use std::ops::Not;
use astconv::AstConv;
use bounds::Bounds;
}
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
- let main_fnsig = tcx.fn_sig(main_def_id);
+ let main_fnsig = tcx.fn_sig(main_def_id).subst_identity();
let main_span = tcx.def_span(main_def_id);
- fn main_fn_diagnostics_hir_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> hir::HirId {
+ fn main_fn_diagnostics_def_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> LocalDefId {
if let Some(local_def_id) = def_id.as_local() {
- let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
let hir_type = tcx.type_of(local_def_id);
if !matches!(hir_type.kind(), ty::FnDef(..)) {
span_bug!(sp, "main has a non-function type: found `{}`", hir_type);
}
- hir_id
+ local_def_id
} else {
- CRATE_HIR_ID
+ CRATE_DEF_ID
}
}
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
- Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
- if !generics.params.is_empty() {
- Some(generics.span)
- } else {
- None
- }
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. })) => {
+ generics.params.is_empty().not().then(|| generics.span)
}
_ => {
span_bug!(tcx.def_span(def_id), "main has a non-function type");
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
- Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. })) => {
Some(generics.where_clause_span)
}
_ => {
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
- Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(ref fn_sig, _, _), .. })) => {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(fn_sig, _, _), .. })) => {
Some(fn_sig.decl.output.span())
}
_ => {
}
let mut error = false;
- let main_diagnostics_hir_id = main_fn_diagnostics_hir_id(tcx, main_def_id, main_span);
+ let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span);
let main_fn_generics = tcx.generics_of(main_def_id);
let main_fn_predicates = tcx.predicates_of(main_def_id);
if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
let param_env = ty::ParamEnv::empty();
let cause = traits::ObligationCause::new(
return_ty_span,
- main_diagnostics_hir_id,
+ main_diagnostics_def_id,
ObligationCauseCode::MainFunctionType,
);
let ocx = traits::ObligationCtxt::new(&infcx);
tcx,
&ObligationCause::new(
main_span,
- main_diagnostics_hir_id,
+ main_diagnostics_def_id,
ObligationCauseCode::MainFunctionType,
),
se_ty,
match start_t.kind() {
ty::FnDef(..) => {
if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
- if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
+ if let hir::ItemKind::Fn(sig, generics, _) = &it.kind {
let mut error = false;
if !generics.params.is_empty() {
struct_span_err!(
require_same_types(
tcx,
- &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType),
+ &ObligationCause::new(
+ start_span,
+ start_def_id,
+ ObligationCauseCode::StartFunctionType,
+ ),
se_ty,
- tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)),
+ tcx.mk_fn_ptr(tcx.fn_sig(start_def_id).subst_identity()),
);
}
_ => {
pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
// In case there are any projections, etc., find the "environment"
// def-ID that will be used to determine the traits/predicates in
- // scope. This is derived from the enclosing item-like thing.
+ // scope. This is derived from the enclosing item-like thing.
let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id);
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
item_cx.astconv().ast_ty_to_ty(hir_ty)
) -> Bounds<'tcx> {
// In case there are any projections, etc., find the "environment"
// def-ID that will be used to determine the traits/predicates in
- // scope. This is derived from the enclosing item-like thing.
+ // scope. This is derived from the enclosing item-like thing.
let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
let mut bounds = Bounds::default();
if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 {
// `unsubstituted_predicate` is `U: 'b` in the
- // example above. So apply the substitution to
+ // example above. So apply the substitution to
// get `T: 'a` (or `predicate`):
let predicate = unsubstituted_predicates
.rebind(*unsubstituted_predicate)
// ```
//
// Here `outlived_region = 'a` and `kind = &'b
- // u32`. Decomposing `&'b u32` into
+ // u32`. Decomposing `&'b u32` into
// components would yield `'b`, and we add the
// where clause that `'b: 'a`.
insert_outlives_predicate(
// ```
//
// Here `outlived_region = 'a` and `kind =
- // Vec<U>`. Decomposing `Vec<U>` into
+ // Vec<U>`. Decomposing `Vec<U>` into
// components would yield `U`, and we add the
// where clause that `U: 'a`.
let ty: Ty<'tcx> = param_ty.to_ty(tcx);
.or_insert(span);
}
- Component::Projection(proj_ty) => {
- // This would arise from something like:
+ Component::Alias(alias_ty) => {
+ // This would either arise from something like:
//
// ```
// struct Foo<'a, T: Iterator> {
// }
// ```
//
- // Here we want to add an explicit `where <T as Iterator>::Item: 'a`.
- let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.def_id, proj_ty.substs);
- required_predicates
- .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
- .or_insert(span);
- }
-
- Component::Opaque(def_id, substs) => {
- // This would arise from something like:
+ // or:
//
// ```rust
// type Opaque<T> = impl Sized;
// struct Ss<'a, T>(&'a Opaque<T>);
// ```
//
- // Here we want to have an implied bound `Opaque<T>: 'a`
-
- let ty = tcx.mk_opaque(def_id, substs);
+ // Here we want to add an explicit `where <T as Iterator>::Item: 'a`
+ // or `Opaque<T>: 'a` depending on the alias kind.
+ let ty = alias_ty.to_ty(tcx);
required_predicates
.entry(ty::OutlivesPredicate(ty.into(), outlived_region))
.or_insert(span);
}
- Component::EscapingProjection(_) => {
+ Component::EscapingAlias(_) => {
// As above, but the projection involves
- // late-bound regions. Therefore, the WF
+ // late-bound regions. Therefore, the WF
// requirement is not checked in type definition
// but at fn call site, so ignore it.
//
// }
//
// The type above might generate a `T: 'b` bound, but we can
- // ignore it. We can't put it on the struct header anyway.
+ // ignore it. We can't put it on the struct header anyway.
ty::ReLateBound(..) => false,
// These regions don't appear in types from type declarations:
if let Some(parent_node) = self.tcx.hir().opt_parent_id(self.path_segment.hir_id)
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
&& let hir::Node::Expr(expr) = parent_node {
- match expr.kind {
- hir::ExprKind::Path(ref qpath) => {
+ match &expr.kind {
+ hir::ExprKind::Path(qpath) => {
self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
err,
qpath,
}
ty::FnDef(..) => {
- self.add_constraints_from_sig(current_item, tcx.fn_sig(def_id), self.covariant);
+ self.add_constraints_from_sig(
+ current_item,
+ tcx.fn_sig(def_id).subst_identity(),
+ self.covariant,
+ );
}
ty::Error(_) => {}
}
ty::Ref(region, ty, mutbl) => {
- let contra = self.contravariant(variance);
- self.add_constraints_from_region(current, region, contra);
+ self.add_constraints_from_region(current, region, variance);
self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance);
}
}
ty::Dynamic(data, r, _) => {
- // The type `Foo<T+'a>` is contravariant w/r/t `'a`:
- let contra = self.contravariant(variance);
- self.add_constraints_from_region(current, r, contra);
+ // The type `dyn Trait<T> +'a` is covariant w/r/t `'a`:
+ self.add_constraints_from_region(current, r, variance);
if let Some(poly_trait_ref) = data.principal() {
self.add_constraints_from_invariant_substs(
// types, where we use Error as the Self type
}
- ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Bound(..) | ty::Infer(..) => {
- bug!(
- "unexpected type encountered in \
- variance inference: {}",
- ty
- );
+ ty::Placeholder(..)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Bound(..)
+ | ty::Infer(..) => {
+ bug!("unexpected type encountered in variance inference: {}", ty);
}
}
}
a.visit_with(self)?;
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
substs.visit_with(self)
}
//! optimal solution to the constraints. The final variance for each
//! inferred is then written into the `variance_map` in the tcx.
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::DefIdMap;
use rustc_middle::ty;
use super::constraints::*;
let ConstraintContext { terms_cx, constraints, .. } = constraints_cx;
let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()];
- for &(id, ref variances) in &terms_cx.lang_items {
- let InferredIndex(start) = terms_cx.inferred_starts[&id];
+ for (id, variances) in &terms_cx.lang_items {
+ let InferredIndex(start) = terms_cx.inferred_starts[id];
for (i, &variance) in variances.iter().enumerate() {
solutions[start + i] = variance;
}
impl<'a, 'tcx> SolveContext<'a, 'tcx> {
fn solve(&mut self) {
- // Propagate constraints until a fixed point is reached. Note
+ // Propagate constraints until a fixed point is reached. Note
// that the maximum number of iterations is 2C where C is the
// number of constraints (each variable can change values at most
// twice). Since number of constraints is linear in size of the
}
}
- fn create_map(&self) -> FxHashMap<DefId, &'tcx [ty::Variance]> {
+ fn create_map(&self) -> DefIdMap<&'tcx [ty::Variance]> {
let tcx = self.terms_cx.tcx;
let solutions = &self.solutions;
- self.terms_cx
- .inferred_starts
- .iter()
- .map(|(&def_id, &InferredIndex(start))| {
+ DefIdMap::from(self.terms_cx.inferred_starts.items().map(
+ |(&def_id, &InferredIndex(start))| {
let generics = tcx.generics_of(def_id);
let count = generics.count();
}
(def_id.to_def_id(), &*variances)
- })
- .collect()
+ },
+ ))
}
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {
-use rustc_errors::struct_span_err;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
for id in tcx.hir().items() {
if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_variance) {
let variances_of = tcx.variances_of(id.owner_id);
- struct_span_err!(tcx.sess, tcx.def_span(id.owner_id), E0208, "{:?}", variances_of)
- .emit();
+
+ tcx.sess.struct_span_err(tcx.def_span(id.owner_id), format!("{variances_of:?}")).emit();
}
}
}
let hir = self.tcx.hir();
// First, check that we're actually in the tail of a function.
- let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) =
- hir.get(self.body_id) else { return; };
+ let Some(body_id) = hir.maybe_body_owned_by(self.body_id) else { return; };
+ let body = hir.body(body_id);
+ let hir::ExprKind::Block(block, _) = body.value.kind else { return; };
let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. })
= block.innermost_block().stmts.last() else { return; };
if last_expr.hir_id != expr.hir_id {
// Next, make sure that we have no type expectation.
let Some(ret) = hir
- .find_by_def_id(self.body_id.owner.def_id)
+ .find_by_def_id(self.body_id)
.and_then(|owner| owner.fn_decl())
.map(|decl| decl.output.span()) else { return; };
let Expectation::IsLast(stmt) = expectation else {
) -> Ty<'tcx> {
let (fn_sig, def_id) = match *callee_ty.kind() {
ty::FnDef(def_id, subst) => {
- let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst);
+ let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, subst);
// Unit testing: function items annotated with
// `#[rustc_evaluate_where_clauses]` trigger special output
if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
let predicates = self.tcx.predicates_of(def_id);
let predicates = predicates.instantiate(self.tcx, subst);
- for (predicate, predicate_span) in
- predicates.predicates.iter().zip(&predicates.spans)
- {
+ for (predicate, predicate_span) in predicates {
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy_with_span(callee_expr.span),
self.param_env,
- *predicate,
+ predicate,
);
let result = self.evaluate_obligation(&obligation);
self.tcx
callee_expr.span,
&format!("evaluate({:?}) = {:?}", predicate, result),
)
- .span_label(*predicate_span, "predicate")
+ .span_label(predicate_span, "predicate")
.emit();
}
}
};
if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
- if let Some((maybe_def, output_ty, _)) =
- self.extract_callable_info(callee_expr, callee_ty)
+ if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty)
&& !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span)
{
let descr = match maybe_def {
use super::FnCtxt;
use crate::type_error_struct;
-use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
+use hir::ExprKind;
+use rustc_errors::{
+ struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
use rustc_hir as hir;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::mir::Mutability;
| ty::Float(_)
| ty::Array(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::RawPtr(_)
| ty::Ref(..)
| ty::FnDef(..)
#[derive(Copy, Clone)]
pub enum CastError {
- ErrorGuaranteed,
+ ErrorGuaranteed(ErrorGuaranteed),
CastToBool,
CastToChar,
}
impl From<ErrorGuaranteed> for CastError {
- fn from(_: ErrorGuaranteed) -> Self {
- CastError::ErrorGuaranteed
+ fn from(err: ErrorGuaranteed) -> Self {
+ CastError::ErrorGuaranteed(err)
}
}
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
match e {
- CastError::ErrorGuaranteed => {
+ CastError::ErrorGuaranteed(_) => {
// an error has already been reported
}
CastError::NeedDeref => {
- let error_span = self.span;
let mut err = make_invalid_casting_error(
fcx.tcx.sess,
self.span,
self.cast_ty,
fcx,
);
- let cast_ty = fcx.ty_to_string(self.cast_ty);
- err.span_label(
- error_span,
- format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty),
- );
- if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr_span) {
- err.span_suggestion(
- self.expr_span,
- "dereference the expression",
- format!("*{}", snippet),
- Applicability::MaybeIncorrect,
+
+ if matches!(self.expr.kind, ExprKind::AddrOf(..)) {
+ // get just the borrow part of the expression
+ let span = self.expr_span.with_hi(self.expr.peel_borrows().span.lo());
+ err.span_suggestion_verbose(
+ span,
+ "remove the unneeded borrow",
+ "",
+ Applicability::MachineApplicable,
);
} else {
- err.span_help(self.expr_span, "dereference the expression with `*`");
+ err.span_suggestion_verbose(
+ self.expr_span.shrink_to_lo(),
+ "dereference the expression",
+ "*",
+ Applicability::MachineApplicable,
+ );
}
+
err.emit();
}
CastError::NeedViaThinPtr | CastError::NeedViaPtr => {
}
));
}
+
+ self.try_suggest_collection_to_bool(fcx, &mut err);
+
err.emit();
}
CastError::NeedViaInt => {
} else {
err.span_label(self.span, "invalid cast");
}
+
+ self.try_suggest_collection_to_bool(fcx, &mut err);
+
err.emit();
}
CastError::SizedUnsizedCast => {
},
);
}
+
+ /// Attempt to suggest using `.is_empty` when trying to cast from a
+ /// collection type to a boolean.
+ fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
+ if self.cast_ty.is_bool() {
+ let derefed = fcx
+ .autoderef(self.expr_span, self.expr_ty)
+ .silence_errors()
+ .find(|t| matches!(t.0.kind(), ty::Str | ty::Slice(..)));
+
+ if let Some((deref_ty, _)) = derefed {
+ // Give a note about what the expr derefs to.
+ if deref_ty != self.expr_ty.peel_refs() {
+ err.span_note(
+ self.expr_span,
+ format!(
+ "this expression `Deref`s to `{}` which implements `is_empty`",
+ fcx.ty_to_string(deref_ty)
+ ),
+ );
+ }
+
+ // Create a multipart suggestion: add `!` and `.is_empty()` in
+ // place of the cast.
+ let suggestion = vec![
+ (self.expr_span.shrink_to_lo(), "!".to_string()),
+ (self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
+ ];
+
+ err.multipart_suggestion_verbose(format!(
+ "consider using the `is_empty` method on `{}` to determine if it contains anything",
+ fcx.ty_to_string(self.expr_ty),
+ ), suggestion, Applicability::MaybeIncorrect);
+ }
+ }
+ }
}
let ret_ty =
fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars(
declared_ret_ty,
- body.value.hir_id,
+ fn_def_id,
decl.output.span(),
fcx.param_env,
));
let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
let interior = fcx
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
- fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
+ fcx.deferred_generator_interiors.borrow_mut().push((
+ fn_def_id,
+ body.id(),
+ interior,
+ gen_kind,
+ ));
let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
Some(GeneratorTypes {
// Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
if let Some(panic_impl_did) = tcx.lang_items().panic_impl()
- && panic_impl_did == hir.local_def_id(fn_id).to_def_id()
+ && panic_impl_did == fn_def_id.to_def_id()
{
check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
}
- if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() {
+ if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == fn_def_id.to_def_id() {
check_lang_start_fn(tcx, fn_sig, decl, fn_def_id);
}
use hir::def::DefKind;
use rustc_hir as hir;
-use rustc_hir::def_id::LocalDefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
debug!(?bound_sig, ?liberated_sig);
- let mut fcx = FnCtxt::new(self, self.param_env.without_const(), body.value.hir_id);
+ let mut fcx = FnCtxt::new(self, self.param_env.without_const(), closure.def_id);
let generator_types = check_fn(
&mut fcx,
liberated_sig,
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if t == self.expected_ty {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
t.super_visit_with(self)
}
// FIXME(#45727): As discussed in [this comment][c1], naively
// forcing equality here actually results in suboptimal error
- // messages in some cases. For now, if there would have been
+ // messages in some cases. For now, if there would have been
// an obvious error, we fallback to declaring the type of the
// closure to be the one the user gave, which allows other
// error message code to trigger.
// function.
Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
debug!("closure is async fn body");
- self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id)
- .unwrap_or_else(|| {
+ let def_id = self.tcx.hir().body_owner_def_id(body.id());
+ self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else(
+ || {
// AFAIK, deducing the future output
// always succeeds *except* in error cases
// like #65159. I'd like to return Error
// *have* reported an
// error. --nikomatsakis
astconv.ty_infer(None, decl.output.span())
- })
+ },
+ )
}
_ => astconv.ty_infer(None, decl.output.span()),
fn deduce_future_output_from_obligations(
&self,
expr_def_id: LocalDefId,
- body_id: hir::HirId,
+ body_def_id: LocalDefId,
) -> Option<Ty<'tcx>> {
let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn")
let InferOk { value: output_ty, obligations } = self
.replace_opaque_types_with_inference_vars(
output_ty,
- body_id,
+ body_def_id,
self.tcx.def_span(expr_def_id),
self.param_env,
);
// If we have a parameter of type `&M T_a` and the value
// provided is `expr`, we will be adding an implicit borrow,
- // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
+ // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
// to type check, we will construct the type that `&M*expr` would
// yield.
continue;
}
- // At this point, we have deref'd `a` to `referent_ty`. So
+ // At this point, we have deref'd `a` to `referent_ty`. So
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
// In the autoderef loop for `&'a mut Vec<T>`, we would get
// three callbacks:
// - if in sub mode, that means we want to use `'b` (the
// region from the target reference) for both
// pointers [2]. This is because sub mode (somewhat
- // arbitrarily) returns the subtype region. In the case
+ // arbitrarily) returns the subtype region. In the case
// where we are coercing to a target type, we know we
// want to use that target type region (`'b`) because --
// for the program to type-check -- it must be the
// annotate the region of a borrow), and regionck has
// code that adds edges from the region of a borrow
// (`'b`, here) into the regions in the borrowed
- // expression (`*x`, here). (Search for "link".)
+ // expression (`*x`, here). (Search for "link".)
// - if in lub mode, things can get fairly complicated. The
// easiest thing is just to make a fresh
// region variable [4], which effectively means we defer
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
// As a special case, if we would produce `&'a *x`, that's
// a total no-op. We end up with the type `&'a T` just as
- // we started with. In that case, just skip it
+ // we started with. In that case, just skip it
// altogether. This is just an optimization.
//
// Note that for `&mut`, we DO want to reborrow --
// if let Some(x) = ... { }
//
// we wind up with a second match arm that is like `_ =>
- // ()`. That is the case we are considering here. We take
+ // ()`. That is the case we are considering here. We take
// a different path to get the right "expected, found"
// message and so forth (and because we know that
// `expression_ty` will be unit).
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
}
+
let reported = err.emit_unless(unsized_return);
self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
}
}
}
+
fn note_unreachable_loop_return(
&self,
err: &mut Diagnostic,
.trait_ref()
.and_then(|t| t.trait_def_id())
.map_or(false, |def_id| {
- fcx.tcx.object_safety_violations(def_id).is_empty()
+ fcx.tcx.check_is_object_safe(def_id)
})
})
}
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
- || self.suggest_floating_point_literal(err, expr, expected);
+ || self.suggest_floating_point_literal(err, expr, expected)
+ || self.note_result_coercion(err, expr, expected, expr_ty);
if !suggested {
- self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
+ self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span);
}
}
self.annotate_expected_due_to_let_ty(err, expr, error);
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
- self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
+ self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
}
/// Requires that the two types unify, and prints an error message if
expr: &hir::Expr<'_>,
found: Ty<'tcx>,
expected: Ty<'tcx>,
+ mismatch_span: Span,
) -> bool {
let map = self.tcx.hir();
lt_op: |_| self.tcx.lifetimes.re_erased,
ct_op: |c| c,
ty_op: |t| match *t.kind() {
- ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))),
+ ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)),
ty::Infer(ty::IntVar(_)) => {
self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 }))
}
},
};
let mut prev = eraser.fold_ty(ty);
- let mut prev_span = None;
+ let mut prev_span: Option<Span> = None;
for binding in expr_finder.uses {
// In every expression where the binding is referenced, we will look at that
{
// We special case methods, because they can influence inference through the
// call's arguments and we can provide a more explicit span.
- let sig = self.tcx.fn_sig(def_id);
+ let sig = self.tcx.fn_sig(def_id).subst_identity();
let def_self_ty = sig.input(0).skip_binder();
let rcvr_ty = self.node_ty(rcvr.hir_id);
// Get the evaluated type *after* calling the method call, so that the influence
// inferred in this method call.
let arg = &args[i];
let arg_ty = self.node_ty(arg.hir_id);
- err.span_label(
- arg.span,
- &format!(
- "this is of type `{arg_ty}`, which causes `{ident}` to be \
- inferred as `{ty}`",
- ),
- );
+ if !arg.span.overlaps(mismatch_span) {
+ err.span_label(
+ arg.span,
+ &format!(
+ "this is of type `{arg_ty}`, which causes `{ident}` to be \
+ inferred as `{ty}`",
+ ),
+ );
+ }
param_args.insert(param_ty, (arg, arg_ty));
}
}
&& self.can_eq(self.param_env, ty, found).is_ok()
{
// We only point at the first place where the found type was inferred.
+ if !segment.ident.span.overlaps(mismatch_span) {
err.span_label(
segment.ident.span,
with_forced_trimmed_paths!(format!(
"here the type of `{ident}` is inferred to be `{ty}`",
)),
- );
+ );}
break;
} else if !param_args.is_empty() {
break;
// We use the *previous* span because if the type is known *here* it means
// it was *evaluated earlier*. We don't do this for method calls because we
// evaluate the method's self type eagerly, but not in any other case.
- err.span_label(
- span,
- with_forced_trimmed_paths!(format!(
- "here the type of `{ident}` is inferred to be `{ty}`",
- )),
- );
+ if !span.overlaps(mismatch_span) {
+ err.span_label(
+ span,
+ with_forced_trimmed_paths!(format!(
+ "here the type of `{ident}` is inferred to be `{ty}`",
+ )),
+ );
+ }
break;
}
prev = ty;
let substs = ty::InternalSubsts::for_item(self.tcx, m.def_id, |param, _| {
self.var_for_def(deref.span, param)
});
+ let mutability =
+ match self.tcx.fn_sig(m.def_id).skip_binder().input(0).skip_binder().kind() {
+ ty::Ref(_, _, hir::Mutability::Mut) => "&mut ",
+ ty::Ref(_, _, _) => "&",
+ _ => "",
+ };
vec![
(
deref.span.until(base.span),
with_no_trimmed_paths!(
self.tcx.def_path_str_with_substs(m.def_id, substs,)
),
- match self.tcx.fn_sig(m.def_id).input(0).skip_binder().kind() {
- ty::Ref(_, _, hir::Mutability::Mut) => "&mut ",
- ty::Ref(_, _, _) => "&",
- _ => "",
- },
+ mutability,
),
),
match &args[..] {
);
}
+ pub(crate) fn note_result_coercion(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) -> bool {
+ let ty::Adt(e, substs_e) = expected.kind() else { return false; };
+ let ty::Adt(f, substs_f) = found.kind() else { return false; };
+ if e.did() != f.did() {
+ return false;
+ }
+ if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) {
+ return false;
+ }
+ let map = self.tcx.hir();
+ if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id)
+ && let hir::ExprKind::Ret(_) = expr.kind
+ {
+ // `return foo;`
+ } else if map.get_return_block(expr.hir_id).is_some() {
+ // Function's tail expression.
+ } else {
+ return false;
+ }
+ let e = substs_e.type_at(1);
+ let f = substs_f.type_at(1);
+ if self
+ .infcx
+ .type_implements_trait(
+ self.tcx.get_diagnostic_item(sym::Into).unwrap(),
+ [f, e],
+ self.param_env,
+ )
+ .must_apply_modulo_regions()
+ {
+ err.multipart_suggestion(
+ "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \
+ in `Ok` so the expression remains of type `Result`",
+ vec![
+ (expr.span.shrink_to_lo(), "Ok(".to_string()),
+ (expr.span.shrink_to_hi(), "?)".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ false
+ }
+
/// If the expected type is an enum (Issue #55250) with any variants whose
/// sole field is of the found type, suggest such variants. (Issue #42764)
fn suggest_compatible_variants(
match method.kind {
ty::AssocKind::Fn => {
method.fn_has_self_parameter
- && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1
+ && self.tcx.fn_sig(method.def_id).skip_binder().inputs().skip_binder().len()
+ == 1
}
_ => false,
}
sugg_sp = receiver.span;
}
}
+
+ if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind
+ && let Some(1) = self.deref_steps(expected, checked_ty) {
+ // We have `*&T`, check if what was expected was `&T`.
+ // If so, we may want to suggest removing a `*`.
+ sugg_sp = sugg_sp.with_hi(inner.span.lo());
+ return Some((
+ sugg_sp,
+ "consider removing deref here".to_string(),
+ "".to_string(),
+ Applicability::MachineApplicable,
+ true,
+ false,
+ ));
+ }
+
if let Ok(src) = sm.span_to_snippet(sugg_sp) {
let needs_parens = match expr.kind {
// parenthesize if needed (Issue #46756)
err.span_label(block.span, "this block is missing a tail expression");
}
}
+
+ fn check_wrong_return_type_due_to_generic_arg(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ checked_ty: Ty<'tcx>,
+ ) {
+ let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { return; };
+ enum CallableKind {
+ Function,
+ Method,
+ Constructor,
+ }
+ let mut maybe_emit_help = |def_id: hir::def_id::DefId,
+ callable: rustc_span::symbol::Ident,
+ args: &[hir::Expr<'_>],
+ kind: CallableKind| {
+ let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
+ let fn_ty = self.tcx.bound_type_of(def_id).0;
+ if !fn_ty.is_fn() {
+ return;
+ }
+ let fn_sig = fn_ty.fn_sig(self.tcx).skip_binder();
+ let Some(&arg) = fn_sig.inputs().get(arg_idx + if matches!(kind, CallableKind::Method) { 1 } else { 0 }) else { return; };
+ if matches!(arg.kind(), ty::Param(_))
+ && fn_sig.output().contains(arg)
+ && self.node_ty(args[arg_idx].hir_id) == checked_ty
+ {
+ let mut multi_span: MultiSpan = parent_expr.span.into();
+ multi_span.push_span_label(
+ args[arg_idx].span,
+ format!(
+ "this argument influences the {} of `{}`",
+ if matches!(kind, CallableKind::Constructor) {
+ "type"
+ } else {
+ "return type"
+ },
+ callable
+ ),
+ );
+ err.span_help(
+ multi_span,
+ format!(
+ "the {} `{}` due to the type of the argument passed",
+ match kind {
+ CallableKind::Function => "return type of this call is",
+ CallableKind::Method => "return type of this call is",
+ CallableKind::Constructor => "type constructed contains",
+ },
+ checked_ty
+ ),
+ );
+ }
+ };
+ match parent_expr.kind {
+ hir::ExprKind::Call(fun, args) => {
+ let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = fun.kind else { return; };
+ let hir::def::Res::Def(kind, def_id) = path.res else { return; };
+ let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
+ CallableKind::Constructor
+ } else {
+ CallableKind::Function
+ };
+ maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind);
+ }
+ hir::ExprKind::MethodCall(method, _receiver, args, _span) => {
+ let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) else { return; };
+ maybe_emit_help(def_id, method.ident, args, CallableKind::Method)
+ }
+ _ => return,
+ }
+ }
}
}
hir::BorrowKind::Ref => {
// Note: at this point, we cannot say what the best lifetime
- // is to use for resulting pointer. We want to use the
+ // is to use for resulting pointer. We want to use the
// shortest lifetime possible so as to avoid spurious borrowck
- // errors. Moreover, the longest lifetime will depend on the
+ // errors. Moreover, the longest lifetime will depend on the
// precise details of the value whose address is being taken
// (and how long it is valid), which we don't know yet until
// type inference is complete.
if let ty::FnDef(did, ..) = *ty.kind() {
let fn_sig = ty.fn_sig(tcx);
- if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute {
+ if tcx.fn_sig(did).skip_binder().abi() == RustIntrinsic
+ && tcx.item_name(did) == sym::transmute
+ {
let from = fn_sig.inputs().skip_binder()[0];
let to = fn_sig.output().skip_binder();
// We defer the transmute to the end of typeck, once all inference vars have
}
} else {
// If `ctxt.coerce` is `None`, we can just ignore
- // the type of the expression. This is because
+ // the type of the expression. This is because
// either this was a break *without* a value, in
// which case it is always a legal type (`()`), or
// else an error would have been flagged by the
// Point any obligations that were registered due to opaque type
// inference at the return expression.
self.select_obligations_where_possible(|errors| {
- self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty);
+ self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty, return_expr.span);
});
}
}
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
span: Span,
return_expr_ty: Ty<'tcx>,
+ return_span: Span,
) {
// Don't point at the whole block if it's empty
- if span == self.tcx.hir().span(self.body_id) {
+ if span == return_span {
return;
}
for err in errors {
let body = self.tcx.hir().body(anon_const.body);
// Create a new function context.
- let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id);
+ let def_id = anon_const.def_id;
+ let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id);
crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
let ty = fcx.check_expr_with_expectation(&body.value, expected);
variant: &'tcx ty::VariantDef,
access_span: Span,
) -> Vec<Symbol> {
+ let body_owner_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
variant
.fields
.iter()
.filter(|field| {
let def_scope = self
.tcx
- .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id)
+ .adjust_ident_and_get_scope(
+ field.ident(self.tcx),
+ variant.def_id,
+ body_owner_hir_id,
+ )
.1;
field.vis.is_accessible_from(def_scope, self.tcx)
&& !matches!(
match deref_base_ty.kind() {
ty::Adt(base_def, substs) if !base_def.is_enum() => {
debug!("struct named {:?}", deref_base_ty);
+ let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
- self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
+ self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id);
let fields = &base_def.non_enum_variant().fields;
if let Some(index) = fields
.iter()
}
fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) {
- let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
+ let generics = self.tcx.generics_of(self.body_id);
let generic_param = generics.type_param(¶m, self.tcx);
if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
return;
// Named constants have to be equated with the value
// being matched, so that's a read of the value being matched.
//
- // FIXME: We don't actually reads for ZSTs.
+ // FIXME: We don't actually reads for ZSTs.
needs_to_be_read = true;
}
_ => {
// We now see if we can make progress. This might cause us to
// unify inference variables for opaque types, since we may
// have unified some other type variables during the first
- // phase of fallback. This means that we only replace
+ // phase of fallback. This means that we only replace
// inference variables with their underlying opaque types as a
// last resort.
//
// (and the setting of `#![feature(never_type_fallback)]`).
//
// Fallback becomes very dubious if we have encountered
- // type-checking errors. In that case, fallback to Error.
+ // type-checking errors. In that case, fallback to Error.
//
// Sets `FnCtxt::fallback_has_occurred` if fallback is performed
// during this call.
/// constrained to have some other type).
///
/// However, the fallback used to be `()` (before the `!` type was
- /// added). Moreover, there are cases where the `!` type 'leaks
+ /// added). Moreover, there are cases where the `!` type 'leaks
/// out' from dead code into type variables that affect live
/// code. The most common case is something like this:
///
/// ```
///
/// Here, coercing the type `!` into `?M` will create a diverging
- /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
+ /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
/// ?M`. If `?M` winds up unconstrained, then `?X` will
/// fallback. If it falls back to `!`, then all the type variables
/// will wind up equal to `!` -- this includes the type `?D`
///
/// The algorithm we use:
/// * Identify all variables that are coerced *into* by a
- /// diverging variable. Do this by iterating over each
+ /// diverging variable. Do this by iterating over each
/// diverging, unsolved variable and finding all variables
/// reachable from there. Call that set `D`.
/// * Walk over all unsolved, non-diverging variables, and find
) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> {
debug!("calculate_diverging_fallback({:?})", unsolved_variables);
- let relationships = self.fulfillment_cx.borrow_mut().relationships().clone();
-
// Construct a coercion graph where an edge `A -> B` indicates
// a type variable is that is coerced
let coercion_graph = self.create_coercion_graph();
roots_reachable_from_non_diverging,
);
- debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations());
debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
- debug!("relationships: {:#?}", relationships);
// For each diverging variable, figure out whether it can
// reach a member of N. If so, it falls back to `()`. Else
.depth_first_search(root_vid)
.any(|n| roots_reachable_from_non_diverging.visited(n));
- let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false };
+ let mut found_infer_var_info = ty::InferVarInfo { self_in_trait: false, output: false };
- for (vid, rel) in relationships.iter() {
- if self.root_var(*vid) == root_vid {
- relationship.self_in_trait |= rel.self_in_trait;
- relationship.output |= rel.output;
+ for (vid, info) in self.inh.infer_var_info.borrow().iter() {
+ if self.infcx.root_var(*vid) == root_vid {
+ found_infer_var_info.self_in_trait |= info.self_in_trait;
+ found_infer_var_info.output |= info.output;
}
}
- if relationship.self_in_trait && relationship.output {
+ if found_infer_var_info.self_in_trait && found_infer_var_info.output {
// This case falls back to () to ensure that the code pattern in
// tests/ui/never_type/fallback-closure-ret.rs continues to
// compile when never_type_fallback is enabled.
}
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
+ if self.tcx.sess.opts.unstable_opts.drop_tracking_mir {
+ self.save_generator_interior_predicates(def_id);
+ return;
+ }
+
+ self.select_obligations_where_possible(|_| {});
+
let mut generators = self.deferred_generator_interiors.borrow_mut();
- for (body_id, interior, kind) in generators.drain(..) {
- self.select_obligations_where_possible(|_| {});
+ for (_, body_id, interior, kind) in generators.drain(..) {
crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
+ self.select_obligations_where_possible(|_| {});
+ }
+ }
+
+ /// Unify the inference variables corresponding to generator witnesses, and save all the
+ /// predicates that were stalled on those inference variables.
+ ///
+ /// This process allows to conservatively save all predicates that do depend on the generator
+ /// interior types, for later processing by `check_generator_obligations`.
+ ///
+ /// We must not attempt to select obligations after this method has run, or risk query cycle
+ /// ICE.
+ #[instrument(level = "debug", skip(self))]
+ fn save_generator_interior_predicates(&self, def_id: DefId) {
+ // Try selecting all obligations that are not blocked on inference variables.
+ // Once we start unifying generator witnesses, trying to select obligations on them will
+ // trigger query cycle ICEs, as doing so requires MIR.
+ self.select_obligations_where_possible(|_| {});
+
+ let generators = std::mem::take(&mut *self.deferred_generator_interiors.borrow_mut());
+ debug!(?generators);
+
+ for &(expr_def_id, body_id, interior, _) in generators.iter() {
+ debug!(?expr_def_id);
+
+ // Create the `GeneratorWitness` type that we will unify with `interior`.
+ let substs = ty::InternalSubsts::identity_for_item(
+ self.tcx,
+ self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
+ );
+ let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs);
+
+ // Unify `interior` with `witness` and collect all the resulting obligations.
+ let span = self.tcx.hir().body(body_id).value.span;
+ let ok = self
+ .at(&self.misc(span), self.param_env)
+ .eq(interior, witness)
+ .expect("Failed to unify generator interior type");
+ let mut obligations = ok.obligations;
+
+ // Also collect the obligations that were unstalled by this unification.
+ obligations
+ .extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx));
+
+ let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)).collect();
+ debug!(?obligations);
+ self.typeck_results
+ .borrow_mut()
+ .generator_interior_predicates
+ .insert(expr_def_id, obligations);
}
}
#[instrument(skip(self), level = "debug")]
- pub(in super::super) fn select_all_obligations_or_error(&self) {
- let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self);
+ pub(in super::super) fn report_ambiguity_errors(&self) {
+ let mut errors = self.fulfillment_cx.borrow_mut().collect_remaining_errors();
if !errors.is_empty() {
self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
}
}
- pub(in super::super) fn note_need_for_fn_pointer(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
- (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
- if sig1 != sig2 {
- return;
- }
- err.note(
- "different `fn` items always have unique types, even if their signatures are \
- the same",
- );
- (sig1, *did1, substs1)
- }
- (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
- if sig1 != *sig2 {
- return;
- }
- (sig1, *did, substs)
- }
- _ => return,
- };
- err.help(&format!("change the expected type to be function pointer `{}`", sig));
- err.help(&format!(
- "if the expected type is due to type inference, cast the expected `fn` to a function \
- pointer: `{} as {}`",
- self.tcx.def_path_str_with_substs(did, substs),
- sig
- ));
- }
-
// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
#[instrument(skip(self, span), level = "debug")]
}
GenericParamDefKind::Const { has_default } => {
if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
+ tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
self.fcx.var_for_def(self.span, param)
}
}
};
InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty)
- .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id));
+ .check_asm(asm, enclosing_id);
}
}
kind: TypeVariableOriginKind::MiscVariable,
span: full_call_span,
});
- self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty);
+ self.point_at_expr_source_of_inferred_type(
+ &mut err,
+ rcvr,
+ expected,
+ callee_ty,
+ provided_arg_span,
+ );
}
// Call out where the function is defined
self.label_fn_like(
match *callee_ty.kind() {
ty::Param(param) => {
let param =
- self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx);
+ self.tcx.generics_of(self.body_id).type_param(¶m, self.tcx);
if param.kind.is_synthetic() {
// if it's `impl Fn() -> ..` then just fall down to the def-id based logic
def_id = param.def_id;
// and point at that.
let instantiated = self
.tcx
- .explicit_predicates_of(self.body_id.owner)
+ .explicit_predicates_of(self.body_id)
.instantiate_identity(self.tcx);
// FIXME(compiler-errors): This could be problematic if something has two
// fn-like predicates with different args, but callable types really never
// do that, so it's OK.
- for (predicate, span) in
- std::iter::zip(instantiated.predicates, instantiated.spans)
+ for (predicate, span) in instantiated
{
if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
&& pred.self_ty().peel_refs() == callee_ty
use crate::coercion::DynamicCoerceMany;
use crate::{Diverges, EnclosingBreakables, Inherited};
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer;
use rustc_infer::infer::error_reporting::TypeErrCtxt;
/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt
/// [`InferCtxt`]: infer::InferCtxt
pub struct FnCtxt<'a, 'tcx> {
- pub(super) body_id: hir::HirId,
+ pub(super) body_id: LocalDefId,
/// The parameter environment used for proving trait obligations
/// in this function. This can change when we descend into
pub fn new(
inh: &'a Inherited<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
) -> FnCtxt<'a, 'tcx> {
FnCtxt {
body_id,
}
fn item_def_id(&self) -> DefId {
- self.body_id.owner.to_def_id()
+ self.body_id.to_def_id()
}
fn get_type_parameter_bounds(
let ty = if !ty.has_escaping_bound_vars() { self.normalize(span, ty) } else { ty };
self.write_ty(hir_id, ty)
}
+
+ fn infcx(&self) -> Option<&infer::InferCtxt<'tcx>> {
+ Some(&self.infcx)
+ }
}
/// Represents a user-provided type in the raw form (never normalized).
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
-use rustc_infer::infer;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::NormalizeExt;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
self.typeck_results
.borrow()
.liberated_fn_sigs()
- .get(self.tcx.hir().parent_id(self.body_id))
+ .get(self.tcx.hir().local_def_id_to_hir_id(self.body_id))
.copied()
}
found: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
) -> bool {
- let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found)
else { return false; };
if can_satisfy(output) {
let (sugg_call, mut applicability) = match inputs.len() {
/// because the callable type must also be well-formed to be called.
pub(in super::super) fn extract_callable_info(
&self,
- expr: &Expr<'_>,
- found: Ty<'tcx>,
+ ty: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
- // Autoderef is useful here because sometimes we box callables, etc.
- let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
- match *found.kind() {
- ty::FnPtr(fn_sig) =>
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
- }
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
- && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Param(param) => {
- let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id;
- self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- _ => None,
- }
- }) else { return None; };
-
- let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
- let inputs = inputs
- .skip_binder()
- .iter()
- .map(|ty| {
- self.replace_bound_vars_with_fresh_vars(
- expr.span,
- infer::FnCall,
- inputs.rebind(*ty),
- )
- })
- .collect();
-
- // We don't want to register any extra obligations, which should be
- // implied by wf, but also because that would possibly result in
- // erroneous errors later on.
- let infer::InferOk { value: output, obligations: _ } =
- self.at(&self.misc(expr.span), self.param_env).normalize(output);
-
- if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ self.err_ctxt().extract_callable_info(body_hir_id, self.param_env, ty)
}
pub fn suggest_two_fn_call(
rhs_ty: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
) -> bool {
- let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
+ let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty)
else { return false; };
- let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+ let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty)
else { return false; };
if can_satisfy(lhs_output_ty, rhs_output_ty) {
&& method_call_list.contains(&conversion_method.name)
// If receiver is `.clone()` and found type has one of those methods,
// we guess that the user wants to convert from a slice type (`&[]` or `&str`)
- // to an owned type (`Vec` or `String`). These conversions clone internally,
+ // to an owned type (`Vec` or `String`). These conversions clone internally,
// so we remove the user's `clone` call.
{
vec![(
}
}
ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
- // Check if the parent expression is a call to Pin::new. If it
+ // Check if the parent expression is a call to Pin::new. If it
// is and we were expecting a Box, ergo Pin<Box<expected>>, we
// can suggest Box::pin.
let parent = self.tcx.hir().parent_id(expr.hir_id);
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::Ty;
use rustc_middle::ty::UserType;
+use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_trait_selection::traits;
_: &'tcx hir::FnDecl<'tcx>,
_: hir::BodyId,
_: Span,
- _: hir::HirId,
+ _: LocalDefId,
) {
}
}
let mut reinit = None;
match expr.kind {
ExprKind::Assign(lhs, rhs, _) => {
- self.visit_expr(lhs);
self.visit_expr(rhs);
+ self.visit_expr(lhs);
reinit = Some(lhs);
}
self.drop_ranges.add_control_edge(self.expr_index, *target)
}),
- ExprKind::Break(destination, ..) => {
+ ExprKind::Break(destination, value) => {
// destination either points to an expression or to a block. We use
// find_target_expression_from_destination to use the last expression of the block
// if destination points to a block.
// will refer to the end of the block due to the post order traversal.
self.find_target_expression_from_destination(destination).map_or((), |target| {
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
- })
+ });
+
+ if let Some(value) = value {
+ self.visit_expr(value);
+ }
}
ExprKind::Call(f, args) => {
ExprKind::AddrOf(..)
| ExprKind::Array(..)
+ // FIXME(eholk): We probably need special handling for AssignOps. The ScopeTree builder
+ // in region.rs runs both lhs then rhs and rhs then lhs and then sets all yields to be
+ // the latest they show up in either traversal. With the older scope-based
+ // approximation, this was fine, but it's probably not right now. What we probably want
+ // to do instead is still run both orders, but consider anything that showed up as a
+ // yield in either order.
| ExprKind::AssignOp(..)
| ExprKind::Binary(..)
| ExprKind::Block(..)
// Increment expr_count here to match what InteriorVisitor expects.
self.expr_index = self.expr_index + 1;
+
+ // Save a node mapping to get better CFG visualization
+ self.drop_ranges.add_node_mapping(pat.hir_id, self.expr_index);
}
}
}
});
}
- debug!("hir_id_map: {:?}", tracked_value_map);
+ debug!("hir_id_map: {:#?}", tracked_value_map);
let num_values = tracked_value_map.len();
Self {
tracked_value_map,
//! flow graph when needed for debugging.
use rustc_graphviz as dot;
+use rustc_hir::{Expr, ExprKind, Node};
use rustc_middle::ty::TyCtxt;
use super::{DropRangesBuilder, PostOrderId};
.post_order_map
.iter()
.find(|(_hir_id, &post_order_id)| post_order_id == *n)
- .map_or("<unknown>".into(), |(hir_id, _)| self
- .tcx
- .hir()
- .node_to_string(*hir_id))
+ .map_or("<unknown>".into(), |(hir_id, _)| format!(
+ "{}{}",
+ self.tcx.hir().node_to_string(*hir_id),
+ match self.tcx.hir().find(*hir_id) {
+ Some(Node::Expr(Expr { kind: ExprKind::Yield(..), .. })) => " (yield)",
+ _ => "",
+ }
+ ))
)
.into(),
)
// where the `identity(...)` (the rvalue) produces a return type
// of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
// `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
- // `a` will be considered borrowed). Other parts of the code will ensure
+ // `a` will be considered borrowed). Other parts of the code will ensure
// that if `y` is live over a yield, `&'y mut A` appears in the generator
// state. If `'y` is live, then any sound region analysis must conclude
// that `'a` is also live. So if this causes a bug, blame some other
yield_data.expr_and_pat_count, self.expr_count, source_span
);
- if self.fcx.sess().opts.unstable_opts.drop_tracking
- && self
- .drop_ranges
- .is_dropped_at(hir_id, yield_data.expr_and_pat_count)
+ if self
+ .is_dropped_at_yield_location(hir_id, yield_data.expr_and_pat_count)
{
debug!("value is dropped at yield point; not recording");
return false;
}
}
}
+
+ /// If drop tracking is enabled, consult drop_ranges to see if a value is
+ /// known to be dropped at a yield point and therefore can be omitted from
+ /// the generator witness.
+ fn is_dropped_at_yield_location(&self, value_hir_id: HirId, yield_location: usize) -> bool {
+ // short-circuit if drop tracking is not enabled.
+ if !self.fcx.sess().opts.unstable_opts.drop_tracking {
+ return false;
+ }
+
+ self.drop_ranges.is_dropped_at(value_hir_id, yield_location)
+ }
}
pub fn resolve_interior<'a, 'tcx>(
use super::callee::DeferredCallResolution;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefIdMap;
use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _};
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _};
use std::cell::RefCell;
use std::ops::Deref;
pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>,
pub(super) deferred_generator_interiors:
- RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
+ RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
pub(super) body_id: Option<hir::BodyId>,
/// we record that type variable here. This is later used to inform
/// fallback. See the `fallback` module for details.
pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>,
+
+ pub(super) infer_var_info: RefCell<FxHashMap<ty::TyVid, ty::InferVarInfo>>,
}
impl<'tcx> Deref for Inherited<'tcx> {
deferred_generator_interiors: RefCell::new(Vec::new()),
diverging_type_vars: RefCell::new(Default::default()),
body_id,
+ infer_var_info: RefCell::new(Default::default()),
}
}
if obligation.has_escaping_bound_vars() {
span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation);
}
+
+ self.update_infer_var_info(&obligation);
+
self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation);
}
self.register_predicates(infer_ok.obligations);
infer_ok.value
}
+
+ pub fn update_infer_var_info(&self, obligation: &PredicateObligation<'tcx>) {
+ let infer_var_info = &mut self.infer_var_info.borrow_mut();
+
+ // (*) binder skipped
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder()
+ && let Some(ty) = self.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| self.root_var(t))
+ && self.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id)
+ {
+ let new_self_ty = self.tcx.types.unit;
+
+ // Then construct a new obligation with Self = () added
+ // to the ParamEnv, and see if it holds.
+ let o = obligation.with(self.tcx,
+ obligation
+ .predicate
+ .kind()
+ .rebind(
+ // (*) binder moved here
+ ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_ty(self.tcx, new_self_ty)))
+ ),
+ );
+ // Don't report overflow errors. Otherwise equivalent to may_hold.
+ if let Ok(result) = self.probe(|_| self.evaluate_obligation(&o)) && result.may_apply() {
+ infer_var_info.entry(ty).or_default().self_in_trait = true;
+ }
+ }
+
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) =
+ obligation.predicate.kind().skip_binder()
+ {
+ // If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
+ // we need to make it into one.
+ if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) {
+ debug!("infer_var_info: {:?}.output = true", vid);
+ infer_var_info.entry(vid).or_default().output = true;
+ }
+ }
+ }
}
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub fn check_transmute(&self, from: Ty<'tcx>, to: Ty<'tcx>, hir_id: HirId) {
let tcx = self.tcx;
+ let dl = &tcx.data_layout;
let span = tcx.hir().span(hir_id);
let normalize = |ty| {
let ty = self.resolve_vars_if_possible(ty);
// Special-case transmuting from `typeof(function)` and
// `Option<typeof(function)>` to present a clearer error.
let from = unpack_option_like(tcx, from);
- if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&tcx) {
+ if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer(dl.instruction_address_space).size(&tcx) {
struct_span_err!(tcx.sess, span, E0591, "can't transmute zero-sized type")
.note(&format!("source type: {from}"))
.note(&format!("target type: {to}"))
} else {
err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
.note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
+ let mut should_delay_as_bug = false;
+ if let Err(LayoutError::Unknown(bad_from)) = sk_from && bad_from.references_error() {
+ should_delay_as_bug = true;
+ }
+ if let Err(LayoutError::Unknown(bad_to)) = sk_to && bad_to.references_error() {
+ should_delay_as_bug = true;
+ }
+ if should_delay_as_bug {
+ err.delay_as_bug();
+ }
}
err.emit();
}
let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
let param_env = tcx.param_env(def_id);
- let mut fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+ let mut fcx = FnCtxt::new(&inh, param_env, def_id);
if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
} else {
- tcx.fn_sig(def_id)
+ tcx.fn_sig(def_id).subst_identity()
};
check_abi(tcx, id, span, fn_sig.abi());
// Before the generator analysis, temporary scopes shall be marked to provide more
// precise information on types to be captured.
fcx.resolve_rvalue_scopes(def_id.to_def_id());
- fcx.resolve_generator_interiors(def_id.to_def_id());
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
let ty = fcx.normalize(span, ty);
fcx.require_type_is_sized(ty, span, code);
}
- fcx.select_all_obligations_or_error();
+ fcx.select_obligations_where_possible(|_| {});
+
+ debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
+
+ // This must be the last thing before `report_ambiguity_errors`.
+ fcx.resolve_generator_interiors(def_id.to_def_id());
+
+ debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
+
+ if let None = fcx.infcx.tainted_by_errors() {
+ fcx.report_ambiguity_errors();
+ }
if let None = fcx.infcx.tainted_by_errors() {
fcx.check_transmutes();
}
PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => {
- // box p1, &p1, &mut p1. we can ignore the mutability of
+ // box p1, &p1, &mut p1. we can ignore the mutability of
// PatKind::Ref since that information is already contained
// in the type.
let subplace = self.cat_deref(pat, place_with_id)?;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits;
-use std::iter;
use std::ops::Deref;
struct ConfirmContext<'a, 'tcx> {
let filler_substs = rcvr_substs
.extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def));
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
- &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
+ self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
);
// Unify the (adjusted) self type with what the method expects.
debug!("method_predicates after subst = {:?}", method_predicates);
- let sig = self.tcx.bound_fn_sig(def_id);
-
- let sig = sig.subst(self.tcx, all_substs);
+ let sig = self.tcx.fn_sig(def_id).subst(self.tcx, all_substs);
debug!("type scheme substituted, sig={:?}", sig);
let sig = self.replace_bound_vars_with_fresh_vars(sig);
fn predicates_require_illegal_sized_bound(
&self,
- predicates: &ty::InstantiatedPredicates<'tcx>,
+ predicates: ty::InstantiatedPredicates<'tcx>,
) -> Option<Span> {
let sized_def_id = self.tcx.lang_items().sized_trait()?;
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
if trait_pred.def_id() == sized_def_id =>
{
- let span = iter::zip(&predicates.predicates, &predicates.spans)
+ let span = predicates
+ .iter()
.find_map(
|(p, span)| {
- if *p == obligation.predicate { Some(*span) } else { None }
+ if p == obligation.predicate { Some(span) } else { None }
},
)
.unwrap_or(rustc_span::DUMMY_SP);
pub unsatisfied_predicates:
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
pub out_of_scope_traits: Vec<DefId>,
- pub lev_candidate: Option<ty::AssocItem>,
+ pub similar_candidate: Option<ty::AssocItem>,
pub mode: probe::Mode,
}
)
.map(|pick| {
let sig = self.tcx.fn_sig(pick.item.def_id);
- sig.inputs().skip_binder().len().saturating_sub(1)
+ sig.skip_binder().inputs().skip_binder().len().saturating_sub(1)
})
.unwrap_or(0);
// N.B., instantiate late-bound regions before normalizing the
// function signature so that normalization does not need to deal
// with bound regions.
- let fn_sig = tcx.bound_fn_sig(def_id);
- let fn_sig = fn_sig.subst(self.tcx, substs);
+ let fn_sig = tcx.fn_sig(def_id).subst(self.tcx, substs);
let fn_sig =
self.replace_bound_vars_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig);
// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
- // so this also effectively registers `obligation` as well. (We
+ // so this also effectively registers `obligation` as well. (We
// used to register `obligation` explicitly, but that resulted in
// double error messages being reported.)
//
pub enum Mode {
// An expression of the form `receiver.method_name(...)`.
// Autoderefs are performed on `receiver`, lookup is done based on the
- // `self` argument of the method, and static methods aren't considered.
+ // `self` argument of the method, and static methods aren't considered.
MethodCall,
// An expression of the form `Type::item` or `<T>::item`.
// No autoderefs are performed, lookup is done based on the type each
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
out_of_scope_traits: Vec::new(),
- lev_candidate: None,
+ similar_candidate: None,
mode,
}));
}
probe_cx.assemble_inherent_candidates();
match scope {
ProbeScope::TraitsInScope => {
- probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)
+ probe_cx.assemble_extension_candidates_for_traits_in_scope()
}
ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
};
let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
let ParamEnvAnd { param_env, value: self_ty } = goal;
- let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty)
- .include_raw_pointers()
- .silence_errors();
+ let mut autoderef =
+ Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
+ .include_raw_pointers()
+ .silence_errors();
let mut reached_raw_pointer = false;
let mut steps: Vec<_> = autoderef
.by_ref()
fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) {
let is_accessible = if let Some(name) = self.method_name {
let item = candidate.item;
- let def_scope = self
- .tcx
- .adjust_ident_and_get_scope(name, item.container_id(self.tcx), self.body_id)
- .1;
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ let def_scope =
+ self.tcx.adjust_ident_and_get_scope(name, item.container_id(self.tcx), hir_id).1;
item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx)
} else {
true
}
}
- fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) {
+ fn assemble_extension_candidates_for_traits_in_scope(&mut self) {
let mut duplicates = FxHashSet::default();
- let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id);
+ let opt_applicable_traits = self.tcx.in_scope_traits(self.scope_expr_id);
if let Some(applicable_traits) = opt_applicable_traits {
for trait_candidate in applicable_traits.iter() {
let trait_did = trait_candidate.def_id;
expected: Ty<'tcx>,
) -> bool {
match method.kind {
- ty::AssocKind::Fn => {
- let fty = self.tcx.bound_fn_sig(method.def_id);
- self.probe(|_| {
- let substs = self.fresh_substs_for_item(self.span, method.def_id);
- let fty = fty.subst(self.tcx, substs);
- let fty =
- self.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, fty);
-
- if let Some(self_ty) = self_ty {
- if self
- .at(&ObligationCause::dummy(), self.param_env)
- .sup(fty.inputs()[0], self_ty)
- .is_err()
- {
- return false;
- }
+ ty::AssocKind::Fn => self.probe(|_| {
+ let substs = self.fresh_substs_for_item(self.span, method.def_id);
+ let fty = self.tcx.fn_sig(method.def_id).subst(self.tcx, substs);
+ let fty = self.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, fty);
+
+ if let Some(self_ty) = self_ty {
+ if self
+ .at(&ObligationCause::dummy(), self.param_env)
+ .sup(fty.inputs()[0], self_ty)
+ .is_err()
+ {
+ return false;
}
- self.can_sub(self.param_env, fty.output(), expected).is_ok()
- })
- }
+ }
+ self.can_sub(self.param_env, fty.output(), expected).is_ok()
+ }),
_ => false,
}
}
if let Some((kind, def_id)) = private_candidate {
return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits));
}
- let lev_candidate = self.probe_for_lev_candidate()?;
+ let similar_candidate = self.probe_for_similar_candidate()?;
Err(MethodError::NoMatch(NoMatchData {
static_candidates,
unsatisfied_predicates,
out_of_scope_traits,
- lev_candidate,
+ similar_candidate,
mode: self.mode,
}))
}
let o = self.resolve_vars_if_possible(o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
- possibly_unsatisfied_predicates.push((
- o.predicate,
- None,
- Some(o.cause),
- ));
+ let parent_o = o.clone();
+ let implied_obligations =
+ traits::elaborate_obligations(self.tcx, vec![o]);
+ for o in implied_obligations {
+ let parent = if o == parent_o {
+ None
+ } else {
+ if o.predicate.to_opt_poly_trait_pred().map(|p| p.def_id())
+ == self.tcx.lang_items().sized_trait()
+ {
+ // We don't care to talk about implicit `Sized` bounds.
+ continue;
+ }
+ Some(parent_o.predicate)
+ };
+ if !self.predicate_may_hold(&o) {
+ possibly_unsatisfied_predicates.push((
+ o.predicate,
+ parent,
+ Some(o.cause),
+ ));
+ }
+ }
}
}
}
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
/// candidate method where the method name may have been misspelled. Similarly to other
/// Levenshtein based suggestions, we provide at most one such suggestion.
- fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
+ fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
debug!("probing for method names similar to {:?}", self.method_name);
let steps = self.steps.clone();
None,
)
}
+ .or_else(|| {
+ applicable_close_candidates
+ .iter()
+ .find(|cand| self.matches_by_doc_alias(cand.def_id))
+ .map(|cand| cand.name)
+ })
.unwrap();
Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name))
}
#[instrument(level = "debug", skip(self))]
fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> {
- let fn_sig = self.tcx.bound_fn_sig(method);
+ let fn_sig = self.tcx.fn_sig(method);
debug!(?fn_sig);
assert!(!substs.has_escaping_bound_vars());
}
}
+ /// Determine if the associated item withe the given DefId matches
+ /// the desired name via a doc alias.
+ fn matches_by_doc_alias(&self, def_id: DefId) -> bool {
+ let Some(name) = self.method_name else { return false; };
+ let Some(local_def_id) = def_id.as_local() else { return false; };
+ let hir_id = self.fcx.tcx.hir().local_def_id_to_hir_id(local_def_id);
+ let attrs = self.fcx.tcx.hir().attrs(hir_id);
+ for attr in attrs {
+ let sym::doc = attr.name_or_empty() else { continue; };
+ let Some(values) = attr.meta_item_list() else { continue; };
+ for v in values {
+ if v.name_or_empty() != sym::alias {
+ continue;
+ }
+ if let Some(nested) = v.meta_item_list() {
+ // #[doc(alias("foo", "bar"))]
+ for n in nested {
+ if let Some(lit) = n.lit() && name.as_str() == lit.symbol.as_str() {
+ return true;
+ }
+ }
+ } else if let Some(meta) = v.meta_item()
+ && let Some(lit) = meta.name_value_literal()
+ && name.as_str() == lit.symbol.as_str() {
+ // #[doc(alias = "foo")]
+ return true;
+ }
+ }
+ }
+ false
+ }
+
/// Finds the method with the appropriate name (or return type, as the case may be). If
/// `allow_similar_names` is set, find methods with close-matching names.
// The length of the returned iterator is nearly always 0 or 1 and this
if !self.is_relevant_kind_for_mode(x.kind) {
return false;
}
+ if self.matches_by_doc_alias(x.def_id) {
+ return true;
+ }
match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
{
Some(d) => d > 0,
let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty));
let is_method = mode == Mode::MethodCall;
let unsatisfied_predicates = &no_match_data.unsatisfied_predicates;
- let lev_candidate = no_match_data.lev_candidate;
+ let similar_candidate = no_match_data.similar_candidate;
let item_kind = if is_method {
"method"
} else if rcvr_ty.is_enum() {
let ty_span = match rcvr_ty.kind() {
ty::Param(param_type) => {
- Some(param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()))
+ Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id()))
}
ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())),
_ => None,
args,
sugg_span,
);
-
self.note_candidates_on_method_error(
rcvr_ty,
item_name,
ty::Param(_) => {
// Account for `fn` items like in `issue-35677.rs` to
// suggest restricting its type params.
- let parent_body =
- hir.body_owner(hir::BodyId { hir_id: self.body_id });
- Some(hir.get(parent_body))
+ Some(hir.get_by_def_id(self.body_id))
}
ty::Adt(def, _) => {
def.did().as_local().map(|def_id| hir.get_by_def_id(def_id))
}
_ => None,
};
- if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
- if let Some(g) = kind.generics() {
- let key = (
- g.tail_span_for_predicate_suggestion(),
- g.add_where_or_trailing_comma(),
- );
- type_params
- .entry(key)
- .or_insert_with(FxHashSet::default)
- .insert(obligation.to_owned());
- }
+ if let Some(hir::Node::Item(hir::Item { kind, .. })) = node
+ && let Some(g) = kind.generics()
+ {
+ let key = (
+ g.tail_span_for_predicate_suggestion(),
+ g.add_where_or_trailing_comma(),
+ );
+ type_params
+ .entry(key)
+ .or_insert_with(FxHashSet::default)
+ .insert(obligation.to_owned());
+ return true;
}
}
+ false
};
let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
let msg = format!(
"auto trait is invoked with no method error, but no error reported?",
);
}
- Some(_) => unreachable!(),
+ Some(Node::Item(hir::Item {
+ ident, kind: hir::ItemKind::Trait(..), ..
+ })) => {
+ skip_list.insert(p);
+ let entry = spanned_predicates.entry(ident.span);
+ let entry = entry.or_insert_with(|| {
+ (FxHashSet::default(), FxHashSet::default(), Vec::new())
+ });
+ entry.0.insert(cause.span);
+ entry.1.insert((ident.span, ""));
+ entry.1.insert((cause.span, "unsatisfied trait bound introduced here"));
+ entry.2.push(p);
+ }
+ Some(node) => unreachable!("encountered `{node:?}`"),
None => (),
}
}
unsatisfied_bounds = true;
}
+ let mut suggested_bounds = FxHashSet::default();
// The requirements that didn't have an `impl` span to show.
let mut bound_list = unsatisfied_predicates
.iter()
.filter_map(|(pred, parent_pred, _cause)| {
+ let mut suggested = false;
format_pred(*pred).map(|(p, self_ty)| {
- collect_type_param_suggestions(self_ty, *pred, &p);
+ if let Some(parent) = parent_pred && suggested_bounds.contains(parent) {
+ // We don't suggest `PartialEq` when we already suggest `Eq`.
+ } else if !suggested_bounds.contains(pred) {
+ if collect_type_param_suggestions(self_ty, *pred, &p) {
+ suggested = true;
+ suggested_bounds.insert(pred);
+ }
+ }
(
match parent_pred {
None => format!("`{}`", &p),
Some(parent_pred) => match format_pred(*parent_pred) {
None => format!("`{}`", &p),
Some((parent_p, _)) => {
- collect_type_param_suggestions(self_ty, *parent_pred, &p);
+ if !suggested
+ && !suggested_bounds.contains(pred)
+ && !suggested_bounds.contains(parent_pred)
+ {
+ if collect_type_param_suggestions(
+ self_ty,
+ *parent_pred,
+ &p,
+ ) {
+ suggested_bounds.insert(pred);
+ }
+ }
format!("`{}`\nwhich is required by `{}`", p, parent_p)
}
},
// give a helping note that it has to be called as `(x.f)(...)`.
if let SelfSource::MethodCall(expr) = source {
if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err)
- && lev_candidate.is_none()
+ && similar_candidate.is_none()
&& !custom_span_label
{
label_span_not_found(&mut err);
if fallback_span {
err.span_label(span, msg);
}
- } else if let Some(lev_candidate) = lev_candidate {
+ } else if let Some(similar_candidate) = similar_candidate {
// Don't emit a suggestion if we found an actual method
// that had unsatisfied trait bounds
if unsatisfied_predicates.is_empty() {
- let def_kind = lev_candidate.kind.as_def_kind();
+ let def_kind = similar_candidate.kind.as_def_kind();
// Methods are defined within the context of a struct and their first parameter is always self,
// which represents the instance of the struct the method is being called on
// Associated functions don’t take self as a parameter and
// they are not methods because they don’t have an instance of the struct to work with.
- if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
+ if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter {
err.span_suggestion(
span,
"there is a method with a similar name",
- lev_candidate.name,
+ similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
&format!(
"there is {} {} with a similar name",
def_kind.article(),
- def_kind.descr(lev_candidate.def_id),
+ def_kind.descr(similar_candidate.def_id),
),
- lev_candidate.name,
+ similar_candidate.name,
Applicability::MaybeIncorrect,
);
}
// the impl, if local to crate (item may be defaulted), else nothing.
let Some(item) = self.associated_value(impl_did, item_name).or_else(|| {
let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
- self.associated_value(impl_trait_ref.def_id, item_name)
+ self.associated_value(impl_trait_ref.skip_binder().def_id, item_name)
}) else {
continue;
};
let insertion = match self.tcx.impl_trait_ref(impl_did) {
None => String::new(),
Some(trait_ref) => {
- format!(" of the trait `{}`", self.tcx.def_path_str(trait_ref.def_id))
+ format!(
+ " of the trait `{}`",
+ self.tcx.def_path_str(trait_ref.skip_binder().def_id)
+ )
}
};
}
if let Some(sugg_span) = sugg_span
&& let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
- let path = self.tcx.def_path_str(trait_ref.def_id);
+ let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id);
let ty = match item.kind {
ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty,
ty::AssocKind::Fn => self
.tcx
.fn_sig(item.def_id)
+ .subst_identity()
.inputs()
.skip_binder()
.get(0)
&& let Some(assoc) = self.associated_value(*impl_did, item_name)
&& assoc.kind == ty::AssocKind::Fn
{
- let sig = self.tcx.fn_sig(assoc.def_id);
+ let sig = self.tcx.fn_sig(assoc.def_id).subst_identity();
sig.inputs().skip_binder().get(0).and_then(|first| if first.peel_refs() == rcvr_ty.peel_refs() {
None
} else {
_ => None,
});
if let Some((field, field_ty)) = field_receiver {
- let scope = tcx.parent_module(self.body_id);
+ let scope = tcx.parent_module_from_def_id(self.body_id);
let is_accessible = field.vis.is_accessible_from(scope, tcx);
if is_accessible {
else { return };
let map = self.infcx.tcx.hir();
- let body = map.body(rustc_hir::BodyId { hir_id: self.body_id });
+ let body_id = self.tcx.hir().body_owned_by(self.body_id);
+ let body = map.body(body_id);
struct LetVisitor<'a> {
result: Option<&'a hir::Expr<'a>>,
ident_name: Symbol,
// just changing the path.
&& pick.item.fn_has_self_parameter
&& let Some(self_ty) =
- self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0)
+ self.tcx.fn_sig(pick.item.def_id).subst_identity().inputs().skip_binder().get(0)
&& self_ty.is_ref()
{
let suggested_path = match deref_ty.kind() {
true
});
- let module_did = self.tcx.parent_module(self.body_id);
+ let module_did = self.tcx.parent_module_from_def_id(self.body_id);
let (module, _, _) = self.tcx.hir().get_module(module_did);
let span = module.spans.inject_use_span;
// implement the `AsRef` trait.
let skip = skippable.contains(&did)
|| (("Pin::new" == *pre) && (sym::as_ref == item_name.name))
- || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().inputs().len() != inputs_len);
+ || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().skip_binder().inputs().len() != inputs_len);
// Make sure the method is defined for the *actual* receiver: we don't
// want to treat `Box<Self>` as a receiver if it only works because of
// an autoderef to `&self`
};
// Obtain the span for `param` and use it for a structured suggestion.
if let Some(param) = param_type {
- let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
+ let generics = self.tcx.generics_of(self.body_id.to_def_id());
let type_param = generics.type_param(param, self.tcx);
let hir = self.tcx.hir();
if let Some(def_id) = type_param.def_id.as_local() {
self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
})
.any(|imp_did| {
- let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
+ let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity();
let imp_simp =
simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder);
imp_simp.map_or(false, |s| s == simp_rcvr_ty)
found: Ty<'tcx>,
expected: Ty<'tcx>,
) -> bool {
- let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
- else { return false; };
+ let Some((_def_id_or_name, output, _inputs)) =
+ self.extract_callable_info(found) else {
+ return false;
+ };
if !self.can_coerce(output, expected) {
return false;
// check the method arguments number
if let Ok(pick) = probe &&
let fn_sig = self.tcx.fn_sig(pick.item.def_id) &&
- let fn_args = fn_sig.skip_binder().inputs() &&
+ let fn_args = fn_sig.skip_binder().skip_binder().inputs() &&
fn_args.len() == args.len() + 1 {
err.span_suggestion_verbose(
method_name.span.shrink_to_hi(),
&self,
body: &'tcx hir::Body<'tcx>,
) -> &'tcx ty::TypeckResults<'tcx> {
- let item_id = self.tcx.hir().body_owner(body.id());
- let item_def_id = self.tcx.hir().local_def_id(item_id);
+ let item_def_id = self.tcx.hir().body_owner_def_id(body.id());
// This attribute causes us to dump some writeback information
// in the form of errors, which is used for unit tests.
// Type only exists for constants and statics, not functions.
match self.tcx.hir().body_owner_kind(item_def_id) {
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => {
- wbcx.visit_node_id(body.value.span, item_id);
+ let item_hir_id = self.tcx.hir().local_def_id_to_hir_id(item_def_id);
+ wbcx.visit_node_id(body.value.span, item_hir_id);
}
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => (),
}
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() {
- let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id };
+ let fcx_closure_kind_origins =
+ fcx_typeck_results.closure_kind_origins().items_in_stable_order();
+
+ for (local_id, origin) in fcx_closure_kind_origins {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id };
let place_span = origin.0;
let place = self.resolve(origin.1.clone(), &place_span);
self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place));
fn visit_coercion_casts(&mut self) {
let fcx_typeck_results = self.fcx.typeck_results.borrow();
- let fcx_coercion_casts = fcx_typeck_results.coercion_casts();
+
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
+ let fcx_coercion_casts = fcx_typeck_results.coercion_casts().to_sorted_stable_ord();
for local_id in fcx_coercion_casts {
- self.typeck_results.set_coercion_cast(*local_id);
+ self.typeck_results.set_coercion_cast(local_id);
}
}
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- let mut errors_buffer = Vec::new();
- for (&local_id, c_ty) in fcx_typeck_results.user_provided_types().iter() {
- let hir_id = hir::HirId { owner: common_hir_owner, local_id };
-
- if cfg!(debug_assertions) && c_ty.needs_infer() {
- span_bug!(
- hir_id.to_span(self.fcx.tcx),
- "writeback: `{:?}` has inference variables",
- c_ty
- );
- };
+ if self.rustc_dump_user_substs {
+ let sorted_user_provided_types =
+ fcx_typeck_results.user_provided_types().items_in_stable_order();
- self.typeck_results.user_provided_types_mut().insert(hir_id, *c_ty);
+ let mut errors_buffer = Vec::new();
+ for (local_id, c_ty) in sorted_user_provided_types {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id };
- if let ty::UserType::TypeOf(_, user_substs) = c_ty.value {
- if self.rustc_dump_user_substs {
+ if let ty::UserType::TypeOf(_, user_substs) = c_ty.value {
// This is a unit-testing mechanism.
let span = self.tcx().hir().span(hir_id);
// We need to buffer the errors in order to guarantee a consistent
err.buffer(&mut errors_buffer);
}
}
- }
- if !errors_buffer.is_empty() {
- errors_buffer.sort_by_key(|diag| diag.span.primary_span());
- for mut diag in errors_buffer {
- self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
+ if !errors_buffer.is_empty() {
+ errors_buffer.sort_by_key(|diag| diag.span.primary_span());
+ for mut diag in errors_buffer {
+ self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
+ }
}
}
+
+ self.typeck_results.user_provided_types_mut().extend(
+ fcx_typeck_results.user_provided_types().items().map(|(local_id, c_ty)| {
+ let hir_id = hir::HirId { owner: common_hir_owner, local_id };
+
+ if cfg!(debug_assertions) && c_ty.needs_infer() {
+ span_bug!(
+ hir_id.to_span(self.fcx.tcx),
+ "writeback: `{:?}` has inference variables",
+ c_ty
+ );
+ };
+
+ (hir_id, *c_ty)
+ }),
+ );
}
fn visit_user_provided_sigs(&mut self) {
let fcx_typeck_results = self.fcx.typeck_results.borrow();
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
- for (&def_id, c_sig) in fcx_typeck_results.user_provided_sigs.iter() {
- if cfg!(debug_assertions) && c_sig.needs_infer() {
- span_bug!(
- self.fcx.tcx.def_span(def_id),
- "writeback: `{:?}` has inference variables",
- c_sig
- );
- };
-
- self.typeck_results.user_provided_sigs.insert(def_id, *c_sig);
- }
+ self.typeck_results.user_provided_sigs.extend(
+ fcx_typeck_results.user_provided_sigs.items().map(|(&def_id, c_sig)| {
+ if cfg!(debug_assertions) && c_sig.needs_infer() {
+ span_bug!(
+ self.fcx.tcx.def_span(def_id),
+ "writeback: `{:?}` has inference variables",
+ c_sig
+ );
+ };
+
+ (def_id, *c_sig)
+ }),
+ );
}
fn visit_generator_interior_types(&mut self) {
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
self.typeck_results.generator_interior_types =
fcx_typeck_results.generator_interior_types.clone();
+ for (&expr_def_id, predicates) in fcx_typeck_results.generator_interior_predicates.iter() {
+ let predicates = self.resolve(predicates.clone(), &self.fcx.tcx.def_span(expr_def_id));
+ self.typeck_results.generator_interior_predicates.insert(expr_def_id, predicates);
+ }
}
#[instrument(skip(self), level = "debug")]
opaque_type_key,
self.fcx.infcx.tcx,
true,
- decl.origin,
);
self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type);
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (&local_id, &fn_sig) in fcx_typeck_results.liberated_fn_sigs().iter() {
+ let fcx_liberated_fn_sigs = fcx_typeck_results.liberated_fn_sigs().items_in_stable_order();
+
+ for (local_id, &fn_sig) in fcx_liberated_fn_sigs {
let hir_id = hir::HirId { owner: common_hir_owner, local_id };
let fn_sig = self.resolve(fn_sig, &hir_id);
self.typeck_results.liberated_fn_sigs_mut().insert(hir_id, fn_sig);
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
let common_hir_owner = fcx_typeck_results.hir_owner;
- for (&local_id, ftys) in fcx_typeck_results.fru_field_types().iter() {
+ let fcx_fru_field_types = fcx_typeck_results.fru_field_types().items_in_stable_order();
+
+ for (local_id, ftys) in fcx_fru_field_types {
let hir_id = hir::HirId { owner: common_hir_owner, local_id };
let ftys = self.resolve(ftys.clone(), &hir_id);
self.typeck_results.fru_field_types_mut().insert(hir_id, ftys);
use rustc_data_structures::graph::implementation::{Direction, NodeIndex, INCOMING, OUTGOING};
use rustc_graphviz as dot;
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::intravisit::{self, Visitor};
use rustc_middle::dep_graph::{
DepGraphQuery, DepKind, DepNode, DepNodeExt, DepNodeFilter, EdgeFilter,
let (if_this_changed, then_this_would_need) = {
let mut visitor =
IfThisChanged { tcx, if_this_changed: vec![], then_this_would_need: vec![] };
- visitor.process_attrs(hir::CRATE_HIR_ID);
+ visitor.process_attrs(CRATE_DEF_ID);
tcx.hir().visit_all_item_likes_in_crate(&mut visitor);
(visitor.if_this_changed, visitor.then_this_would_need)
};
value
}
- fn process_attrs(&mut self, hir_id: hir::HirId) {
- let def_id = self.tcx.hir().local_def_id(hir_id);
+ fn process_attrs(&mut self, def_id: LocalDefId) {
let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id());
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let attrs = self.tcx.hir().attrs(hir_id);
for attr in attrs {
if attr.has_name(sym::rustc_if_this_changed) {
}
fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
- self.process_attrs(item.hir_id());
+ self.process_attrs(item.owner_id.def_id);
intravisit::walk_item(self, item);
}
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
- self.process_attrs(trait_item.hir_id());
+ self.process_attrs(trait_item.owner_id.def_id);
intravisit::walk_trait_item(self, trait_item);
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
- self.process_attrs(impl_item.hir_id());
+ self.process_attrs(impl_item.owner_id.def_id);
intravisit::walk_impl_item(self, impl_item);
}
fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
- self.process_attrs(s.hir_id);
+ self.process_attrs(s.def_id);
intravisit::walk_field_def(self, s);
}
}
) -> FxHashSet<DepKind> {
// This is a bit tricky. We want to include a node only if it is:
// (a) reachable from a source and (b) will reach a target. And we
- // have to be careful about cycles etc. Luckily efficiency is not
+ // have to be careful about cycles etc. Luckily efficiency is not
// a big concern!
#[derive(Copy, Clone, PartialEq)]
-//! Debugging code to test fingerprints computed for query results. For each node marked with
+//! Debugging code to test fingerprints computed for query results. For each node marked with
//! `#[rustc_clean]` we will compare the fingerprint from the current and from the previous
//! compilation session as appropriate:
//!
&'a mut self,
range: R,
) -> impl Iterator<Item = (I, T)> + 'a {
- self.raw.drain(range).enumerate().map(|(n, t)| (I::new(n), t))
+ let begin = match range.start_bound() {
+ std::ops::Bound::Included(i) => *i,
+ std::ops::Bound::Excluded(i) => i.checked_add(1).unwrap(),
+ std::ops::Bound::Unbounded => 0,
+ };
+ self.raw.drain(range).enumerate().map(move |(n, t)| (I::new(begin + n), t))
}
#[inline]
#[subdiagnostic]
pub req_introduces_loc: Option<ReqIntroducedLocations>,
+ pub has_param_name: bool,
+ pub param_name: String,
pub spans_empty: bool,
pub has_lifetime: bool,
pub lifetime: String,
ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Bool
| ty::Char
| ty::Int(..)
use rustc_index::vec::IndexVec;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::GenericArg;
-use rustc_middle::ty::{self, BoundVar, List};
+use rustc_middle::ty::{self, List};
use rustc_span::source_map::Span;
pub use rustc_middle::infer::canonical::*;
variables: &List<CanonicalVarInfo<'tcx>>,
universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
) -> CanonicalVarValues<'tcx> {
- let var_values: IndexVec<BoundVar, GenericArg<'tcx>> = variables
- .iter()
- .map(|info| self.instantiate_canonical_var(span, info, &universe_map))
- .collect();
-
- CanonicalVarValues { var_values }
+ CanonicalVarValues {
+ var_values: self.tcx.mk_substs(
+ variables
+ .iter()
+ .map(|info| self.instantiate_canonical_var(span, info, &universe_map)),
+ ),
+ }
}
/// Given the "info" about a canonical variable, creates a fresh
use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin};
use crate::traits::query::{Fallible, NoSolution};
use crate::traits::{Obligation, ObligationCause, PredicateObligation};
-use crate::traits::{PredicateObligations, TraitEngine};
+use crate::traits::{PredicateObligations, TraitEngine, TraitEngineExt};
use rustc_data_structures::captures::Captures;
use rustc_index::vec::Idx;
use rustc_index::vec::IndexVec;
// given variable in the loop above, use that. Otherwise, use
// a fresh inference variable.
let result_subst = CanonicalVarValues {
- var_values: query_response
- .variables
- .iter()
- .enumerate()
- .map(|(index, info)| {
+ var_values: self.tcx.mk_substs(query_response.variables.iter().enumerate().map(
+ |(index, info)| {
if info.is_existential() {
match opt_values[BoundVar::new(index)] {
Some(k) => k,
universe_map[u.as_usize()]
})
}
- })
- .collect(),
+ },
+ )),
};
let mut obligations = vec![];
value
} else {
let delegate = FnMutDelegate {
- regions: &mut |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() {
+ regions: &mut |br: ty::BoundRegion| match var_values[br.var].unpack() {
GenericArgKind::Lifetime(l) => l,
r => bug!("{:?} is a region but value is {:?}", br, r),
},
- types: &mut |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() {
+ types: &mut |bound_ty: ty::BoundTy| match var_values[bound_ty.var].unpack() {
GenericArgKind::Type(ty) => ty,
r => bug!("{:?} is a type but value is {:?}", bound_ty, r),
},
- consts: &mut |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack()
- {
+ consts: &mut |bound_ct: ty::BoundVar, _| match var_values[bound_ct].unpack() {
GenericArgKind::Const(ct) => ct,
c => bug!("{:?} is a const but value is {:?}", bound_ct, c),
},
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{
+ self, FallibleTypeFolder, InferConst, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
+ TypeVisitable,
+};
use rustc_middle::ty::{IntType, UintType};
use rustc_span::{Span, DUMMY_SP};
let a = self.shallow_resolve(a);
let b = self.shallow_resolve(b);
- let a_is_expected = relation.a_is_expected();
-
match (a.kind(), b.kind()) {
(
ty::ConstKind::Infer(InferConst::Var(a_vid)),
}
(ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
- return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected);
+ return self.unify_const_variable(vid, b);
}
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
- return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected);
+ return self.unify_const_variable(vid, a);
}
(ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
// FIXME(#59490): Need to remove the leak check to accommodate
#[instrument(level = "debug", skip(self))]
fn unify_const_variable(
&self,
- param_env: ty::ParamEnv<'tcx>,
target_vid: ty::ConstVid<'tcx>,
ct: ty::Const<'tcx>,
- vid_is_expected: bool,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
let (for_universe, span) = {
let mut inner = self.inner.borrow_mut();
ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span),
}
};
- let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid }
- .relate(ct, ct)?;
+ let value = ct.try_fold_with(&mut ConstInferUnifier {
+ infcx: self,
+ span,
+ for_universe,
+ target_vid,
+ })?;
self.inner.borrow_mut().const_unification_table().union_value(
target_vid,
debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
// Generalize type of `a_ty` appropriately depending on the
- // direction. As an example, assume:
+ // direction. As an example, assume:
//
// - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an
// inference variable,
span: Span,
- param_env: ty::ParamEnv<'tcx>,
-
for_universe: ty::UniverseIndex,
/// The vid of the const variable that is in the process of being
target_vid: ty::ConstVid<'tcx>,
}
-// We use `TypeRelation` here to propagate `RelateResult` upwards.
-//
-// Both inputs are expected to be the same.
-impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.infcx.tcx
- }
-
- fn intercrate(&self) -> bool {
- assert!(!self.infcx.intercrate);
- false
- }
-
- fn param_env(&self) -> ty::ParamEnv<'tcx> {
- self.param_env
- }
-
- fn tag(&self) -> &'static str {
- "ConstInferUnifier"
- }
-
- fn a_is_expected(&self) -> bool {
- true
- }
-
- fn mark_ambiguous(&mut self) {
- bug!()
- }
-
- fn relate_with_variance<T: Relate<'tcx>>(
- &mut self,
- _variance: ty::Variance,
- _info: ty::VarianceDiagInfo<'tcx>,
- a: T,
- b: T,
- ) -> RelateResult<'tcx, T> {
- // We don't care about variance here.
- self.relate(a, b)
- }
+impl<'tcx> FallibleTypeFolder<'tcx> for ConstInferUnifier<'_, 'tcx> {
+ type Error = TypeError<'tcx>;
- fn binders<T>(
- &mut self,
- a: ty::Binder<'tcx, T>,
- b: ty::Binder<'tcx, T>,
- ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
- where
- T: Relate<'tcx>,
- {
- Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
}
#[instrument(level = "debug", skip(self), ret)]
- fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
- debug_assert_eq!(t, _t);
-
+ fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, TypeError<'tcx>> {
match t.kind() {
&ty::Infer(ty::TyVar(vid)) => {
let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
match probe {
TypeVariableValue::Known { value: u } => {
debug!("ConstOccursChecker: known value {:?}", u);
- self.tys(u, u)
+ u.try_fold_with(self)
}
TypeVariableValue::Unknown { universe } => {
if self.for_universe.can_name(universe) {
}
}
ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t),
- _ => relate::super_relate_tys(self, t, t),
+ _ => t.try_super_fold_with(self),
}
}
- fn regions(
+ #[instrument(level = "debug", skip(self), ret)]
+ fn try_fold_region(
&mut self,
r: ty::Region<'tcx>,
- _r: ty::Region<'tcx>,
- ) -> RelateResult<'tcx, ty::Region<'tcx>> {
- debug_assert_eq!(r, _r);
+ ) -> Result<ty::Region<'tcx>, TypeError<'tcx>> {
debug!("ConstInferUnifier: r={:?}", r);
match *r {
}
}
- #[instrument(level = "debug", skip(self))]
- fn consts(
- &mut self,
- c: ty::Const<'tcx>,
- _c: ty::Const<'tcx>,
- ) -> RelateResult<'tcx, ty::Const<'tcx>> {
- debug_assert_eq!(c, _c);
-
+ #[instrument(level = "debug", skip(self), ret)]
+ fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, TypeError<'tcx>> {
match c.kind() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
// Check if the current unification would end up
let var_value =
self.infcx.inner.borrow_mut().const_unification_table().probe_value(vid);
match var_value.val {
- ConstVariableValue::Known { value: u } => self.consts(u, u),
+ ConstVariableValue::Known { value: u } => u.try_fold_with(self),
ConstVariableValue::Unknown { universe } => {
if self.for_universe.can_name(universe) {
Ok(c)
}
}
}
- ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => {
- let substs = self.relate_with_variance(
- ty::Variance::Invariant,
- ty::VarianceDiagInfo::default(),
- substs,
- substs,
- )?;
-
- Ok(self.tcx().mk_const(ty::UnevaluatedConst { def, substs }, c.ty()))
- }
- _ => relate::super_relate_consts(self, c, c),
+ _ => c.try_super_fold_with(self),
}
}
}
use std::{cmp, fmt, iter};
mod note;
+mod note_and_explain;
mod suggest;
pub(crate) mod need_type_info;
}
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
+ let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+ let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
let mut values = self.cmp_fn_sig(&sig1, &sig2);
let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1));
let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2));
}
(ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
+ let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
let mut values = self.cmp_fn_sig(&sig1, sig2);
values.0.push_highlighted(format!(
" {{{}}}",
}
(ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => {
- let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
+ let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
let mut values = self.cmp_fn_sig(sig1, &sig2);
values.1.push_normal(format!(
" {{{}}}",
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
+ self.suggest_function_pointers(cause, span, &exp_found, diag);
}
}
- // In some (most?) cases cause.body_id points to actual body, but in some cases
- // it's an actual definition. According to the comments (e.g. in
- // rustc_hir_analysis/check/compare_impl_item.rs:compare_predicate_entailment) the latter
- // is relied upon by some other code. This might (or might not) need cleanup.
- let body_owner_def_id =
- self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| {
- self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id })
- });
self.check_and_note_conflicting_crates(diag, terr);
- self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id());
+
+ self.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values
&& let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind()
(ty::Tuple(fields), _) => {
self.emit_tuple_wrap_err(&mut err, span, found, fields)
}
+ // If a byte was expected and the found expression is a char literal
+ // containing a single ASCII character, perhaps the user meant to write `b'c'` to
+ // specify a byte literal
+ (ty::Uint(ty::UintTy::U8), ty::Char) => {
+ if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+ && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
+ && code.chars().next().map_or(false, |c| c.is_ascii())
+ {
+ err.span_suggestion(
+ span,
+ "if you meant to write a byte literal, prefix with `b`",
+ format!("b'{}'", escape_literal(code)),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
// If a character was expected and the found expression is a string literal
// containing a single character, perhaps the user meant to write `'c'` to
// specify a character literal (issue #92479)
let labeled_user_string = match bound_kind {
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
- GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
- GenericKind::Opaque(def_id, substs) => {
- format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs))
- }
+ GenericKind::Alias(ref p) => match p.kind(self.tcx) {
+ ty::AliasKind::Projection => format!("the associated type `{}`", p),
+ ty::AliasKind::Opaque => format!("the opaque type `{}`", p),
+ },
};
if let Some(SubregionOrigin::CompareImplItemObligation {
/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
/// FloatVar inference type are compatible with themselves or their concrete types (Int and
/// Float types, respectively). When comparing two ADTs, these rules apply recursively.
- pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+ pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
let (a, b) = self.resolve_vars_if_possible((a, b));
SameTypeModuloInfer(self).relate(a, b).is_ok()
}
// in the types are about to print
// - Meanwhile, the `maybe_highlighting_region` calls set up
// highlights so that, if they do appear, we will replace
- // them `'0` and whatever. (This replacement takes place
+ // them `'0` and whatever. (This replacement takes place
// inside the closure given to `maybe_highlighting_region`.)
//
// There is some duplication between the calls -- i.e., the
let sp = var_origin.span();
let return_sp = sub_origin.span();
let param = self.find_param_with_region(*sup_r, *sub_r)?;
+ let simple_ident = param.param.pat.simple_ident();
let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
let (mention_influencer, influencer_point) =
req_introduces_loc: subdiag,
has_lifetime: sup_r.has_name(),
- lifetime: sup_r.to_string(),
+ lifetime: lifetime_name.clone(),
+ has_param_name: simple_ident.is_some(),
+ param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
spans_empty,
bound,
};
if let Some(def_id) = preds.principal_def_id() {
self.0.insert(def_id);
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => t.super_visit_with(self),
}
let owner_id = hir.body_owner(body_id);
let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
- let poly_fn_sig = tcx.fn_sig(id);
+ let poly_fn_sig = tcx.fn_sig(id).subst_identity();
let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
let body = hir.body(body_id);
.impl_trait_ref(impl_def_id)
else { return; };
let trait_substs = trait_ref
+ .subst_identity()
// Replace the explicit self type with `Self` for better suggestion rendering
.with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
.substs;
let Ok(trait_predicates) = self
.tcx
- .bound_explicit_predicates_of(trait_item_def_id)
- .map_bound(|p| p.predicates)
- .subst_iter_copied(self.tcx, trait_item_substs)
+ .explicit_predicates_of(trait_item_def_id)
+ .instantiate_own(self.tcx, trait_item_substs)
.map(|(pred, _)| {
if pred.is_suggestable(self.tcx, false) {
Ok(pred.to_string())
--- /dev/null
+use super::TypeErrCtxt;
+use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
+use rustc_errors::{pluralize, Diagnostic, MultiSpan};
+use rustc_hir::{self as hir, def::DefKind};
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::print::Printer;
+use rustc_middle::{
+ traits::ObligationCause,
+ ty::{self, error::TypeError, print::FmtPrinter, suggest_constraining_type_param, Ty},
+};
+use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ pub fn note_and_explain_type_err(
+ &self,
+ diag: &mut Diagnostic,
+ err: TypeError<'tcx>,
+ cause: &ObligationCause<'tcx>,
+ sp: Span,
+ body_owner_def_id: DefId,
+ ) {
+ use ty::error::TypeError::*;
+ debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
+
+ let tcx = self.tcx;
+
+ match err {
+ ArgumentSorts(values, _) | Sorts(values) => {
+ match (values.expected.kind(), values.found.kind()) {
+ (ty::Closure(..), ty::Closure(..)) => {
+ diag.note("no two closures, even if identical, have the same type");
+ diag.help("consider boxing your closure and/or using it as a trait object");
+ }
+ (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
+ // Issue #63167
+ diag.note("distinct uses of `impl Trait` result in different opaque types");
+ }
+ (ty::Float(_), ty::Infer(ty::IntVar(_)))
+ if let Ok(
+ // Issue #53280
+ snippet,
+ ) = tcx.sess.source_map().span_to_snippet(sp) =>
+ {
+ if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
+ diag.span_suggestion(
+ sp,
+ "use a float literal",
+ format!("{}.0", snippet),
+ MachineApplicable,
+ );
+ }
+ }
+ (ty::Param(expected), ty::Param(found)) => {
+ let generics = tcx.generics_of(body_owner_def_id);
+ let e_span = tcx.def_span(generics.type_param(expected, tcx).def_id);
+ if !sp.contains(e_span) {
+ diag.span_label(e_span, "expected type parameter");
+ }
+ let f_span = tcx.def_span(generics.type_param(found, tcx).def_id);
+ if !sp.contains(f_span) {
+ diag.span_label(f_span, "found type parameter");
+ }
+ diag.note(
+ "a type parameter was expected, but a different one was found; \
+ you might be missing a type parameter or trait bound",
+ );
+ diag.note(
+ "for more information, visit \
+ https://doc.rust-lang.org/book/ch10-02-traits.html\
+ #traits-as-parameters",
+ );
+ }
+ (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
+ diag.note("an associated type was expected, but a different one was found");
+ }
+ (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
+ if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
+ {
+ let generics = tcx.generics_of(body_owner_def_id);
+ let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+ if !sp.contains(p_span) {
+ diag.span_label(p_span, "this type parameter");
+ }
+ let hir = tcx.hir();
+ let mut note = true;
+ if let Some(generics) = generics
+ .type_param(p, tcx)
+ .def_id
+ .as_local()
+ .map(|id| hir.local_def_id_to_hir_id(id))
+ .and_then(|id| tcx.hir().find_parent(id))
+ .as_ref()
+ .and_then(|node| node.generics())
+ {
+ // Synthesize the associated type restriction `Add<Output = Expected>`.
+ // FIXME: extract this logic for use in other diagnostics.
+ let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
+ let path =
+ tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
+ let item_name = tcx.item_name(proj.def_id);
+ let item_args = self.format_generic_args(assoc_substs);
+
+ let path = if path.ends_with('>') {
+ format!(
+ "{}, {}{} = {}>",
+ &path[..path.len() - 1],
+ item_name,
+ item_args,
+ p
+ )
+ } else {
+ format!("{}<{}{} = {}>", path, item_name, item_args, p)
+ };
+ note = !suggest_constraining_type_param(
+ tcx,
+ generics,
+ diag,
+ &format!("{}", proj.self_ty()),
+ &path,
+ None,
+ );
+ }
+ if note {
+ diag.note("you might be missing a type parameter or trait bound");
+ }
+ }
+ (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
+ | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
+ let generics = tcx.generics_of(body_owner_def_id);
+ let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+ if !sp.contains(p_span) {
+ diag.span_label(p_span, "this type parameter");
+ }
+ diag.help("type parameters must be constrained to match other types");
+ if tcx.sess.teach(&diag.get_code().unwrap()) {
+ diag.help(
+ "given a type parameter `T` and a method `foo`:
+```
+trait Trait<T> { fn foo(&tcx) -> T; }
+```
+the only ways to implement method `foo` are:
+- constrain `T` with an explicit type:
+```
+impl Trait<String> for X {
+ fn foo(&tcx) -> String { String::new() }
+}
+```
+- add a trait bound to `T` and call a method on that trait that returns `Self`:
+```
+impl<T: std::default::Default> Trait<T> for X {
+ fn foo(&tcx) -> T { <T as std::default::Default>::default() }
+}
+```
+- change `foo` to return an argument of type `T`:
+```
+impl<T> Trait<T> for X {
+ fn foo(&tcx, x: T) -> T { x }
+}
+```",
+ );
+ }
+ diag.note(
+ "for more information, visit \
+ https://doc.rust-lang.org/book/ch10-02-traits.html\
+ #traits-as-parameters",
+ );
+ }
+ (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {
+ let generics = tcx.generics_of(body_owner_def_id);
+ let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+ if !sp.contains(p_span) {
+ diag.span_label(p_span, "this type parameter");
+ }
+ diag.help(&format!(
+ "every closure has a distinct type and so could not always match the \
+ caller-chosen type of parameter `{}`",
+ p
+ ));
+ }
+ (ty::Param(p), _) | (_, ty::Param(p)) => {
+ let generics = tcx.generics_of(body_owner_def_id);
+ let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+ if !sp.contains(p_span) {
+ diag.span_label(p_span, "this type parameter");
+ }
+ }
+ (ty::Alias(ty::Projection, proj_ty), _) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
+ self.expected_projection(
+ diag,
+ proj_ty,
+ values,
+ body_owner_def_id,
+ cause.code(),
+ );
+ }
+ (_, ty::Alias(ty::Projection, proj_ty)) if tcx.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
+ let msg = format!(
+ "consider constraining the associated type `{}` to `{}`",
+ values.found, values.expected,
+ );
+ if !(self.suggest_constraining_opaque_associated_type(
+ diag,
+ &msg,
+ proj_ty,
+ values.expected,
+ ) || self.suggest_constraint(
+ diag,
+ &msg,
+ body_owner_def_id,
+ proj_ty,
+ values.expected,
+ )) {
+ diag.help(&msg);
+ diag.note(
+ "for more information, visit \
+ https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
+ );
+ }
+ }
+ _ => {}
+ }
+ debug!(
+ "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
+ values.expected,
+ values.expected.kind(),
+ values.found,
+ values.found.kind(),
+ );
+ }
+ CyclicTy(ty) => {
+ // Watch out for various cases of cyclic types and try to explain.
+ if ty.is_closure() || ty.is_generator() {
+ diag.note(
+ "closures cannot capture themselves or take themselves as argument;\n\
+ this error may be the result of a recent compiler bug-fix,\n\
+ see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
+ for more information",
+ );
+ }
+ }
+ TargetFeatureCast(def_id) => {
+ let target_spans = tcx.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
+ diag.note(
+ "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
+ );
+ diag.span_labels(target_spans, "`#[target_feature]` added here");
+ }
+ _ => {}
+ }
+ }
+
+ fn suggest_constraint(
+ &self,
+ diag: &mut Diagnostic,
+ msg: &str,
+ body_owner_def_id: DefId,
+ proj_ty: &ty::AliasTy<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> bool {
+ let tcx = self.tcx;
+ let assoc = tcx.associated_item(proj_ty.def_id);
+ let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx);
+ if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) {
+ if let Some(hir_generics) = item.generics() {
+ // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
+ // This will also work for `impl Trait`.
+ let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
+ let generics = tcx.generics_of(body_owner_def_id);
+ generics.type_param(param_ty, tcx).def_id
+ } else {
+ return false;
+ };
+ let Some(def_id) = def_id.as_local() else {
+ return false;
+ };
+
+ // First look in the `where` clause, as this might be
+ // `fn foo<T>(x: T) where T: Trait`.
+ for pred in hir_generics.bounds_for_param(def_id) {
+ if self.constrain_generic_bound_associated_type_structured_suggestion(
+ diag,
+ &trait_ref,
+ pred.bounds,
+ &assoc,
+ assoc_substs,
+ ty,
+ msg,
+ false,
+ ) {
+ return true;
+ }
+ }
+ }
+ }
+ false
+ }
+
+ /// An associated type was expected and a different type was found.
+ ///
+ /// We perform a few different checks to see what we can suggest:
+ ///
+ /// - In the current item, look for associated functions that return the expected type and
+ /// suggest calling them. (Not a structured suggestion.)
+ /// - If any of the item's generic bounds can be constrained, we suggest constraining the
+ /// associated type to the found type.
+ /// - If the associated type has a default type and was expected inside of a `trait`, we
+ /// mention that this is disallowed.
+ /// - If all other things fail, and the error is not because of a mismatch between the `trait`
+ /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
+ /// fn that returns the type.
+ fn expected_projection(
+ &self,
+ diag: &mut Diagnostic,
+ proj_ty: &ty::AliasTy<'tcx>,
+ values: ExpectedFound<Ty<'tcx>>,
+ body_owner_def_id: DefId,
+ cause_code: &ObligationCauseCode<'_>,
+ ) {
+ let tcx = self.tcx;
+
+ let msg = format!(
+ "consider constraining the associated type `{}` to `{}`",
+ values.expected, values.found
+ );
+ let body_owner = tcx.hir().get_if_local(body_owner_def_id);
+ let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
+
+ // We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
+ let callable_scope = matches!(
+ body_owner,
+ Some(
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
+ | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
+ | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
+ )
+ );
+ let impl_comparison =
+ matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
+ let assoc = tcx.associated_item(proj_ty.def_id);
+ if !callable_scope || impl_comparison {
+ // We do not want to suggest calling functions when the reason of the
+ // type error is a comparison of an `impl` with its `trait` or when the
+ // scope is outside of a `Body`.
+ } else {
+ // If we find a suitable associated function that returns the expected type, we don't
+ // want the more general suggestion later in this method about "consider constraining
+ // the associated type or calling a method that returns the associated type".
+ let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type(
+ diag,
+ assoc.container_id(tcx),
+ current_method_ident,
+ proj_ty.def_id,
+ values.expected,
+ );
+ // Possibly suggest constraining the associated type to conform to the
+ // found type.
+ if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
+ || point_at_assoc_fn
+ {
+ return;
+ }
+ }
+
+ self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
+
+ if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
+ return;
+ }
+
+ if !impl_comparison {
+ // Generic suggestion when we can't be more specific.
+ if callable_scope {
+ diag.help(&format!(
+ "{} or calling a method that returns `{}`",
+ msg, values.expected
+ ));
+ } else {
+ diag.help(&msg);
+ }
+ diag.note(
+ "for more information, visit \
+ https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
+ );
+ }
+ if tcx.sess.teach(&diag.get_code().unwrap()) {
+ diag.help(
+ "given an associated type `T` and a method `foo`:
+```
+trait Trait {
+type T;
+fn foo(&tcx) -> Self::T;
+}
+```
+the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
+```
+impl Trait for X {
+type T = String;
+fn foo(&tcx) -> Self::T { String::new() }
+}
+```",
+ );
+ }
+ }
+
+ /// When the expected `impl Trait` is not defined in the current item, it will come from
+ /// a return type. This can occur when dealing with `TryStream` (#71035).
+ fn suggest_constraining_opaque_associated_type(
+ &self,
+ diag: &mut Diagnostic,
+ msg: &str,
+ proj_ty: &ty::AliasTy<'tcx>,
+ ty: Ty<'tcx>,
+ ) -> bool {
+ let tcx = self.tcx;
+
+ let assoc = tcx.associated_item(proj_ty.def_id);
+ if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
+ let opaque_local_def_id = def_id.as_local();
+ let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
+ match &tcx.hir().expect_item(opaque_local_def_id).kind {
+ hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty,
+ _ => bug!("The HirId comes from a `ty::Opaque`"),
+ }
+ } else {
+ return false;
+ };
+
+ let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(tcx);
+
+ self.constrain_generic_bound_associated_type_structured_suggestion(
+ diag,
+ &trait_ref,
+ opaque_hir_ty.bounds,
+ assoc,
+ assoc_substs,
+ ty,
+ msg,
+ true,
+ )
+ } else {
+ false
+ }
+ }
+
+ fn point_at_methods_that_satisfy_associated_type(
+ &self,
+ diag: &mut Diagnostic,
+ assoc_container_id: DefId,
+ current_method_ident: Option<Symbol>,
+ proj_ty_item_def_id: DefId,
+ expected: Ty<'tcx>,
+ ) -> bool {
+ let tcx = self.tcx;
+
+ let items = tcx.associated_items(assoc_container_id);
+ // Find all the methods in the trait that could be called to construct the
+ // expected associated type.
+ // FIXME: consider suggesting the use of associated `const`s.
+ let methods: Vec<(Span, String)> = items
+ .in_definition_order()
+ .filter(|item| {
+ ty::AssocKind::Fn == item.kind && Some(item.name) != current_method_ident
+ })
+ .filter_map(|item| {
+ let method = tcx.fn_sig(item.def_id).subst_identity();
+ match *method.output().skip_binder().kind() {
+ ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
+ if item_def_id == proj_ty_item_def_id =>
+ {
+ Some((
+ tcx.def_span(item.def_id),
+ format!("consider calling `{}`", tcx.def_path_str(item.def_id)),
+ ))
+ }
+ _ => None,
+ }
+ })
+ .collect();
+ if !methods.is_empty() {
+ // Use a single `help:` to show all the methods in the trait that can
+ // be used to construct the expected associated type.
+ let mut span: MultiSpan =
+ methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
+ let msg = format!(
+ "{some} method{s} {are} available that return{r} `{ty}`",
+ some = if methods.len() == 1 { "a" } else { "some" },
+ s = pluralize!(methods.len()),
+ are = pluralize!("is", methods.len()),
+ r = if methods.len() == 1 { "s" } else { "" },
+ ty = expected
+ );
+ for (sp, label) in methods.into_iter() {
+ span.push_span_label(sp, label);
+ }
+ diag.span_help(span, &msg);
+ return true;
+ }
+ false
+ }
+
+ fn point_at_associated_type(
+ &self,
+ diag: &mut Diagnostic,
+ body_owner_def_id: DefId,
+ found: Ty<'tcx>,
+ ) -> bool {
+ let tcx = self.tcx;
+
+ let Some(hir_id) = body_owner_def_id.as_local() else {
+ return false;
+ };
+ let hir_id = tcx.hir().local_def_id_to_hir_id(hir_id);
+ // When `body_owner` is an `impl` or `trait` item, look in its associated types for
+ // `expected` and point at it.
+ let parent_id = tcx.hir().get_parent_item(hir_id);
+ let item = tcx.hir().find_by_def_id(parent_id.def_id);
+
+ debug!("expected_projection parent item {:?}", item);
+
+ let param_env = tcx.param_env(body_owner_def_id);
+
+ match item {
+ Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => {
+ // FIXME: account for `#![feature(specialization)]`
+ for item in &items[..] {
+ match item.kind {
+ hir::AssocItemKind::Type => {
+ // FIXME: account for returning some type in a trait fn impl that has
+ // an assoc type as a return type (#72076).
+ if let hir::Defaultness::Default { has_value: true } =
+ tcx.impl_defaultness(item.id.owner_id)
+ {
+ let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity();
+ if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() {
+ diag.span_label(
+ item.span,
+ "associated type defaults can't be assumed inside the \
+ trait defining them",
+ );
+ return true;
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ Some(hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
+ ..
+ })) => {
+ for item in &items[..] {
+ if let hir::AssocItemKind::Type = item.kind {
+ let assoc_ty = tcx.bound_type_of(item.id.owner_id).subst_identity();
+
+ if self.infcx.can_eq(param_env, assoc_ty, found).is_ok() {
+ diag.span_label(item.span, "expected this associated type");
+ return true;
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ false
+ }
+
+ /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
+ /// requirement, provide a structured suggestion to constrain it to a given type `ty`.
+ ///
+ /// `is_bound_surely_present` indicates whether we know the bound we're looking for is
+ /// inside `bounds`. If that's the case then we can consider `bounds` containing only one
+ /// trait bound as the one we're looking for. This can help in cases where the associated
+ /// type is defined on a supertrait of the one present in the bounds.
+ fn constrain_generic_bound_associated_type_structured_suggestion(
+ &self,
+ diag: &mut Diagnostic,
+ trait_ref: &ty::TraitRef<'tcx>,
+ bounds: hir::GenericBounds<'_>,
+ assoc: &ty::AssocItem,
+ assoc_substs: &[ty::GenericArg<'tcx>],
+ ty: Ty<'tcx>,
+ msg: &str,
+ is_bound_surely_present: bool,
+ ) -> bool {
+ // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
+
+ let trait_bounds = bounds.iter().filter_map(|bound| match bound {
+ hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
+ _ => None,
+ });
+
+ let matching_trait_bounds = trait_bounds
+ .clone()
+ .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
+ .collect::<Vec<_>>();
+
+ let span = match &matching_trait_bounds[..] {
+ &[ptr] => ptr.span,
+ &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
+ &[ptr] => ptr.span,
+ _ => return false,
+ },
+ _ => return false,
+ };
+
+ self.constrain_associated_type_structured_suggestion(
+ diag,
+ span,
+ assoc,
+ assoc_substs,
+ ty,
+ msg,
+ )
+ }
+
+ /// Given a span corresponding to a bound, provide a structured suggestion to set an
+ /// associated type to a given type `ty`.
+ fn constrain_associated_type_structured_suggestion(
+ &self,
+ diag: &mut Diagnostic,
+ span: Span,
+ assoc: &ty::AssocItem,
+ assoc_substs: &[ty::GenericArg<'tcx>],
+ ty: Ty<'tcx>,
+ msg: &str,
+ ) -> bool {
+ let tcx = self.tcx;
+
+ if let Ok(has_params) =
+ tcx.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
+ {
+ let (span, sugg) = if has_params {
+ let pos = span.hi() - BytePos(1);
+ let span = Span::new(pos, pos, span.ctxt(), span.parent());
+ (span, format!(", {} = {}", assoc.ident(tcx), ty))
+ } else {
+ let item_args = self.format_generic_args(assoc_substs);
+ (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(tcx), item_args, ty))
+ };
+ diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
+ return true;
+ }
+ false
+ }
+
+ pub fn format_generic_args(&self, args: &[ty::GenericArg<'tcx>]) -> String {
+ FmtPrinter::new(self.tcx, hir::def::Namespace::TypeNS)
+ .path_generic_args(Ok, args)
+ .expect("could not write to `String`.")
+ .into_buffer()
+ }
+}
StatementAsExpression,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
+use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
use rustc_span::{sym, BytePos, Span};
use crate::errors::SuggAddLetForLetChains;
}
}
+ pub(super) fn suggest_function_pointers(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ span: Span,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
+ let ty::error::ExpectedFound { expected, found } = exp_found;
+ let expected_inner = expected.peel_refs();
+ let found_inner = found.peel_refs();
+ if !expected_inner.is_fn() || !found_inner.is_fn() {
+ return;
+ }
+ match (&expected_inner.kind(), &found_inner.kind()) {
+ (ty::FnPtr(sig), ty::FnDef(did, substs)) => {
+ let expected_sig = &(self.normalize_fn_sig)(*sig);
+ let found_sig =
+ &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).subst(self.tcx, substs));
+
+ let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
+
+ if !self.same_type_modulo_infer(*found_sig, *expected_sig)
+ || !sig.is_suggestable(self.tcx, true)
+ || ty::util::is_intrinsic(self.tcx, *did)
+ {
+ return;
+ }
+
+ let (msg, sug) = match (expected.is_ref(), found.is_ref()) {
+ (true, false) => {
+ let msg = "consider using a reference";
+ let sug = format!("&{fn_name}");
+ (msg, sug)
+ }
+ (false, true) => {
+ let msg = "consider removing the reference";
+ let sug = format!("{fn_name}");
+ (msg, sug)
+ }
+ (true, true) => {
+ diag.note("fn items are distinct from fn pointers");
+ let msg = "consider casting to a fn pointer";
+ let sug = format!("&({fn_name} as {sig})");
+ (msg, sug)
+ }
+ (false, false) => {
+ diag.note("fn items are distinct from fn pointers");
+ let msg = "consider casting to a fn pointer";
+ let sug = format!("{fn_name} as {sig}");
+ (msg, sug)
+ }
+ };
+ diag.span_suggestion(span, msg, sug, Applicability::MaybeIncorrect);
+ }
+ (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+ let expected_sig =
+ &(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).subst(self.tcx, substs1));
+ let found_sig =
+ &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).subst(self.tcx, substs2));
+
+ if self.same_type_modulo_infer(*expected_sig, *found_sig) {
+ diag.note("different fn items have unique types, even if their signatures are the same");
+ }
+
+ if !self.same_type_modulo_infer(*found_sig, *expected_sig)
+ || !found_sig.is_suggestable(self.tcx, true)
+ || !expected_sig.is_suggestable(self.tcx, true)
+ || ty::util::is_intrinsic(self.tcx, *did1)
+ || ty::util::is_intrinsic(self.tcx, *did2)
+ {
+ return;
+ }
+
+ let fn_name = self.tcx.def_path_str_with_substs(*did2, substs2);
+ let sug = if found.is_ref() {
+ format!("&({fn_name} as {found_sig})")
+ } else {
+ format!("{fn_name} as {found_sig}")
+ };
+
+ let msg = format!(
+ "consider casting both fn items to fn pointers using `as {expected_sig}`"
+ );
+
+ diag.span_suggestion_hidden(span, msg, sug, Applicability::MaybeIncorrect);
+ }
+ (ty::FnDef(did, substs), ty::FnPtr(sig)) => {
+ let expected_sig =
+ &(self.normalize_fn_sig)(self.tcx.fn_sig(*did).subst(self.tcx, substs));
+ let found_sig = &(self.normalize_fn_sig)(*sig);
+
+ if !self.same_type_modulo_infer(*found_sig, *expected_sig) {
+ return;
+ }
+
+ let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
+
+ let casting = if expected.is_ref() {
+ format!("&({fn_name} as {found_sig})")
+ } else {
+ format!("{fn_name} as {found_sig}")
+ };
+
+ diag.help(&format!("consider casting the fn item to a fn pointer: `{}`", casting));
+ }
+ _ => {
+ return;
+ }
+ };
+ }
+
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
(expected.kind(), found.kind())
span: Span,
) {
let hir = self.tcx.hir();
- let fn_hir_id = hir.parent_id(cause.body_id);
- if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
+ if let Some(node) = self.tcx.hir().find_by_def_id(cause.body_id) &&
let hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(_sig, _, body_id), ..
}) = node {
| ty::Foreign(..)
| ty::Param(..)
| ty::Closure(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::GeneratorWitness(..) => t.super_fold_with(self),
ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
let origin = Subtype(Box::new(self.fields.trace.clone()));
- Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions(
+ // GLB(&'static u8, &'a u8) == &RegionLUB('static, 'a) u8 == &'static u8
+ Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions(
self.tcx(),
origin,
a,
//
// Example: if the LHS is a type variable, and RHS is
// `Box<i32>`, then we current compare `v` to the RHS first,
- // which will instantiate `v` with `Box<i32>`. Then when `v`
+ // which will instantiate `v` with `Box<i32>`. Then when `v`
// is compared to the LHS, we instantiate LHS with `Box<i32>`.
// But if we did in reverse order, we would create a `v <:
// LHS` (or vice versa) constraint and then instantiate
#[derive(Copy, Clone, Debug)]
pub(crate) enum VarValue<'tcx> {
- /// Empty lifetime is for data that is never accessed. We tag the
+ /// Empty lifetime is for data that is never accessed. We tag the
/// empty lifetime with a universe -- the idea is that we don't
/// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable.
/// Therefore, the `'empty` in a universe `U` is less than all
VarValue::Empty(a_universe) => {
let b_data = var_values.value_mut(b_vid);
- let changed = (|| match *b_data {
+ let changed = match *b_data {
VarValue::Empty(b_universe) => {
// Empty regions are ordered according to the universe
// they are associated with.
};
if lub == cur_region {
- return false;
+ false
+ } else {
+ debug!(
+ "Expanding value of {:?} from {:?} to {:?}",
+ b_vid, cur_region, lub
+ );
+
+ *b_data = VarValue::Value(lub);
+ true
}
-
- debug!(
- "Expanding value of {:?} from {:?} to {:?}",
- b_vid, cur_region, lub
- );
-
- *b_data = VarValue::Value(lub);
- true
}
VarValue::ErrorValue => false,
- })();
+ };
if changed {
changes.push(b_vid);
}
// If both `a` and `b` are free, consult the declared
- // relationships. Note that this can be more precise than the
+ // relationships. Note that this can be more precise than the
// `lub` relationship defined below, since sometimes the "lub"
// is actually the `postdom_upper_bound` (see
// `TransitiveRelation` for more details).
// conflicting regions to report to the user. As we walk, we
// trip the flags from false to true, and if we find that
// we've already reported an error involving any particular
- // node we just stop and don't report the current error. The
+ // node we just stop and don't report the current error. The
// idea is to report errors that derive from independent
// regions of the graph, but not those that derive from
// overlapping locations.
debug!("{}.regions({:?}, {:?})", self.tag(), a, b);
let origin = Subtype(Box::new(self.fields.trace.clone()));
- Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions(
+ // LUB(&'static u8, &'a u8) == &RegionGLB('static, 'a) u8 == &'a u8
+ Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions(
self.tcx(),
origin,
a,
self.tcx.mk_region(ty::ReVar(region_var))
}
- /// Return the universe that the region `r` was created in. For
+ /// Return the universe that the region `r` was created in. For
/// most regions (e.g., `'static`, named regions from the user,
/// etc) this is the root universe U0. For inference variables or
/// placeholders, however, it will return the universe which they
}
/// Resolve any type variables found in `value` -- but only one
- /// level. So, if the variable `?X` is bound to some type
+ /// level. So, if the variable `?X` is bound to some type
/// `Foo<?Y>`, then this would return `Foo<?Y>` (but `?Y` may
/// itself be bound to a type).
///
if let None = self.tainted_by_errors() {
// As a heuristic, just skip reporting region errors
// altogether if other errors have been reported while
- // this infcx was in use. This is totally hokey but
+ // this infcx was in use. This is totally hokey but
// otherwise we have a hard time separating legit region
// errors from silly ones.
self.report_region_errors(generic_param_scope, &errors);
fn value_ty(&self) -> Ty<'tcx>;
/// Extract the scopes that apply to whichever side of the tuple
- /// the vid was found on. See the comment where this is called
+ /// the vid was found on. See the comment where this is called
/// for more details on why we want them.
fn vid_scopes<'r, D: TypeRelatingDelegate<'tcx>>(
&self,
debug!(?v_b);
if self.ambient_covariance() {
- // Covariance: a <= b. Hence, `b: a`.
- self.push_outlives(v_b, v_a, self.ambient_variance_info);
+ // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`.
+ self.push_outlives(v_a, v_b, self.ambient_variance_info);
}
if self.ambient_contravariance() {
- // Contravariant: b <= a. Hence, `a: b`.
- self.push_outlives(v_a, v_b, self.ambient_variance_info);
+ // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`.
+ self.push_outlives(v_b, v_a, self.ambient_variance_info);
}
Ok(a)
/// (these are not explicitly present in the ty representation right
/// now). This visitor handles that: it descends the type, tracking
/// binder depth, and finds late-bound regions targeting the
-/// `for<..`>. For each of those, it creates an entry in
+/// `for<..`>. For each of those, it creates an entry in
/// `bound_region_scope`.
struct ScopeInstantiator<'me, 'tcx> {
next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
t.super_visit_with(self);
self.target_index.shift_out(1);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
_ => {}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
use crate::traits;
use hir::def::DefKind;
use hir::def_id::{DefId, LocalDefId};
-use hir::{HirId, OpaqueTyOrigin};
+use hir::OpaqueTyOrigin;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::vec_map::VecMap;
use rustc_hir as hir;
pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<'tcx>>(
&self,
value: T,
- body_id: HirId,
+ body_id: LocalDefId,
span: Span,
param_env: ty::ParamEnv<'tcx>,
) -> InferOk<'tcx, T> {
DefiningAnchor::Bind(_) => {
// Check that this is `impl Trait` type is
// declared by `parent_def_id` -- i.e., one whose
- // value we are inferring. At present, this is
+ // value we are inferring. At present, this is
// always true during the first phase of
// type-check, but not always true later on during
// NLL. Once we support named opaque types more fully,
};
let item_kind = &self.tcx.hir().expect_item(def_id).kind;
- let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else {
+ let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else {
span_bug!(
span,
"weird opaque type: {:#?}, {:#?}",
t: &ty::Binder<'tcx, T>,
) -> ControlFlow<Self::BreakTy> {
t.super_visit_with(self);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
match *r {
// ignore bound regions, keep visiting
- ty::ReLateBound(_, _) => ControlFlow::CONTINUE,
+ ty::ReLateBound(_, _) => ControlFlow::Continue(()),
_ => {
(self.op)(r);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
// We're only interested in types involving regions
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match ty.kind() {
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, ref substs, .. }) => {
- // Skip lifetime paramters that are not captures.
+ // Skip lifetime parameters that are not captures.
let variances = self.tcx.variances_of(*def_id);
for (v, s) in std::iter::zip(variances, substs.iter()) {
ty::Alias(ty::Projection, proj)
if self.tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder =>
{
- // Skip lifetime paramters that are not captures.
+ // Skip lifetime parameters that are not captures.
let variances = self.tcx.variances_of(proj.def_id);
for (v, s) in std::iter::zip(variances, proj.substs.iter()) {
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
// RFC for reference.
use rustc_data_structures::sso::SsoHashSet;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, SubstsRef, Ty, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
use smallvec::{smallvec, SmallVec};
#[derive(Debug)]
// is not in a position to judge which is the best technique, so
// we just product the projection as a component and leave it to
// the consumer to decide (but see `EscapingProjection` below).
- Projection(ty::AliasTy<'tcx>),
+ Alias(ty::AliasTy<'tcx>),
// In the case where a projection has escaping regions -- meaning
// regions bound within the type itself -- we always use
// projection, so that implied bounds code can avoid relying on
// them. This gives us room to improve the regionck reasoning in
// the future without breaking backwards compat.
- EscapingProjection(Vec<Component<'tcx>>),
-
- Opaque(DefId, SubstsRef<'tcx>),
+ EscapingAlias(Vec<Component<'tcx>>),
}
/// Push onto `out` all the things that must outlive `'a` for the condition
}
// All regions are bound inside a witness
- ty::GeneratorWitness(..) => (),
+ ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => (),
// OutlivesTypeParameterEnv -- the actual checking that `X:'a`
// is implied by the environment is done in regionck.
out.push(Component::Param(p));
}
- // Ignore lifetimes found in opaque types. Opaque types can
- // have lifetimes in their substs which their hidden type doesn't
- // actually use. If we inferred that an opaque type is outlived by
- // its parameter lifetimes, then we could prove that any lifetime
- // outlives any other lifetime, which is unsound.
- // See https://github.com/rust-lang/rust/issues/84305 for
- // more details.
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- out.push(Component::Opaque(def_id, substs));
- },
-
// For projections, we prefer to generate an obligation like
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
// regionck more ways to prove that it holds. However,
// trait-ref. Therefore, if we see any higher-ranked regions,
// we simply fallback to the most restrictive rule, which
// requires that `Pi: 'a` for all `i`.
- ty::Alias(ty::Projection, ref data) => {
- if !data.has_escaping_bound_vars() {
+ ty::Alias(_, alias_ty) => {
+ if !alias_ty.has_escaping_bound_vars() {
// best case: no escaping regions, so push the
// projection and skip the subtree (thus generating no
// constraints for Pi). This defers the choice between
// the rules OutlivesProjectionEnv,
// OutlivesProjectionTraitDef, and
// OutlivesProjectionComponents to regionck.
- out.push(Component::Projection(*data));
+ out.push(Component::Alias(alias_ty));
} else {
// fallback case: hard code
- // OutlivesProjectionComponents. Continue walking
+ // OutlivesProjectionComponents. Continue walking
// through and constrain Pi.
let mut subcomponents = smallvec![];
let mut subvisited = SsoHashSet::new();
compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited);
- out.push(Component::EscapingProjection(subcomponents.into_iter().collect()));
+ out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
}
}
ty::Error(_) => {
// (*) Function pointers and trait objects are both binders.
// In the RFC, this means we would add the bound regions to
- // the "bound regions list". In our representation, no such
+ // the "bound regions list". In our representation, no such
// list is maintained explicitly, because bound regions
// themselves can be readily identified.
compute_components_recursive(tcx, ty.into(), out, visited);
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
- }
- OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
- self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
+ .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {
if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) {
};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
-use rustc_hir::def_id::DefId;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable};
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *param_ty);
}
- Component::Opaque(def_id, substs) => {
- self.opaque_must_outlive(*def_id, substs, origin, region)
- }
- Component::Projection(projection_ty) => {
- self.projection_must_outlive(origin, region, *projection_ty);
- }
- Component::EscapingProjection(subcomponents) => {
+ Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty),
+ Component::EscapingAlias(subcomponents) => {
self.components_must_outlive(origin, &subcomponents, region, category);
}
Component::UnresolvedInferenceVariable(v) => {
}
}
+ #[instrument(level = "debug", skip(self))]
fn param_ty_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
param_ty: ty::ParamTy,
) {
- debug!(
- "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
- region, param_ty, origin
- );
-
- let generic = GenericKind::Param(param_ty);
let verify_bound = self.verify_bound.param_bound(param_ty);
- self.delegate.push_verify(origin, generic, region, verify_bound);
- }
-
- #[instrument(level = "debug", skip(self))]
- fn opaque_must_outlive(
- &mut self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- origin: infer::SubregionOrigin<'tcx>,
- region: ty::Region<'tcx>,
- ) {
- self.generic_must_outlive(
- origin,
- region,
- GenericKind::Opaque(def_id, substs),
- def_id,
- substs,
- true,
- |ty| match *ty.kind() {
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => (def_id, substs),
- _ => bug!("expected only projection types from env, not {:?}", ty),
- },
- );
+ self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
}
#[instrument(level = "debug", skip(self))]
- fn projection_must_outlive(
+ fn alias_ty_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
- projection_ty: ty::AliasTy<'tcx>,
- ) {
- self.generic_must_outlive(
- origin,
- region,
- GenericKind::Projection(projection_ty),
- projection_ty.def_id,
- projection_ty.substs,
- false,
- |ty| match ty.kind() {
- ty::Alias(ty::Projection, projection_ty) => {
- (projection_ty.def_id, projection_ty.substs)
- }
- _ => bug!("expected only projection types from env, not {:?}", ty),
- },
- );
- }
-
- #[instrument(level = "debug", skip(self, filter))]
- fn generic_must_outlive(
- &mut self,
- origin: infer::SubregionOrigin<'tcx>,
- region: ty::Region<'tcx>,
- generic: GenericKind<'tcx>,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- is_opaque: bool,
- filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>),
+ alias_ty: ty::AliasTy<'tcx>,
) {
// An optimization for a common case with opaque types.
- if substs.is_empty() {
+ if alias_ty.substs.is_empty() {
return;
}
// particular). :) First off, we have to choose between using the
// OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
// OutlivesProjectionComponent rules, any one of which is
- // sufficient. If there are no inference variables involved, it's
+ // sufficient. If there are no inference variables involved, it's
// not hard to pick the right rule, but if there are, we're in a
// bit of a catch 22: if we picked which rule we were going to
// use, we could add constraints to the region inference graph
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds: Vec<_> =
- self.verify_bound.declared_region_bounds(def_id, substs).collect();
+ self.verify_bound.declared_bounds_from_definition(alias_ty).collect();
debug!(?trait_bounds);
// Compute the bounds we can derive from the environment. This
// is an "approximate" match -- in some cases, these bounds
// may not apply.
- let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic);
+ let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty);
debug!(?approx_env_bounds);
// Remove outlives bounds that we get from the environment but
// If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait`
// will be invoked with `['b => ^1]` and so we will get `^1` returned.
let bound = bound_outlives.skip_binder();
- let (def_id, substs) = filter(bound.0);
- self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1)
+ let ty::Alias(_, alias_ty) = bound.0.kind() else { bug!("expected AliasTy") };
+ self.verify_bound.declared_bounds_from_definition(*alias_ty).all(|r| r != bound.1)
});
// If declared bounds list is empty, the only applicable rule is
// the problem is to add `T: 'r`, which isn't true. So, if there are no
// inference variables, we use a verify constraint instead of adding
// edges, which winds up enforcing the same condition.
- let needs_infer = substs.needs_infer();
- if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) {
+ if approx_env_bounds.is_empty()
+ && trait_bounds.is_empty()
+ && (alias_ty.needs_infer() || alias_ty.kind(self.tcx) == ty::Opaque)
+ {
debug!("no declared bounds");
-
- self.substs_must_outlive(substs, origin, region);
-
+ self.substs_must_outlive(alias_ty.substs, origin, region);
return;
}
// projection outlive; in some cases, this may add insufficient
// edges into the inference graph, leading to inference failures
// even though a satisfactory solution exists.
- let verify_bound = self.verify_bound.projection_opaque_bounds(
- generic,
- def_id,
- substs,
- &mut Default::default(),
- );
- debug!("projection_must_outlive: pushing {:?}", verify_bound);
- self.delegate.push_verify(origin, generic, region, verify_bound);
+ let verify_bound = self.verify_bound.alias_bound(alias_ty, &mut Default::default());
+ debug!("alias_must_outlive: pushing {:?}", verify_bound);
+ self.delegate.push_verify(origin, GenericKind::Alias(alias_ty), region, verify_bound);
}
fn substs_must_outlive(
use crate::infer::outlives::components::{compute_components_recursive, Component};
use crate::infer::outlives::env::RegionBoundPairs;
use crate::infer::region_constraints::VerifyIfEq;
-use crate::infer::{GenericKind, VerifyBound};
+use crate::infer::VerifyBound;
use rustc_data_structures::sso::SsoHashSet;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::GenericArg;
-use rustc_middle::ty::{self, OutlivesPredicate, SubstsRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
use smallvec::smallvec;
/// this list.
pub fn approx_declared_bounds_from_env(
&self,
- generic: GenericKind<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
- let projection_ty = generic.to_ty(self.tcx);
- let erased_projection_ty = self.tcx.erase_regions(projection_ty);
- self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty)
+ let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx));
+ self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty)
}
#[instrument(level = "debug", skip(self, visited))]
- pub fn projection_opaque_bounds(
+ pub fn alias_bound(
&self,
- generic: GenericKind<'tcx>,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
visited: &mut SsoHashSet<GenericArg<'tcx>>,
) -> VerifyBound<'tcx> {
- let generic_ty = generic.to_ty(self.tcx);
+ let alias_ty_as_ty = alias_ty.to_ty(self.tcx);
// Search the env for where clauses like `P: 'a`.
- let projection_opaque_bounds = self
- .approx_declared_bounds_from_env(generic)
+ let env_bounds = self
+ .approx_declared_bounds_from_env(alias_ty)
.into_iter()
.map(|binder| {
- if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == generic_ty {
+ if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == alias_ty_as_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
VerifyBound::IfEq(verify_if_eq_b)
}
});
- // Extend with bounds that we can find from the trait.
- let trait_bounds =
- self.declared_region_bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r));
+
+ // Extend with bounds that we can find from the definition.
+ let definition_bounds =
+ self.declared_bounds_from_definition(alias_ty).map(|r| VerifyBound::OutlivedBy(r));
// see the extensive comment in projection_must_outlive
let recursive_bound = {
let mut components = smallvec![];
- compute_components_recursive(self.tcx, generic_ty.into(), &mut components, visited);
+ compute_components_recursive(self.tcx, alias_ty_as_ty.into(), &mut components, visited);
self.bound_from_components(&components, visited)
};
- VerifyBound::AnyBound(projection_opaque_bounds.chain(trait_bounds).collect())
- .or(recursive_bound)
+ VerifyBound::AnyBound(env_bounds.chain(definition_bounds).collect()).or(recursive_bound)
}
fn bound_from_components(
let mut bounds = components
.iter()
.map(|component| self.bound_from_single_component(component, visited))
- .filter(|bound| {
- // Remove bounds that must hold, since they are not interesting.
- !bound.must_hold()
- });
+ // Remove bounds that must hold, since they are not interesting.
+ .filter(|bound| !bound.must_hold());
match (bounds.next(), bounds.next()) {
(Some(first), None) => first,
match *component {
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
Component::Param(param_ty) => self.param_bound(param_ty),
- Component::Opaque(did, substs) => self.projection_opaque_bounds(
- GenericKind::Opaque(did, substs),
- did,
- substs,
- visited,
- ),
- Component::Projection(projection_ty) => self.projection_opaque_bounds(
- GenericKind::Projection(projection_ty),
- projection_ty.def_id,
- projection_ty.substs,
- visited,
- ),
- Component::EscapingProjection(ref components) => {
+ Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
+ Component::EscapingAlias(ref components) => {
self.bound_from_components(components, visited)
}
Component::UnresolvedInferenceVariable(v) => {
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
- pub fn declared_region_bounds(
+ pub fn declared_bounds_from_definition(
&self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
- let bounds = tcx.bound_item_bounds(def_id);
+ let bounds = tcx.item_bounds(alias_ty.def_id);
trace!("{:#?}", bounds.0);
bounds
- .subst_iter(tcx, substs)
+ .subst_iter(tcx, alias_ty.substs)
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
.map(|OutlivesPredicate(_, r)| r)
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::undo_log::UndoLogs;
use rustc_data_structures::unify as ut;
-use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion};
-use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ReStatic;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ReLateBound, ReVar};
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
pub enum GenericKind<'tcx> {
Param(ty::ParamTy),
- Projection(ty::AliasTy<'tcx>),
- Opaque(DefId, SubstsRef<'tcx>),
+ Alias(ty::AliasTy<'tcx>),
}
/// Describes the things that some `GenericKind` value `G` is known to
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
GenericKind::Param(ref p) => write!(f, "{:?}", p),
- GenericKind::Projection(ref p) => write!(f, "{:?}", p),
- GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| {
- write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap()))
- }),
+ GenericKind::Alias(ref p) => write!(f, "{:?}", p),
}
}
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
GenericKind::Param(ref p) => write!(f, "{}", p),
- GenericKind::Projection(ref p) => write!(f, "{}", p),
- GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| {
- write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap()))
- }),
+ GenericKind::Alias(ref p) => write!(f, "{}", p),
}
}
}
pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self {
GenericKind::Param(ref p) => p.to_ty(tcx),
- GenericKind::Projection(ref p) => tcx.mk_projection(p.def_id, p.substs),
- GenericKind::Opaque(def_id, substs) => tcx.mk_opaque(def_id, substs),
+ GenericKind::Alias(ref p) => p.to_ty(tcx),
}
}
}
} else if !t.has_non_region_infer() {
// All const/type variables in inference types must already be resolved,
// no need to visit the contents.
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
// Otherwise, keep visiting.
t.super_visit_with(self)
} else if !ct.has_non_region_infer() {
// All const/type variables in inference types must already be resolved,
// no need to visit the contents.
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
// Otherwise, keep visiting.
ct.super_visit_with(self)
// from the "cause" field, we could perhaps give more tailored
// error messages.
let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone()));
+ // Subtype(&'a u8, &'b u8) => Outlives('a: 'b) => SubRegion('b, 'a)
self.fields
.infcx
.inner
.borrow_mut()
.unwrap_region_constraints()
- .make_subregion(origin, a, b);
+ .make_subregion(origin, b, a);
Ok(a)
}
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> {
match (value1, value2) {
// We never equate two type variables, both of which
- // have known types. Instead, we recursively equate
+ // have known types. Instead, we recursively equate
// those types.
(&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => {
bug!("equating two type variables, both of which have known types")
use crate::infer::InferCtxt;
use crate::traits::Obligation;
-use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, ToPredicate, Ty};
obligation: PredicateObligation<'tcx>,
);
- fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
-
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
+ fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>>;
+
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships>;
+ /// Among all pending obligations, collect those are stalled on a inference variable which has
+ /// changed since the last call to `select_where_possible`. Those obligations are marked as
+ /// successful and returned.
+ fn drain_unstalled_obligations(
+ &mut self,
+ infcx: &InferCtxt<'tcx>,
+ ) -> Vec<PredicateObligation<'tcx>>;
}
pub trait TraitEngineExt<'tcx> {
infcx: &InferCtxt<'tcx>,
obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>,
);
+
+ fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
}
impl<'tcx, T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T {
self.register_predicate_obligation(infcx, obligation);
}
}
+
+ fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ let errors = self.select_where_possible(infcx);
+ if !errors.is_empty() {
+ return errors;
+ }
+
+ self.collect_remaining_errors()
+ }
}
mod structural_impls;
pub mod util;
+use hir::def_id::LocalDefId;
use rustc_hir as hir;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Const, ToPredicate, Ty, TyCtxt};
pub fn misc(
tcx: TyCtxt<'tcx>,
span: Span,
- body_id: hir::HirId,
+ body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
trait_ref: impl ToPredicate<'tcx, O>,
) -> Obligation<'tcx, O> {
use smallvec::smallvec;
use crate::infer::outlives::components::{push_outlives_components, Component};
-use crate::traits::{Obligation, ObligationCause, PredicateObligation};
+use crate::traits::{self, Obligation, ObligationCause, PredicateObligation};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
use rustc_span::symbol::Ident;
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());
- let obligations = predicates.predicates.iter().map(|&(mut pred, _)| {
+ let obligations = predicates.predicates.iter().map(|&(mut pred, span)| {
// when parent predicate is non-const, elaborate it to non-const predicates.
if data.constness == ty::BoundConstness::NotConst {
pred = pred.without_const(tcx);
}
+ let cause = obligation.cause.clone().derived_cause(
+ bound_predicate.rebind(data),
+ |derived| {
+ traits::ImplDerivedObligation(Box::new(
+ traits::ImplDerivedObligationCause {
+ derived,
+ impl_def_id: data.def_id(),
+ span,
+ },
+ ))
+ },
+ );
predicate_obligation(
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
obligation.param_env,
- obligation.cause.clone(),
+ cause,
)
});
debug!(?data, ?obligations, "super_predicates");
Component::UnresolvedInferenceVariable(_) => None,
- Component::Opaque(def_id, substs) => {
- let ty = tcx.mk_opaque(def_id, substs);
- Some(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(ty, r_min),
- )))
- }
-
- Component::Projection(projection) => {
+ Component::Alias(alias_ty) => {
// We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
// With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
- let ty = tcx.mk_projection(projection.def_id, projection.substs);
Some(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(ty, r_min),
+ ty::OutlivesPredicate(alias_ty.to_ty(tcx), r_min),
)))
}
- Component::EscapingProjection(_) => {
+ Component::EscapingAlias(_) => {
// We might be able to do more here, but we don't
// want to deal with escaping vars right now.
None
/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
/// define the given associated type `assoc_name`. It uses the
/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
-/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
+/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
/// `T::Item` and helps to avoid cycle errors (see e.g. #35237).
pub fn transitive_bounds_that_define_assoc_type<'tcx>(
tcx: TyCtxt<'tcx>,
use rustc_parse::maybe_new_parser_from_source_str;
use rustc_query_impl::QueryCtxt;
use rustc_session::config::{self, CheckCfg, ErrorOutputType, Input, OutputFilenames};
-use rustc_session::early_error;
use rustc_session::lint;
use rustc_session::parse::{CrateConfig, ParseSess};
use rustc_session::Session;
+use rustc_session::{early_error, CompilerIO};
use rustc_span::source_map::{FileLoader, FileName};
use rustc_span::symbol::sym;
use std::path::PathBuf;
pub struct Compiler {
pub(crate) sess: Lrc<Session>,
codegen_backend: Lrc<Box<dyn CodegenBackend>>,
- pub(crate) input: Input,
- pub(crate) input_path: Option<PathBuf>,
- pub(crate) output_dir: Option<PathBuf>,
- pub(crate) output_file: Option<PathBuf>,
- pub(crate) temps_dir: Option<PathBuf>,
pub(crate) register_lints: Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>>,
pub(crate) override_queries:
Option<fn(&Session, &mut ty::query::Providers, &mut ty::query::ExternProviders)>,
pub fn codegen_backend(&self) -> &Lrc<Box<dyn CodegenBackend>> {
&self.codegen_backend
}
- pub fn input(&self) -> &Input {
- &self.input
- }
- pub fn output_dir(&self) -> &Option<PathBuf> {
- &self.output_dir
- }
- pub fn output_file(&self) -> &Option<PathBuf> {
- &self.output_file
- }
- pub fn temps_dir(&self) -> &Option<PathBuf> {
- &self.temps_dir
- }
pub fn register_lints(&self) -> &Option<Box<dyn Fn(&Session, &mut LintStore) + Send + Sync>> {
&self.register_lints
}
sess: &Session,
attrs: &[ast::Attribute],
) -> OutputFilenames {
- util::build_output_filenames(
- &self.input,
- &self.output_dir,
- &self.output_file,
- &self.temps_dir,
- attrs,
- sess,
- )
+ util::build_output_filenames(attrs, sess)
}
}
pub crate_check_cfg: CheckCfg,
pub input: Input,
- pub input_path: Option<PathBuf>,
pub output_dir: Option<PathBuf>,
pub output_file: Option<PathBuf>,
pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
crate::callbacks::setup_callbacks();
let registry = &config.registry;
+
+ let temps_dir = config.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
let (mut sess, codegen_backend) = util::create_session(
config.opts,
config.crate_cfg,
config.crate_check_cfg,
config.file_loader,
- config.input_path.clone(),
+ CompilerIO {
+ input: config.input,
+ output_dir: config.output_dir,
+ output_file: config.output_file,
+ temps_dir,
+ },
config.lint_caps,
config.make_codegen_backend,
registry.clone(),
parse_sess_created(&mut sess.parse_sess);
}
- let temps_dir = sess.opts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
-
let compiler = Compiler {
sess: Lrc::new(sess),
codegen_backend: Lrc::new(codegen_backend),
- input: config.input,
- input_path: config.input_path,
- output_dir: config.output_dir,
- output_file: config.output_file,
- temps_dir,
register_lints: config.register_lints,
override_queries: config.override_queries,
};
#![feature(internal_output_capture)]
#![feature(thread_spawn_unchecked)]
#![feature(once_cell)]
+#![feature(try_blocks)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
use rustc_borrowck as mir_borrowck;
use rustc_codegen_ssa::traits::CodegenBackend;
use rustc_data_structures::parallel;
-use rustc_data_structures::steal::Steal;
use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
use rustc_errors::{ErrorGuaranteed, PResult};
use rustc_expand::base::{ExtCtxt, LintStoreExpand, ResolverExpand};
-use rustc_hir::def_id::StableCrateId;
+use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE};
use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore};
use rustc_metadata::creader::CStore;
use rustc_middle::arena::Arena;
use rustc_query_impl::{OnDiskCache, Queries as TcxQueries};
use rustc_resolve::{Resolver, ResolverArenas};
use rustc_session::config::{CrateType, Input, OutputFilenames, OutputType};
-use rustc_session::cstore::{MetadataLoader, MetadataLoaderDyn};
+use rustc_session::cstore::{CrateStoreDyn, MetadataLoader, MetadataLoaderDyn, Untracked};
use rustc_session::output::filename_for_input;
use rustc_session::search_paths::PathKind;
use rustc_session::{Limit, Session};
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::rc::Rc;
-use std::sync::LazyLock;
+use std::sync::{Arc, LazyLock};
use std::{env, fs, iter};
-pub fn parse<'a>(sess: &'a Session, input: &Input) -> PResult<'a, ast::Crate> {
- let krate = sess.time("parse_crate", || match input {
+pub fn parse<'a>(sess: &'a Session) -> PResult<'a, ast::Crate> {
+ let krate = sess.time("parse_crate", || match &sess.io.input {
Input::File(file) => parse_crate_from_file(file, &sess.parse_sess),
Input::Str { input, name } => {
parse_crate_from_source_str(name.clone(), input.clone(), &sess.parse_sess)
fn write_out_deps(
sess: &Session,
- boxed_resolver: &RefCell<BoxedResolver>,
+ cstore: &CrateStoreDyn,
outputs: &OutputFilenames,
out_filenames: &[PathBuf],
) {
}
let deps_filename = outputs.path(OutputType::DepInfo);
- let result = (|| -> io::Result<()> {
+ let result: io::Result<()> = try {
// Build a list of files used to compile the output and
// write Makefile-compatible dependency rules
let mut files: Vec<String> = sess
}
}
- boxed_resolver.borrow_mut().access(|resolver| {
- for cnum in resolver.cstore().crates_untracked() {
- let source = resolver.cstore().crate_source_untracked(cnum);
- if let Some((path, _)) = &source.dylib {
- files.push(escape_dep_filename(&path.display().to_string()));
- }
- if let Some((path, _)) = &source.rlib {
- files.push(escape_dep_filename(&path.display().to_string()));
- }
- if let Some((path, _)) = &source.rmeta {
- files.push(escape_dep_filename(&path.display().to_string()));
- }
+ let cstore = cstore.as_any().downcast_ref::<CStore>().unwrap();
+ for cnum in cstore.crates_untracked() {
+ let source = cstore.crate_source_untracked(cnum);
+ if let Some((path, _)) = &source.dylib {
+ files.push(escape_dep_filename(&path.display().to_string()));
}
- });
+ if let Some((path, _)) = &source.rlib {
+ files.push(escape_dep_filename(&path.display().to_string()));
+ }
+ if let Some((path, _)) = &source.rmeta {
+ files.push(escape_dep_filename(&path.display().to_string()));
+ }
+ }
}
let mut file = BufWriter::new(fs::File::create(&deps_filename)?);
writeln!(file)?;
}
}
-
- Ok(())
- })();
+ };
match result {
Ok(_) => {
}
}
-pub fn prepare_outputs(
- sess: &Session,
- compiler: &Compiler,
- krate: &ast::Crate,
- boxed_resolver: &RefCell<BoxedResolver>,
- crate_name: Symbol,
-) -> Result<OutputFilenames> {
+fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc<OutputFilenames> {
+ let sess = tcx.sess;
let _timer = sess.timer("prepare_outputs");
+ let (_, krate) = &*tcx.resolver_for_lowering(()).borrow();
+ let crate_name = tcx.crate_name(LOCAL_CRATE);
// FIXME: rustdoc passes &[] instead of &krate.attrs here
- let outputs = util::build_output_filenames(
- &compiler.input,
- &compiler.output_dir,
- &compiler.output_file,
- &compiler.temps_dir,
- &krate.attrs,
- sess,
- );
+ let outputs = util::build_output_filenames(&krate.attrs, sess);
let output_paths =
- generated_output_paths(sess, &outputs, compiler.output_file.is_some(), crate_name);
+ generated_output_paths(sess, &outputs, sess.io.output_file.is_some(), crate_name);
// Ensure the source file isn't accidentally overwritten during compilation.
- if let Some(ref input_path) = compiler.input_path {
+ if let Some(ref input_path) = sess.io.input.opt_path() {
if sess.opts.will_create_output_file() {
if output_contains_path(&output_paths, input_path) {
- let reported = sess.emit_err(InputFileWouldBeOverWritten { path: input_path });
- return Err(reported);
+ sess.emit_fatal(InputFileWouldBeOverWritten { path: input_path });
}
if let Some(ref dir_path) = output_conflicts_with_dir(&output_paths) {
- let reported =
- sess.emit_err(GeneratedFileConflictsWithDirectory { input_path, dir_path });
- return Err(reported);
+ sess.emit_fatal(GeneratedFileConflictsWithDirectory { input_path, dir_path });
}
}
}
- if let Some(ref dir) = compiler.temps_dir {
+ if let Some(ref dir) = sess.io.temps_dir {
if fs::create_dir_all(dir).is_err() {
- let reported = sess.emit_err(TempsDirError);
- return Err(reported);
+ sess.emit_fatal(TempsDirError);
}
}
- write_out_deps(sess, boxed_resolver, &outputs, &output_paths);
+ write_out_deps(sess, tcx.cstore_untracked(), &outputs, &output_paths);
let only_dep_info = sess.opts.output_types.contains_key(&OutputType::DepInfo)
&& sess.opts.output_types.len() == 1;
if !only_dep_info {
- if let Some(ref dir) = compiler.output_dir {
+ if let Some(ref dir) = sess.io.output_dir {
if fs::create_dir_all(dir).is_err() {
- let reported = sess.emit_err(OutDirError);
- return Err(reported);
+ sess.emit_fatal(OutDirError);
}
}
}
- Ok(outputs)
+ outputs.into()
}
pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
let providers = &mut Providers::default();
providers.analysis = analysis;
providers.hir_crate = rustc_ast_lowering::lower_to_hir;
+ providers.output_filenames = output_filenames;
proc_macro_decls::provide(providers);
rustc_const_eval::provide(providers);
rustc_middle::hir::provide(providers);
pub fn create_global_ctxt<'tcx>(
compiler: &'tcx Compiler,
lint_store: Lrc<LintStore>,
- krate: Lrc<ast::Crate>,
dep_graph: DepGraph,
- resolver: Rc<RefCell<BoxedResolver>>,
- outputs: OutputFilenames,
- crate_name: Symbol,
+ untracked: Untracked,
queries: &'tcx OnceCell<TcxQueries<'tcx>>,
global_ctxt: &'tcx OnceCell<GlobalCtxt<'tcx>>,
arena: &'tcx WorkerLocal<Arena<'tcx>>,
// incr. comp. yet.
dep_graph.assert_ignored();
- let resolver_outputs = BoxedResolver::to_resolver_outputs(resolver);
-
let sess = &compiler.session();
let query_result_on_disk_cache = rustc_incremental::load_query_result_cache(sess);
TcxQueries::new(local_providers, extern_providers, query_result_on_disk_cache)
});
- let ty::ResolverOutputs {
- global_ctxt: untracked_resolutions,
- ast_lowering: untracked_resolver_for_lowering,
- untracked,
- } = resolver_outputs;
-
let gcx = sess.time("setup_global_ctxt", || {
global_ctxt.get_or_init(move || {
TyCtxt::create_global_ctxt(
})
});
- let mut qcx = QueryContext { gcx };
- qcx.enter(|tcx| {
- let feed = tcx.feed_unit_query();
- feed.resolver_for_lowering(
- tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, krate))),
- );
- feed.resolutions(tcx.arena.alloc(untracked_resolutions));
- feed.output_filenames(tcx.arena.alloc(std::sync::Arc::new(outputs)));
- feed.features_query(sess.features_untracked());
- let feed = tcx.feed_local_crate();
- feed.crate_name(crate_name);
- });
- qcx
+ QueryContext { gcx }
}
/// Runs the resolution, type-checking, region checking and other
}
});
+ if tcx.sess.opts.unstable_opts.drop_tracking_mir {
+ tcx.hir().par_body_owners(|def_id| {
+ if let rustc_hir::def::DefKind::Generator = tcx.def_kind(def_id) {
+ tcx.ensure().mir_generator_witnesses(def_id);
+ tcx.ensure().check_generator_obligations(def_id);
+ }
+ });
+ }
+
sess.time("layout_testing", || layout_test::test_layout(tcx));
// Avoid overwhelming user with errors if borrow checking failed.
use rustc_lint::LintStore;
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::DepGraph;
-use rustc_middle::ty::{GlobalCtxt, TyCtxt};
+use rustc_middle::ty::{self, GlobalCtxt, TyCtxt};
use rustc_query_impl::Queries as TcxQueries;
use rustc_session::config::{self, OutputFilenames, OutputType};
use rustc_session::{output::find_crate_name, Session};
}
impl<'a, 'tcx> QueryResult<'a, QueryContext<'tcx>> {
- pub fn enter<T>(mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T {
+ pub fn enter<T>(&mut self, f: impl FnOnce(TyCtxt<'tcx>) -> T) -> T {
(*self.0).get_mut().enter(f)
}
}
register_plugins: Query<(ast::Crate, Lrc<LintStore>)>,
expansion: Query<(Lrc<ast::Crate>, Rc<RefCell<BoxedResolver>>, Lrc<LintStore>)>,
dep_graph: Query<DepGraph>,
- prepare_outputs: Query<OutputFilenames>,
global_ctxt: Query<QueryContext<'tcx>>,
ongoing_codegen: Query<Box<dyn Any>>,
}
register_plugins: Default::default(),
expansion: Default::default(),
dep_graph: Default::default(),
- prepare_outputs: Default::default(),
global_ctxt: Default::default(),
ongoing_codegen: Default::default(),
}
}
pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> {
- self.parse.compute(|| {
- passes::parse(self.session(), &self.compiler.input)
- .map_err(|mut parse_error| parse_error.emit())
- })
+ self.parse
+ .compute(|| passes::parse(self.session()).map_err(|mut parse_error| parse_error.emit()))
}
pub fn register_plugins(&self) -> Result<QueryResult<'_, (ast::Crate, Lrc<LintStore>)>> {
})
}
- pub fn crate_name(&self) -> Result<QueryResult<'_, Symbol>> {
+ fn crate_name(&self) -> Result<QueryResult<'_, Symbol>> {
self.crate_name.compute(|| {
Ok({
let parse_result = self.parse()?;
let krate = parse_result.borrow();
// parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches.
- find_crate_name(self.session(), &krate.attrs, &self.compiler.input)
+ find_crate_name(self.session(), &krate.attrs)
})
})
}
})
}
- pub fn prepare_outputs(&self) -> Result<QueryResult<'_, OutputFilenames>> {
- self.prepare_outputs.compute(|| {
- let expansion = self.expansion()?;
- let (krate, boxed_resolver, _) = &*expansion.borrow();
- let crate_name = *self.crate_name()?.borrow();
- passes::prepare_outputs(
- self.session(),
- self.compiler,
- krate,
- &*boxed_resolver,
- crate_name,
- )
- })
- }
-
pub fn global_ctxt(&'tcx self) -> Result<QueryResult<'_, QueryContext<'tcx>>> {
self.global_ctxt.compute(|| {
let crate_name = *self.crate_name()?.borrow();
- let outputs = self.prepare_outputs()?.steal();
- let dep_graph = self.dep_graph()?.borrow().clone();
let (krate, resolver, lint_store) = self.expansion()?.steal();
- Ok(passes::create_global_ctxt(
+
+ let ty::ResolverOutputs {
+ untracked,
+ global_ctxt: untracked_resolutions,
+ ast_lowering: untracked_resolver_for_lowering,
+ } = BoxedResolver::to_resolver_outputs(resolver);
+
+ let mut qcx = passes::create_global_ctxt(
self.compiler,
lint_store,
- krate,
- dep_graph,
- resolver,
- outputs,
- crate_name,
+ self.dep_graph()?.steal(),
+ untracked,
&self.queries,
&self.gcx,
&self.arena,
&self.hir_arena,
- ))
+ );
+
+ qcx.enter(|tcx| {
+ let feed = tcx.feed_unit_query();
+ feed.resolver_for_lowering(
+ tcx.arena.alloc(Steal::new((untracked_resolver_for_lowering, krate))),
+ );
+ feed.resolutions(tcx.arena.alloc(untracked_resolutions));
+ feed.features_query(tcx.sess.features_untracked());
+ let feed = tcx.feed_local_crate();
+ feed.crate_name(crate_name);
+ });
+ Ok(qcx)
})
}
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
use rustc_session::config::rustc_optgroups;
+use rustc_session::config::Input;
use rustc_session::config::TraitSolver;
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
use rustc_session::config::{
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
+use rustc_session::CompilerIO;
use rustc_session::{build_session, getopts, Session};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym;
+use rustc_span::FileName;
use rustc_span::SourceFileHashAlgorithm;
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
let registry = registry::Registry::new(&[]);
let (sessopts, cfg) = build_session_options_and_crate_config(matches);
- let sess = build_session(sessopts, None, None, registry, Default::default(), None, None);
+ let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
+ let io = CompilerIO {
+ input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
+ output_dir: None,
+ output_file: None,
+ temps_dir,
+ };
+ let sess = build_session(sessopts, io, None, registry, Default::default(), None, None);
(sess, cfg)
}
tracked!(teach, true);
tracked!(thinlto, Some(true));
tracked!(thir_unsafeck, true);
+ tracked!(tiny_const_eval_limit, true);
tracked!(tls_model, Some(TlsModel::GeneralDynamic));
tracked!(trait_solver, TraitSolver::Chalk);
tracked!(translate_remapped_path_to_local_path, false);
use rustc_session as session;
use rustc_session::config::CheckCfg;
use rustc_session::config::{self, CrateType};
-use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
+use rustc_session::config::{ErrorOutputType, OutputFilenames};
use rustc_session::filesearch::sysroot_candidates;
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::CrateConfig;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::FileLoader;
use rustc_span::symbol::{sym, Symbol};
+use session::CompilerIO;
use std::env;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::mem;
cfg: FxHashSet<(String, Option<String>)>,
check_cfg: CheckCfg,
file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
- input_path: Option<PathBuf>,
+ io: CompilerIO,
lint_caps: FxHashMap<lint::LintId, lint::Level>,
make_codegen_backend: Option<
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
let mut sess = session::build_session(
sopts,
- input_path,
+ io,
bundle,
descriptions,
lint_caps,
base
}
-pub fn build_output_filenames(
- input: &Input,
- odir: &Option<PathBuf>,
- ofile: &Option<PathBuf>,
- temps_dir: &Option<PathBuf>,
- attrs: &[ast::Attribute],
- sess: &Session,
-) -> OutputFilenames {
- match *ofile {
+pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
+ match sess.io.output_file {
None => {
// "-" as input file will cause the parser to read from stdin so we
// have to make up a name
// We want to toss everything after the final '.'
- let dirpath = (*odir).as_ref().cloned().unwrap_or_default();
+ let dirpath = sess.io.output_dir.clone().unwrap_or_default();
// If a crate name is present, we use it as the link name
let stem = sess
.crate_name
.clone()
.or_else(|| rustc_attr::find_crate_name(sess, attrs).map(|n| n.to_string()))
- .unwrap_or_else(|| input.filestem().to_owned());
+ .unwrap_or_else(|| sess.io.input.filestem().to_owned());
OutputFilenames::new(
dirpath,
stem,
None,
- temps_dir.clone(),
+ sess.io.temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
)
}
Some(out_file.clone())
};
- if *odir != None {
+ if sess.io.output_dir != None {
sess.warn("ignoring --out-dir flag due to -o flag");
}
out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
ofile,
- temps_dir.clone(),
+ sess.io.temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
)
//!
//! The idea with `rustc_lexer` is to make a reusable library,
//! by separating out pure lexing and rustc-specific concerns, like spans,
-//! error reporting, and interning. So, rustc_lexer operates directly on `&str`,
+//! error reporting, and interning. So, rustc_lexer operates directly on `&str`,
//! produces simple tokens which are a pair of type-tag and a bit of original text,
//! and does not report errors, instead storing them as flags on the token.
//!
let tail = &tail[first_non_space..];
if let Some(c) = tail.chars().nth(0) {
// For error reporting, we would like the span to contain the character that was not
- // skipped. The +1 is necessary to account for the leading \ that started the escape.
+ // skipped. The +1 is necessary to account for the leading \ that started the escape.
let end = start + first_non_space + c.len_utf8() + 1;
if c.is_whitespace() {
callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning));
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
use rustc_hir::intravisit::FnKind as HirFnKind;
-use rustc_hir::{
- Body, FnDecl, ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin,
-};
+use rustc_hir::{Body, FnDecl, ForeignItemKind, GenericParamKind, Node, PatKind, PredicateOrigin};
use rustc_index::vec::Idx;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
use rustc_span::{BytePos, InnerSpan, Span};
use rustc_target::abi::{Abi, VariantIdx};
use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt};
-use rustc_trait_selection::traits::{self, misc::can_type_implement_copy, EvaluationResult};
+use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy};
use crate::nonstandard_style::{method_context, MethodLateContext};
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
// If the method is an impl for a trait, don't doc.
- if method_context(cx, impl_item.hir_id()) == MethodLateContext::TraitImpl {
+ let context = method_context(cx, impl_item.owner_id.def_id);
+ if context == MethodLateContext::TraitImpl {
return;
}
// If the method is an impl for an item with docs_hidden, don't doc.
- if method_context(cx, impl_item.hir_id()) == MethodLateContext::PlainImpl {
+ if context == MethodLateContext::PlainImpl {
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
let impl_ty = cx.tcx.type_of(parent);
let outerdef = match impl_ty.kind() {
// We shouldn't recommend implementing `Copy` on stateful things,
// such as iterators.
- if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator) {
- if cx.tcx.infer_ctxt().build().type_implements_trait(iter_trait, [ty], param_env)
- == EvaluationResult::EvaluatedToOk
- {
- return;
- }
+ if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
+ && cx.tcx
+ .infer_ctxt()
+ .build()
+ .type_implements_trait(iter_trait, [ty], param_env)
+ .must_apply_modulo_regions()
+ {
+ return;
}
// Default value of clippy::trivially_copy_pass_by_ref
}
}
- if can_type_implement_copy(
+ if type_allowed_to_implement_copy(
cx.tcx,
param_env,
ty,
- traits::ObligationCause::misc(item.span, item.hir_id()),
+ traits::ObligationCause::misc(item.span, item.owner_id.def_id),
)
.is_ok()
{
_: &'tcx FnDecl<'_>,
_: &'tcx Body<'_>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
if fn_kind.asyncness() == IsAsync::Async
&& !cx.tcx.features().closure_track_caller
- && let attrs = cx.tcx.hir().attrs(hir_id)
// Now, check if the function has the `#[track_caller]` attribute
- && let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller))
- {
- cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller {
- label: span,
- parse_sess: &cx.tcx.sess.parse_sess,
- });
- }
+ && let Some(attr) = cx.tcx.get_attr(def_id.to_def_id(), sym::track_caller)
+ {
+ cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller {
+ label: span,
+ parse_sess: &cx.tcx.sess.parse_sess,
+ });
+ }
}
}
dropped_predicate_count += 1;
}
- if drop_predicate && !in_where_clause {
- lint_spans.push(predicate_span);
- } else if drop_predicate && i + 1 < num_predicates {
- // If all the bounds on a predicate were inferable and there are
- // further predicates, we want to eat the trailing comma.
- let next_predicate_span = hir_generics.predicates[i + 1].span();
- where_lint_spans.push(predicate_span.to(next_predicate_span.shrink_to_lo()));
+ if drop_predicate {
+ if !in_where_clause {
+ lint_spans.push(predicate_span);
+ } else if predicate_span.from_expansion() {
+ // Don't try to extend the span if it comes from a macro expansion.
+ where_lint_spans.push(predicate_span);
+ } else if i + 1 < num_predicates {
+ // If all the bounds on a predicate were inferable and there are
+ // further predicates, we want to eat the trailing comma.
+ let next_predicate_span = hir_generics.predicates[i + 1].span();
+ if next_predicate_span.from_expansion() {
+ where_lint_spans.push(predicate_span);
+ } else {
+ where_lint_spans
+ .push(predicate_span.to(next_predicate_span.shrink_to_lo()));
+ }
+ } else {
+ // Eat the optional trailing comma after the last predicate.
+ let where_span = hir_generics.where_clause_span;
+ if where_span.from_expansion() {
+ where_lint_spans.push(predicate_span);
+ } else {
+ where_lint_spans.push(predicate_span.to(where_span.shrink_to_hi()));
+ }
+ }
} else {
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
predicate_span.shrink_to_lo(),
Applicability::MaybeIncorrect
};
+ // Due to macros, there might be several predicates with the same span
+ // and we only want to suggest removing them once.
+ lint_spans.sort_unstable();
+ lint_spans.dedup();
+
cx.emit_spanned_lint(
EXPLICIT_OUTLIVES_REQUIREMENTS,
lint_spans.clone(),
/// the symbol should be reported as a clashing declaration.
// FIXME: Technically, we could just store a &'tcx str here without issue; however, the
// `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
- seen_decls: FxHashMap<Symbol, HirId>,
+ seen_decls: FxHashMap<Symbol, hir::OwnerId>,
}
/// Differentiate between whether the name for an extern decl came from the link_name attribute or
pub(crate) fn new() -> Self {
ClashingExternDeclarations { seen_decls: FxHashMap::default() }
}
+
/// Insert a new foreign item into the seen set. If a symbol with the same name already exists
/// for the item, return its HirId without updating the set.
- fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<HirId> {
+ fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<hir::OwnerId> {
let did = fi.owner_id.to_def_id();
let instance = Instance::new(did, ty::List::identity_for_item(tcx, did));
let name = Symbol::intern(tcx.symbol_name(instance).name);
- if let Some(&hir_id) = self.seen_decls.get(&name) {
+ if let Some(&existing_id) = self.seen_decls.get(&name) {
// Avoid updating the map with the new entry when we do find a collision. We want to
// make sure we're always pointing to the first definition as the previous declaration.
// This lets us avoid emitting "knock-on" diagnostics.
- Some(hir_id)
+ Some(existing_id)
} else {
- self.seen_decls.insert(name, fi.hir_id())
+ self.seen_decls.insert(name, fi.owner_id)
}
}
impl_lint_pass!(ClashingExternDeclarations => [CLASHING_EXTERN_DECLARATIONS]);
impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
+ #[instrument(level = "trace", skip(self, cx))]
fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, this_fi: &hir::ForeignItem<'_>) {
- trace!("ClashingExternDeclarations: check_foreign_item: {:?}", this_fi);
if let ForeignItemKind::Fn(..) = this_fi.kind {
let tcx = cx.tcx;
- if let Some(existing_hid) = self.insert(tcx, this_fi) {
- let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid));
+ if let Some(existing_did) = self.insert(tcx, this_fi) {
+ let existing_decl_ty = tcx.type_of(existing_did);
let this_decl_ty = tcx.type_of(this_fi.owner_id);
debug!(
"ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
- existing_hid, existing_decl_ty, this_fi.owner_id, this_decl_ty
+ existing_did, existing_decl_ty, this_fi.owner_id, this_decl_ty
);
// Check that the declarations match.
if !Self::structurally_same_type(
this_decl_ty,
CItemKind::Declaration,
) {
- let orig_fi = tcx.hir().expect_foreign_item(existing_hid.expect_owner());
+ let orig_fi = tcx.hir().expect_foreign_item(existing_did);
let orig = Self::name_of_extern_decl(tcx, orig_fi);
// We want to ensure that we use spans for both decls that include where the
debug!(?param_span, ?use_span, ?deletion_span);
db.span_label(param_span, "this lifetime...");
db.span_label(use_span, "...is used only here");
- let msg = "elide the single-use lifetime";
- let (use_span, replace_lt) = if elide {
- let use_span = sess.source_map().span_extend_while(
- use_span,
- char::is_whitespace,
- ).unwrap_or(use_span);
- (use_span, String::new())
- } else {
- (use_span, "'_".to_owned())
- };
- db.multipart_suggestion(
- msg,
- vec![(deletion_span, String::new()), (use_span, replace_lt)],
- Applicability::MachineApplicable,
- );
+ if let Some(deletion_span) = deletion_span {
+ let msg = "elide the single-use lifetime";
+ let (use_span, replace_lt) = if elide {
+ let use_span = sess.source_map().span_extend_while(
+ use_span,
+ char::is_whitespace,
+ ).unwrap_or(use_span);
+ (use_span, String::new())
+ } else {
+ (use_span, "'_".to_owned())
+ };
+ debug!(?deletion_span, ?use_span);
+ db.multipart_suggestion(
+ msg,
+ vec![(deletion_span, String::new()), (use_span, replace_lt)],
+ Applicability::MachineApplicable,
+ );
+ }
},
BuiltinLintDiagnostics::SingleUseLifetime {
param_span: _,
deletion_span,
} => {
debug!(?deletion_span);
- db.span_suggestion(
- deletion_span,
- "elide the unused lifetime",
- "",
- Applicability::MachineApplicable,
- );
+ if let Some(deletion_span) = deletion_span {
+ db.span_suggestion(
+ deletion_span,
+ "elide the unused lifetime",
+ "",
+ Applicability::MachineApplicable,
+ );
+ }
},
BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
db.span_label(named_arg_sp, "this named argument is referred to by position in formatting string");
}
fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
+ lint_callback!(self, enter_where_predicate, p);
ast_visit::walk_where_predicate(self, p);
+ lint_callback!(self, exit_where_predicate, p);
}
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
let ty = substs.type_at(0);
let infcx = cx.tcx.infer_ctxt().build();
+ let body_def_id = cx.tcx.hir().body_owner_def_id(body_id);
let cause = ObligationCause::new(
span,
- body_id.hir_id,
+ body_def_id,
rustc_infer::traits::ObligationCauseCode::MiscObligation,
);
let errors = rustc_trait_selection::traits::fully_solve_bound(
},
None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
}
- } else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
+ } else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(ty) = is_ty_or_ty_ctxt(cx, &path) {
cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified {
- ty: t.clone(),
+ ty,
suggestion: path.span,
});
}
self.context.last_node_with_lint_attrs = prev;
}
- fn with_param_env<F>(&mut self, id: hir::HirId, f: F)
+ fn with_param_env<F>(&mut self, id: hir::OwnerId, f: F)
where
F: FnOnce(&mut Self),
{
let old_param_env = self.context.param_env;
- self.context.param_env =
- self.context.tcx.param_env(self.context.tcx.hir().local_def_id(id));
+ self.context.param_env = self.context.tcx.param_env(id);
f(self);
self.context.param_env = old_param_env;
}
let old_cached_typeck_results = self.context.cached_typeck_results.take();
let old_enclosing_body = self.context.enclosing_body.take();
self.with_lint_attrs(it.hir_id(), |cx| {
- cx.with_param_env(it.hir_id(), |cx| {
+ cx.with_param_env(it.owner_id, |cx| {
lint_callback!(cx, check_item, it);
hir_visit::walk_item(cx, it);
lint_callback!(cx, check_item_post, it);
fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
self.with_lint_attrs(it.hir_id(), |cx| {
- cx.with_param_env(it.hir_id(), |cx| {
+ cx.with_param_env(it.owner_id, |cx| {
lint_callback!(cx, check_foreign_item, it);
hir_visit::walk_foreign_item(cx, it);
});
decl: &'tcx hir::FnDecl<'tcx>,
body_id: hir::BodyId,
span: Span,
- id: hir::HirId,
+ id: LocalDefId,
) {
// Wrap in typeck results here, not just in visit_nested_body,
// in order for `check_fn` to be able to use them.
let generics = self.context.generics.take();
self.context.generics = Some(&trait_item.generics);
self.with_lint_attrs(trait_item.hir_id(), |cx| {
- cx.with_param_env(trait_item.hir_id(), |cx| {
+ cx.with_param_env(trait_item.owner_id, |cx| {
lint_callback!(cx, check_trait_item, trait_item);
hir_visit::walk_trait_item(cx, trait_item);
});
let generics = self.context.generics.take();
self.context.generics = Some(&impl_item.generics);
self.with_lint_attrs(impl_item.hir_id(), |cx| {
- cx.with_param_env(impl_item.hir_id(), |cx| {
+ cx.with_param_env(impl_item.owner_id, |cx| {
lint_callback!(cx, check_impl_item, impl_item);
hir_visit::walk_impl_item(cx, impl_item);
lint_callback!(cx, check_impl_item_post, impl_item);
}
}
-/// Specifications found at this position in the stack. This map only represents the lints
+/// Specifications found at this position in the stack. This map only represents the lints
/// found for one set of attributes (like `shallow_lint_levels_on` does).
///
/// We store the level specifications as a linked list.
match attrs.map.range(..) {
// There is only something to do if there are attributes at all.
[] => {}
- // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
+ // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
[(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
// Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
// a standard visit.
mod levels;
mod lints;
mod methods;
+mod multiple_supertrait_upcastable;
mod non_ascii_idents;
mod non_fmt_panic;
mod nonstandard_style;
use internal::*;
use let_underscore::*;
use methods::*;
+use multiple_supertrait_upcastable::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
use nonstandard_style::*;
[
pub BuiltinCombinedEarlyLintPass,
[
- UnusedParens: UnusedParens,
+ UnusedParens: UnusedParens::new(),
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
InvalidAtomicOrdering: InvalidAtomicOrdering,
NamedAsmLabels: NamedAsmLabels,
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
+ MultipleSupertraitUpcastable: MultipleSupertraitUpcastable,
]
]
);
) -> rustc_errors::SubdiagnosticMessage,
{
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a
- // bound. Let's see if this type does that.
+ // bound. Let's see if this type does that.
// We use a HIR visitor to walk the type.
use rustc_hir::intravisit::{self, Visitor};
pub unwrap: Span,
}
+// multiple_supertrait_upcastable.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_multple_supertrait_upcastable)]
+pub struct MultipleSupertraitUpcastable {
+ pub ident: Ident,
+}
+
// non_ascii_idents.rs
#[derive(LintDiagnostic)]
#[diag(lint_identifier_non_ascii_char)]
--- /dev/null
+use crate::{LateContext, LateLintPass, LintContext};
+
+use rustc_hir as hir;
+use rustc_span::sym;
+
+declare_lint! {
+ /// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple
+ /// supertraits.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// trait A {}
+ /// trait B {}
+ ///
+ /// #[warn(multiple_supertrait_upcastable)]
+ /// trait C: A + B {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// To support upcasting with multiple supertraits, we need to store multiple vtables and this
+ /// can result in extra space overhead, even if no code actually uses upcasting.
+ /// This lint allows users to identify when such scenarios occur and to decide whether the
+ /// additional overhead is justified.
+ pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
+ Allow,
+ "detect when an object-safe trait has multiple supertraits",
+ @feature_gate = sym::multiple_supertrait_upcastable;
+}
+
+declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);
+
+impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+ let def_id = item.owner_id.to_def_id();
+ // NOTE(nbdd0121): use `object_safety_violations` instead of `check_is_object_safe` because
+ // the latter will report `where_clause_object_safety` lint.
+ if let hir::ItemKind::Trait(_, _, _, _, _) = item.kind
+ && cx.tcx.object_safety_violations(def_id).is_empty()
+ {
+ let direct_super_traits_iter = cx.tcx
+ .super_predicates_of(def_id)
+ .predicates
+ .into_iter()
+ .filter_map(|(pred, _)| pred.to_opt_poly_trait_pred());
+ if direct_super_traits_iter.count() > 1 {
+ cx.emit_spanned_lint(
+ MULTIPLE_SUPERTRAIT_UPCASTABLE,
+ cx.tcx.def_span(def_id),
+ crate::lints::MultipleSupertraitUpcastable {
+ ident: item.ident
+ },
+ );
+ }
+ }
+ }
+}
use rustc_hir::intravisit::FnKind;
use rustc_hir::{GenericParamKind, PatKind};
use rustc_middle::ty;
-use rustc_span::symbol::sym;
-use rustc_span::{symbol::Ident, BytePos, Span};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{BytePos, Span};
use rustc_target::spec::abi::Abi;
#[derive(PartialEq)]
PlainImpl,
}
-pub fn method_context(cx: &LateContext<'_>, id: hir::HirId) -> MethodLateContext {
- let def_id = cx.tcx.hir().local_def_id(id);
- let item = cx.tcx.associated_item(def_id);
+pub fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext {
+ let item = cx.tcx.associated_item(id);
match item.container {
ty::TraitContainer => MethodLateContext::TraitAutoImpl,
ty::ImplContainer => match cx.tcx.impl_trait_ref(item.container_id(cx.tcx)) {
_: &hir::FnDecl<'_>,
_: &hir::Body<'_>,
_: Span,
- id: hir::HirId,
+ id: LocalDefId,
) {
- let attrs = cx.tcx.hir().attrs(id);
match &fk {
FnKind::Method(ident, sig, ..) => match method_context(cx, id) {
MethodLateContext::PlainImpl => {
- if sig.header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle)
+ if sig.header.abi != Abi::Rust
+ && cx.tcx.has_attr(id.to_def_id(), sym::no_mangle)
{
return;
}
},
FnKind::ItemFn(ident, _, header) => {
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
- if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) {
+ if header.abi != Abi::Rust && cx.tcx.has_attr(id.to_def_id(), sym::no_mangle) {
return;
}
self.check_snake_case(cx, "function", ident);
cx.emit_spanned_lint(
PASS_BY_VALUE,
ty.span,
- PassByValueDiag { ty: t.clone(), suggestion: ty.span },
+ PassByValueDiag { ty: t, suggestion: ty.span },
);
}
}
use rustc_hir as hir;
use rustc_session::lint::builtin::HardwiredLints;
use rustc_session::lint::LintPass;
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::Ident;
use rustc_span::Span;
b: &'tcx hir::FnDecl<'tcx>,
c: &'tcx hir::Body<'tcx>,
d: Span,
- e: hir::HirId);
+ e: LocalDefId);
fn check_trait_item(a: &'tcx hir::TraitItem<'tcx>);
fn check_impl_item(a: &'tcx hir::ImplItem<'tcx>);
fn check_impl_item_post(a: &'tcx hir::ImplItem<'tcx>);
/// Counterpart to `enter_lint_attrs`.
fn exit_lint_attrs(a: &[ast::Attribute]);
+
+ fn enter_where_predicate(a: &ast::WherePredicate);
+ fn exit_where_predicate(a: &ast::WherePredicate);
]);
)
}
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map;
use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Placeholder(..)
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.has_opaque_types() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
if let ty::Alias(ty::Opaque, ..) = ty.kind() {
}
}
- fn check_foreign_fn(&mut self, id: hir::HirId, decl: &hir::FnDecl<'_>) {
- let def_id = self.cx.tcx.hir().local_def_id(id);
- let sig = self.cx.tcx.fn_sig(def_id);
+ fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
+ let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
let sig = self.cx.tcx.erase_late_bound_regions(sig);
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
}
}
- fn check_foreign_static(&mut self, id: hir::HirId, span: Span) {
- let def_id = self.cx.tcx.hir().local_def_id(id);
- let ty = self.cx.tcx.type_of(def_id);
+ fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
+ let ty = self.cx.tcx.type_of(id);
self.check_type_for_ffi_and_report_errors(span, ty, true, false);
}
if !vis.is_internal_abi(abi) {
match it.kind {
hir::ForeignItemKind::Fn(ref decl, _, _) => {
- vis.check_foreign_fn(it.hir_id(), decl);
+ vis.check_foreign_fn(it.owner_id.def_id, decl);
}
hir::ForeignItemKind::Static(ref ty, _) => {
- vis.check_foreign_static(it.hir_id(), ty.span);
+ vis.check_foreign_static(it.owner_id, ty.span);
}
hir::ForeignItemKind::Type => (),
}
decl: &'tcx hir::FnDecl<'_>,
_: &'tcx hir::Body<'_>,
_: Span,
- hir_id: hir::HirId,
+ id: LocalDefId,
) {
use hir::intravisit::FnKind;
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
if !vis.is_internal_abi(abi) {
- vis.check_foreign_fn(hir_id, decl);
+ vis.check_foreign_fn(id, decl);
}
}
}
"`if`, `match`, `while` and `return` do not need parentheses"
}
-declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
+pub struct UnusedParens {
+ with_self_ty_parens: bool,
+}
+
+impl UnusedParens {
+ pub fn new() -> Self {
+ Self { with_self_ty_parens: false }
+ }
+}
+
+impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
impl UnusedDelimLint for UnusedParens {
const DELIM_STR: &'static str = "parentheses";
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
- if let ast::TyKind::Paren(r) = &ty.kind {
- match &r.kind {
- ast::TyKind::TraitObject(..) => {}
- ast::TyKind::BareFn(b) if b.generic_params.len() > 0 => {}
- ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
- ast::TyKind::Array(_, len) => {
- self.check_unused_delims_expr(
- cx,
- &len.value,
- UnusedDelimsCtx::ArrayLenExpr,
- false,
- None,
- None,
- );
- }
- _ => {
- let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
- Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
- } else {
- None
- };
- self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ match &ty.kind {
+ ast::TyKind::Array(_, len) => {
+ self.check_unused_delims_expr(
+ cx,
+ &len.value,
+ UnusedDelimsCtx::ArrayLenExpr,
+ false,
+ None,
+ None,
+ );
+ }
+ ast::TyKind::Paren(r) => {
+ match &r.kind {
+ ast::TyKind::TraitObject(..) => {}
+ ast::TyKind::BareFn(b)
+ if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
+ ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
+ _ => {
+ let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
+ Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
+ } else {
+ None
+ };
+ self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ }
}
+ self.with_self_ty_parens = false;
}
+ _ => {}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
+
+ fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
+ use rustc_ast::{WhereBoundPredicate, WherePredicate};
+ if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+ bounded_ty,
+ bound_generic_params,
+ ..
+ }) = pred &&
+ let ast::TyKind::Paren(_) = &bounded_ty.kind &&
+ bound_generic_params.is_empty() {
+ self.with_self_ty_parens = true;
+ }
+ }
+
+ fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
+ assert!(!self.with_self_ty_parens);
+ }
}
declare_lint! {
// ```
// - the block has no attribute and was not created inside a macro
// - if the block is an `anon_const`, the inner expr must be a literal
- // (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`)
- //
+ // not created by a macro, i.e. do not lint on:
+ // ```
+ // struct A<const N: usize>;
+ // let _: A<{ 2 + 3 }>;
+ // let _: A<{produces_literal!()}>;
+ // ```
// FIXME(const_generics): handle paths when #67075 is fixed.
if let [stmt] = inner.stmts.as_slice() {
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
&& (ctx != UnusedDelimsCtx::AnonConst
- || matches!(expr.kind, ast::ExprKind::Lit(_)))
+ || (matches!(expr.kind, ast::ExprKind::Lit(_))
+ && !expr.span.from_expansion()))
&& !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
///
/// ### Example
///
- /// ```rust
+ /// ```rust,compile_fail
/// pub enum Enum {
/// Foo,
/// Bar,
/// [identifier pattern]: https://doc.rust-lang.org/reference/patterns.html#identifier-patterns
/// [path pattern]: https://doc.rust-lang.org/reference/patterns.html#path-patterns
pub BINDINGS_WITH_VARIANT_NAME,
- Warn,
+ Deny,
"detects pattern bindings with the same name as one of the matched variants"
}
};
}
+declare_lint! {
+ /// The `proc_macro_derive_resolution_fallback` lint detects proc macro
+ /// derives using inaccessible names from parent modules.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,ignore (proc-macro)
+ /// // foo.rs
+ /// #![crate_type = "proc-macro"]
+ ///
+ /// extern crate proc_macro;
+ ///
+ /// use proc_macro::*;
+ ///
+ /// #[proc_macro_derive(Foo)]
+ /// pub fn foo1(a: TokenStream) -> TokenStream {
+ /// drop(a);
+ /// "mod __bar { static mut BAR: Option<Something> = None; }".parse().unwrap()
+ /// }
+ /// ```
+ ///
+ /// ```rust,ignore (needs-dependency)
+ /// // bar.rs
+ /// #[macro_use]
+ /// extern crate foo;
+ ///
+ /// struct Something;
+ ///
+ /// #[derive(Foo)]
+ /// struct Another;
+ ///
+ /// fn main() {}
+ /// ```
+ ///
+ /// This will produce:
+ ///
+ /// ```text
+ /// warning: cannot find type `Something` in this scope
+ /// --> src/main.rs:8:10
+ /// |
+ /// 8 | #[derive(Foo)]
+ /// | ^^^ names from parent modules are not accessible without an explicit import
+ /// |
+ /// = note: `#[warn(proc_macro_derive_resolution_fallback)]` on by default
+ /// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ /// = note: for more information, see issue #50504 <https://github.com/rust-lang/rust/issues/50504>
+ /// ```
+ ///
+ /// ### Explanation
+ ///
+ /// If a proc-macro generates a module, the compiler unintentionally
+ /// allowed items in that module to refer to items in the crate root
+ /// without importing them. This is a [future-incompatible] lint to
+ /// transition this to a hard error in the future. See [issue #50504] for
+ /// more details.
+ ///
+ /// [issue #50504]: https://github.com/rust-lang/rust/issues/50504
+ /// [future-incompatible]: ../index.md#future-incompatible-lints
+ pub PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+ Deny,
+ "detects proc macro derives using inaccessible names from parent modules",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #83583 <https://github.com/rust-lang/rust/issues/83583>",
+ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
+ };
+}
+
declare_lint! {
/// The `macro_use_extern_crate` lint detects the use of the
/// [`macro_use` attribute].
"trailing semicolon in macro body used as expression",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #79813 <https://github.com/rust-lang/rust/issues/79813>",
+ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
};
}
UNSTABLE_NAME_COLLISIONS,
IRREFUTABLE_LET_PATTERNS,
WHERE_CLAUSES_OBJECT_SAFETY,
+ PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
MACRO_USE_EXTERN_CRATE,
MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS,
ILL_FORMED_ATTRIBUTE_INPUT,
///
/// ### Explanation
///
- /// Previously, there were very like checks being performed on `#[doc(..)]`
- /// unlike the other attributes. It'll now catch all the issues that it
- /// silently ignored previously.
+ /// Previously, incorrect usage of the `#[doc(..)]` attribute was not
+ /// being validated. Usually these should be rejected as a hard error,
+ /// but this lint was introduced to avoid breaking any existing
+ /// crates which included them.
+ ///
+ /// This is a [future-incompatible] lint to transition this to a hard
+ /// error in the future. See [issue #82730] for more details.
+ ///
+ /// [issue #82730]: https://github.com/rust-lang/rust/issues/82730
pub INVALID_DOC_ATTRIBUTES,
Warn,
"detects invalid `#[doc(...)]` attributes",
extern crate rustc_macros;
pub use self::Level::*;
-use rustc_ast::node_id::{NodeId, NodeMap};
+use rustc_ast::node_id::NodeId;
use rustc_ast::{AttrId, Attribute};
+use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_error_messages::{DiagnosticMessage, MultiSpan};
use rustc_hir::HashStableContext;
param_span: Span,
/// Span of the code that should be removed when eliding this lifetime.
/// This span should include leading or trailing comma.
- deletion_span: Span,
+ deletion_span: Option<Span>,
/// Span of the single use, or None if the lifetime is never used.
/// If true, the lifetime will be fully elided.
use_span: Option<(Span, bool)>,
#[derive(Default)]
pub struct LintBuffer {
- pub map: NodeMap<Vec<BufferedEarlyLint>>,
+ pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
}
impl LintBuffer {
extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) {
// Initializing the command-line options more than once is not allowed. So,
- // check if they've already been initialized. (This could happen if we're
+ // check if they've already been initialized. (This could happen if we're
// being called from rustpkg, for example). If the arguments change, then
// that's just kinda unfortunate.
static bool Initialized = false;
}
// This is what we used to parse upstream bitcode for actual ThinLTO
-// processing. We'll call this once per module optimized through ThinLTO, and
+// processing. We'll call this once per module optimized through ThinLTO, and
// it'll be called concurrently on many threads.
extern "C" LLVMModuleRef
LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
return LLVMBFloatTypeKind;
case Type::X86_AMXTyID:
return LLVMX86_AMXTypeKind;
-#if LLVM_VERSION_GE(15, 0) && LLVM_VERSION_LT(16, 0)
- case Type::DXILPointerTyID:
- report_fatal_error("Rust does not support DirectX typed pointers.");
- break;
-#endif
-#if LLVM_VERSION_GE(16, 0)
- case Type::TypedPointerTyID:
- report_fatal_error("Rust does not support typed pointers.");
- break;
-#endif
+ default:
+ {
+ std::string error;
+ llvm::raw_string_ostream stream(error);
+ stream << "Rust does not support the TypeID: " << unwrap(Ty)->getTypeID()
+ << " for the type: " << *unwrap(Ty);
+ stream.flush();
+ report_fatal_error(error.c_str());
+ }
}
- report_fatal_error("Unhandled TypeID.");
}
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
};
use fluent_bundle::{FluentBundle, FluentError, FluentResource};
use fluent_syntax::{
- ast::{Attribute, Entry, Identifier, Message},
+ ast::{
+ Attribute, Entry, Expression, Identifier, InlineExpression, Message, Pattern,
+ PatternElement,
+ },
parser::ParserError,
};
use proc_macro::{Diagnostic, Level, Span};
};
let mut constants = TokenStream::new();
+ let mut messagerefs = Vec::new();
for entry in resource.entries() {
let span = res.krate.span();
- if let Entry::Message(Message { id: Identifier { name }, attributes, .. }) = entry {
+ if let Entry::Message(Message { id: Identifier { name }, attributes, value, .. }) =
+ entry
+ {
let _ = previous_defns.entry(name.to_string()).or_insert(path_span);
if name.contains('-') {
.emit();
}
+ if let Some(Pattern { elements }) = value {
+ for elt in elements {
+ if let PatternElement::Placeable {
+ expression:
+ Expression::Inline(InlineExpression::MessageReference { id, .. }),
+ } = elt
+ {
+ messagerefs.push((id.name, *name));
+ }
+ }
+ }
+
// Require that the message name starts with the crate name
// `hir_typeck_foo_bar` (in `hir_typeck.ftl`)
// `const_eval_baz` (in `const_eval.ftl`)
}
}
+ for (mref, name) in messagerefs.into_iter() {
+ if !previous_defns.contains_key(mref) {
+ Diagnostic::spanned(
+ path_span,
+ Level::Error,
+ format!("referenced message `{mref}` does not exist (in message `{name}`)"),
+ )
+ .help(&format!("you may have meant to use a variable reference (`{{${mref}}}`)"))
+ .emit();
+ }
+ }
+
if let Err(errs) = bundle.add_resource(resource) {
for e in errs {
match e {
__visitor: &mut __V
) -> ::std::ops::ControlFlow<__V::BreakTy> {
match *self { #body_visit }
- ::std::ops::ControlFlow::CONTINUE
+ ::std::ops::ControlFlow::Continue(())
}
},
)
[lib]
[dependencies]
+bitflags = "1.2.1"
libloading = "0.7.1"
odht = { version = "0.3.1", features = ["nightly"] }
snap = "1"
let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
// If the user requests metadata as output, rename `metadata_filename`
- // to the expected output `out_filename`. The match above should ensure
+ // to the expected output `out_filename`. The match above should ensure
// this file always exists.
let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
let (metadata_filename, metadata_tmpdir) = if need_metadata_file {
Err(MetadataError::LoadFailure(err)) => {
info!("no metadata found: {}", err);
// The file was present and created by the same compiler version, but we
- // couldn't load it for some reason. Give a hard error instead of silently
+ // couldn't load it for some reason. Give a hard error instead of silently
// ignoring it, but only if we would have given an error anyway.
self.crate_rejections
.via_invalid
}
// Update kind and, optionally, the name of all native libraries
- // (there may be more than one) with the specified name. If any
+ // (there may be more than one) with the specified name. If any
// library is mentioned more than once, keep the latest mention
// of it, so that any possible dependent libraries appear before
- // it. (This ensures that the linker is able to see symbols from
+ // it. (This ensures that the linker is able to see symbols from
// all possible dependent libraries before linking in the library
// in question.)
for passed_lib in &self.tcx.sess.opts.libs {
let vis = self.get_visibility(id);
let span = self.get_span(id, sess);
let macro_rules = match kind {
- DefKind::Macro(..) => self.root.tables.macro_rules.get(self, id).is_some(),
+ DefKind::Macro(..) => self.root.tables.is_macro_rules.get(self, id),
_ => false,
};
fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
match self.def_kind(id) {
DefKind::Macro(_) => {
- let macro_rules = self.root.tables.macro_rules.get(self, id).is_some();
+ let macro_rules = self.root.tables.is_macro_rules.get(self, id);
let body =
self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess));
ast::MacroDef { macro_rules, body: ast::ptr::P(body) }
})
}
- fn get_may_have_doc_links(self, index: DefIndex) -> bool {
- self.root.tables.may_have_doc_links.get(self, index).is_some()
+ fn get_attr_flags(self, index: DefIndex) -> AttrFlags {
+ self.root.tables.attr_flags.get(self, index)
}
fn get_is_intrinsic(self, index: DefIndex) -> bool {
- self.root.tables.is_intrinsic.get(self, index).is_some()
+ self.root.tables.is_intrinsic.get(self, index)
}
}
use crate::creader::{CStore, LoadedMacro};
use crate::foreign_modules;
use crate::native_libs;
+use crate::rmeta::AttrFlags;
use rustc_ast as ast;
use rustc_attr::Deprecation;
thir_abstract_const => { table }
optimized_mir => { table }
mir_for_ctfe => { table }
+ mir_generator_witnesses => { table }
promoted_mir => { table }
def_span => { table }
def_ident_span => { table }
generator_kind => { table }
trait_def => { table }
deduced_param_attrs => { table }
+ is_type_alias_impl_trait => {
+ debug_assert_eq!(tcx.def_kind(def_id), DefKind::OpaqueTy);
+ cdata.root.tables.is_type_alias_impl_trait.get(cdata, def_id.index)
+ }
collect_return_position_impl_trait_in_trait_tys => {
Ok(cdata
.root
crate_extern_paths => { cdata.source().paths().cloned().collect() }
expn_that_defined => { cdata.get_expn_that_defined(def_id.index, tcx.sess) }
generator_diagnostic_data => { cdata.get_generator_diagnostic_data(tcx, def_id.index) }
+ is_doc_hidden => { cdata.get_attr_flags(def_id.index).contains(AttrFlags::IS_DOC_HIDDEN) }
}
pub(in crate::rmeta) fn provide(providers: &mut Providers) {
// keys from the former.
// This is a rudimentary check that does not catch all cases,
// just the easiest.
- let mut fallback_map: DefIdMap<DefId> = Default::default();
+ let mut fallback_map: Vec<(DefId, DefId)> = Default::default();
// Issue 46112: We want the map to prefer the shortest
// paths when reporting the path to an item. Therefore we
if let Some(def_id) = child.res.opt_def_id() {
if child.ident.name == kw::Underscore {
- fallback_map.insert(def_id, parent);
+ fallback_map.push((def_id, parent));
return;
}
- if ty::util::is_doc_hidden(tcx, parent) {
- fallback_map.insert(def_id, parent);
+ if tcx.is_doc_hidden(parent) {
+ fallback_map.push((def_id, parent));
return;
}
// Fill in any missing entries with the less preferable path.
// If this path re-exports the child as `_`, we still use this
// path in a diagnostic that suggests importing `::*`.
+
for (child, parent) in fallback_map {
visible_parent_map.entry(child).or_insert(parent);
}
}
pub fn may_have_doc_links_untracked(&self, def_id: DefId) -> bool {
- self.get_crate_data(def_id.krate).get_may_have_doc_links(def_id.index)
+ self.get_crate_data(def_id.krate)
+ .get_attr_flags(def_id.index)
+ .contains(AttrFlags::MAY_HAVE_DOC_LINKS)
+ }
+
+ pub fn is_doc_hidden_untracked(&self, def_id: DefId) -> bool {
+ self.get_crate_data(def_id.krate)
+ .get_attr_flags(def_id.index)
+ .contains(AttrFlags::IS_DOC_HIDDEN)
}
}
use crate::rmeta::table::TableBuilder;
use crate::rmeta::*;
+use rustc_ast::util::comments;
use rustc_ast::Attribute;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
use rustc_span::{
self, DebuggerVisualizerFile, ExternalSource, FileName, SourceFile, Span, SyntaxContext,
};
-use rustc_target::abi::VariantIdx;
use std::borrow::Borrow;
use std::collections::hash_map::Entry;
use std::hash::Hash;
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId {
fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
if self.krate == LOCAL_CRATE {
- // We will only write details for local expansions. Non-local expansions will fetch
+ // We will only write details for local expansions. Non-local expansions will fetch
// data from the corresponding crate's metadata.
// FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external
// metadata from proc-macro crates.
self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map()))
}
- fn encode_source_map(&mut self) -> LazyTable<u32, LazyValue<rustc_span::SourceFile>> {
+ fn encode_source_map(&mut self) -> LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>> {
let source_map = self.tcx.sess.source_map();
let all_source_files = source_map.files();
}
}
+struct AnalyzeAttrState {
+ is_exported: bool,
+ may_have_doc_links: bool,
+ is_doc_hidden: bool,
+}
+
/// Returns whether an attribute needs to be recorded in metadata, that is, if it's usable and
/// useful in downstream crates. Local-only attributes are an obvious example, but some
/// rustdoc-specific attributes can equally be of use while documenting the current crate only.
///
/// Removing these superfluous attributes speeds up compilation by making the metadata smaller.
///
-/// Note: the `is_def_id_public` parameter is used to cache whether the given `DefId` has a public
+/// Note: the `is_exported` parameter is used to cache whether the given `DefId` has a public
/// visibility: this is a piece of data that can be computed once per defid, and not once per
/// attribute. Some attributes would only be usable downstream if they are public.
#[inline]
-fn should_encode_attr(
- tcx: TyCtxt<'_>,
- attr: &Attribute,
- def_id: LocalDefId,
- is_def_id_public: &mut Option<bool>,
-) -> bool {
+fn analyze_attr(attr: &Attribute, state: &mut AnalyzeAttrState) -> bool {
+ let mut should_encode = false;
if rustc_feature::is_builtin_only_local(attr.name_or_empty()) {
// Attributes marked local-only don't need to be encoded for downstream crates.
- false
- } else if attr.doc_str().is_some() {
- // We keep all public doc comments because they might be "imported" into downstream crates
- // if they use `#[doc(inline)]` to copy an item's documentation into their own.
- *is_def_id_public.get_or_insert_with(|| tcx.effective_visibilities(()).is_exported(def_id))
+ } else if let Some(s) = attr.doc_str() {
+ // We keep all doc comments reachable to rustdoc because they might be "imported" into
+ // downstream crates if they use `#[doc(inline)]` to copy an item's documentation into
+ // their own.
+ if state.is_exported {
+ should_encode = true;
+ if comments::may_have_doc_links(s.as_str()) {
+ state.may_have_doc_links = true;
+ }
+ }
} else if attr.has_name(sym::doc) {
- // If this is a `doc` attribute, and it's marked `inline` (as in `#[doc(inline)]`), we can
- // remove it. It won't be inlinable in downstream crates.
- attr.meta_item_list().map(|l| l.iter().any(|l| !l.has_name(sym::inline))).unwrap_or(false)
+ // If this is a `doc` attribute that doesn't have anything except maybe `inline` (as in
+ // `#[doc(inline)]`), then we can remove it. It won't be inlinable in downstream crates.
+ if let Some(item_list) = attr.meta_item_list() {
+ for item in item_list {
+ if !item.has_name(sym::inline) {
+ should_encode = true;
+ if item.has_name(sym::hidden) {
+ state.is_doc_hidden = true;
+ break;
+ }
+ }
+ }
+ }
} else {
- true
+ should_encode = true;
}
+ should_encode
}
fn should_encode_visibility(def_kind: DefKind) -> bool {
| DefKind::AssocConst
| DefKind::Static(..)
| DefKind::Const => (true, false),
- // Full-fledged functions
- DefKind::AssocFn | DefKind::Fn => {
+ // Full-fledged functions + closures
+ DefKind::AssocFn | DefKind::Fn | DefKind::Closure => {
let generics = tcx.generics_of(def_id);
let needs_inline = (generics.requires_monomorphization(tcx)
|| tcx.codegen_fn_attrs(def_id).requests_inline())
let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir;
(is_const_fn, needs_inline || always_encode_mir)
}
- // Closures can't be const fn.
- DefKind::Closure => {
- let generics = tcx.generics_of(def_id);
- let needs_inline = (generics.requires_monomorphization(tcx)
- || tcx.codegen_fn_attrs(def_id).requests_inline())
- && tcx.sess.opts.output_types.should_codegen();
- let always_encode_mir = tcx.sess.opts.unstable_opts.always_encode_mir;
- (false, needs_inline || always_encode_mir)
- }
// Generators require optimized MIR to compute layout.
DefKind::Generator => (false, true),
// The others don't have MIR.
// of the trait fn to look for any RPITITs, but that's kinda doing a lot
// of work. We can probably remove this when we refactor RPITITs to be
// associated types.
- tcx.fn_sig(trait_item_def_id).skip_binder().output().walk().any(|arg| {
+ tcx.fn_sig(trait_item_def_id).subst_identity().skip_binder().output().walk().any(|arg| {
if let ty::GenericArgKind::Type(ty) = arg.unpack()
&& let ty::Alias(ty::Projection, data) = ty.kind()
&& tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder
impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
fn encode_attrs(&mut self, def_id: LocalDefId) {
let tcx = self.tcx;
- let mut is_public: Option<bool> = None;
-
- let mut attrs = tcx
+ let mut state = AnalyzeAttrState {
+ is_exported: tcx.effective_visibilities(()).is_exported(def_id),
+ may_have_doc_links: false,
+ is_doc_hidden: false,
+ };
+ let attr_iter = tcx
.hir()
.attrs(tcx.hir().local_def_id_to_hir_id(def_id))
.iter()
- .filter(move |attr| should_encode_attr(tcx, attr, def_id, &mut is_public));
+ .filter(|attr| analyze_attr(attr, &mut state));
+
+ record_array!(self.tables.attributes[def_id.to_def_id()] <- attr_iter);
- record_array!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone());
- if attrs.any(|attr| attr.may_have_doc_links()) {
- self.tables.may_have_doc_links.set(def_id.local_def_index, ());
+ let mut attr_flags = AttrFlags::empty();
+ if state.may_have_doc_links {
+ attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS;
+ }
+ if state.is_doc_hidden {
+ attr_flags |= AttrFlags::IS_DOC_HIDDEN;
+ }
+ if !attr_flags.is_empty() {
+ self.tables.attr_flags.set_nullable(def_id.local_def_index, attr_flags);
}
}
record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
}
if let DefKind::Enum | DefKind::Struct | DefKind::Union = def_kind {
- let params_in_repr = self.tcx.params_in_repr(def_id);
- record!(self.tables.params_in_repr[def_id] <- params_in_repr);
+ self.encode_info_for_adt(def_id);
}
if should_encode_trait_impl_trait_tys(tcx, def_id)
&& let Ok(table) = self.tcx.collect_return_position_impl_trait_in_trait_tys(def_id)
record!(self.tables.trait_impl_trait_tys[def_id] <- table);
}
}
- let inherent_impls = tcx.crate_inherent_impls(());
- for (def_id, implementations) in inherent_impls.inherent_impls.iter() {
+ let inherent_impls = tcx.with_stable_hashing_context(|hcx| {
+ tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true)
+ });
+
+ for (def_id, implementations) in inherent_impls {
if implementations.is_empty() {
continue;
}
}
}
- fn encode_enum_variant_info(&mut self, def: ty::AdtDef<'tcx>, index: VariantIdx) {
+ #[instrument(level = "trace", skip(self))]
+ fn encode_info_for_adt(&mut self, def_id: DefId) {
let tcx = self.tcx;
- let variant = &def.variant(index);
- let def_id = variant.def_id;
- debug!("EncodeContext::encode_enum_variant_info({:?})", def_id);
-
- let data = VariantData {
- discr: variant.discr,
- ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)),
- is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- };
+ let adt_def = tcx.adt_def(def_id);
+ record!(self.tables.repr_options[def_id] <- adt_def.repr());
- record!(self.tables.variant_data[def_id] <- data);
- self.tables.constness.set(def_id.index, hir::Constness::Const);
- record_array!(self.tables.children[def_id] <- variant.fields.iter().map(|f| {
- assert!(f.did.is_local());
- f.did.index
- }));
- if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor {
- // FIXME(eddyb) encode signature only in `encode_enum_variant_ctor`.
- record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(ctor_def_id));
+ let params_in_repr = self.tcx.params_in_repr(def_id);
+ record!(self.tables.params_in_repr[def_id] <- params_in_repr);
+
+ if adt_def.is_enum() {
+ record_array!(self.tables.children[def_id] <- iter::from_generator(||
+ for variant in tcx.adt_def(def_id).variants() {
+ yield variant.def_id.index;
+ // Encode constructors which take a separate slot in value namespace.
+ if let Some(ctor_def_id) = variant.ctor_def_id() {
+ yield ctor_def_id.index;
+ }
+ }
+ ));
+ } else {
+ // For non-enum, there is only one variant, and its def_id is the adt's.
+ debug_assert_eq!(adt_def.variants().len(), 1);
+ debug_assert_eq!(adt_def.non_enum_variant().def_id, def_id);
+ // Therefore, the loop over variants will encode its fields as the adt's children.
}
- }
- fn encode_enum_variant_ctor(&mut self, def: ty::AdtDef<'tcx>, index: VariantIdx) {
- let variant = &def.variant(index);
- let Some((ctor_kind, def_id)) = variant.ctor else { return };
- debug!("EncodeContext::encode_enum_variant_ctor({:?})", def_id);
+ for variant in adt_def.variants().iter() {
+ let data = VariantData {
+ discr: variant.discr,
+ ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)),
+ is_non_exhaustive: variant.is_field_list_non_exhaustive(),
+ };
+ record!(self.tables.variant_data[variant.def_id] <- data);
- // FIXME(eddyb) encode only the `CtorKind` for constructors.
- let data = VariantData {
- discr: variant.discr,
- ctor: Some((ctor_kind, def_id.index)),
- is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- };
+ self.tables.constness.set(variant.def_id.index, hir::Constness::Const);
+ record_array!(self.tables.children[variant.def_id] <- variant.fields.iter().map(|f| {
+ assert!(f.did.is_local());
+ f.did.index
+ }));
- record!(self.tables.variant_data[def_id] <- data);
- self.tables.constness.set(def_id.index, hir::Constness::Const);
- if ctor_kind == CtorKind::Fn {
- record!(self.tables.fn_sig[def_id] <- self.tcx.fn_sig(def_id));
+ if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor {
+ self.tables.constness.set(ctor_def_id.index, hir::Constness::Const);
+ let fn_sig = tcx.fn_sig(ctor_def_id);
+ record!(self.tables.fn_sig[ctor_def_id] <- fn_sig);
+ // FIXME only encode signature for ctor_def_id
+ record!(self.tables.fn_sig[variant.def_id] <- fn_sig);
+ }
}
}
hir::ItemKind::Struct(ref vdata, _) => {
yield item_id.owner_id.def_id.local_def_index;
// Encode constructors which take a separate slot in value namespace.
- if let Some(ctor_hir_id) = vdata.ctor_hir_id() {
- yield tcx.hir().local_def_id(ctor_hir_id).local_def_index;
+ if let Some(ctor_def_id) = vdata.ctor_def_id() {
+ yield ctor_def_id.local_def_index;
}
}
_ if tcx.def_key(item_id.owner_id.to_def_id()).get_opt_name().is_some() => {
}
}
- fn encode_struct_ctor(&mut self, adt_def: ty::AdtDef<'tcx>) {
- let variant = adt_def.non_enum_variant();
- let Some((ctor_kind, def_id)) = variant.ctor else { return };
- debug!("EncodeContext::encode_struct_ctor({:?})", def_id);
-
- let data = VariantData {
- discr: variant.discr,
- ctor: Some((ctor_kind, def_id.index)),
- is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- };
-
- record!(self.tables.repr_options[def_id] <- adt_def.repr());
- record!(self.tables.variant_data[def_id] <- data);
- self.tables.constness.set(def_id.index, hir::Constness::Const);
- if ctor_kind == CtorKind::Fn {
- record!(self.tables.fn_sig[def_id] <- self.tcx.fn_sig(def_id));
- }
- }
-
fn encode_explicit_item_bounds(&mut self, def_id: DefId) {
debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id);
let bounds = self.tcx.explicit_item_bounds(def_id);
if impl_item.kind == ty::AssocKind::Fn {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set(def_id.index, ());
+ self.tables.is_intrinsic.set_nullable(def_id.index, true);
}
}
}
debug!("EntryBuilder::encode_mir({:?})", def_id);
if encode_opt {
record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id));
+
+ if let DefKind::Generator = self.tcx.def_kind(def_id) && tcx.sess.opts.unstable_opts.drop_tracking_mir {
+ record!(self.tables.mir_generator_witnesses[def_id.to_def_id()] <- tcx.mir_generator_witnesses(def_id));
+ }
}
if encode_const {
record!(self.tables.mir_for_ctfe[def_id.to_def_id()] <- tcx.mir_for_ctfe(def_id));
}
hir::ItemKind::Macro(ref macro_def, _) => {
if macro_def.macro_rules {
- self.tables.macro_rules.set(def_id.index, ());
+ self.tables.is_macro_rules.set_nullable(def_id.index, true);
}
record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
}
hir::ItemKind::Mod(ref m) => {
return self.encode_info_for_mod(item.owner_id.def_id, m);
}
- hir::ItemKind::OpaqueTy(..) => {
+ hir::ItemKind::OpaqueTy(ref opaque) => {
self.encode_explicit_item_bounds(def_id);
- }
- hir::ItemKind::Enum(..) => {
- let adt_def = self.tcx.adt_def(def_id);
- record!(self.tables.repr_options[def_id] <- adt_def.repr());
- }
- hir::ItemKind::Struct(..) => {
- let adt_def = self.tcx.adt_def(def_id);
- record!(self.tables.repr_options[def_id] <- adt_def.repr());
- self.tables.constness.set(def_id.index, hir::Constness::Const);
-
- let variant = adt_def.non_enum_variant();
- record!(self.tables.variant_data[def_id] <- VariantData {
- discr: variant.discr,
- ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)),
- is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- });
- }
- hir::ItemKind::Union(..) => {
- let adt_def = self.tcx.adt_def(def_id);
- record!(self.tables.repr_options[def_id] <- adt_def.repr());
-
- let variant = adt_def.non_enum_variant();
- record!(self.tables.variant_data[def_id] <- VariantData {
- discr: variant.discr,
- ctor: variant.ctor.map(|(kind, def_id)| (kind, def_id.index)),
- is_non_exhaustive: variant.is_field_list_non_exhaustive(),
- });
+ if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) {
+ self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true);
+ }
}
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
self.tables.impl_defaultness.set(def_id.index, *defaultness);
self.tables.constness.set(def_id.index, *constness);
- let trait_ref = self.tcx.impl_trait_ref(def_id);
+ let trait_ref = self.tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::skip_binder);
if let Some(trait_ref) = trait_ref {
let trait_def = self.tcx.trait_def(trait_ref.def_id);
if let Ok(mut an) = trait_def.ancestors(self.tcx, def_id) {
}
hir::ItemKind::Static(..)
| hir::ItemKind::Const(..)
+ | hir::ItemKind::Enum(..)
+ | hir::ItemKind::Struct(..)
+ | hir::ItemKind::Union(..)
| hir::ItemKind::ForeignMod { .. }
| hir::ItemKind::GlobalAsm(..)
| hir::ItemKind::TyAlias(..) => {}
};
// FIXME(eddyb) there should be a nicer way to do this.
match item.kind {
- hir::ItemKind::Enum(..) => {
- record_array!(self.tables.children[def_id] <- iter::from_generator(||
- for variant in tcx.adt_def(def_id).variants() {
- yield variant.def_id.index;
- // Encode constructors which take a separate slot in value namespace.
- if let Some(ctor_def_id) = variant.ctor_def_id() {
- yield ctor_def_id.index;
- }
- }
- ))
- }
- hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
- record_array!(self.tables.children[def_id] <-
- self.tcx.adt_def(def_id).non_enum_variant().fields.iter().map(|f| {
- assert!(f.did.is_local());
- f.did.index
- })
- )
- }
hir::ItemKind::Impl { .. } | hir::ItemKind::Trait(..) => {
let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
record_array!(self.tables.children[def_id] <-
if let hir::ItemKind::Fn(..) = item.kind {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set(def_id.index, ());
+ self.tables.is_intrinsic.set_nullable(def_id.index, true);
}
}
if let hir::ItemKind::Impl { .. } = item.kind {
// so it's easier to do that here then to wait until we would encounter
// normally in the visitor walk.
match item.kind {
- hir::ItemKind::Enum(..) => {
- let def = self.tcx.adt_def(item.owner_id.to_def_id());
- for (i, _) in def.variants().iter_enumerated() {
- self.encode_enum_variant_info(def, i);
- self.encode_enum_variant_ctor(def, i);
- }
- }
- hir::ItemKind::Struct(..) => {
- let def = self.tcx.adt_def(item.owner_id.to_def_id());
- self.encode_struct_ctor(def);
- }
hir::ItemKind::Impl { .. } => {
for &trait_item_def_id in
self.tcx.associated_item_def_ids(item.owner_id.to_def_id()).iter()
ty::Closure(_, substs) => {
let constness = self.tcx.constness(def_id.to_def_id());
self.tables.constness.set(def_id.to_def_id().index, constness);
- record!(self.tables.fn_sig[def_id.to_def_id()] <- substs.as_closure().sig());
+ record!(self.tables.fn_sig[def_id.to_def_id()] <- ty::EarlyBinder(substs.as_closure().sig()));
}
_ => bug!("closure that is neither generator nor closure"),
for id in tcx.hir().items() {
if matches!(tcx.def_kind(id.owner_id), DefKind::Impl) {
if let Some(trait_ref) = tcx.impl_trait_ref(id.owner_id) {
+ let trait_ref = trait_ref.subst_identity();
+
let simplified_self_ty = fast_reject::simplify_type(
self.tcx,
trait_ref.self_ty(),
}
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set(def_id.index, ());
+ self.tables.is_intrinsic.set_nullable(def_id.index, true);
}
}
}
Previous(NonZeroUsize),
}
-type SyntaxContextTable = LazyTable<u32, LazyValue<SyntaxContextData>>;
-type ExpnDataTable = LazyTable<ExpnIndex, LazyValue<ExpnData>>;
-type ExpnHashTable = LazyTable<ExpnIndex, LazyValue<ExpnHash>>;
+type SyntaxContextTable = LazyTable<u32, Option<LazyValue<SyntaxContextData>>>;
+type ExpnDataTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnData>>>;
+type ExpnHashTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnHash>>>;
#[derive(MetadataEncodable, MetadataDecodable)]
pub(crate) struct ProcMacroData {
def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>,
- source_map: LazyTable<u32, LazyValue<rustc_span::SourceFile>>,
+ source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
compiler_builtins: bool,
needs_allocator: bool,
/// Define `LazyTables` and `TableBuilders` at the same time.
macro_rules! define_tables {
- ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => {
+ (
+ - nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
+ - optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+
+ ) => {
#[derive(MetadataEncodable, MetadataDecodable)]
pub(crate) struct LazyTables {
- $($name: LazyTable<$IDX, $T>),+
+ $($name1: LazyTable<$IDX1, $T1>,)+
+ $($name2: LazyTable<$IDX2, Option<$T2>>,)+
}
#[derive(Default)]
struct TableBuilders {
- $($name: TableBuilder<$IDX, $T>),+
+ $($name1: TableBuilder<$IDX1, $T1>,)+
+ $($name2: TableBuilder<$IDX2, Option<$T2>>,)+
}
impl TableBuilders {
fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
LazyTables {
- $($name: self.$name.encode(buf)),+
+ $($name1: self.$name1.encode(buf),)+
+ $($name2: self.$name2.encode(buf),)+
}
}
}
}
define_tables! {
+- nullable:
+ is_intrinsic: Table<DefIndex, bool>,
+ is_macro_rules: Table<DefIndex, bool>,
+ is_type_alias_impl_trait: Table<DefIndex, bool>,
+ attr_flags: Table<DefIndex, AttrFlags>,
+
+- optional:
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
children: Table<DefIndex, LazyArray<DefIndex>>,
-
opt_def_kind: Table<DefIndex, DefKind>,
visibility: Table<DefIndex, LazyValue<ty::Visibility<DefIndex>>>,
def_span: Table<DefIndex, LazyValue<Span>>,
super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
type_of: Table<DefIndex, LazyValue<Ty<'static>>>,
variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
- fn_sig: Table<DefIndex, LazyValue<ty::PolyFnSig<'static>>>,
+ fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<ty::PolyFnSig<'static>>>>,
codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>,
- impl_trait_ref: Table<DefIndex, LazyValue<ty::TraitRef<'static>>>,
- const_param_default: Table<DefIndex, LazyValue<rustc_middle::ty::Const<'static>>>,
+ impl_trait_ref: Table<DefIndex, LazyValue<ty::EarlyBinder<ty::TraitRef<'static>>>>,
+ const_param_default: Table<DefIndex, LazyValue<ty::EarlyBinder<rustc_middle::ty::Const<'static>>>>,
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
+ mir_generator_witnesses: Table<DefIndex, LazyValue<mir::GeneratorLayout<'static>>>,
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
// FIXME(compiler-errors): Why isn't this a LazyArray?
thir_abstract_const: Table<DefIndex, LazyValue<ty::Const<'static>>>,
impl_parent: Table<DefIndex, RawDefId>,
impl_polarity: Table<DefIndex, ty::ImplPolarity>,
constness: Table<DefIndex, hir::Constness>,
- is_intrinsic: Table<DefIndex, ()>,
impl_defaultness: Table<DefIndex, hir::Defaultness>,
// FIXME(eddyb) perhaps compute this on the fly if cheap enough?
coerce_unsized_info: Table<DefIndex, LazyValue<ty::adjustment::CoerceUnsizedInfo>>,
fn_arg_names: Table<DefIndex, LazyArray<Ident>>,
generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
-
trait_item_def_id: Table<DefIndex, RawDefId>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
def_path_hashes: Table<DefIndex, DefPathHash>,
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
- may_have_doc_links: Table<DefIndex, ()>,
variant_data: Table<DefIndex, LazyValue<VariantData>>,
assoc_container: Table<DefIndex, ty::AssocItemContainer>,
- // Slot is full when macro is macro_rules.
- macro_rules: Table<DefIndex, ()>,
macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
proc_macro: Table<DefIndex, MacroKind>,
module_reexports: Table<DefIndex, LazyArray<ModChild>>,
deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>,
-
trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, Ty<'static>>>>,
}
is_non_exhaustive: bool,
}
+bitflags::bitflags! {
+ #[derive(Default)]
+ pub struct AttrFlags: u8 {
+ const MAY_HAVE_DOC_LINKS = 1 << 0;
+ const IS_DOC_HIDDEN = 1 << 1;
+ }
+}
+
// Tags used for encoding Spans:
const TAG_VALID_SPAN_LOCAL: u8 = 0;
const TAG_VALID_SPAN_FOREIGN: u8 = 1;
IncoherentImpls,
CrateRoot,
CrateDep,
+ AttrFlags,
}
/// but this has no impact on safety.
pub(super) trait FixedSizeEncoding: Default {
/// This should be `[u8; BYTE_LEN]`;
+ /// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
type ByteArray;
fn from_bytes(b: &Self::ByteArray) -> Self;
}
}
-impl FixedSizeEncoding for Option<()> {
+impl FixedSizeEncoding for AttrFlags {
type ByteArray = [u8; 1];
#[inline]
fn from_bytes(b: &[u8; 1]) -> Self {
- (b[0] != 0).then(|| ())
+ AttrFlags::from_bits_truncate(b[0])
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 1]) {
- b[0] = self.is_some() as u8
+ b[0] = self.bits();
+ }
+}
+
+impl FixedSizeEncoding for bool {
+ type ByteArray = [u8; 1];
+
+ #[inline]
+ fn from_bytes(b: &[u8; 1]) -> Self {
+ b[0] != 0
+ }
+
+ #[inline]
+ fn write_to_bytes(self, b: &mut [u8; 1]) {
+ b[0] = self as u8
}
}
}
/// Helper for constructing a table's serialization (also see `Table`).
-pub(super) struct TableBuilder<I: Idx, T>
-where
- Option<T>: FixedSizeEncoding,
-{
- blocks: IndexVec<I, <Option<T> as FixedSizeEncoding>::ByteArray>,
+pub(super) struct TableBuilder<I: Idx, T: FixedSizeEncoding> {
+ blocks: IndexVec<I, T::ByteArray>,
_marker: PhantomData<T>,
}
-impl<I: Idx, T> Default for TableBuilder<I, T>
-where
- Option<T>: FixedSizeEncoding,
-{
+impl<I: Idx, T: FixedSizeEncoding> Default for TableBuilder<I, T> {
fn default() -> Self {
TableBuilder { blocks: Default::default(), _marker: PhantomData }
}
}
-impl<I: Idx, T> TableBuilder<I, T>
+impl<I: Idx, const N: usize, T> TableBuilder<I, Option<T>>
where
- Option<T>: FixedSizeEncoding,
+ Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
{
- pub(crate) fn set<const N: usize>(&mut self, i: I, value: T)
- where
- Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
- {
+ pub(crate) fn set(&mut self, i: I, value: T) {
+ self.set_nullable(i, Some(value))
+ }
+}
+
+impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
+ pub(crate) fn set_nullable(&mut self, i: I, value: T) {
// FIXME(eddyb) investigate more compact encodings for sparse tables.
// On the PR @michaelwoerister mentioned:
// > Space requirements could perhaps be optimized by using the HAMT `popcnt`
// > trick (i.e. divide things into buckets of 32 or 64 items and then
// > store bit-masks of which item in each bucket is actually serialized).
self.blocks.ensure_contains_elem(i, || [0; N]);
- Some(value).write_to_bytes(&mut self.blocks[i]);
+ value.write_to_bytes(&mut self.blocks[i]);
}
- pub(crate) fn encode<const N: usize>(&self, buf: &mut FileEncoder) -> LazyTable<I, T>
- where
- Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
- {
+ pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
let pos = buf.position();
for block in &self.blocks {
buf.emit_raw_bytes(block);
}
}
-impl<I: Idx, T: ParameterizedOverTcx> LazyTable<I, T>
+impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]> + ParameterizedOverTcx>
+ LazyTable<I, T>
where
- Option<T>: FixedSizeEncoding,
+ for<'tcx> T::Value<'tcx>: FixedSizeEncoding<ByteArray = [u8; N]>,
{
/// Given the metadata, extract out the value at a particular index (if any).
#[inline(never)]
- pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>, const N: usize>(
- &self,
- metadata: M,
- i: I,
- ) -> Option<T::Value<'tcx>>
- where
- Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
- {
+ pub(super) fn get<'a, 'tcx, M: Metadata<'a, 'tcx>>(&self, metadata: M, i: I) -> T::Value<'tcx> {
debug!("LazyTable::lookup: index={:?} len={:?}", i, self.encoded_size);
let start = self.position.get();
let bytes = &metadata.blob()[start..start + self.encoded_size];
let (bytes, []) = bytes.as_chunks::<N>() else { panic!() };
- let bytes = bytes.get(i.index())?;
- FixedSizeEncoding::from_bytes(bytes)
+ match bytes.get(i.index()) {
+ Some(bytes) => FixedSizeEncoding::from_bytes(bytes),
+ None => FixedSizeEncoding::from_bytes(&[0; N]),
+ }
}
/// Size of the table in entries, including possible gaps.
- pub(super) fn size<const N: usize>(&self) -> usize
- where
- for<'tcx> Option<T::Value<'tcx>>: FixedSizeEncoding<ByteArray = [u8; N]>,
- {
+ pub(super) fn size(&self) -> usize {
self.encoded_size / N
}
}
// (during lowering) and the `librustc_middle` arena (for decoding MIR)
[decode] asm_template: rustc_ast::InlineAsmTemplatePiece,
[decode] used_trait_imports: rustc_data_structures::unord::UnordSet<rustc_hir::def_id::LocalDefId>,
- [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet<rustc_hir::def_id::LocalDefId>,
+ [decode] is_late_bound_map: rustc_data_structures::fx::FxIndexSet<rustc_hir::ItemLocalId>,
[decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>,
[] dep_kind: rustc_middle::dep_graph::DepKindStruct<'tcx>,
use rustc_target::spec::abi::Abi;
#[inline]
-pub fn associated_body(node: Node<'_>) -> Option<BodyId> {
+pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> {
match node {
Node::Item(Item {
+ owner_id,
kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
..
})
| Node::TraitItem(TraitItem {
+ owner_id,
kind:
TraitItemKind::Const(_, Some(body)) | TraitItemKind::Fn(_, TraitFn::Provided(body)),
..
})
| Node::ImplItem(ImplItem {
+ owner_id,
kind: ImplItemKind::Const(_, body) | ImplItemKind::Fn(_, body),
..
- })
- | Node::Expr(Expr { kind: ExprKind::Closure(Closure { body, .. }), .. }) => Some(*body),
+ }) => Some((owner_id.def_id, *body)),
+
+ Node::Expr(Expr { kind: ExprKind::Closure(Closure { def_id, body, .. }), .. }) => {
+ Some((*def_id, *body))
+ }
- Node::AnonConst(constant) => Some(constant.body),
+ Node::AnonConst(constant) => Some((constant.def_id, constant.body)),
_ => None,
}
fn is_body_owner(node: Node<'_>, hir_id: HirId) -> bool {
match associated_body(node) {
- Some(b) => b.hir_id == hir_id,
+ Some((_, b)) => b.hir_id == hir_id,
None => false,
}
}
self.tcx.definitions_untracked().def_key(def_id)
}
- pub fn def_path_from_hir_id(self, id: HirId) -> Option<DefPath> {
- self.opt_local_def_id(id).map(|def_id| self.def_path(def_id))
- }
-
pub fn def_path(self, def_id: LocalDefId) -> DefPath {
// Accessing the DefPath is ok, since it is part of DefPathHash.
self.tcx.definitions_untracked().def_path(def_id)
self.tcx.definitions_untracked().def_path_hash(def_id)
}
- #[inline]
- #[track_caller]
- pub fn local_def_id(self, hir_id: HirId) -> LocalDefId {
- self.opt_local_def_id(hir_id).unwrap_or_else(|| {
- bug!(
- "local_def_id: no entry for `{:?}`, which has a map of `{:?}`",
- hir_id,
- self.find(hir_id)
- )
- })
- }
-
- #[inline]
- pub fn opt_local_def_id(self, hir_id: HirId) -> Option<LocalDefId> {
- if hir_id.local_id == ItemLocalId::new(0) {
- Some(hir_id.owner.def_id)
- } else {
- self.tcx
- .hir_owner_nodes(hir_id.owner)
- .as_owner()?
- .local_id_to_def_id
- .get(&hir_id.local_id)
- .copied()
- }
- }
-
#[inline]
pub fn local_def_id_to_hir_id(self, def_id: LocalDefId) -> HirId {
self.tcx.local_def_id_to_hir_id(def_id)
#[track_caller]
pub fn enclosing_body_owner(self, hir_id: HirId) -> LocalDefId {
for (_, node) in self.parent_iter(hir_id) {
- if let Some(body) = associated_body(node) {
- return self.body_owner_def_id(body);
+ if let Some((def_id, _)) = associated_body(node) {
+ return def_id;
}
}
parent
}
- pub fn body_owner_def_id(self, id: BodyId) -> LocalDefId {
- self.local_def_id(self.body_owner(id))
+ pub fn body_owner_def_id(self, BodyId { hir_id }: BodyId) -> LocalDefId {
+ let parent = self.parent_id(hir_id);
+ associated_body(self.get(parent)).unwrap().0
}
/// Given a `LocalDefId`, returns the `BodyId` associated with it,
/// if the node is a body owner, otherwise returns `None`.
pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<BodyId> {
- self.find_by_def_id(id).and_then(associated_body)
+ let node = self.find_by_def_id(id)?;
+ let (_, body_id) = associated_body(node)?;
+ Some(body_id)
}
/// Given a body owner's id, returns the `BodyId` associated with it.
/// Visits all item-likes in the crate in some deterministic (but unspecified) order. If you
/// need to process every item-like, and don't care about visiting nested items in a particular
- /// order then this method is the best choice. If you do care about this nesting, you should
+ /// order then this method is the best choice. If you do care about this nesting, you should
/// use the `tcx.hir().walk_toplevel_module`.
///
- /// Note that this function will access HIR for all the item-likes in the crate. If you only
+ /// Note that this function will access HIR for all the item-likes in the crate. If you only
/// need to access some of them, it is usually better to manually loop on the iterators
/// provided by `tcx.hir_crate_items(())`.
///
}
/// Gather the LocalDefId for each item-like within a module, including items contained within
-/// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
+/// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
#[derive(Debug, HashStable, Encodable, Decodable)]
pub struct ModuleItems {
submodules: Box<[OwnerId]>,
pub fn impl_subject(self, def_id: DefId) -> ImplSubject<'tcx> {
self.impl_trait_ref(def_id)
+ .map(|t| t.subst_identity())
.map(ImplSubject::Trait)
.unwrap_or_else(|| ImplSubject::Inherent(self.type_of(def_id)))
}
use crate::mir::ConstraintCategory;
use crate::ty::subst::GenericArg;
use crate::ty::{self, BoundVar, List, Region, Ty, TyCtxt};
-use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
use smallvec::SmallVec;
-use std::iter;
use std::ops::Index;
/// A "canonicalized" type `V` is one where all free inference
/// vectors with the original values that were replaced by canonical
/// variables. You will need to supply it later to instantiate the
/// canonicalized query response.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyDecodable, TyEncodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct CanonicalVarValues<'tcx> {
- pub var_values: IndexVec<BoundVar, GenericArg<'tcx>>,
+ pub var_values: ty::SubstsRef<'tcx>,
+}
+
+impl CanonicalVarValues<'_> {
+ pub fn is_identity(&self) -> bool {
+ self.var_values.iter().enumerate().all(|(bv, arg)| match arg.unpack() {
+ ty::GenericArgKind::Lifetime(r) => {
+ matches!(*r, ty::ReLateBound(ty::INNERMOST, br) if br.var.as_usize() == bv)
+ }
+ ty::GenericArgKind::Type(ty) => {
+ matches!(*ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var.as_usize() == bv)
+ }
+ ty::GenericArgKind::Const(ct) => {
+ matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc.as_usize() == bv)
+ }
+ })
+ }
}
/// When we canonicalize a value to form a query, we wind up replacing
}
impl<'tcx> CanonicalVarValues<'tcx> {
- #[inline]
- pub fn len(&self) -> usize {
- self.var_values.len()
- }
-
- /// Makes an identity substitution from this one: each bound var
- /// is matched to the same bound var, preserving the original kinds.
- /// For example, if we have:
- /// `self.var_values == [Type(u32), Lifetime('a), Type(u64)]`
- /// we'll return a substitution `subst` with:
- /// `subst.var_values == [Type(^0), Lifetime(^1), Type(^2)]`.
- pub fn make_identity(&self, tcx: TyCtxt<'tcx>) -> Self {
- use crate::ty::subst::GenericArgKind;
-
+ // Given a list of canonical variables, construct a set of values which are
+ // the identity response.
+ pub fn make_identity(
+ tcx: TyCtxt<'tcx>,
+ infos: CanonicalVarInfos<'tcx>,
+ ) -> CanonicalVarValues<'tcx> {
CanonicalVarValues {
- var_values: iter::zip(&self.var_values, 0..)
- .map(|(kind, i)| match kind.unpack() {
- GenericArgKind::Type(..) => {
- tcx.mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i).into())).into()
- }
- GenericArgKind::Lifetime(..) => {
- let br = ty::BoundRegion {
- var: ty::BoundVar::from_u32(i),
- kind: ty::BrAnon(i, None),
- };
- tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
+ var_values: tcx.mk_substs(infos.iter().enumerate().map(
+ |(i, info)| -> ty::GenericArg<'tcx> {
+ match info.kind {
+ CanonicalVarKind::Ty(_) | CanonicalVarKind::PlaceholderTy(_) => tcx
+ .mk_ty(ty::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i).into()))
+ .into(),
+ CanonicalVarKind::Region(_) | CanonicalVarKind::PlaceholderRegion(_) => {
+ let br = ty::BoundRegion {
+ var: ty::BoundVar::from_usize(i),
+ kind: ty::BrAnon(i as u32, None),
+ };
+ tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
+ }
+ CanonicalVarKind::Const(_, ty)
+ | CanonicalVarKind::PlaceholderConst(_, ty) => tcx
+ .mk_const(
+ ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_usize(i)),
+ ty,
+ )
+ .into(),
}
- GenericArgKind::Const(ct) => tcx
- .mk_const(
- ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i)),
- ct.ty(),
- )
- .into(),
- })
- .collect(),
+ },
+ )),
}
}
+
+ /// Creates dummy var values which should not be used in a
+ /// canonical response.
+ pub fn dummy() -> CanonicalVarValues<'tcx> {
+ CanonicalVarValues { var_values: ty::List::empty() }
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.var_values.len()
+ }
}
impl<'a, 'tcx> IntoIterator for &'a CanonicalVarValues<'tcx> {
type Item = GenericArg<'tcx>;
- type IntoIter = ::std::iter::Cloned<::std::slice::Iter<'a, GenericArg<'tcx>>>;
+ type IntoIter = ::std::iter::Copied<::std::slice::Iter<'a, GenericArg<'tcx>>>;
fn into_iter(self) -> Self::IntoIter {
- self.var_values.iter().cloned()
+ self.var_values.iter()
}
}
type Output = GenericArg<'tcx>;
fn index(&self, value: BoundVar) -> &GenericArg<'tcx> {
- &self.var_values[value]
+ &self.var_values[value.as_usize()]
}
}
#![feature(min_specialization)]
#![feature(trusted_len)]
#![feature(type_alias_impl_trait)]
+#![feature(strict_provenance)]
#![feature(associated_type_bounds)]
#![feature(rustc_attrs)]
#![feature(control_flow_enum)]
_: &mut F)
-> ::std::ops::ControlFlow<F::BreakTy>
{
- ::std::ops::ControlFlow::CONTINUE
+ ::std::ops::ControlFlow::Continue(())
}
}
)+
$($crate::ty::visit::TypeVisitable::visit_with(
$variant_arg, $visitor
)?;)*
- ::std::ops::ControlFlow::CONTINUE
+ ::std::ops::ControlFlow::Continue(())
}
$($output)*
)
$($crate::ty::visit::TypeVisitable::visit_with(
$variant_arg, $visitor
)?;)*
- ::std::ops::ControlFlow::CONTINUE
+ ::std::ops::ControlFlow::Continue(())
}
$($output)*
)
@VisitVariants($this, $visitor)
input($($input)*)
output(
- $variant => { ::std::ops::ControlFlow::CONTINUE }
+ $variant => { ::std::ops::ControlFlow::Continue(()) }
$($output)*
)
)
-use crate::mir::graph_cyclic_cache::GraphIsCyclicCache;
-use crate::mir::predecessors::{PredecessorCache, Predecessors};
-use crate::mir::switch_sources::{SwitchSourceCache, SwitchSources};
-use crate::mir::traversal::PostorderCache;
-use crate::mir::{BasicBlock, BasicBlockData, Successors, START_BLOCK};
+use crate::mir::traversal::Postorder;
+use crate::mir::{BasicBlock, BasicBlockData, Successors, Terminator, TerminatorKind, START_BLOCK};
+use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph;
use rustc_data_structures::graph::dominators::{dominators, Dominators};
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::OnceCell;
use rustc_index::vec::IndexVec;
+use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use smallvec::SmallVec;
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
pub struct BasicBlocks<'tcx> {
basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
- predecessor_cache: PredecessorCache,
- switch_source_cache: SwitchSourceCache,
- is_cyclic: GraphIsCyclicCache,
- postorder_cache: PostorderCache,
+ cache: Cache,
+}
+
+// Typically 95%+ of basic blocks have 4 or fewer predecessors.
+pub type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;
+
+pub type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u128>; 1]>>;
+
+#[derive(Clone, Default, Debug)]
+struct Cache {
+ predecessors: OnceCell<Predecessors>,
+ switch_sources: OnceCell<SwitchSources>,
+ is_cyclic: OnceCell<bool>,
+ postorder: OnceCell<Vec<BasicBlock>>,
}
impl<'tcx> BasicBlocks<'tcx> {
#[inline]
pub fn new(basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>) -> Self {
- BasicBlocks {
- basic_blocks,
- predecessor_cache: PredecessorCache::new(),
- switch_source_cache: SwitchSourceCache::new(),
- is_cyclic: GraphIsCyclicCache::new(),
- postorder_cache: PostorderCache::new(),
- }
+ BasicBlocks { basic_blocks, cache: Cache::default() }
}
/// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
#[inline]
pub fn is_cfg_cyclic(&self) -> bool {
- self.is_cyclic.is_cyclic(self)
+ *self.cache.is_cyclic.get_or_init(|| graph::is_cyclic(self))
}
- #[inline]
pub fn dominators(&self) -> Dominators<BasicBlock> {
dominators(&self)
}
/// Returns predecessors for each basic block.
#[inline]
pub fn predecessors(&self) -> &Predecessors {
- self.predecessor_cache.compute(&self.basic_blocks)
+ self.cache.predecessors.get_or_init(|| {
+ let mut preds = IndexVec::from_elem(SmallVec::new(), &self.basic_blocks);
+ for (bb, data) in self.basic_blocks.iter_enumerated() {
+ if let Some(term) = &data.terminator {
+ for succ in term.successors() {
+ preds[succ].push(bb);
+ }
+ }
+ }
+ preds
+ })
}
/// Returns basic blocks in a postorder.
#[inline]
pub fn postorder(&self) -> &[BasicBlock] {
- self.postorder_cache.compute(&self.basic_blocks)
+ self.cache.postorder.get_or_init(|| {
+ Postorder::new(&self.basic_blocks, START_BLOCK).map(|(bb, _)| bb).collect()
+ })
}
/// `switch_sources()[&(target, switch)]` returns a list of switch
/// values that lead to a `target` block from a `switch` block.
#[inline]
pub fn switch_sources(&self) -> &SwitchSources {
- self.switch_source_cache.compute(&self.basic_blocks)
+ self.cache.switch_sources.get_or_init(|| {
+ let mut switch_sources: SwitchSources = FxHashMap::default();
+ for (bb, data) in self.basic_blocks.iter_enumerated() {
+ if let Some(Terminator {
+ kind: TerminatorKind::SwitchInt { targets, .. }, ..
+ }) = &data.terminator
+ {
+ for (value, target) in targets.iter() {
+ switch_sources.entry((target, bb)).or_default().push(Some(value));
+ }
+ switch_sources.entry((targets.otherwise(), bb)).or_default().push(None);
+ }
+ }
+ switch_sources
+ })
}
/// Returns mutable reference to basic blocks. Invalidates CFG cache.
/// All other methods that allow you to mutate the basic blocks also call this method
/// themselves, thereby avoiding any risk of accidentally cache invalidation.
pub fn invalidate_cfg_cache(&mut self) {
- self.predecessor_cache.invalidate();
- self.switch_source_cache.invalidate();
- self.is_cyclic.invalidate();
- self.postorder_cache.invalidate();
+ self.cache = Cache::default();
}
}
self.predecessors()[node].iter().copied()
}
}
+
+TrivialTypeTraversalAndLiftImpls! {
+ Cache,
+}
+
+impl<S: Encoder> Encodable<S> for Cache {
+ #[inline]
+ fn encode(&self, _s: &mut S) {}
+}
+
+impl<D: Decoder> Decodable<D> for Cache {
+ #[inline]
+ fn decode(_: &mut D) -> Self {
+ Default::default()
+ }
+}
+
+impl<CTX> HashStable<CTX> for Cache {
+ #[inline]
+ fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {}
+}
+++ /dev/null
-use rustc_data_structures::graph::{
- self, DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors,
-};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::OnceCell;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-
-/// Helper type to cache the result of `graph::is_cyclic`.
-#[derive(Clone, Debug)]
-pub(super) struct GraphIsCyclicCache {
- cache: OnceCell<bool>,
-}
-
-impl GraphIsCyclicCache {
- #[inline]
- pub(super) fn new() -> Self {
- GraphIsCyclicCache { cache: OnceCell::new() }
- }
-
- pub(super) fn is_cyclic<G>(&self, graph: &G) -> bool
- where
- G: ?Sized + DirectedGraph + WithStartNode + WithSuccessors + WithNumNodes,
- {
- *self.cache.get_or_init(|| graph::is_cyclic(graph))
- }
-
- /// Invalidates the cache.
- #[inline]
- pub(super) fn invalidate(&mut self) {
- // Invalidating the cache requires mutating the MIR, which in turn requires a unique
- // reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all
- // callers of `invalidate` have a unique reference to the MIR and thus to the
- // cache. This means we never need to do synchronization when `invalidate` is called,
- // we can simply reinitialize the `OnceCell`.
- self.cache = OnceCell::new();
- }
-}
-
-impl<S: Encoder> Encodable<S> for GraphIsCyclicCache {
- #[inline]
- fn encode(&self, s: &mut S) {
- Encodable::encode(&(), s);
- }
-}
-
-impl<D: Decoder> Decodable<D> for GraphIsCyclicCache {
- #[inline]
- fn decode(d: &mut D) -> Self {
- let () = Decodable::decode(d);
- Self::new()
- }
-}
-
-impl<CTX> HashStable<CTX> for GraphIsCyclicCache {
- #[inline]
- fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
- // do nothing
- }
-}
-
-TrivialTypeTraversalAndLiftImpls! {
- GraphIsCyclicCache,
-}
use rustc_macros::HashStable;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_serialize::{Decodable, Encodable};
-use rustc_target::abi::Endian;
+use rustc_target::abi::{AddressSpace, Endian, HasDataLayout};
use crate::mir;
use crate::ty::codec::{TyDecoder, TyEncoder};
_ => bug!("expected vtable, got {:?}", self),
}
}
+
+ /// The address space that this `GlobalAlloc` should be placed in.
+ #[inline]
+ pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace {
+ match self {
+ GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
+ GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
+ AddressSpace::DATA
+ }
+ }
+ }
}
pub(crate) struct AllocMap<'tcx> {
self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
}
- /// Generates an `AllocId` for a function. Depending on the function type,
+ /// Generates an `AllocId` for a function. Depending on the function type,
/// this might get deduplicated or assigned a new ID each time.
pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
// We thus generate a new `AllocId` for every mention of a function. This means that
// `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true.
// However, formatting code relies on function identity (see #58320), so we only do
- // this for generic functions. Lifetime parameters are ignored.
+ // this for generic functions. Lifetime parameters are ignored.
let is_generic = instance
.substs
.into_iter()
}
}
- /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
+ /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
pub fn create_vtable_alloc(
self,
ty: Ty<'tcx>,
pub mod coverage;
mod generic_graph;
pub mod generic_graphviz;
-mod graph_cyclic_cache;
pub mod graphviz;
pub mod interpret;
pub mod mono;
pub mod patch;
-mod predecessors;
pub mod pretty;
mod query;
pub mod spanview;
mod syntax;
pub use syntax::*;
-mod switch_sources;
pub mod tcx;
pub mod terminator;
pub use terminator::*;
AggregateTemp,
/// A temporary created during the pass `Derefer` to avoid it's retagging
DerefTemp,
+ /// A temporary created for borrow checking.
+ FakeBorrow,
}
impl<'tcx> LocalDecl<'tcx> {
}
Coverage(box ref coverage) => write!(fmt, "Coverage::{:?}", coverage.kind),
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
+ ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
Nop => write!(fmt, "nop"),
}
}
// FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
// does not provide the parents generics to anonymous constants. We still allow generic const
- // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
+ // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
// ever try to substitute the generic parameters in their bodies.
//
// While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
let hir_id = tcx.hir().local_def_id_to_hir_id(def.did);
let parent_substs = if let Some(parent_hir_id) = tcx.hir().opt_parent_id(hir_id) {
- if let Some(parent_did) = tcx.hir().opt_local_def_id(parent_hir_id) {
+ if let Some(parent_did) = parent_hir_id.as_owner() {
InternalSubsts::identity_for_item(tcx, parent_did.to_def_id())
} else {
tcx.mk_substs(Vec::<GenericArg<'tcx>>::new().into_iter())
if self.block == other.block {
self.statement_index <= other.statement_index
} else {
- dominators.is_dominated_by(other.block, self.block)
+ dominators.dominates(self.block, other.block)
}
}
}
+++ /dev/null
-//! Lazily compute the reverse control-flow graph for the MIR.
-
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::OnceCell;
-use rustc_index::vec::IndexVec;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-use smallvec::SmallVec;
-
-use crate::mir::{BasicBlock, BasicBlockData};
-
-// Typically 95%+ of basic blocks have 4 or fewer predecessors.
-pub type Predecessors = IndexVec<BasicBlock, SmallVec<[BasicBlock; 4]>>;
-
-#[derive(Clone, Debug)]
-pub(super) struct PredecessorCache {
- cache: OnceCell<Predecessors>,
-}
-
-impl PredecessorCache {
- #[inline]
- pub(super) fn new() -> Self {
- PredecessorCache { cache: OnceCell::new() }
- }
-
- /// Invalidates the predecessor cache.
- #[inline]
- pub(super) fn invalidate(&mut self) {
- // Invalidating the predecessor cache requires mutating the MIR, which in turn requires a
- // unique reference (`&mut`) to the `mir::Body`. Because of this, we can assume that all
- // callers of `invalidate` have a unique reference to the MIR and thus to the predecessor
- // cache. This means we never need to do synchronization when `invalidate` is called, we can
- // simply reinitialize the `OnceCell`.
- self.cache = OnceCell::new();
- }
-
- /// Returns the predecessor graph for this MIR.
- #[inline]
- pub(super) fn compute(
- &self,
- basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
- ) -> &Predecessors {
- self.cache.get_or_init(|| {
- let mut preds = IndexVec::from_elem(SmallVec::new(), basic_blocks);
- for (bb, data) in basic_blocks.iter_enumerated() {
- if let Some(term) = &data.terminator {
- for succ in term.successors() {
- preds[succ].push(bb);
- }
- }
- }
-
- preds
- })
- }
-}
-
-impl<S: Encoder> Encodable<S> for PredecessorCache {
- #[inline]
- fn encode(&self, _s: &mut S) {}
-}
-
-impl<D: Decoder> Decodable<D> for PredecessorCache {
- #[inline]
- fn decode(_: &mut D) -> Self {
- Self::new()
- }
-}
-
-impl<CTX> HashStable<CTX> for PredecessorCache {
- #[inline]
- fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
- // do nothing
- }
-}
-
-TrivialTypeTraversalAndLiftImpls! {
- PredecessorCache,
-}
pub struct GeneratorSavedLocal {}
}
+#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
+pub struct GeneratorSavedTy<'tcx> {
+ pub ty: Ty<'tcx>,
+ /// Source info corresponding to the local in the original MIR body.
+ pub source_info: SourceInfo,
+ /// Whether the local should be ignored for trait bound computations.
+ pub ignore_for_traits: bool,
+}
+
/// The layout of generator state.
#[derive(Clone, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)]
pub struct GeneratorLayout<'tcx> {
/// The type of every local stored inside the generator.
- pub field_tys: IndexVec<GeneratorSavedLocal, Ty<'tcx>>,
+ pub field_tys: IndexVec<GeneratorSavedLocal, GeneratorSavedTy<'tcx>>,
/// Which of the above fields are in each variant. Note that one field may
/// be stored in multiple variants.
AscribeUserType(..) => "AscribeUserType",
Coverage(..) => "Coverage",
Intrinsic(..) => "Intrinsic",
+ ConstEvalCounter => "ConstEvalCounter",
Nop => "Nop",
}
}
fn hir_body(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&rustc_hir::Body<'_>> {
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
- hir::map::associated_body(hir_node).map(|fn_body_id| tcx.hir().body(fn_body_id))
+ hir::map::associated_body(hir_node).map(|(_, fn_body_id)| tcx.hir().body(fn_body_id))
}
fn escape_html(s: &str) -> String {
+++ /dev/null
-//! Lazily compute the inverse of each `SwitchInt`'s switch targets. Modeled after
-//! `Predecessors`/`PredecessorCache`.
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::OnceCell;
-use rustc_index::vec::IndexVec;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-use smallvec::SmallVec;
-
-use crate::mir::{BasicBlock, BasicBlockData, Terminator, TerminatorKind};
-
-pub type SwitchSources = FxHashMap<(BasicBlock, BasicBlock), SmallVec<[Option<u128>; 1]>>;
-
-#[derive(Clone, Debug)]
-pub(super) struct SwitchSourceCache {
- cache: OnceCell<SwitchSources>,
-}
-
-impl SwitchSourceCache {
- #[inline]
- pub(super) fn new() -> Self {
- SwitchSourceCache { cache: OnceCell::new() }
- }
-
- /// Invalidates the switch source cache.
- #[inline]
- pub(super) fn invalidate(&mut self) {
- self.cache = OnceCell::new();
- }
-
- /// Returns the switch sources for this MIR.
- #[inline]
- pub(super) fn compute(
- &self,
- basic_blocks: &IndexVec<BasicBlock, BasicBlockData<'_>>,
- ) -> &SwitchSources {
- self.cache.get_or_init(|| {
- let mut switch_sources: SwitchSources = FxHashMap::default();
- for (bb, data) in basic_blocks.iter_enumerated() {
- if let Some(Terminator {
- kind: TerminatorKind::SwitchInt { targets, .. }, ..
- }) = &data.terminator
- {
- for (value, target) in targets.iter() {
- switch_sources.entry((target, bb)).or_default().push(Some(value));
- }
- switch_sources.entry((targets.otherwise(), bb)).or_default().push(None);
- }
- }
-
- switch_sources
- })
- }
-}
-
-impl<S: Encoder> Encodable<S> for SwitchSourceCache {
- #[inline]
- fn encode(&self, _s: &mut S) {}
-}
-
-impl<D: Decoder> Decodable<D> for SwitchSourceCache {
- #[inline]
- fn decode(_: &mut D) -> Self {
- Self::new()
- }
-}
-
-impl<CTX> HashStable<CTX> for SwitchSourceCache {
- #[inline]
- fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
- // do nothing
- }
-}
-
-TrivialTypeTraversalAndLiftImpls! {
- SwitchSourceCache,
-}
/// This avoids adding a new block and a terminator for simple intrinsics.
Intrinsic(Box<NonDivergingIntrinsic<'tcx>>),
+ /// Instructs the const eval interpreter to increment a counter; this counter is used to track
+ /// how many steps the interpreter has taken. It is used to prevent the user from writing const
+ /// code that runs for too long or infinitely. Other than in the const eval interpreter, this
+ /// is a no-op.
+ ConstEvalCounter,
+
/// No-op. Useful for deleting instructions without affecting statement indices.
Nop,
}
/// must also be `cleanup`. This is a part of the type system and checked statically, so it is
/// still an error to have such an edge in the CFG even if it's known that it won't be taken at
/// runtime.
+/// 4. The control flow between cleanup blocks must look like an upside down tree. Roughly
+/// speaking, this means that control flow that looks like a V is allowed, while control flow
+/// that looks like a W is not. This is necessary to ensure that landing pad information can be
+/// correctly codegened on MSVC. More precisely:
+///
+/// Begin with the standard control flow graph `G`. Modify `G` as follows: for any two cleanup
+/// vertices `u` and `v` such that `u` dominates `v`, contract `u` and `v` into a single vertex,
+/// deleting self edges and duplicate edges in the process. Now remove all vertices from `G`
+/// that are not cleanup vertices or are not reachable. The resulting graph must be an inverted
+/// tree, that is each vertex may have at most one successor and there may be no cycles.
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, TypeFoldable, TypeVisitable)]
pub enum TerminatorKind<'tcx> {
/// Block has one successor; we continue execution there.
}
/// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
- /// specific value. This cannot fail, as it'll return the `otherwise`
+ /// specific value. This cannot fail, as it'll return the `otherwise`
/// branch if there's not a specific match for the value.
pub fn target_for_value(&self, value: u128) -> BasicBlock {
self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::OnceCell;
use rustc_index::bit_set::BitSet;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use super::*;
let len = blocks.len();
ReversePostorderIter { body, blocks, idx: len }
}
-
-#[derive(Clone, Debug)]
-pub(super) struct PostorderCache {
- cache: OnceCell<Vec<BasicBlock>>,
-}
-
-impl PostorderCache {
- #[inline]
- pub(super) fn new() -> Self {
- PostorderCache { cache: OnceCell::new() }
- }
-
- /// Invalidates the postorder cache.
- #[inline]
- pub(super) fn invalidate(&mut self) {
- self.cache = OnceCell::new();
- }
-
- /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR.
- #[inline]
- pub(super) fn compute(&self, body: &IndexVec<BasicBlock, BasicBlockData<'_>>) -> &[BasicBlock] {
- self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
- }
-}
-
-impl<S: Encoder> Encodable<S> for PostorderCache {
- #[inline]
- fn encode(&self, _s: &mut S) {}
-}
-
-impl<D: Decoder> Decodable<D> for PostorderCache {
- #[inline]
- fn decode(_: &mut D) -> Self {
- Self::new()
- }
-}
-
-impl<CTX> HashStable<CTX> for PostorderCache {
- #[inline]
- fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
- // do nothing
- }
-}
-
-TrivialTypeTraversalAndLiftImpls! {
- PostorderCache,
-}
impl<'tcx, R: Idx, C: Idx> TypeVisitable<'tcx> for BitMatrix<R, C> {
fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
}
}
+ StatementKind::ConstEvalCounter => {}
StatementKind::Nop => {}
}
}
/// Given the def_id of a const-generic parameter, computes the associated default const
/// parameter. e.g. `fn example<const N: usize=3>` called on `N` would return `3`.
- query const_param_default(param: DefId) -> ty::Const<'tcx> {
+ query const_param_default(param: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> {
desc { |tcx| "computing const default for a given parameter `{}`", tcx.def_path_str(param) }
cache_on_disk_if { param.is_local() }
separate_provide_extern
separate_provide_extern
}
+ query is_type_alias_impl_trait(key: DefId) -> bool
+ {
+ desc { "determine whether the opaque is a type-alias impl trait" }
+ separate_provide_extern
+ }
+
query analysis(key: ()) -> Result<(), ErrorGuaranteed> {
eval_always
desc { "running analysis passes on this crate" }
/// ```
///
/// Bounds from the parent (e.g. with nested impl trait) are not included.
- query item_bounds(key: DefId) -> &'tcx ty::List<ty::Predicate<'tcx>> {
+ query item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
}
desc { |tcx| "constructing THIR tree for `{}`", tcx.def_path_str(key.did.to_def_id()) }
}
+ /// Create a list-like THIR representation for debugging.
+ query thir_flat(key: ty::WithOptConstParam<LocalDefId>) -> String {
+ no_hash
+ arena_cache
+ desc { |tcx| "constructing flat THIR representation for `{}`", tcx.def_path_str(key.did.to_def_id()) }
+ }
+
/// Set of all the `DefId`s in this crate that have MIR associated with
/// them. This includes all the body owners, but also things like struct
/// constructors.
}
}
+ query mir_generator_witnesses(key: DefId) -> mir::GeneratorLayout<'tcx> {
+ arena_cache
+ desc { |tcx| "generator witness types for `{}`", tcx.def_path_str(key) }
+ cache_on_disk_if { key.is_local() }
+ separate_provide_extern
+ }
+
+ query check_generator_obligations(key: LocalDefId) {
+ desc { |tcx| "verify auto trait bounds for generator interior type `{}`", tcx.def_path_str(key.to_def_id()) }
+ }
+
/// MIR after our optimization passes have run. This is MIR that is ready
/// for codegen. This is also the only query that can fetch non-local MIR, at present.
query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {
/// Given an `impl_id`, return the trait it implements.
/// Return `None` if this is an inherent impl.
- query impl_trait_ref(impl_id: DefId) -> Option<ty::TraitRef<'tcx>> {
+ query impl_trait_ref(impl_id: DefId) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> {
desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) }
cache_on_disk_if { impl_id.is_local() }
separate_provide_extern
}
/// Computes the signature of the function.
- query fn_sig(key: DefId) -> ty::PolyFnSig<'tcx> {
+ query fn_sig(key: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> {
desc { |tcx| "computing function signature of `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
separate_provide_extern
/// Determines whether an item is annotated with `doc(hidden)`.
query is_doc_hidden(def_id: DefId) -> bool {
desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) }
+ separate_provide_extern
}
/// Determines whether an item is annotated with `doc(notable_trait)`.
query object_safety_violations(trait_id: DefId) -> &'tcx [traits::ObjectSafetyViolation] {
desc { |tcx| "determining object safety of trait `{}`", tcx.def_path_str(trait_id) }
}
+ query check_is_object_safe(trait_id: DefId) -> bool {
+ desc { |tcx| "checking if trait `{}` is object safe", tcx.def_path_str(trait_id) }
+ }
/// Gets the ParameterEnvironment for a given item; this environment
/// will be in "user-facing" mode, meaning that it is suitable for
Option<&'tcx FxHashMap<ItemLocalId, Region>> {
desc { "looking up a named region" }
}
- query is_late_bound_map(_: LocalDefId) -> Option<&'tcx FxIndexSet<LocalDefId>> {
+ query is_late_bound_map(_: hir::OwnerId) -> Option<&'tcx FxIndexSet<ItemLocalId>> {
desc { "testing if a region is late bound" }
}
/// For a given item's generic parameter, gets the default lifetimes to be used
///
/// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt`
/// has been destroyed.
- query output_filenames(_: ()) -> &'tcx Arc<OutputFilenames> {
+ query output_filenames(_: ()) -> Arc<OutputFilenames> {
feedable
desc { "getting output filenames" }
+ arena_cache
}
/// Do not call this query directly: invoke `normalize` instead.
separate_provide_extern
}
- query permits_uninit_init(key: TyAndLayout<'tcx>) -> bool {
- desc { "checking to see if `{}` permits being left uninit", key.ty }
+ query permits_uninit_init(key: ty::ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool {
+ desc { "checking to see if `{}` permits being left uninit", key.value.ty }
}
- query permits_zero_init(key: TyAndLayout<'tcx>) -> bool {
- desc { "checking to see if `{}` permits being left zeroed", key.ty }
+ query permits_zero_init(key: ty::ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool {
+ desc { "checking to see if `{}` permits being left zeroed", key.value.ty }
}
query compare_impl_const(
use std::fmt;
use std::ops::Index;
+pub mod print;
pub mod visit;
macro_rules! thir_with_elements {
--- /dev/null
+use crate::thir::*;
+use crate::ty::{self, TyCtxt};
+
+use std::fmt::{self, Write};
+
+impl<'tcx> TyCtxt<'tcx> {
+ pub fn thir_tree_representation<'a>(self, thir: &'a Thir<'tcx>) -> String {
+ let mut printer = ThirPrinter::new(thir);
+ printer.print();
+ printer.into_buffer()
+ }
+}
+
+struct ThirPrinter<'a, 'tcx> {
+ thir: &'a Thir<'tcx>,
+ fmt: String,
+}
+
+const INDENT: &str = " ";
+
+macro_rules! print_indented {
+ ($writer:ident, $s:expr, $indent_lvl:expr) => {
+ let indent = (0..$indent_lvl).map(|_| INDENT).collect::<Vec<_>>().concat();
+ writeln!($writer, "{}{}", indent, $s).expect("unable to write to ThirPrinter");
+ };
+}
+
+impl<'a, 'tcx> Write for ThirPrinter<'a, 'tcx> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ self.fmt.push_str(s);
+ Ok(())
+ }
+}
+
+impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
+ fn new(thir: &'a Thir<'tcx>) -> Self {
+ Self { thir, fmt: String::new() }
+ }
+
+ fn print(&mut self) {
+ print_indented!(self, "params: [", 0);
+ for param in self.thir.params.iter() {
+ self.print_param(param, 1);
+ }
+ print_indented!(self, "]", 0);
+
+ print_indented!(self, "body:", 0);
+ let expr = ExprId::from_usize(self.thir.exprs.len() - 1);
+ self.print_expr(expr, 1);
+ }
+
+ fn into_buffer(self) -> String {
+ self.fmt
+ }
+
+ fn print_param(&mut self, param: &Param<'tcx>, depth_lvl: usize) {
+ let Param { pat, ty, ty_span, self_kind, hir_id } = param;
+
+ print_indented!(self, "Param {", depth_lvl);
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1);
+ print_indented!(self, format!("ty_span: {:?}", ty_span), depth_lvl + 1);
+ print_indented!(self, format!("self_kind: {:?}", self_kind), depth_lvl + 1);
+ print_indented!(self, format!("hir_id: {:?}", hir_id), depth_lvl + 1);
+
+ if let Some(pat) = pat {
+ print_indented!(self, "param: Some( ", depth_lvl + 1);
+ self.print_pat(pat, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl + 1);
+ } else {
+ print_indented!(self, "param: None", depth_lvl + 1);
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_block(&mut self, block_id: BlockId, depth_lvl: usize) {
+ let Block {
+ targeted_by_break,
+ opt_destruction_scope,
+ span,
+ region_scope,
+ stmts,
+ expr,
+ safety_mode,
+ } = &self.thir.blocks[block_id];
+
+ print_indented!(self, "Block {", depth_lvl);
+ print_indented!(self, format!("targeted_by_break: {}", targeted_by_break), depth_lvl + 1);
+ print_indented!(
+ self,
+ format!("opt_destruction_scope: {:?}", opt_destruction_scope),
+ depth_lvl + 1
+ );
+ print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+ print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
+ print_indented!(self, format!("safety_mode: {:?}", safety_mode), depth_lvl + 1);
+
+ if stmts.len() > 0 {
+ print_indented!(self, "stmts: [", depth_lvl + 1);
+ for stmt in stmts.iter() {
+ self.print_stmt(*stmt, depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ } else {
+ print_indented!(self, "stmts: []", depth_lvl + 1);
+ }
+
+ if let Some(expr_id) = expr {
+ print_indented!(self, "expr:", depth_lvl + 1);
+ self.print_expr(*expr_id, depth_lvl + 2);
+ } else {
+ print_indented!(self, "expr: []", depth_lvl + 1);
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_stmt(&mut self, stmt_id: StmtId, depth_lvl: usize) {
+ let Stmt { kind, opt_destruction_scope } = &self.thir.stmts[stmt_id];
+
+ print_indented!(self, "Stmt {", depth_lvl);
+ print_indented!(
+ self,
+ format!("opt_destruction_scope: {:?}", opt_destruction_scope),
+ depth_lvl + 1
+ );
+
+ match kind {
+ StmtKind::Expr { scope, expr } => {
+ print_indented!(self, "kind: Expr {", depth_lvl + 1);
+ print_indented!(self, format!("scope: {:?}", scope), depth_lvl + 2);
+ print_indented!(self, "expr:", depth_lvl + 2);
+ self.print_expr(*expr, depth_lvl + 3);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ StmtKind::Let {
+ remainder_scope,
+ init_scope,
+ pattern,
+ initializer,
+ else_block,
+ lint_level,
+ } => {
+ print_indented!(self, "kind: Let {", depth_lvl + 1);
+ print_indented!(
+ self,
+ format!("remainder_scope: {:?}", remainder_scope),
+ depth_lvl + 2
+ );
+ print_indented!(self, format!("init_scope: {:?}", init_scope), depth_lvl + 2);
+
+ print_indented!(self, "pattern: ", depth_lvl + 2);
+ self.print_pat(pattern, depth_lvl + 3);
+ print_indented!(self, ",", depth_lvl + 2);
+
+ if let Some(init) = initializer {
+ print_indented!(self, "initializer: Some(", depth_lvl + 2);
+ self.print_expr(*init, depth_lvl + 3);
+ print_indented!(self, ")", depth_lvl + 2);
+ } else {
+ print_indented!(self, "initializer: None", depth_lvl + 2);
+ }
+
+ if let Some(else_block) = else_block {
+ print_indented!(self, "else_block: Some(", depth_lvl + 2);
+ self.print_block(*else_block, depth_lvl + 3);
+ print_indented!(self, ")", depth_lvl + 2);
+ } else {
+ print_indented!(self, "else_block: None", depth_lvl + 2);
+ }
+
+ print_indented!(self, format!("lint_level: {:?}", lint_level), depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_expr(&mut self, expr: ExprId, depth_lvl: usize) {
+ let Expr { ty, temp_lifetime, span, kind } = &self.thir[expr];
+ print_indented!(self, "Expr {", depth_lvl);
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1);
+ print_indented!(self, format!("temp_lifetime: {:?}", temp_lifetime), depth_lvl + 1);
+ print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+ print_indented!(self, "kind: ", depth_lvl + 1);
+ self.print_expr_kind(kind, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_expr_kind(&mut self, expr_kind: &ExprKind<'tcx>, depth_lvl: usize) {
+ use rustc_middle::thir::ExprKind::*;
+
+ match expr_kind {
+ Scope { region_scope, value, lint_level } => {
+ print_indented!(self, "Scope {", depth_lvl);
+ print_indented!(self, format!("region_scope: {:?}", region_scope), depth_lvl + 1);
+ print_indented!(self, format!("lint_level: {:?}", lint_level), depth_lvl + 1);
+ print_indented!(self, "value:", depth_lvl + 1);
+ self.print_expr(*value, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Box { value } => {
+ print_indented!(self, "Box {", depth_lvl);
+ self.print_expr(*value, depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ If { if_then_scope, cond, then, else_opt } => {
+ print_indented!(self, "If {", depth_lvl);
+ print_indented!(self, format!("if_then_scope: {:?}", if_then_scope), depth_lvl + 1);
+ print_indented!(self, "cond:", depth_lvl + 1);
+ self.print_expr(*cond, depth_lvl + 2);
+ print_indented!(self, "then:", depth_lvl + 1);
+ self.print_expr(*then, depth_lvl + 2);
+
+ if let Some(else_expr) = else_opt {
+ print_indented!(self, "else:", depth_lvl + 1);
+ self.print_expr(*else_expr, depth_lvl + 2);
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+ Call { fun, args, ty, from_hir_call, fn_span } => {
+ print_indented!(self, "Call {", depth_lvl);
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1);
+ print_indented!(self, format!("from_hir_call: {}", from_hir_call), depth_lvl + 1);
+ print_indented!(self, format!("fn_span: {:?}", fn_span), depth_lvl + 1);
+ print_indented!(self, "fun:", depth_lvl + 1);
+ self.print_expr(*fun, depth_lvl + 2);
+
+ if args.len() > 0 {
+ print_indented!(self, "args: [", depth_lvl + 1);
+ for arg in args.iter() {
+ self.print_expr(*arg, depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ } else {
+ print_indented!(self, "args: []", depth_lvl + 1);
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+ Deref { arg } => {
+ print_indented!(self, "Deref {", depth_lvl);
+ self.print_expr(*arg, depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Binary { op, lhs, rhs } => {
+ print_indented!(self, "Binary {", depth_lvl);
+ print_indented!(self, format!("op: {:?}", op), depth_lvl + 1);
+ print_indented!(self, "lhs:", depth_lvl + 1);
+ self.print_expr(*lhs, depth_lvl + 2);
+ print_indented!(self, "rhs:", depth_lvl + 1);
+ self.print_expr(*rhs, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ LogicalOp { op, lhs, rhs } => {
+ print_indented!(self, "LogicalOp {", depth_lvl);
+ print_indented!(self, format!("op: {:?}", op), depth_lvl + 1);
+ print_indented!(self, "lhs:", depth_lvl + 1);
+ self.print_expr(*lhs, depth_lvl + 2);
+ print_indented!(self, "rhs:", depth_lvl + 1);
+ self.print_expr(*rhs, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Unary { op, arg } => {
+ print_indented!(self, "Unary {", depth_lvl);
+ print_indented!(self, format!("op: {:?}", op), depth_lvl + 1);
+ print_indented!(self, "arg:", depth_lvl + 1);
+ self.print_expr(*arg, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Cast { source } => {
+ print_indented!(self, "Cast {", depth_lvl);
+ print_indented!(self, "source:", depth_lvl + 1);
+ self.print_expr(*source, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Use { source } => {
+ print_indented!(self, "Use {", depth_lvl);
+ print_indented!(self, "source:", depth_lvl + 1);
+ self.print_expr(*source, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ NeverToAny { source } => {
+ print_indented!(self, "NeverToAny {", depth_lvl);
+ print_indented!(self, "source:", depth_lvl + 1);
+ self.print_expr(*source, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Pointer { cast, source } => {
+ print_indented!(self, "Pointer {", depth_lvl);
+ print_indented!(self, format!("cast: {:?}", cast), depth_lvl + 1);
+ print_indented!(self, "source:", depth_lvl + 1);
+ self.print_expr(*source, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Loop { body } => {
+ print_indented!(self, "Loop (", depth_lvl);
+ print_indented!(self, "body:", depth_lvl + 1);
+ self.print_expr(*body, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl);
+ }
+ Let { expr, pat } => {
+ print_indented!(self, "Let {", depth_lvl);
+ print_indented!(self, "expr:", depth_lvl + 1);
+ self.print_expr(*expr, depth_lvl + 2);
+ print_indented!(self, format!("pat: {:?}", pat), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Match { scrutinee, arms } => {
+ print_indented!(self, "Match {", depth_lvl);
+ print_indented!(self, "scrutinee:", depth_lvl + 1);
+ self.print_expr(*scrutinee, depth_lvl + 2);
+
+ print_indented!(self, "arms: [", depth_lvl + 1);
+ for arm_id in arms.iter() {
+ self.print_arm(*arm_id, depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Block { block } => self.print_block(*block, depth_lvl),
+ Assign { lhs, rhs } => {
+ print_indented!(self, "Assign {", depth_lvl);
+ print_indented!(self, "lhs:", depth_lvl + 1);
+ self.print_expr(*lhs, depth_lvl + 2);
+ print_indented!(self, "rhs:", depth_lvl + 1);
+ self.print_expr(*rhs, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ AssignOp { op, lhs, rhs } => {
+ print_indented!(self, "AssignOp {", depth_lvl);
+ print_indented!(self, format!("op: {:?}", op), depth_lvl + 1);
+ print_indented!(self, "lhs:", depth_lvl + 1);
+ self.print_expr(*lhs, depth_lvl + 2);
+ print_indented!(self, "rhs:", depth_lvl + 1);
+ self.print_expr(*rhs, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Field { lhs, variant_index, name } => {
+ print_indented!(self, "Field {", depth_lvl);
+ print_indented!(self, format!("variant_index: {:?}", variant_index), depth_lvl + 1);
+ print_indented!(self, format!("name: {:?}", name), depth_lvl + 1);
+ print_indented!(self, "lhs:", depth_lvl + 1);
+ self.print_expr(*lhs, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Index { lhs, index } => {
+ print_indented!(self, "Index {", depth_lvl);
+ print_indented!(self, format!("index: {:?}", index), depth_lvl + 1);
+ print_indented!(self, "lhs:", depth_lvl + 1);
+ self.print_expr(*lhs, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ VarRef { id } => {
+ print_indented!(self, "VarRef {", depth_lvl);
+ print_indented!(self, format!("id: {:?}", id), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ UpvarRef { closure_def_id, var_hir_id } => {
+ print_indented!(self, "UpvarRef {", depth_lvl);
+ print_indented!(
+ self,
+ format!("closure_def_id: {:?}", closure_def_id),
+ depth_lvl + 1
+ );
+ print_indented!(self, format!("var_hir_id: {:?}", var_hir_id), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Borrow { borrow_kind, arg } => {
+ print_indented!(self, "Borrow (", depth_lvl);
+ print_indented!(self, format!("borrow_kind: {:?}", borrow_kind), depth_lvl + 1);
+ print_indented!(self, "arg:", depth_lvl + 1);
+ self.print_expr(*arg, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl);
+ }
+ AddressOf { mutability, arg } => {
+ print_indented!(self, "AddressOf {", depth_lvl);
+ print_indented!(self, format!("mutability: {:?}", mutability), depth_lvl + 1);
+ print_indented!(self, "arg:", depth_lvl + 1);
+ self.print_expr(*arg, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Break { label, value } => {
+ print_indented!(self, "Break (", depth_lvl);
+ print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
+
+ if let Some(value) = value {
+ print_indented!(self, "value:", depth_lvl + 1);
+ self.print_expr(*value, depth_lvl + 2);
+ }
+
+ print_indented!(self, ")", depth_lvl);
+ }
+ Continue { label } => {
+ print_indented!(self, "Continue {", depth_lvl);
+ print_indented!(self, format!("label: {:?}", label), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Return { value } => {
+ print_indented!(self, "Return {", depth_lvl);
+ print_indented!(self, "value:", depth_lvl + 1);
+
+ if let Some(value) = value {
+ self.print_expr(*value, depth_lvl + 2);
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+ ConstBlock { did, substs } => {
+ print_indented!(self, "ConstBlock {", depth_lvl);
+ print_indented!(self, format!("did: {:?}", did), depth_lvl + 1);
+ print_indented!(self, format!("substs: {:?}", substs), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Repeat { value, count } => {
+ print_indented!(self, "Repeat {", depth_lvl);
+ print_indented!(self, format!("count: {:?}", count), depth_lvl + 1);
+ print_indented!(self, "value:", depth_lvl + 1);
+ self.print_expr(*value, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Array { fields } => {
+ print_indented!(self, "Array {", depth_lvl);
+ print_indented!(self, "fields: [", depth_lvl + 1);
+ for field_id in fields.iter() {
+ self.print_expr(*field_id, depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Tuple { fields } => {
+ print_indented!(self, "Tuple {", depth_lvl);
+ print_indented!(self, "fields: [", depth_lvl + 1);
+ for field_id in fields.iter() {
+ self.print_expr(*field_id, depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Adt(adt_expr) => {
+ print_indented!(self, "Adt {", depth_lvl);
+ self.print_adt_expr(&**adt_expr, depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ PlaceTypeAscription { source, user_ty } => {
+ print_indented!(self, "PlaceTypeAscription {", depth_lvl);
+ print_indented!(self, format!("user_ty: {:?}", user_ty), depth_lvl + 1);
+ print_indented!(self, "source:", depth_lvl + 1);
+ self.print_expr(*source, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ ValueTypeAscription { source, user_ty } => {
+ print_indented!(self, "ValueTypeAscription {", depth_lvl);
+ print_indented!(self, format!("user_ty: {:?}", user_ty), depth_lvl + 1);
+ print_indented!(self, "source:", depth_lvl + 1);
+ self.print_expr(*source, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Closure(closure_expr) => {
+ print_indented!(self, "Closure {", depth_lvl);
+ print_indented!(self, "closure_expr:", depth_lvl + 1);
+ self.print_closure_expr(&**closure_expr, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Literal { lit, neg } => {
+ print_indented!(
+ self,
+ format!("Literal( lit: {:?}, neg: {:?})\n", lit, neg),
+ depth_lvl
+ );
+ }
+ NonHirLiteral { lit, user_ty } => {
+ print_indented!(self, "NonHirLiteral {", depth_lvl);
+ print_indented!(self, format!("lit: {:?}", lit), depth_lvl + 1);
+ print_indented!(self, format!("user_ty: {:?}", user_ty), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ ZstLiteral { user_ty } => {
+ print_indented!(self, format!("ZstLiteral(user_ty: {:?})", user_ty), depth_lvl);
+ }
+ NamedConst { def_id, substs, user_ty } => {
+ print_indented!(self, "NamedConst {", depth_lvl);
+ print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);
+ print_indented!(self, format!("user_ty: {:?}", user_ty), depth_lvl + 1);
+ print_indented!(self, format!("substs: {:?}", substs), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ ConstParam { param, def_id } => {
+ print_indented!(self, "ConstParam {", depth_lvl);
+ print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);
+ print_indented!(self, format!("param: {:?}", param), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ StaticRef { alloc_id, ty, def_id } => {
+ print_indented!(self, "StaticRef {", depth_lvl);
+ print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1);
+ print_indented!(self, format!("alloc_id: {:?}", alloc_id), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ InlineAsm(expr) => {
+ print_indented!(self, "InlineAsm {", depth_lvl);
+ print_indented!(self, "expr:", depth_lvl + 1);
+ self.print_inline_asm_expr(&**expr, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ ThreadLocalRef(def_id) => {
+ print_indented!(self, "ThreadLocalRef {", depth_lvl);
+ print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+ Yield { value } => {
+ print_indented!(self, "Yield {", depth_lvl);
+ print_indented!(self, "value:", depth_lvl + 1);
+ self.print_expr(*value, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl);
+ }
+ }
+ }
+
+ fn print_adt_expr(&mut self, adt_expr: &AdtExpr<'tcx>, depth_lvl: usize) {
+ print_indented!(self, "adt_def:", depth_lvl);
+ self.print_adt_def(adt_expr.adt_def, depth_lvl + 1);
+ print_indented!(
+ self,
+ format!("variant_index: {:?}", adt_expr.variant_index),
+ depth_lvl + 1
+ );
+ print_indented!(self, format!("substs: {:?}", adt_expr.substs), depth_lvl + 1);
+ print_indented!(self, format!("user_ty: {:?}", adt_expr.user_ty), depth_lvl + 1);
+
+ for (i, field_expr) in adt_expr.fields.iter().enumerate() {
+ print_indented!(self, format!("field {}:", i), depth_lvl + 1);
+ self.print_expr(field_expr.expr, depth_lvl + 2);
+ }
+
+ if let Some(ref base) = adt_expr.base {
+ print_indented!(self, "base:", depth_lvl + 1);
+ self.print_fru_info(base, depth_lvl + 2);
+ } else {
+ print_indented!(self, "base: None", depth_lvl + 1);
+ }
+ }
+
+ fn print_adt_def(&mut self, adt_def: ty::AdtDef<'tcx>, depth_lvl: usize) {
+ print_indented!(self, "AdtDef {", depth_lvl);
+ print_indented!(self, format!("did: {:?}", adt_def.did()), depth_lvl + 1);
+ print_indented!(self, format!("variants: {:?}", adt_def.variants()), depth_lvl + 1);
+ print_indented!(self, format!("flags: {:?}", adt_def.flags()), depth_lvl + 1);
+ print_indented!(self, format!("repr: {:?}", adt_def.repr()), depth_lvl + 1);
+ }
+
+ fn print_fru_info(&mut self, fru_info: &FruInfo<'tcx>, depth_lvl: usize) {
+ print_indented!(self, "FruInfo {", depth_lvl);
+ print_indented!(self, "base: ", depth_lvl + 1);
+ self.print_expr(fru_info.base, depth_lvl + 2);
+ print_indented!(self, "field_types: [", depth_lvl + 1);
+ for ty in fru_info.field_types.iter() {
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 2);
+ }
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_arm(&mut self, arm_id: ArmId, depth_lvl: usize) {
+ print_indented!(self, "Arm {", depth_lvl);
+
+ let arm = &self.thir.arms[arm_id];
+ let Arm { pattern, guard, body, lint_level, scope, span } = arm;
+
+ print_indented!(self, "pattern: ", depth_lvl + 1);
+ self.print_pat(pattern, depth_lvl + 2);
+
+ if let Some(guard) = guard {
+ print_indented!(self, "guard: ", depth_lvl + 1);
+ self.print_guard(guard, depth_lvl + 2);
+ } else {
+ print_indented!(self, "guard: None", depth_lvl + 1);
+ }
+
+ print_indented!(self, "body: ", depth_lvl + 1);
+ self.print_expr(*body, depth_lvl + 2);
+ print_indented!(self, format!("lint_level: {:?}", lint_level), depth_lvl + 1);
+ print_indented!(self, format!("scope: {:?}", scope), depth_lvl + 1);
+ print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_pat(&mut self, pat: &Box<Pat<'tcx>>, depth_lvl: usize) {
+ let Pat { ty, span, kind } = &**pat;
+
+ print_indented!(self, "Pat: {", depth_lvl);
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 1);
+ print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+ self.print_pat_kind(kind, depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_pat_kind(&mut self, pat_kind: &PatKind<'tcx>, depth_lvl: usize) {
+ print_indented!(self, "kind: PatKind {", depth_lvl);
+
+ match pat_kind {
+ PatKind::Wild => {
+ print_indented!(self, "Wild", depth_lvl + 1);
+ }
+ PatKind::AscribeUserType { ascription, subpattern } => {
+ print_indented!(self, "AscribeUserType: {", depth_lvl + 1);
+ print_indented!(self, format!("ascription: {:?}", ascription), depth_lvl + 2);
+ print_indented!(self, "subpattern: ", depth_lvl + 2);
+ self.print_pat(subpattern, depth_lvl + 3);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Binding { mutability, name, mode, var, ty, subpattern, is_primary } => {
+ print_indented!(self, "Binding {", depth_lvl + 1);
+ print_indented!(self, format!("mutability: {:?}", mutability), depth_lvl + 2);
+ print_indented!(self, format!("name: {:?}", name), depth_lvl + 2);
+ print_indented!(self, format!("mode: {:?}", mode), depth_lvl + 2);
+ print_indented!(self, format!("var: {:?}", var), depth_lvl + 2);
+ print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 2);
+ print_indented!(self, format!("is_primary: {:?}", is_primary), depth_lvl + 2);
+
+ if let Some(subpattern) = subpattern {
+ print_indented!(self, "subpattern: Some( ", depth_lvl + 2);
+ self.print_pat(subpattern, depth_lvl + 3);
+ print_indented!(self, ")", depth_lvl + 2);
+ } else {
+ print_indented!(self, "subpattern: None", depth_lvl + 2);
+ }
+
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Variant { adt_def, substs, variant_index, subpatterns } => {
+ print_indented!(self, "Variant {", depth_lvl + 1);
+ print_indented!(self, "adt_def: ", depth_lvl + 2);
+ self.print_adt_def(*adt_def, depth_lvl + 3);
+ print_indented!(self, format!("substs: {:?}", substs), depth_lvl + 2);
+ print_indented!(self, format!("variant_index: {:?}", variant_index), depth_lvl + 2);
+
+ if subpatterns.len() > 0 {
+ print_indented!(self, "subpatterns: [", depth_lvl + 2);
+ for field_pat in subpatterns.iter() {
+ self.print_pat(&field_pat.pattern, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+ } else {
+ print_indented!(self, "subpatterns: []", depth_lvl + 2);
+ }
+
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Leaf { subpatterns } => {
+ print_indented!(self, "Leaf { ", depth_lvl + 1);
+ print_indented!(self, "subpatterns: [", depth_lvl + 2);
+ for field_pat in subpatterns.iter() {
+ self.print_pat(&field_pat.pattern, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Deref { subpattern } => {
+ print_indented!(self, "Deref { ", depth_lvl + 1);
+ print_indented!(self, "subpattern: ", depth_lvl + 2);
+ self.print_pat(subpattern, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Constant { value } => {
+ print_indented!(self, "Constant {", depth_lvl + 1);
+ print_indented!(self, format!("value: {:?}", value), depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Range(pat_range) => {
+ print_indented!(self, format!("Range ( {:?} )", pat_range), depth_lvl + 1);
+ }
+ PatKind::Slice { prefix, slice, suffix } => {
+ print_indented!(self, "Slice {", depth_lvl + 1);
+
+ print_indented!(self, "prefix: [", depth_lvl + 2);
+ for prefix_pat in prefix.iter() {
+ self.print_pat(prefix_pat, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+
+ if let Some(slice) = slice {
+ print_indented!(self, "slice: ", depth_lvl + 2);
+ self.print_pat(slice, depth_lvl + 3);
+ }
+
+ print_indented!(self, "suffix: [", depth_lvl + 2);
+ for suffix_pat in suffix.iter() {
+ self.print_pat(suffix_pat, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Array { prefix, slice, suffix } => {
+ print_indented!(self, "Array {", depth_lvl + 1);
+
+ print_indented!(self, "prefix: [", depth_lvl + 2);
+ for prefix_pat in prefix.iter() {
+ self.print_pat(prefix_pat, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+
+ if let Some(slice) = slice {
+ print_indented!(self, "slice: ", depth_lvl + 2);
+ self.print_pat(slice, depth_lvl + 3);
+ }
+
+ print_indented!(self, "suffix: [", depth_lvl + 2);
+ for suffix_pat in suffix.iter() {
+ self.print_pat(suffix_pat, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ PatKind::Or { pats } => {
+ print_indented!(self, "Or {", depth_lvl + 1);
+ print_indented!(self, "pats: [", depth_lvl + 2);
+ for pat in pats.iter() {
+ self.print_pat(pat, depth_lvl + 3);
+ }
+ print_indented!(self, "]", depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_guard(&mut self, guard: &Guard<'tcx>, depth_lvl: usize) {
+ print_indented!(self, "Guard {", depth_lvl);
+
+ match guard {
+ Guard::If(expr_id) => {
+ print_indented!(self, "If (", depth_lvl + 1);
+ self.print_expr(*expr_id, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl + 1);
+ }
+ Guard::IfLet(pat, expr_id) => {
+ print_indented!(self, "IfLet (", depth_lvl + 1);
+ self.print_pat(pat, depth_lvl + 2);
+ print_indented!(self, ",", depth_lvl + 1);
+ self.print_expr(*expr_id, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl + 1);
+ }
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_closure_expr(&mut self, expr: &ClosureExpr<'tcx>, depth_lvl: usize) {
+ let ClosureExpr { closure_id, substs, upvars, movability, fake_reads } = expr;
+
+ print_indented!(self, "ClosureExpr {", depth_lvl);
+ print_indented!(self, format!("closure_id: {:?}", closure_id), depth_lvl + 1);
+ print_indented!(self, format!("substs: {:?}", substs), depth_lvl + 1);
+
+ if upvars.len() > 0 {
+ print_indented!(self, "upvars: [", depth_lvl + 1);
+ for upvar in upvars.iter() {
+ self.print_expr(*upvar, depth_lvl + 2);
+ print_indented!(self, ",", depth_lvl + 1);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ } else {
+ print_indented!(self, "upvars: []", depth_lvl + 1);
+ }
+
+ print_indented!(self, format!("movability: {:?}", movability), depth_lvl + 1);
+
+ if fake_reads.len() > 0 {
+ print_indented!(self, "fake_reads: [", depth_lvl + 1);
+ for (fake_read_expr, cause, hir_id) in fake_reads.iter() {
+ print_indented!(self, "(", depth_lvl + 2);
+ self.print_expr(*fake_read_expr, depth_lvl + 3);
+ print_indented!(self, ",", depth_lvl + 2);
+ print_indented!(self, format!("cause: {:?}", cause), depth_lvl + 3);
+ print_indented!(self, ",", depth_lvl + 2);
+ print_indented!(self, format!("hir_id: {:?}", hir_id), depth_lvl + 3);
+ print_indented!(self, "),", depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+ } else {
+ print_indented!(self, "fake_reads: []", depth_lvl + 1);
+ }
+
+ print_indented!(self, "}", depth_lvl);
+ }
+
+ fn print_inline_asm_expr(&mut self, expr: &InlineAsmExpr<'tcx>, depth_lvl: usize) {
+ let InlineAsmExpr { template, operands, options, line_spans } = expr;
+
+ print_indented!(self, "InlineAsmExpr {", depth_lvl);
+
+ print_indented!(self, "template: [", depth_lvl + 1);
+ for template_piece in template.iter() {
+ print_indented!(self, format!("{:?}", template_piece), depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+
+ print_indented!(self, "operands: [", depth_lvl + 1);
+ for operand in operands.iter() {
+ self.print_inline_operand(operand, depth_lvl + 2);
+ }
+ print_indented!(self, "]", depth_lvl + 1);
+
+ print_indented!(self, format!("options: {:?}", options), depth_lvl + 1);
+ print_indented!(self, format!("line_spans: {:?}", line_spans), depth_lvl + 1);
+ }
+
+ fn print_inline_operand(&mut self, operand: &InlineAsmOperand<'tcx>, depth_lvl: usize) {
+ match operand {
+ InlineAsmOperand::In { reg, expr } => {
+ print_indented!(self, "InlineAsmOperand::In {", depth_lvl);
+ print_indented!(self, format!("reg: {:?}", reg), depth_lvl + 1);
+ print_indented!(self, "expr: ", depth_lvl + 1);
+ self.print_expr(*expr, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ InlineAsmOperand::Out { reg, late, expr } => {
+ print_indented!(self, "InlineAsmOperand::Out {", depth_lvl);
+ print_indented!(self, format!("reg: {:?}", reg), depth_lvl + 1);
+ print_indented!(self, format!("late: {:?}", late), depth_lvl + 1);
+
+ if let Some(out) = expr {
+ print_indented!(self, "place: Some( ", depth_lvl + 1);
+ self.print_expr(*out, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl + 1);
+ } else {
+ print_indented!(self, "place: None", depth_lvl + 1);
+ }
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ InlineAsmOperand::InOut { reg, late, expr } => {
+ print_indented!(self, "InlineAsmOperand::InOut {", depth_lvl);
+ print_indented!(self, format!("reg: {:?}", reg), depth_lvl + 1);
+ print_indented!(self, format!("late: {:?}", late), depth_lvl + 1);
+ print_indented!(self, "expr: ", depth_lvl + 1);
+ self.print_expr(*expr, depth_lvl + 2);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
+ print_indented!(self, "InlineAsmOperand::SplitInOut {", depth_lvl);
+ print_indented!(self, format!("reg: {:?}", reg), depth_lvl + 1);
+ print_indented!(self, format!("late: {:?}", late), depth_lvl + 1);
+ print_indented!(self, "in_expr: ", depth_lvl + 1);
+ self.print_expr(*in_expr, depth_lvl + 2);
+
+ if let Some(out_expr) = out_expr {
+ print_indented!(self, "out_expr: Some( ", depth_lvl + 1);
+ self.print_expr(*out_expr, depth_lvl + 2);
+ print_indented!(self, ")", depth_lvl + 1);
+ } else {
+ print_indented!(self, "out_expr: None", depth_lvl + 1);
+ }
+
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ InlineAsmOperand::Const { value, span } => {
+ print_indented!(self, "InlineAsmOperand::Const {", depth_lvl);
+ print_indented!(self, format!("value: {:?}", value), depth_lvl + 1);
+ print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ InlineAsmOperand::SymFn { value, span } => {
+ print_indented!(self, "InlineAsmOperand::SymFn {", depth_lvl);
+ print_indented!(self, format!("value: {:?}", *value), depth_lvl + 1);
+ print_indented!(self, format!("span: {:?}", span), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ InlineAsmOperand::SymStatic { def_id } => {
+ print_indented!(self, "InlineAsmOperand::SymStatic {", depth_lvl);
+ print_indented!(self, format!("def_id: {:?}", def_id), depth_lvl + 1);
+ print_indented!(self, "}", depth_lvl + 1);
+ }
+ }
+ }
+}
}
chalk_ir::TyKind::Array(ty, len) => Some(write!(fmt, "[{:?}; {:?}]", ty, len)),
chalk_ir::TyKind::Slice(ty) => Some(write!(fmt, "[{:?}]", ty)),
- chalk_ir::TyKind::Tuple(len, substs) => Some((|| {
- write!(fmt, "(")?;
- for (idx, substitution) in substs.interned().iter().enumerate() {
- if idx == *len && *len != 1 {
- // Don't add a trailing comma if the tuple has more than one element
- write!(fmt, "{:?}", substitution)?;
- } else {
- write!(fmt, "{:?},", substitution)?;
+ chalk_ir::TyKind::Tuple(len, substs) => Some(
+ try {
+ write!(fmt, "(")?;
+ for (idx, substitution) in substs.interned().iter().enumerate() {
+ if idx == *len && *len != 1 {
+ // Don't add a trailing comma if the tuple has more than one element
+ write!(fmt, "{:?}", substitution)?;
+ } else {
+ write!(fmt, "{:?},", substitution)?;
+ }
}
- }
- write!(fmt, ")")
- })()),
+ write!(fmt, ")")?;
+ },
+ ),
_ => None,
}
}
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, Diagnostic};
use rustc_hir as hir;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::DefId;
+use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID};
use rustc_span::symbol::Symbol;
use rustc_span::{Span, DUMMY_SP};
use smallvec::SmallVec;
/// Depending on the stage of compilation, we want projection to be
/// more or less conservative.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable, Encodable, Decodable)]
pub enum Reveal {
/// At type-checking time, we refuse to project any associated
/// type that is marked `default`. Non-`default` ("final") types
///
/// We do not want to intern this as there are a lot of obligation causes which
/// only live for a short period of time.
-#[derive(Clone, Debug, PartialEq, Eq, Lift)]
+#[derive(Clone, Debug, PartialEq, Eq, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub struct ObligationCause<'tcx> {
pub span: Span,
/// (in particular, closures can add new assumptions). See the
/// field `region_obligations` of the `FulfillmentContext` for more
/// information.
- pub body_id: hir::HirId,
+ pub body_id: LocalDefId,
code: InternedObligationCauseCode<'tcx>,
}
#[inline]
pub fn new(
span: Span,
- body_id: hir::HirId,
+ body_id: LocalDefId,
code: ObligationCauseCode<'tcx>,
) -> ObligationCause<'tcx> {
ObligationCause { span, body_id, code: code.into() }
}
- pub fn misc(span: Span, body_id: hir::HirId) -> ObligationCause<'tcx> {
+ pub fn misc(span: Span, body_id: LocalDefId) -> ObligationCause<'tcx> {
ObligationCause::new(span, body_id, MiscObligation)
}
#[inline(always)]
pub fn dummy_with_span(span: Span) -> ObligationCause<'tcx> {
- ObligationCause { span, body_id: hir::CRATE_HIR_ID, code: Default::default() }
+ ObligationCause { span, body_id: CRATE_DEF_ID, code: Default::default() }
}
pub fn span(&self) -> Span {
}
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub struct UnifyReceiverContext<'tcx> {
pub assoc_item: ty::AssocItem,
pub param_env: ty::ParamEnv<'tcx>,
pub substs: SubstsRef<'tcx>,
}
-#[derive(Clone, PartialEq, Eq, Hash, Lift, Default)]
+#[derive(Clone, PartialEq, Eq, Hash, Lift, Default, HashStable)]
+#[derive(TypeVisitable, TypeFoldable, TyEncodable, TyDecodable)]
pub struct InternedObligationCauseCode<'tcx> {
/// `None` for `ObligationCauseCode::MiscObligation` (a common case, occurs ~60% of
/// the time). `Some` otherwise.
}
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub enum ObligationCauseCode<'tcx> {
/// Not well classified or should be obvious from the span.
MiscObligation,
/// This information is used to obtain an `hir::Ty`, which
/// we can walk in order to obtain precise spans for any
/// 'nested' types (e.g. `Foo` in `Option<Foo>`).
-#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, Encodable, Decodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub enum WellFormedLoc {
/// Use the type of the provided definition.
Ty(LocalDefId),
},
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub struct ImplDerivedObligationCause<'tcx> {
pub derived: DerivedObligationCause<'tcx>,
pub impl_def_id: DefId,
}
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub struct MatchExpressionArmCause<'tcx> {
pub arm_block_id: Option<hir::HirId>,
pub arm_ty: Ty<'tcx>,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
-#[derive(Lift, TypeFoldable, TypeVisitable)]
+#[derive(Lift, TypeFoldable, TypeVisitable, HashStable, TyEncodable, TyDecodable)]
pub struct IfExpressionCause<'tcx> {
pub then_id: hir::HirId,
pub else_id: hir::HirId,
pub opt_suggest_box_span: Option<Span>,
}
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
pub struct DerivedObligationCause<'tcx> {
/// The trait predicate of the parent obligation that led to the
/// current obligation. Note that only trait obligations lead to
use crate::error::DropCheckOverflow;
use crate::infer::canonical::{Canonical, QueryResponse};
use crate::ty::error::TypeError;
-use crate::ty::subst::{GenericArg, SubstsRef};
+use crate::ty::subst::GenericArg;
use crate::ty::{self, Ty, TyCtxt};
-use rustc_hir::def_id::DefId;
use rustc_span::source_map::Span;
pub mod type_op {
pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
- RegionSubProjection(ty::Region<'tcx>, ty::AliasTy<'tcx>),
- RegionSubOpaque(ty::Region<'tcx>, DefId, SubstsRef<'tcx>),
+ RegionSubAlias(ty::Region<'tcx>, ty::AliasTy<'tcx>),
}
}
}
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, TyEncodable, TyDecodable)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, HashStable, TyEncodable, TyDecodable)]
pub enum AdtKind {
Struct,
Union,
Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())
}
+ /// Gets the defaultness of the associated item.
+ /// To get the default associated type, use the [`type_of`] query on the
+ /// [`DefId`] of the type.
+ ///
+ /// [`type_of`]: crate::ty::TyCtxt::type_of
pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness {
tcx.impl_defaultness(self.def_id)
}
ty::AssocKind::Fn => {
// We skip the binder here because the binder would deanonymize all
// late-bound regions, and we don't want method signatures to show up
- // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
+ // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
- tcx.fn_sig(self.def_id).skip_binder().to_string()
+ tcx.fn_sig(self.def_id).subst_identity().skip_binder().to_string()
}
ty::AssocKind::Type => format!("type {};", self.name),
ty::AssocKind::Const => {
/// done only on items with the same name.
#[derive(Debug, Clone, PartialEq, HashStable)]
pub struct AssocItems<'tcx> {
- pub(super) items: SortedIndexMultiMap<u32, Symbol, &'tcx ty::AssocItem>,
+ items: SortedIndexMultiMap<u32, Symbol, &'tcx ty::AssocItem>,
}
impl<'tcx> AssocItems<'tcx> {
}
}
+impl<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>> Encodable<E> for ty::ParamEnv<'tcx> {
+ fn encode(&self, e: &mut E) {
+ self.caller_bounds().encode(e);
+ self.reveal().encode(e);
+ self.constness().encode(e);
+ }
+}
+
#[inline]
fn decode_arena_allocable<
'tcx,
}
}
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::ParamEnv<'tcx> {
+ fn decode(d: &mut D) -> Self {
+ let caller_bounds = Decodable::decode(d);
+ let reveal = Decodable::decode(d);
+ let constness = Decodable::decode(d);
+ ty::ParamEnv::new(caller_bounds, reveal, constness)
+ }
+}
+
macro_rules! impl_decodable_via_ref {
- ($($t:ty),+) => {
+ ($($t:ty,)+) => {
$(impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for $t {
fn decode(decoder: &mut D) -> Self {
RefDecodable::decode(decoder)
}
}
+impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> RefDecodable<'tcx, D> for ty::List<ty::Predicate<'tcx>> {
+ fn decode(decoder: &mut D) -> &'tcx Self {
+ let len = decoder.read_usize();
+ let predicates: Vec<_> =
+ (0..len).map::<ty::Predicate<'tcx>, _>(|_| Decodable::decode(decoder)).collect();
+ decoder.interner().intern_predicates(&predicates)
+ }
+}
+
impl_decodable_via_ref! {
&'tcx ty::TypeckResults<'tcx>,
&'tcx ty::List<Ty<'tcx>>,
&'tcx mir::UnsafetyCheckResult,
&'tcx mir::BorrowCheckResult<'tcx>,
&'tcx mir::coverage::CodeRegion,
- &'tcx ty::List<ty::BoundVariableKind>
+ &'tcx ty::List<ty::BoundVariableKind>,
+ &'tcx ty::List<ty::Predicate<'tcx>>,
}
#[macro_export]
impl_binder_encode_decode! {
&'tcx ty::List<Ty<'tcx>>,
ty::FnSig<'tcx>,
+ ty::Predicate<'tcx>,
+ ty::TraitPredicate<'tcx>,
ty::ExistentialPredicate<'tcx>,
ty::TraitRef<'tcx>,
Vec<ty::GeneratorInteriorTypeCause<'tcx>>,
}
}
-pub fn const_param_default(tcx: TyCtxt<'_>, def_id: DefId) -> Const<'_> {
+pub fn const_param_default(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Const<'_>> {
let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) {
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Const { default: Some(ac), .. },
"`const_param_default` expected a generic parameter with a constant"
),
};
- Const::from_anon_const(tcx, default_def_id)
+ ty::EarlyBinder(Const::from_anon_const(tcx, default_def_id))
}
#![allow(rustc::usage_of_ty_tykind)]
+pub mod tls;
+
use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKindStruct};
use crate::infer::canonical::{CanonicalVarInfo, CanonicalVarInfos};
use std::mem;
use std::ops::{Bound, Deref};
+const TINY_CONST_EVAL_LIMIT: Limit = Limit(20);
+
pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync {
/// Creates a new `OnDiskCache` instance from the serialized data in `data`.
fn new(sess: &'tcx Session, data: Mmap, start_pos: usize) -> Self
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
- // while iterating. If some query needs to add definitions, it should be `ensure`d above.
+ // while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.untracked.definitions.leak();
definitions.def_path_table()
}
// definitions change.
self.ensure().hir_crate(());
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
- // while iterating. If some query needs to add definitions, it should be `ensure`d above.
+ // while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.untracked.definitions.leak();
definitions.def_path_hash_to_def_index_map()
}
v.0
}
+ /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type and associated alias span when type alias is used
+ pub fn return_type_impl_or_dyn_traits_with_type_alias(
+ self,
+ scope_def_id: LocalDefId,
+ ) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span)> {
+ let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
+ let mut v = TraitObjectVisitor(vec![], self.hir());
+ // when the return type is a type alias
+ if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir().fn_decl_by_hir_id(hir_id)
+ && let hir::TyKind::Path(hir::QPath::Resolved(
+ None,
+ hir::Path { res: hir::def::Res::Def(DefKind::TyAlias, def_id), .. }, )) = hir_output.kind
+ && let Some(local_id) = def_id.as_local()
+ && let Some(alias_ty) = self.hir().get_by_def_id(local_id).alias_ty() // it is type alias
+ && let Some(alias_generics) = self.hir().get_by_def_id(local_id).generics()
+ {
+ v.visit_ty(alias_ty);
+ if !v.0.is_empty() {
+ return Some((v.0, alias_generics.span));
+ }
+ }
+ return None;
+ }
+
pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
// `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures.
match self.hir().get_by_def_id(scope_def_id) {
}
pub fn const_eval_limit(self) -> Limit {
- self.limits(()).const_eval_limit
+ if self.sess.opts.unstable_opts.tiny_const_eval_limit {
+ TINY_CONST_EVAL_LIMIT
+ } else {
+ self.limits(()).const_eval_limit
+ }
}
pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
Constness, traits::WellFormedLoc, ImplPolarity, crate::mir::ReturnConstraint,
} }
-pub mod tls {
- use super::{ptr_eq, GlobalCtxt, TyCtxt};
-
- use crate::dep_graph::TaskDepsRef;
- use crate::ty::query;
- use rustc_data_structures::sync::{self, Lock};
- use rustc_errors::Diagnostic;
- use std::mem;
- use thin_vec::ThinVec;
-
- #[cfg(not(parallel_compiler))]
- use std::cell::Cell;
-
- #[cfg(parallel_compiler)]
- use rustc_rayon_core as rayon_core;
-
- /// This is the implicit state of rustc. It contains the current
- /// `TyCtxt` and query. It is updated when creating a local interner or
- /// executing a new query. Whenever there's a `TyCtxt` value available
- /// you should also have access to an `ImplicitCtxt` through the functions
- /// in this module.
- #[derive(Clone)]
- pub struct ImplicitCtxt<'a, 'tcx> {
- /// The current `TyCtxt`.
- pub tcx: TyCtxt<'tcx>,
-
- /// The current query job, if any. This is updated by `JobOwner::start` in
- /// `ty::query::plumbing` when executing a query.
- pub query: Option<query::QueryJobId>,
-
- /// Where to store diagnostics for the current query job, if any.
- /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
- pub diagnostics: Option<&'a Lock<ThinVec<Diagnostic>>>,
-
- /// Used to prevent queries from calling too deeply.
- pub query_depth: usize,
-
- /// The current dep graph task. This is used to add dependencies to queries
- /// when executing them.
- pub task_deps: TaskDepsRef<'a>,
- }
-
- impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> {
- pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self {
- let tcx = TyCtxt { gcx };
- ImplicitCtxt {
- tcx,
- query: None,
- diagnostics: None,
- query_depth: 0,
- task_deps: TaskDepsRef::Ignore,
- }
- }
- }
-
- /// Sets Rayon's thread-local variable, which is preserved for Rayon jobs
- /// to `value` during the call to `f`. It is restored to its previous value after.
- /// This is used to set the pointer to the new `ImplicitCtxt`.
- #[cfg(parallel_compiler)]
- #[inline]
- fn set_tlv<F: FnOnce() -> R, R>(value: usize, f: F) -> R {
- rayon_core::tlv::with(value, f)
- }
-
- /// Gets Rayon's thread-local variable, which is preserved for Rayon jobs.
- /// This is used to get the pointer to the current `ImplicitCtxt`.
- #[cfg(parallel_compiler)]
- #[inline]
- pub fn get_tlv() -> usize {
- rayon_core::tlv::get()
- }
-
- #[cfg(not(parallel_compiler))]
- thread_local! {
- /// A thread local variable that stores a pointer to the current `ImplicitCtxt`.
- static TLV: Cell<usize> = const { Cell::new(0) };
- }
-
- /// Sets TLV to `value` during the call to `f`.
- /// It is restored to its previous value after.
- /// This is used to set the pointer to the new `ImplicitCtxt`.
- #[cfg(not(parallel_compiler))]
- #[inline]
- fn set_tlv<F: FnOnce() -> R, R>(value: usize, f: F) -> R {
- let old = get_tlv();
- let _reset = rustc_data_structures::OnDrop(move || TLV.with(|tlv| tlv.set(old)));
- TLV.with(|tlv| tlv.set(value));
- f()
- }
-
- /// Gets the pointer to the current `ImplicitCtxt`.
- #[cfg(not(parallel_compiler))]
- #[inline]
- fn get_tlv() -> usize {
- TLV.with(|tlv| tlv.get())
- }
-
- /// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`.
- #[inline]
- pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R
- where
- F: FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R,
- {
- set_tlv(context as *const _ as usize, || f(&context))
- }
-
- /// Allows access to the current `ImplicitCtxt` in a closure if one is available.
- #[inline]
- pub fn with_context_opt<F, R>(f: F) -> R
- where
- F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R,
- {
- let context = get_tlv();
- if context == 0 {
- f(None)
- } else {
- // We could get an `ImplicitCtxt` pointer from another thread.
- // Ensure that `ImplicitCtxt` is `Sync`.
- sync::assert_sync::<ImplicitCtxt<'_, '_>>();
-
- unsafe { f(Some(&*(context as *const ImplicitCtxt<'_, '_>))) }
- }
- }
-
- /// Allows access to the current `ImplicitCtxt`.
- /// Panics if there is no `ImplicitCtxt` available.
- #[inline]
- pub fn with_context<F, R>(f: F) -> R
- where
- F: for<'a, 'tcx> FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R,
- {
- with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
- }
-
- /// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument
- /// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime
- /// as the `TyCtxt` passed in.
- /// This will panic if you pass it a `TyCtxt` which is different from the current
- /// `ImplicitCtxt`'s `tcx` field.
- #[inline]
- pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R
- where
- F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R,
- {
- with_context(|context| unsafe {
- assert!(ptr_eq(context.tcx.gcx, tcx.gcx));
- let context: &ImplicitCtxt<'_, '_> = mem::transmute(context);
- f(context)
- })
- }
-
- /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
- /// Panics if there is no `ImplicitCtxt` available.
- #[inline]
- pub fn with<F, R>(f: F) -> R
- where
- F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R,
- {
- with_context(|context| f(context.tcx))
- }
-
- /// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
- /// The closure is passed None if there is no `ImplicitCtxt` available.
- #[inline]
- pub fn with_opt<F, R>(f: F) -> R
- where
- F: for<'tcx> FnOnce(Option<TyCtxt<'tcx>>) -> R,
- {
- with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
- }
-}
-
macro_rules! sty_debug_print {
($fmt: expr, $ctxt: expr, $($variant: ident),*) => {{
// Curious inner module to allow variant names to be used as
Placeholder,
Generator,
GeneratorWitness,
+ GeneratorWitnessMIR,
Dynamic,
Closure,
Tuple,
self.mk_ty(GeneratorWitness(types))
}
+ /// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes.
+ pub fn mk_task_context(self) -> Ty<'tcx> {
+ let context_did = self.require_lang_item(LangItem::Context, None);
+ let context_adt_ref = self.adt_def(context_did);
+ let context_substs = self.intern_substs(&[self.lifetimes.re_erased.into()]);
+ let context_ty = self.mk_adt(context_adt_ref, context_substs);
+ self.mk_mut_ref(self.lifetimes.re_erased, context_ty)
+ }
+
+ #[inline]
+ pub fn mk_generator_witness_mir(self, id: DefId, substs: SubstsRef<'tcx>) -> Ty<'tcx> {
+ self.mk_ty(GeneratorWitnessMIR(id, substs))
+ }
+
#[inline]
pub fn mk_ty_var(self, v: TyVid) -> Ty<'tcx> {
self.mk_ty_infer(TyVar(v))
}
pub fn is_late_bound(self, id: HirId) -> bool {
- self.is_late_bound_map(id.owner.def_id).map_or(false, |set| {
- let def_id = self.hir().local_def_id(id);
- set.contains(&def_id)
- })
+ self.is_late_bound_map(id.owner).map_or(false, |set| set.contains(&id.local_id))
}
pub fn late_bound_vars(self, id: HirId) -> &'tcx List<ty::BoundVariableKind> {
pub read_only: bool,
}
-// We are comparing types with different invariant lifetimes, so `ptr::eq`
-// won't work for us.
-fn ptr_eq<T, U>(t: *const T, u: *const U) -> bool {
- t as *const () == u as *const ()
-}
-
pub fn provide(providers: &mut ty::query::Providers) {
providers.module_reexports =
|tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]);
--- /dev/null
+use super::{GlobalCtxt, TyCtxt};
+
+use crate::dep_graph::TaskDepsRef;
+use crate::ty::query;
+use rustc_data_structures::sync::{self, Lock};
+use rustc_errors::Diagnostic;
+use std::mem;
+use std::ptr;
+use thin_vec::ThinVec;
+
+/// This is the implicit state of rustc. It contains the current
+/// `TyCtxt` and query. It is updated when creating a local interner or
+/// executing a new query. Whenever there's a `TyCtxt` value available
+/// you should also have access to an `ImplicitCtxt` through the functions
+/// in this module.
+#[derive(Clone)]
+pub struct ImplicitCtxt<'a, 'tcx> {
+ /// The current `TyCtxt`.
+ pub tcx: TyCtxt<'tcx>,
+
+ /// The current query job, if any. This is updated by `JobOwner::start` in
+ /// `ty::query::plumbing` when executing a query.
+ pub query: Option<query::QueryJobId>,
+
+ /// Where to store diagnostics for the current query job, if any.
+ /// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
+ pub diagnostics: Option<&'a Lock<ThinVec<Diagnostic>>>,
+
+ /// Used to prevent queries from calling too deeply.
+ pub query_depth: usize,
+
+ /// The current dep graph task. This is used to add dependencies to queries
+ /// when executing them.
+ pub task_deps: TaskDepsRef<'a>,
+}
+
+impl<'a, 'tcx> ImplicitCtxt<'a, 'tcx> {
+ pub fn new(gcx: &'tcx GlobalCtxt<'tcx>) -> Self {
+ let tcx = TyCtxt { gcx };
+ ImplicitCtxt {
+ tcx,
+ query: None,
+ diagnostics: None,
+ query_depth: 0,
+ task_deps: TaskDepsRef::Ignore,
+ }
+ }
+}
+
+#[cfg(parallel_compiler)]
+mod tlv {
+ use rustc_rayon_core as rayon_core;
+ use std::ptr;
+
+ /// Gets Rayon's thread-local variable, which is preserved for Rayon jobs.
+ /// This is used to get the pointer to the current `ImplicitCtxt`.
+ #[inline]
+ pub(super) fn get_tlv() -> *const () {
+ ptr::from_exposed_addr(rayon_core::tlv::get())
+ }
+
+ /// Sets Rayon's thread-local variable, which is preserved for Rayon jobs
+ /// to `value` during the call to `f`. It is restored to its previous value after.
+ /// This is used to set the pointer to the new `ImplicitCtxt`.
+ #[inline]
+ pub(super) fn with_tlv<F: FnOnce() -> R, R>(value: *const (), f: F) -> R {
+ rayon_core::tlv::with(value.expose_addr(), f)
+ }
+}
+
+#[cfg(not(parallel_compiler))]
+mod tlv {
+ use std::cell::Cell;
+ use std::ptr;
+
+ thread_local! {
+ /// A thread local variable that stores a pointer to the current `ImplicitCtxt`.
+ static TLV: Cell<*const ()> = const { Cell::new(ptr::null()) };
+ }
+
+ /// Gets the pointer to the current `ImplicitCtxt`.
+ #[inline]
+ pub(super) fn get_tlv() -> *const () {
+ TLV.with(|tlv| tlv.get())
+ }
+
+ /// Sets TLV to `value` during the call to `f`.
+ /// It is restored to its previous value after.
+ /// This is used to set the pointer to the new `ImplicitCtxt`.
+ #[inline]
+ pub(super) fn with_tlv<F: FnOnce() -> R, R>(value: *const (), f: F) -> R {
+ let old = get_tlv();
+ let _reset = rustc_data_structures::OnDrop(move || TLV.with(|tlv| tlv.set(old)));
+ TLV.with(|tlv| tlv.set(value));
+ f()
+ }
+}
+
+#[inline]
+fn erase(context: &ImplicitCtxt<'_, '_>) -> *const () {
+ context as *const _ as *const ()
+}
+
+#[inline]
+unsafe fn downcast<'a, 'tcx>(context: *const ()) -> &'a ImplicitCtxt<'a, 'tcx> {
+ &*(context as *const ImplicitCtxt<'a, 'tcx>)
+}
+
+/// Sets `context` as the new current `ImplicitCtxt` for the duration of the function `f`.
+#[inline]
+pub fn enter_context<'a, 'tcx, F, R>(context: &ImplicitCtxt<'a, 'tcx>, f: F) -> R
+where
+ F: FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R,
+{
+ tlv::with_tlv(erase(context), || f(&context))
+}
+
+/// Allows access to the current `ImplicitCtxt` in a closure if one is available.
+#[inline]
+pub fn with_context_opt<F, R>(f: F) -> R
+where
+ F: for<'a, 'tcx> FnOnce(Option<&ImplicitCtxt<'a, 'tcx>>) -> R,
+{
+ let context = tlv::get_tlv();
+ if context.is_null() {
+ f(None)
+ } else {
+ // We could get an `ImplicitCtxt` pointer from another thread.
+ // Ensure that `ImplicitCtxt` is `Sync`.
+ sync::assert_sync::<ImplicitCtxt<'_, '_>>();
+
+ unsafe { f(Some(downcast(context))) }
+ }
+}
+
+/// Allows access to the current `ImplicitCtxt`.
+/// Panics if there is no `ImplicitCtxt` available.
+#[inline]
+pub fn with_context<F, R>(f: F) -> R
+where
+ F: for<'a, 'tcx> FnOnce(&ImplicitCtxt<'a, 'tcx>) -> R,
+{
+ with_context_opt(|opt_context| f(opt_context.expect("no ImplicitCtxt stored in tls")))
+}
+
+/// Allows access to the current `ImplicitCtxt` whose tcx field is the same as the tcx argument
+/// passed in. This means the closure is given an `ImplicitCtxt` with the same `'tcx` lifetime
+/// as the `TyCtxt` passed in.
+/// This will panic if you pass it a `TyCtxt` which is different from the current
+/// `ImplicitCtxt`'s `tcx` field.
+#[inline]
+pub fn with_related_context<'tcx, F, R>(tcx: TyCtxt<'tcx>, f: F) -> R
+where
+ F: FnOnce(&ImplicitCtxt<'_, 'tcx>) -> R,
+{
+ with_context(|context| {
+ // The two gcx have different invariant lifetimes, so we need to erase them for the comparison.
+ assert!(ptr::eq(
+ context.tcx.gcx as *const _ as *const (),
+ tcx.gcx as *const _ as *const ()
+ ));
+
+ let context: &ImplicitCtxt<'_, '_> = unsafe { mem::transmute(context) };
+
+ f(context)
+ })
+}
+
+/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
+/// Panics if there is no `ImplicitCtxt` available.
+#[inline]
+pub fn with<F, R>(f: F) -> R
+where
+ F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> R,
+{
+ with_context(|context| f(context.tcx))
+}
+
+/// Allows access to the `TyCtxt` in the current `ImplicitCtxt`.
+/// The closure is passed None if there is no `ImplicitCtxt` available.
+#[inline]
+pub fn with_opt<F, R>(f: F) -> R
+where
+ F: for<'tcx> FnOnce(Option<TyCtxt<'tcx>>) -> R,
+{
+ with_context_opt(|opt_context| f(opt_context.map(|context| context.tcx)))
+}
use crate::ty::{
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
- PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
+ PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::WherePredicate;
use rustc_span::Span;
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
- format!("{}", self).into_diagnostic_arg()
+ self.to_string().into_diagnostic_arg()
}
}
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- match t.kind() {
+ match *t.kind() {
Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
FnDef(..)
}
Alias(Opaque, AliasTy { def_id, .. }) => {
- let parent = self.tcx.parent(*def_id);
- if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
- && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = self.tcx.type_of(parent).kind()
+ let parent = self.tcx.parent(def_id);
+ if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent)
+ && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
&& parent_opaque_def_id == def_id
{
// Okay
}
}
+ Alias(Projection, AliasTy { def_id, .. }) => {
+ if self.tcx.def_kind(def_id) != DefKind::AssocTy {
+ return ControlFlow::Break(());
+ }
+ }
+
Param(param) => {
// FIXME: It would be nice to make this not use string manipulation,
// but it's pretty hard to do this, since `ty::ParamTy` is missing
-use crate::traits::{ObligationCause, ObligationCauseCode};
-use crate::ty::diagnostics::suggest_constraining_type_param;
-use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, Printer};
+use crate::ty::print::{with_forced_trimmed_paths, FmtPrinter, PrettyPrinter};
use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
-use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
-use rustc_errors::{pluralize, Diagnostic, MultiSpan};
+use rustc_errors::pluralize;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind};
use rustc_hir::def_id::DefId;
-use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{BytePos, Span};
+use rustc_span::symbol::Symbol;
use rustc_target::spec::abi;
-
use std::borrow::Cow;
use std::collections::hash_map::DefaultHasher;
use std::fmt;
-use std::hash::{Hash, Hasher};
+use std::hash::Hash;
+use std::hash::Hasher;
use std::path::PathBuf;
-use super::print::PrettyPrinter;
-
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable, Lift)]
pub struct ExpectedFound<T> {
pub expected: T,
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(),
- ty::GeneratorWitness(..) => "generator witness".into(),
+ ty::GeneratorWitness(..) |
+ ty::GeneratorWitnessMIR(..) => "generator witness".into(),
ty::Tuple(..) => "tuple".into(),
ty::Infer(ty::TyVar(_)) => "inferred type".into(),
ty::Infer(ty::IntVar(_)) => "integer".into(),
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Generator(def_id, ..) => tcx.generator_kind(def_id).unwrap().descr().into(),
- ty::GeneratorWitness(..) => "generator witness".into(),
+ ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => "generator witness".into(),
ty::Tuple(..) => "tuple".into(),
ty::Placeholder(..) => "higher-ranked type".into(),
ty::Bound(..) => "bound type variable".into(),
}
impl<'tcx> TyCtxt<'tcx> {
- pub fn note_and_explain_type_err(
- self,
- diag: &mut Diagnostic,
- err: TypeError<'tcx>,
- cause: &ObligationCause<'tcx>,
- sp: Span,
- body_owner_def_id: DefId,
- ) {
- use self::TypeError::*;
- debug!("note_and_explain_type_err err={:?} cause={:?}", err, cause);
- match err {
- ArgumentSorts(values, _) | Sorts(values) => {
- match (values.expected.kind(), values.found.kind()) {
- (ty::Closure(..), ty::Closure(..)) => {
- diag.note("no two closures, even if identical, have the same type");
- diag.help("consider boxing your closure and/or using it as a trait object");
- }
- (ty::Alias(ty::Opaque, ..), ty::Alias(ty::Opaque, ..)) => {
- // Issue #63167
- diag.note("distinct uses of `impl Trait` result in different opaque types");
- }
- (ty::Float(_), ty::Infer(ty::IntVar(_)))
- if let Ok(
- // Issue #53280
- snippet,
- ) = self.sess.source_map().span_to_snippet(sp) =>
- {
- if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
- diag.span_suggestion(
- sp,
- "use a float literal",
- format!("{}.0", snippet),
- MachineApplicable,
- );
- }
- }
- (ty::Param(expected), ty::Param(found)) => {
- let generics = self.generics_of(body_owner_def_id);
- let e_span = self.def_span(generics.type_param(expected, self).def_id);
- if !sp.contains(e_span) {
- diag.span_label(e_span, "expected type parameter");
- }
- let f_span = self.def_span(generics.type_param(found, self).def_id);
- if !sp.contains(f_span) {
- diag.span_label(f_span, "found type parameter");
- }
- diag.note(
- "a type parameter was expected, but a different one was found; \
- you might be missing a type parameter or trait bound",
- );
- diag.note(
- "for more information, visit \
- https://doc.rust-lang.org/book/ch10-02-traits.html\
- #traits-as-parameters",
- );
- }
- (ty::Alias(ty::Projection, _), ty::Alias(ty::Projection, _)) => {
- diag.note("an associated type was expected, but a different one was found");
- }
- (ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
- if self.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
- {
- let generics = self.generics_of(body_owner_def_id);
- let p_span = self.def_span(generics.type_param(p, self).def_id);
- if !sp.contains(p_span) {
- diag.span_label(p_span, "this type parameter");
- }
- let hir = self.hir();
- let mut note = true;
- if let Some(generics) = generics
- .type_param(p, self)
- .def_id
- .as_local()
- .map(|id| hir.local_def_id_to_hir_id(id))
- .and_then(|id| self.hir().find_parent(id))
- .as_ref()
- .and_then(|node| node.generics())
- {
- // Synthesize the associated type restriction `Add<Output = Expected>`.
- // FIXME: extract this logic for use in other diagnostics.
- let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(self);
- let path =
- self.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
- let item_name = self.item_name(proj.def_id);
- let item_args = self.format_generic_args(assoc_substs);
-
- let path = if path.ends_with('>') {
- format!(
- "{}, {}{} = {}>",
- &path[..path.len() - 1],
- item_name,
- item_args,
- p
- )
- } else {
- format!("{}<{}{} = {}>", path, item_name, item_args, p)
- };
- note = !suggest_constraining_type_param(
- self,
- generics,
- diag,
- &format!("{}", proj.self_ty()),
- &path,
- None,
- );
- }
- if note {
- diag.note("you might be missing a type parameter or trait bound");
- }
- }
- (ty::Param(p), ty::Dynamic(..) | ty::Alias(ty::Opaque, ..))
- | (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
- let generics = self.generics_of(body_owner_def_id);
- let p_span = self.def_span(generics.type_param(p, self).def_id);
- if !sp.contains(p_span) {
- diag.span_label(p_span, "this type parameter");
- }
- diag.help("type parameters must be constrained to match other types");
- if self.sess.teach(&diag.get_code().unwrap()) {
- diag.help(
- "given a type parameter `T` and a method `foo`:
-```
-trait Trait<T> { fn foo(&self) -> T; }
-```
-the only ways to implement method `foo` are:
-- constrain `T` with an explicit type:
-```
-impl Trait<String> for X {
- fn foo(&self) -> String { String::new() }
-}
-```
-- add a trait bound to `T` and call a method on that trait that returns `Self`:
-```
-impl<T: std::default::Default> Trait<T> for X {
- fn foo(&self) -> T { <T as std::default::Default>::default() }
-}
-```
-- change `foo` to return an argument of type `T`:
-```
-impl<T> Trait<T> for X {
- fn foo(&self, x: T) -> T { x }
-}
-```",
- );
- }
- diag.note(
- "for more information, visit \
- https://doc.rust-lang.org/book/ch10-02-traits.html\
- #traits-as-parameters",
- );
- }
- (ty::Param(p), ty::Closure(..) | ty::Generator(..)) => {
- let generics = self.generics_of(body_owner_def_id);
- let p_span = self.def_span(generics.type_param(p, self).def_id);
- if !sp.contains(p_span) {
- diag.span_label(p_span, "this type parameter");
- }
- diag.help(&format!(
- "every closure has a distinct type and so could not always match the \
- caller-chosen type of parameter `{}`",
- p
- ));
- }
- (ty::Param(p), _) | (_, ty::Param(p)) => {
- let generics = self.generics_of(body_owner_def_id);
- let p_span = self.def_span(generics.type_param(p, self).def_id);
- if !sp.contains(p_span) {
- diag.span_label(p_span, "this type parameter");
- }
- }
- (ty::Alias(ty::Projection, proj_ty), _) if self.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
- self.expected_projection(
- diag,
- proj_ty,
- values,
- body_owner_def_id,
- cause.code(),
- );
- }
- (_, ty::Alias(ty::Projection, proj_ty)) if self.def_kind(proj_ty.def_id) != DefKind::ImplTraitPlaceholder => {
- let msg = format!(
- "consider constraining the associated type `{}` to `{}`",
- values.found, values.expected,
- );
- if !(self.suggest_constraining_opaque_associated_type(
- diag,
- &msg,
- proj_ty,
- values.expected,
- ) || self.suggest_constraint(
- diag,
- &msg,
- body_owner_def_id,
- proj_ty,
- values.expected,
- )) {
- diag.help(&msg);
- diag.note(
- "for more information, visit \
- https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
- );
- }
- }
- _ => {}
- }
- debug!(
- "note_and_explain_type_err expected={:?} ({:?}) found={:?} ({:?})",
- values.expected,
- values.expected.kind(),
- values.found,
- values.found.kind(),
- );
- }
- CyclicTy(ty) => {
- // Watch out for various cases of cyclic types and try to explain.
- if ty.is_closure() || ty.is_generator() {
- diag.note(
- "closures cannot capture themselves or take themselves as argument;\n\
- this error may be the result of a recent compiler bug-fix,\n\
- see issue #46062 <https://github.com/rust-lang/rust/issues/46062>\n\
- for more information",
- );
- }
- }
- TargetFeatureCast(def_id) => {
- let target_spans =
- self.get_attrs(def_id, sym::target_feature).map(|attr| attr.span);
- diag.note(
- "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
- );
- diag.span_labels(target_spans, "`#[target_feature]` added here");
- }
- _ => {}
- }
- }
-
- fn suggest_constraint(
- self,
- diag: &mut Diagnostic,
- msg: &str,
- body_owner_def_id: DefId,
- proj_ty: &ty::AliasTy<'tcx>,
- ty: Ty<'tcx>,
- ) -> bool {
- let assoc = self.associated_item(proj_ty.def_id);
- let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self);
- if let Some(item) = self.hir().get_if_local(body_owner_def_id) {
- if let Some(hir_generics) = item.generics() {
- // Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
- // This will also work for `impl Trait`.
- let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
- let generics = self.generics_of(body_owner_def_id);
- generics.type_param(param_ty, self).def_id
- } else {
- return false;
- };
- let Some(def_id) = def_id.as_local() else {
- return false;
- };
-
- // First look in the `where` clause, as this might be
- // `fn foo<T>(x: T) where T: Trait`.
- for pred in hir_generics.bounds_for_param(def_id) {
- if self.constrain_generic_bound_associated_type_structured_suggestion(
- diag,
- &trait_ref,
- pred.bounds,
- &assoc,
- assoc_substs,
- ty,
- msg,
- false,
- ) {
- return true;
- }
- }
- }
- }
- false
- }
-
- /// An associated type was expected and a different type was found.
- ///
- /// We perform a few different checks to see what we can suggest:
- ///
- /// - In the current item, look for associated functions that return the expected type and
- /// suggest calling them. (Not a structured suggestion.)
- /// - If any of the item's generic bounds can be constrained, we suggest constraining the
- /// associated type to the found type.
- /// - If the associated type has a default type and was expected inside of a `trait`, we
- /// mention that this is disallowed.
- /// - If all other things fail, and the error is not because of a mismatch between the `trait`
- /// and the `impl`, we provide a generic `help` to constrain the assoc type or call an assoc
- /// fn that returns the type.
- fn expected_projection(
- self,
- diag: &mut Diagnostic,
- proj_ty: &ty::AliasTy<'tcx>,
- values: ExpectedFound<Ty<'tcx>>,
- body_owner_def_id: DefId,
- cause_code: &ObligationCauseCode<'_>,
- ) {
- let msg = format!(
- "consider constraining the associated type `{}` to `{}`",
- values.expected, values.found
- );
- let body_owner = self.hir().get_if_local(body_owner_def_id);
- let current_method_ident = body_owner.and_then(|n| n.ident()).map(|i| i.name);
-
- // We don't want to suggest calling an assoc fn in a scope where that isn't feasible.
- let callable_scope = matches!(
- body_owner,
- Some(
- hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })
- | hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
- | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }),
- )
- );
- let impl_comparison =
- matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
- let assoc = self.associated_item(proj_ty.def_id);
- if !callable_scope || impl_comparison {
- // We do not want to suggest calling functions when the reason of the
- // type error is a comparison of an `impl` with its `trait` or when the
- // scope is outside of a `Body`.
- } else {
- // If we find a suitable associated function that returns the expected type, we don't
- // want the more general suggestion later in this method about "consider constraining
- // the associated type or calling a method that returns the associated type".
- let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type(
- diag,
- assoc.container_id(self),
- current_method_ident,
- proj_ty.def_id,
- values.expected,
- );
- // Possibly suggest constraining the associated type to conform to the
- // found type.
- if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
- || point_at_assoc_fn
- {
- return;
- }
- }
-
- self.suggest_constraining_opaque_associated_type(diag, &msg, proj_ty, values.found);
-
- if self.point_at_associated_type(diag, body_owner_def_id, values.found) {
- return;
- }
-
- if !impl_comparison {
- // Generic suggestion when we can't be more specific.
- if callable_scope {
- diag.help(&format!(
- "{} or calling a method that returns `{}`",
- msg, values.expected
- ));
- } else {
- diag.help(&msg);
- }
- diag.note(
- "for more information, visit \
- https://doc.rust-lang.org/book/ch19-03-advanced-traits.html",
- );
- }
- if self.sess.teach(&diag.get_code().unwrap()) {
- diag.help(
- "given an associated type `T` and a method `foo`:
-```
-trait Trait {
-type T;
-fn foo(&self) -> Self::T;
-}
-```
-the only way of implementing method `foo` is to constrain `T` with an explicit associated type:
-```
-impl Trait for X {
-type T = String;
-fn foo(&self) -> Self::T { String::new() }
-}
-```",
- );
- }
- }
-
- /// When the expected `impl Trait` is not defined in the current item, it will come from
- /// a return type. This can occur when dealing with `TryStream` (#71035).
- fn suggest_constraining_opaque_associated_type(
- self,
- diag: &mut Diagnostic,
- msg: &str,
- proj_ty: &ty::AliasTy<'tcx>,
- ty: Ty<'tcx>,
- ) -> bool {
- let assoc = self.associated_item(proj_ty.def_id);
- if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *proj_ty.self_ty().kind() {
- let opaque_local_def_id = def_id.as_local();
- let opaque_hir_ty = if let Some(opaque_local_def_id) = opaque_local_def_id {
- match &self.hir().expect_item(opaque_local_def_id).kind {
- hir::ItemKind::OpaqueTy(opaque_hir_ty) => opaque_hir_ty,
- _ => bug!("The HirId comes from a `ty::Opaque`"),
- }
- } else {
- return false;
- };
-
- let (trait_ref, assoc_substs) = proj_ty.trait_ref_and_own_substs(self);
-
- self.constrain_generic_bound_associated_type_structured_suggestion(
- diag,
- &trait_ref,
- opaque_hir_ty.bounds,
- assoc,
- assoc_substs,
- ty,
- msg,
- true,
- )
- } else {
- false
- }
- }
-
- fn point_at_methods_that_satisfy_associated_type(
- self,
- diag: &mut Diagnostic,
- assoc_container_id: DefId,
- current_method_ident: Option<Symbol>,
- proj_ty_item_def_id: DefId,
- expected: Ty<'tcx>,
- ) -> bool {
- let items = self.associated_items(assoc_container_id);
- // Find all the methods in the trait that could be called to construct the
- // expected associated type.
- // FIXME: consider suggesting the use of associated `const`s.
- let methods: Vec<(Span, String)> = items
- .items
- .iter()
- .filter(|(name, item)| {
- ty::AssocKind::Fn == item.kind && Some(**name) != current_method_ident
- })
- .filter_map(|(_, item)| {
- let method = self.fn_sig(item.def_id);
- match *method.output().skip_binder().kind() {
- ty::Alias(ty::Projection, ty::AliasTy { def_id: item_def_id, .. })
- if item_def_id == proj_ty_item_def_id =>
- {
- Some((
- self.def_span(item.def_id),
- format!("consider calling `{}`", self.def_path_str(item.def_id)),
- ))
- }
- _ => None,
- }
- })
- .collect();
- if !methods.is_empty() {
- // Use a single `help:` to show all the methods in the trait that can
- // be used to construct the expected associated type.
- let mut span: MultiSpan =
- methods.iter().map(|(sp, _)| *sp).collect::<Vec<Span>>().into();
- let msg = format!(
- "{some} method{s} {are} available that return{r} `{ty}`",
- some = if methods.len() == 1 { "a" } else { "some" },
- s = pluralize!(methods.len()),
- are = pluralize!("is", methods.len()),
- r = if methods.len() == 1 { "s" } else { "" },
- ty = expected
- );
- for (sp, label) in methods.into_iter() {
- span.push_span_label(sp, label);
- }
- diag.span_help(span, &msg);
- return true;
- }
- false
- }
-
- fn point_at_associated_type(
- self,
- diag: &mut Diagnostic,
- body_owner_def_id: DefId,
- found: Ty<'tcx>,
- ) -> bool {
- let Some(hir_id) = body_owner_def_id.as_local() else {
- return false;
- };
- let hir_id = self.hir().local_def_id_to_hir_id(hir_id);
- // When `body_owner` is an `impl` or `trait` item, look in its associated types for
- // `expected` and point at it.
- let parent_id = self.hir().get_parent_item(hir_id);
- let item = self.hir().find_by_def_id(parent_id.def_id);
- debug!("expected_projection parent item {:?}", item);
- match item {
- Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Trait(.., items), .. })) => {
- // FIXME: account for `#![feature(specialization)]`
- for item in &items[..] {
- match item.kind {
- hir::AssocItemKind::Type => {
- // FIXME: account for returning some type in a trait fn impl that has
- // an assoc type as a return type (#72076).
- if let hir::Defaultness::Default { has_value: true } =
- self.impl_defaultness(item.id.owner_id)
- {
- if self.type_of(item.id.owner_id) == found {
- diag.span_label(
- item.span,
- "associated type defaults can't be assumed inside the \
- trait defining them",
- );
- return true;
- }
- }
- }
- _ => {}
- }
- }
- }
- Some(hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Impl(hir::Impl { items, .. }),
- ..
- })) => {
- for item in &items[..] {
- if let hir::AssocItemKind::Type = item.kind {
- if self.type_of(item.id.owner_id) == found {
- diag.span_label(item.span, "expected this associated type");
- return true;
- }
- }
- }
- }
- _ => {}
- }
- false
- }
-
- /// Given a slice of `hir::GenericBound`s, if any of them corresponds to the `trait_ref`
- /// requirement, provide a structured suggestion to constrain it to a given type `ty`.
- ///
- /// `is_bound_surely_present` indicates whether we know the bound we're looking for is
- /// inside `bounds`. If that's the case then we can consider `bounds` containing only one
- /// trait bound as the one we're looking for. This can help in cases where the associated
- /// type is defined on a supertrait of the one present in the bounds.
- fn constrain_generic_bound_associated_type_structured_suggestion(
- self,
- diag: &mut Diagnostic,
- trait_ref: &ty::TraitRef<'tcx>,
- bounds: hir::GenericBounds<'_>,
- assoc: &ty::AssocItem,
- assoc_substs: &[ty::GenericArg<'tcx>],
- ty: Ty<'tcx>,
- msg: &str,
- is_bound_surely_present: bool,
- ) -> bool {
- // FIXME: we would want to call `resolve_vars_if_possible` on `ty` before suggesting.
-
- let trait_bounds = bounds.iter().filter_map(|bound| match bound {
- hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::None) => Some(ptr),
- _ => None,
- });
-
- let matching_trait_bounds = trait_bounds
- .clone()
- .filter(|ptr| ptr.trait_ref.trait_def_id() == Some(trait_ref.def_id))
- .collect::<Vec<_>>();
-
- let span = match &matching_trait_bounds[..] {
- &[ptr] => ptr.span,
- &[] if is_bound_surely_present => match &trait_bounds.collect::<Vec<_>>()[..] {
- &[ptr] => ptr.span,
- _ => return false,
- },
- _ => return false,
- };
-
- self.constrain_associated_type_structured_suggestion(
- diag,
- span,
- assoc,
- assoc_substs,
- ty,
- msg,
- )
- }
-
- /// Given a span corresponding to a bound, provide a structured suggestion to set an
- /// associated type to a given type `ty`.
- fn constrain_associated_type_structured_suggestion(
- self,
- diag: &mut Diagnostic,
- span: Span,
- assoc: &ty::AssocItem,
- assoc_substs: &[ty::GenericArg<'tcx>],
- ty: Ty<'tcx>,
- msg: &str,
- ) -> bool {
- if let Ok(has_params) =
- self.sess.source_map().span_to_snippet(span).map(|snippet| snippet.ends_with('>'))
- {
- let (span, sugg) = if has_params {
- let pos = span.hi() - BytePos(1);
- let span = Span::new(pos, pos, span.ctxt(), span.parent());
- (span, format!(", {} = {}", assoc.ident(self), ty))
- } else {
- let item_args = self.format_generic_args(assoc_substs);
- (span.shrink_to_hi(), format!("<{}{} = {}>", assoc.ident(self), item_args, ty))
- };
- diag.span_suggestion_verbose(span, msg, sugg, MaybeIncorrect);
- return true;
- }
- false
- }
-
pub fn short_ty_string(self, ty: Ty<'tcx>) -> (String, Option<PathBuf>) {
let width = self.sess.diagnostic_width();
let length_limit = width.saturating_sub(30);
Err(_) => (regular, None),
}
}
-
- fn format_generic_args(self, args: &[ty::GenericArg<'tcx>]) -> String {
- FmtPrinter::new(self, hir::def::Namespace::TypeNS)
- .path_generic_args(Ok, args)
- .expect("could not write to `String`.")
- .into_buffer()
- }
}
ClosureSimplifiedType(DefId),
GeneratorSimplifiedType(DefId),
GeneratorWitnessSimplifiedType(usize),
+ GeneratorWitnessMIRSimplifiedType(DefId),
FunctionSimplifiedType(usize),
PlaceholderSimplifiedType,
}
ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(ClosureSimplifiedType(def_id)),
ty::Generator(def_id, _, _) => Some(GeneratorSimplifiedType(def_id)),
ty::GeneratorWitness(tys) => Some(GeneratorWitnessSimplifiedType(tys.skip_binder().len())),
+ ty::GeneratorWitnessMIR(def_id, _) => Some(GeneratorWitnessMIRSimplifiedType(def_id)),
ty::Never => Some(NeverSimplifiedType),
ty::Tuple(tys) => Some(TupleSimplifiedType(tys.len())),
ty::FnPtr(f) => Some(FunctionSimplifiedType(f.skip_binder().inputs().len())),
| ForeignSimplifiedType(d)
| TraitSimplifiedType(d)
| ClosureSimplifiedType(d)
- | GeneratorSimplifiedType(d) => Some(d),
+ | GeneratorSimplifiedType(d)
+ | GeneratorWitnessMIRSimplifiedType(d) => Some(d),
_ => None,
}
}
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Placeholder(..)
| ty::Bound(..)
| ty::Infer(_) => bug!("unexpected impl_ty: {impl_ty}"),
ty::Error(_) => true,
- ty::GeneratorWitness(..) | ty::Bound(..) => {
+ ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) | ty::Bound(..) => {
bug!("unexpected obligation type: {:?}", obligation_ty)
}
}
self.bound_computation(ts, |flags, ts| flags.add_tys(ts));
}
+ &ty::GeneratorWitnessMIR(_, ref substs) => {
+ let should_remove_further_specializable =
+ !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
+ self.add_substs(substs);
+ if should_remove_further_specializable {
+ self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE;
+ }
+ self.add_flags(TypeFlags::HAS_TY_GENERATOR);
+ }
+
&ty::Closure(_, substs) => {
let substs = substs.as_closure();
let should_remove_further_specializable =
tcx: TyCtxt<'tcx>,
/// Stores the index of a binder *just outside* the stuff we have
- /// visited. So this begins as INNERMOST; when we pass through a
+ /// visited. So this begins as INNERMOST; when we pass through a
/// binder, it is incremented (via `shift_in`).
current_index: ty::DebruijnIndex,
Some(tcx.bound_type_of(self.def_id).map_bound(|t| t.into()))
}
GenericParamDefKind::Const { has_default } if has_default => {
- Some(tcx.bound_const_param_default(self.def_id).map_bound(|c| c.into()))
+ Some(tcx.const_param_default(self.def_id).map_bound(|c| c.into()))
}
_ => None,
}
&self,
tcx: TyCtxt<'tcx>,
substs: SubstsRef<'tcx>,
- ) -> InstantiatedPredicates<'tcx> {
- InstantiatedPredicates {
- predicates: self
- .predicates
- .iter()
- .map(|(p, _)| EarlyBinder(*p).subst(tcx, substs))
- .collect(),
- spans: self.predicates.iter().map(|(_, sp)| *sp).collect(),
- }
+ ) -> impl Iterator<Item = (Predicate<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
+ {
+ EarlyBinder(self.predicates).subst_iter_copied(tcx, substs)
}
#[instrument(level = "debug", skip(self, tcx))]
substs: SubstsRef<'tcx>,
) -> Option<Instance<'tcx>> {
debug!("resolve_for_vtable(def_id={:?}, substs={:?})", def_id, substs);
- let fn_sig = tcx.fn_sig(def_id);
+ let fn_sig = tcx.fn_sig(def_id).subst_identity();
let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty()
&& fn_sig.input(0).skip_binder().is_param(0)
&& tcx.generics_of(def_id).has_self;
Ok(false)
}
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
- // The closure fn `llfn` is a `fn(&self, ...)`. We want a
+ // The closure fn `llfn` is a `fn(&self, ...)`. We want a
// `fn(&mut self, ...)`. In fact, at codegen time, these are
// basically the same thing, so we can just return llfn.
Ok(false)
}
(ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
- // self, ...)`. We want a `fn(self, ...)`. We can produce
+ // self, ...)`. We want a `fn(self, ...)`. We can produce
// this by doing something like:
//
// fn call_once(self, ...) { call_mut(&self, ...) }
Int(i, signed) => i.to_ty(tcx, signed),
F32 => tcx.types.f32,
F64 => tcx.types.f64,
- Pointer => tcx.mk_mut_ptr(tcx.mk_unit()),
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ Pointer(_) => tcx.mk_mut_ptr(tcx.mk_unit()),
}
}
fn to_int_ty<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self {
Int(i, signed) => i.to_ty(tcx, signed),
- Pointer => tcx.types.usize,
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ Pointer(_) => {
+ let signed = false;
+ tcx.data_layout().ptr_sized_integer().to_ty(tcx, signed)
+ }
F32 | F64 => bug!("floats do not have an int type"),
}
}
| ty::Never
| ty::FnDef(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Foreign(..)
| ty::Dynamic(_, _, ty::Dyn) => {
bug!("TyAndLayout::field({:?}): not applicable", this)
let tcx = cx.tcx();
let param_env = cx.param_env();
- let addr_space_of_ty = |ty: Ty<'tcx>| {
- if ty.is_fn() { cx.data_layout().instruction_address_space } else { AddressSpace::DATA }
- };
-
- let pointee_info = match *this.ty.kind() {
- ty::RawPtr(mt) if offset.bytes() == 0 => {
- tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
- size: layout.size,
- align: layout.align.abi,
- safe: None,
- address_space: addr_space_of_ty(mt.ty),
- })
- }
- ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
- tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
- size: layout.size,
- align: layout.align.abi,
- safe: None,
- address_space: cx.data_layout().instruction_address_space,
- })
- }
- ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
- let address_space = addr_space_of_ty(ty);
- let kind = if tcx.sess.opts.optimize == OptLevel::No {
- // Use conservative pointer kind if not optimizing. This saves us the
- // Freeze/Unpin queries, and can save time in the codegen backend (noalias
- // attributes in LLVM have compile-time cost even in unoptimized builds).
- PointerKind::SharedMutable
- } else {
- match mt {
- hir::Mutability::Not => {
- if ty.is_freeze(tcx, cx.param_env()) {
- PointerKind::Frozen
- } else {
- PointerKind::SharedMutable
+ let pointee_info =
+ match *this.ty.kind() {
+ ty::RawPtr(mt) if offset.bytes() == 0 => {
+ tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
+ size: layout.size,
+ align: layout.align.abi,
+ safe: None,
+ })
+ }
+ ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
+ tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| {
+ PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
+ })
+ }
+ ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
+ let kind = if tcx.sess.opts.optimize == OptLevel::No {
+ // Use conservative pointer kind if not optimizing. This saves us the
+ // Freeze/Unpin queries, and can save time in the codegen backend (noalias
+ // attributes in LLVM have compile-time cost even in unoptimized builds).
+ PointerKind::SharedMutable
+ } else {
+ match mt {
+ hir::Mutability::Not => {
+ if ty.is_freeze(tcx, cx.param_env()) {
+ PointerKind::Frozen
+ } else {
+ PointerKind::SharedMutable
+ }
}
- }
- hir::Mutability::Mut => {
- // References to self-referential structures should not be considered
- // noalias, as another pointer to the structure can be obtained, that
- // is not based-on the original reference. We consider all !Unpin
- // types to be potentially self-referential here.
- if ty.is_unpin(tcx, cx.param_env()) {
- PointerKind::UniqueBorrowed
- } else {
- PointerKind::UniqueBorrowedPinned
+ hir::Mutability::Mut => {
+ // References to self-referential structures should not be considered
+ // noalias, as another pointer to the structure can be obtained, that
+ // is not based-on the original reference. We consider all !Unpin
+ // types to be potentially self-referential here.
+ if ty.is_unpin(tcx, cx.param_env()) {
+ PointerKind::UniqueBorrowed
+ } else {
+ PointerKind::UniqueBorrowedPinned
+ }
}
}
- }
- };
+ };
- tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
- size: layout.size,
- align: layout.align.abi,
- safe: Some(kind),
- address_space,
- })
- }
+ tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
+ size: layout.size,
+ align: layout.align.abi,
+ safe: Some(kind),
+ })
+ }
- _ => {
- let mut data_variant = match this.variants {
- // Within the discriminant field, only the niche itself is
- // always initialized, so we only check for a pointer at its
- // offset.
- //
- // If the niche is a pointer, it's either valid (according
- // to its type), or null (which the niche field's scalar
- // validity range encodes). This allows using
- // `dereferenceable_or_null` for e.g., `Option<&T>`, and
- // this will continue to work as long as we don't start
- // using more niches than just null (e.g., the first page of
- // the address space, or unaligned pointers).
- Variants::Multiple {
- tag_encoding: TagEncoding::Niche { untagged_variant, .. },
- tag_field,
- ..
- } if this.fields.offset(tag_field) == offset => {
- Some(this.for_variant(cx, untagged_variant))
- }
- _ => Some(this),
- };
+ _ => {
+ let mut data_variant = match this.variants {
+ // Within the discriminant field, only the niche itself is
+ // always initialized, so we only check for a pointer at its
+ // offset.
+ //
+ // If the niche is a pointer, it's either valid (according
+ // to its type), or null (which the niche field's scalar
+ // validity range encodes). This allows using
+ // `dereferenceable_or_null` for e.g., `Option<&T>`, and
+ // this will continue to work as long as we don't start
+ // using more niches than just null (e.g., the first page of
+ // the address space, or unaligned pointers).
+ Variants::Multiple {
+ tag_encoding: TagEncoding::Niche { untagged_variant, .. },
+ tag_field,
+ ..
+ } if this.fields.offset(tag_field) == offset => {
+ Some(this.for_variant(cx, untagged_variant))
+ }
+ _ => Some(this),
+ };
- if let Some(variant) = data_variant {
- // We're not interested in any unions.
- if let FieldsShape::Union(_) = variant.fields {
- data_variant = None;
+ if let Some(variant) = data_variant {
+ // We're not interested in any unions.
+ if let FieldsShape::Union(_) = variant.fields {
+ data_variant = None;
+ }
}
- }
- let mut result = None;
-
- if let Some(variant) = data_variant {
- let ptr_end = offset + Pointer.size(cx);
- for i in 0..variant.fields.count() {
- let field_start = variant.fields.offset(i);
- if field_start <= offset {
- let field = variant.field(cx, i);
- result = field.to_result().ok().and_then(|field| {
- if ptr_end <= field_start + field.size {
- // We found the right field, look inside it.
- let field_info =
- field.pointee_info_at(cx, offset - field_start);
- field_info
- } else {
- None
+ let mut result = None;
+
+ if let Some(variant) = data_variant {
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ // (requires passing in the expected address space from the caller)
+ let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
+ for i in 0..variant.fields.count() {
+ let field_start = variant.fields.offset(i);
+ if field_start <= offset {
+ let field = variant.field(cx, i);
+ result = field.to_result().ok().and_then(|field| {
+ if ptr_end <= field_start + field.size {
+ // We found the right field, look inside it.
+ let field_info =
+ field.pointee_info_at(cx, offset - field_start);
+ field_info
+ } else {
+ None
+ }
+ });
+ if result.is_some() {
+ break;
}
- });
- if result.is_some() {
- break;
}
}
}
- }
- // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
- if let Some(ref mut pointee) = result {
- if let ty::Adt(def, _) = this.ty.kind() {
- if def.is_box() && offset.bytes() == 0 {
- pointee.safe = Some(PointerKind::UniqueOwned);
+ // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
+ if let Some(ref mut pointee) = result {
+ if let ty::Adt(def, _) = this.ty.kind() {
+ if def.is_box() && offset.bytes() == 0 {
+ pointee.safe = Some(PointerKind::UniqueOwned);
+ }
}
}
- }
- result
- }
- };
+ result
+ }
+ };
debug!(
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
pub use adt::*;
pub use assoc::*;
pub use generics::*;
-use hir::OpaqueTyOrigin;
use rustc_ast as ast;
use rustc_ast::node_id::NodeMap;
use rustc_attr as attr;
-use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, LifetimeRes, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
use rustc_hir::Node;
use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
/// For each item with generics, maps to a vector of the variance
/// of its generics. If an item has no generics, it will have no
/// entry.
- pub variances: FxHashMap<DefId, &'tcx [ty::Variance]>,
+ pub variances: DefIdMap<&'tcx [ty::Variance]>,
}
// Contains information needed to resolve types and (in the future) look up
#[rustc_pass_by_value]
pub struct Ty<'tcx>(Interned<'tcx, WithCachedTypeInfo<TyKind<'tcx>>>);
-impl<'tcx> TyCtxt<'tcx> {
- /// A "bool" type used in rustc_mir_transform unit tests when we
- /// have not spun up a TyCtxt.
- pub const BOOL_TY_FOR_UNIT_TESTING: Ty<'tcx> =
- Ty(Interned::new_unchecked(&WithCachedTypeInfo {
- internee: ty::Bool,
- stable_hash: Fingerprint::ZERO,
- flags: TypeFlags::empty(),
- outer_exclusive_binder: DebruijnIndex::from_usize(0),
- }));
-}
-
impl ty::EarlyBoundRegion {
/// Does this early bound region have a name? Early bound regions normally
/// always have names except when using anonymous lifetimes (`'_`).
//
// In terms of why this is sound, the idea is that whenever there
// is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>`
- // holds. So if there is an impl of `T:Foo<'a>` that applies to
+ // holds. So if there is an impl of `T:Foo<'a>` that applies to
// all `'a`, then we must know that `T:Bar<'a,'a>` holds for all
// `'a`.
//
// Here, if we have `for<'x> T: Foo1<'x>`, then what do we know?
// The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The
// reason is similar to the previous example: any impl of
- // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So
+ // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So
// basically we would want to collapse the bound lifetimes from
// the input (`trait_ref`) and the supertraits.
//
pub fn is_empty(&self) -> bool {
self.predicates.is_empty()
}
+
+ pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
+ (&self).into_iter()
+ }
+}
+
+impl<'tcx> IntoIterator for InstantiatedPredicates<'tcx> {
+ type Item = (Predicate<'tcx>, Span);
+
+ type IntoIter = std::iter::Zip<std::vec::IntoIter<Predicate<'tcx>>, std::vec::IntoIter<Span>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ debug_assert_eq!(self.predicates.len(), self.spans.len());
+ std::iter::zip(self.predicates, self.spans)
+ }
+}
+
+impl<'a, 'tcx> IntoIterator for &'a InstantiatedPredicates<'tcx> {
+ type Item = (Predicate<'tcx>, Span);
+
+ type IntoIter = std::iter::Zip<
+ std::iter::Copied<std::slice::Iter<'a, Predicate<'tcx>>>,
+ std::iter::Copied<std::slice::Iter<'a, Span>>,
+ >;
+
+ fn into_iter(self) -> Self::IntoIter {
+ debug_assert_eq!(self.predicates.len(), self.spans.len());
+ std::iter::zip(self.predicates.iter().copied(), self.spans.iter().copied())
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)]
tcx: TyCtxt<'tcx>,
// typeck errors have subpar spans for opaque types, so delay error reporting until borrowck.
ignore_errors: bool,
- origin: OpaqueTyOrigin,
) -> Self {
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
debug!(?id_substs);
// This zip may have several times the same lifetime in `substs` paired with a different
- // lifetime from `id_substs`. Simply `collect`ing the iterator is the correct behaviour:
+ // lifetime from `id_substs`. Simply `collect`ing the iterator is the correct behaviour:
// it will pick the last one, which is the one we introduced in the impl-trait desugaring.
- let map = substs.iter().zip(id_substs);
-
- let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = match origin {
- // HACK: The HIR lowering for async fn does not generate
- // any `+ Captures<'x>` bounds for the `impl Future<...>`, so all async fns with lifetimes
- // would now fail to compile. We should probably just make hir lowering fill this in properly.
- OpaqueTyOrigin::AsyncFn(_) => map.collect(),
- OpaqueTyOrigin::FnReturn(_) | OpaqueTyOrigin::TyAlias => {
- // Opaque types may only use regions that are bound. So for
- // ```rust
- // type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
- // ```
- // we may not use `'c` in the hidden type.
- let variances = tcx.variances_of(def_id);
- debug!(?variances);
-
- map.filter(|(_, v)| {
- let ty::GenericArgKind::Lifetime(lt) = v.unpack() else { return true };
- let ty::ReEarlyBound(ebr) = lt.kind() else { bug!() };
- variances[ebr.index as usize] == ty::Variance::Invariant
- })
- .collect()
- }
- };
+ let map = substs.iter().zip(id_substs).collect();
debug!("map = {:#?}", map);
// Convert the type from the function into a type valid outside
/// Look up the name of a definition across crates. This does not look at HIR.
///
- /// This method will ICE if the corresponding item does not have a name. In these cases, use
+ /// This method will ICE if the corresponding item does not have a name. In these cases, use
/// [`opt_item_name`] instead.
///
/// [`opt_item_name`]: Self::opt_item_name
) -> Option<ImplOverlapKind> {
// If either trait impl references an error, they're allowed to overlap,
// as one of them essentially doesn't exist.
- if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error())
- || self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error())
+ if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.subst_identity().references_error())
+ || self
+ .impl_trait_ref(def_id2)
+ .map_or(false, |tr| tr.subst_identity().references_error())
{
return Some(ImplOverlapKind::Permitted { marker: false });
}
let is_marker_overlap = {
let is_marker_impl = |def_id: DefId| -> bool {
let trait_ref = self.impl_trait_ref(def_id);
- trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker)
+ trait_ref.map_or(false, |tr| self.trait_def(tr.skip_binder().def_id).is_marker)
};
is_marker_impl(def_id1) && is_marker_impl(def_id2)
};
self.trait_def(trait_def_id).has_auto_impl
}
+ /// Returns `true` if this is a trait alias.
+ pub fn trait_is_alias(self, trait_def_id: DefId) -> bool {
+ self.def_kind(trait_def_id) == DefKind::TraitAlias
+ }
+
pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool {
self.trait_is_auto(trait_def_id) || self.lang_items().sized_trait() == Some(trait_def_id)
}
/// Given the `DefId` of an impl, returns the `DefId` of the trait it implements.
/// If it implements no trait, returns `None`.
pub fn trait_id_of_impl(self, def_id: DefId) -> Option<DefId> {
- self.impl_trait_ref(def_id).map(|tr| tr.def_id)
+ self.impl_trait_ref(def_id).map(|tr| tr.skip_binder().def_id)
}
/// If the given `DefId` describes an item belonging to a trait,
ident
}
+ // FIXME(vincenzoapalzzo): move the HirId to a LocalDefId
pub fn adjust_ident_and_get_scope(
self,
mut ident: Ident,
}
}
- pub fn is_object_safe(self, key: DefId) -> bool {
- self.object_safety_violations(key).is_empty()
- }
-
#[inline]
pub fn is_const_fn_raw(self, def_id: DefId) -> bool {
matches!(
}
#[derive(Debug, Default, Copy, Clone)]
-pub struct FoundRelationships {
+pub struct InferVarInfo {
/// This is true if we identified that this Ty (`?T`) is found in a `?T: Foo`
/// obligation, where:
///
use crate::ty::subst::{GenericArg, GenericArgKind};
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc_data_structures::fx::FxHashMap;
+use rustc_span::def_id::DefId;
use rustc_span::Span;
/// Converts generic params of a TypeFoldable from one
assert!(!self.do_not_error);
kind.fold_with(self)
}
+
+ fn fold_closure_substs(
+ &mut self,
+ def_id: DefId,
+ substs: ty::SubstsRef<'tcx>,
+ ) -> ty::SubstsRef<'tcx> {
+ // I am a horrible monster and I pray for death. When
+ // we encounter a closure here, it is always a closure
+ // from within the function that we are currently
+ // type-checking -- one that is now being encapsulated
+ // in an opaque type. Ideally, we would
+ // go through the types/lifetimes that it references
+ // and treat them just like we would any other type,
+ // which means we would error out if we find any
+ // reference to a type/region that is not in the
+ // "reverse map".
+ //
+ // **However,** in the case of closures, there is a
+ // somewhat subtle (read: hacky) consideration. The
+ // problem is that our closure types currently include
+ // all the lifetime parameters declared on the
+ // enclosing function, even if they are unused by the
+ // closure itself. We can't readily filter them out,
+ // so here we replace those values with `'empty`. This
+ // can't really make a difference to the rest of the
+ // compiler; those regions are ignored for the
+ // outlives relation, and hence don't affect trait
+ // selection or auto traits, and they are erased
+ // during codegen.
+
+ let generics = self.tcx.generics_of(def_id);
+ self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
+ if index < generics.parent_count {
+ // Accommodate missing regions in the parent kinds...
+ self.fold_kind_no_missing_regions_error(kind)
+ } else {
+ // ...but not elsewhere.
+ self.fold_kind_normally(kind)
+ }
+ }))
+ }
}
impl<'tcx> TypeFolder<'tcx> for ReverseMapper<'tcx> {
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
match *ty.kind() {
ty::Closure(def_id, substs) => {
- // I am a horrible monster and I pray for death. When
- // we encounter a closure here, it is always a closure
- // from within the function that we are currently
- // type-checking -- one that is now being encapsulated
- // in an opaque type. Ideally, we would
- // go through the types/lifetimes that it references
- // and treat them just like we would any other type,
- // which means we would error out if we find any
- // reference to a type/region that is not in the
- // "reverse map".
- //
- // **However,** in the case of closures, there is a
- // somewhat subtle (read: hacky) consideration. The
- // problem is that our closure types currently include
- // all the lifetime parameters declared on the
- // enclosing function, even if they are unused by the
- // closure itself. We can't readily filter them out,
- // so here we replace those values with `'empty`. This
- // can't really make a difference to the rest of the
- // compiler; those regions are ignored for the
- // outlives relation, and hence don't affect trait
- // selection or auto traits, and they are erased
- // during codegen.
-
- let generics = self.tcx.generics_of(def_id);
- let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
- if index < generics.parent_count {
- // Accommodate missing regions in the parent kinds...
- self.fold_kind_no_missing_regions_error(kind)
- } else {
- // ...but not elsewhere.
- self.fold_kind_normally(kind)
- }
- }));
-
+ let substs = self.fold_closure_substs(def_id, substs);
self.tcx.mk_closure(def_id, substs)
}
ty::Generator(def_id, substs, movability) => {
- let generics = self.tcx.generics_of(def_id);
- let substs = self.tcx.mk_substs(substs.iter().enumerate().map(|(index, kind)| {
- if index < generics.parent_count {
- // Accommodate missing regions in the parent kinds...
- self.fold_kind_no_missing_regions_error(kind)
- } else {
- // ...but not elsewhere.
- self.fold_kind_normally(kind)
- }
- }));
-
+ let substs = self.fold_closure_substs(def_id, substs);
self.tcx.mk_generator(def_id, substs, movability)
}
+ ty::GeneratorWitnessMIR(def_id, substs) => {
+ let substs = self.fold_closure_substs(def_id, substs);
+ self.tcx.mk_generator_witness_mir(def_id, substs)
+ }
+
ty::Param(param) => {
// Look it up in the substitution list.
match self.map.get(&ty.into()).map(|k| k.unpack()) {
type Value<'tcx> = ty::Binder<'tcx, T::Value<'tcx>>;
}
+impl<T: ParameterizedOverTcx> ParameterizedOverTcx for ty::EarlyBinder<T> {
+ type Value<'tcx> = ty::EarlyBinder<T::Value<'tcx>>;
+}
+
#[macro_export]
macro_rules! trivially_parameterized_over_tcx {
($($ty:ty),+ $(,)?) => {
usize,
(),
u32,
+ bool,
std::string::String,
crate::metadata::ModChild,
crate::middle::codegen_fn_attrs::CodegenFnAttrs,
parameterized_over_tcx! {
crate::middle::exported_symbols::ExportedSymbol,
crate::mir::Body,
+ crate::mir::GeneratorLayout,
ty::Ty,
ty::FnSig,
ty::GenericPredicates,
DefPathData::Impl => {
let generics = self.tcx().generics_of(def_id);
let self_ty = self.tcx().bound_type_of(def_id);
- let impl_trait_ref = self.tcx().bound_impl_trait_ref(def_id);
+ let impl_trait_ref = self.tcx().impl_trait_ref(def_id);
let (self_ty, impl_trait_ref) = if substs.len() >= generics.count() {
(
self_ty.subst(self.tcx(), substs),
ty::FnDef(def_id, _)
| ty::Closure(def_id, _)
| ty::Generator(def_id, _, _)
+ | ty::GeneratorWitnessMIR(def_id, _)
| ty::Foreign(def_id) => Some(def_id),
ty::Bool
match self.tcx().trimmed_def_paths(()).get(&def_id) {
None => Ok((self, false)),
Some(symbol) => {
- self.write_str(symbol.as_str())?;
+ write!(self, "{}", Ident::with_dummy_span(*symbol))?;
Ok((self, true))
}
}
p!(")")
}
ty::FnDef(def_id, substs) => {
- let sig = self.tcx().bound_fn_sig(def_id).subst(self.tcx(), substs);
+ let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
p!(print(sig), " {{", print_value_path(def_id, substs), "}}");
}
ty::FnPtr(ref bare_fn) => p!(print(bare_fn)),
ty::GeneratorWitness(types) => {
p!(in_binder(&types));
}
+ ty::GeneratorWitnessMIR(did, substs) => {
+ p!(write("["));
+ if !self.tcx().sess.verbose() {
+ p!("generator witness");
+ // FIXME(eddyb) should use `def_span`.
+ if let Some(did) = did.as_local() {
+ let span = self.tcx().def_span(did);
+ p!(write(
+ "@{}",
+ // This may end up in stderr diagnostics but it may also be emitted
+ // into MIR. Hence we use the remapped path if available
+ self.tcx().sess.source_map().span_to_embeddable_string(span)
+ ));
+ } else {
+ p!(write("@"), print_def_path(did, substs));
+ }
+ } else {
+ p!(print_def_path(did, substs));
+ }
+
+ p!("]")
+ }
ty::Closure(did, substs) => {
p!(write("["));
if !self.should_print_verbose() {
}
p!("]");
}
- ty::Array(ty, sz) => {
- p!("[", print(ty), "; ");
- if self.should_print_verbose() {
- p!(write("{:?}", sz));
- } else if let ty::ConstKind::Unevaluated(..) = sz.kind() {
- // Do not try to evaluate unevaluated constants. If we are const evaluating an
- // array length anon const, rustc will (with debug assertions) print the
- // constant's path. Which will end up here again.
- p!("_");
- } else if let Some(n) = sz.kind().try_to_bits(self.tcx().data_layout.pointer_size) {
- p!(write("{}", n));
- } else if let ty::ConstKind::Param(param) = sz.kind() {
- p!(print(param));
- } else {
- p!("_");
- }
- p!("]")
- }
+ ty::Array(ty, sz) => p!("[", print(ty), "; ", print(sz), "]"),
ty::Slice(ty) => p!("[", print(ty), "]"),
}
match ct.kind() {
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => {
match self.tcx().def_kind(def.did) {
- DefKind::Static(..) | DefKind::Const | DefKind::AssocConst => {
+ DefKind::Const | DefKind::AssocConst => {
p!(print_value_path(def.did, substs))
}
- _ => {
- if def.is_local() {
- let span = self.tcx().def_span(def.did);
- if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
- p!(write("{}", snip))
- } else {
- print_underscore!()
- }
+ DefKind::AnonConst => {
+ if def.is_local()
+ && let span = self.tcx().def_span(def.did)
+ && let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span)
+ {
+ p!(write("{}", snip))
} else {
- print_underscore!()
+ // Do not call `print_value_path` as if a parent of this anon const is an impl it will
+ // attempt to print out the impl trait ref i.e. `<T as Trait>::{constant#0}`. This would
+ // cause printing to enter an infinite recursion if the anon const is in the self type i.e.
+ // `impl<T: Default> Default for [T; 32 - 1 - 1 - 1] {`
+ // where we would try to print `<[T; /* print `constant#0` again */] as Default>::{constant#0}`
+ p!(write("{}::{}", self.tcx().crate_name(def.did.krate), self.tcx().def_path(def.did).to_string_no_crate_verbose()))
}
}
+ defkind => bug!("`{:?}` has unexpcted defkind {:?}", ct, defkind),
}
}
ty::ConstKind::Infer(infer_ct) => {
ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
// FIXME(generic_const_exprs):
// write out some legible representation of an abstract const?
- ty::ConstKind::Expr(_) => p!("[Const Expr]"),
+ ty::ConstKind::Expr(_) => p!("[const expr]"),
ty::ConstKind::Error(_) => p!("[const error]"),
};
Ok(self)
let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions;
- // These printouts are concise. They do not contain all the information
+ // These printouts are concise. They do not contain all the information
// the user might want to diagnose an error, but there is basically no way
- // to fit that into a short string. Hence the recommendation to use
+ // to fit that into a short string. Hence the recommendation to use
// `explain_region()` or `note_and_explain_region()`.
match *region {
ty::ReEarlyBound(ref data) => {
if not_previously_inserted {
ty.super_visit_with(self)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
macro_rules! query_helper_param_ty {
(DefId) => { impl IntoQueryParam<DefId> };
+ (LocalDefId) => { impl IntoQueryParam<LocalDefId> };
($K:ty) => { $K };
}
}
}
+ impl IntoQueryParam<LocalDefId> for OwnerId {
+ #[inline(always)]
+ fn into_query_param(self) -> LocalDefId {
+ self.def_id
+ }
+ }
+
impl IntoQueryParam<DefId> for LocalDefId {
#[inline(always)]
fn into_query_param(self) -> DefId {
self.opt_def_kind(def_id)
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
}
+
+ pub fn bound_type_of(self, def_id: impl IntoQueryParam<DefId>) -> ty::EarlyBinder<Ty<'tcx>> {
+ ty::EarlyBinder(self.type_of(def_id))
+ }
}
impl<'tcx> TyCtxtAt<'tcx> {
self.opt_def_kind(def_id)
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
}
+
+ pub fn bound_type_of(self, def_id: impl IntoQueryParam<DefId>) -> ty::EarlyBinder<Ty<'tcx>> {
+ ty::EarlyBinder(self.type_of(def_id))
+ }
}
if a_repr == b_repr =>
{
let region_bound = relation.with_cause(Cause::ExistentialRegionBound, |relation| {
- relation.relate_with_variance(
- ty::Contravariant,
- ty::VarianceDiagInfo::default(),
- a_region,
- b_region,
- )
+ relation.relate(a_region, b_region)
})?;
Ok(tcx.mk_dynamic(relation.relate(a_obj, b_obj)?, region_bound, a_repr))
}
Ok(tcx.mk_generator_witness(types))
}
+ (&ty::GeneratorWitnessMIR(a_id, a_substs), &ty::GeneratorWitnessMIR(b_id, b_substs))
+ if a_id == b_id =>
+ {
+ // All GeneratorWitness types with the same id represent
+ // the (anonymous) type of the same generator expression. So
+ // all of their regions should be equated.
+ let substs = relation.relate(a_substs, b_substs)?;
+ Ok(tcx.mk_generator_witness_mir(a_id, substs))
+ }
+
(&ty::Closure(a_id, a_substs), &ty::Closure(b_id, b_substs)) if a_id == b_id => {
// All Closure types with the same id represent
// the (anonymous) type of the same closure expression. So
}
(&ty::Ref(a_r, a_ty, a_mutbl), &ty::Ref(b_r, b_ty, b_mutbl)) => {
- let r = relation.relate_with_variance(
- ty::Contravariant,
- ty::VarianceDiagInfo::default(),
- a_r,
- b_r,
- )?;
+ let r = relation.relate(a_r, b_r)?;
let a_mt = ty::TypeAndMut { ty: a_ty, mutbl: a_mutbl };
let b_mt = ty::TypeAndMut { ty: b_ty, mutbl: b_mutbl };
let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?;
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use crate::ty::{self, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
+use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
use rustc_data_structures::functor::IdFunctor;
use rustc_hir::def::Namespace;
use rustc_index::vec::{Idx, IndexVec};
+use rustc_target::abi::TyAndLayout;
use std::fmt;
use std::mem::ManuallyDrop;
}
}
+impl<'tcx> fmt::Debug for AliasTy<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("AliasTy")
+ .field("substs", &self.substs)
+ .field("def_id", &self.def_id)
+ .finish()
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// Atomic structs
//
bool,
usize,
::rustc_target::abi::VariantIdx,
+ u16,
u32,
u64,
String,
crate::ty::BoundRegionKind,
crate::ty::AssocItem,
crate::ty::AssocKind,
+ crate::ty::AliasKind,
crate::ty::Placeholder<crate::ty::BoundRegionKind>,
crate::ty::ClosureKind,
crate::ty::FreeRegion,
impl<'tcx> TypeVisitable<'tcx> for ty::AdtDef<'tcx> {
fn visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
let slot = Rc::get_mut_unchecked(&mut unique);
// Semantically move the contained type out from `unique`, fold
- // it, then move the folded value back into `unique`. Should
+ // it, then move the folded value back into `unique`. Should
// folding fail, `ManuallyDrop` ensures that the "moved-out"
// value is not re-dropped.
let owned = ManuallyDrop::take(slot);
let slot = Arc::get_mut_unchecked(&mut unique);
// Semantically move the contained type out from `unique`, fold
- // it, then move the folded value back into `unique`. Should
+ // it, then move the folded value back into `unique`. Should
// folding fail, `ManuallyDrop` ensures that the "moved-out"
// value is not re-dropped.
let owned = ManuallyDrop::take(slot);
ty::Generator(did, substs.try_fold_with(folder)?, movability)
}
ty::GeneratorWitness(types) => ty::GeneratorWitness(types.try_fold_with(folder)?),
+ ty::GeneratorWitnessMIR(did, substs) => {
+ ty::GeneratorWitnessMIR(did, substs.try_fold_with(folder)?)
+ }
ty::Closure(did, substs) => ty::Closure(did, substs.try_fold_with(folder)?),
ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?),
}
ty::Generator(_did, ref substs, _) => substs.visit_with(visitor),
ty::GeneratorWitness(ref types) => types.visit_with(visitor),
+ ty::GeneratorWitnessMIR(_did, ref substs) => substs.visit_with(visitor),
ty::Closure(_did, ref substs) => substs.visit_with(visitor),
ty::Alias(_, ref data) => data.visit_with(visitor),
| ty::Placeholder(..)
| ty::Param(..)
| ty::Never
- | ty::Foreign(..) => ControlFlow::CONTINUE,
+ | ty::Foreign(..) => ControlFlow::Continue(()),
}
}
}
impl<'tcx> TypeSuperVisitable<'tcx> for ty::Region<'tcx> {
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
impl<'tcx> TypeVisitable<'tcx> for InferConst<'tcx> {
fn visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
self.substs.visit_with(visitor)
}
}
+
+impl<'tcx> TypeVisitable<'tcx> for TyAndLayout<'tcx, Ty<'tcx>> {
+ fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+ visitor.visit_ty(self.ty)
+ }
+}
use crate::ty::visit::ValidateBoundVars;
use crate::ty::InferTy::*;
use crate::ty::{
- self, AdtDef, DefIdTree, Discr, Term, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable,
- TypeVisitor,
+ self, AdtDef, DefIdTree, Discr, FallibleTypeFolder, Term, Ty, TyCtxt, TypeFlags, TypeFoldable,
+ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use crate::ty::{List, ParamEnv};
use hir::def::DefKind;
None
}
+
+ pub fn get_id(&self) -> Option<DefId> {
+ match *self {
+ BoundRegionKind::BrNamed(id, _) => return Some(id),
+ _ => None,
+ }
+ }
}
pub trait Article {
///
/// ## Generators
///
-/// Generators are handled similarly in `GeneratorSubsts`. The set of
+/// Generators are handled similarly in `GeneratorSubsts`. The set of
/// type parameters is similar, but `CK` and `CS` are replaced by the
/// following type parameters:
///
) -> impl Iterator<Item = impl Iterator<Item = Ty<'tcx>> + Captures<'tcx>> {
let layout = tcx.generator_layout(def_id).unwrap();
layout.variant_fields.iter().map(move |variant| {
- variant
- .iter()
- .map(move |field| ty::EarlyBinder(layout.field_tys[*field]).subst(tcx, self.substs))
+ variant.iter().map(move |field| {
+ ty::EarlyBinder(layout.field_tys[*field].ty).subst(tcx, self.substs)
+ })
})
}
}
}
+struct SkipBindersAt<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ index: ty::DebruijnIndex,
+}
+
+impl<'tcx> FallibleTypeFolder<'tcx> for SkipBindersAt<'tcx> {
+ type Error = ();
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn try_fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Result<Binder<'tcx, T>, Self::Error>
+ where
+ T: ty::TypeFoldable<'tcx>,
+ {
+ self.index.shift_in(1);
+ let value = t.try_map_bound(|t| t.try_fold_with(self));
+ self.index.shift_out(1);
+ value
+ }
+
+ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+ if !ty.has_escaping_bound_vars() {
+ Ok(ty)
+ } else if let ty::Bound(index, bv) = *ty.kind() {
+ if index == self.index {
+ Err(())
+ } else {
+ Ok(self.tcx().mk_ty(ty::Bound(index.shifted_out(1), bv)))
+ }
+ } else {
+ ty.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> {
+ if !r.has_escaping_bound_vars() {
+ Ok(r)
+ } else if let ty::ReLateBound(index, bv) = r.kind() {
+ if index == self.index {
+ Err(())
+ } else {
+ Ok(self.tcx().mk_region(ty::ReLateBound(index.shifted_out(1), bv)))
+ }
+ } else {
+ r.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
+ if !ct.has_escaping_bound_vars() {
+ Ok(ct)
+ } else if let ty::ConstKind::Bound(index, bv) = ct.kind() {
+ if index == self.index {
+ Err(())
+ } else {
+ Ok(self.tcx().mk_const(
+ ty::ConstKind::Bound(index.shifted_out(1), bv),
+ ct.ty().try_fold_with(self)?,
+ ))
+ }
+ } else {
+ ct.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_predicate(
+ &mut self,
+ p: ty::Predicate<'tcx>,
+ ) -> Result<ty::Predicate<'tcx>, Self::Error> {
+ if !p.has_escaping_bound_vars() { Ok(p) } else { p.try_super_fold_with(self) }
+ }
+}
+
/// Represents the projection of an associated type.
///
/// For a projection, this would be `<Ty as Trait<...>>::N`.
///
/// For an opaque type, there is no explicit syntax.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct AliasTy<'tcx> {
/// The parameters of the associated or opaque item.
/// aka. `tcx.parent(def_id)`.
pub def_id: DefId,
- /// This field exists to prevent the creation of `ProjectionTy` without using
+ /// This field exists to prevent the creation of `AliasTy` without using
/// [TyCtxt::mk_alias_ty].
pub(super) _use_mk_alias_ty_instead: (),
}
+impl<'tcx> AliasTy<'tcx> {
+ pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind {
+ match tcx.def_kind(self.def_id) {
+ DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection,
+ DefKind::OpaqueTy => ty::Opaque,
+ kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
+ }
+ }
+
+ pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ tcx.mk_ty(ty::Alias(self.kind(tcx), self))
+ }
+}
+
+/// The following methods work only with associated type projections.
impl<'tcx> AliasTy<'tcx> {
pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
match tcx.def_kind(self.def_id) {
DefKind::ImplTraitPlaceholder => {
tcx.parent(tcx.impl_trait_in_trait_parent(self.def_id))
}
- kind => bug!("unexpected DefKind in ProjectionTy: {kind:?}"),
+ kind => bug!("expected a projection AliasTy; found {kind:?}"),
}
}
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- if self.0 == t { ControlFlow::BREAK } else { t.super_visit_with(self) }
+ if self.0 == t { ControlFlow::Break(()) } else { t.super_visit_with(self) }
}
}
pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
match self.kind() {
- FnDef(def_id, substs) => tcx.bound_fn_sig(*def_id).subst(tcx, substs),
+ FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs),
FnPtr(f) => *f,
Error(_) => {
// ignore errors (#54954)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Never
| ty::Tuple(_)
| ty::Error(_)
| ty::Ref(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Array(..)
| ty::Closure(..)
| ty::Never
| ty::Ref(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Array(..)
| ty::Closure(..)
| ty::Never
// anything with custom metadata it might be more complicated.
ty::Ref(_, _, hir::Mutability::Not) | ty::RawPtr(..) => false,
- ty::Generator(..) | ty::GeneratorWitness(..) => false,
+ ty::Generator(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => false,
// Might be, but not "trivial" so just giving the safe answer.
ty::Adt(..) | ty::Closure(..) => false,
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
use rustc_data_structures::intern::Interned;
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_serialize::{self, Decodable, Encodable};
marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>,
}
+impl<'tcx> IntoDiagnosticArg for GenericArg<'tcx> {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ self.to_string().into_diagnostic_arg()
+ }
+}
+
const TAG_MASK: usize = 0b11;
const TYPE_TAG: usize = 0b00;
const REGION_TAG: usize = 0b01;
/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
/// needs `T` substituted immediately. This type primarily exists to avoid forgetting to call
/// `subst`.
+///
+/// If you don't have anything to `subst`, you may be looking for
+/// [`subst_identity`](EarlyBinder::subst_identity) or [`skip_binder`](EarlyBinder::skip_binder).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Encodable, Decodable, HashStable)]
pub struct EarlyBinder<T>(pub T);
EarlyBinder(value)
}
+ /// Skips the binder and returns the "bound" value.
+ /// This can be used to extract data that does not depend on generic parameters
+ /// (e.g., getting the `DefId` of the inner value or getting the number of
+ /// arguments of an `FnSig`). Otherwise, consider using
+ /// [`subst_identity`](EarlyBinder::subst_identity).
+ ///
+ /// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is
+ /// the analogous operation on [`super::Binder`].
pub fn skip_binder(self) -> T {
self.0
}
}
}
+impl<'tcx, I: IntoIterator> ExactSizeIterator for SubstIter<'_, 'tcx, I>
+where
+ I::IntoIter: ExactSizeIterator,
+ I::Item: TypeFoldable<'tcx>,
+{
+}
+
impl<'tcx, 's, I: IntoIterator> EarlyBinder<I>
where
I::Item: Deref,
}
}
+impl<'tcx, I: IntoIterator> ExactSizeIterator for SubstIterCopied<'_, 'tcx, I>
+where
+ I::IntoIter: ExactSizeIterator,
+ I::Item: Deref,
+ <I::Item as Deref>::Target: Copy + TypeFoldable<'tcx>,
+{
+}
+
pub struct EarlyBinderIter<T> {
t: T,
}
let mut folder = SubstFolder { tcx, substs, binders_passed: 0 };
self.0.fold_with(&mut folder)
}
+
+ /// Makes the identity substitution `T0 => T0, ..., TN => TN`.
+ /// Conceptually, this converts universally bound variables into placeholders
+ /// when inside of a given item.
+ ///
+ /// For example, consider `for<T> fn foo<T>(){ .. }`:
+ /// - Outside of `foo`, `T` is bound (represented by the presence of `EarlyBinder`).
+ /// - Inside of the body of `foo`, we treat `T` as a placeholder by calling
+ /// `subst_identity` to discharge the `EarlyBinder`.
+ pub fn subst_identity(self) -> T {
+ self.0
+ }
+
+ /// Returns the inner value, but only if it contains no bound vars.
+ pub fn no_bound_vars(self) -> Option<T> {
+ if !self.0.needs_subst() { Some(self.0) } else { None }
+ }
}
///////////////////////////////////////////////////////////////////////////
use crate::{
hir::place::Place as HirPlace,
infer::canonical::Canonical,
+ traits::ObligationCause,
ty::{
self, tls, BindingMode, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData,
GenericArgKind, InternalSubsts, SubstsRef, Ty, UserSubsts,
},
};
-use rustc_data_structures::{fx::FxHashMap, sync::Lrc, unord::UnordSet, vec_map::VecMap};
+use rustc_data_structures::{
+ fx::FxHashMap,
+ sync::Lrc,
+ unord::{UnordItems, UnordSet},
+ vec_map::VecMap,
+};
use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::{
use rustc_middle::mir::FakeReadCause;
use rustc_session::Session;
use rustc_span::Span;
-use std::{
- collections::hash_map::{self, Entry},
- hash::Hash,
- iter,
-};
+use std::{collections::hash_map::Entry, hash::Hash, iter};
use super::RvalueScopes;
/// that are live across the yield of this generator (if a generator).
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
+ /// Stores the predicates that apply on generator witness types.
+ /// formatting modified file tests/ui/generator/retain-resume-ref.rs
+ pub generator_interior_predicates:
+ FxHashMap<LocalDefId, Vec<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>>,
+
/// We sometimes treat byte string literals (which are of type `&[u8; N]`)
- /// as `&[u8]`, depending on the pattern in which they are used.
+ /// as `&[u8]`, depending on the pattern in which they are used.
/// This hashset records all instances where we behave
/// like this to allow `const_to_pat` to reliably handle this situation.
pub treat_byte_string_as_slice: ItemLocalSet,
closure_fake_reads: Default::default(),
rvalue_scopes: Default::default(),
generator_interior_types: ty::Binder::dummy(Default::default()),
+ generator_interior_predicates: Default::default(),
treat_byte_string_as_slice: Default::default(),
closure_size_eval: Default::default(),
}
/// Returns the type of an expression as a monotype.
///
- /// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
+ /// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
/// some cases, we insert `Adjustment` annotations such as auto-deref or
- /// auto-ref. The type returned by this function does not consider such
- /// adjustments. See `expr_ty_adjusted()` instead.
+ /// auto-ref. The type returned by this function does not consider such
+ /// adjustments. See `expr_ty_adjusted()` instead.
///
/// NB (2): This type doesn't provide type parameter substitutions; e.g., if you
/// ask for the type of `id` in `id(3)`, it will return `fn(&isize) -> isize`
self.data.get(&id.local_id)
}
- pub fn iter(&self) -> hash_map::Iter<'_, hir::ItemLocalId, V> {
- self.data.iter()
+ pub fn items(
+ &'a self,
+ ) -> UnordItems<(hir::ItemLocalId, &'a V), impl Iterator<Item = (hir::ItemLocalId, &'a V)>>
+ {
+ self.data.items().map(|(id, value)| (*id, value))
+ }
+
+ pub fn items_in_stable_order(&self) -> Vec<(ItemLocalId, &'a V)> {
+ self.data.to_sorted_stable_ord()
}
}
validate_hir_id_for_typeck_results(self.hir_owner, id);
self.data.remove(&id.local_id)
}
+
+ pub fn extend(
+ &mut self,
+ items: UnordItems<(hir::HirId, V), impl Iterator<Item = (hir::HirId, V)>>,
+ ) {
+ self.data.extend(items.map(|(id, value)| {
+ validate_hir_id_for_typeck_results(self.hir_owner, id);
+ (id.local_id, value)
+ }))
+ }
}
rustc_index::newtype_index! {
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir;
use crate::ty::layout::IntegerExt;
-use crate::ty::query::TyCtxtAt;
use crate::ty::{
self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitable,
}
}
+ /// Return the set of types that should be taken into accound when checking
+ /// trait bounds on a generator's internal state.
+ pub fn generator_hidden_types(
+ self,
+ def_id: DefId,
+ ) -> impl Iterator<Item = ty::EarlyBinder<Ty<'tcx>>> {
+ let generator_layout = &self.mir_generator_witnesses(def_id);
+ generator_layout
+ .field_tys
+ .iter()
+ .filter(|decl| !decl.ignore_for_traits)
+ .map(|decl| ty::EarlyBinder(decl.ty))
+ }
+
+ /// Normalizes all opaque types in the given value, replacing them
+ /// with their underlying types.
+ pub fn expand_opaque_types(self, val: Ty<'tcx>) -> Ty<'tcx> {
+ let mut visitor = OpaqueTypeExpander {
+ seen_opaque_tys: FxHashSet::default(),
+ expanded_cache: FxHashMap::default(),
+ primary_def_id: None,
+ found_recursion: false,
+ found_any_recursion: false,
+ check_recursion: false,
+ expand_generators: false,
+ tcx: self,
+ };
+ val.fold_with(&mut visitor)
+ }
+
/// Expands the given impl trait type, stopping if the type is recursive.
#[instrument(skip(self), level = "debug", ret)]
pub fn try_expand_impl_trait_type(
found_recursion: false,
found_any_recursion: false,
check_recursion: true,
+ expand_generators: true,
tcx: self,
};
if visitor.found_recursion { Err(expanded_type) } else { Ok(expanded_type) }
}
- pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> {
- ty::EarlyBinder(self.type_of(def_id))
- }
-
pub fn bound_return_position_impl_trait_in_trait_tys(
self,
def_id: DefId,
ty::EarlyBinder(self.collect_return_position_impl_trait_in_trait_tys(def_id))
}
- pub fn bound_fn_sig(self, def_id: DefId) -> ty::EarlyBinder<ty::PolyFnSig<'tcx>> {
- ty::EarlyBinder(self.fn_sig(def_id))
- }
-
- pub fn bound_impl_trait_ref(
- self,
- def_id: DefId,
- ) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> {
- self.impl_trait_ref(def_id).map(|i| ty::EarlyBinder(i))
- }
-
pub fn bound_explicit_item_bounds(
self,
def_id: DefId,
ty::EarlyBinder(self.explicit_item_bounds(def_id))
}
- pub fn bound_item_bounds(
- self,
- def_id: DefId,
- ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
- ty::EarlyBinder(self.item_bounds(def_id))
- }
-
- pub fn bound_const_param_default(self, def_id: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> {
- ty::EarlyBinder(self.const_param_default(def_id))
- }
-
- pub fn bound_predicates_of(
- self,
- def_id: DefId,
- ) -> ty::EarlyBinder<ty::generics::GenericPredicates<'tcx>> {
- ty::EarlyBinder(self.predicates_of(def_id))
- }
-
- pub fn bound_explicit_predicates_of(
- self,
- def_id: DefId,
- ) -> ty::EarlyBinder<ty::generics::GenericPredicates<'tcx>> {
- ty::EarlyBinder(self.explicit_predicates_of(def_id))
- }
-
pub fn bound_impl_subject(self, def_id: DefId) -> ty::EarlyBinder<ty::ImplSubject<'tcx>> {
ty::EarlyBinder(self.impl_subject(def_id))
}
}
}
-impl<'tcx> TyCtxtAt<'tcx> {
- pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> {
- ty::EarlyBinder(self.type_of(def_id))
- }
-}
-
struct OpaqueTypeExpander<'tcx> {
// Contains the DefIds of the opaque types that are currently being
// expanded. When we expand an opaque type we insert the DefId of
primary_def_id: Option<DefId>,
found_recursion: bool,
found_any_recursion: bool,
+ expand_generators: bool,
/// Whether or not to check for recursive opaque types.
/// This is `true` when we're explicitly checking for opaque type
/// recursion, and 'false' otherwise to avoid unnecessary work.
None
}
}
+
+ fn expand_generator(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> Option<Ty<'tcx>> {
+ if self.found_any_recursion {
+ return None;
+ }
+ let substs = substs.fold_with(self);
+ if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
+ let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) {
+ Some(expanded_ty) => *expanded_ty,
+ None => {
+ for bty in self.tcx.generator_hidden_types(def_id) {
+ let hidden_ty = bty.subst(self.tcx, substs);
+ self.fold_ty(hidden_ty);
+ }
+ let expanded_ty = self.tcx.mk_generator_witness_mir(def_id, substs);
+ self.expanded_cache.insert((def_id, substs), expanded_ty);
+ expanded_ty
+ }
+ };
+ if self.check_recursion {
+ self.seen_opaque_tys.remove(&def_id);
+ }
+ Some(expanded_ty)
+ } else {
+ // If another opaque type that we contain is recursive, then it
+ // will report the error, so we don't have to.
+ self.found_any_recursion = true;
+ self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap();
+ None
+ }
+ }
}
impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> {
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
- if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *t.kind() {
+ let mut t = if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *t.kind() {
self.expand_opaque_ty(def_id, substs).unwrap_or(t)
- } else if t.has_opaque_types() {
+ } else if t.has_opaque_types() || t.has_generators() {
t.super_fold_with(self)
} else {
t
+ };
+ if self.expand_generators {
+ if let ty::GeneratorWitnessMIR(def_id, substs) = *t.kind() {
+ t = self.expand_generator(def_id, substs).unwrap_or(t);
+ }
}
+ t
}
}
| ty::Foreign(_)
| ty::Generator(..)
| ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
| ty::Infer(_)
| ty::Alias(..)
| ty::Param(_)
| ty::Foreign(_)
| ty::Generator(..)
| ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
| ty::Infer(_)
| ty::Alias(..)
| ty::Param(_)
false
}
- ty::Foreign(_) | ty::GeneratorWitness(..) | ty::Error(_) => false,
+ ty::Foreign(_)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Error(_) => false,
}
}
| ty::FnPtr(_)
| ty::Char
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::RawPtr(_)
| ty::Ref(..)
| ty::Str => Ok(SmallVec::new()),
// Not trivial because they have components, and instead of looking inside,
// we'll just perform trait selection.
- ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(_) | ty::Adt(..) => false,
+ ty::Closure(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Adt(..) => false,
ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty),
found_recursion: false,
found_any_recursion: false,
check_recursion: false,
+ expand_generators: false,
tcx,
};
val.fold_with(&mut visitor)
}
/// Determines whether an item is annotated with `doc(hidden)`.
-pub fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ assert!(def_id.is_local());
tcx.get_attrs(def_id, sym::doc)
.filter_map(|attr| attr.meta_item_list())
.any(|items| items.iter().any(|item| item.has_name(sym::hidden)))
/// Determines whether an item is an intrinsic by Abi.
pub fn is_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- matches!(tcx.fn_sig(def_id).abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
+ matches!(tcx.fn_sig(def_id).skip_binder().abi(), Abi::RustIntrinsic | Abi::PlatformIntrinsic)
}
pub fn provide(providers: &mut ty::query::Providers) {
fn has_opaque_types(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
}
+ fn has_generators(&self) -> bool {
+ self.has_type_flags(TypeFlags::HAS_TY_GENERATOR)
+ }
fn references_error(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_ERROR)
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
match *r {
ty::ReLateBound(debruijn, _) if debruijn < self.outer_index => {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => {
if (self.callback)(r) {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
if ty.flags().intersects(TypeFlags::HAS_FREE_REGIONS) {
ty.super_visit_with(self)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
if t.outer_exclusive_binder() < self.binder_index
|| !self.visited.insert((self.binder_index, t))
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
match *t.kind() {
ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
if t.outer_exclusive_binder() > self.outer_index {
ControlFlow::Break(FoundEscapingVars)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
if r.bound_at_or_above_binder(self.outer_index) {
ControlFlow::Break(FoundEscapingVars)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
if predicate.outer_exclusive_binder() > self.outer_index {
ControlFlow::Break(FoundEscapingVars)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
if predicate.flags().intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
// in the normalized form
if self.just_constrained {
if let ty::Alias(..) = t.kind() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
}
// in the normalized form
if self.just_constrained {
if let ty::ConstKind::Unevaluated(..) = c.kind() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
}
self.regions.insert(br.kind);
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
);
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
ty::Adt(_, substs)
| ty::Closure(_, substs)
| ty::Generator(_, substs, _)
+ | ty::GeneratorWitnessMIR(_, substs)
| ty::FnDef(_, substs) => {
stack.extend(substs.iter().rev());
}
}
}
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<Ty<'_>> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
+ ty::EarlyBinder(Ty::from_cycle_error(tcx, cycle))
+ }
+}
+
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
+ ty::EarlyBinder(ty::Binder::from_cycle_error(tcx, cycle))
+ }
+}
+
// item_and_field_ids should form a cycle where each field contains the
// type in the next element in the list
pub fn recursive_type_error(
block_map: FxHashMap::default(),
};
- let res = (|| {
+ let res: PResult<_> = try {
pctxt.parse_args(¶ms)?;
- pctxt.parse_body(expr)
- })();
+ pctxt.parse_body(expr)?;
+ };
if let Err(err) = res {
tcx.sess.diagnostic().span_fatal(
err.span,
impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
pub fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> {
parse_by_kind!(self, expr_id, _, "statement",
+ @call("mir_storage_live", args) => {
+ Ok(StatementKind::StorageLive(self.parse_local(args[0])?))
+ },
+ @call("mir_storage_dead", args) => {
+ Ok(StatementKind::StorageDead(self.parse_local(args[0])?))
+ },
+ @call("mir_deinit", args) => {
+ Ok(StatementKind::Deinit(Box::new(self.parse_place(args[0])?)))
+ },
@call("mir_retag", args) => {
Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?)))
},
fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
parse_by_kind!(self, expr_id, _, "rvalue",
@call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
+ @call("mir_checked", args) => {
+ parse_by_kind!(self, args[0], _, "binary op",
+ ExprKind::Binary { op, lhs, rhs } => Ok(Rvalue::CheckedBinaryOp(
+ *op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))
+ )),
+ )
+ },
+ @call("mir_len", args) => Ok(Rvalue::Len(self.parse_place(args[0])?)),
ExprKind::Borrow { borrow_kind, arg } => Ok(
Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?)
),
ExprKind::AddressOf { mutability, arg } => Ok(
Rvalue::AddressOf(*mutability, self.parse_place(*arg)?)
),
+ ExprKind::Binary { op, lhs, rhs } => Ok(
+ Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?)))
+ ),
+ ExprKind::Unary { op, arg } => Ok(
+ Rvalue::UnaryOp(*op, self.parse_operand(*arg)?)
+ ),
+ ExprKind::Repeat { value, count } => Ok(
+ Rvalue::Repeat(self.parse_operand(*value)?, *count)
+ ),
_ => self.parse_operand(expr_id).map(Rvalue::Use),
)
}
/// suitable also to be passed as function arguments.
///
/// The operand returned from this function will *not be valid* after an ExprKind::Scope is
- /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
+ /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
/// operand suitable for use as a call argument. This is almost always equivalent to
/// `as_operand`, except for the particular case of passing values of (potentially) unsized
/// types "by value" (see details below).
// question raised here -- should we "freeze" the
// value of the lhs here? I'm inclined to think not,
// since it seems closer to the semantics of the
- // overloaded version, which takes `&mut self`. This
+ // overloaded version, which takes `&mut self`. This
// only affects weird things like `x += {x += 1; x}`
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?
//
// it is usually better to focus on `the_value` rather
// than the entirety of block(s) surrounding it.
- let adjusted_span = (|| {
+ let adjusted_span =
if let ExprKind::Block { block } = expr.kind
&& let Some(tail_ex) = this.thir[block].expr
{
tail_result_is_ignored: true,
span: expr.span,
});
- return Some(expr.span);
- }
- None
- })();
+ Some(expr.span)
+ } else {
+ None
+ };
let temp =
unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not));
};
let fake_borrow_deref_ty = matched_place.ty(&self.local_decls, tcx).ty;
let fake_borrow_ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, fake_borrow_deref_ty);
- let fake_borrow_temp =
- self.local_decls.push(LocalDecl::new(fake_borrow_ty, temp_span));
+ let mut fake_borrow_temp = LocalDecl::new(fake_borrow_ty, temp_span);
+ fake_borrow_temp.internal = self.local_decls[matched_place.local].internal;
+ fake_borrow_temp.local_info = Some(Box::new(LocalInfo::FakeBorrow));
+ let fake_borrow_temp = self.local_decls.push(fake_borrow_temp);
(matched_place, fake_borrow_temp)
})
// ```
// let place = Foo::new();
// match place { foo if inspect(foo)
- // => feed(foo), ... }
+ // => feed(foo), ... }
// ```
//
// will be treated as if it were really something like:
// ```
// let place = Foo::new();
// match place { ref mut foo if inspect(foo)
- // => feed(foo), ... }
+ // => feed(foo), ... }
// ```
//
// will be treated as if it were really something like:
span: source_info.span,
// FIXME(#54571): This constant comes from user input (a
- // constant in a pattern). Are there forms where users can add
+ // constant in a pattern). Are there forms where users can add
// type annotations here? For example, an associated constant?
// Need to experiment.
user_ty: None,
/// This is used by the overall `match_candidates` algorithm to structure
/// the match as a whole. See `match_candidates` for more details.
///
- /// FIXME(#29623). In some cases, we have some tricky choices to make. for
+ /// FIXME(#29623). In some cases, we have some tricky choices to make. for
/// example, if we are testing that `x == 22`, but the candidate is `x @
/// 13..55`, what should we do? In the event that the test is true, we know
/// that the candidate applies, but in the event of false, we don't know
let fn_id = tcx.hir().local_def_id_to_hir_id(fn_def.did);
let generator_kind = tcx.generator_kind(fn_def.did);
+ // The representation of thir for `-Zunpretty=thir-tree` relies on
+ // the entry expression being the last element of `thir.exprs`.
+ assert_eq!(expr.as_usize(), thir.exprs.len() - 1);
+
// Figure out what primary body this item has.
let body_id = tcx.hir().body_owned_by(fn_def.did);
let span_with_body = tcx.hir().span_with_body(fn_id);
let ty = tcx.ty_error();
let num_params = match body_owner_kind {
- hir::BodyOwnerKind::Fn => tcx.fn_sig(def).inputs().skip_binder().len(),
+ hir::BodyOwnerKind::Fn => tcx.fn_sig(def).skip_binder().inputs().skip_binder().len(),
hir::BodyOwnerKind::Closure => {
let ty = tcx.type_of(def);
match ty.kind() {
```
When processing the `let x`, we will add one drop to the scope for
-`x`. The break will then insert a drop for `x`. When we process `let
+`x`. The break will then insert a drop for `x`. When we process `let
y`, we will add another drop (in fact, to a subscope, but let's ignore
that for now); any later drops would also drop `y`.
if self.tcx.sess.opts.unstable_opts.maximal_hir_to_mir_coverage {
// Some consumers of rustc need to map MIR locations back to HIR nodes. Currently the
// the only part of rustc that tracks MIR -> HIR is the `SourceScopeLocalData::lint_root`
- // field that tracks lint levels for MIR locations. Normally the number of source scopes
+ // field that tracks lint levels for MIR locations. Normally the number of source scopes
// is limited to the set of nodes with lint annotations. The -Zmaximal-hir-to-mir-coverage
// flag changes this behavior to maximize the number of source scopes, increasing the
// granularity of the MIR->HIR mapping.
pub struct MultipleMutBorrows {
#[primary_span]
pub span: Span,
- #[label]
- pub binding_span: Span,
#[subdiagnostic]
- pub occurences: Vec<MultipleMutBorrowOccurence>,
- pub name: Ident,
+ pub occurences: Vec<Conflict>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_already_borrowed)]
+pub struct AlreadyBorrowed {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub occurences: Vec<Conflict>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_already_mut_borrowed)]
+pub struct AlreadyMutBorrowed {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub occurences: Vec<Conflict>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_moved_while_borrowed)]
+pub struct MovedWhileBorrowed {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub occurences: Vec<Conflict>,
}
#[derive(Subdiagnostic)]
-pub enum MultipleMutBorrowOccurence {
- #[label(mutable_borrow)]
- Mutable {
+pub enum Conflict {
+ #[label(mir_build_mutable_borrow)]
+ Mut {
#[primary_span]
span: Span,
- name_mut: Ident,
+ name: Ident,
},
- #[label(immutable_borrow)]
- Immutable {
+ #[label(mir_build_borrow)]
+ Ref {
#[primary_span]
span: Span,
- name_immut: Ident,
+ name: Ident,
},
- #[label(moved)]
+ #[label(mir_build_moved)]
Moved {
#[primary_span]
span: Span,
- name_moved: Ident,
+ name: Ident,
},
}
#[subdiagnostic]
pub let_suggestion: Option<SuggestLet>,
#[subdiagnostic]
+ pub misc_suggestion: Option<MiscPatternSuggestion>,
+ #[subdiagnostic]
pub res_defined_here: Option<ResDefinedHere>,
}
count: usize,
},
}
+
+#[derive(Subdiagnostic)]
+pub enum MiscPatternSuggestion {
+ #[suggestion(
+ mir_build_suggest_attempted_int_lit,
+ code = "_",
+ applicability = "maybe-incorrect"
+ )]
+ AttemptedIntegerLiteral {
+ #[primary_span]
+ start_span: Span,
+ },
+}
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(min_specialization)]
providers.thir_check_unsafety_for_const_arg = check_unsafety::thir_check_unsafety_for_const_arg;
providers.thir_body = thir::cx::thir_body;
providers.thir_tree = thir::cx::thir_tree;
+ providers.thir_flat = thir::cx::thir_flat;
}
/// Returns `true` if `func` refers to the function we are searching in.
fn is_recursive_call(&self, func: &Operand<'tcx>, args: &[Operand<'tcx>]) -> bool {
let Search { tcx, body, trait_substs, .. } = *self;
- // Resolving function type to a specific instance that is being called is expensive. To
+ // Resolving function type to a specific instance that is being called is expensive. To
// avoid the cost we check the number of arguments first, which is sufficient to reject
// most of calls as non-recursive.
if args.len() != body.arg_count {
// A diverging InlineAsm is treated as non-recursing
TerminatorKind::InlineAsm { destination, .. } => {
if destination.is_some() {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
ControlFlow::Break(NonRecursive)
}
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
- | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE,
+ | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()),
}
}
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool {
}
pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String {
+ match thir_body(tcx, owner_def) {
+ Ok((thir, _)) => {
+ let thir = thir.steal();
+ tcx.thir_tree_representation(&thir)
+ }
+ Err(_) => "error".into(),
+ }
+}
+
+pub(crate) fn thir_flat(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String {
match thir_body(tcx, owner_def) {
Ok((thir, _)) => format!("{:#?}", thir.steal()),
Err(_) => "error".into(),
use crate::errors::*;
+use hir::{ExprKind, PatKind};
use rustc_arena::TypedArena;
-use rustc_ast::Mutability;
+use rustc_ast::{LitKind, Mutability};
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
// Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
// when the iterator is an uninhabited type. unreachable_code will trigger instead.
hir::MatchSource::ForLoopDesugar if arms.len() == 1 => {}
- hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
- report_arm_reachability(&cx, &report)
- }
+ hir::MatchSource::ForLoopDesugar
+ | hir::MatchSource::Normal
+ | hir::MatchSource::FormatArgs => report_arm_reachability(&cx, &report),
// Unreachable patterns in try and await expressions occur when one of
// the arms are an uninhabited type. Which is OK.
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
return;
}
- let (inform, interpreted_as_const, res_defined_here,let_suggestion) =
+ let (inform, interpreted_as_const, res_defined_here,let_suggestion, misc_suggestion) =
if let hir::PatKind::Path(hir::QPath::Resolved(
None,
hir::Path {
}
},
None,
+ None,
)
} else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) {
let mut bindings = vec![];
let end_span = semi_span.shrink_to_lo();
let count = witnesses.len();
+ // If the pattern to match is an integer literal:
+ let int_suggestion = if
+ let PatKind::Lit(expr) = &pat.kind
+ && bindings.is_empty()
+ && let ExprKind::Lit(Spanned { node: LitKind::Int(_, _), span }) = expr.kind {
+ // Then give a suggestion, the user might've meant to create a binding instead.
+ Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: span.shrink_to_lo() })
+ } else { None };
+
let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }};
- (sp.map(|_|Inform), None, None, Some(let_suggestion))
+ (sp.map(|_|Inform), None, None, Some(let_suggestion), int_suggestion)
} else{
- (sp.map(|_|Inform), None, None, None)
+ (sp.map(|_|Inform), None, None, None, None)
};
let adt_defined_here = try {
_p: (),
pattern_ty,
let_suggestion,
+ misc_suggestion,
res_defined_here,
adt_defined_here,
});
sub.each_binding(|_, hir_id, span, name| {
match typeck_results.extract_binding_mode(sess, hir_id, span) {
Some(ty::BindByReference(mut_inner)) => match (mut_outer, mut_inner) {
- (Mutability::Not, Mutability::Not) => {} // Both sides are `ref`.
- (Mutability::Mut, Mutability::Mut) => conflicts_mut_mut.push((span, name)), // 2x `ref mut`.
- _ => conflicts_mut_ref.push((span, name)), // `ref` + `ref mut` in either direction.
+ // Both sides are `ref`.
+ (Mutability::Not, Mutability::Not) => {}
+ // 2x `ref mut`.
+ (Mutability::Mut, Mutability::Mut) => {
+ conflicts_mut_mut.push(Conflict::Mut { span, name })
+ }
+ (Mutability::Not, Mutability::Mut) => {
+ conflicts_mut_ref.push(Conflict::Mut { span, name })
+ }
+ (Mutability::Mut, Mutability::Not) => {
+ conflicts_mut_ref.push(Conflict::Ref { span, name })
+ }
},
Some(ty::BindByValue(_)) if is_binding_by_move(cx, hir_id) => {
- conflicts_move.push((span, name)) // `ref mut?` + by-move conflict.
+ conflicts_move.push(Conflict::Moved { span, name }) // `ref mut?` + by-move conflict.
}
Some(ty::BindByValue(_)) | None => {} // `ref mut?` + by-copy is fine.
}
});
+ let report_mut_mut = !conflicts_mut_mut.is_empty();
+ let report_mut_ref = !conflicts_mut_ref.is_empty();
+ let report_move_conflict = !conflicts_move.is_empty();
+
+ let mut occurences = match mut_outer {
+ Mutability::Mut => vec![Conflict::Mut { span: binding_span, name }],
+ Mutability::Not => vec![Conflict::Ref { span: binding_span, name }],
+ };
+ occurences.extend(conflicts_mut_mut);
+ occurences.extend(conflicts_mut_ref);
+ occurences.extend(conflicts_move);
+
// Report errors if any.
- if !conflicts_mut_mut.is_empty() {
+ if report_mut_mut {
// Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
- let mut occurences = vec![];
-
- for (span, name_mut) in conflicts_mut_mut {
- occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut });
- }
- for (span, name_immut) in conflicts_mut_ref {
- occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut });
- }
- for (span, name_moved) in conflicts_move {
- occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved });
- }
- sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name });
- } else if !conflicts_mut_ref.is_empty() {
+ sess.emit_err(MultipleMutBorrows { span: pat.span, occurences });
+ } else if report_mut_ref {
// Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
- let (primary, also) = match mut_outer {
- Mutability::Mut => ("mutable", "immutable"),
- Mutability::Not => ("immutable", "mutable"),
+ match mut_outer {
+ Mutability::Mut => {
+ sess.emit_err(AlreadyMutBorrowed { span: pat.span, occurences });
+ }
+ Mutability::Not => {
+ sess.emit_err(AlreadyBorrowed { span: pat.span, occurences });
+ }
};
- let msg =
- format!("cannot borrow value as {} because it is also borrowed as {}", also, primary);
- let mut err = sess.struct_span_err(pat.span, &msg);
- err.span_label(binding_span, format!("{} borrow, by `{}`, occurs here", primary, name));
- for (span, name) in conflicts_mut_ref {
- err.span_label(span, format!("{} borrow, by `{}`, occurs here", also, name));
- }
- for (span, name) in conflicts_move {
- err.span_label(span, format!("also moved into `{}` here", name));
- }
- err.emit();
- } else if !conflicts_move.is_empty() {
+ } else if report_move_conflict {
// Report by-ref and by-move conflicts, e.g. `ref x @ y`.
- let mut err =
- sess.struct_span_err(pat.span, "cannot move out of value because it is borrowed");
- err.span_label(binding_span, format!("value borrowed, by `{}`, here", name));
- for (span, name) in conflicts_move {
- err.span_label(span, format!("value moved into `{}` here", name));
- }
- err.emit();
+ sess.emit_err(MovedWhileBorrowed { span: pat.span, occurences });
}
}
let obligation: PredicateObligation<'_> = predicate_for_trait_def(
self.tcx(),
self.param_env,
- ObligationCause::misc(self.span, self.id),
+ ObligationCause::misc(self.span, self.id.owner.def_id),
partial_eq_trait_id,
0,
[ty, ty],
) -> Option<IntRange> {
let ty = value.ty();
if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) {
- let val = (|| {
- match value {
- mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) => {
- // For this specific pattern we can skip a lot of effort and go
- // straight to the result, after doing a bit of checking. (We
- // could remove this branch and just fall through, which
- // is more general but much slower.)
- return scalar.to_bits_or_ptr_internal(target_size).unwrap().left();
- }
- mir::ConstantKind::Ty(c) => match c.kind() {
- ty::ConstKind::Value(_) => bug!(
- "encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val"
- ),
- _ => {}
- },
- _ => {}
+ let val = if let mir::ConstantKind::Val(ConstValue::Scalar(scalar), _) = value {
+ // For this specific pattern we can skip a lot of effort and go
+ // straight to the result, after doing a bit of checking. (We
+ // could remove this branch and just fall through, which
+ // is more general but much slower.)
+ scalar.to_bits_or_ptr_internal(target_size).unwrap().left()?
+ } else {
+ if let mir::ConstantKind::Ty(c) = value
+ && let ty::ConstKind::Value(_) = c.kind()
+ {
+ bug!("encountered ConstValue in mir::ConstantKind::Ty, whereas this is expected to be in ConstantKind::Val");
}
// This is a more general form of the previous case.
- value.try_eval_bits(tcx, param_env, ty)
- })()?;
+ value.try_eval_bits(tcx, param_env, ty)?
+ };
let val = val ^ bias;
Some(IntRange { range: val..=val, bias })
} else {
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => None,
};
if let Some(destination) = destination {
/// Calls `f` for each mutable borrow or raw reference in the program.
///
-/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
+/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
/// initializedness, because we cannot move from an `UnsafeCell` (outside of `core::cell`), but
/// other analyses will likely need to check for `!Freeze`.
fn for_each_mut_borrow<'tcx>(
StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::FakeRead(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop
| StatementKind::Retag(..)
| StatementKind::Intrinsic(..)
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
}
StatementKind::Retag(..) => {
// We don't track references.
}
- StatementKind::Nop
+ StatementKind::ConstEvalCounter
+ | StatementKind::Nop
| StatementKind::FakeRead(..)
| StatementKind::Coverage(..)
| StatementKind::AscribeUserType(..) => (),
// PART 3
// Add retag after assignments where data "enters" this function: the RHS is behind a deref and the LHS is not.
for block_data in basic_blocks {
- // We want to insert statements as we iterate. To this end, we
+ // We want to insert statements as we iterate. To this end, we
// iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
let (retag_kind, place) = match block_data.statements[i].kind {
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {
// safe (at least as emitted during MIR construction)
}
_fd: &'tcx hir::FnDecl<'tcx>,
b: hir::BodyId,
_s: rustc_span::Span,
- _id: HirId,
+ _id: LocalDefId,
) {
if matches!(fk, intravisit::FnKind::Closure) {
self.visit_body(self.tcx.hir().body(b))
}
impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
+ fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
+ if data.is_cleanup {
+ // Because of the restrictions around control flow in cleanup blocks, we don't perform
+ // this optimization at all in such blocks.
+ return;
+ }
+ self.super_basic_block_data(block, data);
+ }
+
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
let _: Option<_> = try {
let target = terminator.kind.as_goto()?;
--- /dev/null
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::visit::*;
+use rustc_middle::mir::*;
+use rustc_middle::ty::TyCtxt;
+use rustc_mir_dataflow::impls::borrowed_locals;
+
+use crate::ssa::SsaLocals;
+use crate::MirPass;
+
+/// Unify locals that copy each other.
+///
+/// We consider patterns of the form
+/// _a = rvalue
+/// _b = move? _a
+/// _c = move? _a
+/// _d = move? _c
+/// where each of the locals is only assigned once.
+///
+/// We want to replace all those locals by `_a`, either copied or moved.
+pub struct CopyProp;
+
+impl<'tcx> MirPass<'tcx> for CopyProp {
+ fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+ sess.mir_opt_level() >= 4
+ }
+
+ #[instrument(level = "trace", skip(self, tcx, body))]
+ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ debug!(def_id = ?body.source.def_id());
+ propagate_ssa(tcx, body);
+ }
+}
+
+fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
+ let borrowed_locals = borrowed_locals(body);
+ let ssa = SsaLocals::new(tcx, param_env, body, &borrowed_locals);
+
+ let fully_moved = fully_moved_locals(&ssa, body);
+ debug!(?fully_moved);
+
+ let mut storage_to_remove = BitSet::new_empty(fully_moved.domain_size());
+ for (local, &head) in ssa.copy_classes().iter_enumerated() {
+ if local != head {
+ storage_to_remove.insert(head);
+ }
+ }
+
+ let any_replacement = ssa.copy_classes().iter_enumerated().any(|(l, &h)| l != h);
+
+ Replacer {
+ tcx,
+ copy_classes: &ssa.copy_classes(),
+ fully_moved,
+ borrowed_locals,
+ storage_to_remove,
+ }
+ .visit_body_preserves_cfg(body);
+
+ if any_replacement {
+ crate::simplify::remove_unused_definitions(body);
+ }
+}
+
+/// `SsaLocals` computed equivalence classes between locals considering copy/move assignments.
+///
+/// This function also returns whether all the `move?` in the pattern are `move` and not copies.
+/// A local which is in the bitset can be replaced by `move _a`. Otherwise, it must be
+/// replaced by `copy _a`, as we cannot move multiple times from `_a`.
+///
+/// If an operand copies `_c`, it must happen before the assignment `_d = _c`, otherwise it is UB.
+/// This means that replacing it by a copy of `_a` if ok, since this copy happens before `_c` is
+/// moved, and therefore that `_d` is moved.
+#[instrument(level = "trace", skip(ssa, body))]
+fn fully_moved_locals(ssa: &SsaLocals, body: &Body<'_>) -> BitSet<Local> {
+ let mut fully_moved = BitSet::new_filled(body.local_decls.len());
+
+ for (_, rvalue) in ssa.assignments(body) {
+ let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) | Rvalue::CopyForDeref(place))
+ = rvalue
+ else { continue };
+
+ let Some(rhs) = place.as_local() else { continue };
+ if !ssa.is_ssa(rhs) {
+ continue;
+ }
+
+ if let Rvalue::Use(Operand::Copy(_)) | Rvalue::CopyForDeref(_) = rvalue {
+ fully_moved.remove(rhs);
+ }
+ }
+
+ ssa.meet_copy_equivalence(&mut fully_moved);
+
+ fully_moved
+}
+
+/// Utility to help performing subtitution of `*pattern` by `target`.
+struct Replacer<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ fully_moved: BitSet<Local>,
+ storage_to_remove: BitSet<Local>,
+ borrowed_locals: BitSet<Local>,
+ copy_classes: &'a IndexVec<Local, Local>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for Replacer<'_, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn visit_local(&mut self, local: &mut Local, ctxt: PlaceContext, _: Location) {
+ let new_local = self.copy_classes[*local];
+ match ctxt {
+ // Do not modify the local in storage statements.
+ PlaceContext::NonUse(NonUseContext::StorageLive | NonUseContext::StorageDead) => {}
+ // The local should have been marked as non-SSA.
+ PlaceContext::MutatingUse(_) => assert_eq!(*local, new_local),
+ // We access the value.
+ _ => *local = new_local,
+ }
+ }
+
+ fn visit_place(&mut self, place: &mut Place<'tcx>, ctxt: PlaceContext, loc: Location) {
+ if let Some(new_projection) = self.process_projection(&place.projection, loc) {
+ place.projection = self.tcx().intern_place_elems(&new_projection);
+ }
+
+ let observes_address = match ctxt {
+ PlaceContext::NonMutatingUse(
+ NonMutatingUseContext::SharedBorrow
+ | NonMutatingUseContext::ShallowBorrow
+ | NonMutatingUseContext::UniqueBorrow
+ | NonMutatingUseContext::AddressOf,
+ ) => true,
+ // For debuginfo, merging locals is ok.
+ PlaceContext::NonUse(NonUseContext::VarDebugInfo) => {
+ self.borrowed_locals.contains(place.local)
+ }
+ _ => false,
+ };
+ if observes_address && !place.is_indirect() {
+ // We observe the address of `place.local`. Do not replace it.
+ } else {
+ self.visit_local(
+ &mut place.local,
+ PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
+ loc,
+ )
+ }
+ }
+
+ fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {
+ if let Operand::Move(place) = *operand
+ && let Some(local) = place.as_local()
+ && !self.fully_moved.contains(local)
+ {
+ *operand = Operand::Copy(place);
+ }
+ self.super_operand(operand, loc);
+ }
+
+ fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
+ if let StatementKind::StorageDead(l) = stmt.kind
+ && self.storage_to_remove.contains(l)
+ {
+ stmt.make_nop();
+ } else if let StatementKind::Assign(box (ref place, ref mut rvalue)) = stmt.kind
+ && place.as_local().is_some()
+ {
+ // Do not replace assignments.
+ self.visit_rvalue(rvalue, loc)
+ } else {
+ self.super_statement(stmt, loc);
+ }
+ }
+}
let mut found_loop_exit = false;
for &branch in branches.iter() {
if backedge_from_bcbs.iter().any(|&backedge_from_bcb| {
- self.bcb_is_dominated_by(backedge_from_bcb, branch.target_bcb)
+ self.bcb_dominates(branch.target_bcb, backedge_from_bcb)
}) {
if let Some(reloop_branch) = some_reloop_branch {
if reloop_branch.counter(&self.basic_coverage_blocks).is_none() {
}
#[inline]
- fn bcb_is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool {
- self.basic_coverage_blocks.is_dominated_by(node, dom)
+ fn bcb_dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
+ self.basic_coverage_blocks.dominates(dom, node)
}
#[inline]
}
#[inline(always)]
- pub fn is_dominated_by(&self, node: BasicCoverageBlock, dom: BasicCoverageBlock) -> bool {
- self.dominators.as_ref().unwrap().is_dominated_by(node, dom)
+ pub fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
+ self.dominators.as_ref().unwrap().dominates(dom, node)
}
#[inline(always)]
/// to the BCB's primary counter or expression).
///
/// The BCB CFG is critical to simplifying the coverage analysis by ensuring graph path-based
-/// queries (`is_dominated_by()`, `predecessors`, `successors`, etc.) have branch (control flow)
+/// queries (`dominates()`, `predecessors`, `successors`, etc.) have branch (control flow)
/// significance.
#[derive(Debug, Clone)]
pub(super) struct BasicCoverageBlockData {
// branching block would have given an `Expression` (or vice versa).
let (some_successor_to_add, some_loop_header) =
if let Some((_, loop_header)) = context.loop_backedges {
- if basic_coverage_blocks.is_dominated_by(successor, loop_header) {
+ if basic_coverage_blocks.dominates(loop_header, successor) {
(Some(successor), Some(loop_header))
} else {
(None, None)
//
// The overall complexity appears to be comparable to many other MIR transform algorithms, and I
// don't expect that this function is creating a performance hot spot, but if this becomes an
- // issue, there may be ways to optimize the `is_dominated_by` algorithm (as indicated by an
+ // issue, there may be ways to optimize the `dominates` algorithm (as indicated by an
// existing `FIXME` comment in that code), or possibly ways to optimize it's usage here, perhaps
// by keeping track of results for visited `BasicCoverageBlock`s if they can be used to short
- // circuit downstream `is_dominated_by` checks.
+ // circuit downstream `dominates` checks.
//
// For now, that kind of optimization seems unnecessarily complicated.
for (bcb, _) in basic_coverage_blocks.iter_enumerated() {
for &successor in &basic_coverage_blocks.successors[bcb] {
- if basic_coverage_blocks.is_dominated_by(bcb, successor) {
+ if basic_coverage_blocks.dominates(successor, bcb) {
let loop_header = successor;
let backedge_from_bcb = bcb;
debug!(
// FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
// to HIR for it.
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
- let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
+ let (_, fn_body_id) =
+ hir::map::associated_body(hir_node).expect("HIR node is a function with body");
(hir_node.fn_sig(), tcx.hir().body(fn_body_id))
}
/// Note: A `CoverageStatement` merged into another CoverageSpan may come from a `BasicBlock` that
/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches
/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock`
-/// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`.
+/// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
#[derive(Debug, Clone)]
pub(super) struct CoverageSpan {
pub span: Span,
if a.is_in_same_bcb(b) {
Some(Ordering::Equal)
} else {
- // Sort equal spans by dominator relationship, in reverse order (so
- // dominators always come after the dominated equal spans). When later
- // comparing two spans in order, the first will either dominate the second,
- // or they will have no dominator relationship.
- self.basic_coverage_blocks.dominators().rank_partial_cmp(b.bcb, a.bcb)
+ // Sort equal spans by dominator relationship (so dominators always come
+ // before the dominated equal spans). When later comparing two spans in
+ // order, the first will either dominate the second, or they will have no
+ // dominator relationship.
+ self.basic_coverage_blocks.dominators().rank_partial_cmp(a.bcb, b.bcb)
}
} else {
// Sort hi() in reverse order so shorter spans are attempted after longer spans.
fn hold_pending_dups_unless_dominated(&mut self) {
// Equal coverage spans are ordered by dominators before dominated (if any), so it should be
// impossible for `curr` to dominate any previous `CoverageSpan`.
- debug_assert!(!self.span_bcb_is_dominated_by(self.prev(), self.curr()));
+ debug_assert!(!self.span_bcb_dominates(self.curr(), self.prev()));
let initial_pending_count = self.pending_dups.len();
if initial_pending_count > 0 {
let mut pending_dups = self.pending_dups.split_off(0);
- pending_dups.retain(|dup| !self.span_bcb_is_dominated_by(self.curr(), dup));
+ pending_dups.retain(|dup| !self.span_bcb_dominates(dup, self.curr()));
self.pending_dups.append(&mut pending_dups);
if self.pending_dups.len() < initial_pending_count {
debug!(
}
}
- if self.span_bcb_is_dominated_by(self.curr(), self.prev()) {
+ if self.span_bcb_dominates(self.prev(), self.curr()) {
debug!(
" different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}",
self.prev()
}
}
- fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &CoverageSpan) -> bool {
- self.basic_coverage_blocks.is_dominated_by(covspan.bcb, dom_covspan.bcb)
+ fn span_bcb_dominates(&self, dom_covspan: &CoverageSpan, covspan: &CoverageSpan) -> bool {
+ self.basic_coverage_blocks.dominates(dom_covspan.bcb, covspan.bcb)
}
}
| StatementKind::StorageDead(_)
// Coverage should not be encountered, but don't inject coverage coverage
| StatementKind::Coverage(_)
+ // Ignore `ConstEvalCounter`s
+ | StatementKind::ConstEvalCounter
// Ignore `Nop`s
| StatementKind::Nop => None,
--- /dev/null
+//! A pass that inserts the `ConstEvalCounter` instruction into any blocks that have a back edge
+//! (thus indicating there is a loop in the CFG), or whose terminator is a function call.
+use crate::MirPass;
+
+use rustc_data_structures::graph::dominators::Dominators;
+use rustc_middle::mir::{
+ BasicBlock, BasicBlockData, Body, Statement, StatementKind, TerminatorKind,
+};
+use rustc_middle::ty::TyCtxt;
+
+pub struct CtfeLimit;
+
+impl<'tcx> MirPass<'tcx> for CtfeLimit {
+ #[instrument(skip(self, _tcx, body))]
+ fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let doms = body.basic_blocks.dominators();
+ let indices: Vec<BasicBlock> = body
+ .basic_blocks
+ .iter_enumerated()
+ .filter_map(|(node, node_data)| {
+ if matches!(node_data.terminator().kind, TerminatorKind::Call { .. })
+ // Back edges in a CFG indicate loops
+ || has_back_edge(&doms, node, &node_data)
+ {
+ Some(node)
+ } else {
+ None
+ }
+ })
+ .collect();
+ for index in indices {
+ insert_counter(
+ body.basic_blocks_mut()
+ .get_mut(index)
+ .expect("basic_blocks index {index} should exist"),
+ );
+ }
+ }
+}
+
+fn has_back_edge(
+ doms: &Dominators<BasicBlock>,
+ node: BasicBlock,
+ node_data: &BasicBlockData<'_>,
+) -> bool {
+ if !doms.is_reachable(node) {
+ return false;
+ }
+ // Check if any of the dominators of the node are also the node's successor.
+ doms.dominators(node)
+ .any(|dom| node_data.terminator().successors().into_iter().any(|succ| succ == dom))
+}
+
+fn insert_counter(basic_block_data: &mut BasicBlockData<'_>) {
+ basic_block_data.statements.push(Statement {
+ source_info: basic_block_data.terminator().source_info,
+ kind: StatementKind::ConstEvalCounter,
+ });
+}
| StatementKind::StorageDead(_)
| StatementKind::Coverage(_)
| StatementKind::Intrinsic(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => (),
StatementKind::FakeRead(_) | StatementKind::AscribeUserType(_, _) => {
match &statement.kind {
StatementKind::Assign(box (dest, rvalue)) => {
match rvalue {
- Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => {
+ Rvalue::CopyForDeref(place)
+ | Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) => {
// These might've been turned into self-assignments by the replacement
// (this includes the original statement we wanted to eliminate).
if dest == place {
self.add_place(**place);
}
StatementKind::Intrinsic(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop
| StatementKind::Coverage(_)
| StatementKind::StorageLive(_)
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
if let StatementKind::Assign(box (
lhs,
- Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)),
+ Rvalue::CopyForDeref(rhs) | Rvalue::Use(Operand::Copy(rhs) | Operand::Move(rhs)),
)) = &statement.kind
{
let Some((src, dest)) = places_to_candidate_pair(*lhs, *rhs, self.body) else {
for bound in bounds {
if let Some(bound_ty) = self.is_pointer_trait(&bound.kind().skip_binder()) {
// Get the argument types as they appear in the function signature.
- let arg_defs = self.tcx.fn_sig(def_id).skip_binder().inputs();
+ let arg_defs = self.tcx.fn_sig(def_id).subst_identity().skip_binder().inputs();
for (arg_num, arg_def) in arg_defs.iter().enumerate() {
// For all types reachable from the argument type in the fn sig
for generic_inner_ty in arg_def.walk() {
.as_ref()
.assert_crate_local()
.lint_root;
- let fn_sig = self.tcx.fn_sig(fn_id);
+ // FIXME: use existing printing routines to print the function signature
+ let fn_sig = self.tcx.fn_sig(fn_id).subst(self.tcx, fn_substs);
let unsafety = fn_sig.unsafety().prefix_str();
let abi = match fn_sig.abi() {
Abi::Rust => String::from(""),
use crate::simplify;
use crate::util::expand_aggregate;
use crate::MirPass;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::pluralize;
use rustc_hir as hir;
use rustc_hir::lang_items::LangItem;
use rustc_hir::GeneratorKind;
};
use rustc_mir_dataflow::storage::always_storage_live_locals;
use rustc_mir_dataflow::{self, Analysis};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
use rustc_target::abi::VariantIdx;
use rustc_target::spec::PanicStrategy;
use std::{iter, ops};
new_local
}
+/// Transforms the `body` of the generator applying the following transforms:
+///
+/// - Eliminates all the `get_context` calls that async lowering created.
+/// - Replace all `Local` `ResumeTy` types with `&mut Context<'_>` (`context_mut_ref`).
+///
+/// The `Local`s that have their types replaced are:
+/// - The `resume` argument itself.
+/// - The argument to `get_context`.
+/// - The yielded value of a `yield`.
+///
+/// The `ResumeTy` hides a `&mut Context<'_>` behind an unsafe raw pointer, and the
+/// `get_context` function is being used to convert that back to a `&mut Context<'_>`.
+///
+/// Ideally the async lowering would not use the `ResumeTy`/`get_context` indirection,
+/// but rather directly use `&mut Context<'_>`, however that would currently
+/// lead to higher-kinded lifetime errors.
+/// See <https://github.com/rust-lang/rust/issues/105501>.
+///
+/// The async lowering step and the type / lifetime inference / checking are
+/// still using the `ResumeTy` indirection for the time being, and that indirection
+/// is removed here. After this transform, the generator body only knows about `&mut Context<'_>`.
+fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let context_mut_ref = tcx.mk_task_context();
+
+ // replace the type of the `resume` argument
+ replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref);
+
+ let get_context_def_id = tcx.require_lang_item(LangItem::GetContext, None);
+
+ for bb in BasicBlock::new(0)..body.basic_blocks.next_index() {
+ let bb_data = &body[bb];
+ if bb_data.is_cleanup {
+ continue;
+ }
+
+ match &bb_data.terminator().kind {
+ TerminatorKind::Call { func, .. } => {
+ let func_ty = func.ty(body, tcx);
+ if let ty::FnDef(def_id, _) = *func_ty.kind() {
+ if def_id == get_context_def_id {
+ let local = eliminate_get_context_call(&mut body[bb]);
+ replace_resume_ty_local(tcx, body, local, context_mut_ref);
+ }
+ } else {
+ continue;
+ }
+ }
+ TerminatorKind::Yield { resume_arg, .. } => {
+ replace_resume_ty_local(tcx, body, resume_arg.local, context_mut_ref);
+ }
+ _ => {}
+ }
+ }
+}
+
+fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local {
+ let terminator = bb_data.terminator.take().unwrap();
+ if let TerminatorKind::Call { mut args, destination, target, .. } = terminator.kind {
+ let arg = args.pop().unwrap();
+ let local = arg.place().unwrap().local;
+
+ let arg = Rvalue::Use(arg);
+ let assign = Statement {
+ source_info: terminator.source_info,
+ kind: StatementKind::Assign(Box::new((destination, arg))),
+ };
+ bb_data.statements.push(assign);
+ bb_data.terminator = Some(Terminator {
+ source_info: terminator.source_info,
+ kind: TerminatorKind::Goto { target: target.unwrap() },
+ });
+ local
+ } else {
+ bug!();
+ }
+}
+
+#[cfg_attr(not(debug_assertions), allow(unused))]
+fn replace_resume_ty_local<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ body: &mut Body<'tcx>,
+ local: Local,
+ context_mut_ref: Ty<'tcx>,
+) {
+ let local_ty = std::mem::replace(&mut body.local_decls[local].ty, context_mut_ref);
+ // We have to replace the `ResumeTy` that is used for type and borrow checking
+ // with `&mut Context<'_>` in MIR.
+ #[cfg(debug_assertions)]
+ {
+ if let ty::Adt(resume_ty_adt, _) = local_ty.kind() {
+ let expected_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None));
+ assert_eq!(*resume_ty_adt, expected_adt);
+ } else {
+ panic!("expected `ResumeTy`, found `{:?}`", local_ty);
+ };
+ }
+}
+
struct LivenessInfo {
/// Which locals are live across any suspension point.
saved_locals: GeneratorSavedLocals,
body: &Body<'tcx>,
witness: Ty<'tcx>,
upvars: Vec<Ty<'tcx>>,
- saved_locals: &GeneratorSavedLocals,
+ layout: &GeneratorLayout<'tcx>,
) {
let did = body.source.def_id();
let param_env = tcx.param_env(did);
}
};
- for (local, decl) in body.local_decls.iter_enumerated() {
- // Ignore locals which are internal or not saved between yields.
- if !saved_locals.contains(local) || decl.internal {
+ let mut mismatches = Vec::new();
+ for fty in &layout.field_tys {
+ if fty.ignore_for_traits {
continue;
}
- let decl_ty = tcx.normalize_erasing_regions(param_env, decl.ty);
+ let decl_ty = tcx.normalize_erasing_regions(param_env, fty.ty);
// Sanity check that typeck knows about the type of locals which are
// live across a suspension point
if !allowed.contains(&decl_ty) && !allowed_upvars.contains(&decl_ty) {
- span_bug!(
- body.span,
- "Broken MIR: generator contains type {} in MIR, \
- but typeck only knows about {} and {:?}",
- decl_ty,
- allowed,
- allowed_upvars
- );
+ mismatches.push(decl_ty);
}
}
+
+ if !mismatches.is_empty() {
+ span_bug!(
+ body.span,
+ "Broken MIR: generator contains type {:?} in MIR, \
+ but typeck only knows about {} and {:?}",
+ mismatches,
+ allowed,
+ allowed_upvars
+ );
+ }
}
fn compute_layout<'tcx>(
+ tcx: TyCtxt<'tcx>,
liveness: LivenessInfo,
- body: &mut Body<'tcx>,
+ body: &Body<'tcx>,
) -> (
FxHashMap<Local, (Ty<'tcx>, VariantIdx, usize)>,
GeneratorLayout<'tcx>,
let mut locals = IndexVec::<GeneratorSavedLocal, _>::new();
let mut tys = IndexVec::<GeneratorSavedLocal, _>::new();
for (saved_local, local) in saved_locals.iter_enumerated() {
- locals.push(local);
- tys.push(body.local_decls[local].ty);
debug!("generator saved local {:?} => {:?}", saved_local, local);
+
+ locals.push(local);
+ let decl = &body.local_decls[local];
+ debug!(?decl);
+
+ let ignore_for_traits = if tcx.sess.opts.unstable_opts.drop_tracking_mir {
+ match decl.local_info {
+ // Do not include raw pointers created from accessing `static` items, as those could
+ // well be re-created by another access to the same static.
+ Some(box LocalInfo::StaticRef { is_thread_local, .. }) => !is_thread_local,
+ // Fake borrows are only read by fake reads, so do not have any reality in
+ // post-analysis MIR.
+ Some(box LocalInfo::FakeBorrow) => true,
+ _ => false,
+ }
+ } else {
+ // FIXME(#105084) HIR-based drop tracking does not account for all the temporaries that
+ // MIR building may introduce. This leads to wrongly ignored types, but this is
+ // necessary for internal consistency and to avoid ICEs.
+ decl.internal
+ };
+ let decl =
+ GeneratorSavedTy { ty: decl.ty, source_info: decl.source_info, ignore_for_traits };
+ debug!(?decl);
+
+ tys.push(decl);
}
// Leave empty variants for the UNRESUMED, RETURNED, and POISONED states.
// just use the first one here. That's fine; fields do not move
// around inside generators, so it doesn't matter which variant
// index we access them by.
- remap.entry(locals[saved_local]).or_insert((tys[saved_local], variant_index, idx));
+ remap.entry(locals[saved_local]).or_insert((tys[saved_local].ty, variant_index, idx));
}
variant_fields.push(fields);
variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]);
let layout =
GeneratorLayout { field_tys: tys, variant_fields, variant_source_info, storage_conflicts };
+ debug!(?layout);
(remap, layout, storage_liveness)
}
.collect()
}
+#[instrument(level = "debug", skip(tcx), ret)]
+pub(crate) fn mir_generator_witnesses<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+) -> GeneratorLayout<'tcx> {
+ assert!(tcx.sess.opts.unstable_opts.drop_tracking_mir);
+ let def_id = def_id.expect_local();
+
+ let (body, _) = tcx.mir_promoted(ty::WithOptConstParam::unknown(def_id));
+ let body = body.borrow();
+ let body = &*body;
+
+ // The first argument is the generator type passed by value
+ let gen_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
+
+ // Get the interior types and substs which typeck computed
+ let movable = match *gen_ty.kind() {
+ ty::Generator(_, _, movability) => movability == hir::Movability::Movable,
+ _ => span_bug!(body.span, "unexpected generator type {}", gen_ty),
+ };
+
+ // When first entering the generator, move the resume argument into its new local.
+ let always_live_locals = always_storage_live_locals(&body);
+
+ let liveness_info = locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
+
+ // Extract locals which are live across suspension point into `layout`
+ // `remap` gives a mapping from local indices onto generator struct indices
+ // `storage_liveness` tells us which locals have live storage at suspension points
+ let (_, generator_layout, _) = compute_layout(tcx, liveness_info, body);
+
+ check_suspend_tys(tcx, &generator_layout, &body);
+
+ generator_layout
+}
+
impl<'tcx> MirPass<'tcx> for StateTransform {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let Some(yield_ty) = body.yield_ty() else {
// The first argument is the generator type passed by value
let gen_ty = body.local_decls.raw[1].ty;
- // Get the interior types and substs which typeck computed
- let (upvars, interior, discr_ty, movable) = match *gen_ty.kind() {
+ // Get the discriminant type and substs which typeck computed
+ let (discr_ty, upvars, interior, movable) = match *gen_ty.kind() {
ty::Generator(_, substs, movability) => {
let substs = substs.as_generator();
(
- substs.upvar_tys().collect(),
- substs.witness(),
substs.discr_ty(tcx),
+ substs.upvar_tys().collect::<Vec<_>>(),
+ substs.witness(),
movability == hir::Movability::Movable,
)
}
}
};
- let is_async_kind = body.generator_kind().unwrap() != GeneratorKind::Gen;
+ let is_async_kind = matches!(body.generator_kind(), Some(GeneratorKind::Async(_)));
let (state_adt_ref, state_substs) = if is_async_kind {
// Compute Poll<return_ty>
- let state_did = tcx.require_lang_item(LangItem::Poll, None);
- let state_adt_ref = tcx.adt_def(state_did);
- let state_substs = tcx.intern_substs(&[body.return_ty().into()]);
- (state_adt_ref, state_substs)
+ let poll_did = tcx.require_lang_item(LangItem::Poll, None);
+ let poll_adt_ref = tcx.adt_def(poll_did);
+ let poll_substs = tcx.intern_substs(&[body.return_ty().into()]);
+ (poll_adt_ref, poll_substs)
} else {
// Compute GeneratorState<yield_ty, return_ty>
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
// RETURN_PLACE then is a fresh unused local with type ret_ty.
let new_ret_local = replace_local(RETURN_PLACE, ret_ty, body, tcx);
+ // Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies.
+ if is_async_kind {
+ transform_async_context(tcx, body);
+ }
+
// We also replace the resume argument and insert an `Assign`.
// This is needed because the resume argument `_2` might be live across a `yield`, in which
// case there is no `Assign` to it that the transform can turn into a store to the generator
// state. After the yield the slot in the generator state would then be uninitialized.
let resume_local = Local::new(2);
- let new_resume_local =
- replace_local(resume_local, body.local_decls[resume_local].ty, body, tcx);
+ let resume_ty =
+ if is_async_kind { tcx.mk_task_context() } else { body.local_decls[resume_local].ty };
+ let new_resume_local = replace_local(resume_local, resume_ty, body, tcx);
// When first entering the generator, move the resume argument into its new local.
let source_info = SourceInfo::outermost(body.span);
let liveness_info =
locals_live_across_suspend_points(tcx, body, &always_live_locals, movable);
- sanitize_witness(tcx, body, interior, upvars, &liveness_info.saved_locals);
-
if tcx.sess.opts.unstable_opts.validate_mir {
let mut vis = EnsureGeneratorFieldAssignmentsNeverAlias {
assigned_local: None,
// Extract locals which are live across suspension point into `layout`
// `remap` gives a mapping from local indices onto generator struct indices
// `storage_liveness` tells us which locals have live storage at suspension points
- let (remap, layout, storage_liveness) = compute_layout(liveness_info, body);
+ let (remap, layout, storage_liveness) = compute_layout(tcx, liveness_info, body);
+
+ if tcx.sess.opts.unstable_opts.validate_mir
+ && !tcx.sess.opts.unstable_opts.drop_tracking_mir
+ {
+ sanitize_witness(tcx, body, interior, upvars, &layout);
+ }
let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id()));
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
| StatementKind::Intrinsic(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
}
}
}
}
+
+fn check_suspend_tys<'tcx>(tcx: TyCtxt<'tcx>, layout: &GeneratorLayout<'tcx>, body: &Body<'tcx>) {
+ let mut linted_tys = FxHashSet::default();
+
+ // We want a user-facing param-env.
+ let param_env = tcx.param_env(body.source.def_id());
+
+ for (variant, yield_source_info) in
+ layout.variant_fields.iter().zip(&layout.variant_source_info)
+ {
+ debug!(?variant);
+ for &local in variant {
+ let decl = &layout.field_tys[local];
+ debug!(?decl);
+
+ if !decl.ignore_for_traits && linted_tys.insert(decl.ty) {
+ let Some(hir_id) = decl.source_info.scope.lint_root(&body.source_scopes) else { continue };
+
+ check_must_not_suspend_ty(
+ tcx,
+ decl.ty,
+ hir_id,
+ param_env,
+ SuspendCheckData {
+ source_span: decl.source_info.span,
+ yield_span: yield_source_info.span,
+ plural_len: 1,
+ ..Default::default()
+ },
+ );
+ }
+ }
+ }
+}
+
+#[derive(Default)]
+struct SuspendCheckData<'a> {
+ source_span: Span,
+ yield_span: Span,
+ descr_pre: &'a str,
+ descr_post: &'a str,
+ plural_len: usize,
+}
+
+// Returns whether it emitted a diagnostic or not
+// Note that this fn and the proceeding one are based on the code
+// for creating must_use diagnostics
+//
+// Note that this technique was chosen over things like a `Suspend` marker trait
+// as it is simpler and has precedent in the compiler
+fn check_must_not_suspend_ty<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ hir_id: hir::HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ data: SuspendCheckData<'_>,
+) -> bool {
+ if ty.is_unit() {
+ return false;
+ }
+
+ let plural_suffix = pluralize!(data.plural_len);
+
+ debug!("Checking must_not_suspend for {}", ty);
+
+ match *ty.kind() {
+ ty::Adt(..) if ty.is_box() => {
+ let boxed_ty = ty.boxed_ty();
+ let descr_pre = &format!("{}boxed ", data.descr_pre);
+ check_must_not_suspend_ty(
+ tcx,
+ boxed_ty,
+ hir_id,
+ param_env,
+ SuspendCheckData { descr_pre, ..data },
+ )
+ }
+ ty::Adt(def, _) => check_must_not_suspend_def(tcx, def.did(), hir_id, data),
+ // FIXME: support adding the attribute to TAITs
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
+ let mut has_emitted = false;
+ for &(predicate, _) in tcx.explicit_item_bounds(def) {
+ // We only look at the `DefId`, so it is safe to skip the binder here.
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(ref poly_trait_predicate)) =
+ predicate.kind().skip_binder()
+ {
+ let def_id = poly_trait_predicate.trait_ref.def_id;
+ let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix);
+ if check_must_not_suspend_def(
+ tcx,
+ def_id,
+ hir_id,
+ SuspendCheckData { descr_pre, ..data },
+ ) {
+ has_emitted = true;
+ break;
+ }
+ }
+ }
+ has_emitted
+ }
+ ty::Dynamic(binder, _, _) => {
+ let mut has_emitted = false;
+ for predicate in binder.iter() {
+ if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+ let def_id = trait_ref.def_id;
+ let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post);
+ if check_must_not_suspend_def(
+ tcx,
+ def_id,
+ hir_id,
+ SuspendCheckData { descr_post, ..data },
+ ) {
+ has_emitted = true;
+ break;
+ }
+ }
+ }
+ has_emitted
+ }
+ ty::Tuple(fields) => {
+ let mut has_emitted = false;
+ for (i, ty) in fields.iter().enumerate() {
+ let descr_post = &format!(" in tuple element {i}");
+ if check_must_not_suspend_ty(
+ tcx,
+ ty,
+ hir_id,
+ param_env,
+ SuspendCheckData { descr_post, ..data },
+ ) {
+ has_emitted = true;
+ }
+ }
+ has_emitted
+ }
+ ty::Array(ty, len) => {
+ let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix);
+ check_must_not_suspend_ty(
+ tcx,
+ ty,
+ hir_id,
+ param_env,
+ SuspendCheckData {
+ descr_pre,
+ plural_len: len.try_eval_usize(tcx, param_env).unwrap_or(0) as usize + 1,
+ ..data
+ },
+ )
+ }
+ // If drop tracking is enabled, we want to look through references, since the referrent
+ // may not be considered live across the await point.
+ ty::Ref(_region, ty, _mutability) => {
+ let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix);
+ check_must_not_suspend_ty(
+ tcx,
+ ty,
+ hir_id,
+ param_env,
+ SuspendCheckData { descr_pre, ..data },
+ )
+ }
+ _ => false,
+ }
+}
+
+fn check_must_not_suspend_def(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+ hir_id: hir::HirId,
+ data: SuspendCheckData<'_>,
+) -> bool {
+ if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) {
+ let msg = format!(
+ "{}`{}`{} held across a suspend point, but should not be",
+ data.descr_pre,
+ tcx.def_path_str(def_id),
+ data.descr_post,
+ );
+ tcx.struct_span_lint_hir(
+ rustc_session::lint::builtin::MUST_NOT_SUSPEND,
+ hir_id,
+ data.source_span,
+ msg,
+ |lint| {
+ // add span pointing to the offending yield/await
+ lint.span_label(data.yield_span, "the value is held across this suspend point");
+
+ // Add optional reason note
+ if let Some(note) = attr.value_str() {
+ // FIXME(guswynn): consider formatting this better
+ lint.span_note(data.source_span, note.as_str());
+ }
+
+ // Add some quick suggestions on what to do
+ // FIXME: can `drop` work as a suggestion here as well?
+ lint.span_help(
+ data.source_span,
+ "consider using a block (`{ ... }`) \
+ to shrink the value's scope, ending before the suspend point",
+ )
+ },
+ );
+
+ true
+ } else {
+ false
+ }
+}
return None;
}
- let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, substs);
+ let fn_sig = self.tcx.fn_sig(def_id).subst(self.tcx, substs);
let source_info = SourceInfo { span: fn_span, ..terminator.source_info };
return Some(CallSite { callee, fn_sig, block: bb, target, source_info });
destination
};
+ // Always create a local to hold the destination, as `RETURN_PLACE` may appear
+ // where a full `Place` is not allowed.
+ let (remap_destination, destination_local) = if let Some(d) = dest.as_local() {
+ (false, d)
+ } else {
+ (
+ true,
+ self.new_call_temp(
+ caller_body,
+ &callsite,
+ destination.ty(caller_body, self.tcx).ty,
+ ),
+ )
+ };
+
// Copy the arguments if needed.
let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body);
new_locals: Local::new(caller_body.local_decls.len())..,
new_scopes: SourceScope::new(caller_body.source_scopes.len())..,
new_blocks: BasicBlock::new(caller_body.basic_blocks.len())..,
- destination: dest,
+ destination: destination_local,
callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(),
callsite,
cleanup_block: cleanup,
// To avoid repeated O(n) insert, push any new statements to the end and rotate
// the slice once.
let mut n = 0;
+ if remap_destination {
+ caller_body[block].statements.push(Statement {
+ source_info: callsite.source_info,
+ kind: StatementKind::Assign(Box::new((
+ dest,
+ Rvalue::Use(Operand::Move(destination_local.into())),
+ ))),
+ });
+ n += 1;
+ }
for local in callee_body.vars_and_temps_iter().rev() {
if !callee_body.local_decls[local].internal
&& integrator.always_live_locals.contains(local)
return;
};
- let Some(&f_ty) = layout.field_tys.get(local) else {
+ let Some(f_ty) = layout.field_tys.get(local) else {
self.validation = Err("malformed MIR");
return;
};
- f_ty
+ f_ty.ty
} else {
let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
self.validation = Err("malformed MIR");
new_locals: RangeFrom<Local>,
new_scopes: RangeFrom<SourceScope>,
new_blocks: RangeFrom<BasicBlock>,
- destination: Place<'tcx>,
+ destination: Local,
callsite_scope: SourceScopeData<'tcx>,
callsite: &'a CallSite<'tcx>,
cleanup_block: Option<BasicBlock>,
impl Integrator<'_, '_> {
fn map_local(&self, local: Local) -> Local {
let new = if local == RETURN_PLACE {
- self.destination.local
+ self.destination
} else {
let idx = local.index() - 1;
if idx < self.args.len() {
*span = span.fresh_expansion(self.expn_data);
}
- fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- for elem in place.projection {
- // FIXME: Make sure that return place is not used in an indexing projection, since it
- // won't be rebased as it is supposed to be.
- assert_ne!(ProjectionElem::Index(RETURN_PLACE), elem);
- }
-
- // If this is the `RETURN_PLACE`, we need to rebase any projections onto it.
- let dest_proj_len = self.destination.projection.len();
- if place.local == RETURN_PLACE && dest_proj_len > 0 {
- let mut projs = Vec::with_capacity(dest_proj_len + place.projection.len());
- projs.extend(self.destination.projection);
- projs.extend(place.projection);
-
- place.projection = self.tcx.intern_place_elems(&*projs);
- }
- // Handles integrating any locals that occur in the base
- // or projections
- self.super_place(place, context, location)
- }
-
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
self.in_cleanup_block = data.is_cleanup;
self.super_basic_block_data(block, data);
BinOp, Body, Constant, ConstantKind, LocalDecls, Operand, Place, ProjectionElem, Rvalue,
SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp,
};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, layout::TyAndLayout, ParamEnv, ParamEnvAnd, SubstsRef, Ty, TyCtxt};
+use rustc_span::symbol::{sym, Symbol};
pub struct InstCombine;
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let ctx = InstCombineContext { tcx, local_decls: &body.local_decls };
+ let ctx = InstCombineContext {
+ tcx,
+ local_decls: &body.local_decls,
+ param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()),
+ };
for block in body.basic_blocks.as_mut() {
for statement in block.statements.iter_mut() {
match statement.kind {
&mut block.terminator.as_mut().unwrap(),
&mut block.statements,
);
+ ctx.combine_intrinsic_assert(
+ &mut block.terminator.as_mut().unwrap(),
+ &mut block.statements,
+ );
}
}
}
struct InstCombineContext<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
+ param_env: ParamEnv<'tcx>,
}
impl<'tcx> InstCombineContext<'tcx, '_> {
});
terminator.kind = TerminatorKind::Goto { target: destination_block };
}
+
+ fn combine_intrinsic_assert(
+ &self,
+ terminator: &mut Terminator<'tcx>,
+ _statements: &mut Vec<Statement<'tcx>>,
+ ) {
+ let TerminatorKind::Call { func, target, .. } = &mut terminator.kind else { return; };
+ let Some(target_block) = target else { return; };
+ let func_ty = func.ty(self.local_decls, self.tcx);
+ let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(self.tcx, func_ty) else {
+ return;
+ };
+ // The intrinsics we are interested in have one generic parameter
+ if substs.is_empty() {
+ return;
+ }
+ let ty = substs.type_at(0);
+
+ // Check this is a foldable intrinsic before we query the layout of our generic parameter
+ let Some(assert_panics) = intrinsic_assert_panics(intrinsic_name) else { return; };
+ let Ok(layout) = self.tcx.layout_of(self.param_env.and(ty)) else { return; };
+ if assert_panics(self.tcx, self.param_env.and(layout)) {
+ // If we know the assert panics, indicate to later opts that the call diverges
+ *target = None;
+ } else {
+ // If we know the assert does not panic, turn the call into a Goto
+ terminator.kind = TerminatorKind::Goto { target: *target_block };
+ }
+ }
+}
+
+fn intrinsic_assert_panics<'tcx>(
+ intrinsic_name: Symbol,
+) -> Option<fn(TyCtxt<'tcx>, ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool> {
+ fn inhabited_predicate<'tcx>(
+ _tcx: TyCtxt<'tcx>,
+ param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
+ ) -> bool {
+ let (_param_env, layout) = param_env_and_layout.into_parts();
+ layout.abi.is_uninhabited()
+ }
+ fn zero_valid_predicate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
+ ) -> bool {
+ !tcx.permits_zero_init(param_env_and_layout)
+ }
+ fn mem_uninitialized_valid_predicate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
+ ) -> bool {
+ !tcx.permits_uninit_init(param_env_and_layout)
+ }
+
+ match intrinsic_name {
+ sym::assert_inhabited => Some(inhabited_predicate),
+ sym::assert_zero_valid => Some(zero_valid_predicate),
+ sym::assert_mem_uninitialized_valid => Some(mem_uninitialized_valid_predicate),
+ _ => None,
+ }
+}
+
+fn resolve_rust_intrinsic<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ func_ty: Ty<'tcx>,
+) -> Option<(Symbol, SubstsRef<'tcx>)> {
+ if let ty::FnDef(def_id, substs) = *func_ty.kind() {
+ if tcx.is_intrinsic(def_id) {
+ return Some((tcx.item_name(def_id), substs));
+ }
+ }
+ None
}
mod const_goto;
mod const_prop;
mod const_prop_lint;
+mod copy_prop;
mod coverage;
+mod ctfe_limit;
mod dataflow_const_prop;
mod dead_store_elimination;
mod deaggregator;
mod reveal_all;
mod separate_const_switch;
mod shim;
+mod ssa;
// This pass is public to allow external drivers to perform MIR cleanup
pub mod simplify;
mod simplify_branches;
mod simplify_comparison_integral;
-mod simplify_try;
mod sroa;
mod uninhabited_enum_branching;
mod unreachable_prop;
mir_drops_elaborated_and_const_checked,
mir_for_ctfe,
mir_for_ctfe_of_const_arg,
+ mir_generator_witnesses: generator::mir_generator_witnesses,
optimized_mir,
is_mir_available,
is_ctfe_mir_available: |tcx, did| is_mir_available(tcx, did),
}
}
+ pm::run_passes(tcx, &mut body, &[&ctfe_limit::CtfeLimit], None);
+
debug_assert!(!body.has_free_regions(), "Free regions in MIR for CTFE");
body
return tcx.mir_drops_elaborated_and_const_checked(def);
}
+ if tcx.generator_kind(def.did).is_some() && tcx.sess.opts.unstable_opts.drop_tracking_mir {
+ tcx.ensure().mir_generator_witnesses(def.did);
+ }
let mir_borrowck = tcx.mir_borrowck_opt_const_arg(def);
let is_fn_like = tcx.def_kind(def.did).is_fn_like();
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
&cleanup_post_borrowck::CleanupPostBorrowck,
- &simplify_branches::SimplifyConstCondition::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("early-opt"),
&deref_separator::Derefer,
&instcombine::InstCombine,
&separate_const_switch::SeparateConstSwitch,
&simplify::SimplifyLocals::new("before-const-prop"),
+ ©_prop::CopyProp,
//
// FIXME(#70073): This pass is responsible for both optimization as well as some lints.
&const_prop::ConstProp,
&o1(simplify_branches::SimplifyConstCondition::new("after-const-prop")),
&early_otherwise_branch::EarlyOtherwiseBranch,
&simplify_comparison_integral::SimplifyComparisonIntegral,
- &simplify_try::SimplifyArmIdentity,
- &simplify_try::SimplifyBranchSame,
&dead_store_elimination::DeadStoreElimination,
&dest_prop::DestinationPropagation,
&o1(simplify_branches::SimplifyConstCondition::new("final")),
| StatementKind::StorageDead(_)
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {
// These are all noops in a landing pad
}
| StatementKind::Coverage(_)
| StatementKind::StorageDead(_)
| StatementKind::Intrinsic(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
}
}
| StatementKind::AscribeUserType(_, _)
| StatementKind::Coverage(_)
| StatementKind::Intrinsic(_)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => {}
// If the discriminant is set, it is always set
} else {
InternalSubsts::identity_for_item(tcx, def_id)
};
- let sig = tcx.bound_fn_sig(def_id).subst(tcx, substs);
+ let sig = tcx.fn_sig(def_id).subst(tcx, substs);
let sig = tcx.erase_late_bound_regions(sig);
let span = tcx.def_span(def_id);
// we must subst the self_ty because it's
// otherwise going to be TySelf and we can't index
// or access fields of a Place of type TySelf.
- let sig = tcx.bound_fn_sig(def_id).subst(tcx, &[self_ty.into()]);
+ let sig = tcx.fn_sig(def_id).subst(tcx, &[self_ty.into()]);
let sig = tcx.erase_late_bound_regions(sig);
let span = tcx.def_span(def_id);
};
let def_id = instance.def_id();
- let sig = tcx.bound_fn_sig(def_id);
+ let sig = tcx.fn_sig(def_id);
let sig = sig.map_bound(|sig| tcx.erase_late_bound_regions(sig));
assert_eq!(sig_substs.is_some(), !instance.has_polymorphic_mir_body());
let param_env = tcx.param_env(ctor_id);
// Normalize the sig.
- let sig = tcx.fn_sig(ctor_id).no_bound_vars().expect("LBR in ADT constructor signature");
+ let sig = tcx
+ .fn_sig(ctor_id)
+ .subst_identity()
+ .no_bound_vars()
+ .expect("LBR in ADT constructor signature");
let sig = tcx.normalize_erasing_regions(param_env, sig);
let ty::Adt(adt_def, substs) = sig.output().kind() else {
}
}
+pub fn remove_unused_definitions<'tcx>(body: &mut Body<'tcx>) {
+ // First, we're going to get a count of *actual* uses for every `Local`.
+ let mut used_locals = UsedLocals::new(body);
+
+ // Next, we're going to remove any `Local` with zero actual uses. When we remove those
+ // `Locals`, we're also going to subtract any uses of other `Locals` from the `used_locals`
+ // count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
+ // `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
+ // fixedpoint where there are no more unused locals.
+ remove_unused_definitions_helper(&mut used_locals, body);
+}
+
pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) {
// First, we're going to get a count of *actual* uses for every `Local`.
let mut used_locals = UsedLocals::new(body);
// count. For example, if we removed `_2 = discriminant(_1)`, then we'll subtract one from
// `use_counts[_1]`. That in turn might make `_1` unused, so we loop until we hit a
// fixedpoint where there are no more unused locals.
- remove_unused_definitions(&mut used_locals, body);
+ remove_unused_definitions_helper(&mut used_locals, body);
// Finally, we'll actually do the work of shrinking `body.local_decls` and remapping the `Local`s.
let map = make_local_map(&mut body.local_decls, &used_locals);
self.super_statement(statement, location);
}
- StatementKind::Nop => {}
+ StatementKind::ConstEvalCounter | StatementKind::Nop => {}
StatementKind::StorageLive(_local) | StatementKind::StorageDead(_local) => {}
}
/// Removes unused definitions. Updates the used locals to reflect the changes made.
-fn remove_unused_definitions(used_locals: &mut UsedLocals, body: &mut Body<'_>) {
+fn remove_unused_definitions_helper(used_locals: &mut UsedLocals, body: &mut Body<'_>) {
// The use counts are updated as we remove the statements. A local might become unused
// during the retain operation, leading to a temporary inconsistency (storage statements or
// definitions referencing the local might remain). For correctness it is crucial that this
+++ /dev/null
-//! The general point of the optimizations provided here is to simplify something like:
-//!
-//! ```rust
-//! # fn foo<T, E>(x: Result<T, E>) -> Result<T, E> {
-//! match x {
-//! Ok(x) => Ok(x),
-//! Err(x) => Err(x)
-//! }
-//! # }
-//! ```
-//!
-//! into just `x`.
-
-use crate::{simplify, MirPass};
-use itertools::Itertools as _;
-use rustc_index::{bit_set::BitSet, vec::IndexVec};
-use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::*;
-use rustc_middle::ty::{self, List, Ty, TyCtxt};
-use rustc_target::abi::VariantIdx;
-use std::iter::{once, Enumerate, Peekable};
-use std::slice::Iter;
-
-/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
-///
-/// This is done by transforming basic blocks where the statements match:
-///
-/// ```ignore (MIR)
-/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY );
-/// _TMP_2 = _LOCAL_TMP;
-/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2;
-/// discriminant(_LOCAL_0) = VAR_IDX;
-/// ```
-///
-/// into:
-///
-/// ```ignore (MIR)
-/// _LOCAL_0 = move _LOCAL_1
-/// ```
-pub struct SimplifyArmIdentity;
-
-#[derive(Debug)]
-struct ArmIdentityInfo<'tcx> {
- /// Storage location for the variant's field
- local_temp_0: Local,
- /// Storage location holding the variant being read from
- local_1: Local,
- /// The variant field being read from
- vf_s0: VarField<'tcx>,
- /// Index of the statement which loads the variant being read
- get_variant_field_stmt: usize,
-
- /// Tracks each assignment to a temporary of the variant's field
- field_tmp_assignments: Vec<(Local, Local)>,
-
- /// Storage location holding the variant's field that was read from
- local_tmp_s1: Local,
- /// Storage location holding the enum that we are writing to
- local_0: Local,
- /// The variant field being written to
- vf_s1: VarField<'tcx>,
-
- /// Storage location that the discriminant is being written to
- set_discr_local: Local,
- /// The variant being written
- set_discr_var_idx: VariantIdx,
-
- /// Index of the statement that should be overwritten as a move
- stmt_to_overwrite: usize,
- /// SourceInfo for the new move
- source_info: SourceInfo,
-
- /// Indices of matching Storage{Live,Dead} statements encountered.
- /// (StorageLive index,, StorageDead index, Local)
- storage_stmts: Vec<(usize, usize, Local)>,
-
- /// The statements that should be removed (turned into nops)
- stmts_to_remove: Vec<usize>,
-
- /// Indices of debug variables that need to be adjusted to point to
- // `{local_0}.{dbg_projection}`.
- dbg_info_to_adjust: Vec<usize>,
-
- /// The projection used to rewrite debug info.
- dbg_projection: &'tcx List<PlaceElem<'tcx>>,
-}
-
-fn get_arm_identity_info<'a, 'tcx>(
- stmts: &'a [Statement<'tcx>],
- locals_count: usize,
- debug_info: &'a [VarDebugInfo<'tcx>],
-) -> Option<ArmIdentityInfo<'tcx>> {
- // This can't possibly match unless there are at least 3 statements in the block
- // so fail fast on tiny blocks.
- if stmts.len() < 3 {
- return None;
- }
-
- let mut tmp_assigns = Vec::new();
- let mut nop_stmts = Vec::new();
- let mut storage_stmts = Vec::new();
- let mut storage_live_stmts = Vec::new();
- let mut storage_dead_stmts = Vec::new();
-
- type StmtIter<'a, 'tcx> = Peekable<Enumerate<Iter<'a, Statement<'tcx>>>>;
-
- fn is_storage_stmt(stmt: &Statement<'_>) -> bool {
- matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_))
- }
-
- /// Eats consecutive Statements which match `test`, performing the specified `action` for each.
- /// The iterator `stmt_iter` is not advanced if none were matched.
- fn try_eat<'a, 'tcx>(
- stmt_iter: &mut StmtIter<'a, 'tcx>,
- test: impl Fn(&'a Statement<'tcx>) -> bool,
- mut action: impl FnMut(usize, &'a Statement<'tcx>),
- ) {
- while stmt_iter.peek().map_or(false, |(_, stmt)| test(stmt)) {
- let (idx, stmt) = stmt_iter.next().unwrap();
-
- action(idx, stmt);
- }
- }
-
- /// Eats consecutive `StorageLive` and `StorageDead` Statements.
- /// The iterator `stmt_iter` is not advanced if none were found.
- fn try_eat_storage_stmts(
- stmt_iter: &mut StmtIter<'_, '_>,
- storage_live_stmts: &mut Vec<(usize, Local)>,
- storage_dead_stmts: &mut Vec<(usize, Local)>,
- ) {
- try_eat(stmt_iter, is_storage_stmt, |idx, stmt| {
- if let StatementKind::StorageLive(l) = stmt.kind {
- storage_live_stmts.push((idx, l));
- } else if let StatementKind::StorageDead(l) = stmt.kind {
- storage_dead_stmts.push((idx, l));
- }
- })
- }
-
- fn is_tmp_storage_stmt(stmt: &Statement<'_>) -> bool {
- use rustc_middle::mir::StatementKind::Assign;
- if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind {
- place.as_local().is_some() && p.as_local().is_some()
- } else {
- false
- }
- }
-
- /// Eats consecutive `Assign` Statements.
- // The iterator `stmt_iter` is not advanced if none were found.
- fn try_eat_assign_tmp_stmts(
- stmt_iter: &mut StmtIter<'_, '_>,
- tmp_assigns: &mut Vec<(Local, Local)>,
- nop_stmts: &mut Vec<usize>,
- ) {
- try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| {
- use rustc_middle::mir::StatementKind::Assign;
- if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) =
- &stmt.kind
- {
- tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap()));
- nop_stmts.push(idx);
- }
- })
- }
-
- fn find_storage_live_dead_stmts_for_local(
- local: Local,
- stmts: &[Statement<'_>],
- ) -> Option<(usize, usize)> {
- trace!("looking for {:?}", local);
- let mut storage_live_stmt = None;
- let mut storage_dead_stmt = None;
- for (idx, stmt) in stmts.iter().enumerate() {
- if stmt.kind == StatementKind::StorageLive(local) {
- storage_live_stmt = Some(idx);
- } else if stmt.kind == StatementKind::StorageDead(local) {
- storage_dead_stmt = Some(idx);
- }
- }
-
- Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX)))
- }
-
- // Try to match the expected MIR structure with the basic block we're processing.
- // We want to see something that looks like:
- // ```
- // (StorageLive(_) | StorageDead(_));*
- // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
- // (StorageLive(_) | StorageDead(_));*
- // (tmp_n+1 = tmp_n);*
- // (StorageLive(_) | StorageDead(_));*
- // (tmp_n+1 = tmp_n);*
- // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp;
- // discriminant(LOCAL_FROM) = VariantIdx;
- // (StorageLive(_) | StorageDead(_));*
- // ```
- let mut stmt_iter = stmts.iter().enumerate().peekable();
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- let (get_variant_field_stmt, stmt) = stmt_iter.next()?;
- let (local_tmp_s0, local_1, vf_s0, dbg_projection) = match_get_variant_field(stmt)?;
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts);
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts);
-
- let (idx, stmt) = stmt_iter.next()?;
- let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?;
- nop_stmts.push(idx);
-
- let (idx, stmt) = stmt_iter.next()?;
- let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?;
- let discr_stmt_source_info = stmt.source_info;
- nop_stmts.push(idx);
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- for (live_idx, live_local) in storage_live_stmts {
- if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) {
- let (dead_idx, _) = storage_dead_stmts.swap_remove(i);
- storage_stmts.push((live_idx, dead_idx, live_local));
-
- if live_local == local_tmp_s0 {
- nop_stmts.push(get_variant_field_stmt);
- }
- }
- }
- // We sort primitive usize here so we can use unstable sort
- nop_stmts.sort_unstable();
-
- // Use one of the statements we're going to discard between the point
- // where the storage location for the variant field becomes live and
- // is killed.
- let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?;
- let stmt_to_overwrite =
- nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx);
-
- let mut tmp_assigned_vars = BitSet::new_empty(locals_count);
- for (l, r) in &tmp_assigns {
- tmp_assigned_vars.insert(*l);
- tmp_assigned_vars.insert(*r);
- }
-
- let dbg_info_to_adjust: Vec<_> = debug_info
- .iter()
- .enumerate()
- .filter_map(|(i, var_info)| {
- if let VarDebugInfoContents::Place(p) = var_info.value {
- if tmp_assigned_vars.contains(p.local) {
- return Some(i);
- }
- }
-
- None
- })
- .collect();
-
- Some(ArmIdentityInfo {
- local_temp_0: local_tmp_s0,
- local_1,
- vf_s0,
- get_variant_field_stmt,
- field_tmp_assignments: tmp_assigns,
- local_tmp_s1,
- local_0,
- vf_s1,
- set_discr_local,
- set_discr_var_idx,
- stmt_to_overwrite: *stmt_to_overwrite?,
- source_info: discr_stmt_source_info,
- storage_stmts,
- stmts_to_remove: nop_stmts,
- dbg_info_to_adjust,
- dbg_projection,
- })
-}
-
-fn optimization_applies<'tcx>(
- opt_info: &ArmIdentityInfo<'tcx>,
- local_decls: &IndexVec<Local, LocalDecl<'tcx>>,
- local_uses: &IndexVec<Local, usize>,
- var_debug_info: &[VarDebugInfo<'tcx>],
-) -> bool {
- trace!("testing if optimization applies...");
-
- // FIXME(wesleywiser): possibly relax this restriction?
- if opt_info.local_0 == opt_info.local_1 {
- trace!("NO: moving into ourselves");
- return false;
- } else if opt_info.vf_s0 != opt_info.vf_s1 {
- trace!("NO: the field-and-variant information do not match");
- return false;
- } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty {
- // FIXME(Centril,oli-obk): possibly relax to same layout?
- trace!("NO: source and target locals have different types");
- return false;
- } else if (opt_info.local_0, opt_info.vf_s0.var_idx)
- != (opt_info.set_discr_local, opt_info.set_discr_var_idx)
- {
- trace!("NO: the discriminants do not match");
- return false;
- }
-
- // Verify the assignment chain consists of the form b = a; c = b; d = c; etc...
- if opt_info.field_tmp_assignments.is_empty() {
- trace!("NO: no assignments found");
- return false;
- }
- let mut last_assigned_to = opt_info.field_tmp_assignments[0].1;
- let source_local = last_assigned_to;
- for (l, r) in &opt_info.field_tmp_assignments {
- if *r != last_assigned_to {
- trace!("NO: found unexpected assignment {:?} = {:?}", l, r);
- return false;
- }
-
- last_assigned_to = *l;
- }
-
- // Check that the first and last used locals are only used twice
- // since they are of the form:
- //
- // ```
- // _first = ((_x as Variant).n: ty);
- // _n = _first;
- // ...
- // ((_y as Variant).n: ty) = _n;
- // discriminant(_y) = z;
- // ```
- for (l, r) in &opt_info.field_tmp_assignments {
- if local_uses[*l] != 2 {
- warn!("NO: FAILED assignment chain local {:?} was used more than twice", l);
- return false;
- } else if local_uses[*r] != 2 {
- warn!("NO: FAILED assignment chain local {:?} was used more than twice", r);
- return false;
- }
- }
-
- // Check that debug info only points to full Locals and not projections.
- for dbg_idx in &opt_info.dbg_info_to_adjust {
- let dbg_info = &var_debug_info[*dbg_idx];
- if let VarDebugInfoContents::Place(p) = dbg_info.value {
- if !p.projection.is_empty() {
- trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, p);
- return false;
- }
- }
- }
-
- if source_local != opt_info.local_temp_0 {
- trace!(
- "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}",
- source_local,
- opt_info.local_temp_0
- );
- return false;
- } else if last_assigned_to != opt_info.local_tmp_s1 {
- trace!(
- "NO: end of assignment chain does not match written enum temp: {:?} != {:?}",
- last_assigned_to,
- opt_info.local_tmp_s1
- );
- return false;
- }
-
- trace!("SUCCESS: optimization applies!");
- true
-}
-
-impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- // FIXME(77359): This optimization can result in unsoundness.
- if !tcx.sess.opts.unstable_opts.unsound_mir_opts {
- return;
- }
-
- let source = body.source;
- trace!("running SimplifyArmIdentity on {:?}", source);
-
- let local_uses = LocalUseCounter::get_local_uses(body);
- for bb in body.basic_blocks.as_mut() {
- if let Some(opt_info) =
- get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info)
- {
- trace!("got opt_info = {:#?}", opt_info);
- if !optimization_applies(
- &opt_info,
- &body.local_decls,
- &local_uses,
- &body.var_debug_info,
- ) {
- debug!("optimization skipped for {:?}", source);
- continue;
- }
-
- // Also remove unused Storage{Live,Dead} statements which correspond
- // to temps used previously.
- for (live_idx, dead_idx, local) in &opt_info.storage_stmts {
- // The temporary that we've read the variant field into is scoped to this block,
- // so we can remove the assignment.
- if *local == opt_info.local_temp_0 {
- bb.statements[opt_info.get_variant_field_stmt].make_nop();
- }
-
- for (left, right) in &opt_info.field_tmp_assignments {
- if local == left || local == right {
- bb.statements[*live_idx].make_nop();
- bb.statements[*dead_idx].make_nop();
- }
- }
- }
-
- // Right shape; transform
- for stmt_idx in opt_info.stmts_to_remove {
- bb.statements[stmt_idx].make_nop();
- }
-
- let stmt = &mut bb.statements[opt_info.stmt_to_overwrite];
- stmt.source_info = opt_info.source_info;
- stmt.kind = StatementKind::Assign(Box::new((
- opt_info.local_0.into(),
- Rvalue::Use(Operand::Move(opt_info.local_1.into())),
- )));
-
- bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop);
-
- // Fix the debug info to point to the right local
- for dbg_index in opt_info.dbg_info_to_adjust {
- let dbg_info = &mut body.var_debug_info[dbg_index];
- assert!(
- matches!(dbg_info.value, VarDebugInfoContents::Place(_)),
- "value was not a Place"
- );
- if let VarDebugInfoContents::Place(p) = &mut dbg_info.value {
- assert!(p.projection.is_empty());
- p.local = opt_info.local_0;
- p.projection = opt_info.dbg_projection;
- }
- }
-
- trace!("block is now {:?}", bb.statements);
- }
- }
- }
-}
-
-struct LocalUseCounter {
- local_uses: IndexVec<Local, usize>,
-}
-
-impl LocalUseCounter {
- fn get_local_uses(body: &Body<'_>) -> IndexVec<Local, usize> {
- let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) };
- counter.visit_body(body);
- counter.local_uses
- }
-}
-
-impl Visitor<'_> for LocalUseCounter {
- fn visit_local(&mut self, local: Local, context: PlaceContext, _location: Location) {
- if context.is_storage_marker()
- || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo)
- {
- return;
- }
-
- self.local_uses[local] += 1;
- }
-}
-
-/// Match on:
-/// ```ignore (MIR)
-/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
-/// ```
-fn match_get_variant_field<'tcx>(
- stmt: &Statement<'tcx>,
-) -> Option<(Local, Local, VarField<'tcx>, &'tcx List<PlaceElem<'tcx>>)> {
- match &stmt.kind {
- StatementKind::Assign(box (
- place_into,
- Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)),
- )) => {
- let local_into = place_into.as_local()?;
- let (local_from, vf) = match_variant_field_place(*pf)?;
- Some((local_into, local_from, vf, pf.projection))
- }
- _ => None,
- }
-}
-
-/// Match on:
-/// ```ignore (MIR)
-/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO;
-/// ```
-fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
- match &stmt.kind {
- StatementKind::Assign(box (place_from, Rvalue::Use(Operand::Move(place_into)))) => {
- let local_into = place_into.as_local()?;
- let (local_from, vf) = match_variant_field_place(*place_from)?;
- Some((local_into, local_from, vf))
- }
- _ => None,
- }
-}
-
-/// Match on:
-/// ```ignore (MIR)
-/// discriminant(_LOCAL_TO_SET) = VAR_IDX;
-/// ```
-fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> {
- match &stmt.kind {
- StatementKind::SetDiscriminant { place, variant_index } => {
- Some((place.as_local()?, *variant_index))
- }
- _ => None,
- }
-}
-
-#[derive(PartialEq, Debug)]
-struct VarField<'tcx> {
- field: Field,
- field_ty: Ty<'tcx>,
- var_idx: VariantIdx,
-}
-
-/// Match on `((_LOCAL as Variant).FIELD: TY)`.
-fn match_variant_field_place(place: Place<'_>) -> Option<(Local, VarField<'_>)> {
- match place.as_ref() {
- PlaceRef {
- local,
- projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)],
- } => Some((local, VarField { field, field_ty: ty, var_idx })),
- _ => None,
- }
-}
-
-/// Simplifies `SwitchInt(_) -> [targets]`,
-/// where all the `targets` have the same form,
-/// into `goto -> target_first`.
-pub struct SimplifyBranchSame;
-
-impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- // This optimization is disabled by default for now due to
- // soundness concerns; see issue #89485 and PR #89489.
- if !tcx.sess.opts.unstable_opts.unsound_mir_opts {
- return;
- }
-
- trace!("Running SimplifyBranchSame on {:?}", body.source);
- let finder = SimplifyBranchSameOptimizationFinder { body, tcx };
- let opts = finder.find();
-
- let did_remove_blocks = opts.len() > 0;
- for opt in opts.iter() {
- trace!("SUCCESS: Applying optimization {:?}", opt);
- // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`.
- body.basic_blocks_mut()[opt.bb_to_opt_terminator].terminator_mut().kind =
- TerminatorKind::Goto { target: opt.bb_to_goto };
- }
-
- if did_remove_blocks {
- // We have dead blocks now, so remove those.
- simplify::remove_dead_blocks(tcx, body);
- }
- }
-}
-
-#[derive(Debug)]
-struct SimplifyBranchSameOptimization {
- /// All basic blocks are equal so go to this one
- bb_to_goto: BasicBlock,
- /// Basic block where the terminator can be simplified to a goto
- bb_to_opt_terminator: BasicBlock,
-}
-
-struct SwitchTargetAndValue {
- target: BasicBlock,
- // None in case of the `otherwise` case
- value: Option<u128>,
-}
-
-struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
- body: &'a Body<'tcx>,
- tcx: TyCtxt<'tcx>,
-}
-
-impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> {
- fn find(&self) -> Vec<SimplifyBranchSameOptimization> {
- self.body
- .basic_blocks
- .iter_enumerated()
- .filter_map(|(bb_idx, bb)| {
- let (discr_switched_on, targets_and_values) = match &bb.terminator().kind {
- TerminatorKind::SwitchInt { targets, discr, .. } => {
- let targets_and_values: Vec<_> = targets.iter()
- .map(|(val, target)| SwitchTargetAndValue { target, value: Some(val) })
- .chain(once(SwitchTargetAndValue { target: targets.otherwise(), value: None }))
- .collect();
- (discr, targets_and_values)
- },
- _ => return None,
- };
-
- // find the adt that has its discriminant read
- // assuming this must be the last statement of the block
- let adt_matched_on = match &bb.statements.last()?.kind {
- StatementKind::Assign(box (place, rhs))
- if Some(*place) == discr_switched_on.place() =>
- {
- match rhs {
- Rvalue::Discriminant(adt_place) if adt_place.ty(self.body, self.tcx).ty.is_enum() => adt_place,
- _ => {
- trace!("NO: expected a discriminant read of an enum instead of: {:?}", rhs);
- return None;
- }
- }
- }
- other => {
- trace!("NO: expected an assignment of a discriminant read to a place. Found: {:?}", other);
- return None
- },
- };
-
- let mut iter_bbs_reachable = targets_and_values
- .iter()
- .map(|target_and_value| (target_and_value, &self.body.basic_blocks[target_and_value.target]))
- .filter(|(_, bb)| {
- // Reaching `unreachable` is UB so assume it doesn't happen.
- bb.terminator().kind != TerminatorKind::Unreachable
- })
- .peekable();
-
- let bb_first = iter_bbs_reachable.peek().map_or(&targets_and_values[0], |(idx, _)| *idx);
- let mut all_successors_equivalent = StatementEquality::TrivialEqual;
-
- // All successor basic blocks must be equal or contain statements that are pairwise considered equal.
- for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() {
- let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup
- && bb_l.terminator().kind == bb_r.terminator().kind
- && bb_l.statements.len() == bb_r.statements.len();
- let statement_check = || {
- bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| {
- let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r);
- if matches!(stmt_equality, StatementEquality::NotEqual) {
- // short circuit
- None
- } else {
- Some(acc.combine(&stmt_equality))
- }
- })
- .unwrap_or(StatementEquality::NotEqual)
- };
- if !trivial_checks {
- all_successors_equivalent = StatementEquality::NotEqual;
- break;
- }
- all_successors_equivalent = all_successors_equivalent.combine(&statement_check());
- };
-
- match all_successors_equivalent{
- StatementEquality::TrivialEqual => {
- // statements are trivially equal, so just take first
- trace!("Statements are trivially equal");
- Some(SimplifyBranchSameOptimization {
- bb_to_goto: bb_first.target,
- bb_to_opt_terminator: bb_idx,
- })
- }
- StatementEquality::ConsideredEqual(bb_to_choose) => {
- trace!("Statements are considered equal");
- Some(SimplifyBranchSameOptimization {
- bb_to_goto: bb_to_choose,
- bb_to_opt_terminator: bb_idx,
- })
- }
- StatementEquality::NotEqual => {
- trace!("NO: not all successors of basic block {:?} were equivalent", bb_idx);
- None
- }
- }
- })
- .collect()
- }
-
- /// Tests if two statements can be considered equal
- ///
- /// Statements can be trivially equal if the kinds match.
- /// But they can also be considered equal in the following case A:
- /// ```ignore (MIR)
- /// discriminant(_0) = 0; // bb1
- /// _0 = move _1; // bb2
- /// ```
- /// In this case the two statements are equal iff
- /// - `_0` is an enum where the variant index 0 is fieldless, and
- /// - bb1 was targeted by a switch where the discriminant of `_1` was switched on
- fn statement_equality(
- &self,
- adt_matched_on: Place<'tcx>,
- x: &Statement<'tcx>,
- x_target_and_value: &SwitchTargetAndValue,
- y: &Statement<'tcx>,
- y_target_and_value: &SwitchTargetAndValue,
- ) -> StatementEquality {
- let helper = |rhs: &Rvalue<'tcx>,
- place: &Place<'tcx>,
- variant_index: VariantIdx,
- switch_value: u128,
- side_to_choose| {
- let place_type = place.ty(self.body, self.tcx).ty;
- let adt = match *place_type.kind() {
- ty::Adt(adt, _) if adt.is_enum() => adt,
- _ => return StatementEquality::NotEqual,
- };
- // We need to make sure that the switch value that targets the bb with
- // SetDiscriminant is the same as the variant discriminant.
- let variant_discr = adt.discriminant_for_variant(self.tcx, variant_index).val;
- if variant_discr != switch_value {
- trace!(
- "NO: variant discriminant {} does not equal switch value {}",
- variant_discr,
- switch_value
- );
- return StatementEquality::NotEqual;
- }
- let variant_is_fieldless = adt.variant(variant_index).fields.is_empty();
- if !variant_is_fieldless {
- trace!("NO: variant {:?} was not fieldless", variant_index);
- return StatementEquality::NotEqual;
- }
-
- match rhs {
- Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => {
- StatementEquality::ConsideredEqual(side_to_choose)
- }
- _ => {
- trace!(
- "NO: RHS of assignment was {:?}, but expected it to match the adt being matched on in the switch, which is {:?}",
- rhs,
- adt_matched_on
- );
- StatementEquality::NotEqual
- }
- }
- };
- match (&x.kind, &y.kind) {
- // trivial case
- (x, y) if x == y => StatementEquality::TrivialEqual,
-
- // check for case A
- (
- StatementKind::Assign(box (_, rhs)),
- &StatementKind::SetDiscriminant { ref place, variant_index },
- ) if y_target_and_value.value.is_some() => {
- // choose basic block of x, as that has the assign
- helper(
- rhs,
- place,
- variant_index,
- y_target_and_value.value.unwrap(),
- x_target_and_value.target,
- )
- }
- (
- &StatementKind::SetDiscriminant { ref place, variant_index },
- &StatementKind::Assign(box (_, ref rhs)),
- ) if x_target_and_value.value.is_some() => {
- // choose basic block of y, as that has the assign
- helper(
- rhs,
- place,
- variant_index,
- x_target_and_value.value.unwrap(),
- y_target_and_value.target,
- )
- }
- _ => {
- trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y);
- StatementEquality::NotEqual
- }
- }
- }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq)]
-enum StatementEquality {
- /// The two statements are trivially equal; same kind
- TrivialEqual,
- /// The two statements are considered equal, but may be of different kinds. The BasicBlock field is the basic block to jump to when performing the branch-same optimization.
- /// For example, `_0 = _1` and `discriminant(_0) = discriminant(0)` are considered equal if 0 is a fieldless variant of an enum. But we don't want to jump to the basic block with the SetDiscriminant, as that is not legal if _1 is not the 0 variant index
- ConsideredEqual(BasicBlock),
- /// The two statements are not equal
- NotEqual,
-}
-
-impl StatementEquality {
- fn combine(&self, other: &StatementEquality) -> StatementEquality {
- use StatementEquality::*;
- match (self, other) {
- (TrivialEqual, TrivialEqual) => TrivialEqual,
- (TrivialEqual, ConsideredEqual(b)) | (ConsideredEqual(b), TrivialEqual) => {
- ConsideredEqual(*b)
- }
- (ConsideredEqual(b1), ConsideredEqual(b2)) => {
- if b1 == b2 {
- ConsideredEqual(*b1)
- } else {
- NotEqual
- }
- }
- (_, NotEqual) | (NotEqual, _) => NotEqual,
- }
- }
-}
replacements: ReplacementMap<'tcx>,
/// This is used to check that we are not leaving references to replaced locals behind.
all_dead_locals: BitSet<Local>,
- /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
+ /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
/// and deinit statement and debuginfo.
fragments: IndexVec<Local, Vec<(&'tcx [PlaceElem<'tcx>], Local)>>,
}
--- /dev/null
+use either::Either;
+use rustc_data_structures::graph::dominators::Dominators;
+use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
+use rustc_middle::middle::resolve_lifetime::Set1;
+use rustc_middle::mir::visit::*;
+use rustc_middle::mir::*;
+use rustc_middle::ty::{ParamEnv, TyCtxt};
+
+#[derive(Debug)]
+pub struct SsaLocals {
+ /// Assignments to each local. This defines whether the local is SSA.
+ assignments: IndexVec<Local, Set1<LocationExtended>>,
+ /// We visit the body in reverse postorder, to ensure each local is assigned before it is used.
+ /// We remember the order in which we saw the assignments to compute the SSA values in a single
+ /// pass.
+ assignment_order: Vec<Local>,
+ /// Copy equivalence classes between locals. See `copy_classes` for documentation.
+ copy_classes: IndexVec<Local, Local>,
+}
+
+impl SsaLocals {
+ pub fn new<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ body: &Body<'tcx>,
+ borrowed_locals: &BitSet<Local>,
+ ) -> SsaLocals {
+ let assignment_order = Vec::new();
+
+ let assignments = IndexVec::from_elem(Set1::Empty, &body.local_decls);
+ let dominators = body.basic_blocks.dominators();
+ let mut visitor = SsaVisitor { assignments, assignment_order, dominators };
+
+ for (local, decl) in body.local_decls.iter_enumerated() {
+ if matches!(body.local_kind(local), LocalKind::Arg) {
+ visitor.assignments[local] = Set1::One(LocationExtended::Arg);
+ }
+ if borrowed_locals.contains(local) && !decl.ty.is_freeze(tcx, param_env) {
+ visitor.assignments[local] = Set1::Many;
+ }
+ }
+
+ for (bb, data) in traversal::reverse_postorder(body) {
+ visitor.visit_basic_block_data(bb, data);
+ }
+
+ for var_debug_info in &body.var_debug_info {
+ visitor.visit_var_debug_info(var_debug_info);
+ }
+
+ debug!(?visitor.assignments);
+
+ visitor
+ .assignment_order
+ .retain(|&local| matches!(visitor.assignments[local], Set1::One(_)));
+ debug!(?visitor.assignment_order);
+
+ let copy_classes = compute_copy_classes(&visitor, body);
+
+ SsaLocals {
+ assignments: visitor.assignments,
+ assignment_order: visitor.assignment_order,
+ copy_classes,
+ }
+ }
+
+ pub fn is_ssa(&self, local: Local) -> bool {
+ matches!(self.assignments[local], Set1::One(_))
+ }
+
+ pub fn assignments<'a, 'tcx>(
+ &'a self,
+ body: &'a Body<'tcx>,
+ ) -> impl Iterator<Item = (Local, &'a Rvalue<'tcx>)> + 'a {
+ self.assignment_order.iter().filter_map(|&local| {
+ if let Set1::One(LocationExtended::Plain(loc)) = self.assignments[local] {
+ // `loc` must point to a direct assignment to `local`.
+ let Either::Left(stmt) = body.stmt_at(loc) else { bug!() };
+ let Some((target, rvalue)) = stmt.kind.as_assign() else { bug!() };
+ assert_eq!(target.as_local(), Some(local));
+ Some((local, rvalue))
+ } else {
+ None
+ }
+ })
+ }
+
+ /// Compute the equivalence classes for locals, based on copy statements.
+ ///
+ /// The returned vector maps each local to the one it copies. In the following case:
+ /// _a = &mut _0
+ /// _b = move? _a
+ /// _c = move? _a
+ /// _d = move? _c
+ /// We return the mapping
+ /// _a => _a // not a copy so, represented by itself
+ /// _b => _a
+ /// _c => _a
+ /// _d => _a // transitively through _c
+ ///
+ /// Exception: we do not see through the return place, as it cannot be substituted.
+ pub fn copy_classes(&self) -> &IndexVec<Local, Local> {
+ &self.copy_classes
+ }
+
+ /// Make a property uniform on a copy equivalence class by removing elements.
+ pub fn meet_copy_equivalence(&self, property: &mut BitSet<Local>) {
+ // Consolidate to have a local iff all its copies are.
+ //
+ // `copy_classes` defines equivalence classes between locals. The `local`s that recursively
+ // move/copy the same local all have the same `head`.
+ for (local, &head) in self.copy_classes.iter_enumerated() {
+ // If any copy does not have `property`, then the head is not.
+ if !property.contains(local) {
+ property.remove(head);
+ }
+ }
+ for (local, &head) in self.copy_classes.iter_enumerated() {
+ // If any copy does not have `property`, then the head doesn't either,
+ // then no copy has `property`.
+ if !property.contains(head) {
+ property.remove(local);
+ }
+ }
+
+ // Verify that we correctly computed equivalence classes.
+ #[cfg(debug_assertions)]
+ for (local, &head) in self.copy_classes.iter_enumerated() {
+ assert_eq!(property.contains(local), property.contains(head));
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum LocationExtended {
+ Plain(Location),
+ Arg,
+}
+
+struct SsaVisitor {
+ dominators: Dominators<BasicBlock>,
+ assignments: IndexVec<Local, Set1<LocationExtended>>,
+ assignment_order: Vec<Local>,
+}
+
+impl<'tcx> Visitor<'tcx> for SsaVisitor {
+ fn visit_local(&mut self, local: Local, ctxt: PlaceContext, loc: Location) {
+ match ctxt {
+ PlaceContext::MutatingUse(MutatingUseContext::Store) => {
+ self.assignments[local].insert(LocationExtended::Plain(loc));
+ self.assignment_order.push(local);
+ }
+ // Anything can happen with raw pointers, so remove them.
+ PlaceContext::NonMutatingUse(NonMutatingUseContext::AddressOf)
+ | PlaceContext::MutatingUse(_) => self.assignments[local] = Set1::Many,
+ // Immutable borrows are taken into account in `SsaLocals::new` by
+ // removing non-freeze locals.
+ PlaceContext::NonMutatingUse(_) => {
+ let set = &mut self.assignments[local];
+ let assign_dominates = match *set {
+ Set1::Empty | Set1::Many => false,
+ Set1::One(LocationExtended::Arg) => true,
+ Set1::One(LocationExtended::Plain(assign)) => {
+ assign.successor_within_block().dominates(loc, &self.dominators)
+ }
+ };
+ // We are visiting a use that is not dominated by an assignment.
+ // Either there is a cycle involved, or we are reading for uninitialized local.
+ // Bail out.
+ if !assign_dominates {
+ *set = Set1::Many;
+ }
+ }
+ PlaceContext::NonUse(_) => {}
+ }
+ }
+}
+
+#[instrument(level = "trace", skip(ssa, body))]
+fn compute_copy_classes(ssa: &SsaVisitor, body: &Body<'_>) -> IndexVec<Local, Local> {
+ let mut copies = IndexVec::from_fn_n(|l| l, body.local_decls.len());
+
+ for &local in &ssa.assignment_order {
+ debug!(?local);
+
+ if local == RETURN_PLACE {
+ // `_0` is special, we cannot rename it.
+ continue;
+ }
+
+ // This is not SSA: mark that we don't know the value.
+ debug!(assignments = ?ssa.assignments[local]);
+ let Set1::One(LocationExtended::Plain(loc)) = ssa.assignments[local] else { continue };
+
+ // `loc` must point to a direct assignment to `local`.
+ let Either::Left(stmt) = body.stmt_at(loc) else { bug!() };
+ let Some((_target, rvalue)) = stmt.kind.as_assign() else { bug!() };
+ assert_eq!(_target.as_local(), Some(local));
+
+ let (Rvalue::Use(Operand::Copy(place) | Operand::Move(place)) | Rvalue::CopyForDeref(place))
+ = rvalue
+ else { continue };
+
+ let Some(rhs) = place.as_local() else { continue };
+ let Set1::One(_) = ssa.assignments[rhs] else { continue };
+
+ // We visit in `assignment_order`, ie. reverse post-order, so `rhs` has been
+ // visited before `local`, and we just have to copy the representing local.
+ copies[local] = copies[rhs];
+ }
+
+ debug!(?copies);
+
+ // Invariant: `copies` must point to the head of an equivalence class.
+ #[cfg(debug_assertions)]
+ for &head in copies.iter() {
+ assert_eq!(copies[head], head);
+ }
+
+ copies
+}
};
let start_def_id = self.tcx.require_lang_item(LangItem::Start, None);
- let main_ret_ty = self.tcx.fn_sig(main_def_id).output();
+ let main_ret_ty = self.tcx.fn_sig(main_def_id).no_bound_vars().unwrap().output();
// Given that `main()` has no arguments,
// then its return type cannot have
);
if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) {
+ let trait_ref = trait_ref.subst_identity();
+
let param_env = ty::ParamEnv::reveal_all();
let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref);
let overridden_methods = tcx.impl_item_implementor_ids(item.owner_id);
#![feature(array_windows)]
-#![feature(control_flow_enum)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
#[instrument(level = "debug", skip(self))]
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if !c.has_non_region_param() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match c.kind() {
ty::ConstKind::Param(param) => {
debug!(?param);
self.unused_parameters.mark_used(param.index);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs })
if matches!(self.tcx.def_kind(def.did), DefKind::AnonConst) =>
{
self.visit_child_body(def.did, substs);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => c.super_visit_with(self),
}
#[instrument(level = "debug", skip(self))]
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.has_non_region_param() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match *ty.kind() {
debug!(?def_id);
// Avoid cycle errors with generators.
if def_id == self.def_id {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
// Consider any generic parameters used by any closures/generators as used in the
// parent.
self.visit_child_body(def_id, substs);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
ty::Param(param) => {
debug!(?param);
self.unused_parameters.mark_used(param.index);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => ty.super_visit_with(self),
}
#[primary_span]
pub if_span: Span,
#[subdiagnostic]
- pub sub: IfExpressionMissingThenBlockSub,
+ pub missing_then_block_sub: IfExpressionMissingThenBlockSub,
+ #[subdiagnostic]
+ pub let_else_sub: Option<IfExpressionLetSomeSub>,
}
#[derive(Subdiagnostic)]
AddThenBlock(#[primary_span] Span),
}
+#[derive(Subdiagnostic)]
+#[help(parse_extra_if_in_let_else)]
+pub(crate) struct IfExpressionLetSomeSub {
+ #[primary_span]
+ pub if_span: Span,
+}
+
#[derive(Diagnostic)]
#[diag(parse_if_expression_missing_condition)]
pub(crate) struct IfExpressionMissingCondition {
pub sub: MatchArmBodyWithoutBracesSugg,
}
+#[derive(Diagnostic)]
+#[diag(parse_inclusive_range_extra_equals)]
+#[note]
+pub(crate) struct InclusiveRangeExtraEquals {
+ #[primary_span]
+ #[suggestion(
+ suggestion_remove_eq,
+ style = "short",
+ code = "..=",
+ applicability = "maybe-incorrect"
+ )]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_inclusive_range_match_arrow)]
+pub(crate) struct InclusiveRangeMatchArrow {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion(
+ suggestion_add_space,
+ style = "verbose",
+ code = " ",
+ applicability = "machine-applicable"
+ )]
+ pub after_pat: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_inclusive_range_no_end, code = "E0586")]
+#[note]
+pub(crate) struct InclusiveRangeNoEnd {
+ #[primary_span]
+ #[suggestion(
+ suggestion_open_range,
+ code = "..",
+ applicability = "machine-applicable",
+ style = "short"
+ )]
+ pub span: Span,
+}
+
#[derive(Subdiagnostic)]
pub(crate) enum MatchArmBodyWithoutBracesSugg {
#[multipart_suggestion(suggestion_add_braces, applicability = "machine-applicable")]
--- /dev/null
+use super::UnmatchedBrace;
+use rustc_ast::token::Delimiter;
+use rustc_errors::Diagnostic;
+use rustc_span::source_map::SourceMap;
+use rustc_span::Span;
+
+#[derive(Default)]
+pub struct TokenTreeDiagInfo {
+ /// Stack of open delimiters and their spans. Used for error message.
+ pub open_braces: Vec<(Delimiter, Span)>,
+ pub unmatched_braces: Vec<UnmatchedBrace>,
+
+ /// Used only for error recovery when arriving to EOF with mismatched braces.
+ pub last_unclosed_found_span: Option<Span>,
+
+ /// Collect empty block spans that might have been auto-inserted by editors.
+ pub empty_block_spans: Vec<Span>,
+
+ /// Collect the spans of braces (Open, Close). Used only
+ /// for detecting if blocks are empty and only braces.
+ pub matching_block_spans: Vec<(Span, Span)>,
+}
+
+pub fn same_identation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) -> bool {
+ match (sm.span_to_margin(open_sp), sm.span_to_margin(close_sp)) {
+ (Some(open_padding), Some(close_padding)) => open_padding == close_padding,
+ _ => false,
+ }
+}
+
+// When we get a `)` or `]` for `{`, we should emit help message here
+// it's more friendly compared to report `unmatched error` in later phase
+pub fn report_missing_open_delim(
+ err: &mut Diagnostic,
+ unmatched_braces: &[UnmatchedBrace],
+) -> bool {
+ let mut reported_missing_open = false;
+ for unmatch_brace in unmatched_braces.iter() {
+ if let Some(delim) = unmatch_brace.found_delim
+ && matches!(delim, Delimiter::Parenthesis | Delimiter::Bracket)
+ {
+ let missed_open = match delim {
+ Delimiter::Parenthesis => "(",
+ Delimiter::Bracket => "[",
+ _ => unreachable!(),
+ };
+ err.span_label(
+ unmatch_brace.found_span.shrink_to_lo(),
+ format!("missing open `{}` for this delimiter", missed_open),
+ );
+ reported_missing_open = true;
+ }
+ }
+ reported_missing_open
+}
+
+pub fn report_suspicious_mismatch_block(
+ err: &mut Diagnostic,
+ diag_info: &TokenTreeDiagInfo,
+ sm: &SourceMap,
+ delim: Delimiter,
+) {
+ if report_missing_open_delim(err, &diag_info.unmatched_braces) {
+ return;
+ }
+
+ let mut matched_spans: Vec<(Span, bool)> = diag_info
+ .matching_block_spans
+ .iter()
+ .map(|&(open, close)| (open.with_hi(close.lo()), same_identation_level(sm, open, close)))
+ .collect();
+
+ // sort by `lo`, so the large block spans in the front
+ matched_spans.sort_by(|a, b| a.0.lo().cmp(&b.0.lo()));
+
+ // We use larger block whose identation is well to cover those inner mismatched blocks
+ // O(N^2) here, but we are on error reporting path, so it is fine
+ for i in 0..matched_spans.len() {
+ let (block_span, same_ident) = matched_spans[i];
+ if same_ident {
+ for j in i + 1..matched_spans.len() {
+ let (inner_block, inner_same_ident) = matched_spans[j];
+ if block_span.contains(inner_block) && !inner_same_ident {
+ matched_spans[j] = (inner_block, true);
+ }
+ }
+ }
+ }
+
+ // Find the inner-most span candidate for final report
+ let candidate_span =
+ matched_spans.into_iter().rev().find(|&(_, same_ident)| !same_ident).map(|(span, _)| span);
+
+ if let Some(block_span) = candidate_span {
+ err.span_label(block_span.shrink_to_lo(), "this delimiter might not be properly closed...");
+ err.span_label(
+ block_span.shrink_to_hi(),
+ "...as it matches this but it has different indentation",
+ );
+
+ // If there is a empty block in the mismatched span, note it
+ if delim == Delimiter::Brace {
+ for span in diag_info.empty_block_spans.iter() {
+ if block_span.contains(*span) {
+ err.span_label(*span, "block is empty, you might have not meant to close it");
+ break;
+ }
+ }
+ }
+ } else {
+ // If there is no suspicious span, give the last properly closed block may help
+ if let Some(parent) = diag_info.matching_block_spans.last()
+ && diag_info.open_braces.last().is_none()
+ && diag_info.empty_block_spans.iter().all(|&sp| sp != parent.0.to(parent.1)) {
+ err.span_label(parent.0, "this opening brace...");
+ err.span_label(parent.1, "...matches this closing brace");
+ }
+ }
+}
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{edition::Edition, BytePos, Pos, Span};
+mod diagnostics;
mod tokentrees;
mod unescape_error_reporting;
mod unicode_chars;
}
let cursor = Cursor::new(src);
- let string_reader =
- StringReader { sess, start_pos, pos: start_pos, src, cursor, override_span };
+ let string_reader = StringReader {
+ sess,
+ start_pos,
+ pos: start_pos,
+ src,
+ cursor,
+ override_span,
+ nbsp_is_whitespace: false,
+ };
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader)
}
/// Cursor for getting lexer tokens.
cursor: Cursor<'a>,
override_span: Option<Span>,
+ /// When a "unknown start of token: \u{a0}" has already been emitted earlier
+ /// in this file, it's safe to treat further occurrences of the non-breaking
+ /// space character as whitespace.
+ nbsp_is_whitespace: bool,
}
impl<'a> StringReader<'a> {
/// preceded by whitespace.
fn next_token(&mut self) -> (Token, bool) {
let mut preceded_by_whitespace = false;
-
+ let mut swallow_next_invalid = 0;
// Skip trivial (whitespace & comments) tokens
loop {
let token = self.cursor.advance_token();
rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
- let c = self.str_from(start).chars().next().unwrap();
+ // Don't emit diagnostics for sequences of the same invalid token
+ if swallow_next_invalid > 0 {
+ swallow_next_invalid -= 1;
+ continue;
+ }
+ let mut it = self.str_from_to_end(start).chars();
+ let c = it.next().unwrap();
+ if c == '\u{00a0}' {
+ // If an error has already been reported on non-breaking
+ // space characters earlier in the file, treat all
+ // subsequent occurrences as whitespace.
+ if self.nbsp_is_whitespace {
+ preceded_by_whitespace = true;
+ continue;
+ }
+ self.nbsp_is_whitespace = true;
+ }
+ let repeats = it.take_while(|c1| *c1 == c).count();
let mut err =
- self.struct_err_span_char(start, self.pos, "unknown start of token", c);
+ self.struct_err_span_char(start, self.pos + Pos::from_usize(repeats * c.len_utf8()), "unknown start of token", c);
// FIXME: the lexer could be used to turn the ASCII version of unicode
// homoglyphs, instead of keeping a table in `check_for_substitution`into the
// token. Ideally, this should be inside `rustc_lexer`. However, we should
// first remove compound tokens like `<<` from `rustc_lexer`, and then add
// fancier error recovery to it, as there will be less overall work to do this
// way.
- let token = unicode_chars::check_for_substitution(self, start, c, &mut err);
+ let token = unicode_chars::check_for_substitution(self, start, c, &mut err, repeats+1);
if c == '\x00' {
err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used");
}
+ if repeats > 0 {
+ if repeats == 1 {
+ err.note(format!("character appears once more"));
+ } else {
+ err.note(format!("character appears {repeats} more times"));
+ }
+ swallow_next_invalid = repeats;
+ }
err.emit();
if let Some(token) = token {
token
/// Slice of the source text from `start` up to but excluding `self.pos`,
/// meaning the slice does not include the character `self.ch`.
- fn str_from(&self, start: BytePos) -> &str {
+ fn str_from(&self, start: BytePos) -> &'a str {
self.str_from_to(start, self.pos)
}
}
/// Slice of the source text spanning from `start` up to but excluding `end`.
- fn str_from_to(&self, start: BytePos, end: BytePos) -> &str {
+ fn str_from_to(&self, start: BytePos, end: BytePos) -> &'a str {
&self.src[self.src_index(start)..self.src_index(end)]
}
+ /// Slice of the source text spanning from `start` until the end
+ fn str_from_to_end(&self, start: BytePos) -> &'a str {
+ &self.src[self.src_index(start)..]
+ }
+
fn report_raw_str_error(&self, start: BytePos, prefix_len: u32) -> ! {
match rustc_lexer::validate_raw_str(self.str_from(start), prefix_len) {
Err(RawStrError::InvalidStarter { bad_char }) => {
+use super::diagnostics::report_suspicious_mismatch_block;
+use super::diagnostics::same_identation_level;
+use super::diagnostics::TokenTreeDiagInfo;
use super::{StringReader, UnmatchedBrace};
use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{DelimSpan, Spacing, TokenStream, TokenTree};
use rustc_ast_pretty::pprust::token_to_string;
-use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{PErr, PResult};
-use rustc_span::Span;
pub(super) struct TokenTreesReader<'a> {
string_reader: StringReader<'a>,
/// The "next" token, which has been obtained from the `StringReader` but
/// not yet handled by the `TokenTreesReader`.
token: Token,
- /// Stack of open delimiters and their spans. Used for error message.
- open_braces: Vec<(Delimiter, Span)>,
- unmatched_braces: Vec<UnmatchedBrace>,
- /// The type and spans for all braces
- ///
- /// Used only for error recovery when arriving to EOF with mismatched braces.
- matching_delim_spans: Vec<(Delimiter, Span, Span)>,
- last_unclosed_found_span: Option<Span>,
- /// Collect empty block spans that might have been auto-inserted by editors.
- last_delim_empty_block_spans: FxHashMap<Delimiter, Span>,
- /// Collect the spans of braces (Open, Close). Used only
- /// for detecting if blocks are empty and only braces.
- matching_block_spans: Vec<(Span, Span)>,
+ diag_info: TokenTreeDiagInfo,
}
impl<'a> TokenTreesReader<'a> {
let mut tt_reader = TokenTreesReader {
string_reader,
token: Token::dummy(),
- open_braces: Vec::new(),
- unmatched_braces: Vec::new(),
- matching_delim_spans: Vec::new(),
- last_unclosed_found_span: None,
- last_delim_empty_block_spans: FxHashMap::default(),
- matching_block_spans: Vec::new(),
+ diag_info: TokenTreeDiagInfo::default(),
};
let res = tt_reader.parse_token_trees(/* is_delimited */ false);
- (res, tt_reader.unmatched_braces)
+ (res, tt_reader.diag_info.unmatched_braces)
}
// Parse a stream of tokens into a list of `TokenTree`s.
fn eof_err(&mut self) -> PErr<'a> {
let msg = "this file contains an unclosed delimiter";
let mut err = self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, msg);
- for &(_, sp) in &self.open_braces {
+ for &(_, sp) in &self.diag_info.open_braces {
err.span_label(sp, "unclosed delimiter");
- self.unmatched_braces.push(UnmatchedBrace {
+ self.diag_info.unmatched_braces.push(UnmatchedBrace {
expected_delim: Delimiter::Brace,
found_delim: None,
found_span: self.token.span,
});
}
- if let Some((delim, _)) = self.open_braces.last() {
- if let Some((_, open_sp, close_sp)) =
- self.matching_delim_spans.iter().find(|(d, open_sp, close_sp)| {
- let sm = self.string_reader.sess.source_map();
- if let Some(close_padding) = sm.span_to_margin(*close_sp) {
- if let Some(open_padding) = sm.span_to_margin(*open_sp) {
- return delim == d && close_padding != open_padding;
- }
- }
- false
- })
- // these are in reverse order as they get inserted on close, but
- {
- // we want the last open/first close
- err.span_label(*open_sp, "this delimiter might not be properly closed...");
- err.span_label(*close_sp, "...as it matches this but it has different indentation");
- }
+ if let Some((delim, _)) = self.diag_info.open_braces.last() {
+ report_suspicious_mismatch_block(
+ &mut err,
+ &self.diag_info,
+ &self.string_reader.sess.source_map(),
+ *delim,
+ )
}
err
}
// The span for beginning of the delimited section
let pre_span = self.token.span;
- self.open_braces.push((open_delim, self.token.span));
+ self.diag_info.open_braces.push((open_delim, self.token.span));
// Parse the token trees within the delimiters.
// We stop at any delimiter so we can try to recover if the user
// Expand to cover the entire delimited token tree
let delim_span = DelimSpan::from_pair(pre_span, self.token.span);
+ let sm = self.string_reader.sess.source_map();
match self.token.kind {
// Correct delimiter.
token::CloseDelim(close_delim) if close_delim == open_delim => {
- let (open_brace, open_brace_span) = self.open_braces.pop().unwrap();
+ let (open_brace, open_brace_span) = self.diag_info.open_braces.pop().unwrap();
let close_brace_span = self.token.span;
- if tts.is_empty() {
+ if tts.is_empty() && close_delim == Delimiter::Brace {
let empty_block_span = open_brace_span.to(close_brace_span);
- let sm = self.string_reader.sess.source_map();
if !sm.is_multiline(empty_block_span) {
// Only track if the block is in the form of `{}`, otherwise it is
// likely that it was written on purpose.
- self.last_delim_empty_block_spans.insert(open_delim, empty_block_span);
+ self.diag_info.empty_block_spans.push(empty_block_span);
}
}
- //only add braces
+ // only add braces
if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, open_delim) {
- self.matching_block_spans.push((open_brace_span, close_brace_span));
+ // Add all the matching spans, we will sort by span later
+ self.diag_info.matching_block_spans.push((open_brace_span, close_brace_span));
}
- if self.open_braces.is_empty() {
- // Clear up these spans to avoid suggesting them as we've found
- // properly matched delimiters so far for an entire block.
- self.matching_delim_spans.clear();
- } else {
- self.matching_delim_spans.push((open_brace, open_brace_span, close_brace_span));
- }
// Move past the closing delimiter.
self.token = self.string_reader.next_token().0;
}
let mut unclosed_delimiter = None;
let mut candidate = None;
- if self.last_unclosed_found_span != Some(self.token.span) {
+ if self.diag_info.last_unclosed_found_span != Some(self.token.span) {
// do not complain about the same unclosed delimiter multiple times
- self.last_unclosed_found_span = Some(self.token.span);
+ self.diag_info.last_unclosed_found_span = Some(self.token.span);
// This is a conservative error: only report the last unclosed
// delimiter. The previous unclosed delimiters could actually be
// closed! The parser just hasn't gotten to them yet.
- if let Some(&(_, sp)) = self.open_braces.last() {
+ if let Some(&(_, sp)) = self.diag_info.open_braces.last() {
unclosed_delimiter = Some(sp);
};
- let sm = self.string_reader.sess.source_map();
- if let Some(current_padding) = sm.span_to_margin(self.token.span) {
- for (brace, brace_span) in &self.open_braces {
- if let Some(padding) = sm.span_to_margin(*brace_span) {
- // high likelihood of these two corresponding
- if current_padding == padding && brace == &close_delim {
- candidate = Some(*brace_span);
- }
- }
+ for (brace, brace_span) in &self.diag_info.open_braces {
+ if same_identation_level(&sm, self.token.span, *brace_span)
+ && brace == &close_delim
+ {
+ // high likelihood of these two corresponding
+ candidate = Some(*brace_span);
}
}
- let (tok, _) = self.open_braces.pop().unwrap();
- self.unmatched_braces.push(UnmatchedBrace {
+ let (tok, _) = self.diag_info.open_braces.pop().unwrap();
+ self.diag_info.unmatched_braces.push(UnmatchedBrace {
expected_delim: tok,
found_delim: Some(close_delim),
found_span: self.token.span,
candidate_span: candidate,
});
} else {
- self.open_braces.pop();
+ self.diag_info.open_braces.pop();
}
// If the incorrect delimiter matches an earlier opening
// fn foo() {
// bar(baz(
// } // Incorrect delimiter but matches the earlier `{`
- if !self.open_braces.iter().any(|&(b, _)| b == close_delim) {
+ if !self.diag_info.open_braces.iter().any(|&(b, _)| b == close_delim) {
self.token = self.string_reader.next_token().0;
}
}
let mut err =
self.string_reader.sess.span_diagnostic.struct_span_err(self.token.span, &msg);
- // Braces are added at the end, so the last element is the biggest block
- if let Some(parent) = self.matching_block_spans.last() {
- if let Some(span) = self.last_delim_empty_block_spans.remove(&delim) {
- // Check if the (empty block) is in the last properly closed block
- if (parent.0.to(parent.1)).contains(span) {
- err.span_label(span, "block is empty, you might have not meant to close it");
- } else {
- err.span_label(parent.0, "this opening brace...");
- err.span_label(parent.1, "...matches this closing brace");
- }
- } else {
- err.span_label(parent.0, "this opening brace...");
- err.span_label(parent.1, "...matches this closing brace");
- }
- }
-
+ report_suspicious_mismatch_block(
+ &mut err,
+ &self.diag_info,
+ &self.string_reader.sess.source_map(),
+ delim,
+ );
err.span_label(self.token.span, "unexpected closing delimiter");
err
}
use rustc_span::{symbol::kw, BytePos, Pos, Span};
#[rustfmt::skip] // for line breaks
-pub(crate) const UNICODE_ARRAY: &[(char, &str, char)] = &[
- ('
', "Line Separator", ' '),
- ('
', "Paragraph Separator", ' '),
- (' ', "Ogham Space mark", ' '),
- (' ', "En Quad", ' '),
- (' ', "Em Quad", ' '),
- (' ', "En Space", ' '),
- (' ', "Em Space", ' '),
- (' ', "Three-Per-Em Space", ' '),
- (' ', "Four-Per-Em Space", ' '),
- (' ', "Six-Per-Em Space", ' '),
- (' ', "Punctuation Space", ' '),
- (' ', "Thin Space", ' '),
- (' ', "Hair Space", ' '),
- (' ', "Medium Mathematical Space", ' '),
- (' ', "No-Break Space", ' '),
- (' ', "Figure Space", ' '),
- (' ', "Narrow No-Break Space", ' '),
- (' ', "Ideographic Space", ' '),
-
- ('ߺ', "Nko Lajanyalan", '_'),
- ('﹍', "Dashed Low Line", '_'),
- ('﹎', "Centreline Low Line", '_'),
- ('﹏', "Wavy Low Line", '_'),
- ('_', "Fullwidth Low Line", '_'),
-
- ('‐', "Hyphen", '-'),
- ('‑', "Non-Breaking Hyphen", '-'),
- ('‒', "Figure Dash", '-'),
- ('–', "En Dash", '-'),
- ('—', "Em Dash", '-'),
- ('﹘', "Small Em Dash", '-'),
- ('۔', "Arabic Full Stop", '-'),
- ('⁃', "Hyphen Bullet", '-'),
- ('˗', "Modifier Letter Minus Sign", '-'),
- ('−', "Minus Sign", '-'),
- ('➖', "Heavy Minus Sign", '-'),
- ('Ⲻ', "Coptic Letter Dialect-P Ni", '-'),
- ('ー', "Katakana-Hiragana Prolonged Sound Mark", '-'),
- ('-', "Fullwidth Hyphen-Minus", '-'),
- ('―', "Horizontal Bar", '-'),
- ('─', "Box Drawings Light Horizontal", '-'),
- ('━', "Box Drawings Heavy Horizontal", '-'),
- ('㇐', "CJK Stroke H", '-'),
- ('ꟷ', "Latin Epigraphic Letter Sideways I", '-'),
- ('ᅳ', "Hangul Jungseong Eu", '-'),
- ('ㅡ', "Hangul Letter Eu", '-'),
- ('一', "CJK Unified Ideograph-4E00", '-'),
- ('⼀', "Kangxi Radical One", '-'),
-
- ('؍', "Arabic Date Separator", ','),
- ('٫', "Arabic Decimal Separator", ','),
- ('‚', "Single Low-9 Quotation Mark", ','),
- ('¸', "Cedilla", ','),
- ('ꓹ', "Lisu Letter Tone Na Po", ','),
- (',', "Fullwidth Comma", ','),
-
- (';', "Greek Question Mark", ';'),
- (';', "Fullwidth Semicolon", ';'),
- ('︔', "Presentation Form For Vertical Semicolon", ';'),
-
- ('ः', "Devanagari Sign Visarga", ':'),
- ('ઃ', "Gujarati Sign Visarga", ':'),
- (':', "Fullwidth Colon", ':'),
- ('։', "Armenian Full Stop", ':'),
- ('܃', "Syriac Supralinear Colon", ':'),
- ('܄', "Syriac Sublinear Colon", ':'),
- ('᛬', "Runic Multiple Punctuation", ':'),
- ('︰', "Presentation Form For Vertical Two Dot Leader", ':'),
- ('᠃', "Mongolian Full Stop", ':'),
- ('᠉', "Mongolian Manchu Full Stop", ':'),
- ('⁚', "Two Dot Punctuation", ':'),
- ('׃', "Hebrew Punctuation Sof Pasuq", ':'),
- ('˸', "Modifier Letter Raised Colon", ':'),
- ('꞉', "Modifier Letter Colon", ':'),
- ('∶', "Ratio", ':'),
- ('ː', "Modifier Letter Triangular Colon", ':'),
- ('ꓽ', "Lisu Letter Tone Mya Jeu", ':'),
- ('︓', "Presentation Form For Vertical Colon", ':'),
-
- ('!', "Fullwidth Exclamation Mark", '!'),
- ('ǃ', "Latin Letter Retroflex Click", '!'),
- ('ⵑ', "Tifinagh Letter Tuareg Yang", '!'),
- ('︕', "Presentation Form For Vertical Exclamation Mark", '!'),
-
- ('ʔ', "Latin Letter Glottal Stop", '?'),
- ('Ɂ', "Latin Capital Letter Glottal Stop", '?'),
- ('ॽ', "Devanagari Letter Glottal Stop", '?'),
- ('Ꭾ', "Cherokee Letter He", '?'),
- ('ꛫ', "Bamum Letter Ntuu", '?'),
- ('?', "Fullwidth Question Mark", '?'),
- ('︖', "Presentation Form For Vertical Question Mark", '?'),
-
- ('𝅭', "Musical Symbol Combining Augmentation Dot", '.'),
- ('․', "One Dot Leader", '.'),
- ('܁', "Syriac Supralinear Full Stop", '.'),
- ('܂', "Syriac Sublinear Full Stop", '.'),
- ('꘎', "Vai Full Stop", '.'),
- ('𐩐', "Kharoshthi Punctuation Dot", '.'),
- ('٠', "Arabic-Indic Digit Zero", '.'),
- ('۰', "Extended Arabic-Indic Digit Zero", '.'),
- ('ꓸ', "Lisu Letter Tone Mya Ti", '.'),
- ('·', "Middle Dot", '.'),
- ('・', "Katakana Middle Dot", '.'),
- ('・', "Halfwidth Katakana Middle Dot", '.'),
- ('᛫', "Runic Single Punctuation", '.'),
- ('·', "Greek Ano Teleia", '.'),
- ('⸱', "Word Separator Middle Dot", '.'),
- ('𐄁', "Aegean Word Separator Dot", '.'),
- ('•', "Bullet", '.'),
- ('‧', "Hyphenation Point", '.'),
- ('∙', "Bullet Operator", '.'),
- ('⋅', "Dot Operator", '.'),
- ('ꞏ', "Latin Letter Sinological Dot", '.'),
- ('ᐧ', "Canadian Syllabics Final Middle Dot", '.'),
- ('ᐧ', "Canadian Syllabics Final Middle Dot", '.'),
- ('.', "Fullwidth Full Stop", '.'),
- ('。', "Ideographic Full Stop", '.'),
- ('︒', "Presentation Form For Vertical Ideographic Full Stop", '.'),
-
- ('՝', "Armenian Comma", '\''),
- (''', "Fullwidth Apostrophe", '\''),
- ('‘', "Left Single Quotation Mark", '\''),
- ('’', "Right Single Quotation Mark", '\''),
- ('‛', "Single High-Reversed-9 Quotation Mark", '\''),
- ('′', "Prime", '\''),
- ('‵', "Reversed Prime", '\''),
- ('՚', "Armenian Apostrophe", '\''),
- ('׳', "Hebrew Punctuation Geresh", '\''),
- ('`', "Grave Accent", '\''),
- ('`', "Greek Varia", '\''),
- ('`', "Fullwidth Grave Accent", '\''),
- ('´', "Acute Accent", '\''),
- ('΄', "Greek Tonos", '\''),
- ('´', "Greek Oxia", '\''),
- ('᾽', "Greek Koronis", '\''),
- ('᾿', "Greek Psili", '\''),
- ('῾', "Greek Dasia", '\''),
- ('ʹ', "Modifier Letter Prime", '\''),
- ('ʹ', "Greek Numeral Sign", '\''),
- ('ˈ', "Modifier Letter Vertical Line", '\''),
- ('ˊ', "Modifier Letter Acute Accent", '\''),
- ('ˋ', "Modifier Letter Grave Accent", '\''),
- ('˴', "Modifier Letter Middle Grave Accent", '\''),
- ('ʻ', "Modifier Letter Turned Comma", '\''),
- ('ʽ', "Modifier Letter Reversed Comma", '\''),
- ('ʼ', "Modifier Letter Apostrophe", '\''),
- ('ʾ', "Modifier Letter Right Half Ring", '\''),
- ('ꞌ', "Latin Small Letter Saltillo", '\''),
- ('י', "Hebrew Letter Yod", '\''),
- ('ߴ', "Nko High Tone Apostrophe", '\''),
- ('ߵ', "Nko Low Tone Apostrophe", '\''),
- ('ᑊ', "Canadian Syllabics West-Cree P", '\''),
- ('ᛌ', "Runic Letter Short-Twig-Sol S", '\''),
- ('𖽑', "Miao Sign Aspiration", '\''),
- ('𖽒', "Miao Sign Reformed Voicing", '\''),
-
- ('᳓', "Vedic Sign Nihshvasa", '"'),
- ('"', "Fullwidth Quotation Mark", '"'),
- ('“', "Left Double Quotation Mark", '"'),
- ('”', "Right Double Quotation Mark", '"'),
- ('‟', "Double High-Reversed-9 Quotation Mark", '"'),
- ('″', "Double Prime", '"'),
- ('‶', "Reversed Double Prime", '"'),
- ('〃', "Ditto Mark", '"'),
- ('״', "Hebrew Punctuation Gershayim", '"'),
- ('˝', "Double Acute Accent", '"'),
- ('ʺ', "Modifier Letter Double Prime", '"'),
- ('˶', "Modifier Letter Middle Double Acute Accent", '"'),
- ('˵', "Modifier Letter Middle Double Grave Accent", '"'),
- ('ˮ', "Modifier Letter Double Apostrophe", '"'),
- ('ײ', "Hebrew Ligature Yiddish Double Yod", '"'),
- ('❞', "Heavy Double Comma Quotation Mark Ornament", '"'),
- ('❝', "Heavy Double Turned Comma Quotation Mark Ornament", '"'),
-
- ('(', "Fullwidth Left Parenthesis", '('),
- ('❨', "Medium Left Parenthesis Ornament", '('),
- ('﴾', "Ornate Left Parenthesis", '('),
-
- (')', "Fullwidth Right Parenthesis", ')'),
- ('❩', "Medium Right Parenthesis Ornament", ')'),
- ('﴿', "Ornate Right Parenthesis", ')'),
-
- ('[', "Fullwidth Left Square Bracket", '['),
- ('❲', "Light Left Tortoise Shell Bracket Ornament", '['),
- ('「', "Left Corner Bracket", '['),
- ('『', "Left White Corner Bracket", '['),
- ('【', "Left Black Lenticular Bracket", '['),
- ('〔', "Left Tortoise Shell Bracket", '['),
- ('〖', "Left White Lenticular Bracket", '['),
- ('〘', "Left White Tortoise Shell Bracket", '['),
- ('〚', "Left White Square Bracket", '['),
-
- (']', "Fullwidth Right Square Bracket", ']'),
- ('❳', "Light Right Tortoise Shell Bracket Ornament", ']'),
- ('」', "Right Corner Bracket", ']'),
- ('』', "Right White Corner Bracket", ']'),
- ('】', "Right Black Lenticular Bracket", ']'),
- ('〕', "Right Tortoise Shell Bracket", ']'),
- ('〗', "Right White Lenticular Bracket", ']'),
- ('〙', "Right White Tortoise Shell Bracket", ']'),
- ('〛', "Right White Square Bracket", ']'),
-
- ('❴', "Medium Left Curly Bracket Ornament", '{'),
- ('𝄔', "Musical Symbol Brace", '{'),
- ('{', "Fullwidth Left Curly Bracket", '{'),
-
- ('❵', "Medium Right Curly Bracket Ornament", '}'),
- ('}', "Fullwidth Right Curly Bracket", '}'),
-
- ('⁎', "Low Asterisk", '*'),
- ('٭', "Arabic Five Pointed Star", '*'),
- ('∗', "Asterisk Operator", '*'),
- ('𐌟', "Old Italic Letter Ess", '*'),
- ('*', "Fullwidth Asterisk", '*'),
-
- ('᜵', "Philippine Single Punctuation", '/'),
- ('⁁', "Caret Insertion Point", '/'),
- ('∕', "Division Slash", '/'),
- ('⁄', "Fraction Slash", '/'),
- ('╱', "Box Drawings Light Diagonal Upper Right To Lower Left", '/'),
- ('⟋', "Mathematical Rising Diagonal", '/'),
- ('⧸', "Big Solidus", '/'),
- ('𝈺', "Greek Instrumental Notation Symbol-47", '/'),
- ('㇓', "CJK Stroke Sp", '/'),
- ('〳', "Vertical Kana Repeat Mark Upper Half", '/'),
- ('Ⳇ', "Coptic Capital Letter Old Coptic Esh", '/'),
- ('ノ', "Katakana Letter No", '/'),
- ('丿', "CJK Unified Ideograph-4E3F", '/'),
- ('⼃', "Kangxi Radical Slash", '/'),
- ('/', "Fullwidth Solidus", '/'),
-
- ('\', "Fullwidth Reverse Solidus", '\\'),
- ('﹨', "Small Reverse Solidus", '\\'),
- ('∖', "Set Minus", '\\'),
- ('⟍', "Mathematical Falling Diagonal", '\\'),
- ('⧵', "Reverse Solidus Operator", '\\'),
- ('⧹', "Big Reverse Solidus", '\\'),
- ('⧹', "Greek Vocal Notation Symbol-16", '\\'),
- ('⧹', "Greek Instrumental Symbol-48", '\\'),
- ('㇔', "CJK Stroke D", '\\'),
- ('丶', "CJK Unified Ideograph-4E36", '\\'),
- ('⼂', "Kangxi Radical Dot", '\\'),
- ('、', "Ideographic Comma", '\\'),
- ('ヽ', "Katakana Iteration Mark", '\\'),
-
- ('ꝸ', "Latin Small Letter Um", '&'),
- ('&', "Fullwidth Ampersand", '&'),
-
- ('᛭', "Runic Cross Punctuation", '+'),
- ('➕', "Heavy Plus Sign", '+'),
- ('𐊛', "Lycian Letter H", '+'),
- ('﬩', "Hebrew Letter Alternative Plus Sign", '+'),
- ('+', "Fullwidth Plus Sign", '+'),
-
- ('‹', "Single Left-Pointing Angle Quotation Mark", '<'),
- ('❮', "Heavy Left-Pointing Angle Quotation Mark Ornament", '<'),
- ('˂', "Modifier Letter Left Arrowhead", '<'),
- ('𝈶', "Greek Instrumental Symbol-40", '<'),
- ('ᐸ', "Canadian Syllabics Pa", '<'),
- ('ᚲ', "Runic Letter Kauna", '<'),
- ('❬', "Medium Left-Pointing Angle Bracket Ornament", '<'),
- ('⟨', "Mathematical Left Angle Bracket", '<'),
- ('〈', "Left-Pointing Angle Bracket", '<'),
- ('〈', "Left Angle Bracket", '<'),
- ('㇛', "CJK Stroke Pd", '<'),
- ('く', "Hiragana Letter Ku", '<'),
- ('𡿨', "CJK Unified Ideograph-21FE8", '<'),
- ('《', "Left Double Angle Bracket", '<'),
- ('<', "Fullwidth Less-Than Sign", '<'),
-
- ('᐀', "Canadian Syllabics Hyphen", '='),
- ('⹀', "Double Hyphen", '='),
- ('゠', "Katakana-Hiragana Double Hyphen", '='),
- ('꓿', "Lisu Punctuation Full Stop", '='),
- ('=', "Fullwidth Equals Sign", '='),
-
- ('›', "Single Right-Pointing Angle Quotation Mark", '>'),
- ('❯', "Heavy Right-Pointing Angle Quotation Mark Ornament", '>'),
- ('˃', "Modifier Letter Right Arrowhead", '>'),
- ('𝈷', "Greek Instrumental Symbol-42", '>'),
- ('ᐳ', "Canadian Syllabics Po", '>'),
- ('𖼿', "Miao Letter Archaic Zza", '>'),
- ('❭', "Medium Right-Pointing Angle Bracket Ornament", '>'),
- ('⟩', "Mathematical Right Angle Bracket", '>'),
- ('〉', "Right-Pointing Angle Bracket", '>'),
- ('〉', "Right Angle Bracket", '>'),
- ('》', "Right Double Angle Bracket", '>'),
- ('>', "Fullwidth Greater-Than Sign", '>'),
+pub(crate) const UNICODE_ARRAY: &[(char, &str, &str)] = &[
+ ('
', "Line Separator", " "),
+ ('
', "Paragraph Separator", " "),
+ (' ', "Ogham Space mark", " "),
+ (' ', "En Quad", " "),
+ (' ', "Em Quad", " "),
+ (' ', "En Space", " "),
+ (' ', "Em Space", " "),
+ (' ', "Three-Per-Em Space", " "),
+ (' ', "Four-Per-Em Space", " "),
+ (' ', "Six-Per-Em Space", " "),
+ (' ', "Punctuation Space", " "),
+ (' ', "Thin Space", " "),
+ (' ', "Hair Space", " "),
+ (' ', "Medium Mathematical Space", " "),
+ (' ', "No-Break Space", " "),
+ (' ', "Figure Space", " "),
+ (' ', "Narrow No-Break Space", " "),
+ (' ', "Ideographic Space", " "),
+
+ ('ߺ', "Nko Lajanyalan", "_"),
+ ('﹍', "Dashed Low Line", "_"),
+ ('﹎', "Centreline Low Line", "_"),
+ ('﹏', "Wavy Low Line", "_"),
+ ('_', "Fullwidth Low Line", "_"),
+
+ ('‐', "Hyphen", "-"),
+ ('‑', "Non-Breaking Hyphen", "-"),
+ ('‒', "Figure Dash", "-"),
+ ('–', "En Dash", "-"),
+ ('—', "Em Dash", "-"),
+ ('﹘', "Small Em Dash", "-"),
+ ('۔', "Arabic Full Stop", "-"),
+ ('⁃', "Hyphen Bullet", "-"),
+ ('˗', "Modifier Letter Minus Sign", "-"),
+ ('−', "Minus Sign", "-"),
+ ('➖', "Heavy Minus Sign", "-"),
+ ('Ⲻ', "Coptic Letter Dialect-P Ni", "-"),
+ ('ー', "Katakana-Hiragana Prolonged Sound Mark", "-"),
+ ('-', "Fullwidth Hyphen-Minus", "-"),
+ ('―', "Horizontal Bar", "-"),
+ ('─', "Box Drawings Light Horizontal", "-"),
+ ('━', "Box Drawings Heavy Horizontal", "-"),
+ ('㇐', "CJK Stroke H", "-"),
+ ('ꟷ', "Latin Epigraphic Letter Sideways I", "-"),
+ ('ᅳ', "Hangul Jungseong Eu", "-"),
+ ('ㅡ', "Hangul Letter Eu", "-"),
+ ('一', "CJK Unified Ideograph-4E00", "-"),
+ ('⼀', "Kangxi Radical One", "-"),
+
+ ('؍', "Arabic Date Separator", ","),
+ ('٫', "Arabic Decimal Separator", ","),
+ ('‚', "Single Low-9 Quotation Mark", ","),
+ ('¸', "Cedilla", ","),
+ ('ꓹ', "Lisu Letter Tone Na Po", ","),
+ (',', "Fullwidth Comma", ","),
+
+ (';', "Greek Question Mark", ";"),
+ (';', "Fullwidth Semicolon", ";"),
+ ('︔', "Presentation Form For Vertical Semicolon", ";"),
+
+ ('ः', "Devanagari Sign Visarga", ":"),
+ ('ઃ', "Gujarati Sign Visarga", ":"),
+ (':', "Fullwidth Colon", ":"),
+ ('։', "Armenian Full Stop", ":"),
+ ('܃', "Syriac Supralinear Colon", ":"),
+ ('܄', "Syriac Sublinear Colon", ":"),
+ ('᛬', "Runic Multiple Punctuation", ":"),
+ ('︰', "Presentation Form For Vertical Two Dot Leader", ":"),
+ ('᠃', "Mongolian Full Stop", ":"),
+ ('᠉', "Mongolian Manchu Full Stop", ":"),
+ ('⁚', "Two Dot Punctuation", ":"),
+ ('׃', "Hebrew Punctuation Sof Pasuq", ":"),
+ ('˸', "Modifier Letter Raised Colon", ":"),
+ ('꞉', "Modifier Letter Colon", ":"),
+ ('∶', "Ratio", ":"),
+ ('ː', "Modifier Letter Triangular Colon", ":"),
+ ('ꓽ', "Lisu Letter Tone Mya Jeu", ":"),
+ ('︓', "Presentation Form For Vertical Colon", ":"),
+
+ ('!', "Fullwidth Exclamation Mark", "!"),
+ ('ǃ', "Latin Letter Retroflex Click", "!"),
+ ('ⵑ', "Tifinagh Letter Tuareg Yang", "!"),
+ ('︕', "Presentation Form For Vertical Exclamation Mark", "!"),
+
+ ('ʔ', "Latin Letter Glottal Stop", "?"),
+ ('Ɂ', "Latin Capital Letter Glottal Stop", "?"),
+ ('ॽ', "Devanagari Letter Glottal Stop", "?"),
+ ('Ꭾ', "Cherokee Letter He", "?"),
+ ('ꛫ', "Bamum Letter Ntuu", "?"),
+ ('?', "Fullwidth Question Mark", "?"),
+ ('︖', "Presentation Form For Vertical Question Mark", "?"),
+
+ ('𝅭', "Musical Symbol Combining Augmentation Dot", "."),
+ ('․', "One Dot Leader", "."),
+ ('܁', "Syriac Supralinear Full Stop", "."),
+ ('܂', "Syriac Sublinear Full Stop", "."),
+ ('꘎', "Vai Full Stop", "."),
+ ('𐩐', "Kharoshthi Punctuation Dot", "."),
+ ('٠', "Arabic-Indic Digit Zero", "."),
+ ('۰', "Extended Arabic-Indic Digit Zero", "."),
+ ('ꓸ', "Lisu Letter Tone Mya Ti", "."),
+ ('·', "Middle Dot", "."),
+ ('・', "Katakana Middle Dot", "."),
+ ('・', "Halfwidth Katakana Middle Dot", "."),
+ ('᛫', "Runic Single Punctuation", "."),
+ ('·', "Greek Ano Teleia", "."),
+ ('⸱', "Word Separator Middle Dot", "."),
+ ('𐄁', "Aegean Word Separator Dot", "."),
+ ('•', "Bullet", "."),
+ ('‧', "Hyphenation Point", "."),
+ ('∙', "Bullet Operator", "."),
+ ('⋅', "Dot Operator", "."),
+ ('ꞏ', "Latin Letter Sinological Dot", "."),
+ ('ᐧ', "Canadian Syllabics Final Middle Dot", "."),
+ ('ᐧ', "Canadian Syllabics Final Middle Dot", "."),
+ ('.', "Fullwidth Full Stop", "."),
+ ('。', "Ideographic Full Stop", "."),
+ ('︒', "Presentation Form For Vertical Ideographic Full Stop", "."),
+
+ ('՝', "Armenian Comma", "\'"),
+ (''', "Fullwidth Apostrophe", "\'"),
+ ('‘', "Left Single Quotation Mark", "\'"),
+ ('’', "Right Single Quotation Mark", "\'"),
+ ('‛', "Single High-Reversed-9 Quotation Mark", "\'"),
+ ('′', "Prime", "\'"),
+ ('‵', "Reversed Prime", "\'"),
+ ('՚', "Armenian Apostrophe", "\'"),
+ ('׳', "Hebrew Punctuation Geresh", "\'"),
+ ('`', "Grave Accent", "\'"),
+ ('`', "Greek Varia", "\'"),
+ ('`', "Fullwidth Grave Accent", "\'"),
+ ('´', "Acute Accent", "\'"),
+ ('΄', "Greek Tonos", "\'"),
+ ('´', "Greek Oxia", "\'"),
+ ('᾽', "Greek Koronis", "\'"),
+ ('᾿', "Greek Psili", "\'"),
+ ('῾', "Greek Dasia", "\'"),
+ ('ʹ', "Modifier Letter Prime", "\'"),
+ ('ʹ', "Greek Numeral Sign", "\'"),
+ ('ˈ', "Modifier Letter Vertical Line", "\'"),
+ ('ˊ', "Modifier Letter Acute Accent", "\'"),
+ ('ˋ', "Modifier Letter Grave Accent", "\'"),
+ ('˴', "Modifier Letter Middle Grave Accent", "\'"),
+ ('ʻ', "Modifier Letter Turned Comma", "\'"),
+ ('ʽ', "Modifier Letter Reversed Comma", "\'"),
+ ('ʼ', "Modifier Letter Apostrophe", "\'"),
+ ('ʾ', "Modifier Letter Right Half Ring", "\'"),
+ ('ꞌ', "Latin Small Letter Saltillo", "\'"),
+ ('י', "Hebrew Letter Yod", "\'"),
+ ('ߴ', "Nko High Tone Apostrophe", "\'"),
+ ('ߵ', "Nko Low Tone Apostrophe", "\'"),
+ ('ᑊ', "Canadian Syllabics West-Cree P", "\'"),
+ ('ᛌ', "Runic Letter Short-Twig-Sol S", "\'"),
+ ('𖽑', "Miao Sign Aspiration", "\'"),
+ ('𖽒', "Miao Sign Reformed Voicing", "\'"),
+
+ ('᳓', "Vedic Sign Nihshvasa", "\""),
+ ('"', "Fullwidth Quotation Mark", "\""),
+ ('“', "Left Double Quotation Mark", "\""),
+ ('”', "Right Double Quotation Mark", "\""),
+ ('‟', "Double High-Reversed-9 Quotation Mark", "\""),
+ ('″', "Double Prime", "\""),
+ ('‶', "Reversed Double Prime", "\""),
+ ('〃', "Ditto Mark", "\""),
+ ('״', "Hebrew Punctuation Gershayim", "\""),
+ ('˝', "Double Acute Accent", "\""),
+ ('ʺ', "Modifier Letter Double Prime", "\""),
+ ('˶', "Modifier Letter Middle Double Acute Accent", "\""),
+ ('˵', "Modifier Letter Middle Double Grave Accent", "\""),
+ ('ˮ', "Modifier Letter Double Apostrophe", "\""),
+ ('ײ', "Hebrew Ligature Yiddish Double Yod", "\""),
+ ('❞', "Heavy Double Comma Quotation Mark Ornament", "\""),
+ ('❝', "Heavy Double Turned Comma Quotation Mark Ornament", "\""),
+
+ ('(', "Fullwidth Left Parenthesis", "("),
+ ('❨', "Medium Left Parenthesis Ornament", "("),
+ ('﴾', "Ornate Left Parenthesis", "("),
+
+ (')', "Fullwidth Right Parenthesis", ")"),
+ ('❩', "Medium Right Parenthesis Ornament", ")"),
+ ('﴿', "Ornate Right Parenthesis", ")"),
+
+ ('[', "Fullwidth Left Square Bracket", "["),
+ ('❲', "Light Left Tortoise Shell Bracket Ornament", "["),
+ ('「', "Left Corner Bracket", "["),
+ ('『', "Left White Corner Bracket", "["),
+ ('【', "Left Black Lenticular Bracket", "["),
+ ('〔', "Left Tortoise Shell Bracket", "["),
+ ('〖', "Left White Lenticular Bracket", "["),
+ ('〘', "Left White Tortoise Shell Bracket", "["),
+ ('〚', "Left White Square Bracket", "["),
+
+ (']', "Fullwidth Right Square Bracket", "]"),
+ ('❳', "Light Right Tortoise Shell Bracket Ornament", "]"),
+ ('」', "Right Corner Bracket", "]"),
+ ('』', "Right White Corner Bracket", "]"),
+ ('】', "Right Black Lenticular Bracket", "]"),
+ ('〕', "Right Tortoise Shell Bracket", "]"),
+ ('〗', "Right White Lenticular Bracket", "]"),
+ ('〙', "Right White Tortoise Shell Bracket", "]"),
+ ('〛', "Right White Square Bracket", "]"),
+
+ ('❴', "Medium Left Curly Bracket Ornament", "{"),
+ ('𝄔', "Musical Symbol Brace", "{"),
+ ('{', "Fullwidth Left Curly Bracket", "{"),
+
+ ('❵', "Medium Right Curly Bracket Ornament", "}"),
+ ('}', "Fullwidth Right Curly Bracket", "}"),
+
+ ('⁎', "Low Asterisk", "*"),
+ ('٭', "Arabic Five Pointed Star", "*"),
+ ('∗', "Asterisk Operator", "*"),
+ ('𐌟', "Old Italic Letter Ess", "*"),
+ ('*', "Fullwidth Asterisk", "*"),
+
+ ('᜵', "Philippine Single Punctuation", "/"),
+ ('⁁', "Caret Insertion Point", "/"),
+ ('∕', "Division Slash", "/"),
+ ('⁄', "Fraction Slash", "/"),
+ ('╱', "Box Drawings Light Diagonal Upper Right To Lower Left", "/"),
+ ('⟋', "Mathematical Rising Diagonal", "/"),
+ ('⧸', "Big Solidus", "/"),
+ ('𝈺', "Greek Instrumental Notation Symbol-47", "/"),
+ ('㇓', "CJK Stroke Sp", "/"),
+ ('〳', "Vertical Kana Repeat Mark Upper Half", "/"),
+ ('Ⳇ', "Coptic Capital Letter Old Coptic Esh", "/"),
+ ('ノ', "Katakana Letter No", "/"),
+ ('丿', "CJK Unified Ideograph-4E3F", "/"),
+ ('⼃', "Kangxi Radical Slash", "/"),
+ ('/', "Fullwidth Solidus", "/"),
+
+ ('\', "Fullwidth Reverse Solidus", "\\"),
+ ('﹨', "Small Reverse Solidus", "\\"),
+ ('∖', "Set Minus", "\\"),
+ ('⟍', "Mathematical Falling Diagonal", "\\"),
+ ('⧵', "Reverse Solidus Operator", "\\"),
+ ('⧹', "Big Reverse Solidus", "\\"),
+ ('⧹', "Greek Vocal Notation Symbol-16", "\\"),
+ ('⧹', "Greek Instrumental Symbol-48", "\\"),
+ ('㇔', "CJK Stroke D", "\\"),
+ ('丶', "CJK Unified Ideograph-4E36", "\\"),
+ ('⼂', "Kangxi Radical Dot", "\\"),
+ ('、', "Ideographic Comma", "\\"),
+ ('ヽ', "Katakana Iteration Mark", "\\"),
+
+ ('ꝸ', "Latin Small Letter Um", "&"),
+ ('&', "Fullwidth Ampersand", "&"),
+
+ ('᛭', "Runic Cross Punctuation", "+"),
+ ('➕', "Heavy Plus Sign", "+"),
+ ('𐊛', "Lycian Letter H", "+"),
+ ('﬩', "Hebrew Letter Alternative Plus Sign", "+"),
+ ('+', "Fullwidth Plus Sign", "+"),
+
+ ('‹', "Single Left-Pointing Angle Quotation Mark", "<"),
+ ('❮', "Heavy Left-Pointing Angle Quotation Mark Ornament", "<"),
+ ('˂', "Modifier Letter Left Arrowhead", "<"),
+ ('𝈶', "Greek Instrumental Symbol-40", "<"),
+ ('ᐸ', "Canadian Syllabics Pa", "<"),
+ ('ᚲ', "Runic Letter Kauna", "<"),
+ ('❬', "Medium Left-Pointing Angle Bracket Ornament", "<"),
+ ('⟨', "Mathematical Left Angle Bracket", "<"),
+ ('〈', "Left-Pointing Angle Bracket", "<"),
+ ('〈', "Left Angle Bracket", "<"),
+ ('㇛', "CJK Stroke Pd", "<"),
+ ('く', "Hiragana Letter Ku", "<"),
+ ('𡿨', "CJK Unified Ideograph-21FE8", "<"),
+ ('《', "Left Double Angle Bracket", "<"),
+ ('<', "Fullwidth Less-Than Sign", "<"),
+
+ ('᐀', "Canadian Syllabics Hyphen", "="),
+ ('⹀', "Double Hyphen", "="),
+ ('゠', "Katakana-Hiragana Double Hyphen", "="),
+ ('꓿', "Lisu Punctuation Full Stop", "="),
+ ('=', "Fullwidth Equals Sign", "="),
+
+ ('›', "Single Right-Pointing Angle Quotation Mark", ">"),
+ ('❯', "Heavy Right-Pointing Angle Quotation Mark Ornament", ">"),
+ ('˃', "Modifier Letter Right Arrowhead", ">"),
+ ('𝈷', "Greek Instrumental Symbol-42", ">"),
+ ('ᐳ', "Canadian Syllabics Po", ">"),
+ ('𖼿', "Miao Letter Archaic Zza", ">"),
+ ('❭', "Medium Right-Pointing Angle Bracket Ornament", ">"),
+ ('⟩', "Mathematical Right Angle Bracket", ">"),
+ ('〉', "Right-Pointing Angle Bracket", ">"),
+ ('〉', "Right Angle Bracket", ">"),
+ ('》', "Right Double Angle Bracket", ">"),
+ ('>', "Fullwidth Greater-Than Sign", ">"),
+ ('⩵', "Two Consecutive Equals Signs", "==")
];
// FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs, instead of
// keeping the substitution token in this table. Ideally, this should be inside `rustc_lexer`.
// However, we should first remove compound tokens like `<<` from `rustc_lexer`, and then add
// fancier error recovery to it, as there will be less overall work to do this way.
-const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
- (' ', "Space", None),
- ('_', "Underscore", Some(token::Ident(kw::Underscore, false))),
- ('-', "Minus/Hyphen", Some(token::BinOp(token::Minus))),
- (',', "Comma", Some(token::Comma)),
- (';', "Semicolon", Some(token::Semi)),
- (':', "Colon", Some(token::Colon)),
- ('!', "Exclamation Mark", Some(token::Not)),
- ('?', "Question Mark", Some(token::Question)),
- ('.', "Period", Some(token::Dot)),
- ('(', "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
- (')', "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
- ('[', "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
- (']', "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
- ('{', "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
- ('}', "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
- ('*', "Asterisk", Some(token::BinOp(token::Star))),
- ('/', "Slash", Some(token::BinOp(token::Slash))),
- ('\\', "Backslash", None),
- ('&', "Ampersand", Some(token::BinOp(token::And))),
- ('+', "Plus Sign", Some(token::BinOp(token::Plus))),
- ('<', "Less-Than Sign", Some(token::Lt)),
- ('=', "Equals Sign", Some(token::Eq)),
- ('>', "Greater-Than Sign", Some(token::Gt)),
+const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[
+ (" ", "Space", None),
+ ("_", "Underscore", Some(token::Ident(kw::Underscore, false))),
+ ("-", "Minus/Hyphen", Some(token::BinOp(token::Minus))),
+ (",", "Comma", Some(token::Comma)),
+ (";", "Semicolon", Some(token::Semi)),
+ (":", "Colon", Some(token::Colon)),
+ ("!", "Exclamation Mark", Some(token::Not)),
+ ("?", "Question Mark", Some(token::Question)),
+ (".", "Period", Some(token::Dot)),
+ ("(", "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
+ (")", "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
+ ("[", "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
+ ("]", "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
+ ("{", "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
+ ("}", "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
+ ("*", "Asterisk", Some(token::BinOp(token::Star))),
+ ("/", "Slash", Some(token::BinOp(token::Slash))),
+ ("\\", "Backslash", None),
+ ("&", "Ampersand", Some(token::BinOp(token::And))),
+ ("+", "Plus Sign", Some(token::BinOp(token::Plus))),
+ ("<", "Less-Than Sign", Some(token::Lt)),
+ ("=", "Equals Sign", Some(token::Eq)),
+ ("==", "Double Equals Sign", Some(token::EqEq)),
+ (">", "Greater-Than Sign", Some(token::Gt)),
// FIXME: Literals are already lexed by this point, so we can't recover gracefully just by
// spitting the correct token out.
- ('\'', "Single Quote", None),
- ('"', "Quotation Mark", None),
+ ("\'", "Single Quote", None),
+ ("\"", "Quotation Mark", None),
];
pub(super) fn check_for_substitution<'a>(
pos: BytePos,
ch: char,
err: &mut Diagnostic,
+ count: usize,
) -> Option<token::TokenKind> {
- let &(_u_char, u_name, ascii_char) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
+ let &(_, u_name, ascii_str) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
- let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8()));
+ let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count));
- let Some((_ascii_char, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(c, _, _)| c == ascii_char) else {
+ let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
let msg = format!("substitution character not found for '{}'", ch);
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
return None;
let msg = format!(
"Unicode characters '“' (Left Double Quotation Mark) and \
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not",
- ascii_char, ascii_name
+ ascii_str, ascii_name
);
err.span_suggestion(
Span::with_root_ctxt(
} else {
let msg = format!(
"Unicode character '{}' ({}) looks like '{}' ({}), but it is not",
- ch, u_name, ascii_char, ascii_name
+ ch, u_name, ascii_str, ascii_name
+ );
+ err.span_suggestion(
+ span,
+ &msg,
+ ascii_str.to_string().repeat(count),
+ Applicability::MaybeIncorrect,
);
- err.span_suggestion(span, &msg, ascii_char, Applicability::MaybeIncorrect);
}
token.clone()
}
Err(err)
}
+ /// Try to recover from an unbraced const argument whose first token [could begin a type][ty].
+ ///
+ /// [ty]: token::Token::can_begin_type
+ pub(crate) fn recover_unbraced_const_arg_that_can_begin_ty(
+ &mut self,
+ mut snapshot: SnapshotParser<'a>,
+ ) -> Option<P<ast::Expr>> {
+ match snapshot.parse_expr_res(Restrictions::CONST_EXPR, None) {
+ // Since we don't know the exact reason why we failed to parse the type or the
+ // expression, employ a simple heuristic to weed out some pathological cases.
+ Ok(expr) if let token::Comma | token::Gt = snapshot.token.kind => {
+ self.restore_snapshot(snapshot);
+ Some(expr)
+ }
+ Ok(_) => None,
+ Err(err) => {
+ err.cancel();
+ None
+ }
+ }
+ }
+
/// Creates a dummy const argument, and reports that the expression must be enclosed in braces
pub fn dummy_const_arg_needs_braces(
&self,
/// Some special error handling for the "top-level" patterns in a match arm,
/// `for` loop, `let`, &c. (in contrast to subpatterns within such).
- pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
+ pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum(
&mut self,
mut first_pat: P<Pat>,
expected: Expected,
if !matches!(first_pat.kind, PatKind::Ident(_, _, None) | PatKind::Path(..))
|| !self.look_ahead(1, |token| token.is_ident() && !token.is_reserved_ident())
{
+ let mut snapshot_type = self.create_snapshot_for_diagnostic();
+ snapshot_type.bump(); // `:`
+ match snapshot_type.parse_ty() {
+ Err(inner_err) => {
+ inner_err.cancel();
+ }
+ Ok(ty) => {
+ let Err(mut err) = self.expected_one_of_not_found(&[], &[]) else {
+ return first_pat;
+ };
+ err.span_label(ty.span, "specifying the type of a pattern isn't supported");
+ self.restore_snapshot(snapshot_type);
+ let span = first_pat.span.to(ty.span);
+ first_pat = self.mk_pat(span, PatKind::Wild);
+ err.emit();
+ }
+ }
return first_pat;
}
// The pattern looks like it might be a path with a `::` -> `:` typo:
// `match foo { bar:baz => {} }`
- let span = self.token.span;
+ let colon_span = self.token.span;
// We only emit "unexpected `:`" error here if we can successfully parse the
// whole pattern correctly in that case.
- let snapshot = self.create_snapshot_for_diagnostic();
+ let mut snapshot_pat = self.create_snapshot_for_diagnostic();
+ let mut snapshot_type = self.create_snapshot_for_diagnostic();
// Create error for "unexpected `:`".
match self.expected_one_of_not_found(&[], &[]) {
Err(mut err) => {
- self.bump(); // Skip the `:`.
- match self.parse_pat_no_top_alt(expected) {
+ snapshot_pat.bump(); // Skip the `:`.
+ snapshot_type.bump(); // Skip the `:`.
+ match snapshot_pat.parse_pat_no_top_alt(expected) {
Err(inner_err) => {
- // Carry on as if we had not done anything, callers will emit a
- // reasonable error.
inner_err.cancel();
- err.cancel();
- self.restore_snapshot(snapshot);
}
Ok(mut pat) => {
// We've parsed the rest of the pattern.
_ => {}
}
if show_sugg {
- err.span_suggestion(
- span,
+ err.span_suggestion_verbose(
+ colon_span.until(self.look_ahead(1, |t| t.span)),
"maybe write a path separator here",
"::",
Applicability::MaybeIncorrect,
} else {
first_pat = self.mk_pat(new_span, PatKind::Wild);
}
- err.emit();
+ self.restore_snapshot(snapshot_pat);
}
}
+ match snapshot_type.parse_ty() {
+ Err(inner_err) => {
+ inner_err.cancel();
+ }
+ Ok(ty) => {
+ err.span_label(ty.span, "specifying the type of a pattern isn't supported");
+ self.restore_snapshot(snapshot_type);
+ let new_span = first_pat.span.to(ty.span);
+ first_pat = self.mk_pat(new_span, PatKind::Wild);
+ }
+ }
+ err.emit();
}
_ => {
// Carry on as if we had not done anything. This should be unreachable.
- self.restore_snapshot(snapshot);
}
};
first_pat
ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet,
FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
- IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
- InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
- InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
- InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
- LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
- MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
- MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
- NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
- OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
+ IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock,
+ IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator,
+ InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex,
+ InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported,
+ LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath,
+ MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg,
+ MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub,
+ MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator,
+ NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
pub(super) enum LhsExpr {
NotYetParsed,
AttributesParsed(AttrWrapper),
- AlreadyParsed(P<Expr>, bool), // (expr, starts_statement)
+ AlreadyParsed { expr: P<Expr>, starts_statement: bool },
}
impl From<Option<AttrWrapper>> for LhsExpr {
}
impl From<P<Expr>> for LhsExpr {
- /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed(expr)`.
+ /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`.
///
/// This conversion does not allocate.
fn from(expr: P<Expr>) -> Self {
- LhsExpr::AlreadyParsed(expr, false)
+ LhsExpr::AlreadyParsed { expr, starts_statement: false }
}
}
lhs: LhsExpr,
) -> PResult<'a, P<Expr>> {
let mut starts_stmt = false;
- let mut lhs = if let LhsExpr::AlreadyParsed(expr, starts_statement) = lhs {
+ let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs {
starts_stmt = starts_statement;
expr
} else {
// Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()
match this.token.uninterpolate().kind {
- token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), // `!expr`
- token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `~expr`
+ // `!expr`
+ token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)),
+ // `~expr`
+ token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)),
+ // `-expr`
token::BinOp(token::Minus) => {
make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Neg))
- } // `-expr`
+ }
+ // `*expr`
token::BinOp(token::Star) => {
make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Deref))
- } // `*expr`
+ }
+ // `&expr` and `&&expr`
token::BinOp(token::And) | token::AndAnd => {
make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo))
}
+ // `+lit`
token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {
let mut err =
LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None };
this.bump();
this.parse_prefix_expr(None)
- } // `+expr`
+ }
// Recover from `++x`:
token::BinOp(token::Plus)
if this.look_ahead(1, |t| *t == token::BinOp(token::Plus)) =>
Ok((span, self.mk_unary(op, expr)))
}
- // Recover on `!` suggesting for bitwise negation instead.
+ /// Recover on `~expr` in favor of `!expr`.
fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
self.sess.emit_err(TildeAsUnaryOperator(lo));
/// Recover on `not expr` in favor of `!expr`.
fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
- // Emit the error...
let negated_token = self.look_ahead(1, |t| t.clone());
let sub_diag = if negated_token.is_numeric_lit() {
),
});
- // ...and recover!
self.parse_unary_expr(lo, UnOp::Not)
}
err.span_label(sp, "while parsing this `loop` expression");
err
})
- } else if self.eat_keyword(kw::Continue) {
- let kind = ExprKind::Continue(self.eat_label());
- Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
} else if self.eat_keyword(kw::Match) {
let match_sp = self.prev_token.span;
self.parse_match_expr().map_err(|mut err| {
self.parse_try_block(lo)
} else if self.eat_keyword(kw::Return) {
self.parse_return_expr()
+ } else if self.eat_keyword(kw::Continue) {
+ self.parse_continue_expr(lo)
} else if self.eat_keyword(kw::Break) {
self.parse_break_expr()
} else if self.eat_keyword(kw::Yield) {
} else if self.eat(&token::Comma) {
// Vector with two or more elements.
let sep = SeqSep::trailing_allowed(token::Comma);
- let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;
- let mut exprs = vec![first_expr];
- exprs.extend(remaining_exprs);
+ let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;
+ exprs.insert(0, first_expr);
ExprKind::Array(exprs)
} else {
// Vector with one element
vis.0
};
- // Suggestion involves adding a (as of time of writing this, unstable) labeled block.
+ // Suggestion involves adding a labeled block.
//
// If there are no breaks that may use this label, suggest removing the label and
// recover to the unmodified expression.
fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let mut label = self.eat_label();
- let kind = if label.is_some() && self.token == token::Colon {
+ let kind = if self.token == token::Colon && let Some(label) = label.take() {
// The value expression can be a labeled loop, see issue #86948, e.g.:
// `loop { break 'label: loop { break 'label 42; }; }`
- let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
+ let lexpr = self.parse_labeled_expr(label, true)?;
self.sess.emit_err(LabeledLoopInBreak {
span: lexpr.span,
sub: WrapExpressionInParentheses {
} else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
- let expr = self.parse_expr_opt()?;
- if let Some(expr) = &expr {
+ let mut expr = self.parse_expr_opt()?;
+ if let Some(expr) = &mut expr {
if label.is_some()
&& matches!(
expr.kind,
BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span),
);
}
+
+ // Recover `break label aaaaa`
+ if self.may_recover()
+ && let ExprKind::Path(None, p) = &expr.kind
+ && let [segment] = &*p.segments
+ && let &ast::PathSegment { ident, args: None, .. } = segment
+ && let Some(next) = self.parse_expr_opt()?
+ {
+ label = Some(self.recover_ident_into_label(ident));
+ *expr = next;
+ }
}
+
expr
} else {
None
self.maybe_recover_from_bad_qpath(expr)
}
+ /// Parse `"continue" label?`.
+ fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
+ let mut label = self.eat_label();
+
+ // Recover `continue label` -> `continue 'label`
+ if self.may_recover()
+ && label.is_none()
+ && let Some((ident, _)) = self.token.ident()
+ {
+ self.bump();
+ label = Some(self.recover_ident_into_label(ident));
+ }
+
+ let kind = ExprKind::Continue(label);
+ Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
+ }
+
/// Parse `"yield" expr?`.
fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
if let ExprKind::Block(_, None) = right.kind => {
self.sess.emit_err(IfExpressionMissingThenBlock {
if_span: lo,
- sub: IfExpressionMissingThenBlockSub::UnfinishedCondition(
- cond_span.shrink_to_lo().to(*binop_span)
- ),
+ missing_then_block_sub:
+ IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)),
+ let_else_sub: None,
+
});
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
},
if let Some(block) = recover_block_from_condition(self) {
block
} else {
+ let let_else_sub = matches!(cond.kind, ExprKind::Let(..))
+ .then(|| IfExpressionLetSomeSub { if_span: lo });
+
self.sess.emit_err(IfExpressionMissingThenBlock {
if_span: lo,
- sub: IfExpressionMissingThenBlockSub::AddThenBlock(cond_span.shrink_to_hi()),
+ missing_then_block_sub: IfExpressionMissingThenBlockSub::AddThenBlock(
+ cond_span.shrink_to_hi(),
+ ),
+ let_else_sub,
});
self.mk_block_err(cond_span.shrink_to_hi())
}
false
}
+ /// Converts an ident into 'label and emits an "expected a label, found an identifier" error.
+ fn recover_ident_into_label(&mut self, ident: Ident) -> Label {
+ // Convert `label` -> `'label`,
+ // so that nameres doesn't complain about non-existing label
+ let label = format!("'{}", ident.name);
+ let ident = Ident { name: Symbol::intern(&label), span: ident.span };
+
+ self.struct_span_err(ident.span, "expected a label, found an identifier")
+ .span_suggestion(
+ ident.span,
+ "labels start with a tick",
+ label,
+ Applicability::MachineApplicable,
+ )
+ .emit();
+
+ Label { ident }
+ }
+
/// Parses `ident (COLON expr)?`.
fn parse_expr_field(&mut self) -> PResult<'a, ExprField> {
let attrs = self.parse_outer_attributes()?;
limits: RangeLimits,
) -> ExprKind {
if end.is_none() && limits == RangeLimits::Closed {
- self.inclusive_range_with_incorrect_end(self.prev_token.span);
+ self.inclusive_range_with_incorrect_end();
ExprKind::Err
} else {
ExprKind::Range(start, end, limits)
}
}
- /// Expect next token to be edible or inedible token. If edible,
+ /// Expect next token to be edible or inedible token. If edible,
/// then consume it; if inedible, then return without consuming
- /// anything. Signal a fatal error if next token is unexpected.
+ /// anything. Signal a fatal error if next token is unexpected.
pub fn expect_one_of(
&mut self,
edible: &[TokenKind],
use super::{ForceCollect, Parser, PathStyle, TrailingToken};
-use crate::errors::RemoveLet;
+use crate::errors::{
+ InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, RemoveLet,
+};
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
use rustc_ast::ptr::P;
PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
};
use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
use rustc_session::errors::ExprParenthesesNeeded;
use rustc_span::source_map::{respan, Span, Spanned};
use rustc_span::symbol::{kw, sym, Ident};
// Check if the user wrote `foo:bar` instead of `foo::bar`.
if ra == RecoverColon::Yes {
- first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected);
+ first_pat =
+ self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected);
}
if let Some(leading_vert_span) = leading_vert_span {
/// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
///
/// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs`
- /// should already have been parsed by now at this point,
+ /// should already have been parsed by now at this point,
/// if the next token is `@` then we can try to parse the more general form.
///
/// Consult `parse_pat_ident` for the `binding` grammar.
// Parsing e.g. `X..`.
if let RangeEnd::Included(_) = re.node {
// FIXME(Centril): Consider semantic errors instead in `ast_validation`.
- self.inclusive_range_with_incorrect_end(re.span);
+ self.inclusive_range_with_incorrect_end();
}
None
};
Ok(PatKind::Range(Some(begin), end, re))
}
- pub(super) fn inclusive_range_with_incorrect_end(&mut self, span: Span) {
+ pub(super) fn inclusive_range_with_incorrect_end(&mut self) {
let tok = &self.token;
-
+ let span = self.prev_token.span;
// If the user typed "..==" instead of "..=", we want to give them
// a specific error message telling them to use "..=".
+ // If they typed "..=>", suggest they use ".. =>".
// Otherwise, we assume that they meant to type a half open exclusive
// range and give them an error telling them to do that instead.
- if matches!(tok.kind, token::Eq) && tok.span.lo() == span.hi() {
- let span_with_eq = span.to(tok.span);
+ let no_space = tok.span.lo() == span.hi();
+ match tok.kind {
+ token::Eq if no_space => {
+ let span_with_eq = span.to(tok.span);
- // Ensure the user doesn't receive unhelpful unexpected token errors
- self.bump();
- if self.is_pat_range_end_start(0) {
- let _ = self.parse_pat_range_end().map_err(|e| e.cancel());
- }
+ // Ensure the user doesn't receive unhelpful unexpected token errors
+ self.bump();
+ if self.is_pat_range_end_start(0) {
+ let _ = self.parse_pat_range_end().map_err(|e| e.cancel());
+ }
- self.error_inclusive_range_with_extra_equals(span_with_eq);
- } else {
- self.error_inclusive_range_with_no_end(span);
+ self.error_inclusive_range_with_extra_equals(span_with_eq);
+ }
+ token::Gt if no_space => {
+ self.error_inclusive_range_match_arrow(span);
+ }
+ _ => self.error_inclusive_range_with_no_end(span),
}
}
fn error_inclusive_range_with_extra_equals(&self, span: Span) {
- self.struct_span_err(span, "unexpected `=` after inclusive range")
- .span_suggestion_short(span, "use `..=` instead", "..=", Applicability::MaybeIncorrect)
- .note("inclusive ranges end with a single equals sign (`..=`)")
- .emit();
+ self.sess.emit_err(InclusiveRangeExtraEquals { span });
+ }
+
+ fn error_inclusive_range_match_arrow(&self, span: Span) {
+ let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi();
+ self.sess.emit_err(InclusiveRangeMatchArrow { span, after_pat });
}
fn error_inclusive_range_with_no_end(&self, span: Span) {
- struct_span_err!(self.sess.span_diagnostic, span, E0586, "inclusive range with no end")
- .span_suggestion_short(span, "use `..` instead", "..", Applicability::MachineApplicable)
- .note("inclusive ranges must be bounded at the end (`..=b` or `a..=b`)")
- .emit();
+ self.sess.emit_err(InclusiveRangeNoEnd { span });
}
/// Parse a range-to pattern, `..X` or `..=X` where `X` remains to be parsed.
GenericArg::Const(self.parse_const_arg()?)
} else if self.check_type() {
// Parse type argument.
- let is_const_fn =
- self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis));
- let mut snapshot = self.create_snapshot_for_diagnostic();
+
+ // Proactively create a parser snapshot enabling us to rewind and try to reparse the
+ // input as a const expression in case we fail to parse a type. If we successfully
+ // do so, we will report an error that it needs to be wrapped in braces.
+ let mut snapshot = None;
+ if self.may_recover() && self.token.can_begin_expr() {
+ snapshot = Some(self.create_snapshot_for_diagnostic());
+ }
+
match self.parse_ty() {
- Ok(ty) => GenericArg::Type(ty),
+ Ok(ty) => {
+ // Since the type parser recovers from some malformed slice and array types and
+ // successfully returns a type, we need to look for `TyKind::Err`s in the
+ // type to determine if error recovery has occurred and if the input is not a
+ // syntactically valid type after all.
+ if let ast::TyKind::Slice(inner_ty) | ast::TyKind::Array(inner_ty, _) = &ty.kind
+ && let ast::TyKind::Err = inner_ty.kind
+ && let Some(snapshot) = snapshot
+ && let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot)
+ {
+ return Ok(Some(self.dummy_const_arg_needs_braces(
+ self.struct_span_err(expr.span, "invalid const generic expression"),
+ expr.span,
+ )));
+ }
+
+ GenericArg::Type(ty)
+ }
Err(err) => {
- if is_const_fn {
- match (*snapshot).parse_expr_res(Restrictions::CONST_EXPR, None) {
- Ok(expr) => {
- self.restore_snapshot(snapshot);
- return Ok(Some(self.dummy_const_arg_needs_braces(err, expr.span)));
- }
- Err(err) => {
- err.cancel();
- }
- }
+ if let Some(snapshot) = snapshot
+ && let Some(expr) = self.recover_unbraced_const_arg_that_can_begin_ty(snapshot)
+ {
+ return Ok(Some(self.dummy_const_arg_needs_braces(
+ err,
+ expr.span,
+ )));
}
// Try to recover from possible `const` arg without braces.
return self.recover_const_arg(start, err).map(Some);
// Perform this outside of the `collect_tokens_trailing_token` closure,
// since our outer attributes do not apply to this part of the expression
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
- this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr, true))
+ this.parse_assoc_expr_with(
+ 0,
+ LhsExpr::AlreadyParsed { expr, starts_statement: true },
+ )
})?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
} else {
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
let e = self.maybe_recover_from_bad_qpath(e)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
- let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e, false))?;
+ let e = self.parse_assoc_expr_with(
+ 0,
+ LhsExpr::AlreadyParsed { expr: e, starts_statement: false },
+ )?;
StmtKind::Expr(e)
};
Ok(self.mk_stmt(lo.to(hi), kind))
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
};
+use rustc_ast_pretty::pprust;
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident};
No,
}
-#[derive(PartialEq)]
+#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQPath {
Yes,
No,
}
+#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQuestionMark {
Yes,
No,
}
+#[derive(PartialEq, Clone, Copy)]
+pub(super) enum RecoverAnonEnum {
+ Yes,
+ No,
+}
+
/// Signals whether parsing a type should recover `->`.
///
/// More specifically, when parsing a function like:
}
// Is `...` (`CVarArgs`) legal at this level of type parsing?
-#[derive(PartialEq)]
+#[derive(PartialEq, Clone, Copy)]
enum AllowCVariadic {
Yes,
No,
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
Some(ty_params),
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::Yes,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
+ RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
+ RecoverAnonEnum::No,
)
}
RecoverReturnSign::OnlyFatArrow,
None,
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::No,
)
}
recover_return_sign,
None,
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) {
recover_return_sign,
None,
RecoverQuestionMark::Yes,
+ RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else {
recover_return_sign: RecoverReturnSign,
ty_generics: Option<&Generics>,
recover_question_mark: RecoverQuestionMark,
+ recover_anon_enum: RecoverAnonEnum,
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
let mut ty = self.mk_ty(span, kind);
// Try to recover from use of `+` with incorrect priority.
- if matches!(allow_plus, AllowPlus::Yes) {
+ if allow_plus == AllowPlus::Yes {
self.maybe_recover_from_bad_type_plus(&ty)?;
} else {
self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty);
}
- if let RecoverQuestionMark::Yes = recover_question_mark {
+ if RecoverQuestionMark::Yes == recover_question_mark {
ty = self.maybe_recover_from_question_mark(ty);
}
+ if recover_anon_enum == RecoverAnonEnum::Yes
+ && self.check_noexpect(&token::BinOp(token::Or))
+ && self.look_ahead(1, |t| t.can_begin_type())
+ {
+ let mut pipes = vec![self.token.span];
+ let mut types = vec![ty];
+ loop {
+ if !self.eat(&token::BinOp(token::Or)) {
+ break;
+ }
+ pipes.push(self.prev_token.span);
+ types.push(self.parse_ty_common(
+ allow_plus,
+ allow_c_variadic,
+ recover_qpath,
+ recover_return_sign,
+ ty_generics,
+ recover_question_mark,
+ RecoverAnonEnum::No,
+ )?);
+ }
+ let mut err = self.struct_span_err(pipes, "anonymous enums are not supported");
+ for ty in &types {
+ err.span_label(ty.span, "");
+ }
+ err.help(&format!(
+ "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}",
+ types
+ .iter()
+ .enumerate()
+ .map(|(i, t)| format!(
+ " Variant{}({}),",
+ i + 1, // Lets not confuse people with zero-indexing :)
+ pprust::to_string(|s| s.print_type(&t)),
+ ))
+ .collect::<Vec<_>>()
+ .join("\n"),
+ ));
+ err.emit();
+ return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err));
+ }
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
}
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
+ // In addition to looping while we find generic bounds:
+ // We continue even if we find a keyword. This is necessary for error recovery on,
+ // for example, `impl fn()`. The only keyword that can go after generic bounds is
+ // `where`, so stop if it's it.
+ // We also continue if we find types (not traits), again for error recovery.
while self.can_begin_bound()
- // Continue even if we find a keyword.
- // This is necessary for error recover on, for example, `impl fn()`.
- //
- // The only keyword that can go after generic bounds is `where`, so stop if it's it.
+ || self.token.can_begin_type()
|| (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
{
if self.token.is_keyword(kw::Dyn) {
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& let Some(path) = self.recover_path_from_fn()
{
+ path
+ } else if !self.token.is_path_start() && self.token.can_begin_type() {
+ let ty = self.parse_ty_no_plus()?;
+ // Instead of finding a path (a trait), we found a type.
+ let mut err = self.struct_span_err(ty.span, "expected a trait, found type");
+
+ // If we can recover, try to extract a path from the type. Note
+ // that we do not use the try operator when parsing the type because
+ // if it fails then we get a parser error which we don't want (we're trying
+ // to recover from errors, not make more).
+ let path = if self.may_recover()
+ && matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..))
+ && let TyKind::Path(_, path) = &ty.peel_refs().kind {
+ // Just get the indirection part of the type.
+ let span = ty.span.until(path.span);
+
+ err.span_suggestion_verbose(
+ span,
+ "consider removing the indirection",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+
+ path.clone()
+ } else {
+ return Err(err);
+ };
+
+ err.emit();
+
path
} else {
self.parse_path(PathStyle::Type)?
self.parse_remaining_bounds(bounds, true)?;
self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
let sp = vec![lo, self.prev_token.span];
- let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
+ let sugg = vec![(lo, String::from(" ")), (self.prev_token.span, String::new())];
self.struct_span_err(sp, "incorrect braces around trait bounds")
.multipart_suggestion(
"remove the parentheses",
pub use Alignment::*;
pub use Count::*;
-pub use Flag::*;
pub use Piece::*;
pub use Position::*;
-use rustc_lexer::unescape;
use std::iter;
use std::str;
use std::string;
pub fill: Option<char>,
/// Optionally specified alignment.
pub align: Alignment,
- /// Packed version of various flags provided.
- pub flags: u32,
+ /// The `+` or `-` flag.
+ pub sign: Option<Sign>,
+ /// The `#` flag.
+ pub alternate: bool,
+ /// The `0` flag.
+ pub zero_pad: bool,
+ /// The `x` or `X` flag. (Only for `Debug`.)
+ pub debug_hex: Option<DebugHex>,
/// The integer precision to use.
pub precision: Count<'a>,
/// The span of the precision formatting flag (for diagnostics).
AlignUnknown,
}
-/// Various flags which can be applied to format strings. The meaning of these
-/// flags is defined by the formatters themselves.
+/// Enum for the sign flags.
#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum Flag {
- /// A `+` will be used to denote positive numbers.
- FlagSignPlus,
- /// A `-` will be used to denote negative numbers. This is the default.
- FlagSignMinus,
- /// An alternate form will be used for the value. In the case of numbers,
- /// this means that the number will be prefixed with the supplied string.
- FlagAlternate,
- /// For numbers, this means that the number will be padded with zeroes,
- /// and the sign (`+` or `-`) will precede them.
- FlagSignAwareZeroPad,
- /// For Debug / `?`, format integers in lower-case hexadecimal.
- FlagDebugLowerHex,
- /// For Debug / `?`, format integers in upper-case hexadecimal.
- FlagDebugUpperHex,
+pub enum Sign {
+ /// The `+` flag.
+ Plus,
+ /// The `-` flag.
+ Minus,
+}
+
+/// Enum for the debug hex flags.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum DebugHex {
+ /// The `x` flag in `{:x?}`.
+ Lower,
+ /// The `X` flag in `{:X?}`.
+ Upper,
}
/// A count is used for the precision and width parameters of an integer, and
append_newline: bool,
mode: ParseMode,
) -> Parser<'a> {
- let input_string_kind = find_width_map_from_snippet(s, snippet, style);
+ let input_string_kind = find_width_map_from_snippet(snippet, style);
let (width_map, is_literal) = match input_string_kind {
InputStringKind::Literal { width_mappings } => (width_mappings, true),
InputStringKind::NotALiteral => (Vec::new(), false),
};
+
Parser {
mode,
input: s,
let mut spec = FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
precision_span: None,
width: CountImplied,
}
// Sign flags
if self.consume('+') {
- spec.flags |= 1 << (FlagSignPlus as u32);
+ spec.sign = Some(Sign::Plus);
} else if self.consume('-') {
- spec.flags |= 1 << (FlagSignMinus as u32);
+ spec.sign = Some(Sign::Minus);
}
// Alternate marker
if self.consume('#') {
- spec.flags |= 1 << (FlagAlternate as u32);
+ spec.alternate = true;
}
// Width and precision
let mut havewidth = false;
spec.width_span = Some(self.span(end - 1, end + 1));
havewidth = true;
} else {
- spec.flags |= 1 << (FlagSignAwareZeroPad as u32);
+ spec.zero_pad = true;
}
}
// Optional radix followed by the actual format specifier
if self.consume('x') {
if self.consume('?') {
- spec.flags |= 1 << (FlagDebugLowerHex as u32);
+ spec.debug_hex = Some(DebugHex::Lower);
spec.ty = "?";
} else {
spec.ty = "x";
}
} else if self.consume('X') {
if self.consume('?') {
- spec.flags |= 1 << (FlagDebugUpperHex as u32);
+ spec.debug_hex = Some(DebugHex::Upper);
spec.ty = "?";
} else {
spec.ty = "X";
let mut spec = FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
precision_span: None,
width: CountImplied,
/// written code (code snippet) and the `InternedString` that gets processed in the `Parser`
/// in order to properly synthesise the intra-string `Span`s for error diagnostics.
fn find_width_map_from_snippet(
- input: &str,
snippet: Option<string::String>,
str_style: Option<usize>,
) -> InputStringKind {
return InputStringKind::Literal { width_mappings: Vec::new() };
}
- // Strip quotes.
let snippet = &snippet[1..snippet.len() - 1];
- // Macros like `println` add a newline at the end. That technically doens't make them "literals" anymore, but it's fine
- // since we will never need to point our spans there, so we lie about it here by ignoring it.
- // Since there might actually be newlines in the source code, we need to normalize away all trailing newlines.
- // If we only trimmed it off the input, `format!("\n")` would cause a mismatch as here we they actually match up.
- // Alternatively, we could just count the trailing newlines and only trim one from the input if they don't match up.
- let input_no_nl = input.trim_end_matches('\n');
- let Ok(unescaped) = unescape_string(snippet) else {
- return InputStringKind::NotALiteral;
- };
-
- let unescaped_no_nl = unescaped.trim_end_matches('\n');
-
- if unescaped_no_nl != input_no_nl {
- // The source string that we're pointing at isn't our input, so spans pointing at it will be incorrect.
- // This can for example happen with proc macros that respan generated literals.
- return InputStringKind::NotALiteral;
- }
-
let mut s = snippet.char_indices();
let mut width_mappings = vec![];
while let Some((pos, c)) = s.next() {
InputStringKind::Literal { width_mappings }
}
-fn unescape_string(string: &str) -> Result<string::String, unescape::EscapeError> {
- let mut buf = string::String::new();
- let mut error = Ok(());
- unescape::unescape_literal(string, unescape::Mode::Str, &mut |_, unescaped_char| {
- match unescaped_char {
- Ok(c) => buf.push(c),
- Err(err) => error = Err(err),
- }
- });
-
- error.map(|_| buf)
-}
-
// Assert a reasonable size for `Piece`
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(Piece<'_>, 16);
return FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: None,
align: AlignRight,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: Some('0'),
align: AlignLeft,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: Some('*'),
align: AlignLeft,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
precision_span: None,
width: CountIs(10),
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountIs(10),
precision_span: Some(InnerSpan { start: 6, end: 9 }),
width: CountIsParam(10),
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountIs(10),
precision_span: Some(InnerSpan { start: 6, end: 9 }),
width: CountIsParam(0),
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountIsStar(0),
precision_span: Some(InnerSpan { start: 3, end: 5 }),
width: CountImplied,
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountIsParam(10),
width: CountImplied,
precision_span: Some(InnerSpan::new(3, 7)),
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountIsName("b", InnerSpan { start: 6, end: 7 }),
precision_span: Some(InnerSpan { start: 5, end: 8 }),
width: CountIsName("a", InnerSpan { start: 3, end: 4 }),
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountIs(4),
precision_span: Some(InnerSpan { start: 3, end: 5 }),
width: CountImplied,
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: (1 << FlagSignMinus as u32),
+ sign: Some(Sign::Minus),
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: (1 << FlagSignPlus as u32) | (1 << FlagAlternate as u32),
+ sign: Some(Sign::Plus),
+ alternate: true,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
format: FormatSpec {
fill: None,
align: AlignUnknown,
- flags: 0,
+ sign: None,
+ alternate: false,
+ zero_pad: false,
+ debug_hex: None,
precision: CountImplied,
width: CountImplied,
precision_span: None,
use crate::errors::{
self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
- OnlyHasEffectOn, TransparentIncompatible, UnrecognizedReprHint,
+ OnlyHasEffectOn, ProcMacroDiffArguments, ProcMacroInvalidAbi, ProcMacroMissingArguments,
+ ProcMacroTypeError, ProcMacroUnsafe, TransparentIncompatible, UnrecognizedReprHint,
};
use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{fluent, Applicability, MultiSpan};
+use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan};
use rustc_expand::base::resolve_path;
use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
use rustc_hir as hir;
use rustc_hir::{
self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
};
-use rustc_hir::{MethodKind, Target};
+use rustc_hir::{MethodKind, Target, Unsafety};
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::resolve_lifetime::ObjectLifetimeDefault;
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{ParamEnv, TyCtxt};
use rustc_session::lint::builtin::{
CONFLICTING_REPR_HINTS, INVALID_DOC_ATTRIBUTES, UNUSED_ATTRIBUTES,
};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
+use std::cell::Cell;
use std::collections::hash_map::Entry;
pub(crate) fn target_from_impl_item<'tcx>(
ForeignItem,
}
+#[derive(Copy, Clone)]
+pub(crate) enum ProcMacroKind {
+ FunctionLike,
+ Derive,
+ Attribute,
+}
+
+impl IntoDiagnosticArg for ProcMacroKind {
+ fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
+ match self {
+ ProcMacroKind::Attribute => "attribute proc macro",
+ ProcMacroKind::Derive => "derive proc macro",
+ ProcMacroKind::FunctionLike => "function-like proc macro",
+ }
+ .into_diagnostic_arg()
+ }
+}
+
struct CheckAttrVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
+
+ // Whether or not this visitor should abort after finding errors
+ abort: Cell<bool>,
}
impl CheckAttrVisitor<'_> {
sym::path => self.check_generic_attr(hir_id, attr, target, Target::Mod),
sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
sym::macro_export => self.check_macro_export(hir_id, attr, target),
- sym::ignore | sym::should_panic | sym::proc_macro_derive => {
+ sym::ignore | sym::should_panic => {
self.check_generic_attr(hir_id, attr, target, Target::Fn)
}
sym::automatically_derived => {
self.check_generic_attr(hir_id, attr, target, Target::Mod)
}
sym::rustc_object_lifetime_default => self.check_object_lifetime_default(hir_id),
+ sym::proc_macro => {
+ self.check_proc_macro(hir_id, target, ProcMacroKind::FunctionLike)
+ }
+ sym::proc_macro_attribute => {
+ self.check_proc_macro(hir_id, target, ProcMacroKind::Attribute);
+ }
+ sym::proc_macro_derive => {
+ self.check_generic_attr(hir_id, attr, target, Target::Fn);
+ self.check_proc_macro(hir_id, target, ProcMacroKind::Derive)
+ }
_ => {}
}
/// Debugging aid for `object_lifetime_default` query.
fn check_object_lifetime_default(&self, hir_id: HirId) {
let tcx = self.tcx;
- if let Some(generics) = tcx.hir().get_generics(tcx.hir().local_def_id(hir_id)) {
+ if let Some(owner_id) = hir_id.as_owner()
+ && let Some(generics) = tcx.hir().get_generics(owner_id.def_id)
+ {
for p in generics.params {
let hir::GenericParamKind::Type { .. } = p.kind else { continue };
let default = tcx.object_lifetime_default(p.def_id);
) -> bool {
match target {
Target::Fn | Target::Method(_)
- if self.tcx.is_const_fn_raw(self.tcx.hir().local_def_id(hir_id).to_def_id()) =>
+ if self.tcx.is_const_fn_raw(hir_id.expect_owner().to_def_id()) =>
{
true
}
errors::Unused { attr_span: attr.span, note },
);
}
+
+ /// A best effort attempt to create an error for a mismatching proc macro signature.
+ ///
+ /// If this best effort goes wrong, it will just emit a worse error later (see #102923)
+ fn check_proc_macro(&self, hir_id: HirId, target: Target, kind: ProcMacroKind) {
+ let expected_input_count = match kind {
+ ProcMacroKind::Attribute => 2,
+ ProcMacroKind::Derive | ProcMacroKind::FunctionLike => 1,
+ };
+
+ let expected_signature = match kind {
+ ProcMacroKind::Attribute => "fn(TokenStream, TokenStream) -> TokenStream",
+ ProcMacroKind::Derive | ProcMacroKind::FunctionLike => "fn(TokenStream) -> TokenStream",
+ };
+
+ let tcx = self.tcx;
+ if target == Target::Fn {
+ let Some(tokenstream) = tcx.get_diagnostic_item(sym::TokenStream) else {return};
+ let tokenstream = tcx.type_of(tokenstream);
+
+ let id = hir_id.expect_owner();
+ let hir_sig = tcx.hir().fn_sig_by_hir_id(hir_id).unwrap();
+
+ let sig =
+ tcx.liberate_late_bound_regions(id.to_def_id(), tcx.fn_sig(id).subst_identity());
+ let sig = tcx.normalize_erasing_regions(ParamEnv::empty(), sig);
+
+ // We don't currently require that the function signature is equal to
+ // `fn(TokenStream) -> TokenStream`, but instead monomorphizes to
+ // `fn(TokenStream) -> TokenStream` after some substitution of generic arguments.
+ //
+ // Properly checking this means pulling in additional `rustc` crates, so we don't.
+ let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
+
+ if sig.abi != Abi::Rust {
+ tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
+ self.abort.set(true);
+ }
+
+ if sig.unsafety == Unsafety::Unsafe {
+ tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
+ self.abort.set(true);
+ }
+
+ let output = sig.output();
+
+ // Typecheck the output
+ if !drcx.types_may_unify(output, tokenstream) {
+ tcx.sess.emit_err(ProcMacroTypeError {
+ span: hir_sig.decl.output.span(),
+ found: output,
+ kind,
+ expected_signature,
+ });
+ self.abort.set(true);
+ }
+
+ if sig.inputs().len() < expected_input_count {
+ tcx.sess.emit_err(ProcMacroMissingArguments {
+ expected_input_count,
+ span: hir_sig.span,
+ kind,
+ expected_signature,
+ });
+ self.abort.set(true);
+ }
+
+ // Check that the inputs are correct, if there are enough.
+ if sig.inputs().len() >= expected_input_count {
+ for (arg, input) in
+ sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
+ {
+ if !drcx.types_may_unify(*arg, tokenstream) {
+ tcx.sess.emit_err(ProcMacroTypeError {
+ span: input.span,
+ found: *arg,
+ kind,
+ expected_signature,
+ });
+ self.abort.set(true);
+ }
+ }
+ }
+
+ // Check that there are not too many arguments
+ let body_id = tcx.hir().body_owned_by(id.def_id);
+ let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
+ if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
+ tcx.sess.emit_err(ProcMacroDiffArguments {
+ span: begin.span.to(end.span),
+ count: excess.len(),
+ kind,
+ expected_signature,
+ });
+ self.abort.set(true);
+ }
+ }
+ }
}
impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
}
fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
- let check_attr_visitor = &mut CheckAttrVisitor { tcx };
+ let check_attr_visitor = &mut CheckAttrVisitor { tcx, abort: Cell::new(false) };
tcx.hir().visit_item_likes_in_module(module_def_id, check_attr_visitor);
if module_def_id.is_top_level_module() {
check_attr_visitor.check_attributes(CRATE_HIR_ID, DUMMY_SP, Target::Mod, None);
check_invalid_crate_level_attr(tcx, tcx.hir().krate_attrs());
}
+ if check_attr_visitor.abort.get() {
+ tcx.sess.abort_if_errors()
+ }
}
pub(crate) fn provide(providers: &mut Providers) {
Self::Match(TryDesugar) => &[sym::const_try],
// All other expressions are allowed.
- Self::Loop(Loop | While) | Self::Match(Normal) => &[],
+ Self::Loop(Loop | While) | Self::Match(Normal | FormatArgs) => &[],
};
Some(gates)
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
{
- let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap();
+ let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().subst_identity();
if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind()
&& let Some(adt_def_id) = adt_def.did().as_local()
{
// referenced by it should be considered as used.
let in_pat = mem::replace(&mut self.in_pat, false);
- self.live_symbols.insert(self.tcx.hir().local_def_id(c.hir_id));
+ self.live_symbols.insert(c.def_id);
intravisit::walk_anon_const(self, c);
self.in_pat = in_pat;
}
}
-fn has_allow_dead_code_or_lang_attr_helper(
- tcx: TyCtxt<'_>,
- id: hir::HirId,
- lint: &'static lint::Lint,
-) -> bool {
- let attrs = tcx.hir().attrs(id);
- if tcx.sess.contains_name(attrs, sym::lang) {
+fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
+ if tcx.has_attr(def_id.to_def_id(), sym::lang) {
return true;
}
// Stable attribute for #[lang = "panic_impl"]
- if tcx.sess.contains_name(attrs, sym::panic_handler) {
+ if tcx.has_attr(def_id.to_def_id(), sym::panic_handler) {
return true;
}
- let def_id = tcx.hir().local_def_id(id);
if tcx.def_kind(def_id).has_codegen_attrs() {
let cg_attrs = tcx.codegen_fn_attrs(def_id);
}
}
- tcx.lint_level_at_node(lint, id).0 == lint::Allow
-}
-
-fn has_allow_dead_code_or_lang_attr(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
- has_allow_dead_code_or_lang_attr_helper(tcx, id, lint::builtin::DEAD_CODE)
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).0 == lint::Allow
}
// These check_* functions seeds items that
struct_constructors: &mut FxHashMap<LocalDefId, LocalDefId>,
id: hir::ItemId,
) {
- let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.hir_id());
+ let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id);
if allow_dead_code {
worklist.push(id.owner_id.def_id);
}
// And we access the Map here to get HirId from LocalDefId
for id in local_def_ids {
- if of_trait.is_some()
- || has_allow_dead_code_or_lang_attr(tcx, tcx.hir().local_def_id_to_hir_id(id))
- {
+ if of_trait.is_some() || has_allow_dead_code_or_lang_attr(tcx, id) {
worklist.push(id);
}
}
DefKind::Struct => {
let item = tcx.hir().item(id);
if let hir::ItemKind::Struct(ref variant_data, _) = item.kind
- && let Some(ctor_hir_id) = variant_data.ctor_hir_id()
+ && let Some(ctor_def_id) = variant_data.ctor_def_id()
{
- struct_constructors.insert(tcx.hir().local_def_id(ctor_hir_id), item.owner_id.def_id);
+ struct_constructors.insert(ctor_def_id, item.owner_id.def_id);
}
}
DefKind::GlobalAsm => {
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
let trait_item = tcx.hir().trait_item(id);
if matches!(trait_item.kind, Const(_, Some(_)) | Fn(_, hir::TraitFn::Provided(_)))
- && has_allow_dead_code_or_lang_attr(tcx, trait_item.hir_id())
+ && has_allow_dead_code_or_lang_attr(tcx, trait_item.owner_id.def_id)
{
worklist.push(trait_item.owner_id.def_id);
}
fn check_foreign_item(tcx: TyCtxt<'_>, worklist: &mut Vec<LocalDefId>, id: hir::ForeignItemId) {
if matches!(tcx.def_kind(id.owner_id), DefKind::Static(_) | DefKind::Fn)
- && has_allow_dead_code_or_lang_attr(tcx, id.hir_id())
+ && has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id)
{
worklist.push(id.owner_id.def_id);
}
if self.live_symbols.contains(&def_id) {
return;
}
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- if has_allow_dead_code_or_lang_attr(self.tcx, hir_id) {
+ if has_allow_dead_code_or_lang_attr(self.tcx, def_id) {
return;
}
let Some(name) = self.tcx.opt_item_name(def_id.to_def_id()) else {
// There is no main function.
let mut has_filename = true;
- let filename = tcx.sess.local_crate_source_file.clone().unwrap_or_else(|| {
+ let filename = tcx.sess.local_crate_source_file().unwrap_or_else(|| {
has_filename = false;
Default::default()
});
use rustc_middle::ty::{MainDefinition, Ty};
use rustc_span::{Span, Symbol, DUMMY_SP};
+use crate::check_attr::ProcMacroKind;
use crate::lang_items::Duplicate;
#[derive(Diagnostic)]
#[suggestion_part(code = "()")]
pub spans: Vec<Span>,
}
+
+#[derive(Diagnostic)]
+#[diag(passes_proc_macro_typeerror)]
+#[note]
+pub(crate) struct ProcMacroTypeError<'tcx> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub found: Ty<'tcx>,
+ pub kind: ProcMacroKind,
+ pub expected_signature: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_proc_macro_diff_arg_count)]
+pub(crate) struct ProcMacroDiffArguments {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub count: usize,
+ pub kind: ProcMacroKind,
+ pub expected_signature: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_proc_macro_missing_args)]
+pub(crate) struct ProcMacroMissingArguments {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub expected_input_count: usize,
+ pub kind: ProcMacroKind,
+ pub expected_signature: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_proc_macro_invalid_abi)]
+pub(crate) struct ProcMacroInvalidAbi {
+ #[primary_span]
+ pub span: Span,
+ pub abi: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(passes_proc_macro_unsafe)]
+pub(crate) struct ProcMacroUnsafe {
+ #[primary_span]
+ pub span: Span,
+}
use rustc_middle::hir::map::Map;
use rustc_middle::ty::TyCtxt;
use rustc_middle::util::common::to_readable_str;
+use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
fd: &'v hir::FnDecl<'v>,
b: hir::BodyId,
_: Span,
- id: hir::HirId,
+ id: LocalDefId,
) {
self.record("FnDecl", Id::None, fd);
hir_visit::walk_fn(self, fk, fd, b, id)
Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
- InlineAsm, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
+ InlineAsm, FormatArgs, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, IncludedBytes, Err
]
);
ast_visit::walk_expr(self, e)
use rustc_hir as hir;
use rustc_hir::def::DefKind;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::{extract, GenericRequirement};
-use rustc_hir::{HirId, LangItem, LanguageItems, Target};
+use rustc_hir::{LangItem, LanguageItems, Target};
use rustc_middle::ty::TyCtxt;
use rustc_session::cstore::ExternCrate;
use rustc_span::{symbol::kw::Empty, Span};
LanguageItemCollector { tcx, items: LanguageItems::new() }
}
- fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) {
- let attrs = self.tcx.hir().attrs(hir_id);
+ fn check_for_lang(&mut self, actual_target: Target, def_id: LocalDefId) {
+ let attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
if let Some((name, span)) = extract(&attrs) {
match LangItem::from_name(name) {
// Known lang item with attribute on correct target.
Some(lang_item) if actual_target == lang_item.target() => {
- self.collect_item_extended(lang_item, hir_id, span);
+ self.collect_item_extended(lang_item, def_id, span);
}
// Known lang item with attribute on incorrect target.
Some(lang_item) => {
// Like collect_item() above, but also checks whether the lang item is declared
// with the right number of generic arguments.
- fn collect_item_extended(&mut self, lang_item: LangItem, hir_id: HirId, span: Span) {
- let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id();
+ fn collect_item_extended(&mut self, lang_item: LangItem, item_def_id: LocalDefId, span: Span) {
let name = lang_item.name();
// Now check whether the lang_item has the expected number of generic
// Some other types like Box and various functions like drop_in_place
// have minimum requirements.
- if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) = self.tcx.hir().get(hir_id)
+ if let hir::Node::Item(hir::Item { kind, span: item_span, .. }) =
+ self.tcx.hir().get_by_def_id(item_def_id)
{
let (actual_num, generics_span) = match kind.generics() {
Some(generics) => (generics.params.len(), generics.span),
}
}
- self.collect_item(lang_item, item_def_id);
+ self.collect_item(lang_item, item_def_id.to_def_id());
}
}
let crate_items = tcx.hir_crate_items(());
for id in crate_items.items() {
- collector.check_for_lang(Target::from_def_kind(tcx.def_kind(id.owner_id)), id.hir_id());
+ collector
+ .check_for_lang(Target::from_def_kind(tcx.def_kind(id.owner_id)), id.owner_id.def_id);
if matches!(tcx.def_kind(id.owner_id), DefKind::Enum) {
let item = tcx.hir().item(id);
if let hir::ItemKind::Enum(def, ..) = &item.kind {
for variant in def.variants {
- collector.check_for_lang(Target::Variant, variant.hir_id);
+ collector.check_for_lang(Target::Variant, variant.def_id);
}
}
}
// FIXME: avoid calling trait_item() when possible
for id in crate_items.trait_items() {
let item = tcx.hir().trait_item(id);
- collector.check_for_lang(Target::from_trait_item(item), item.hir_id())
+ collector.check_for_lang(Target::from_trait_item(item), item.owner_id.def_id)
}
// FIXME: avoid calling impl_item() when possible
for id in crate_items.impl_items() {
let item = tcx.hir().impl_item(id);
- collector.check_for_lang(target_from_impl_item(tcx, item), item.hir_id())
+ collector.check_for_lang(target_from_impl_item(tcx, item), item.owner_id.def_id)
}
// Extract out the found lang items.
}
fn lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures {
+ // If `staged_api` is not enabled then we aren't allowed to define lib
+ // features; there is no point collecting them.
+ if !tcx.features().staged_api {
+ return new_lib_features();
+ }
+
let mut collector = LibFeatureCollector::new(tcx);
tcx.hir().walk_attributes(&mut collector);
collector.lib_features
// Creating ir_maps
//
// This is the first pass and the one that drives the main
-// computation. It walks up and down the IR once. On the way down,
+// computation. It walks up and down the IR once. On the way down,
// we count for each function the number of variables as well as
-// liveness nodes. A liveness node is basically an expression or
+// liveness nodes. A liveness node is basically an expression or
// capture clause that does something of interest: either it has
// interesting control flow or it uses/defines a local variable.
//
// of live variables at each program point.
//
// Finally, we run back over the IR one last time and, using the
-// computed liveness, check various safety conditions. For example,
+// computed liveness, check various safety conditions. For example,
// there must be no live nodes at the definition site for a variable
-// unless it has an initializer. Similarly, each non-mutable local
+// unless it has an initializer. Similarly, each non-mutable local
// variable must not be assigned if there is some successor
-// assignment. And so forth.
+// assignment. And so forth.
struct CaptureInfo {
ln: LiveNode,
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
// Make a live_node for each mentioned variable, with the span
- // being the location that the variable is used. This results
+ // being the location that the variable is used. This results
// in better error messages than just pointing at the closure
// construction site.
let mut call_caps = Vec::new();
match stmt.kind {
hir::StmtKind::Local(ref local) => {
// Note: we mark the variable as defined regardless of whether
- // there is an initializer. Initially I had thought to only mark
+ // there is an initializer. Initially I had thought to only mark
// the live variable as defined if it was initialized, and then we
// could check for uninit variables just by scanning what is live
// at the start of the function. But that doesn't work so well for
//
// # Tracked places
//
- // A tracked place is a local variable/argument `x`. In
+ // A tracked place is a local variable/argument `x`. In
// these cases, the link_node where the write occurs is linked
- // to node id of `x`. The `write_place()` routine generates
- // the contents of this node. There are no subcomponents to
+ // to node id of `x`. The `write_place()` routine generates
+ // the contents of this node. There are no subcomponents to
// consider.
//
// # Non-tracked places
//
- // These are places like `x[5]` or `x.f`. In that case, we
+ // These are places like `x[5]` or `x.f`. In that case, we
// basically ignore the value which is written to but generate
- // reads for the components---`x` in these two examples. The
+ // reads for the components---`x` in these two examples. The
// components reads are generated by
// `propagate_through_place_components()` (this fn).
//
// # Illegal places
//
// It is still possible to observe assignments to non-places;
- // these errors are detected in the later pass borrowck. We
+ // these errors are detected in the later pass borrowck. We
// just ignore such cases and treat them as reads.
match expr.kind {
}
// We do not track other places, so just propagate through
- // to their subcomponents. Also, it may happen that
+ // to their subcomponents. Also, it may happen that
// non-places occur here, because those are detected in the
// later pass borrowck.
_ => succ,
}
if !self.tcx.features().staged_api {
- // Propagate unstability. This can happen even for non-staged-api crates in case
+ // Propagate unstability. This can happen even for non-staged-api crates in case
// -Zforce-unstable-if-unmarked is set.
if let Some(stab) = self.parent_stab {
if inherit_deprecation.yes() && stab.is_unstable() {
/// See issue #94972 for details on why this is a special case
fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
// Get the LocalDefId so we can lookup the item to check the kind.
- let Some(def_id) = tcx.hir().opt_local_def_id(id) else { return false; };
+ let Some(owner) = id.as_owner() else { return false; };
+ let def_id = owner.def_id;
let Some(stab) = tcx.stability().local_stability(def_id) else {
return false;
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(associated_type_defaults)]
-#![feature(control_flow_enum)]
#![feature(rustc_private)]
#![feature(try_blocks)]
#![feature(let_chains)]
use rustc_data_structures::intern::Interned;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AssocItemKind, HirIdSet, ItemId, Node, PatKind};
use rustc_middle::bug;
fn visit_trait(&mut self, trait_ref: TraitRef<'tcx>) -> ControlFlow<V::BreakTy> {
let TraitRef { def_id, substs, .. } = trait_ref;
self.def_id_visitor.visit_def_id(def_id, "trait", &trait_ref.print_only_trait_path())?;
- if self.def_id_visitor.shallow() { ControlFlow::CONTINUE } else { substs.visit_with(self) }
+ if self.def_id_visitor.shallow() {
+ ControlFlow::Continue(())
+ } else {
+ substs.visit_with(self)
+ }
}
fn visit_projection_ty(&mut self, projection: ty::AliasTy<'tcx>) -> ControlFlow<V::BreakTy> {
};
self.visit_trait(trait_ref)?;
if self.def_id_visitor.shallow() {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
assoc_substs.iter().try_for_each(|subst| subst.visit_with(self))
}
ty,
_region,
))) => ty.visit_with(self),
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => ControlFlow::CONTINUE,
+ ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..)) => ControlFlow::Continue(()),
ty::PredicateKind::ConstEvaluatable(ct) => ct.visit_with(self),
ty::PredicateKind::WellFormed(arg) => arg.visit_with(self),
_ => bug!("unexpected predicate: {:?}", predicate),
| ty::Generator(def_id, ..) => {
self.def_id_visitor.visit_def_id(def_id, "type", &ty)?;
if self.def_id_visitor.shallow() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
// Default type visitor doesn't visit signatures of fn types.
// Something like `fn() -> Priv {my_func}` is considered a private type even if
// `my_func` is public, so we need to visit signatures.
if let ty::FnDef(..) = ty.kind() {
- tcx.fn_sig(def_id).visit_with(self)?;
+ // FIXME: this should probably use `substs` from `FnDef`
+ tcx.fn_sig(def_id).subst_identity().visit_with(self)?;
}
// Inherent static methods don't have self type in substs.
// Something like `fn() {my_method}` type of the method
// as visible/reachable even if both `Type` and `Trait` are private.
// Ideally, associated types should be substituted in the same way as
// free type aliases, but this isn't done yet.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
// This will also visit substs if necessary, so we don't need to recurse.
return self.visit_projection_ty(proj);
| ty::FnPtr(..)
| ty::Param(..)
| ty::Error(_)
- | ty::GeneratorWitness(..) => {}
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..) => {}
ty::Bound(..) | ty::Placeholder(..) | ty::Infer(..) => {
bug!("unexpected type: {:?}", ty)
}
}
if self.def_id_visitor.shallow() {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
ty.super_visit_with(self)
}
if let Some(def_id) = def_id.as_local() {
self.min = VL::new_min(self, def_id);
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
let mut find = FindMin { tcx, effective_visibilities, min: Self::MAX };
find.visit(tcx.type_of(def_id));
if let Some(trait_ref) = tcx.impl_trait_ref(def_id) {
- find.visit_trait(trait_ref);
+ find.visit_trait(trait_ref.subst_identity());
}
find.min
}
GenericParamDefKind::Const { has_default } => {
self.visit(self.ev.tcx.type_of(param.def_id));
if has_default {
- self.visit(self.ev.tcx.const_param_default(param.def_id));
+ self.visit(self.ev.tcx.const_param_default(param.def_id).subst_identity());
}
}
}
fn trait_ref(&mut self) -> &mut Self {
if let Some(trait_ref) = self.ev.tcx.impl_trait_ref(self.item_def_id) {
- self.visit_trait(trait_ref);
+ self.visit_trait(trait_ref.subst_identity());
}
self
}
self.ev.update(def_id, self.level);
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if self.check_def_id(def_id, kind, descr) {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if self.check_def_id(def_id, kind, descr) {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
struct PrivateItemsInPublicInterfacesChecker<'tcx> {
tcx: TyCtxt<'tcx>,
- old_error_set_ancestry: LocalDefIdSet,
+ old_error_set_ancestry: HirIdSet,
}
impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx> {
tcx: self.tcx,
item_def_id: def_id,
required_visibility,
- has_old_errors: self.old_error_set_ancestry.contains(&def_id),
+ has_old_errors: self
+ .old_error_set_ancestry
+ .contains(&self.tcx.hir().local_def_id_to_hir_id(def_id)),
in_assoc_ty: false,
}
}
}
// Check for private types and traits in public interfaces.
- let mut checker = PrivateItemsInPublicInterfacesChecker {
- tcx,
- // Only definition IDs are ever searched in `old_error_set_ancestry`,
- // so we can filter away all non-definition IDs at this point.
- old_error_set_ancestry: old_error_set_ancestry
- .into_iter()
- .filter_map(|hir_id| tcx.hir().opt_local_def_id(hir_id))
- .collect(),
- };
+ let mut checker = PrivateItemsInPublicInterfacesChecker { tcx, old_error_set_ancestry };
for id in tcx.hir().items() {
checker.check_item(id);
/// This is used to remove cycles during type-checking const generic parameters.
///
/// As usual in the query system, we consider the current state of the calling query
- /// only depends on the list of dependencies up to now. As a consequence, the value
- /// that this query gives us can only depend on those dependencies too. Therefore,
+ /// only depends on the list of dependencies up to now. As a consequence, the value
+ /// that this query gives us can only depend on those dependencies too. Therefore,
/// it is sound to use the current dependency set for the created node.
///
/// During replay, the order of the nodes is relevant in the dependency graph.
hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
) -> DepNodeIndex {
if let Some(data) = self.data.as_ref() {
- // The caller query has more dependencies than the node we are creating. We may
+ // The caller query has more dependencies than the node we are creating. We may
// encounter a case where this created node is marked as green, but the caller query is
- // subsequently marked as red or recomputed. In this case, we will end up feeding a
+ // subsequently marked as red or recomputed. In this case, we will end up feeding a
// value to an existing node.
//
// For sanity, we still check that the loaded stable hash and the new one match.
/// graph: they are only added.
///
/// The nodes in it are identified by a `DepNodeIndex`. We avoid keeping the nodes
-/// in memory. This is important, because these graph structures are some of the
+/// in memory. This is important, because these graph structures are some of the
/// largest in the compiler.
///
/// For this reason, we avoid storing `DepNode`s more than once as map
//! The data that we will serialize and deserialize.
//!
//! The dep-graph is serialized as a sequence of NodeInfo, with the dependencies
-//! specified inline. The total number of nodes and edges are stored as the last
+//! specified inline. The total number of nodes and edges are stored as the last
//! 16 bytes of the file, so we can find them easily at decoding time.
//!
//! The serialisation is performed on-demand when each node is emitted. Using this
//! scheme, we do not need to keep the current graph in memory.
//!
//! The deserialization is performed manually, in order to convert from the stored
-//! sequence of NodeInfos to the different arrays in SerializedDepGraph. Since the
+//! sequence of NodeInfos to the different arrays in SerializedDepGraph. Since the
//! node and edge count are stored at the end of the file, all the arrays can be
//! pre-allocated with the right length.
let mut lock = self.cache.get_shard_by_value(&key).lock();
#[cfg(not(parallel_compiler))]
let mut lock = self.cache.lock();
- // We may be overwriting another value. This is all right, since the dep-graph
+ // We may be overwriting another value. This is all right, since the dep-graph
// will check that the fingerprint matches.
lock.insert(key, (value.clone(), index));
value
let mut lock = self.cache.get_shard_by_value(&key).lock();
#[cfg(not(parallel_compiler))]
let mut lock = self.cache.lock();
- // We may be overwriting another value. This is all right, since the dep-graph
+ // We may be overwriting another value. This is all right, since the dep-graph
// will check that the fingerprint matches.
lock.insert(key, value);
&value.0
use crate::Resolver;
use rustc_ast as ast;
-use rustc_ast::node_id::NodeMap;
use rustc_ast::visit::{self, Visitor};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::UnordSet;
use rustc_errors::{pluralize, MultiSpan};
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS};
use rustc_session::lint::BuiltinLintDiagnostics;
use_tree: &'a ast::UseTree,
use_tree_id: ast::NodeId,
item_span: Span,
- unused: FxHashSet<ast::NodeId>,
+ unused: UnordSet<ast::NodeId>,
}
impl<'a> UnusedImport<'a> {
struct UnusedImportCheckVisitor<'a, 'b> {
r: &'a mut Resolver<'b>,
/// All the (so far) unused imports, grouped path list
- unused_imports: NodeMap<UnusedImport<'a>>,
+ unused_imports: FxIndexMap<ast::NodeId, UnusedImport<'a>>,
base_use_tree: Option<&'a ast::UseTree>,
base_id: ast::NodeId,
item_span: Span,
use_tree,
use_tree_id,
item_span,
- unused: FxHashSet::default(),
+ unused: Default::default(),
})
}
}
use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
use rustc_errors::{
pluralize, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
+use rustc_errors::{struct_span_err, SuggestionStyle};
use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS};
let root_module = this.resolve_crate_root(root_ident);
this.add_module_candidates(root_module, &mut suggestions, filter_fn, None);
}
- Scope::Module(module) => {
+ Scope::Module(module, _) => {
this.add_module_candidates(module, &mut suggestions, filter_fn, None);
}
Scope::MacroUsePrelude => {
let source_map = self.r.session.source_map();
+ // Make sure this is actually crate-relative.
+ let is_definitely_crate = import
+ .module_path
+ .first()
+ .map_or(false, |f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super);
+
// Add the import to the start, with a `{` if required.
let start_point = source_map.start_point(after_crate_name);
- if let Ok(start_snippet) = source_map.span_to_snippet(start_point) {
+ if is_definitely_crate && let Ok(start_snippet) = source_map.span_to_snippet(start_point) {
corrections.push((
start_point,
if has_nested {
format!("{{{}, {}", import_snippet, start_snippet)
},
));
- }
- // Add a `};` to the end if nested, matching the `{` added at the start.
- if !has_nested {
- corrections.push((source_map.end_point(after_crate_name), "};".to_string()));
+ // Add a `};` to the end if nested, matching the `{` added at the start.
+ if !has_nested {
+ corrections.push((source_map.end_point(after_crate_name), "};".to_string()));
+ }
+ } else {
+ // If the root import is module-relative, add the import separately
+ corrections.push((
+ import.use_span.shrink_to_lo(),
+ format!("use {module_name}::{import_snippet};\n"),
+ ));
}
}
}
if let Some(span) = use_placement_span {
- let add_use = match mode {
+ let (add_use, trailing) = match mode {
DiagnosticMode::Pattern => {
err.span_suggestions(
span,
);
return;
}
- DiagnosticMode::Import => "",
- DiagnosticMode::Normal => "use ",
+ DiagnosticMode::Import => ("", ""),
+ DiagnosticMode::Normal => ("use ", ";\n"),
};
for candidate in &mut accessible_path_strings {
// produce an additional newline to separate the new use statement
// from the directly following item.
- let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
- candidate.0 = format!("{add_use}{}{append};\n{additional_newline}", &candidate.0);
+ let additional_newline = if let FoundUse::No = found_use && let DiagnosticMode::Normal = mode { "\n" } else { "" };
+ candidate.0 =
+ format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0);
}
- err.span_suggestions(
+ err.span_suggestions_with_style(
span,
&msg,
accessible_path_strings.into_iter().map(|a| a.0),
Applicability::MaybeIncorrect,
+ SuggestionStyle::ShowAlways,
);
if let [first, .., last] = &path[..] {
let sp = first.ident.span.until(last.ident.span);
msg.push_str(&candidate.0);
}
- err.note(&msg);
+ err.help(&msg);
}
} else if !matches!(mode, DiagnosticMode::Import) {
assert!(!inaccessible_path_strings.is_empty());
-use rustc_ast as ast;
+use rustc_ast::{self as ast, NodeId};
use rustc_feature::is_builtin_attr_name;
use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS};
use rustc_hir::PrimTy;
use rustc_middle::bug;
use rustc_middle::ty;
+use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
+use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::def_id::LocalDefId;
use rustc_span::edition::Edition;
use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext};
};
let mut scope = match ns {
_ if is_absolute_path => Scope::CrateRoot,
- TypeNS | ValueNS => Scope::Module(module),
+ TypeNS | ValueNS => Scope::Module(module, None),
MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
};
let mut ctxt = ctxt.normalize_to_macros_2_0();
MacroRulesScope::Invocation(invoc_id) => {
Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
}
- MacroRulesScope::Empty => Scope::Module(module),
+ MacroRulesScope::Empty => Scope::Module(module, None),
},
Scope::CrateRoot => match ns {
TypeNS => {
}
ValueNS | MacroNS => break,
},
- Scope::Module(module) => {
+ Scope::Module(module, prev_lint_id) => {
use_prelude = !module.no_implicit_prelude;
- match self.hygienic_lexical_parent(module, &mut ctxt) {
- Some(parent_module) => Scope::Module(parent_module),
+ let derive_fallback_lint_id = match scope_set {
+ ScopeSet::Late(.., lint_id) => lint_id,
+ _ => None,
+ };
+ match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
+ Some((parent_module, lint_id)) => {
+ Scope::Module(parent_module, lint_id.or(prev_lint_id))
+ }
None => {
ctxt.adjust(ExpnId::root());
match ns {
&mut self,
module: Module<'a>,
ctxt: &mut SyntaxContext,
- ) -> Option<Module<'a>> {
+ derive_fallback_lint_id: Option<NodeId>,
+ ) -> Option<(Module<'a>, Option<NodeId>)> {
if !module.expansion.outer_expn_is_descendant_of(*ctxt) {
- return Some(self.expn_def_scope(ctxt.remove_mark()));
+ return Some((self.expn_def_scope(ctxt.remove_mark()), None));
}
if let ModuleKind::Block = module.kind {
- return Some(module.parent.unwrap().nearest_item_scope());
+ return Some((module.parent.unwrap().nearest_item_scope(), None));
+ }
+
+ // We need to support the next case under a deprecation warning
+ // ```
+ // struct MyStruct;
+ // ---- begin: this comes from a proc macro derive
+ // mod implementation_details {
+ // // Note that `MyStruct` is not in scope here.
+ // impl SomeTrait for MyStruct { ... }
+ // }
+ // ---- end
+ // ```
+ // So we have to fall back to the module's parent during lexical resolution in this case.
+ if derive_fallback_lint_id.is_some() {
+ if let Some(parent) = module.parent {
+ // Inner module is inside the macro, parent module is outside of the macro.
+ if module.expansion != parent.expansion
+ && module.expansion.is_descendant_of(parent.expansion)
+ {
+ // The macro is a proc macro derive
+ if let Some(def_id) = module.expansion.expn_data().macro_def_id {
+ let ext = self.get_macro_by_def_id(def_id).ext;
+ if ext.builtin_name.is_none()
+ && ext.macro_kind() == MacroKind::Derive
+ && parent.expansion.outer_expn_is_descendant_of(*ctxt)
+ {
+ return Some((parent, derive_fallback_lint_id));
+ }
+ }
+ }
+ }
}
None
Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
}
}
- Scope::Module(module) => {
+ Scope::Module(module, derive_fallback_lint_id) => {
let adjusted_parent_scope = &ParentScope { module, ..*parent_scope };
let binding = this.resolve_ident_in_module_unadjusted_ext(
ModuleOrUniformRoot::Module(module),
);
match binding {
Ok(binding) => {
+ if let Some(lint_id) = derive_fallback_lint_id {
+ this.lint_buffer.buffer_lint_with_diagnostic(
+ PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+ lint_id,
+ orig_ident.span,
+ &format!(
+ "cannot find {} `{}` in this scope",
+ ns.descr(),
+ ident
+ ),
+ BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(
+ orig_ident.span,
+ ),
+ );
+ }
let misc_flags = if ptr::eq(module, this.graph_root) {
Flags::MISC_SUGGEST_CRATE
} else if module.is_normal() {
&& let Some(partial_res) = self.r.partial_res_map.get(&ty.id)
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) = partial_res.full_res()
{
- // This path is actually a bare trait object. In case of a bare `Fn`-trait
+ // This path is actually a bare trait object. In case of a bare `Fn`-trait
// object with anonymous lifetimes, we need this rib to correctly place the
// synthetic lifetimes.
let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo());
// Probe the lifetime ribs to know how to behave.
for rib in self.lifetime_ribs.iter().rev() {
match rib.kind {
- // We are inside a `PolyTraitRef`. The lifetimes are
+ // We are inside a `PolyTraitRef`. The lifetimes are
// to be intoduced in that (maybe implicit) `for<>` binder.
LifetimeRibKind::Generics {
binder,
);
break;
}
- // We have nowhere to introduce generics. Code is malformed,
+ // We have nowhere to introduce generics. Code is malformed,
// so use regular lifetime resolution to avoid spurious errors.
LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => {
visit::walk_generic_args(self, args);
break;
}
// `LifetimeRes::Error`, which would usually be used in the case of
- // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead,
+ // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead,
// we simply resolve to an implicit lifetime, which will be checked later, at
// which point a suitable error will be emitted.
LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
sugg.to_string(),
Applicability::MaybeIncorrect,
))
- } else if res.is_none() {
+ } else if res.is_none() && matches!(source, PathSource::Type) {
this.report_missing_type_error(path)
} else {
None
if let Some(qself) = qself {
if qself.position == 0 {
// This is a case like `<T>::B`, where there is no
- // trait to resolve. In that case, we leave the `B`
+ // trait to resolve. In that case, we leave the `B`
// segment to be resolved by type-check.
return Ok(Some(PartialRes::with_unresolved_segments(
Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()),
// Make sure `A::B` in `<T as A::B>::C` is a trait item.
//
// Currently, `path` names the full item (`A::B::C`, in
- // our example). so we extract the prefix of that that is
+ // our example). so we extract the prefix of that that is
// the trait (the slice upto and including
// `qself.position`). And then we recursively resolve that,
// but with `qself` set to `None`.
&& let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt()
&& let Some(items) = self.diagnostic_metadata.current_impl_items
&& let Some(item) = items.iter().find(|i| {
- if let AssocItemKind::Fn(_) = &i.kind && i.ident.name == item_str.name
+ if let AssocItemKind::Fn(..) | AssocItemKind::Const(..) = &i.kind
+ && i.ident.name == item_str.name
{
debug!(?item_str.name);
return true
}
false
})
- && let AssocItemKind::Fn(fn_) = &item.kind
{
- debug!(?fn_);
- let self_sugg = if fn_.sig.decl.has_self() { "self." } else { "Self::" };
+ let self_sugg = match &item.kind {
+ AssocItemKind::Fn(fn_) if fn_.sig.decl.has_self() => "self.",
+ _ => "Self::",
+ };
+
Some((
item_span.shrink_to_lo(),
- "consider using the associated function",
+ match &item.kind {
+ AssocItemKind::Fn(..) => "consider using the associated function",
+ AssocItemKind::Const(..) => "consider using the associated constant",
+ _ => unreachable!("item kind was filtered above"),
+ },
self_sugg.to_string()
))
} else {
let deletion_span = || {
if params.len() == 1 {
// if sole lifetime, remove the entire `<>` brackets
- generics_span
+ Some(generics_span)
} else if param_index == 0 {
// if removing within `<>` brackets, we also want to
// delete a leading or trailing comma as appropriate
- param.span().to(params[param_index + 1].span().shrink_to_lo())
+ match (
+ param.span().find_ancestor_inside(generics_span),
+ params[param_index + 1].span().find_ancestor_inside(generics_span),
+ ) {
+ (Some(param_span), Some(next_param_span)) => {
+ Some(param_span.to(next_param_span.shrink_to_lo()))
+ }
+ _ => None,
+ }
} else {
// if removing within `<>` brackets, we also want to
// delete a leading or trailing comma as appropriate
- params[param_index - 1].span().shrink_to_hi().to(param.span())
+ match (
+ param.span().find_ancestor_inside(generics_span),
+ params[param_index - 1].span().find_ancestor_inside(generics_span),
+ ) {
+ (Some(param_span), Some(prev_param_span)) => {
+ Some(prev_param_span.shrink_to_hi().to(param_span))
+ }
+ _ => None,
+ }
}
};
match use_set {
DeriveHelpersCompat,
MacroRules(MacroRulesScopeRef<'a>),
CrateRoot,
- Module(Module<'a>),
+ // The node ID is for reporting the `PROC_MACRO_DERIVE_RESOLUTION_FALLBACK`
+ // lint if it should be reported.
+ Module(Module<'a>, Option<NodeId>),
MacroUsePrelude,
BuiltinAttrs,
ExternPrelude,
self.visit_scopes(ScopeSet::All(TypeNS, false), parent_scope, ctxt, |this, scope, _, _| {
match scope {
- Scope::Module(module) => {
+ Scope::Module(module, _) => {
this.traits_in_module(module, assoc_item, &mut found_traits);
}
Scope::StdLibPrelude => {
}
pub fn dump_crate_info(&mut self, name: Symbol) {
- let source_file = self.tcx.sess.local_crate_source_file.as_ref();
- let crate_root = source_file.map(|source_file| {
- let source_file = Path::new(source_file);
+ let crate_root = self.tcx.sess.local_crate_source_file().map(|source_file| {
match source_file.file_name() {
Some(_) => source_file.parent().unwrap().display(),
None => source_file.display(),
.enumerate()
.filter(|(i, _)| !remap_arg_indices.contains(i))
.map(|(_, arg)| match input {
- Input::File(ref path) if path == Path::new(&arg) => {
- let mapped = &self.tcx.sess.local_crate_source_file;
- mapped.as_ref().unwrap().to_string_lossy().into()
- }
+ Input::File(ref path) if path == Path::new(&arg) => self
+ .tcx
+ .sess
+ .local_crate_source_file()
+ .as_ref()
+ .unwrap()
+ .to_string_lossy()
+ .into(),
_ => arg,
});
None => continue,
};
if !self.span.filter_generated(ident.span) {
- let id = id_from_hir_id(hir_id, &self.save_ctxt);
+ let id = id_from_hir_id(hir_id);
let span = self.span_from_span(ident.span);
self.dumper.dump_def(
&mut self,
sig: &'tcx hir::FnSig<'tcx>,
body: Option<hir::BodyId>,
- def_id: LocalDefId,
+ owner_id: hir::OwnerId,
ident: Ident,
generics: &'tcx hir::Generics<'tcx>,
span: Span,
) {
- debug!("process_method: {:?}:{}", def_id, ident);
+ debug!("process_method: {:?}:{}", owner_id, ident);
let map = self.tcx.hir();
- let hir_id = map.local_def_id_to_hir_id(def_id);
- self.nest_typeck_results(def_id, |v| {
- if let Some(mut method_data) = v.save_ctxt.get_method_data(hir_id, ident, span) {
+ let hir_id: hir::HirId = owner_id.into();
+ self.nest_typeck_results(owner_id.def_id, |v| {
+ if let Some(mut method_data) = v.save_ctxt.get_method_data(owner_id, ident, span) {
if let Some(body) = body {
v.process_formals(map.body(body).params, &method_data.qualname);
}
method_data.value =
fn_to_string(sig.decl, sig.header, Some(ident.name), generics, &[], None);
- method_data.sig = sig::method_signature(hir_id, ident, generics, sig, &v.save_ctxt);
+ method_data.sig =
+ sig::method_signature(owner_id, ident, generics, sig, &v.save_ctxt);
- v.dumper.dump_def(&access_from!(v.save_ctxt, def_id), method_data);
+ v.dumper.dump_def(&access_from!(v.save_ctxt, owner_id.def_id), method_data);
}
// walk arg and return types
fn process_struct_field_def(
&mut self,
field: &'tcx hir::FieldDef<'tcx>,
- parent_id: hir::HirId,
+ parent_id: LocalDefId,
) {
let field_data = self.save_ctxt.get_field_data(field, parent_id);
if let Some(field_data) = field_data {
- self.dumper.dump_def(
- &access_from!(self.save_ctxt, self.tcx.hir().local_def_id(field.hir_id)),
- field_data,
- );
+ self.dumper.dump_def(&access_from!(self.save_ctxt, field.def_id), field_data);
}
}
// Append $id to name to make sure each one is unique.
let qualname = format!("{}::{}${}", prefix, name, id);
if !self.span.filter_generated(param_ss) {
- let id = id_from_hir_id(param.hir_id, &self.save_ctxt);
+ let id = id_from_def_id(param.def_id.to_def_id());
let span = self.span_from_span(param_ss);
self.dumper.dump_def(
fn process_assoc_const(
&mut self,
- def_id: LocalDefId,
+ owner_id: hir::OwnerId,
ident: Ident,
typ: &'tcx hir::Ty<'tcx>,
expr: Option<&'tcx hir::Expr<'tcx>>,
parent_id: DefId,
attrs: &'tcx [ast::Attribute],
) {
- let qualname = format!("::{}", self.tcx.def_path_str(def_id.to_def_id()));
+ let qualname = format!("::{}", self.tcx.def_path_str(owner_id.to_def_id()));
if !self.span.filter_generated(ident.span) {
- let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- let sig = sig::assoc_const_signature(hir_id, ident.name, typ, expr, &self.save_ctxt);
+ let sig = sig::assoc_const_signature(owner_id, ident.name, typ, expr, &self.save_ctxt);
let span = self.span_from_span(ident.span);
self.dumper.dump_def(
- &access_from!(self.save_ctxt, def_id),
+ &access_from!(self.save_ctxt, owner_id.def_id),
Def {
kind: DefKind::Const,
- id: id_from_hir_id(hir_id, &self.save_ctxt),
+ id: id_from_def_id(owner_id.to_def_id()),
span,
name: ident.name.to_string(),
qualname,
}
// walk type and init value
- self.nest_typeck_results(def_id, |v| {
+ self.nest_typeck_results(owner_id.def_id, |v| {
v.visit_ty(typ);
if let Some(expr) = expr {
v.visit_expr(expr);
if include_priv_fields {
return Some(f.ident.to_string());
}
- let def_id = self.save_ctxt.tcx.hir().local_def_id(f.hir_id);
- if self.save_ctxt.tcx.visibility(def_id).is_public() {
+ if self.save_ctxt.tcx.visibility(f.def_id).is_public() {
Some(f.ident.to_string())
} else {
None
.collect::<Vec<_>>()
.join(", ");
let value = format!("{} {{ {} }}", name, fields_str);
- (value, fields.iter().map(|f| id_from_hir_id(f.hir_id, &self.save_ctxt)).collect())
+ (value, fields.iter().map(|f| id_from_def_id(f.def_id.to_def_id())).collect())
}
_ => (String::new(), vec![]),
};
self.nest_typeck_results(item.owner_id.def_id, |v| {
for field in def.fields() {
- v.process_struct_field_def(field, item.hir_id());
+ v.process_struct_field_def(field, item.owner_id.def_id);
v.visit_ty(&field.ty);
}
let value = format!("{}::{} {{ {} }}", enum_data.name, name, fields_str);
if !self.span.filter_generated(name_span) {
let span = self.span_from_span(name_span);
- let id = id_from_hir_id(variant.hir_id, &self.save_ctxt);
+ let id = id_from_def_id(variant.def_id.to_def_id());
let parent = Some(id_from_def_id(item.owner_id.to_def_id()));
let attrs = self.tcx.hir().attrs(variant.hir_id);
}
if !self.span.filter_generated(name_span) {
let span = self.span_from_span(name_span);
- let id = id_from_hir_id(variant.hir_id, &self.save_ctxt);
+ let id = id_from_def_id(variant.def_id.to_def_id());
let parent = Some(id_from_def_id(item.owner_id.to_def_id()));
let attrs = self.tcx.hir().attrs(variant.hir_id);
}
for field in variant.data.fields() {
- self.process_struct_field_def(field, variant.hir_id);
+ self.process_struct_field_def(field, variant.def_id);
self.visit_ty(field.ty);
}
}
// Rust uses the id of the pattern for var lookups, so we'll use it too.
if !self.span.filter_generated(ident.span) {
let qualname = format!("{}${}", ident, hir_id);
- let id = id_from_hir_id(hir_id, &self.save_ctxt);
+ let id = id_from_hir_id(hir_id);
let span = self.span_from_span(ident.span);
self.dumper.dump_def(
let body = body.map(|b| self.tcx.hir().body(b).value);
let attrs = self.tcx.hir().attrs(trait_item.hir_id());
self.process_assoc_const(
- trait_item.owner_id.def_id,
+ trait_item.owner_id,
trait_item.ident,
&ty,
body,
self.process_method(
sig,
body,
- trait_item.owner_id.def_id,
+ trait_item.owner_id,
trait_item.ident,
&trait_item.generics,
trait_item.span,
decl_id: None,
docs: self.save_ctxt.docs_for_attrs(attrs),
sig: sig::assoc_type_signature(
- trait_item.hir_id(),
+ trait_item.owner_id,
trait_item.ident,
Some(bounds),
default_ty.as_deref(),
let body = self.tcx.hir().body(body);
let attrs = self.tcx.hir().attrs(impl_item.hir_id());
self.process_assoc_const(
- impl_item.owner_id.def_id,
+ impl_item.owner_id,
impl_item.ident,
&ty,
Some(&body.value),
self.process_method(
sig,
Some(body),
- impl_item.owner_id.def_id,
+ impl_item.owner_id,
impl_item.ident,
&impl_item.generics,
impl_item.span,
}
pub(crate) fn process_crate(&mut self) {
- let id = hir::CRATE_HIR_ID;
- let qualname =
- format!("::{}", self.tcx.def_path_str(self.tcx.hir().local_def_id(id).to_def_id()));
+ let qualname = format!("::{}", self.tcx.def_path_str(CRATE_DEF_ID.to_def_id()));
let sm = self.tcx.sess.source_map();
let krate_mod = self.tcx.hir().root_module();
let filename = sm.span_to_filename(krate_mod.spans.inner_span);
- let data_id = id_from_hir_id(id, &self.save_ctxt);
+ let data_id = id_from_def_id(CRATE_DEF_ID.to_def_id());
let children =
krate_mod.item_ids.iter().map(|i| id_from_def_id(i.owner_id.to_def_id())).collect();
let span = self.span_from_span(krate_mod.spans.inner_span);
- let attrs = self.tcx.hir().attrs(id);
+ let attrs = self.tcx.hir().attrs(hir::CRATE_HIR_ID);
self.dumper.dump_def(
&Access { public: true, reachable: true },
// output the inferred type here? :shrug:
hir::ArrayLen::Infer(..) => {}
hir::ArrayLen::Body(anon_const) => self
- .nest_typeck_results(self.tcx.hir().local_def_id(anon_const.hir_id), |v| {
+ .nest_typeck_results(anon_const.def_id, |v| {
v.visit_expr(&map.body(anon_const.body).value)
}),
}
}
}
}
- hir::ExprKind::Closure(&hir::Closure { ref fn_decl, body, .. }) => {
+ hir::ExprKind::Closure(&hir::Closure { ref fn_decl, body, def_id, .. }) => {
let id = format!("${}", ex.hir_id);
// walk arg and return types
// walk the body
let map = self.tcx.hir();
- self.nest_typeck_results(self.tcx.hir().local_def_id(ex.hir_id), |v| {
+ self.nest_typeck_results(def_id, |v| {
let body = map.body(body);
v.process_formals(body.params, &id);
v.visit_expr(&body.value)
// output the inferred type here? :shrug:
hir::ArrayLen::Infer(..) => {}
hir::ArrayLen::Body(anon_const) => self
- .nest_typeck_results(self.tcx.hir().local_def_id(anon_const.hir_id), |v| {
+ .nest_typeck_results(anon_const.def_id, |v| {
v.visit_expr(&map.body(anon_const.body).value)
}),
}
use rustc_ast_pretty::pprust::attribute_to_string;
use rustc_hir as hir;
use rustc_hir::def::{DefKind as HirDefKind, Res};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::Node;
use rustc_hir_pretty::{enum_def_to_string, fn_to_string, ty_to_string};
qualname,
value,
parent: None,
- children: def.variants.iter().map(|v| id_from_hir_id(v.hir_id, self)).collect(),
+ children: def.variants.iter().map(|v| id_from_def_id(v.def_id.to_def_id())).collect(),
decl_id: None,
docs: self.docs_for_attrs(attrs),
sig: sig::item_signature(item, self),
}
}
- pub fn get_field_data(&self, field: &hir::FieldDef<'_>, scope: hir::HirId) -> Option<Def> {
+ pub fn get_field_data(&self, field: &hir::FieldDef<'_>, scope: LocalDefId) -> Option<Def> {
let name = field.ident.to_string();
- let scope_def_id = self.tcx.hir().local_def_id(scope).to_def_id();
- let qualname = format!("::{}::{}", self.tcx.def_path_str(scope_def_id), field.ident);
+ let qualname = format!("::{}::{}", self.tcx.def_path_str(scope.to_def_id()), field.ident);
filter!(self.span_utils, field.ident.span);
- let field_def_id = self.tcx.hir().local_def_id(field.hir_id).to_def_id();
+ let field_def_id = field.def_id.to_def_id();
let typ = self.tcx.type_of(field_def_id).to_string();
let id = id_from_def_id(field_def_id);
name,
qualname,
value: typ,
- parent: Some(id_from_def_id(scope_def_id)),
+ parent: Some(id_from_def_id(scope.to_def_id())),
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(attrs),
// FIXME would be nice to take a MethodItem here, but the ast provides both
// trait and impl flavours, so the caller must do the disassembly.
- pub fn get_method_data(&self, hir_id: hir::HirId, ident: Ident, span: Span) -> Option<Def> {
+ pub fn get_method_data(&self, owner_id: hir::OwnerId, ident: Ident, span: Span) -> Option<Def> {
// The qualname for a method is the trait name or name of the struct in an impl in
// which the method is declared in, followed by the method's name.
- let def_id = self.tcx.hir().local_def_id(hir_id).to_def_id();
let (qualname, parent_scope, decl_id, docs, attributes) =
- match self.tcx.impl_of_method(def_id) {
+ match self.tcx.impl_of_method(owner_id.to_def_id()) {
Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) {
Some(Node::Item(item)) => match item.kind {
hir::ItemKind::Impl(hir::Impl { ref self_ty, .. }) => {
let trait_id = self.tcx.trait_id_of_impl(impl_id);
let mut docs = String::new();
let mut attrs = vec![];
- if let Some(Node::ImplItem(_)) = hir.find(hir_id) {
- attrs = self.tcx.hir().attrs(hir_id).to_vec();
+ if let Some(Node::ImplItem(_)) = hir.find(owner_id.into()) {
+ attrs = self.tcx.hir().attrs(owner_id.into()).to_vec();
docs = self.docs_for_attrs(&attrs);
}
_ => {
span_bug!(
span,
- "Container {:?} for method {} not an impl?",
+ "Container {:?} for method {:?} not an impl?",
impl_id,
- hir_id
+ owner_id,
);
}
},
r => {
span_bug!(
span,
- "Container {:?} for method {} is not a node item {:?}",
+ "Container {:?} for method {:?} is not a node item {:?}",
impl_id,
- hir_id,
+ owner_id,
r
);
}
},
- None => match self.tcx.trait_of_item(def_id) {
+ None => match self.tcx.trait_of_item(owner_id.to_def_id()) {
Some(def_id) => {
let mut docs = String::new();
let mut attrs = vec![];
- if let Some(Node::TraitItem(_)) = self.tcx.hir().find(hir_id) {
- attrs = self.tcx.hir().attrs(hir_id).to_vec();
+ if let Some(Node::TraitItem(_)) = self.tcx.hir().find(owner_id.into()) {
+ attrs = self.tcx.hir().attrs(owner_id.into()).to_vec();
docs = self.docs_for_attrs(&attrs);
}
)
}
None => {
- debug!("could not find container for method {} at {:?}", hir_id, span);
+ debug!("could not find container for method {:?} at {:?}", owner_id, span);
// This is not necessarily a bug, if there was a compilation error,
// the typeck results we need might not exist.
return None;
Some(Def {
kind: DefKind::Method,
- id: id_from_def_id(def_id),
+ id: id_from_def_id(owner_id.to_def_id()),
span: self.span_from_span(ident.span),
name: ident.name.to_string(),
qualname,
match res {
Res::Local(id) => {
- Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_hir_id(id, self) })
+ Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_hir_id(id) })
}
Res::Def(HirDefKind::Trait, def_id) if fn_type(path_seg) => {
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) })
rls_data::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
}
-fn id_from_hir_id(id: hir::HirId, scx: &SaveContext<'_>) -> rls_data::Id {
- let def_id = scx.tcx.hir().opt_local_def_id(id);
- def_id.map(|id| id_from_def_id(id.to_def_id())).unwrap_or_else(|| {
- // Create a *fake* `DefId` out of a `HirId` by combining the owner
- // `local_def_index` and the `local_id`.
- // This will work unless you have *billions* of definitions in a single
- // crate (very unlikely to actually happen).
- rls_data::Id {
- krate: LOCAL_CRATE.as_u32(),
- index: id.owner.def_id.local_def_index.as_u32() | id.local_id.as_u32().reverse_bits(),
- }
- })
+fn id_from_hir_id(id: hir::HirId) -> rls_data::Id {
+ // Create a *fake* `DefId` out of a `HirId` by combining the owner
+ // `local_def_index` and the `local_id`.
+ // This will work unless you have *billions* of definitions in a single
+ // crate (very unlikely to actually happen).
+ rls_data::Id {
+ krate: LOCAL_CRATE.as_u32(),
+ index: id.owner.def_id.local_def_index.as_u32() | id.local_id.as_u32().reverse_bits(),
+ }
}
fn null_id() -> rls_data::Id {
//
// FIXME where clauses need implementing, defs/refs in generics are mostly missing.
-use crate::{id_from_def_id, id_from_hir_id, SaveContext};
+use crate::{id_from_def_id, SaveContext};
use rls_data::{SigElement, Signature};
use rustc_hir::def::{DefKind, Res};
use rustc_hir_pretty::id_to_string;
use rustc_hir_pretty::{bounds_to_string, path_segment_to_string, path_to_string, ty_to_string};
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{Ident, Symbol};
pub fn item_signature(item: &hir::Item<'_>, scx: &SaveContext<'_>) -> Option<Signature> {
}
pub fn method_signature(
- id: hir::HirId,
+ id: hir::OwnerId,
ident: Ident,
generics: &hir::Generics<'_>,
m: &hir::FnSig<'_>,
}
pub fn assoc_const_signature(
- id: hir::HirId,
+ id: hir::OwnerId,
ident: Symbol,
ty: &hir::Ty<'_>,
default: Option<&hir::Expr<'_>>,
}
pub fn assoc_type_signature(
- id: hir::HirId,
+ id: hir::OwnerId,
ident: Ident,
bounds: Option<hir::GenericBounds<'_>>,
default: Option<&hir::Ty<'_>>,
type Result = std::result::Result<Signature, &'static str>;
trait Sig {
- fn make(&self, offset: usize, id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result;
+ type Parent;
+ fn make(&self, offset: usize, id: Option<Self::Parent>, scx: &SaveContext<'_>) -> Result;
}
fn extend_sig(
}
impl<'hir> Sig for hir::Ty<'hir> {
+ type Parent = hir::HirId;
fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
let id = Some(self.hir_id);
match self.kind {
}
impl<'hir> Sig for hir::Item<'hir> {
+ type Parent = hir::HirId;
fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
let id = Some(self.hir_id());
text.push_str("fn ");
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
sig.text.push('(');
for i in decl.inputs {
hir::ItemKind::TyAlias(ref ty, ref generics) => {
let text = "type ".to_owned();
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
sig.text.push_str(" = ");
let ty = ty.make(offset + sig.text.len(), id, scx)?;
hir::ItemKind::Enum(_, ref generics) => {
let text = "enum ".to_owned();
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
sig.text.push_str(" {}");
Ok(sig)
}
hir::ItemKind::Struct(_, ref generics) => {
let text = "struct ".to_owned();
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
sig.text.push_str(" {}");
Ok(sig)
}
hir::ItemKind::Union(_, ref generics) => {
let text = "union ".to_owned();
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
sig.text.push_str(" {}");
Ok(sig)
}
}
text.push_str("trait ");
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
if !bounds.is_empty() {
sig.text.push_str(": ");
let mut text = String::new();
text.push_str("trait ");
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
if !bounds.is_empty() {
sig.text.push_str(" = ");
text.push_str(" const");
}
- let generics_sig = generics.make(offset + text.len(), id, scx)?;
+ let generics_sig =
+ generics.make(offset + text.len(), Some(self.owner_id.def_id), scx)?;
text.push_str(&generics_sig.text);
text.push(' ');
}
impl<'hir> Sig for hir::Path<'hir> {
+ type Parent = hir::HirId;
fn make(&self, offset: usize, id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
let res = scx.get_path_res(id.ok_or("Missing id for Path")?);
// This does not cover the where clause, which must be processed separately.
impl<'hir> Sig for hir::Generics<'hir> {
- fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
+ type Parent = LocalDefId;
+ fn make(&self, offset: usize, _parent_id: Option<LocalDefId>, scx: &SaveContext<'_>) -> Result {
if self.params.is_empty() {
return Ok(text_sig(String::new()));
}
}
param_text.push_str(param.name.ident().as_str());
defs.push(SigElement {
- id: id_from_hir_id(param.hir_id, scx),
+ id: id_from_def_id(param.def_id.to_def_id()),
start: offset + text.len(),
end: offset + text.len() + param_text.as_str().len(),
});
}
impl<'hir> Sig for hir::FieldDef<'hir> {
- fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
+ type Parent = LocalDefId;
+ fn make(&self, offset: usize, _parent_id: Option<LocalDefId>, scx: &SaveContext<'_>) -> Result {
let mut text = String::new();
text.push_str(&self.ident.to_string());
let defs = Some(SigElement {
- id: id_from_hir_id(self.hir_id, scx),
+ id: id_from_def_id(self.def_id.to_def_id()),
start: offset,
end: offset + text.len(),
});
}
impl<'hir> Sig for hir::Variant<'hir> {
- fn make(&self, offset: usize, parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
+ type Parent = LocalDefId;
+ fn make(&self, offset: usize, parent_id: Option<LocalDefId>, scx: &SaveContext<'_>) -> Result {
let mut text = self.ident.to_string();
match self.data {
hir::VariantData::Struct(fields, r) => {
let id = parent_id.ok_or("Missing id for Variant's parent")?;
let name_def = SigElement {
- id: id_from_hir_id(id, scx),
+ id: id_from_def_id(id.to_def_id()),
start: offset,
end: offset + text.len(),
};
text.push('}');
Ok(Signature { text, defs, refs })
}
- hir::VariantData::Tuple(fields, id, _) => {
+ hir::VariantData::Tuple(fields, _, def_id) => {
let name_def = SigElement {
- id: id_from_hir_id(id, scx),
+ id: id_from_def_id(def_id.to_def_id()),
start: offset,
end: offset + text.len(),
};
let mut defs = vec![name_def];
let mut refs = vec![];
for f in fields {
- let field_sig = f.make(offset + text.len(), Some(id), scx)?;
+ let field_sig = f.make(offset + text.len(), Some(def_id), scx)?;
text.push_str(&field_sig.text);
text.push_str(", ");
defs.extend(field_sig.defs.into_iter());
text.push(')');
Ok(Signature { text, defs, refs })
}
- hir::VariantData::Unit(id, _) => {
+ hir::VariantData::Unit(_, def_id) => {
let name_def = SigElement {
- id: id_from_hir_id(id, scx),
+ id: id_from_def_id(def_id.to_def_id()),
start: offset,
end: offset + text.len(),
};
}
impl<'hir> Sig for hir::ForeignItem<'hir> {
+ type Parent = hir::HirId;
fn make(&self, offset: usize, _parent_id: Option<hir::HirId>, scx: &SaveContext<'_>) -> Result {
let id = Some(self.hir_id());
match self.kind {
text.push_str("fn ");
let mut sig =
- name_and_generics(text, offset, generics, self.hir_id(), self.ident, scx)?;
+ name_and_generics(text, offset, generics, self.owner_id, self.ident, scx)?;
sig.text.push('(');
for i in decl.inputs {
mut text: String,
offset: usize,
generics: &hir::Generics<'_>,
- id: hir::HirId,
+ id: hir::OwnerId,
name: Ident,
scx: &SaveContext<'_>,
) -> Result {
let name = name.to_string();
let def = SigElement {
- id: id_from_hir_id(id, scx),
+ id: id_from_def_id(id.to_def_id()),
start: offset + text.len(),
end: offset + text.len() + name.len(),
};
text.push_str(&name);
- let generics: Signature = generics.make(offset + text.len(), Some(id), scx)?;
+ let generics: Signature = generics.make(offset + text.len(), Some(id.def_id), scx)?;
// FIXME where clause
let text = format!("{}{}", text, generics.text);
Ok(extend_sig(generics, text, vec![def], vec![]))
}
fn make_assoc_type_signature(
- id: hir::HirId,
+ id: hir::OwnerId,
ident: Ident,
bounds: Option<hir::GenericBounds<'_>>,
default: Option<&hir::Ty<'_>>,
let mut text = "type ".to_owned();
let name = ident.to_string();
let mut defs = vec![SigElement {
- id: id_from_hir_id(id, scx),
+ id: id_from_def_id(id.to_def_id()),
start: text.len(),
end: text.len() + name.len(),
}];
}
if let Some(default) = default {
text.push_str(" = ");
- let ty_sig = default.make(text.len(), Some(id), scx)?;
+ let ty_sig = default.make(text.len(), Some(id.into()), scx)?;
text.push_str(&ty_sig.text);
defs.extend(ty_sig.defs.into_iter());
refs.extend(ty_sig.refs.into_iter());
}
fn make_assoc_const_signature(
- id: hir::HirId,
+ id: hir::OwnerId,
ident: Symbol,
ty: &hir::Ty<'_>,
default: Option<&hir::Expr<'_>>,
let mut text = "const ".to_owned();
let name = ident.to_string();
let mut defs = vec![SigElement {
- id: id_from_hir_id(id, scx),
+ id: id_from_def_id(id.to_def_id()),
start: text.len(),
end: text.len() + name.len(),
}];
text.push_str(&name);
text.push_str(": ");
- let ty_sig = ty.make(text.len(), Some(id), scx)?;
+ let ty_sig = ty.make(text.len(), Some(id.into()), scx)?;
text.push_str(&ty_sig.text);
defs.extend(ty_sig.defs.into_iter());
refs.extend(ty_sig.refs.into_iter());
}
fn make_method_signature(
- id: hir::HirId,
+ id: hir::OwnerId,
ident: Ident,
generics: &hir::Generics<'_>,
m: &hir::FnSig<'_>,
match &file.name {
FileName::Real(RealFileName::LocalPath(path)) => {
if path.is_absolute() {
- self.sess
- .source_map()
- .path_mapping()
- .map_prefix(path.into())
- .0
- .display()
- .to_string()
+ self.sess.source_map().path_mapping().map_prefix(path).0.display().to_string()
} else {
self.sess
.opts
Input::Str { ref name, .. } => name.clone(),
}
}
+
+ pub fn opt_path(&self) -> Option<&Path> {
+ match self {
+ Input::File(file) => Some(file),
+ Input::Str { name, .. } => match name {
+ FileName::Real(real) => real.local_path(),
+ FileName::QuoteExpansion(_) => None,
+ FileName::Anon(_) => None,
+ FileName::MacroExpansion(_) => None,
+ FileName::ProcMacroSourceCode(_) => None,
+ FileName::CfgSpec(_) => None,
+ FileName::CliCrateAttr(_) => None,
+ FileName::Custom(_) => None,
+ FileName::DocTest(path, _) => Some(path),
+ FileName::InlineAsm(_) => None,
+ },
+ }
+ }
}
#[derive(Clone, Hash, Debug, HashStable_Generic)]
pub fn host_triple() -> &'static str {
// Get the host triple out of the build environment. This ensures that our
// idea of the host triple is the same as for the set of libraries we've
- // actually built. We can't just take LLVM's host triple because they
+ // actually built. We can't just take LLVM's host triple because they
// normalize all ix86 architectures to i386.
//
// Instead of grabbing the host triple (for the current host), we grab (at
ProcMacro,
}
-impl CrateType {
- /// When generated, is this crate type an archive?
- pub fn is_archive(&self) -> bool {
- match *self {
- CrateType::Rlib | CrateType::Staticlib => true,
- CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => {
- false
- }
- }
- }
-}
-
#[derive(Clone, Hash, Debug, PartialEq, Eq)]
pub enum Passes {
Some(Vec<String>),
if sess.target.has_thread_local {
ret.insert((sym::target_thread_local, None));
}
+ let mut has_atomic = false;
for (i, align) in [
(8, layout.i8_align.abi),
(16, layout.i16_align.abi),
(128, layout.i128_align.abi),
] {
if i >= min_atomic_width && i <= max_atomic_width {
+ has_atomic = true;
let mut insert_atomic = |s, align: Align| {
ret.insert((sym::target_has_atomic_load_store, Some(Symbol::intern(s))));
if atomic_cas {
}
}
}
+ if sess.is_nightly_build() && has_atomic {
+ ret.insert((sym::target_has_atomic_load_store, None));
+ if atomic_cas {
+ ret.insert((sym::target_has_atomic, None));
+ }
+ }
let panic_strategy = sess.panic_strategy();
ret.insert((sym::panic, Some(panic_strategy.desc_symbol())));
// The `opt` local module holds wrappers around the `getopts` API that
// adds extra rustc-specific metadata to each option; such metadata
-// is exposed by . The public
+// is exposed by . The public
// functions below ending with `_u` are the functions that return
// *unstable* options, i.e., options that are only enabled when the
// user also passes the `-Z unstable-options` debugging flag.
.map(|s| {
// Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
// where KIND is one of "dylib", "framework", "static", "link-arg" and
- // where MODIFIERS are a comma separated list of supported modifiers
+ // where MODIFIERS are a comma separated list of supported modifiers
// (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed
// with either + or - to indicate whether it is enabled or disabled.
// The last value specified for a given modifier wins.
let pretty = parse_pretty(&unstable_opts, error_format);
+ // query-dep-graph is required if dump-dep-graph is given #106736
+ if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
+ early_error(error_format, "can't dump dependency graph without `-Z query-dep-graph`");
+ }
+
// Try to find a directory containing the Rust `src`, for more details see
// the doc comment on the `real_rust_source_base_dir` field.
let tmp_buf;
early_error(error_format, &format!("Current directory is invalid: {e}"));
});
- let (path, remapped) =
- FilePathMapping::new(remap_path_prefix.clone()).map_prefix(working_dir.clone());
+ let remap = FilePathMapping::new(remap_path_prefix.clone());
+ let (path, remapped) = remap.map_prefix(&working_dir);
let working_dir = if remapped {
- RealFileName::Remapped { local_path: Some(working_dir), virtual_name: path }
+ RealFileName::Remapped { virtual_name: path.into_owned(), local_path: Some(working_dir) }
} else {
- RealFileName::LocalPath(path)
+ RealFileName::LocalPath(path.into_owned())
};
Options {
"hir,typed" => Hir(PpHirMode::Typed),
"hir-tree" => HirTree,
"thir-tree" => ThirTree,
+ "thir-flat" => ThirFlat,
"mir" => Mir,
"mir-cfg" => MirCFG,
name => early_error(
"argument to `unpretty` must be one of `normal`, `identified`, \
`expanded`, `expanded,identified`, `expanded,hygiene`, \
`ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \
- `hir,typed`, `hir-tree`, `thir-tree`, `mir` or `mir-cfg`; got {name}"
+ `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir` or \
+ `mir-cfg`; got {name}"
),
),
};
HirTree,
/// `-Zunpretty=thir-tree`
ThirTree,
+ /// `-Zunpretty=`thir-flat`
+ ThirFlat,
/// `-Zunpretty=mir`
Mir,
/// `-Zunpretty=mir-cfg`
| Hir(_)
| HirTree
| ThirTree
+ | ThirFlat
| Mir
| MirCFG => true,
}
match *self {
Source(_) | AstTree(_) => false,
- Hir(_) | HirTree | ThirTree | Mir | MirCFG => true,
+ Hir(_) | HirTree | ThirTree | ThirFlat | Mir | MirCFG => true,
}
}
pub fn needs_analysis(&self) -> bool {
use PpMode::*;
- matches!(*self, Mir | MirCFG | ThirTree)
+ matches!(*self, Mir | MirCFG | ThirTree | ThirFlat)
}
}
fn def_path_hash(&self, def: DefId) -> DefPathHash;
// This information is safe to access, since it's hashed as part of the StableCrateId, which
- // incr. comp. uses to identify a CrateNum.
+ // incr. comp. uses to identify a CrateNum.
fn crate_name(&self, cnum: CrateNum) -> Symbol;
fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId;
fn stable_crate_id_to_crate_num(&self, stable_crate_id: StableCrateId) -> CrateNum;
/// This function checks if sysroot is found using env::args().next(), and if it
/// is not found, finds sysroot from current rustc_driver dll.
pub fn get_or_default_sysroot() -> Result<PathBuf, String> {
- // Follow symlinks. If the resolved path is relative, make it absolute.
+ // Follow symlinks. If the resolved path is relative, make it absolute.
fn canonicalize(path: PathBuf) -> PathBuf {
let path = fs::canonicalize(&path).unwrap_or(path);
// See comments on this target function, but the gist is that
(default: no)"),
drop_tracking: bool = (false, parse_bool, [TRACKED],
"enables drop tracking in generators (default: no)"),
+ drop_tracking_mir: bool = (false, parse_bool, [TRACKED],
+ "enables drop tracking on MIR in generators (default: no)"),
dual_proc_macros: bool = (false, parse_bool, [TRACKED],
"load proc macros for both target and host, but only link to the target (default: no)"),
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"measure time of each LLVM pass (default: no)"),
time_passes: bool = (false, parse_bool, [UNTRACKED],
"measure time of each rustc pass (default: no)"),
+ tiny_const_eval_limit: bool = (false, parse_bool, [TRACKED],
+ "sets a tiny, non-configurable limit for const eval; useful for compiler tests"),
#[rustc_lint_opt_deny_field_access("use `Session::tls_model` instead of this field")]
tls_model: Option<TlsModel> = (None, parse_tls_model, [TRACKED],
"choose the TLS model to use (`rustc --print tls-models` for details)"),
out_filename
}
-/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
+/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
/// check this already -- however, the Linux linker will happily overwrite a
-/// read-only file. We should be consistent.
+/// read-only file. We should be consistent.
pub fn check_file_is_writeable(file: &Path, sess: &Session) {
if !is_writeable(file) {
sess.emit_fatal(FileIsNotWriteable { file });
}
}
-pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> Symbol {
+pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol {
let validate = |s: Symbol, span: Option<Span>| {
validate_crate_name(sess, s, span);
s
if let Some((attr, s)) = attr_crate_name {
return validate(s, Some(attr.span));
}
- if let Input::File(ref path) = *input {
+ if let Input::File(ref path) = sess.io.input {
if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
if s.starts_with('-') {
sess.emit_err(CrateNameInvalid { s });
use crate::cgu_reuse_tracker::CguReuseTracker;
use crate::code_stats::CodeStats;
pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
+use crate::config::Input;
use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
use crate::errors::{
BranchProtectionRequiresAArch64, CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers,
pub const_eval_limit: Limit,
}
+pub struct CompilerIO {
+ pub input: Input,
+ pub output_dir: Option<PathBuf>,
+ pub output_file: Option<PathBuf>,
+ pub temps_dir: Option<PathBuf>,
+}
+
/// Represents the data associated with a compilation
/// session for a single crate.
pub struct Session {
pub target_tlib_path: Lrc<SearchPath>,
pub parse_sess: ParseSess,
pub sysroot: PathBuf,
- /// The name of the root source file of the crate, in the local file system.
- /// `None` means that there is no source file.
- pub local_crate_source_file: Option<PathBuf>,
+ /// Input, input file path and output file path to this compilation process.
+ pub io: CompilerIO,
crate_types: OnceCell<Vec<CrateType>>,
/// The `stable_crate_id` is constructed out of the crate name and all the
pub ctfe_backtrace: Lock<CtfeBacktrace>,
/// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a
- /// const check, optionally with the relevant feature gate. We use this to
+ /// const check, optionally with the relevant feature gate. We use this to
/// warn about unleashing, but with a single diagnostic instead of dozens that
/// drown everything else in noise.
miri_unleashed_features: Lock<Vec<(Span, Option<Symbol>)>>,
self.miri_unleashed_features.lock().push((span, feature_gate));
}
+ pub fn local_crate_source_file(&self) -> Option<PathBuf> {
+ let path = self.io.input.opt_path()?;
+ Some(self.opts.file_path_mapping().map_prefix(path).0.into_owned())
+ }
+
fn check_miri_unleashed_features(&self) {
let unleashed_features = self.miri_unleashed_features.lock();
if !unleashed_features.is_empty() {
#[allow(rustc::bad_opt_access)]
pub fn build_session(
sopts: config::Options,
- local_crate_source_file: Option<PathBuf>,
+ io: CompilerIO,
bundle: Option<Lrc<rustc_errors::FluentBundle>>,
registry: rustc_errors::registry::Registry,
driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
};
- let file_path_mapping = sopts.file_path_mapping();
-
- let local_crate_source_file =
- local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0);
-
let optimization_fuel = Lock::new(OptimizationFuel {
remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |&(_, i)| i),
out_of_fuel: false,
target_tlib_path,
parse_sess,
sysroot,
- local_crate_source_file,
+ io,
crate_types: OnceCell::new(),
stable_crate_id: OnceCell::new(),
features: OnceCell::new(),
/// first and then resolved later), so we use an `Option` here.
local_expn_data: IndexVec<LocalExpnId, Option<ExpnData>>,
local_expn_hashes: IndexVec<LocalExpnId, ExpnHash>,
- /// Data and hash information from external crates. We may eventually want to remove these
+ /// Data and hash information from external crates. We may eventually want to remove these
/// maps, and fetch the information directly from the other crate's metadata like DefIds do.
foreign_expn_data: FxHashMap<ExpnId, ExpnData>,
foreign_expn_hashes: FxHashMap<ExpnId, ExpnHash>,
use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
use std::cmp;
use std::hash::Hash;
-use std::path::{Path, PathBuf};
+use std::path::{self, Path, PathBuf};
use std::sync::atomic::Ordering;
use std::fs;
pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
source_file.add_external_src(|| {
- match source_file.name {
- FileName::Real(ref name) if let Some(local_path) = name.local_path() => {
- self.file_loader.read_file(local_path).ok()
+ let FileName::Real(ref name) = source_file.name else {
+ return None;
+ };
+
+ let local_path: Cow<'_, Path> = match name {
+ RealFileName::LocalPath(local_path) => local_path.into(),
+ RealFileName::Remapped { local_path: Some(local_path), .. } => local_path.into(),
+ RealFileName::Remapped { local_path: None, virtual_name } => {
+ // The compiler produces better error messages if the sources of dependencies
+ // are available. Attempt to undo any path mapping so we can find remapped
+ // dependencies.
+ // We can only use the heuristic because `add_external_src` checks the file
+ // content hash.
+ self.path_mapping.reverse_map_prefix_heuristically(virtual_name)?.into()
}
- _ => None,
- }
+ };
+
+ self.file_loader.read_file(&local_path).ok()
})
}
/// Applies any path prefix substitution as defined by the mapping.
/// The return value is the remapped path and a boolean indicating whether
/// the path was affected by the mapping.
- pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
+ pub fn map_prefix<'a>(&'a self, path: impl Into<Cow<'a, Path>>) -> (Cow<'a, Path>, bool) {
+ let path = path.into();
if path.as_os_str().is_empty() {
// Exit early if the path is empty and therefore there's nothing to remap.
// This is mostly to reduce spam for `RUSTC_LOG=[remap_path_prefix]`.
return remap_path_prefix(&self.mapping, path);
#[instrument(level = "debug", skip(mapping), ret)]
- fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) {
+ fn remap_path_prefix<'a>(
+ mapping: &'a [(PathBuf, PathBuf)],
+ path: Cow<'a, Path>,
+ ) -> (Cow<'a, Path>, bool) {
// NOTE: We are iterating over the mapping entries from last to first
// because entries specified later on the command line should
// take precedence.
// in remapped paths down the line.
// So, if we have an exact match, we just return that without a call
// to `Path::join()`.
- to.clone()
+ to.into()
} else {
- to.join(rest)
+ to.join(rest).into()
};
debug!("Match - remapped");
fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
match file {
FileName::Real(realfile) if let RealFileName::LocalPath(local_path) = realfile => {
- let (mapped_path, mapped) = self.map_prefix(local_path.to_path_buf());
+ let (mapped_path, mapped) = self.map_prefix(local_path);
let realfile = if mapped {
RealFileName::Remapped {
local_path: Some(local_path.clone()),
- virtual_name: mapped_path,
+ virtual_name: mapped_path.into_owned(),
}
} else {
realfile.clone()
let (new_path, was_remapped) = self.map_prefix(unmapped_file_path);
if was_remapped {
// It was remapped, so don't modify further
- return RealFileName::Remapped { local_path: None, virtual_name: new_path };
+ return RealFileName::Remapped {
+ local_path: None,
+ virtual_name: new_path.into_owned(),
+ };
}
if new_path.is_absolute() {
// No remapping has applied to this path and it is absolute,
// so the working directory cannot influence it either, so
// we are done.
- return RealFileName::LocalPath(new_path);
+ return RealFileName::LocalPath(new_path.into_owned());
}
debug_assert!(new_path.is_relative());
RealFileName::Remapped {
// Erase the actual path
local_path: None,
- virtual_name: file_path_abs,
+ virtual_name: file_path_abs.into_owned(),
}
} else {
// No kind of remapping applied to this path, so
// we leave it as it is.
- RealFileName::LocalPath(file_path_abs)
+ RealFileName::LocalPath(file_path_abs.into_owned())
}
}
RealFileName::Remapped {
}
}
}
+
+ /// Attempts to (heuristically) reverse a prefix mapping.
+ ///
+ /// Returns [`Some`] if there is exactly one mapping where the "to" part is
+ /// a prefix of `path` and has at least one non-empty
+ /// [`Normal`](path::Component::Normal) component. The component
+ /// restriction exists to avoid reverse mapping overly generic paths like
+ /// `/` or `.`).
+ ///
+ /// This is a heuristic and not guaranteed to return the actual original
+ /// path! Do not rely on the result unless you have other means to verify
+ /// that the mapping is correct (e.g. by checking the file content hash).
+ #[instrument(level = "debug", skip(self), ret)]
+ fn reverse_map_prefix_heuristically(&self, path: &Path) -> Option<PathBuf> {
+ let mut found = None;
+
+ for (from, to) in self.mapping.iter() {
+ let has_normal_component = to.components().any(|c| match c {
+ path::Component::Normal(s) => !s.is_empty(),
+ _ => false,
+ });
+
+ if !has_normal_component {
+ continue;
+ }
+
+ let Ok(rest) = path.strip_prefix(to) else {
+ continue;
+ };
+
+ if found.is_some() {
+ return None;
+ }
+
+ found = Some(from.join(rest));
+ }
+
+ found
+ }
}
mapping.map_prefix(path(p)).0.to_string_lossy().to_string()
}
+fn reverse_map_prefix(mapping: &FilePathMapping, p: &str) -> Option<String> {
+ mapping.reverse_map_prefix_heuristically(&path(p)).map(|q| q.to_string_lossy().to_string())
+}
+
#[test]
fn path_prefix_remapping() {
// Relative to relative
let working_directory = path("/foo");
let working_directory = RealFileName::Remapped {
local_path: Some(working_directory.clone()),
- virtual_name: mapping.map_prefix(working_directory).0,
+ virtual_name: mapping.map_prefix(working_directory).0.into_owned(),
};
assert_eq!(working_directory.remapped_path_if_available(), path("FOO"));
);
}
+#[test]
+fn path_prefix_remapping_reverse() {
+ // Ignores options without alphanumeric chars.
+ {
+ let mapping =
+ &FilePathMapping::new(vec![(path("abc"), path("/")), (path("def"), path("."))]);
+
+ assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None);
+ assert_eq!(reverse_map_prefix(mapping, "./hello.rs"), None);
+ }
+
+ // Returns `None` if multiple options match.
+ {
+ let mapping = &FilePathMapping::new(vec![
+ (path("abc"), path("/redacted")),
+ (path("def"), path("/redacted")),
+ ]);
+
+ assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None);
+ }
+
+ // Distinct reverse mappings.
+ {
+ let mapping = &FilePathMapping::new(vec![
+ (path("abc"), path("/redacted")),
+ (path("def/ghi"), path("/fake/dir")),
+ ]);
+
+ assert_eq!(
+ reverse_map_prefix(mapping, "/redacted/path/hello.rs"),
+ Some(path_str("abc/path/hello.rs"))
+ );
+ assert_eq!(
+ reverse_map_prefix(mapping, "/fake/dir/hello.rs"),
+ Some(path_str("def/ghi/hello.rs"))
+ );
+ }
+}
+
#[test]
fn test_next_point() {
let sm = SourceMap::new(FilePathMapping::empty());
Capture,
Center,
Clone,
+ Context,
Continue,
Copy,
Count,
Target,
ToOwned,
ToString,
+ TokenStream,
Try,
TryCaptureGeneric,
TryCapturePrintable,
forbid,
forget,
format,
+ format_alignment,
format_args,
format_args_capture,
format_args_macro,
format_args_nl,
+ format_argument,
+ format_arguments,
+ format_count,
format_macro,
+ format_placeholder,
+ format_unsafe_arg,
freeze,
freg,
frem_fast,
mul,
mul_assign,
mul_with_overflow,
+ multiple_supertrait_upcastable,
must_not_suspend,
must_use,
naked,
ty::Bound(..)
| ty::Error(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Infer(..)
| ty::Alias(..)
| ty::Param(..)
ty::Bound(..)
| ty::Error(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Infer(..)
| ty::Alias(..)
| ty::Param(..)
}
ty::GeneratorWitness(_) => bug!("symbol_names: unexpected `GeneratorWitness`"),
+ ty::GeneratorWitnessMIR(..) => bug!("symbol_names: unexpected `GeneratorWitnessMIR`"),
}
// Only cache types that do not refer to an enclosing
{
match arg_layout.abi {
Abi::Scalar(scalar) => match scalar.primitive() {
- abi::Int(..) | abi::Pointer => {
+ abi::Int(..) | abi::Pointer(_) => {
if arg_layout.size.bits() > xlen {
return Err(CannotUseFpConv);
}
// The primitive for this algorithm.
Abi::Scalar(scalar) => {
let kind = match scalar.primitive() {
- abi::Int(..) | abi::Pointer => RegKind::Integer,
+ abi::Int(..) | abi::Pointer(_) => RegKind::Integer,
abi::F32 | abi::F64 => RegKind::Float,
};
Ok(HomogeneousAggregate::Homogeneous(Reg { kind, size: self.size }))
{
match arg_layout.abi {
Abi::Scalar(scalar) => match scalar.primitive() {
- abi::Int(..) | abi::Pointer => {
+ abi::Int(..) | abi::Pointer(_) => {
if arg_layout.size.bits() > xlen {
return Err(CannotUseFpConv);
}
{
let dl = cx.data_layout();
- if !scalar.primitive().is_float() {
+ if !matches!(scalar.primitive(), abi::F32 | abi::F64) {
return data;
}
(abi::F32, _) => offset += Reg::f32().size,
(_, abi::F64) => offset += Reg::f64().size,
(abi::Int(i, _signed), _) => offset += i.size(),
- (abi::Pointer, _) => offset += Reg::i64().size,
+ (abi::Pointer(_), _) => offset += Reg::i64().size,
_ => {}
}
- if (offset.bytes() % 4) != 0 && scalar2.primitive().is_float() {
+ if (offset.bytes() % 4) != 0 && matches!(scalar2.primitive(), abi::F32 | abi::F64) {
offset += Size::from_bytes(4 - (offset.bytes() % 4));
}
data = arg_scalar(cx, scalar2, offset, data);
Abi::Uninhabited => return Ok(()),
Abi::Scalar(scalar) => match scalar.primitive() {
- abi::Int(..) | abi::Pointer => Class::Int,
+ abi::Int(..) | abi::Pointer(_) => Class::Int,
abi::F32 | abi::F64 => Class::Sse,
},
C: HasDataLayout,
{
match self.abi {
- Abi::Scalar(scalar) => scalar.primitive().is_float(),
+ Abi::Scalar(scalar) => matches!(scalar.primitive(), F32 | F64),
Abi::Aggregate { .. } => {
if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 {
self.field(cx, 0).is_single_fp_element(cx)
}
/// Returns a suggested template modifier to use for this type and an
- /// example of a register named formatted with it.
+ /// example of a register named formatted with it.
///
/// Such suggestions are useful if a type smaller than the full register
/// size is used and a modifier can be used to point to the subregister of
Target {
// Clang automatically chooses a more specific target based on
- // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
// correctly, we do too.
llvm_target: macos_llvm_target(arch).into(),
pointer_width: 64,
allow_asm: true,
endian,
linker_flavor: LinkerFlavor::Bpf,
- atomic_cas: true,
+ atomic_cas: false,
dynamic_linking: true,
no_builtins: true,
panic_strategy: PanicStrategy::Abort,
Target {
// Clang automatically chooses a more specific target based on
- // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
// correctly, we do too.
//
// While ld64 doesn't understand i686, LLVM does.
LinkerFlavor::Unix(Cc::Yes),
&[
// The illumos libc contains a stack unwinding implementation, as
- // does libgcc_s. The latter implementation includes several
- // additional symbols that are not always in base libc. To force
+ // does libgcc_s. The latter implementation includes several
+ // additional symbols that are not always in base libc. To force
// the consistent use of just one unwinder, we ensure libc appears
// after libgcc_s in the NEEDED list for the resultant binary by
// ignoring any attempts to add it as a dynamic dependency until the
"-lc",
// LLVM will insert calls to the stack protector functions
// "__stack_chk_fail" and "__stack_chk_guard" into code in native
- // object files. Some platforms include these symbols directly in
+ // object files. Some platforms include these symbols directly in
// libc, but at least historically these have been provided in
// libssp.so on illumos and Solaris systems.
"-lssp",
// cleanup handlers (in C, this would be something along the lines of:
// void register_callback(void (*fn)(void *), void *arg);
// (see src/libstd/sys/unix/fast_thread_local.rs) that is currently
- // missing in illumos. For now at least, we must fallback to using
+ // missing in illumos. For now at least, we must fallback to using
// pthread_{get,set}specific.
//has_thread_local: true,
// FIXME: Currently, rust is invoking cc to link, which ends up
- // causing these to get included twice. We should eventually transition
+ // causing these to get included twice. We should eventually transition
// to having rustc invoke ld directly, in which case these will need to
// be uncommented.
//
- // We want XPG6 behavior from libc and libm. See standards(5)
+ // We want XPG6 behavior from libc and libm. See standards(5)
//pre_link_objects_exe: vec![
// "/usr/lib/amd64/values-Xc.o".into(),
// "/usr/lib/amd64/values-xpg6.o".into(),
/// Search for a JSON file specifying the given target triple.
///
/// If none is found in `$RUST_TARGET_PATH`, look for a file called `target.json` inside the
- /// sysroot under the target-triple's `rustlib` directory. Note that it could also just be a
+ /// sysroot under the target-triple's `rustlib` directory. Note that it could also just be a
/// bare filename already, so also check for that. If one of the hardcoded targets we know
/// about, just return it directly.
///
use crate::abi::Endian;
-use crate::spec::{StackProbeType, Target};
+use crate::spec::{SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_gnu_base::opts();
base.max_atomic_width = Some(64);
base.min_global_align = Some(16);
base.stack_probes = StackProbeType::Inline;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD;
Target {
llvm_target: "s390x-unknown-linux-gnu".into(),
use crate::abi::Endian;
-use crate::spec::{StackProbeType, Target};
+use crate::spec::{SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_musl_base::opts();
base.min_global_align = Some(16);
base.static_position_independent_executables = true;
base.stack_probes = StackProbeType::Inline;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD;
Target {
llvm_target: "s390x-unknown-linux-musl".into(),
pointer_width: 64,
data_layout: "E-m:e-i64:64-n32:64-S128".into(),
// Use "sparc64" instead of "sparcv9" here, since the former is already
- // used widely in the source base. If we ever needed ABI
+ // used widely in the source base. If we ever needed ABI
// differentiation from the sparc64, we could, but that would probably
// just be confusing.
arch: "sparc64".into(),
Target {
// Clang automatically chooses a more specific target based on
- // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
// correctly, we do too.
llvm_target: macos_llvm_target(arch).into(),
pointer_width: 64,
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_query_system = { path = "../rustc_query_system" }
+rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
#![feature(never_type)]
#![feature(result_option_inspect)]
#![feature(type_alias_impl_trait)]
+#![feature(min_specialization)]
#![recursion_limit = "512"] // For rustdoc
#[macro_use]
//! Code shared by trait and projection goals for candidate assembly.
use super::infcx_ext::InferCtxtExt;
-use super::{
- fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
- EvalCtxt, Goal,
-};
+#[cfg(doc)]
+use super::trait_goals::structural_traits::*;
+use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
use rustc_hir::def_id::DefId;
-use rustc_infer::infer::TyCtxtInferExt;
-use rustc_infer::infer::{
- canonical::{CanonicalVarValues, OriginalQueryValues},
- InferCtxt,
-};
use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::util::elaborate_predicates;
use rustc_middle::ty::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::DUMMY_SP;
use std::fmt::Debug;
/// A candidate is a possible way to prove a goal.
///
/// It consists of both the `source`, which describes how that goal would be proven,
/// and the `result` when using the given `source`.
-///
-/// For the list of possible candidates, please look at the documentation of
-/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource].
#[derive(Debug, Clone)]
-pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> {
- pub(super) source: G::CandidateSource,
+pub(super) struct Candidate<'tcx> {
+ pub(super) source: CandidateSource,
pub(super) result: CanonicalResponse<'tcx>,
}
-pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
- type CandidateSource: Debug + Copy;
+/// Possible ways the given goal can be proven.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum CandidateSource {
+ /// A user written impl.
+ ///
+ /// ## Examples
+ ///
+ /// ```rust
+ /// fn main() {
+ /// let x: Vec<u32> = Vec::new();
+ /// // This uses the impl from the standard library to prove `Vec<T>: Clone`.
+ /// let y = x.clone();
+ /// }
+ /// ```
+ Impl(DefId),
+ /// A builtin impl generated by the compiler. When adding a new special
+ /// trait, try to use actual impls whenever possible. Builtin impls should
+ /// only be used in cases where the impl cannot be manually be written.
+ ///
+ /// Notable examples are auto traits, `Sized`, and `DiscriminantKind`.
+ /// For a list of all traits with builtin impls, check out the
+ /// [`EvalCtxt::assemble_builtin_impl_candidates`] method. Not
+ BuiltinImpl,
+ /// An assumption from the environment.
+ ///
+ /// More precicely we've used the `n-th` assumption in the `param_env`.
+ ///
+ /// ## Examples
+ ///
+ /// ```rust
+ /// fn is_clone<T: Clone>(x: T) -> (T, T) {
+ /// // This uses the assumption `T: Clone` from the `where`-bounds
+ /// // to prove `T: Clone`.
+ /// (x.clone(), x)
+ /// }
+ /// ```
+ ParamEnv(usize),
+ /// If the self type is an alias type, e.g. an opaque type or a projection,
+ /// we know the bounds on that alias to hold even without knowing its concrete
+ /// underlying type.
+ ///
+ /// More precisely this candidate is using the `n-th` bound in the `item_bounds` of
+ /// the self type.
+ ///
+ /// ## Examples
+ ///
+ /// ```rust
+ /// trait Trait {
+ /// type Assoc: Clone;
+ /// }
+ ///
+ /// fn foo<T: Trait>(x: <T as Trait>::Assoc) {
+ /// // We prove `<T as Trait>::Assoc` by looking at the bounds on `Assoc` in
+ /// // in the trait definition.
+ /// let _y = x.clone();
+ /// }
+ /// ```
+ AliasBound,
+}
+pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
fn self_ty(self) -> Ty<'tcx>;
fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
fn consider_impl_candidate(
- acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+ ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
impl_def_id: DefId,
- );
-}
+ ) -> QueryResult<'tcx>;
-/// An abstraction which correctly deals with the canonical results for candidates.
-///
-/// It also deduplicates the behavior between trait and projection predicates.
-pub(super) struct AssemblyCtxt<'a, 'tcx, G: GoalKind<'tcx>> {
- pub(super) cx: &'a mut EvalCtxt<'tcx>,
- pub(super) infcx: &'a InferCtxt<'tcx>,
- var_values: CanonicalVarValues<'tcx>,
- candidates: Vec<Candidate<'tcx, G>>,
-}
+ fn consider_assumption(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ assumption: ty::Predicate<'tcx>,
+ ) -> QueryResult<'tcx>;
-impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
- pub(super) fn assemble_and_evaluate_candidates(
- cx: &'a mut EvalCtxt<'tcx>,
- goal: CanonicalGoal<'tcx, G>,
- ) -> Vec<Candidate<'tcx, G>> {
- let (ref infcx, goal, var_values) =
- cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
- let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() };
+ // A type implements an `auto trait` if its components do as well. These components
+ // are given by built-in rules from [`instantiate_constituent_tys_for_auto_trait`].
+ fn consider_auto_trait_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
- acx.assemble_candidates_after_normalizing_self_ty(goal);
+ // A trait alias holds if the RHS traits and `where` clauses hold.
+ fn consider_trait_alias_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
- acx.assemble_impl_candidates(goal);
+ // A type is `Copy` or `Clone` if its components are `Sized`. These components
+ // are given by built-in rules from [`instantiate_constituent_tys_for_sized_trait`].
+ fn consider_builtin_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
- acx.candidates
- }
+ // A type is `Copy` or `Clone` if its components are `Copy` or `Clone`. These
+ // components are given by built-in rules from [`instantiate_constituent_tys_for_copy_clone_trait`].
+ fn consider_builtin_copy_clone_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ // A type is `PointerSized` if we can compute its layout, and that layout
+ // matches the layout of `usize`.
+ fn consider_builtin_pointer_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ // A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
+ // family of traits where `A` is given by the signature of the type.
+ fn consider_builtin_fn_trait_candidates(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ kind: ty::ClosureKind,
+ ) -> QueryResult<'tcx>;
- pub(super) fn try_insert_candidate(
+ // `Tuple` is implemented if the `Self` type is a tuple.
+ fn consider_builtin_tuple_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ // `Pointee` is always implemented.
+ //
+ // See the projection implementation for the `Metadata` types for all of
+ // the built-in types. For structs, the metadata type is given by the struct
+ // tail.
+ fn consider_builtin_pointee_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ // A generator (that comes from an `async` desugaring) is known to implement
+ // `Future<Output = O>`, where `O` is given by the generator's return type
+ // that was computed during type-checking.
+ fn consider_builtin_future_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ // A generator (that doesn't come from an `async` desugaring) is known to
+ // implement `Generator<R, Yield = Y, Return = O>`, given the resume, yield,
+ // and return types of the generator computed during type-checking.
+ fn consider_builtin_generator_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
&mut self,
- source: G::CandidateSource,
- certainty: Certainty,
- ) {
- match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
- Ok(result) => self.candidates.push(Candidate { source, result }),
- Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
+ goal: Goal<'tcx, G>,
+ ) -> Vec<Candidate<'tcx>> {
+ debug_assert_eq!(goal, self.infcx.resolve_vars_if_possible(goal));
+
+ // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
+ // object bound, alias bound, etc. We are unable to determine this until we can at
+ // least structually resolve the type one layer.
+ if goal.predicate.self_ty().is_ty_var() {
+ return vec![Candidate {
+ source: CandidateSource::BuiltinImpl,
+ result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(),
+ }];
}
+
+ let mut candidates = Vec::new();
+
+ self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
+
+ self.assemble_impl_candidates(goal, &mut candidates);
+
+ self.assemble_builtin_impl_candidates(goal, &mut candidates);
+
+ self.assemble_param_env_candidates(goal, &mut candidates);
+
+ self.assemble_alias_bound_candidates(goal, &mut candidates);
+
+ self.assemble_object_bound_candidates(goal, &mut candidates);
+
+ candidates
}
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
/// this case as projections as self types add `
- fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) {
- let tcx = self.cx.tcx;
+ fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let tcx = self.tcx();
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
return
term: normalized_ty.into(),
}),
);
- let normalization_certainty =
- match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
- Ok((_, certainty)) => certainty,
- Err(NoSolution) => return,
- };
+ let normalization_certainty = match self.evaluate_goal(normalizes_to_goal) {
+ Ok((_, certainty)) => certainty,
+ Err(NoSolution) => return,
+ };
+ let normalized_ty = self.infcx.resolve_vars_if_possible(normalized_ty);
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
- // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
+ // This doesn't work as long as we use `CandidateSource` in winnowing.
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
- let mut orig_values = OriginalQueryValues::default();
- let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
- let normalized_candidates =
- AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal);
-
- // Map each candidate from being canonical wrt the current inference context to being
- // canonical wrt the caller.
- for Candidate { source, result } in normalized_candidates {
- self.infcx.probe(|_| {
- let candidate_certainty = fixme_instantiate_canonical_query_response(
- &self.infcx,
- &orig_values,
- result,
- );
-
- // FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
- //
- // If we have an ambiguous candidate it hides that normalization
- // caused an overflow which may cause issues.
- self.try_insert_candidate(
- source,
- normalization_certainty.unify_and(candidate_certainty),
- )
- })
+ let normalized_candidates = self.assemble_and_evaluate_candidates(goal);
+ for mut normalized_candidate in normalized_candidates {
+ normalized_candidate.result =
+ normalized_candidate.result.unchecked_map(|mut response| {
+ // FIXME: This currently hides overflow in the normalization step of the self type
+ // which is probably wrong. Maybe `unify_and` should actually keep overflow as
+ // we treat it as non-fatal anyways.
+ response.certainty = response.certainty.unify_and(normalization_certainty);
+ response
+ });
+ candidates.push(normalized_candidate);
}
})
}
- fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) {
- self.cx.tcx.for_each_relevant_impl(
- goal.predicate.trait_def_id(self.cx.tcx),
+ fn assemble_impl_candidates<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let tcx = self.tcx();
+ tcx.for_each_relevant_impl(
+ goal.predicate.trait_def_id(tcx),
goal.predicate.self_ty(),
- |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
+ |impl_def_id| match G::consider_impl_candidate(self, goal, impl_def_id) {
+ Ok(result) => candidates
+ .push(Candidate { source: CandidateSource::Impl(impl_def_id), result }),
+ Err(NoSolution) => (),
+ },
);
}
+
+ fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let lang_items = self.tcx().lang_items();
+ let trait_def_id = goal.predicate.trait_def_id(self.tcx());
+ let result = if self.tcx().trait_is_auto(trait_def_id) {
+ G::consider_auto_trait_candidate(self, goal)
+ } else if self.tcx().trait_is_alias(trait_def_id) {
+ G::consider_trait_alias_candidate(self, goal)
+ } else if lang_items.sized_trait() == Some(trait_def_id) {
+ G::consider_builtin_sized_candidate(self, goal)
+ } else if lang_items.copy_trait() == Some(trait_def_id)
+ || lang_items.clone_trait() == Some(trait_def_id)
+ {
+ G::consider_builtin_copy_clone_candidate(self, goal)
+ } else if lang_items.pointer_sized() == Some(trait_def_id) {
+ G::consider_builtin_pointer_sized_candidate(self, goal)
+ } else if let Some(kind) = self.tcx().fn_trait_kind_from_def_id(trait_def_id) {
+ G::consider_builtin_fn_trait_candidates(self, goal, kind)
+ } else if lang_items.tuple_trait() == Some(trait_def_id) {
+ G::consider_builtin_tuple_candidate(self, goal)
+ } else if lang_items.pointee_trait() == Some(trait_def_id) {
+ G::consider_builtin_pointee_candidate(self, goal)
+ } else if lang_items.future_trait() == Some(trait_def_id) {
+ G::consider_builtin_future_candidate(self, goal)
+ } else if lang_items.gen_trait() == Some(trait_def_id) {
+ G::consider_builtin_generator_candidate(self, goal)
+ } else {
+ Err(NoSolution)
+ };
+
+ match result {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
+ }
+ Err(NoSolution) => (),
+ }
+ }
+
+ fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
+ match G::consider_assumption(self, goal, assumption) {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::ParamEnv(i), result })
+ }
+ Err(NoSolution) => (),
+ }
+ }
+ }
+
+ fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let alias_ty = match goal.predicate.self_ty().kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Adt(_, _)
+ | ty::Foreign(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::Ref(_, _, _)
+ | ty::FnDef(_, _)
+ | ty::FnPtr(_)
+ | ty::Dynamic(..)
+ | ty::Closure(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Never
+ | ty::Tuple(_)
+ | ty::Param(_)
+ | ty::Placeholder(..)
+ | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::Error(_) => return,
+ ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+ | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
+ ty::Alias(_, alias_ty) => alias_ty,
+ };
+
+ for (assumption, _) in self
+ .tcx()
+ .bound_explicit_item_bounds(alias_ty.def_id)
+ .subst_iter_copied(self.tcx(), alias_ty.substs)
+ {
+ match G::consider_assumption(self, goal, assumption) {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::AliasBound, result })
+ }
+ Err(NoSolution) => (),
+ }
+ }
+ }
+
+ fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let self_ty = goal.predicate.self_ty();
+ let bounds = match *self_ty.kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Adt(_, _)
+ | ty::Foreign(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::Ref(_, _, _)
+ | ty::FnDef(_, _)
+ | ty::FnPtr(_)
+ | ty::Alias(..)
+ | ty::Closure(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Never
+ | ty::Tuple(_)
+ | ty::Param(_)
+ | ty::Placeholder(..)
+ | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::Error(_) => return,
+ ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+ | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
+ ty::Dynamic(bounds, ..) => bounds,
+ };
+
+ let tcx = self.tcx();
+ for assumption in
+ elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
+ {
+ match G::consider_assumption(self, goal, assumption.predicate) {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
+ }
+ Err(NoSolution) => (),
+ }
+ }
+ }
}
+++ /dev/null
-//! This module both handles the global cache which stores "finished" goals,
-//! and the provisional cache which contains partially computed goals.
-//!
-//! The provisional cache is necessary when dealing with coinductive cycles.
-//!
-//! For more information about the provisional cache and coinduction in general,
-//! check out the relevant section of the rustc-dev-guide.
-//!
-//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
-//! before then or if I still haven't done that before January 2023.
-use super::overflow::OverflowData;
-use super::CanonicalGoal;
-use super::{EvalCtxt, QueryResult};
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty::TyCtxt;
-use std::{cmp::Ordering, collections::hash_map::Entry};
-
-#[derive(Debug, Clone)]
-struct ProvisionalEntry<'tcx> {
- // In case we have a coinductive cycle, this is the
- // the currently least restrictive result of this goal.
- response: QueryResult<'tcx>,
- // The lowest element on the stack on which this result
- // relies on. Starts out as just being the depth at which
- // we've proven this obligation, but gets lowered to the
- // depth of another goal if we rely on it in a cycle.
- depth: usize,
-}
-
-struct StackElem<'tcx> {
- goal: CanonicalGoal<'tcx>,
- has_been_used: bool,
-}
-
-/// The cache used for goals which are currently in progress or which depend
-/// on in progress results.
-///
-/// Once we're done with a goal we can store it in the global trait solver
-/// cache of the `TyCtxt`. For goals which we're currently proving, or which
-/// have only been proven via a coinductive cycle using a goal still on our stack
-/// we have to use this separate data structure.
-///
-/// The current data structure is not perfect, so there may still be room for
-/// improvement here. We have the following requirements:
-///
-/// ## Is there is a provisional entry for the given goal:
-///
-/// ```ignore (for syntax highlighting)
-/// self.entries.get(goal)
-/// ```
-///
-/// ## Get all goals on the stack involved in a cycle:
-///
-/// ```ignore (for syntax highlighting)
-/// let entry = self.entries.get(goal).unwrap();
-/// let involved_goals = self.stack.iter().skip(entry.depth);
-/// ```
-///
-/// ## Capping the depth of all entries
-///
-/// Needed whenever we encounter a cycle. The current implementation always
-/// iterates over all entries instead of only the ones with a larger depth.
-/// Changing this may result in notable performance improvements.
-///
-/// ```ignore (for syntax highlighting)
-/// let cycle_depth = self.entries.get(goal).unwrap().depth;
-/// for e in &mut self.entries {
-/// e.depth = e.depth.min(cycle_depth);
-/// }
-/// ```
-///
-/// ## Checking whether we have to rerun the current goal
-///
-/// A goal has to be rerun if its provisional result was used in a cycle
-/// and that result is different from its final result. We update
-/// [StackElem::has_been_used] for the deepest stack element involved in a cycle.
-///
-/// ## Moving all finished goals into the global cache
-///
-/// If `stack_elem.has_been_used` is true, iterate over all entries, moving the ones
-/// with equal depth. If not, simply move this single entry.
-pub(super) struct ProvisionalCache<'tcx> {
- stack: Vec<StackElem<'tcx>>,
- entries: FxHashMap<CanonicalGoal<'tcx>, ProvisionalEntry<'tcx>>,
-}
-
-impl<'tcx> ProvisionalCache<'tcx> {
- pub(super) fn empty() -> ProvisionalCache<'tcx> {
- ProvisionalCache { stack: Vec::new(), entries: Default::default() }
- }
-
- pub(super) fn current_depth(&self) -> usize {
- self.stack.len()
- }
-}
-
-impl<'tcx> EvalCtxt<'tcx> {
- /// Tries putting the new goal on the stack, returning an error if it is already cached.
- ///
- /// This correctly updates the provisional cache if there is a cycle.
- pub(super) fn try_push_stack(
- &mut self,
- goal: CanonicalGoal<'tcx>,
- ) -> Result<(), QueryResult<'tcx>> {
- // FIXME: start by checking the global cache
-
- // Look at the provisional cache to check for cycles.
- let cache = &mut self.provisional_cache;
- match cache.entries.entry(goal) {
- // No entry, simply push this goal on the stack after dealing with overflow.
- Entry::Vacant(v) => {
- if self.overflow_data.has_overflow(cache.stack.len()) {
- return Err(self.deal_with_overflow());
- }
-
- v.insert(ProvisionalEntry {
- response: fixme_response_yes_no_constraints(),
- depth: cache.stack.len(),
- });
- cache.stack.push(StackElem { goal, has_been_used: false });
- Ok(())
- }
- // We have a nested goal which relies on a goal `root` deeper in the stack.
- //
- // We first store that we may have to rerun `evaluate_goal` for `root` in case the
- // provisional response is not equal to the final response. We also update the depth
- // of all goals which recursively depend on our current goal to depend on `root`
- // instead.
- //
- // Finally we can return either the provisional response for that goal if we have a
- // coinductive cycle or an ambiguous result if the cycle is inductive.
- Entry::Occupied(entry) => {
- // FIXME: `ProvisionalEntry` should be `Copy`.
- let entry = entry.get().clone();
- cache.stack[entry.depth].has_been_used = true;
- for provisional_entry in cache.entries.values_mut() {
- provisional_entry.depth = provisional_entry.depth.min(entry.depth);
- }
-
- // NOTE: The goals on the stack aren't the only goals involved in this cycle.
- // We can also depend on goals which aren't part of the stack but coinductively
- // depend on the stack themselves. We already checked whether all the goals
- // between these goals and their root on the stack. This means that as long as
- // each goal in a cycle is checked for coinductivity by itself simply checking
- // the stack is enough.
- if cache.stack[entry.depth..]
- .iter()
- .all(|g| g.goal.value.predicate.is_coinductive(self.tcx))
- {
- Err(entry.response)
- } else {
- Err(fixme_response_maybe_no_constraints())
- }
- }
- }
- }
-
- /// We cannot simply store the result of [EvalCtxt::compute_goal] as we have to deal with
- /// coinductive cycles.
- ///
- /// When we encounter a coinductive cycle, we have to prove the final result of that cycle
- /// while we are still computing that result. Because of this we continously recompute the
- /// cycle until the result of the previous iteration is equal to the final result, at which
- /// point we are done.
- ///
- /// This function returns `true` if we were able to finalize the goal and `false` if it has
- /// updated the provisional cache and we have to recompute the current goal.
- ///
- /// FIXME: Refer to the rustc-dev-guide entry once it exists.
- pub(super) fn try_finalize_goal(
- &mut self,
- actual_goal: CanonicalGoal<'tcx>,
- response: QueryResult<'tcx>,
- ) -> bool {
- let cache = &mut self.provisional_cache;
- let StackElem { goal, has_been_used } = cache.stack.pop().unwrap();
- assert_eq!(goal, actual_goal);
-
- let provisional_entry = cache.entries.get_mut(&goal).unwrap();
- // Check whether the current stack entry is the root of a cycle.
- //
- // If so, we either move all participants of that cycle to the global cache
- // or, in case the provisional response used in the cycle is not equal to the
- // final response, have to recompute the goal after updating the provisional
- // response to the final response of this iteration.
- if has_been_used {
- if provisional_entry.response == response {
- // We simply drop all entries according to an immutable condition, so
- // query instability is not a concern here.
- #[allow(rustc::potential_query_instability)]
- cache.entries.retain(|goal, entry| match entry.depth.cmp(&cache.stack.len()) {
- Ordering::Less => true,
- Ordering::Equal => {
- Self::try_move_finished_goal_to_global_cache(
- self.tcx,
- &mut self.overflow_data,
- &cache.stack,
- // FIXME: these should be `Copy` :(
- goal.clone(),
- entry.response.clone(),
- );
- false
- }
- Ordering::Greater => bug!("entry with greater depth than the current leaf"),
- });
-
- true
- } else {
- provisional_entry.response = response;
- cache.stack.push(StackElem { goal, has_been_used: false });
- false
- }
- } else {
- Self::try_move_finished_goal_to_global_cache(
- self.tcx,
- &mut self.overflow_data,
- &cache.stack,
- goal,
- response,
- );
- cache.entries.remove(&goal);
- true
- }
- }
-
- fn try_move_finished_goal_to_global_cache(
- tcx: TyCtxt<'tcx>,
- overflow_data: &mut OverflowData,
- stack: &[StackElem<'tcx>],
- goal: CanonicalGoal<'tcx>,
- response: QueryResult<'tcx>,
- ) {
- // We move goals to the global cache if we either did not hit an overflow or if it's
- // the root goal as that will now always hit the same overflow limit.
- //
- // NOTE: We cannot move any non-root goals to the global cache even if their final result
- // isn't impacted by the overflow as that goal still has unstable query dependencies
- // because it didn't go its full depth.
- //
- // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though.
- // Tracking that info correctly isn't trivial, so I haven't implemented it for now.
- let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty();
- if should_cache_globally {
- // FIXME: move the provisional entry to the global cache.
- let _ = (tcx, goal, response);
- }
- }
-}
-
-fn fixme_response_yes_no_constraints<'tcx>() -> QueryResult<'tcx> {
- unimplemented!()
-}
-
-fn fixme_response_maybe_no_constraints<'tcx>() -> QueryResult<'tcx> {
- unimplemented!()
-}
use std::mem;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_infer::{
- infer::InferCtxt,
- traits::{
- query::NoSolution, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
- SelectionError, TraitEngine,
- },
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::{
+ query::NoSolution, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
+ PredicateObligation, SelectionError, TraitEngine,
};
use rustc_middle::ty;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
-use super::{Certainty, EvalCtxt};
+use super::{Certainty, InferCtxtEvalExt};
/// A trait engine using the new trait solver.
///
self.obligations.push(obligation);
}
- fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
- let errors = self.select_where_possible(infcx);
- if !errors.is_empty() {
- return errors;
- }
-
+ fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> {
self.obligations
.drain(..)
.map(|obligation| FulfillmentError {
obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeSelectionError(SelectionError::Unimplemented),
+ code: FulfillmentErrorCode::CodeAmbiguity,
root_obligation: obligation,
})
.collect()
let mut errors = Vec::new();
for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) {
- unimplemented!("overflow")
+ unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
}
let mut has_changed = false;
for obligation in mem::take(&mut self.obligations) {
- let mut cx = EvalCtxt::new(infcx.tcx);
- let (changed, certainty) = match cx.evaluate_goal(infcx, obligation.clone().into())
- {
+ let goal = obligation.clone().into();
+ let (changed, certainty) = match infcx.evaluate_root_goal(goal) {
Ok(result) => result,
Err(NoSolution) => {
errors.push(FulfillmentError {
obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeAmbiguity,
+ code: match goal.predicate.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::Clause::Projection(_)) => {
+ FulfillmentErrorCode::CodeProjectionError(
+ // FIXME: This could be a `Sorts` if the term is a type
+ MismatchedProjectionTypes { err: TypeError::Mismatch },
+ )
+ }
+ ty::PredicateKind::Subtype(pred) => {
+ let (a, b) = infcx.replace_bound_vars_with_placeholders(
+ goal.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(true, a, b);
+ FulfillmentErrorCode::CodeSubtypeError(
+ expected_found,
+ TypeError::Sorts(expected_found),
+ )
+ }
+ ty::PredicateKind::Coerce(pred) => {
+ let (a, b) = infcx.replace_bound_vars_with_placeholders(
+ goal.predicate.kind().rebind((pred.a, pred.b)),
+ );
+ let expected_found = ExpectedFound::new(false, a, b);
+ FulfillmentErrorCode::CodeSubtypeError(
+ expected_found,
+ TypeError::Sorts(expected_found),
+ )
+ }
+ ty::PredicateKind::ConstEquate(a, b) => {
+ let (a, b) = infcx.replace_bound_vars_with_placeholders(
+ goal.predicate.kind().rebind((a, b)),
+ );
+ let expected_found = ExpectedFound::new(true, a, b);
+ FulfillmentErrorCode::CodeConstEquateError(
+ expected_found,
+ TypeError::ConstMismatch(expected_found),
+ )
+ }
+ ty::PredicateKind::Clause(_)
+ | ty::PredicateKind::WellFormed(_)
+ | ty::PredicateKind::ObjectSafe(_)
+ | ty::PredicateKind::ClosureKind(_, _, _)
+ | ty::PredicateKind::ConstEvaluatable(_)
+ | ty::PredicateKind::TypeWellFormedFromEnv(_)
+ | ty::PredicateKind::Ambiguous => {
+ FulfillmentErrorCode::CodeSelectionError(
+ SelectionError::Unimplemented,
+ )
+ }
+ },
root_obligation: obligation,
});
continue;
self.obligations.clone()
}
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
- unimplemented!("Should be moved out of `TraitEngine`")
+ fn drain_unstalled_obligations(
+ &mut self,
+ _: &InferCtxt<'tcx>,
+ ) -> Vec<PredicateObligation<'tcx>> {
+ unimplemented!()
}
}
-use rustc_infer::infer::canonical::CanonicalVarValues;
+use rustc_infer::infer::at::ToTrace;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::InferCtxt;
+use rustc_infer::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime};
use rustc_infer::traits::query::NoSolution;
-use rustc_middle::ty::Ty;
+use rustc_infer::traits::ObligationCause;
+use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
+use rustc_middle::ty::{self, Ty, TypeFoldable};
use rustc_span::DUMMY_SP;
-use crate::solve::ExternalConstraints;
-
-use super::{Certainty, QueryResult, Response};
+use super::Goal;
/// Methods used inside of the canonical queries of the solver.
+///
+/// Most notably these do not care about diagnostics information.
+/// If you find this while looking for methods to use outside of the
+/// solver, you may look at the implementation of these method for
+/// help.
pub(super) trait InferCtxtExt<'tcx> {
fn next_ty_infer(&self) -> Ty<'tcx>;
+ fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx>;
+
+ fn eq<T: ToTrace<'tcx>>(
+ &self,
+ param_env: ty::ParamEnv<'tcx>,
+ lhs: T,
+ rhs: T,
+ ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution>;
- fn make_canonical_response(
+ fn instantiate_bound_vars_with_infer<T: TypeFoldable<'tcx> + Copy>(
&self,
- var_values: CanonicalVarValues<'tcx>,
- certainty: Certainty,
- ) -> QueryResult<'tcx>;
+ value: ty::Binder<'tcx, T>,
+ ) -> T;
}
impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
span: DUMMY_SP,
})
}
+ fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> {
+ self.next_const_var(
+ ty,
+ ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span: DUMMY_SP },
+ )
+ }
- fn make_canonical_response(
+ #[instrument(level = "debug", skip(self, param_env), ret)]
+ fn eq<T: ToTrace<'tcx>>(
&self,
- var_values: CanonicalVarValues<'tcx>,
- certainty: Certainty,
- ) -> QueryResult<'tcx> {
- let external_constraints = take_external_constraints(self)?;
-
- Ok(self.canonicalize_response(Response { var_values, external_constraints, certainty }))
+ param_env: ty::ParamEnv<'tcx>,
+ lhs: T,
+ rhs: T,
+ ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+ self.at(&ObligationCause::dummy(), param_env)
+ .define_opaque_types(false)
+ .eq(lhs, rhs)
+ .map(|InferOk { value: (), obligations }| {
+ obligations.into_iter().map(|o| o.into()).collect()
+ })
+ .map_err(|e| {
+ debug!(?e, "failed to equate");
+ NoSolution
+ })
}
-}
-#[instrument(level = "debug", skip(infcx), ret)]
-fn take_external_constraints<'tcx>(
- infcx: &InferCtxt<'tcx>,
-) -> Result<ExternalConstraints<'tcx>, NoSolution> {
- let region_obligations = infcx.take_registered_region_obligations();
- let opaque_types = infcx.take_opaque_types_for_query_response();
- Ok(ExternalConstraints {
- // FIXME: Now that's definitely wrong :)
- //
- // Should also do the leak check here I think
- regions: drop(region_obligations),
- opaque_types,
- })
+ fn instantiate_bound_vars_with_infer<T: TypeFoldable<'tcx> + Copy>(
+ &self,
+ value: ty::Binder<'tcx, T>,
+ ) -> T {
+ self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::HigherRankedType,
+ value,
+ )
+ }
}
// preserves universes and creates a unique var (in the highest universe) for each
// appearance of a region.
-// FIXME: `CanonicalVarValues` should be interned and `Copy`.
-
// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
use std::mem;
-use rustc_infer::infer::canonical::OriginalQueryValues;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc_infer::infer::canonical::{OriginalQueryValues, QueryRegionConstraints, QueryResponse};
+use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::Obligation;
-use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc_middle::infer::canonical::Certainty as OldCertainty;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate};
+use rustc_middle::ty::{
+ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
+};
use rustc_span::DUMMY_SP;
-use self::infcx_ext::InferCtxtExt;
+use crate::traits::ObligationCause;
mod assembly;
-mod cache;
mod fulfill;
mod infcx_ext;
-mod overflow;
mod project_goals;
+mod search_graph;
mod trait_goals;
pub use fulfill::FulfillmentCtxt;
}
impl Certainty {
+ pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
+
/// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
/// use this function to unify the certainty of these goals
pub fn unify_and(self, other: Certainty) -> Certainty {
}
/// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable, Default)]
pub struct ExternalConstraints<'tcx> {
// FIXME: implement this.
regions: (),
/// solver, merge the two responses again.
pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
-pub trait TyCtxtExt<'tcx> {
- fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx>;
+pub trait InferCtxtEvalExt<'tcx> {
+ /// Evaluates a goal from **outside** of the trait solver.
+ ///
+ /// Using this while inside of the solver is wrong as it uses a new
+ /// search graph which would break cycle detection.
+ fn evaluate_root_goal(
+ &self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ ) -> Result<(bool, Certainty), NoSolution>;
}
-impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> {
- fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
- let mut cx = EvalCtxt::new(self);
- cx.evaluate_canonical_goal(goal)
+impl<'tcx> InferCtxtEvalExt<'tcx> for InferCtxt<'tcx> {
+ fn evaluate_root_goal(
+ &self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ ) -> Result<(bool, Certainty), NoSolution> {
+ let mut search_graph = search_graph::SearchGraph::new(self.tcx);
+
+ let result = EvalCtxt {
+ search_graph: &mut search_graph,
+ infcx: self,
+ var_values: CanonicalVarValues::dummy(),
+ }
+ .evaluate_goal(goal);
+
+ assert!(search_graph.is_empty());
+ result
}
}
-struct EvalCtxt<'tcx> {
- tcx: TyCtxt<'tcx>,
+struct EvalCtxt<'a, 'tcx> {
+ infcx: &'a InferCtxt<'tcx>,
+ var_values: CanonicalVarValues<'tcx>,
- provisional_cache: cache::ProvisionalCache<'tcx>,
- overflow_data: overflow::OverflowData,
+ search_graph: &'a mut search_graph::SearchGraph<'tcx>,
}
-impl<'tcx> EvalCtxt<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> EvalCtxt<'tcx> {
- EvalCtxt {
- tcx,
- provisional_cache: cache::ProvisionalCache::empty(),
- overflow_data: overflow::OverflowData::new(tcx),
- }
- }
-
- /// Recursively evaluates `goal`, returning whether any inference vars have
- /// been constrained and the certainty of the result.
- fn evaluate_goal(
- &mut self,
- infcx: &InferCtxt<'tcx>,
- goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> Result<(bool, Certainty), NoSolution> {
- let mut orig_values = OriginalQueryValues::default();
- let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
- let canonical_response = self.evaluate_canonical_goal(canonical_goal)?;
- Ok((
- true, // FIXME: check whether `var_values` are an identity substitution.
- fixme_instantiate_canonical_query_response(infcx, &orig_values, canonical_response),
- ))
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
}
- fn evaluate_canonical_goal(&mut self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
- match self.try_push_stack(goal) {
+ /// The entry point of the solver.
+ ///
+ /// This function deals with (coinductive) cycles, overflow, and caching
+ /// and then calls [`EvalCtxt::compute_goal`] which contains the actual
+ /// logic of the solver.
+ ///
+ /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
+ /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
+ /// outside of it.
+ #[instrument(level = "debug", skip(tcx, search_graph), ret)]
+ fn evaluate_canonical_goal(
+ tcx: TyCtxt<'tcx>,
+ search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+ canonical_goal: CanonicalGoal<'tcx>,
+ ) -> QueryResult<'tcx> {
+ match search_graph.try_push_stack(tcx, canonical_goal) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
Err(response) => return response,
//
// FIXME: Similar to `evaluate_all`, this has to check for overflow.
loop {
- let result = self.compute_goal(goal);
+ let (ref infcx, goal, var_values) =
+ tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
+ let mut ecx = EvalCtxt { infcx, var_values, search_graph };
+ let result = ecx.compute_goal(goal);
// FIXME: `Response` should be `Copy`
- if self.try_finalize_goal(goal, result.clone()) {
+ if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
return result;
}
}
}
- fn compute_goal(&mut self, canonical_goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
- // WARNING: We're looking at a canonical value without instantiating it here.
- //
- // We have to be incredibly careful to not change the order of bound variables or
- // remove any. As we go from `Goal<'tcx, Predicate>` to `Goal` with the variants
- // of `PredicateKind` this is the case and it is and faster than instantiating and
- // recanonicalizing.
- let Goal { param_env, predicate } = canonical_goal.value;
- if let Some(kind) = predicate.kind().no_bound_vars() {
+ fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
+ let external_constraints = take_external_constraints(self.infcx)?;
+
+ Ok(self.infcx.canonicalize_response(Response {
+ var_values: self.var_values,
+ external_constraints,
+ certainty,
+ }))
+ }
+
+ /// Recursively evaluates `goal`, returning whether any inference vars have
+ /// been constrained and the certainty of the result.
+ fn evaluate_goal(
+ &mut self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ ) -> Result<(bool, Certainty), NoSolution> {
+ let mut orig_values = OriginalQueryValues::default();
+ let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
+ let canonical_response =
+ EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
+ Ok((
+ !canonical_response.value.var_values.is_identity(),
+ instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response),
+ ))
+ }
+
+ fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
+ let Goal { param_env, predicate } = goal;
+ let kind = predicate.kind();
+ if let Some(kind) = kind.no_bound_vars() {
match kind {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => self.compute_trait_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => self
- .compute_projection_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => self
- .compute_type_outlives_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => self
- .compute_region_outlives_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
+ ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
+ self.compute_trait_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
+ self.compute_projection_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
+ self.compute_type_outlives_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
+ self.compute_region_outlives_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Subtype(predicate) => {
+ self.compute_subtype_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Coerce(predicate) => {
+ self.compute_coerce_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
+ .compute_closure_kind_goal(Goal {
+ param_env,
+ predicate: (def_id, substs, kind),
+ }),
+ ty::PredicateKind::ObjectSafe(trait_def_id) => {
+ self.compute_object_safe_goal(trait_def_id)
+ }
+ ty::PredicateKind::WellFormed(arg) => {
+ self.compute_well_formed_goal(Goal { param_env, predicate: arg })
+ }
+ ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
// FIXME: implement these predicates :)
- ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::ObjectSafe(_)
- | ty::PredicateKind::ClosureKind(_, _, _)
- | ty::PredicateKind::Subtype(_)
- | ty::PredicateKind::Coerce(_)
- | ty::PredicateKind::ConstEvaluatable(_)
- | ty::PredicateKind::ConstEquate(_, _)
- | ty::PredicateKind::TypeWellFormedFromEnv(_)
- | ty::PredicateKind::Ambiguous => unimplemented!(),
+ ty::PredicateKind::ConstEvaluatable(_) | ty::PredicateKind::ConstEquate(_, _) => {
+ self.make_canonical_response(Certainty::Yes)
+ }
+ ty::PredicateKind::TypeWellFormedFromEnv(..) => {
+ bug!("TypeWellFormedFromEnv is only used for Chalk")
+ }
}
} else {
- let (infcx, goal, var_values) =
- self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
- let kind = infcx.replace_bound_vars_with_placeholders(goal.predicate.kind());
- let goal = goal.with(self.tcx, ty::Binder::dummy(kind));
- let (_, certainty) = self.evaluate_goal(&infcx, goal)?;
- infcx.make_canonical_response(var_values, certainty)
+ let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
+ let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
+ let (_, certainty) = self.evaluate_goal(goal)?;
+ self.make_canonical_response(certainty)
}
}
fn compute_type_outlives_goal(
&mut self,
- _goal: CanonicalGoal<'tcx, TypeOutlivesPredicate<'tcx>>,
+ _goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- todo!()
+ self.make_canonical_response(Certainty::Yes)
}
fn compute_region_outlives_goal(
&mut self,
- _goal: CanonicalGoal<'tcx, RegionOutlivesPredicate<'tcx>>,
+ _goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ self.make_canonical_response(Certainty::Yes)
+ }
+
+ fn compute_coerce_goal(
+ &mut self,
+ goal: Goal<'tcx, CoercePredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ self.compute_subtype_goal(Goal {
+ param_env: goal.param_env,
+ predicate: SubtypePredicate {
+ a_is_expected: false,
+ a: goal.predicate.a,
+ b: goal.predicate.b,
+ },
+ })
+ }
+
+ fn compute_subtype_goal(
+ &mut self,
+ goal: Goal<'tcx, SubtypePredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
+ // FIXME: Do we want to register a subtype relation between these vars?
+ // That won't actually reflect in the query response, so it seems moot.
+ self.make_canonical_response(Certainty::AMBIGUOUS)
+ } else {
+ let InferOk { value: (), obligations } = self
+ .infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .sub(goal.predicate.a, goal.predicate.b)?;
+ self.evaluate_all_and_make_canonical_response(
+ obligations.into_iter().map(|pred| pred.into()).collect(),
+ )
+ }
+ }
+
+ fn compute_closure_kind_goal(
+ &mut self,
+ goal: Goal<'tcx, (DefId, ty::SubstsRef<'tcx>, ty::ClosureKind)>,
) -> QueryResult<'tcx> {
- todo!()
+ let (_, substs, expected_kind) = goal.predicate;
+ let found_kind = substs.as_closure().kind_ty().to_opt_closure_kind();
+
+ let Some(found_kind) = found_kind else {
+ return self.make_canonical_response(Certainty::AMBIGUOUS);
+ };
+ if found_kind.extends(expected_kind) {
+ self.make_canonical_response(Certainty::Yes)
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
+ if self.tcx().check_is_object_safe(trait_def_id) {
+ self.make_canonical_response(Certainty::Yes)
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn compute_well_formed_goal(
+ &mut self,
+ goal: Goal<'tcx, ty::GenericArg<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ match crate::traits::wf::unnormalized_obligations(
+ self.infcx,
+ goal.param_env,
+ goal.predicate,
+ ) {
+ Some(obligations) => self.evaluate_all_and_make_canonical_response(
+ obligations.into_iter().map(|o| o.into()).collect(),
+ ),
+ None => self.make_canonical_response(Certainty::AMBIGUOUS),
+ }
}
}
-impl<'tcx> EvalCtxt<'tcx> {
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ // Recursively evaluates a list of goals to completion, returning the certainty
+ // of all of the goals.
fn evaluate_all(
&mut self,
- infcx: &InferCtxt<'tcx>,
mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
) -> Result<Certainty, NoSolution> {
let mut new_goals = Vec::new();
self.repeat_while_none(|this| {
let mut has_changed = Err(Certainty::Yes);
for goal in goals.drain(..) {
- let (changed, certainty) = match this.evaluate_goal(infcx, goal) {
+ let (changed, certainty) = match this.evaluate_goal(goal) {
Ok(result) => result,
Err(NoSolution) => return Some(Err(NoSolution)),
};
}
})
}
+
+ // Recursively evaluates a list of goals to completion, making a query response.
+ //
+ // This is just a convenient way of calling [`EvalCtxt::evaluate_all`],
+ // then [`EvalCtxt::make_canonical_response`].
+ fn evaluate_all_and_make_canonical_response(
+ &mut self,
+ goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+ ) -> QueryResult<'tcx> {
+ self.evaluate_all(goals).and_then(|certainty| self.make_canonical_response(certainty))
+ }
+}
+
+#[instrument(level = "debug", skip(infcx), ret)]
+fn take_external_constraints<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+) -> Result<ExternalConstraints<'tcx>, NoSolution> {
+ let region_obligations = infcx.take_registered_region_obligations();
+ let opaque_types = infcx.take_opaque_types_for_query_response();
+ Ok(ExternalConstraints {
+ // FIXME: Now that's definitely wrong :)
+ //
+ // Should also do the leak check here I think
+ regions: drop(region_obligations),
+ opaque_types,
+ })
}
-fn fixme_instantiate_canonical_query_response<'tcx>(
- _: &InferCtxt<'tcx>,
- _: &OriginalQueryValues<'tcx>,
- _: CanonicalResponse<'tcx>,
+fn instantiate_canonical_query_response<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ original_values: &OriginalQueryValues<'tcx>,
+ response: CanonicalResponse<'tcx>,
) -> Certainty {
- unimplemented!()
+ let Ok(InferOk { value, obligations }) = infcx
+ .instantiate_query_response_and_region_obligations(
+ &ObligationCause::dummy(),
+ ty::ParamEnv::empty(),
+ original_values,
+ &response.unchecked_map(|resp| QueryResponse {
+ var_values: resp.var_values,
+ region_constraints: QueryRegionConstraints::default(),
+ certainty: match resp.certainty {
+ Certainty::Yes => OldCertainty::Proven,
+ Certainty::Maybe(_) => OldCertainty::Ambiguous,
+ },
+ opaque_types: resp.external_constraints.opaque_types,
+ value: resp.certainty,
+ }),
+ ) else { bug!(); };
+ assert!(obligations.is_empty());
+ value
+}
+
+pub(super) fn response_no_constraints<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ goal: Canonical<'tcx, impl Sized>,
+ certainty: Certainty,
+) -> QueryResult<'tcx> {
+ Ok(Canonical {
+ max_universe: goal.max_universe,
+ variables: goal.variables,
+ value: Response {
+ var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
+ external_constraints: Default::default(),
+ certainty,
+ },
+ })
}
+++ /dev/null
-use rustc_infer::traits::query::NoSolution;
-use rustc_middle::ty::TyCtxt;
-use rustc_session::Limit;
-
-use super::{Certainty, EvalCtxt, MaybeCause, QueryResult};
-
-/// When detecting a solver overflow, we return ambiguity. Overflow can be
-/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
-///
-/// This is in issue in case of exponential blowup, e.g. if each goal on the stack
-/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit
-/// used by the solver when hitting the default limit for the first time.
-///
-/// FIXME: Get tests where always using the `default_limit` results in a hang and refer
-/// to them here. We can also improve the overflow strategy if necessary.
-pub(super) struct OverflowData {
- default_limit: Limit,
- current_limit: Limit,
- /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals.
- ///
- /// Because of this each iteration also increases the depth in addition to the stack
- /// depth.
- additional_depth: usize,
-}
-
-impl OverflowData {
- pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData {
- let default_limit = tcx.recursion_limit();
- OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 }
- }
-
- #[inline]
- pub(super) fn did_overflow(&self) -> bool {
- self.default_limit.0 != self.current_limit.0
- }
-
- #[inline]
- pub(super) fn has_overflow(&self, depth: usize) -> bool {
- !self.current_limit.value_within_limit(depth + self.additional_depth)
- }
-
- /// Updating the current limit when hitting overflow.
- fn deal_with_overflow(&mut self) {
- // When first hitting overflow we reduce the overflow limit
- // for all future goals to prevent hangs if there's an exponental
- // blowup.
- self.current_limit.0 = self.default_limit.0 / 8;
- }
-}
-
-impl<'tcx> EvalCtxt<'tcx> {
- pub(super) fn deal_with_overflow(&mut self) -> QueryResult<'tcx> {
- self.overflow_data.deal_with_overflow();
- fixme_response_overflow_no_constraints()
- }
-
- /// A `while`-loop which tracks overflow.
- pub(super) fn repeat_while_none(
- &mut self,
- mut loop_body: impl FnMut(&mut Self) -> Option<Result<Certainty, NoSolution>>,
- ) -> Result<Certainty, NoSolution> {
- let start_depth = self.overflow_data.additional_depth;
- let depth = self.provisional_cache.current_depth();
- while !self.overflow_data.has_overflow(depth) {
- if let Some(result) = loop_body(self) {
- self.overflow_data.additional_depth = start_depth;
- return result;
- }
-
- self.overflow_data.additional_depth += 1;
- }
- self.overflow_data.additional_depth = start_depth;
- self.overflow_data.deal_with_overflow();
- Ok(Certainty::Maybe(MaybeCause::Overflow))
- }
-}
-
-fn fixme_response_overflow_no_constraints<'tcx>() -> QueryResult<'tcx> {
- unimplemented!()
-}
use crate::traits::{specialization_graph, translate_substs};
-use super::assembly::{self, AssemblyCtxt};
-use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
+use super::assembly::{self, Candidate, CandidateSource};
+use super::infcx_ext::InferCtxtExt;
+use super::trait_goals::structural_traits;
+use super::{Certainty, EvalCtxt, Goal, QueryResult};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_hir::LangItem;
+use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::specialization_graph::LeafDef;
-use rustc_infer::traits::{ObligationCause, Reveal};
+use rustc_infer::traits::Reveal;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::ProjectionPredicate;
-use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
+use rustc_middle::ty::{ToPredicate, TypeVisitable};
+use rustc_span::{sym, DUMMY_SP};
use std::iter;
+use std::ops::ControlFlow;
-#[allow(dead_code)] // FIXME: implement and use all variants.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum CandidateSource {
- Impl(DefId),
- ParamEnv(usize),
- Builtin,
-}
-
-type Candidate<'tcx> = assembly::Candidate<'tcx, ProjectionPredicate<'tcx>>;
-
-impl<'tcx> EvalCtxt<'tcx> {
+impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn compute_projection_goal(
&mut self,
- goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>,
+ goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
- self.merge_project_candidates(candidates)
+ // To only compute normalization once for each projection we only
+ // normalize if the expected term is an unconstrained inference variable.
+ //
+ // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
+ // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
+ // `U` and equate it with `u32`. This means that we don't need a separate
+ // projection cache in the solver.
+ if self.term_is_fully_unconstrained(goal) {
+ let candidates = self.assemble_and_evaluate_candidates(goal);
+ self.merge_project_candidates(candidates)
+ } else {
+ let predicate = goal.predicate;
+ let unconstrained_rhs = match predicate.term.unpack() {
+ ty::TermKind::Ty(_) => self.infcx.next_ty_infer().into(),
+ ty::TermKind::Const(ct) => self.infcx.next_const_infer(ct.ty()).into(),
+ };
+ let unconstrained_predicate = ty::Clause::Projection(ProjectionPredicate {
+ projection_ty: goal.predicate.projection_ty,
+ term: unconstrained_rhs,
+ });
+ let (_has_changed, normalize_certainty) =
+ self.evaluate_goal(goal.with(self.tcx(), unconstrained_predicate))?;
+
+ let nested_eq_goals =
+ self.infcx.eq(goal.param_env, unconstrained_rhs, predicate.term)?;
+ let eval_certainty = self.evaluate_all(nested_eq_goals)?;
+ self.make_canonical_response(normalize_certainty.unify_and(eval_certainty))
+ }
+ }
+
+ /// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
+ ///
+ /// This is the case if the `term` is an inference variable in the innermost universe
+ /// and does not occur in any other part of the predicate.
+ fn term_is_fully_unconstrained(&self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>) -> bool {
+ let infcx = self.infcx;
+ let term_is_infer = match goal.predicate.term.unpack() {
+ ty::TermKind::Ty(ty) => {
+ if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
+ match infcx.probe_ty_var(vid) {
+ Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"),
+ Err(universe) => universe == infcx.universe(),
+ }
+ } else {
+ false
+ }
+ }
+ ty::TermKind::Const(ct) => {
+ if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
+ match self.infcx.probe_const_var(vid) {
+ Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"),
+ Err(universe) => universe == infcx.universe(),
+ }
+ } else {
+ false
+ }
+ }
+ };
+
+ // Guard against `<T as Trait<?0>>::Assoc = ?0>`.
+ struct ContainsTerm<'tcx> {
+ term: ty::Term<'tcx>,
+ }
+ impl<'tcx> TypeVisitor<'tcx> for ContainsTerm<'tcx> {
+ type BreakTy = ();
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if t.needs_infer() {
+ if ty::Term::from(t) == self.term {
+ ControlFlow::Break(())
+ } else {
+ t.super_visit_with(self)
+ }
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if c.needs_infer() {
+ if ty::Term::from(c) == self.term {
+ ControlFlow::Break(())
+ } else {
+ c.super_visit_with(self)
+ }
+ } else {
+ ControlFlow::Continue(())
+ }
+ }
+ }
+
+ let mut visitor = ContainsTerm { term: goal.predicate.term };
+
+ term_is_infer
+ && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue()
+ && goal.param_env.visit_with(&mut visitor).is_continue()
}
fn merge_project_candidates(
match (candidate.source, other.source) {
(CandidateSource::Impl(_), _)
| (CandidateSource::ParamEnv(_), _)
- | (CandidateSource::Builtin, _) => unimplemented!(),
+ | (CandidateSource::BuiltinImpl, _)
+ | (CandidateSource::AliasBound, _) => unimplemented!(),
}
}
}
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
- type CandidateSource = CandidateSource;
-
fn self_ty(self) -> Ty<'tcx> {
self.self_ty()
}
}
fn consider_impl_candidate(
- acx: &mut AssemblyCtxt<'_, 'tcx, ProjectionPredicate<'tcx>>,
+ ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
impl_def_id: DefId,
- ) {
- let tcx = acx.cx.tcx;
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+
let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
- let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs)
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
{
- return;
+ return Err(NoSolution);
}
- acx.infcx.probe(|_| {
- let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ ecx.infcx.probe(|_| {
+ let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
- let Ok(InferOk { obligations, .. }) = acx
- .infcx
- .at(&ObligationCause::dummy(), goal.param_env)
- .define_opaque_types(false)
- .eq(goal_trait_ref, impl_trait_ref)
- .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
- else {
- return
- };
+ let mut nested_goals = ecx.infcx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
.instantiate(tcx, impl_substs)
.into_iter()
.map(|pred| goal.with(tcx, pred));
- let nested_goals = obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect();
- let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+ nested_goals.extend(where_clause_bounds);
+ let trait_ref_certainty = ecx.evaluate_all(nested_goals)?;
+ // In case the associated item is hidden due to specialization, we have to
+ // return ambiguity this would otherwise be incomplete, resulting in
+ // unsoundness during coherence (#105782).
let Some(assoc_def) = fetch_eligible_assoc_item_def(
- acx.infcx,
+ ecx.infcx,
goal.param_env,
goal_trait_ref,
goal.predicate.def_id(),
impl_def_id
- ) else {
- return
+ )? else {
+ return ecx.make_canonical_response(trait_ref_certainty.unify_and(Certainty::AMBIGUOUS));
};
if !assoc_def.item.defaultness(tcx).has_value() {
let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
tcx,
goal_trait_ref.def_id,
- impl_trait_ref.substs,
+ impl_substs,
);
let substs = translate_substs(
- acx.infcx,
+ ecx.infcx,
goal.param_env,
impl_def_id,
impl_substs_with_gat,
let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst);
let ty = tcx.bound_type_of(assoc_def.item.def_id);
let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
- let identity_substs = ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
+ let identity_substs =
+ ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
let did = ty::WithOptConstParam::unknown(assoc_def.item.def_id);
let kind =
ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
ty.map_bound(|ty| ty.into())
};
- let Ok(InferOk { obligations, .. }) = acx
+ // The term of our goal should be fully unconstrained, so this should never fail.
+ //
+ // It can however be ambiguous when the resolved type is a projection.
+ let nested_goals = ecx
.infcx
- .at(&ObligationCause::dummy(), goal.param_env)
- .define_opaque_types(false)
- .eq(goal.predicate.term, term.subst(tcx, substs))
- .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
- else {
- return
- };
+ .eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
+ .expect("failed to unify with unconstrained term");
+ let rhs_certainty =
+ ecx.evaluate_all(nested_goals).expect("failed to unify with unconstrained term");
+
+ ecx.make_canonical_response(trait_ref_certainty.unify_and(rhs_certainty))
+ })
+ }
+
+ fn consider_assumption(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ assumption: ty::Predicate<'tcx>,
+ ) -> QueryResult<'tcx> {
+ if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
+ && poly_projection_pred.projection_def_id() == goal.predicate.def_id()
+ {
+ ecx.infcx.probe(|_| {
+ let assumption_projection_pred =
+ ecx.infcx.instantiate_bound_vars_with_infer(poly_projection_pred);
+ let nested_goals = ecx.infcx.eq(
+ goal.param_env,
+ goal.predicate.projection_ty,
+ assumption_projection_pred.projection_ty,
+ )?;
+ let subst_certainty = ecx.evaluate_all(nested_goals)?;
+
+ // The term of our goal should be fully unconstrained, so this should never fail.
+ //
+ // It can however be ambiguous when the resolved type is a projection.
+ let nested_goals = ecx
+ .infcx
+ .eq(goal.param_env, goal.predicate.term, assumption_projection_pred.term)
+ .expect("failed to unify with unconstrained term");
+ let rhs_certainty = ecx
+ .evaluate_all(nested_goals)
+ .expect("failed to unify with unconstrained term");
+
+ ecx.make_canonical_response(subst_certainty.unify_and(rhs_certainty))
+ })
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_auto_trait_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("auto traits do not have associated types: {:?}", goal);
+ }
+
+ fn consider_trait_alias_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("trait aliases do not have associated types: {:?}", goal);
+ }
+
+ fn consider_builtin_sized_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Sized` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_copy_clone_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_pointer_sized_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`PointerSized` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_fn_trait_candidates(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ goal_kind: ty::ClosureKind,
+ ) -> QueryResult<'tcx> {
+ if let Some(tupled_inputs_and_output) =
+ structural_traits::extract_tupled_inputs_and_output_from_callable(
+ ecx.tcx(),
+ goal.predicate.self_ty(),
+ goal_kind,
+ )?
+ {
+ let pred = tupled_inputs_and_output
+ .map_bound(|(inputs, output)| ty::ProjectionPredicate {
+ projection_ty: ecx
+ .tcx()
+ .mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
+ term: output.into(),
+ })
+ .to_predicate(ecx.tcx());
+ Self::consider_assumption(ecx, goal, pred)
+ } else {
+ ecx.make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ }
+
+ fn consider_builtin_tuple_candidate(
+ _ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ bug!("`Tuple` does not have an associated type: {:?}", goal);
+ }
+
+ fn consider_builtin_pointee_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+ ecx.infcx.probe(|_| {
+ let metadata_ty = match goal.predicate.self_ty().kind() {
+ ty::Bool
+ | ty::Char
+ | ty::Int(..)
+ | ty::Uint(..)
+ | ty::Float(..)
+ | ty::Array(..)
+ | ty::RawPtr(..)
+ | ty::Ref(..)
+ | ty::FnDef(..)
+ | ty::FnPtr(..)
+ | ty::Closure(..)
+ | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))
+ | ty::Generator(..)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Never
+ | ty::Foreign(..) => tcx.types.unit,
+
+ ty::Error(e) => tcx.ty_error_with_guaranteed(*e),
+
+ ty::Str | ty::Slice(_) => tcx.types.usize,
+
+ ty::Dynamic(_, _, _) => {
+ let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
+ tcx.bound_type_of(dyn_metadata)
+ .subst(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+ }
+
+ ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
+ // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+ let sized_predicate = ty::Binder::dummy(tcx.at(DUMMY_SP).mk_trait_ref(
+ LangItem::Sized,
+ [ty::GenericArg::from(goal.predicate.self_ty())],
+ ));
- let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
- let Ok(rhs_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+ let mut nested_goals = ecx.infcx.eq(
+ goal.param_env,
+ goal.predicate.term.ty().unwrap(),
+ tcx.types.unit,
+ )?;
+ nested_goals.push(goal.with(tcx, sized_predicate));
- let certainty = trait_ref_certainty.unify_and(rhs_certainty);
- acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ return ecx.evaluate_all_and_make_canonical_response(nested_goals);
+ }
+
+ ty::Adt(def, substs) if def.is_struct() => {
+ match def.non_enum_variant().fields.last() {
+ None => tcx.types.unit,
+ Some(field_def) => {
+ let self_ty = field_def.ty(tcx, substs);
+ let new_goal = goal.with(
+ tcx,
+ ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
+ );
+ return ecx.evaluate_all_and_make_canonical_response(vec![new_goal]);
+ }
+ }
+ }
+ ty::Adt(_, _) => tcx.types.unit,
+
+ ty::Tuple(elements) => match elements.last() {
+ None => tcx.types.unit,
+ Some(&self_ty) => {
+ let new_goal = goal.with(
+ tcx,
+ ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)),
+ );
+ return ecx.evaluate_all_and_make_canonical_response(vec![new_goal]);
+ }
+ },
+
+ ty::Infer(
+ ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
+ )
+ | ty::Bound(..) => bug!(
+ "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
+ goal.predicate.self_ty()
+ ),
+ };
+
+ let nested_goals =
+ ecx.infcx.eq(goal.param_env, goal.predicate.term.ty().unwrap(), metadata_ty)?;
+ ecx.evaluate_all_and_make_canonical_response(nested_goals)
})
}
+
+ fn consider_builtin_future_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ // Generators are not futures unless they come from `async` desugaring
+ let tcx = ecx.tcx();
+ if !tcx.generator_is_async(def_id) {
+ return Err(NoSolution);
+ }
+
+ let term = substs.as_generator().return_ty().into();
+
+ Self::consider_assumption(
+ ecx,
+ goal,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
+ term,
+ })
+ .to_predicate(tcx),
+ )
+ }
+
+ fn consider_builtin_generator_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ // `async`-desugared generators do not implement the generator trait
+ let tcx = ecx.tcx();
+ if tcx.generator_is_async(def_id) {
+ return Err(NoSolution);
+ }
+
+ let generator = substs.as_generator();
+
+ let name = tcx.associated_item(goal.predicate.def_id()).name;
+ let term = if name == sym::Return {
+ generator.return_ty().into()
+ } else if name == sym::Yield {
+ generator.yield_ty().into()
+ } else {
+ bug!("unexpected associated item `<{self_ty} as Generator>::{name}`")
+ };
+
+ Self::consider_assumption(
+ ecx,
+ goal,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty: ecx
+ .tcx()
+ .mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
+ term,
+ })
+ .to_predicate(tcx),
+ )
+ }
}
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
goal_trait_ref: ty::TraitRef<'tcx>,
trait_assoc_def_id: DefId,
impl_def_id: DefId,
-) -> Option<LeafDef> {
+) -> Result<Option<LeafDef>, NoSolution> {
let node_item = specialization_graph::assoc_def(infcx.tcx, impl_def_id, trait_assoc_def_id)
- .map_err(|ErrorGuaranteed { .. }| ())
- .ok()?;
+ .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
let eligible = if node_item.is_final() {
// Non-specializable items are always projectable.
}
};
- if eligible { Some(node_item) } else { None }
+ if eligible { Ok(Some(node_item)) } else { Ok(None) }
}
--- /dev/null
+//! This module both handles the global cache which stores "finished" goals,
+//! and the provisional cache which contains partially computed goals.
+//!
+//! The provisional cache is necessary when dealing with coinductive cycles.
+//!
+//! For more information about the provisional cache and coinduction in general,
+//! check out the relevant section of the rustc-dev-guide.
+//!
+//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
+//! before then or if I still haven't done that before January 2023.
+use super::overflow::OverflowData;
+use super::StackDepth;
+use crate::solve::{CanonicalGoal, QueryResult};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_index::vec::IndexVec;
+use rustc_middle::ty::TyCtxt;
+
+rustc_index::newtype_index! {
+ pub struct EntryIndex {}
+}
+
+#[derive(Debug, Clone)]
+pub(super) struct ProvisionalEntry<'tcx> {
+ // In case we have a coinductive cycle, this is the
+ // the currently least restrictive result of this goal.
+ pub(super) response: QueryResult<'tcx>,
+ // In case of a cycle, the position of deepest stack entry involved
+ // in that cycle. This is monotonically decreasing in the stack as all
+ // elements between the current stack element in the deepest stack entry
+ // involved have to also be involved in that cycle.
+ //
+ // We can only move entries to the global cache once we're complete done
+ // with the cycle. If this entry has not been involved in a cycle,
+ // this is just its own depth.
+ pub(super) depth: StackDepth,
+
+ // The goal for this entry. Should always be equal to the corresponding goal
+ // in the lookup table.
+ pub(super) goal: CanonicalGoal<'tcx>,
+}
+
+pub(super) struct ProvisionalCache<'tcx> {
+ pub(super) entries: IndexVec<EntryIndex, ProvisionalEntry<'tcx>>,
+ // FIXME: This is only used to quickly check whether a given goal
+ // is in the cache. We should experiment with using something like
+ // `SsoHashSet` here because in most cases there are only a few entries.
+ pub(super) lookup_table: FxHashMap<CanonicalGoal<'tcx>, EntryIndex>,
+}
+
+impl<'tcx> ProvisionalCache<'tcx> {
+ pub(super) fn empty() -> ProvisionalCache<'tcx> {
+ ProvisionalCache { entries: Default::default(), lookup_table: Default::default() }
+ }
+
+ pub(super) fn is_empty(&self) -> bool {
+ self.entries.is_empty() && self.lookup_table.is_empty()
+ }
+
+ /// Adds a dependency from the current leaf to `target` in the cache
+ /// to prevent us from moving any goals which depend on the current leaf
+ /// to the global cache while we're still computing `target`.
+ ///
+ /// Its important to note that `target` may already be part of a different cycle.
+ /// In this case we have to ensure that we also depend on all other goals
+ /// in the existing cycle in addition to the potentially direct cycle with `target`.
+ pub(super) fn add_dependency_of_leaf_on(&mut self, target: EntryIndex) {
+ let depth = self.entries[target].depth;
+ for provisional_entry in &mut self.entries.raw[target.index()..] {
+ // The depth of `target` is the position of the deepest goal in the stack
+ // on which `target` depends. That goal is the `root` of this cycle.
+ //
+ // Any entry which was added after `target` is either on the stack itself
+ // at which point its depth is definitely at least as high as the depth of
+ // `root`. If it's not on the stack itself it has to depend on a goal
+ // between `root` and `leaf`. If it were to depend on a goal deeper in the
+ // stack than `root`, then `root` would also depend on that goal, at which
+ // point `root` wouldn't be the root anymore.
+ debug_assert!(provisional_entry.depth >= depth);
+ provisional_entry.depth = depth;
+ }
+
+ // We only update entries which were added after `target` as no other
+ // entry should have a higher depth.
+ //
+ // Any entry which previously had a higher depth than target has to
+ // be between `target` and `root`. Because of this we would have updated
+ // its depth when calling `add_dependency_of_leaf_on(root)` for `target`.
+ if cfg!(debug_assertions) {
+ self.entries.iter().all(|e| e.depth <= depth);
+ }
+ }
+
+ pub(super) fn depth(&self, entry_index: EntryIndex) -> StackDepth {
+ self.entries[entry_index].depth
+ }
+
+ pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> QueryResult<'tcx> {
+ // FIXME: Responses should probably be `Copy` as well
+ self.entries[entry_index].response.clone()
+ }
+}
+
+pub(super) fn try_move_finished_goal_to_global_cache<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ overflow_data: &mut OverflowData,
+ stack: &IndexVec<super::StackDepth, super::StackElem<'tcx>>,
+ goal: CanonicalGoal<'tcx>,
+ response: QueryResult<'tcx>,
+) {
+ // We move goals to the global cache if we either did not hit an overflow or if it's
+ // the root goal as that will now always hit the same overflow limit.
+ //
+ // NOTE: We cannot move any non-root goals to the global cache even if their final result
+ // isn't impacted by the overflow as that goal still has unstable query dependencies
+ // because it didn't go its full depth.
+ //
+ // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though.
+ // Tracking that info correctly isn't trivial, so I haven't implemented it for now.
+ let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty();
+ if should_cache_globally {
+ // FIXME: move the provisional entry to the global cache.
+ let _ = (tcx, goal, response);
+ }
+}
--- /dev/null
+mod cache;
+mod overflow;
+
+use self::cache::ProvisionalEntry;
+use super::{CanonicalGoal, Certainty, MaybeCause, QueryResult};
+use cache::ProvisionalCache;
+use overflow::OverflowData;
+use rustc_index::vec::IndexVec;
+use rustc_middle::ty::TyCtxt;
+use std::collections::hash_map::Entry;
+
+rustc_index::newtype_index! {
+ pub struct StackDepth {}
+}
+
+struct StackElem<'tcx> {
+ goal: CanonicalGoal<'tcx>,
+ has_been_used: bool,
+}
+
+pub(super) struct SearchGraph<'tcx> {
+ /// The stack of goals currently being computed.
+ ///
+ /// An element is *deeper* in the stack if its index is *lower*.
+ stack: IndexVec<StackDepth, StackElem<'tcx>>,
+ overflow_data: OverflowData,
+ provisional_cache: ProvisionalCache<'tcx>,
+}
+
+impl<'tcx> SearchGraph<'tcx> {
+ pub(super) fn new(tcx: TyCtxt<'tcx>) -> SearchGraph<'tcx> {
+ Self {
+ stack: Default::default(),
+ overflow_data: OverflowData::new(tcx),
+ provisional_cache: ProvisionalCache::empty(),
+ }
+ }
+
+ pub(super) fn is_empty(&self) -> bool {
+ self.stack.is_empty()
+ && self.provisional_cache.is_empty()
+ && !self.overflow_data.did_overflow()
+ }
+
+ /// Tries putting the new goal on the stack, returning an error if it is already cached.
+ ///
+ /// This correctly updates the provisional cache if there is a cycle.
+ pub(super) fn try_push_stack(
+ &mut self,
+ tcx: TyCtxt<'tcx>,
+ goal: CanonicalGoal<'tcx>,
+ ) -> Result<(), QueryResult<'tcx>> {
+ // FIXME: start by checking the global cache
+
+ // Look at the provisional cache to check for cycles.
+ let cache = &mut self.provisional_cache;
+ match cache.lookup_table.entry(goal) {
+ // No entry, simply push this goal on the stack after dealing with overflow.
+ Entry::Vacant(v) => {
+ if self.overflow_data.has_overflow(self.stack.len()) {
+ return Err(self.deal_with_overflow(tcx, goal));
+ }
+
+ let depth = self.stack.push(StackElem { goal, has_been_used: false });
+ let response = super::response_no_constraints(tcx, goal, Certainty::Yes);
+ let entry_index = cache.entries.push(ProvisionalEntry { response, depth, goal });
+ v.insert(entry_index);
+ Ok(())
+ }
+ // We have a nested goal which relies on a goal `root` deeper in the stack.
+ //
+ // We first store that we may have to rerun `evaluate_goal` for `root` in case the
+ // provisional response is not equal to the final response. We also update the depth
+ // of all goals which recursively depend on our current goal to depend on `root`
+ // instead.
+ //
+ // Finally we can return either the provisional response for that goal if we have a
+ // coinductive cycle or an ambiguous result if the cycle is inductive.
+ Entry::Occupied(entry_index) => {
+ let entry_index = *entry_index.get();
+
+ cache.add_dependency_of_leaf_on(entry_index);
+ let stack_depth = cache.depth(entry_index);
+
+ self.stack[stack_depth].has_been_used = true;
+ // NOTE: The goals on the stack aren't the only goals involved in this cycle.
+ // We can also depend on goals which aren't part of the stack but coinductively
+ // depend on the stack themselves. We already checked whether all the goals
+ // between these goals and their root on the stack. This means that as long as
+ // each goal in a cycle is checked for coinductivity by itself, simply checking
+ // the stack is enough.
+ if self.stack.raw[stack_depth.index()..]
+ .iter()
+ .all(|g| g.goal.value.predicate.is_coinductive(tcx))
+ {
+ Err(cache.provisional_result(entry_index))
+ } else {
+ Err(super::response_no_constraints(
+ tcx,
+ goal,
+ Certainty::Maybe(MaybeCause::Overflow),
+ ))
+ }
+ }
+ }
+ }
+
+ /// We cannot simply store the result of [super::EvalCtxt::compute_goal] as we have to deal with
+ /// coinductive cycles.
+ ///
+ /// When we encounter a coinductive cycle, we have to prove the final result of that cycle
+ /// while we are still computing that result. Because of this we continously recompute the
+ /// cycle until the result of the previous iteration is equal to the final result, at which
+ /// point we are done.
+ ///
+ /// This function returns `true` if we were able to finalize the goal and `false` if it has
+ /// updated the provisional cache and we have to recompute the current goal.
+ ///
+ /// FIXME: Refer to the rustc-dev-guide entry once it exists.
+ pub(super) fn try_finalize_goal(
+ &mut self,
+ tcx: TyCtxt<'tcx>,
+ actual_goal: CanonicalGoal<'tcx>,
+ response: QueryResult<'tcx>,
+ ) -> bool {
+ let StackElem { goal, has_been_used } = self.stack.pop().unwrap();
+ assert_eq!(goal, actual_goal);
+
+ let cache = &mut self.provisional_cache;
+ let provisional_entry_index = *cache.lookup_table.get(&goal).unwrap();
+ let provisional_entry = &mut cache.entries[provisional_entry_index];
+ let depth = provisional_entry.depth;
+ // Was the current goal the root of a cycle and was the provisional response
+ // different from the final one.
+ if has_been_used && provisional_entry.response != response {
+ // If so, update the provisional reponse for this goal...
+ provisional_entry.response = response;
+ // ...remove all entries whose result depends on this goal
+ // from the provisional cache...
+ //
+ // That's not completely correct, as a nested goal can also
+ // depend on a goal which is lower in the stack so it doesn't
+ // actually depend on the current goal. This should be fairly
+ // rare and is hopefully not relevant for performance.
+ #[allow(rustc::potential_query_instability)]
+ cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index);
+ cache.entries.truncate(provisional_entry_index.index() + 1);
+
+ // ...and finally push our goal back on the stack and reevaluate it.
+ self.stack.push(StackElem { goal, has_been_used: false });
+ false
+ } else {
+ // If not, we're done with this goal.
+ //
+ // Check whether that this goal doesn't depend on a goal deeper on the stack
+ // and if so, move it and all nested goals to the global cache.
+ //
+ // Note that if any nested goal were to depend on something deeper on the stack,
+ // this would have also updated the depth of the current goal.
+ if depth == self.stack.next_index() {
+ for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..)
+ {
+ let actual_index = cache.lookup_table.remove(&entry.goal);
+ debug_assert_eq!(Some(i), actual_index);
+ debug_assert!(entry.depth == depth);
+ cache::try_move_finished_goal_to_global_cache(
+ tcx,
+ &mut self.overflow_data,
+ &self.stack,
+ entry.goal,
+ entry.response,
+ );
+ }
+ }
+ true
+ }
+ }
+}
--- /dev/null
+use rustc_infer::infer::canonical::Canonical;
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::Limit;
+
+use super::SearchGraph;
+use crate::solve::{response_no_constraints, Certainty, EvalCtxt, MaybeCause, QueryResult};
+
+/// When detecting a solver overflow, we return ambiguity. Overflow can be
+/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
+///
+/// This is in issue in case of exponential blowup, e.g. if each goal on the stack
+/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit
+/// used by the solver when hitting the default limit for the first time.
+///
+/// FIXME: Get tests where always using the `default_limit` results in a hang and refer
+/// to them here. We can also improve the overflow strategy if necessary.
+pub(super) struct OverflowData {
+ default_limit: Limit,
+ current_limit: Limit,
+ /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals.
+ ///
+ /// Because of this each iteration also increases the depth in addition to the stack
+ /// depth.
+ additional_depth: usize,
+}
+
+impl OverflowData {
+ pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData {
+ let default_limit = tcx.recursion_limit();
+ OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 }
+ }
+
+ #[inline]
+ pub(super) fn did_overflow(&self) -> bool {
+ self.default_limit.0 != self.current_limit.0
+ }
+
+ #[inline]
+ pub(super) fn has_overflow(&self, depth: usize) -> bool {
+ !self.current_limit.value_within_limit(depth + self.additional_depth)
+ }
+
+ /// Updating the current limit when hitting overflow.
+ fn deal_with_overflow(&mut self) {
+ // When first hitting overflow we reduce the overflow limit
+ // for all future goals to prevent hangs if there's an exponental
+ // blowup.
+ self.current_limit.0 = self.default_limit.0 / 8;
+ }
+}
+
+impl<'tcx> SearchGraph<'tcx> {
+ pub fn deal_with_overflow(
+ &mut self,
+ tcx: TyCtxt<'tcx>,
+ goal: Canonical<'tcx, impl Sized>,
+ ) -> QueryResult<'tcx> {
+ self.overflow_data.deal_with_overflow();
+ response_no_constraints(tcx, goal, Certainty::Maybe(MaybeCause::Overflow))
+ }
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ /// A `while`-loop which tracks overflow.
+ pub fn repeat_while_none(
+ &mut self,
+ mut loop_body: impl FnMut(&mut Self) -> Option<Result<Certainty, NoSolution>>,
+ ) -> Result<Certainty, NoSolution> {
+ let start_depth = self.search_graph.overflow_data.additional_depth;
+ let depth = self.search_graph.stack.len();
+ while !self.search_graph.overflow_data.has_overflow(depth) {
+ if let Some(result) = loop_body(self) {
+ self.search_graph.overflow_data.additional_depth = start_depth;
+ return result;
+ }
+
+ self.search_graph.overflow_data.additional_depth += 1;
+ }
+ self.search_graph.overflow_data.additional_depth = start_depth;
+ self.search_graph.overflow_data.deal_with_overflow();
+ Ok(Certainty::Maybe(MaybeCause::Overflow))
+ }
+}
use std::iter;
-use super::assembly::{self, AssemblyCtxt};
-use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
+use super::assembly::{self, Candidate, CandidateSource};
+use super::infcx_ext::InferCtxtExt;
+use super::{Certainty, EvalCtxt, Goal, QueryResult};
use rustc_hir::def_id::DefId;
-use rustc_infer::infer::InferOk;
+use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::ObligationCause;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::TraitPredicate;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{TraitPredicate, TypeVisitable};
use rustc_span::DUMMY_SP;
-#[allow(dead_code)] // FIXME: implement and use all variants.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum CandidateSource {
- /// Some user-defined impl with the given `DefId`.
- Impl(DefId),
- /// The n-th caller bound in the `param_env` of our goal.
- ///
- /// This is pretty much always a bound from the `where`-clauses of the
- /// currently checked item.
- ParamEnv(usize),
- /// A bound on the `self_ty` in case it is a projection or an opaque type.
- ///
- /// # Examples
- ///
- /// ```ignore (for syntax highlighting)
- /// trait Trait {
- /// type Assoc: OtherTrait;
- /// }
- /// ```
- ///
- /// We know that `<Whatever as Trait>::Assoc: OtherTrait` holds by looking at
- /// the bounds on `Trait::Assoc`.
- AliasBound(usize),
- /// A builtin implementation for some specific traits, used in cases
- /// where we cannot rely an ordinary library implementations.
- ///
- /// The most notable examples are `Sized`, `Copy` and `Clone`. This is also
- /// used for the `DiscriminantKind` and `Pointee` trait, both of which have
- /// an associated type.
- Builtin,
- /// An automatic impl for an auto trait, e.g. `Send`. These impls recursively look
- /// at the constituent types of the `self_ty` to check whether the auto trait
- /// is implemented for those.
- AutoImpl,
-}
-
-type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>;
+pub mod structural_traits;
impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
- type CandidateSource = CandidateSource;
-
fn self_ty(self) -> Ty<'tcx> {
self.self_ty()
}
}
fn consider_impl_candidate(
- acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+ ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, TraitPredicate<'tcx>>,
impl_def_id: DefId,
- ) {
- let tcx = acx.cx.tcx;
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
- let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
{
- return;
+ return Err(NoSolution);
}
- acx.infcx.probe(|_| {
- let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ ecx.infcx.probe(|_| {
+ let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
- let Ok(InferOk { obligations, .. }) = acx
- .infcx
- .at(&ObligationCause::dummy(), goal.param_env)
- .define_opaque_types(false)
- .eq(goal.predicate.trait_ref, impl_trait_ref)
- .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
- else {
- return
- };
+ let mut nested_goals =
+ ecx.infcx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
.instantiate(tcx, impl_substs)
.predicates
.into_iter()
.map(|pred| goal.with(tcx, pred));
+ nested_goals.extend(where_clause_bounds);
+ ecx.evaluate_all_and_make_canonical_response(nested_goals)
+ })
+ }
- let nested_goals =
- obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect();
+ fn consider_assumption(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ assumption: ty::Predicate<'tcx>,
+ ) -> QueryResult<'tcx> {
+ if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
+ && poly_trait_pred.def_id() == goal.predicate.def_id()
+ {
+ // FIXME: Constness and polarity
+ ecx.infcx.probe(|_| {
+ let assumption_trait_pred =
+ ecx.infcx.instantiate_bound_vars_with_infer(poly_trait_pred);
+ let nested_goals = ecx.infcx.eq(
+ goal.param_env,
+ goal.predicate.trait_ref,
+ assumption_trait_pred.trait_ref,
+ )?;
+ ecx.evaluate_all_and_make_canonical_response(nested_goals)
+ })
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_auto_trait_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.probe_and_evaluate_goal_for_constituent_tys(
+ goal,
+ structural_traits::instantiate_constituent_tys_for_auto_trait,
+ )
+ }
+
+ fn consider_trait_alias_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
- let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
- acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ ecx.infcx.probe(|_| {
+ let nested_obligations = tcx
+ .predicates_of(goal.predicate.def_id())
+ .instantiate(tcx, goal.predicate.trait_ref.substs);
+ ecx.evaluate_all_and_make_canonical_response(
+ nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)).collect(),
+ )
})
}
+
+ fn consider_builtin_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.probe_and_evaluate_goal_for_constituent_tys(
+ goal,
+ structural_traits::instantiate_constituent_tys_for_sized_trait,
+ )
+ }
+
+ fn consider_builtin_copy_clone_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.probe_and_evaluate_goal_for_constituent_tys(
+ goal,
+ structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
+ )
+ }
+
+ fn consider_builtin_pointer_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ if goal.predicate.self_ty().has_non_region_infer() {
+ return ecx.make_canonical_response(Certainty::AMBIGUOUS);
+ }
+
+ let tcx = ecx.tcx();
+ let self_ty = tcx.erase_regions(goal.predicate.self_ty());
+
+ if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
+ && let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
+ && layout.layout.size() == usize_layout.size()
+ && layout.layout.align().abi == usize_layout.align().abi
+ {
+ // FIXME: We could make this faster by making a no-constraints response
+ ecx.make_canonical_response(Certainty::Yes)
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_builtin_fn_trait_candidates(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ goal_kind: ty::ClosureKind,
+ ) -> QueryResult<'tcx> {
+ if let Some(tupled_inputs_and_output) =
+ structural_traits::extract_tupled_inputs_and_output_from_callable(
+ ecx.tcx(),
+ goal.predicate.self_ty(),
+ goal_kind,
+ )?
+ {
+ let pred = tupled_inputs_and_output
+ .map_bound(|(inputs, _)| {
+ ecx.tcx()
+ .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
+ })
+ .to_predicate(ecx.tcx());
+ Self::consider_assumption(ecx, goal, pred)
+ } else {
+ ecx.make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ }
+
+ fn consider_builtin_tuple_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
+ ecx.make_canonical_response(Certainty::Yes)
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_builtin_pointee_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ _goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.make_canonical_response(Certainty::Yes)
+ }
+
+ fn consider_builtin_future_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let ty::Generator(def_id, _, _) = *goal.predicate.self_ty().kind() else {
+ return Err(NoSolution);
+ };
+
+ // Generators are not futures unless they come from `async` desugaring
+ let tcx = ecx.tcx();
+ if !tcx.generator_is_async(def_id) {
+ return Err(NoSolution);
+ }
+
+ // Async generator unconditionally implement `Future`
+ ecx.make_canonical_response(Certainty::Yes)
+ }
+
+ fn consider_builtin_generator_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let self_ty = goal.predicate.self_ty();
+ let ty::Generator(def_id, substs, _) = *self_ty.kind() else {
+ return Err(NoSolution);
+ };
+
+ // `async`-desugared generators do not implement the generator trait
+ let tcx = ecx.tcx();
+ if tcx.generator_is_async(def_id) {
+ return Err(NoSolution);
+ }
+
+ let generator = substs.as_generator();
+ Self::consider_assumption(
+ ecx,
+ goal,
+ ty::Binder::dummy(
+ tcx.mk_trait_ref(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
+ )
+ .to_predicate(tcx),
+ )
+ }
}
-impl<'tcx> EvalCtxt<'tcx> {
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ /// Convenience function for traits that are structural, i.e. that only
+ /// have nested subgoals that only change the self type. Unlike other
+ /// evaluate-like helpers, this does a probe, so it doesn't need to be
+ /// wrapped in one.
+ fn probe_and_evaluate_goal_for_constituent_tys(
+ &mut self,
+ goal: Goal<'tcx, TraitPredicate<'tcx>>,
+ constituent_tys: impl Fn(&InferCtxt<'tcx>, Ty<'tcx>) -> Result<Vec<Ty<'tcx>>, NoSolution>,
+ ) -> QueryResult<'tcx> {
+ self.infcx.probe(|_| {
+ self.evaluate_all_and_make_canonical_response(
+ constituent_tys(self.infcx, goal.predicate.self_ty())?
+ .into_iter()
+ .map(|ty| {
+ goal.with(
+ self.tcx(),
+ ty::Binder::dummy(goal.predicate.with_self_ty(self.tcx(), ty)),
+ )
+ })
+ .collect(),
+ )
+ })
+ }
+
pub(super) fn compute_trait_goal(
&mut self,
- goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>,
+ goal: Goal<'tcx, TraitPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
+ let candidates = self.assemble_and_evaluate_candidates(goal);
self.merge_trait_candidates_discard_reservation_impls(candidates)
}
match (candidate.source, other.source) {
(CandidateSource::Impl(_), _)
| (CandidateSource::ParamEnv(_), _)
- | (CandidateSource::AliasBound(_), _)
- | (CandidateSource::Builtin, _)
- | (CandidateSource::AutoImpl, _) => unimplemented!(),
+ | (CandidateSource::AliasBound, _)
+ | (CandidateSource::BuiltinImpl, _) => unimplemented!(),
}
}
fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> {
if let CandidateSource::Impl(def_id) = candidate.source {
- if let ty::ImplPolarity::Reservation = self.tcx.impl_polarity(def_id) {
+ if let ty::ImplPolarity::Reservation = self.tcx().impl_polarity(def_id) {
debug!("Selected reservation impl");
// FIXME: reduce candidate to ambiguous
// FIXME: replace `var_values` with identity, yeet external constraints.
--- /dev/null
+use rustc_hir::{Movability, Mutability};
+use rustc_infer::{infer::InferCtxt, traits::query::NoSolution};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+// Calculates the constituent types of a type for `auto trait` purposes.
+//
+// For types with an "existential" binder, i.e. generator witnesses, we also
+// instantiate the binder with placeholders eagerly.
+pub(super) fn instantiate_constituent_tys_for_auto_trait<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ ty: Ty<'tcx>,
+) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+ let tcx = infcx.tcx;
+ match *ty.kind() {
+ ty::Uint(_)
+ | ty::Int(_)
+ | ty::Bool
+ | ty::Float(_)
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::Str
+ | ty::Error(_)
+ | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::Never
+ | ty::Char => Ok(vec![]),
+
+ ty::Dynamic(..)
+ | ty::Param(..)
+ | ty::Foreign(..)
+ | ty::Alias(ty::Projection, ..)
+ | ty::Placeholder(..) => Err(NoSolution),
+
+ ty::Bound(..)
+ | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+ bug!("unexpected type `{ty}`")
+ }
+
+ ty::RawPtr(ty::TypeAndMut { ty: element_ty, .. }) | ty::Ref(_, element_ty, _) => {
+ Ok(vec![element_ty])
+ }
+
+ ty::Array(element_ty, _) | ty::Slice(element_ty) => Ok(vec![element_ty]),
+
+ ty::Tuple(ref tys) => {
+ // (T1, ..., Tn) -- meets any bound that all of T1...Tn meet
+ Ok(tys.iter().collect())
+ }
+
+ ty::Closure(_, ref substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]),
+
+ ty::Generator(_, ref substs, _) => {
+ let generator_substs = substs.as_generator();
+ Ok(vec![generator_substs.tupled_upvars_ty(), generator_substs.witness()])
+ }
+
+ ty::GeneratorWitness(types) => {
+ Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec())
+ }
+
+ ty::GeneratorWitnessMIR(..) => todo!(),
+
+ // For `PhantomData<T>`, we pass `T`.
+ ty::Adt(def, substs) if def.is_phantom_data() => Ok(vec![substs.type_at(0)]),
+
+ ty::Adt(def, substs) => Ok(def.all_fields().map(|f| f.ty(tcx, substs)).collect()),
+
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ // We can resolve the `impl Trait` to its concrete type,
+ // which enforces a DAG between the functions requiring
+ // the auto trait bounds in question.
+ Ok(vec![tcx.bound_type_of(def_id).subst(tcx, substs)])
+ }
+ }
+}
+
+pub(super) fn instantiate_constituent_tys_for_sized_trait<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ ty: Ty<'tcx>,
+) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+ match *ty.kind() {
+ ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::Uint(_)
+ | ty::Int(_)
+ | ty::Bool
+ | ty::Float(_)
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::RawPtr(..)
+ | ty::Char
+ | ty::Ref(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Array(..)
+ | ty::Closure(..)
+ | ty::Never
+ | ty::Dynamic(_, _, ty::DynStar)
+ | ty::Error(_) => Ok(vec![]),
+
+ ty::Str
+ | ty::Slice(_)
+ | ty::Dynamic(..)
+ | ty::Foreign(..)
+ | ty::Alias(..)
+ | ty::Param(_)
+ | ty::Placeholder(..) => Err(NoSolution),
+
+ ty::Bound(..)
+ | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+ bug!("unexpected type `{ty}`")
+ }
+
+ ty::Tuple(tys) => Ok(tys.to_vec()),
+
+ ty::Adt(def, substs) => {
+ let sized_crit = def.sized_constraint(infcx.tcx);
+ Ok(sized_crit
+ .0
+ .iter()
+ .map(|ty| sized_crit.rebind(*ty).subst(infcx.tcx, substs))
+ .collect())
+ }
+ }
+}
+
+pub(super) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ ty: Ty<'tcx>,
+) -> Result<Vec<Ty<'tcx>>, NoSolution> {
+ match *ty.kind() {
+ ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::Error(_) => Ok(vec![]),
+
+ // Implementations are provided in core
+ ty::Uint(_)
+ | ty::Int(_)
+ | ty::Bool
+ | ty::Float(_)
+ | ty::Char
+ | ty::RawPtr(..)
+ | ty::Never
+ | ty::Ref(_, _, Mutability::Not)
+ | ty::Array(..) => Err(NoSolution),
+
+ ty::Dynamic(..)
+ | ty::Str
+ | ty::Slice(_)
+ | ty::Generator(_, _, Movability::Static)
+ | ty::Foreign(..)
+ | ty::Ref(_, _, Mutability::Mut)
+ | ty::Adt(_, _)
+ | ty::Alias(_, _)
+ | ty::Param(_)
+ | ty::Placeholder(..) => Err(NoSolution),
+
+ ty::Bound(..)
+ | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+ bug!("unexpected type `{ty}`")
+ }
+
+ ty::Tuple(tys) => Ok(tys.to_vec()),
+
+ ty::Closure(_, substs) => Ok(vec![substs.as_closure().tupled_upvars_ty()]),
+
+ ty::Generator(_, substs, Movability::Movable) => {
+ if infcx.tcx.features().generator_clone {
+ let generator = substs.as_generator();
+ Ok(vec![generator.tupled_upvars_ty(), generator.witness()])
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ ty::GeneratorWitness(types) => {
+ Ok(infcx.replace_bound_vars_with_placeholders(types).to_vec())
+ }
+
+ ty::GeneratorWitnessMIR(..) => todo!(),
+ }
+}
+
+// Returns a binder of the tupled inputs types and output type from a builtin callable type.
+pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ self_ty: Ty<'tcx>,
+ goal_kind: ty::ClosureKind,
+) -> Result<Option<ty::Binder<'tcx, (Ty<'tcx>, Ty<'tcx>)>>, NoSolution> {
+ match *self_ty.kind() {
+ ty::FnDef(def_id, substs) => Ok(Some(
+ tcx.fn_sig(def_id)
+ .subst(tcx, substs)
+ .map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output())),
+ )),
+ ty::FnPtr(sig) => {
+ Ok(Some(sig.map_bound(|sig| (tcx.mk_tup(sig.inputs().iter()), sig.output()))))
+ }
+ ty::Closure(_, substs) => {
+ let closure_substs = substs.as_closure();
+ match closure_substs.kind_ty().to_opt_closure_kind() {
+ Some(closure_kind) if closure_kind.extends(goal_kind) => {}
+ None => return Ok(None),
+ _ => return Err(NoSolution),
+ }
+ Ok(Some(closure_substs.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
+ }
+ ty::Bool
+ | ty::Char
+ | ty::Int(_)
+ | ty::Uint(_)
+ | ty::Float(_)
+ | ty::Adt(_, _)
+ | ty::Foreign(_)
+ | ty::Str
+ | ty::Array(_, _)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::Ref(_, _, _)
+ | ty::Dynamic(_, _, _)
+ | ty::Generator(_, _, _)
+ | ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Never
+ | ty::Tuple(_)
+ | ty::Alias(_, _)
+ | ty::Param(_)
+ | ty::Placeholder(..)
+ | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
+ | ty::Error(_) => Err(NoSolution),
+
+ ty::Bound(..)
+ | ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
+ bug!("unexpected type `{self_ty}`")
+ }
+ }
+}
ChalkEnvironmentAndGoal, FulfillmentError, FulfillmentErrorCode, PredicateObligation,
SelectionError, TraitEngine,
};
-use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
-use rustc_middle::ty::{self, TypeVisitable};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_middle::ty::TypeVisitable;
pub struct FulfillmentContext<'tcx> {
obligations: FxIndexSet<PredicateObligation<'tcx>>,
- relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
-
usable_in_snapshot: bool,
}
impl FulfillmentContext<'_> {
pub(super) fn new() -> Self {
- FulfillmentContext {
- obligations: FxIndexSet::default(),
- relationships: FxHashMap::default(),
- usable_in_snapshot: false,
- }
+ FulfillmentContext { obligations: FxIndexSet::default(), usable_in_snapshot: false }
}
pub(crate) fn new_in_snapshot() -> Self {
}
let obligation = infcx.resolve_vars_if_possible(obligation);
- super::relationships::update(self, infcx, &obligation);
-
self.obligations.insert(obligation);
}
- fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
- {
- let errors = self.select_where_possible(infcx);
-
- if !errors.is_empty() {
- return errors;
- }
- }
-
+ fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> {
// any remaining obligations are errors
self.obligations
.iter()
errors
}
- fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
- self.obligations.iter().cloned().collect()
+ fn drain_unstalled_obligations(
+ &mut self,
+ _: &InferCtxt<'tcx>,
+ ) -> Vec<PredicateObligation<'tcx>> {
+ unimplemented!()
}
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
- &mut self.relationships
+ fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+ self.obligations.iter().cloned().collect()
}
}
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_hir::CRATE_HIR_ID;
use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::util;
use rustc_middle::traits::specialization_graph::OverlapMode;
let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
let may_overlap = match (impl1_ref, impl2_ref) {
- (Some(a), Some(b)) => iter::zip(a.substs, b.substs)
+ (Some(a), Some(b)) => iter::zip(a.skip_binder().substs, b.skip_binder().substs)
.all(|(arg1, arg2)| drcx.generic_args_may_unify(arg1, arg2)),
(None, None) => {
let self_ty1 = tcx.type_of(impl1_def_id);
let header = ty::ImplHeader {
impl_def_id,
self_ty: tcx.bound_type_of(impl_def_id).subst(tcx, impl_substs),
- trait_ref: tcx.bound_impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
+ trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
};
return false;
}
- let (body_id, body_def_id) = if let Some(body_def_id) = body_def_id.as_local() {
- (tcx.hir().local_def_id_to_hir_id(body_def_id), body_def_id)
- } else {
- (CRATE_HIR_ID, CRATE_DEF_ID)
- };
+ let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID);
let ocx = ObligationCtxt::new(&infcx);
let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id);
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
Some(&infcx),
- infcx.implied_bounds_tys(param_env, body_id, wf_tys),
+ infcx.implied_bounds_tys(param_env, body_def_id, wf_tys),
);
infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env);
infcx.resolve_regions(&outlives_env).is_empty()
}
+#[instrument(level = "debug", skip(tcx), ret)]
pub fn trait_ref_is_knowable<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), Conflict> {
- debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref);
- if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
+ if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
// A downstream or cousin crate is allowed to implement some
// substitution of this trait-ref.
return Err(Conflict::Downstream);
// and if we are an intermediate owner, then we don't care
// about future-compatibility, which means that we're OK if
// we are an owner.
- if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() {
- debug!("trait_ref_is_knowable: orphan check passed");
+ if orphan_check_trait_ref(trait_ref, InCrate::Local).is_ok() {
Ok(())
} else {
- debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned");
Err(Conflict::Upstream)
}
}
trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
}
+#[derive(Debug)]
pub enum OrphanCheckErr<'tcx> {
NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>),
UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
///
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
/// 2. Some local type must appear in `Self`.
+#[instrument(level = "debug", skip(tcx), ret)]
pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
- debug!("orphan_check({:?})", impl_def_id);
-
// We only except this routine to be invoked on implementations
// of a trait, not inherent implementations.
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
- debug!("orphan_check: trait_ref={:?}", trait_ref);
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
+ debug!(?trait_ref);
// If the *trait* is local to the crate, ok.
if trait_ref.def_id.is_local() {
return Ok(());
}
- orphan_check_trait_ref(tcx, trait_ref, InCrate::Local)
+ orphan_check_trait_ref(trait_ref, InCrate::Local)
}
/// Checks whether a trait-ref is potentially implementable by a crate.
///
/// Note that this function is never called for types that have both type
/// parameters and inference variables.
+#[instrument(level = "trace", ret)]
fn orphan_check_trait_ref<'tcx>(
- tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
in_crate: InCrate,
) -> Result<(), OrphanCheckErr<'tcx>> {
- debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate);
-
if trait_ref.needs_infer() && trait_ref.needs_subst() {
bug!(
"can't orphan check a trait ref with both params and inference variables {:?}",
);
}
- let mut checker = OrphanChecker::new(tcx, in_crate);
+ let mut checker = OrphanChecker::new(in_crate);
match trait_ref.visit_with(&mut checker) {
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
}
struct OrphanChecker<'tcx> {
- tcx: TyCtxt<'tcx>,
in_crate: InCrate,
in_self_ty: bool,
/// Ignore orphan check failures and exclusively search for the first
}
impl<'tcx> OrphanChecker<'tcx> {
- fn new(tcx: TyCtxt<'tcx>, in_crate: InCrate) -> Self {
+ fn new(in_crate: InCrate) -> Self {
OrphanChecker {
- tcx,
in_crate,
in_self_ty: true,
search_first_local_ty: false,
fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
self.non_local_tys.push((t, self.in_self_ty));
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
if self.search_first_local_ty {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
}
impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> {
type BreakTy = OrphanCheckEarlyExit<'tcx>;
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
}
}
ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
- ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
- self.tcx.sess.delay_span_bug(
- DUMMY_SP,
- format!("ty_is_local invoked on closure or generator: {:?}", ty),
- );
+ ty::Closure(did, ..) | ty::Generator(did, ..) => {
+ if self.def_id_is_local(did) {
+ ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+ } else {
+ self.found_non_local_ty(ty)
+ }
+ }
+ // This should only be created when checking whether we have to check whether some
+ // auto trait impl applies. There will never be multiple impls, so we can just
+ // act as if it were a local type here.
+ ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(..) => {
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
}
ty::Alias(ty::Opaque, ..) => {
/// parameters, allowing uncovered const parameters in impls seems more useful
/// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile.
fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
// If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const
// this will be incorrect. It might be worth investigating making `predicates_of` elaborate
// all of the `ConstEvaluatable` bounds rather than having a visitor here.
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
let tcx = self.infcx.tcx;
let assumed_wf_types = tcx.assumed_wf_types(def_id);
let mut implied_bounds = FxIndexSet::default();
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let cause = ObligationCause::misc(span, hir_id);
+ let cause = ObligationCause::misc(span, def_id);
for ty in assumed_wf_types {
// FIXME(@lcnr): rustc currently does not check wf for types
// pre-normalization, meaning that implied bounds are sometimes
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
- let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
if let Err(_) =
);
let predicates =
- tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx);
- for obligation in
- elaborate_predicates_with_span(tcx, std::iter::zip(predicates.predicates, predicates.spans))
- {
+ tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
+ for obligation in elaborate_predicates_with_span(tcx, predicates.into_iter()) {
let kind = obligation.predicate.kind();
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
}
pub trait TypeErrCtxtExt<'tcx> {
+ fn build_overflow_error<T>(
+ &self,
+ predicate: &T,
+ span: Span,
+ suggest_increasing_limit: bool,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
+ where
+ T: fmt::Display
+ + TypeFoldable<'tcx>
+ + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
+ <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
+
fn report_overflow_error<T>(
&self,
predicate: &T,
})
}
}
+
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn report_fulfillment_errors(
&self,
}
}
- for (error, suppressed) in iter::zip(errors, is_suppressed) {
- if !suppressed {
- self.report_fulfillment_error(error, body_id);
+ for from_expansion in [false, true] {
+ for (error, suppressed) in iter::zip(errors, &is_suppressed) {
+ if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
+ self.report_fulfillment_error(error, body_id);
+ }
}
}
suggest_increasing_limit: bool,
mutate: impl FnOnce(&mut Diagnostic),
) -> !
+ where
+ T: fmt::Display
+ + TypeFoldable<'tcx>
+ + Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
+ <T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
+ {
+ let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
+ mutate(&mut err);
+ err.emit();
+
+ self.tcx.sess.abort_if_errors();
+ bug!();
+ }
+
+ fn build_overflow_error<T>(
+ &self,
+ predicate: &T,
+ span: Span,
+ suggest_increasing_limit: bool,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
where
T: fmt::Display
+ TypeFoldable<'tcx>
self.suggest_new_overflow_limit(&mut err);
}
- mutate(&mut err);
-
- err.emit();
- self.tcx.sess.abort_if_errors();
- bug!();
+ err
}
/// Reports that an overflow has occurred and halts compilation. We
err.note(s.as_str());
}
if let Some(ref s) = parent_label {
- let body = tcx
- .hir()
- .opt_local_def_id(obligation.cause.body_id)
- .unwrap_or_else(|| {
- tcx.hir().body_owner_def_id(hir::BodyId {
- hir_id: obligation.cause.body_id,
- })
- });
+ let body = obligation.cause.body_id;
err.span_label(tcx.def_span(body), s);
}
let mut suggested =
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
+ let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
+ suggested = if let &[cand] = &impl_candidates[..] {
+ let cand = cand.trait_ref;
+ if let (ty::FnPtr(_), ty::FnDef(..)) =
+ (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
+ {
+ err.span_suggestion(
+ span.shrink_to_hi(),
+ format!(
+ "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`",
+ cand.print_only_trait_path(),
+ cand.self_ty(),
+ ),
+ format!(" as {}", cand.self_ty()),
+ Applicability::MaybeIncorrect,
+ );
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ } || suggested;
suggested |=
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_semicolon_removal(
);
}
+ let body_hir_id =
+ self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
// Try to report a help message
if is_fn_trait
&& let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
if !self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
- obligation.cause.body_id,
+ body_hir_id,
&mut err,
true,
) {
self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
- obligation.cause.body_id,
+ body_hir_id,
&mut err,
true,
);
expected_trait_ref,
obligation.cause.code(),
found_node,
+ obligation.param_env,
)
} else {
let (closure_span, closure_arg_span, found) = found_did
ty::Generator(..) => Some(16),
ty::Foreign(..) => Some(17),
ty::GeneratorWitness(..) => Some(18),
+ ty::GeneratorWitnessMIR(..) => Some(19),
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
}
}
return None;
}
- let imp = self.tcx.impl_trait_ref(def_id).unwrap();
+ let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder();
self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false)
.map(|similarity| ImplCandidate { trait_ref: imp, similarity })
candidates.sort();
candidates.dedup();
let len = candidates.len();
- if candidates.len() == 0 {
+ if candidates.is_empty() {
return false;
}
- if candidates.len() == 1 {
- let ty_desc = match candidates[0].self_ty().kind() {
- ty::FnPtr(_) => Some("fn pointer"),
- _ => None,
- };
- let the_desc = match ty_desc {
- Some(desc) => format!(" implemented for {} `", desc),
- None => " implemented for `".to_string(),
- };
+ if let &[cand] = &candidates[..] {
+ let (desc, mention_castable) =
+ match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
+ (ty::FnPtr(_), ty::FnDef(..)) => {
+ (" implemented for fn pointer `", ", cast using `as`")
+ }
+ (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
+ _ => (" implemented for `", ""),
+ };
err.highlighted_help(vec![
- (
- format!("the trait `{}` ", candidates[0].print_only_trait_path()),
- Style::NoStyle,
- ),
+ (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle),
("is".to_string(), Style::Highlight),
- (the_desc, Style::NoStyle),
- (candidates[0].self_ty().to_string(), Style::Highlight),
+ (desc.to_string(), Style::NoStyle),
+ (cand.self_ty().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
+ (mention_castable.to_string(), Style::NoStyle),
]);
return true;
}
|| self.tcx.is_builtin_derive(def_id)
})
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
+ .map(ty::EarlyBinder::subst_identity)
.filter(|trait_ref| {
let self_ty = trait_ref.self_ty();
// Avoid mentioning type parameters.
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
- // #0> doesn't implement Sized". It may even be true that we
+ // #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
predicate.to_opt_poly_trait_pred().unwrap(),
);
if impl_candidates.len() < 10 {
+ let hir =
+ self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
- body_id.map(|id| id.hir_id).unwrap_or(obligation.cause.body_id),
+ body_id.map(|id| id.hir_id).unwrap_or(hir),
&mut err,
false,
);
}
impl<'hir> FindExprBySpan<'hir> {
- fn new(span: Span) -> Self {
+ pub fn new(span: Span) -> Self {
Self { span, result: None, ty_result: None }
}
}
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
+#[derive(Copy, Clone)]
pub enum DefIdOrName {
DefId(DefId),
Name(&'static str),
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
- let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
let impl_self_ty = impl_trait_ref.self_ty();
.unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs));
let trait_ref = trait_ref.skip_binder();
- let mut flags = vec![(
- sym::ItemContext,
- self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
- )];
+ let body_hir = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
+ let mut flags =
+ vec![(sym::ItemContext, self.describe_enclosure(body_hir).map(|s| s.to_owned()))];
match obligation.cause.code() {
ObligationCauseCode::BuiltinDerivedObligation(..)
use crate::traits::{NormalizeExt, ObligationCtxt};
use hir::def::CtorOf;
-use hir::{Expr, HirId};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
+use rustc_hir::{Expr, HirId};
use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime};
IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitable, TypeckResults,
};
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
associated_item: Option<(&'static str, Ty<'tcx>)>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
);
fn suggest_dereferences(
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
+ fn extract_callable_info(
+ &self,
+ hir_id: HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>;
+
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
fn note_conflicting_closure_bounds(
/// param for cleaner code.
fn suggest_restriction<'tcx>(
tcx: TyCtxt<'tcx>,
- hir_id: HirId,
+ item_id: LocalDefId,
hir_generics: &hir::Generics<'tcx>,
msg: &str,
err: &mut Diagnostic,
{
return;
}
- let Some(item_id) = hir_id.as_owner() else { return; };
let generics = tcx.generics_of(item_id);
// Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...
if let Some((param, bound_str, fn_sig)) =
mut err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
associated_ty: Option<(&'static str, Ty<'tcx>)>,
- body_id: hir::HirId,
+ mut body_id: LocalDefId,
) {
let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
// don't suggest `T: Sized + ?Sized`.
- let mut hir_id = body_id;
- while let Some(node) = self.tcx.hir().find(hir_id) {
+ while let Some(node) = self.tcx.hir().find_by_def_id(body_id) {
match node {
hir::Node::Item(hir::Item {
ident,
// Restricting `Self` for a single method.
suggest_restriction(
self.tcx,
- hir_id,
+ body_id,
&generics,
"`Self`",
err,
assert!(param_ty);
// Restricting `Self` for a single method.
suggest_restriction(
- self.tcx, hir_id, &generics, "`Self`", err, None, projection, trait_pred,
+ self.tcx, body_id, &generics, "`Self`", err, None, projection, trait_pred,
None,
);
return;
// Missing restriction on associated type of type parameter (unmet projection).
suggest_restriction(
self.tcx,
- hir_id,
+ body_id,
&generics,
"the associated type",
err,
// Missing restriction on associated type of type parameter (unmet projection).
suggest_restriction(
self.tcx,
- hir_id,
+ body_id,
&generics,
"the associated type",
err,
_ => {}
}
-
- hir_id = self.tcx.hir().get_parent_item(hir_id).into();
+ body_id = self.tcx.local_parent(body_id);
}
}
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
+ // It doesn't make sense to make this suggestion outside of typeck...
+ // (also autoderef will ICE...)
+ if self.typeck_results.is_none() {
+ return false;
+ }
+
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
{
return false;
}
- // This is duplicated from `extract_callable_info` in typeck, which
- // relies on autoderef, so we can't use it here.
- let found = trait_pred.self_ty().skip_binder().peel_refs();
- let Some((def_id_or_name, output, inputs)) = (match *found.kind()
- {
- ty::FnPtr(fn_sig) => {
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs()))
- }
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((
- DefIdOrName::DefId(def_id),
- fn_sig.output(),
- fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
- ))
- }
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
- && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Param(_) => {
- obligation.param_env.caller_bounds().iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::Name("type parameter"),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- _ => None,
- }) else { return false; };
- let output = self.replace_bound_vars_with_fresh_vars(
- obligation.cause.span,
+ let self_ty = self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
LateBoundRegionConversionTime::FnCall,
- output,
+ trait_pred.self_ty(),
);
- let inputs = inputs.skip_binder().iter().map(|ty| {
- self.replace_bound_vars_with_fresh_vars(
- obligation.cause.span,
- LateBoundRegionConversionTime::FnCall,
- inputs.rebind(*ty),
- )
- });
+
+ let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(
+ body_hir_id,
+ obligation.param_env,
+ self_ty,
+ ) else { return false; };
// Remapping bound vars here
let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));
};
let args = inputs
+ .into_iter()
.map(|ty| {
if ty.is_suggestable(self.tcx, false) {
format!("/* {ty} */")
span.remove_mark();
}
let mut expr_finder = FindExprBySpan::new(span);
- let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return; };
- expr_finder.visit_expr(&body);
+ let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return; };
+ let body = self.tcx.hir().body(body_id);
+ expr_finder.visit_expr(body.value);
let Some(expr) = expr_finder.result else { return; };
let Some(typeck) = &self.typeck_results else { return; };
let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; };
) -> bool {
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
let ty = self.tcx.erase_late_bound_regions(self_ty);
- let owner = self.tcx.hir().get_parent_item(obligation.cause.body_id);
- let Some(generics) = self.tcx.hir().get_generics(owner.def_id) else { return false };
+ let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else { return false };
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
let ty::Param(param) = inner_ty.kind() else { return false };
let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else { return false };
false
}
+ /// Extracts information about a callable type for diagnostics. This is a
+ /// heuristic -- it doesn't necessarily mean that a type is always callable,
+ /// because the callable type must also be well-formed to be called.
+ // FIXME(vincenzopalazzo): move the HirId to a LocalDefId
+ fn extract_callable_info(
+ &self,
+ hir_id: HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
+ // Autoderef is useful here because sometimes we box callables, etc.
+ let Some((def_id_or_name, output, inputs)) = (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
+ match *found.kind() {
+ ty::FnPtr(fn_sig) =>
+ Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
+ ty::FnDef(def_id, _) => {
+ let fn_sig = found.fn_sig(self.tcx);
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+ }
+ ty::Closure(def_id, substs) => {
+ let fn_sig = substs.as_closure().sig();
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
+ }
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ DefIdOrName::DefId(def_id),
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Dynamic(data, _, ty::Dyn) => {
+ data.iter().find_map(|pred| {
+ if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+ && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
+ // for existential projection, substs are shifted over by 1
+ && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+ {
+ Some((
+ DefIdOrName::Name("trait object"),
+ pred.rebind(proj.term.ty().unwrap()),
+ pred.rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Param(param) => {
+ let generics = self.tcx.generics_of(hir_id.owner.to_def_id());
+ let name = if generics.count() > param.index as usize
+ && let def = generics.param_at(param.index as usize, self.tcx)
+ && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
+ && def.name == param.name
+ {
+ DefIdOrName::DefId(def.def_id)
+ } else {
+ DefIdOrName::Name("type parameter")
+ };
+ param_env.caller_bounds().iter().find_map(|pred| {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+ && proj.projection_ty.self_ty() == found
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ name,
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ _ => None,
+ }
+ }) else { return None; };
+
+ let output = self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::FnCall,
+ output,
+ );
+ let inputs = inputs
+ .skip_binder()
+ .iter()
+ .map(|ty| {
+ self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::FnCall,
+ inputs.rebind(*ty),
+ )
+ })
+ .collect();
+
+ // We don't want to register any extra obligations, which should be
+ // implied by wf, but also because that would possibly result in
+ // erroneous errors later on.
+ let InferOk { value: output, obligations: _ } =
+ self.at(&ObligationCause::dummy(), param_env).normalize(output);
+
+ if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ }
+
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
span.remove_mark();
}
let mut expr_finder = super::FindExprBySpan::new(span);
- let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else {
+ let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
return false;
};
- expr_finder.visit_expr(&body);
+ let body = self.tcx.hir().body(body_id);
+ expr_finder.visit_expr(body.value);
let mut maybe_suggest = |suggested_ty, count, suggestions| {
// Remapping bound vars here
let trait_pred_and_suggested_ty =
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
let hir = self.tcx.hir();
- let parent_node = hir.parent_id(obligation.cause.body_id);
- let node = hir.find(parent_node);
+ let node = hir.find_by_def_id(obligation.cause.body_id);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node
&& let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind
&& sig.decl.output.span().overlaps(span)
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
let hir = self.tcx.hir();
- let parent_node = hir.parent_id(obligation.cause.body_id);
- let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find(parent_node) else {
+ let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find_by_def_id(obligation.cause.body_id) else {
return None;
};
}
let hir = self.tcx.hir();
- let fn_hir_id = hir.parent_id(obligation.cause.body_id);
- let node = hir.find(fn_hir_id);
+ let fn_hir_id = hir.local_def_id_to_hir_id(obligation.cause.body_id);
+ let node = hir.find_by_def_id(obligation.cause.body_id);
let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(sig, _, body_id),
..
// If the `dyn Trait` is not object safe, do not suggest `Box<dyn Trait>`.
predicates
.principal_def_id()
- .map_or(true, |def_id| self.tcx.object_safety_violations(def_id).is_empty())
+ .map_or(true, |def_id| self.tcx.check_is_object_safe(def_id))
}
// We only want to suggest `impl Trait` to `dyn Trait`s.
// For example, `fn foo() -> str` needs to be filtered out.
match liberated_sig.output().kind() {
ty::Dynamic(predicates, _, ty::Dyn) => {
- let cause = ObligationCause::misc(ret_ty.span, fn_hir_id);
+ let cause = ObligationCause::misc(ret_ty.span, obligation.cause.body_id);
let param_env = ty::ParamEnv::empty();
if !only_never_return {
}
let hir = self.tcx.hir();
- let parent_node = hir.parent_id(obligation.cause.body_id);
- let node = hir.find(parent_node);
+ let node = hir.find_by_def_id(obligation.cause.body_id);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) =
node
{
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
pub(crate) fn build_fn_sig_ty<'tcx>(
infcx: &InferCtxt<'tcx>,
self.note_conflicting_closure_bounds(cause, &mut err);
if let Some(found_node) = found_node {
- hint_missing_borrow(span, found, expected, found_node, &mut err);
+ hint_missing_borrow(self, param_env, span, found, expected, found_node, &mut err);
}
err
// Find another predicate whose self-type is equal to the expected self type,
// but whose substs don't match.
- let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans)
+ let other_pred = predicates.into_iter()
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
// If we found one, then it's very likely the cause of the error.
if let Some((_, (_, other_pred_span))) = other_pred {
err.span_note(
- *other_pred_span,
+ other_pred_span,
"closure inferred to have a different signature due to this bound",
);
}
);
match *ty.kind() {
- ty::Generator(did, ..) => {
+ ty::Generator(did, ..) | ty::GeneratorWitnessMIR(did, _) => {
generator = generator.or(Some(did));
outer_generator = Some(did);
}
);
match *ty.kind() {
- ty::Generator(did, ..) => {
+ ty::Generator(did, ..) | ty::GeneratorWitnessMIR(did, ..) => {
generator = generator.or(Some(did));
outer_generator = Some(did);
}
// generator interior are not generally known, so we
// want to erase them when comparing (and anyway,
// `Send` and other bounds are generally unaffected by
- // the choice of region). When erasing regions, we
+ // the choice of region). When erasing regions, we
// also have to erase late-bound regions. This is
// because the types that appear in the generator
// interior generally contain "bound regions" to
};
// Get the typeck results from the infcx if the generator is the function we are currently
- // type-checking; otherwise, get them by performing a query. This is needed to avoid
+ // type-checking; otherwise, get them by performing a query. This is needed to avoid
// cycles. If we can't use resolved types because the generator comes from another crate,
// we still provide a targeted error but without all the relevant spans.
let generator_data = match &self.typeck_results {
_ => return false,
};
+ let generator_within_in_progress_typeck = match &self.typeck_results {
+ Some(t) => t.hir_owner.to_def_id() == generator_did_root,
+ _ => false,
+ };
+
let mut interior_or_upvar_span = None;
let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches);
*span,
Some((*scope_span, *yield_span, *expr, from_awaited_ty)),
));
+
+ if interior_or_upvar_span.is_none() && generator_data.is_foreign() {
+ interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span, None));
+ }
+ } else if self.tcx.sess.opts.unstable_opts.drop_tracking_mir
+ // Avoid disclosing internal information to downstream crates.
+ && generator_did.is_local()
+ // Try to avoid cycles.
+ && !generator_within_in_progress_typeck
+ {
+ let generator_info = &self.tcx.mir_generator_witnesses(generator_did);
+ debug!(?generator_info);
+
+ 'find_source: for (variant, source_info) in
+ generator_info.variant_fields.iter().zip(&generator_info.variant_source_info)
+ {
+ debug!(?variant);
+ for &local in variant {
+ let decl = &generator_info.field_tys[local];
+ debug!(?decl);
+ if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits {
+ interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(
+ decl.source_info.span,
+ Some((None, source_info.span, None, from_awaited_ty)),
+ ));
+ break 'find_source;
+ }
+ }
+ }
}
if interior_or_upvar_span.is_none() {
}
err.note(msg.trim_end_matches(", "))
}
+ ty::GeneratorWitnessMIR(def_id, substs) => {
+ use std::fmt::Write;
+
+ // FIXME: this is kind of an unusual format for rustc, can we make it more clear?
+ // Maybe we should just remove this note altogether?
+ // FIXME: only print types which don't meet the trait requirement
+ let mut msg =
+ "required because it captures the following types: ".to_owned();
+ for bty in tcx.generator_hidden_types(*def_id) {
+ let ty = bty.subst(tcx, substs);
+ write!(msg, "`{}`, ", ty).unwrap();
+ }
+ err.note(msg.trim_end_matches(", "))
+ }
ty::Generator(def_id, _, _) => {
let sp = self.tcx.def_span(def_id);
trait_pred: ty::PolyTraitPredicate<'tcx>,
span: Span,
) {
- let body_hir_id = obligation.cause.body_id;
- let item_id = self.tcx.hir().parent_id(body_hir_id);
-
- if let Some(body_id) =
- self.tcx.hir().maybe_body_owned_by(self.tcx.hir().local_def_id(item_id))
- {
+ if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) {
let body = self.tcx.hir().body(body_id);
if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
term: ty_var.into(),
},
)));
+ let body_def_id = self.tcx.hir().enclosing_body_owner(body_id);
// Add `<ExprTy as Iterator>::Item = _` obligation.
ocx.register_obligation(Obligation::misc(
- self.tcx, span, body_id, param_env, projection,
+ self.tcx,
+ span,
+ body_def_id,
+ param_env,
+ projection,
));
if ocx.select_where_possible().is_empty() {
// `ty_var` now holds the type that `Item` is for `ExprTy`.
/// Add a hint to add a missing borrow or remove an unnecessary one.
fn hint_missing_borrow<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
span: Span,
found: Ty<'tcx>,
expected: Ty<'tcx>,
err: &mut Diagnostic,
) {
let found_args = match found.kind() {
- ty::FnPtr(f) => f.inputs().skip_binder().iter(),
+ ty::FnPtr(f) => infcx.replace_bound_vars_with_placeholders(*f).inputs().iter(),
kind => {
span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind)
}
};
let expected_args = match expected.kind() {
- ty::FnPtr(f) => f.inputs().skip_binder().iter(),
+ ty::FnPtr(f) => infcx.replace_bound_vars_with_placeholders(*f).inputs().iter(),
kind => {
span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
}
// This could be a variant constructor, for example.
let Some(fn_decl) = found_node.fn_decl() else { return; };
- let arg_spans = fn_decl.inputs.iter().map(|ty| ty.span);
+ let args = fn_decl.inputs.iter().map(|ty| ty);
- fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
- let mut refs = 0;
+ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
+ let mut refs = vec![];
- while let ty::Ref(_, new_ty, _) = ty.kind() {
+ while let ty::Ref(_, new_ty, mutbl) = ty.kind() {
ty = *new_ty;
- refs += 1;
+ refs.push(*mutbl);
}
(ty, refs)
let mut to_borrow = Vec::new();
let mut remove_borrow = Vec::new();
- for ((found_arg, expected_arg), arg_span) in found_args.zip(expected_args).zip(arg_spans) {
+ for ((found_arg, expected_arg), arg) in found_args.zip(expected_args).zip(args) {
let (found_ty, found_refs) = get_deref_type_and_refs(*found_arg);
let (expected_ty, expected_refs) = get_deref_type_and_refs(*expected_arg);
- if found_ty == expected_ty {
- if found_refs < expected_refs {
- to_borrow.push((arg_span, expected_arg.to_string()));
- } else if found_refs > expected_refs {
- remove_borrow.push((arg_span, expected_arg.to_string()));
+ if infcx.can_eq(param_env, found_ty, expected_ty).is_ok() {
+ // FIXME: This could handle more exotic cases like mutability mismatches too!
+ if found_refs.len() < expected_refs.len()
+ && found_refs[..] == expected_refs[expected_refs.len() - found_refs.len()..]
+ {
+ to_borrow.push((
+ arg.span.shrink_to_lo(),
+ expected_refs[..expected_refs.len() - found_refs.len()]
+ .iter()
+ .map(|mutbl| format!("&{}", mutbl.prefix_str()))
+ .collect::<Vec<_>>()
+ .join(""),
+ ));
+ } else if found_refs.len() > expected_refs.len() {
+ let mut span = arg.span.shrink_to_lo();
+ let mut left = found_refs.len() - expected_refs.len();
+ let mut ty = arg;
+ while let hir::TyKind::Ref(_, mut_ty) = &ty.kind && left > 0 {
+ span = span.with_hi(mut_ty.ty.span.lo());
+ ty = mut_ty.ty;
+ left -= 1;
+ }
+ let sugg = if left == 0 {
+ (span, String::new())
+ } else {
+ (arg.span, expected_arg.to_string())
+ };
+ remove_borrow.push(sugg);
}
}
}
if !to_borrow.is_empty() {
- err.multipart_suggestion(
+ err.multipart_suggestion_verbose(
"consider borrowing the argument",
to_borrow,
Applicability::MaybeIncorrect,
}
if !remove_borrow.is_empty() {
- err.multipart_suggestion(
+ err.multipart_suggestion_verbose(
"do not borrow the argument",
remove_borrow,
Applicability::MaybeIncorrect,
use crate::infer::{InferCtxt, TyOrConstInferVar};
-use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::obligation_forest::ProcessResult;
use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
use rustc_data_structures::obligation_forest::{ObligationForest, ObligationProcessor};
// fulfillment context.
predicates: ObligationForest<PendingPredicateObligation<'tcx>>,
- relationships: FxHashMap<ty::TyVid, ty::FoundRelationships>,
-
// Is it OK to register obligations into this infcx inside
// an infcx snapshot?
//
impl<'a, 'tcx> FulfillmentContext<'tcx> {
/// Creates a new fulfillment context.
pub(super) fn new() -> FulfillmentContext<'tcx> {
- FulfillmentContext {
- predicates: ObligationForest::new(),
- relationships: FxHashMap::default(),
- usable_in_snapshot: false,
- }
+ FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: false }
}
pub(super) fn new_in_snapshot() -> FulfillmentContext<'tcx> {
- FulfillmentContext {
- predicates: ObligationForest::new(),
- relationships: FxHashMap::default(),
- usable_in_snapshot: true,
- }
+ FulfillmentContext { predicates: ObligationForest::new(), usable_in_snapshot: true }
}
/// Attempts to select obligations using `selcx`.
assert!(!infcx.is_in_snapshot() || self.usable_in_snapshot);
- super::relationships::update(self, infcx, &obligation);
-
self.predicates
.register_obligation(PendingPredicateObligation { obligation, stalled_on: vec![] });
}
- fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
- {
- let errors = self.select_where_possible(infcx);
- if !errors.is_empty() {
- return errors;
- }
- }
-
+ fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>> {
self.predicates.to_errors(CodeAmbiguity).into_iter().map(to_fulfillment_error).collect()
}
self.select(selcx)
}
- fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
- self.predicates.map_pending_obligations(|o| o.obligation.clone())
+ fn drain_unstalled_obligations(
+ &mut self,
+ infcx: &InferCtxt<'tcx>,
+ ) -> Vec<PredicateObligation<'tcx>> {
+ let mut processor = DrainProcessor { removed_predicates: Vec::new(), infcx };
+ let outcome: Outcome<_, _> = self.predicates.process_obligations(&mut processor);
+ assert!(outcome.errors.is_empty());
+ return processor.removed_predicates;
+
+ struct DrainProcessor<'a, 'tcx> {
+ infcx: &'a InferCtxt<'tcx>,
+ removed_predicates: Vec<PredicateObligation<'tcx>>,
+ }
+
+ impl<'tcx> ObligationProcessor for DrainProcessor<'_, 'tcx> {
+ type Obligation = PendingPredicateObligation<'tcx>;
+ type Error = !;
+ type OUT = Outcome<Self::Obligation, Self::Error>;
+
+ fn needs_process_obligation(&self, pending_obligation: &Self::Obligation) -> bool {
+ pending_obligation
+ .stalled_on
+ .iter()
+ .any(|&var| self.infcx.ty_or_const_infer_var_changed(var))
+ }
+
+ fn process_obligation(
+ &mut self,
+ pending_obligation: &mut PendingPredicateObligation<'tcx>,
+ ) -> ProcessResult<PendingPredicateObligation<'tcx>, !> {
+ assert!(self.needs_process_obligation(pending_obligation));
+ self.removed_predicates.push(pending_obligation.obligation.clone());
+ ProcessResult::Changed(vec![])
+ }
+
+ fn process_backedge<'c, I>(
+ &mut self,
+ cycle: I,
+ _marker: PhantomData<&'c PendingPredicateObligation<'tcx>>,
+ ) -> Result<(), !>
+ where
+ I: Clone + Iterator<Item = &'c PendingPredicateObligation<'tcx>>,
+ {
+ self.removed_predicates.extend(cycle.map(|c| c.obligation.clone()));
+ Ok(())
+ }
+ }
}
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
- &mut self.relationships
+ fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+ self.predicates.map_pending_obligations(|o| o.obligation.clone())
}
}
}
ty::PredicateKind::ObjectSafe(trait_def_id) => {
- if !self.selcx.tcx().is_object_safe(trait_def_id) {
+ if !self.selcx.tcx().check_is_object_safe(trait_def_id) {
ProcessResult::Error(CodeSelectionError(Unimplemented))
} else {
ProcessResult::Changed(vec![])
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
-use crate::infer::InferCtxtExt as _;
use crate::traits::{self, ObligationCause};
+use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
+use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
-use crate::traits::error_reporting::TypeErrCtxtExt;
+use super::outlives_bounds::InferCtxtExt;
-#[derive(Clone)]
pub enum CopyImplementationError<'tcx> {
- InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>)>),
+ InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
NotAnAdt,
HasDestructor,
}
-pub fn can_type_implement_copy<'tcx>(
+pub enum InfringingFieldsReason<'tcx> {
+ Fulfill(Vec<FulfillmentError<'tcx>>),
+ Regions(Vec<RegionResolutionError<'tcx>>),
+}
+
+/// Checks that the fields of the type (an ADT) all implement copy.
+///
+/// If fields don't implement copy, return an error containing a list of
+/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`.
+pub fn type_allowed_to_implement_copy<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
self_type: Ty<'tcx>,
parent_cause: ObligationCause<'tcx>,
) -> Result<(), CopyImplementationError<'tcx>> {
- // FIXME: (@jroesch) float this code up
- let infcx = tcx.infer_ctxt().build();
let (adt, substs) = match self_type.kind() {
// These types used to have a builtin impl.
// Now libcore provides that impl.
_ => return Err(CopyImplementationError::NotAnAdt),
};
+ let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));
+
let mut infringing = Vec::new();
for variant in adt.variants() {
for field in &variant.fields {
- let ty = field.ty(tcx, substs);
- if ty.references_error() {
+ // Do this per-field to get better error messages.
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = traits::ObligationCtxt::new(&infcx);
+
+ let unnormalized_ty = field.ty(tcx, substs);
+ if unnormalized_ty.references_error() {
continue;
}
- let span = tcx.def_span(field.did);
+
+ let field_span = tcx.def_span(field.did);
+ let field_ty_span = match tcx.hir().get_if_local(field.did) {
+ Some(hir::Node::Field(field_def)) => field_def.ty.span,
+ _ => field_span,
+ };
+
// FIXME(compiler-errors): This gives us better spans for bad
// projection types like in issue-50480.
// If the ADT has substs, point to the cause we are given.
// If it does not, then this field probably doesn't normalize
// to begin with, and point to the bad field's span instead.
- let cause = if field
+ let normalization_cause = if field
.ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
.has_non_region_param()
{
parent_cause.clone()
} else {
- ObligationCause::dummy_with_span(span)
- };
- match traits::fully_normalize(&infcx, cause, param_env, ty) {
- Ok(ty) => {
- if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
- infringing.push((field, ty));
- }
- }
- Err(errors) => {
- infcx.err_ctxt().report_fulfillment_errors(&errors, None);
- }
+ ObligationCause::dummy_with_span(field_ty_span)
};
+ let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
+ let normalization_errors = ocx.select_where_possible();
+ if !normalization_errors.is_empty() {
+ tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation"));
+ continue;
+ }
+
+ ocx.register_bound(
+ ObligationCause::dummy_with_span(field_ty_span),
+ param_env,
+ ty,
+ copy_def_id,
+ );
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
+ }
+
+ // Check regions assuming the self type of the impl is WF
+ let outlives_env = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(&infcx),
+ infcx.implied_bounds_tys(
+ param_env,
+ parent_cause.body_id,
+ FxIndexSet::from_iter([self_type]),
+ ),
+ );
+ infcx.process_registered_region_obligations(
+ outlives_env.region_bound_pairs(),
+ param_env,
+ );
+ let errors = infcx.resolve_regions(&outlives_env);
+ if !errors.is_empty() {
+ infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
+ }
}
}
+
if !infringing.is_empty() {
return Err(CopyImplementationError::InfrigingFields(infringing));
}
+
if adt.has_dtor(tcx) {
return Err(CopyImplementationError::HasDestructor);
}
pub mod outlives_bounds;
mod project;
pub mod query;
-pub(crate) mod relationships;
mod select;
mod specialize;
mod structural_match;
use crate::traits::error_reporting::TypeErrCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_span::def_id::{DefId, CRATE_DEF_ID};
use rustc_span::Span;
use std::fmt::Debug;
param_env: ty::ParamEnv<'tcx>,
generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
- std::iter::zip(generic_bounds.predicates, generic_bounds.spans).enumerate().map(
- move |(idx, (predicate, span))| Obligation {
- cause: cause(idx, span),
- recursion_depth: 0,
- param_env,
- predicate,
- },
- )
+ generic_bounds.into_iter().enumerate().map(move |(idx, (predicate, span))| Obligation {
+ cause: cause(idx, span),
+ recursion_depth: 0,
+ param_env,
+ predicate,
+ })
}
/// Determines whether the type `ty` is known to meet `bound` and
// We can use a dummy node-id here because we won't pay any mind
// to region obligations that arise (there shouldn't really be any
// anyhow).
- cause: ObligationCause::misc(span, hir::CRATE_HIR_ID),
+ cause: ObligationCause::misc(span, CRATE_DEF_ID),
recursion_depth: 0,
predicate: pred.to_predicate(infcx.tcx),
};
// that guess. While imperfect, I believe this is sound.
// FIXME(@lcnr): this function doesn't seem right.
+ //
// The handling of regions in this area of the code is terrible,
// see issue #29149. We should be able to improve on this with
// NLL.
let errors = fully_solve_obligation(infcx, obligation);
- // Note: we only assume something is `Copy` if we can
- // *definitively* show that it implements `Copy`. Otherwise,
- // assume it is move; linear is always ok.
match &errors[..] {
[] => true,
errors => {
// the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
// then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
//
- // This works fairly well because trait matching does not actually care about param-env
+ // This works fairly well because trait matching does not actually care about param-env
// TypeOutlives predicates - these are normally used by regionck.
let outlives_predicates: Vec<_> = predicates
.drain_filter(|predicate| {
&& let param_def_id = self.generics.type_param(param, self.tcx).def_id
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
t.super_visit_with(self)
}
&& let param_def_id = self.generics.region_param(¶m, self.tcx).def_id
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
r.super_visit_with(self)
}
&& let param_def_id = self.generics.const_param(¶m, self.tcx).def_id
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
ct.super_visit_with(self)
}
let generics = tcx.generics_of(trait_item_def_id);
let predicates = tcx.predicates_of(trait_item_def_id);
- let impl_trait_ref =
- tcx.impl_trait_ref(impl_def_id).expect("expected impl to correspond to trait");
+ let impl_trait_ref = tcx
+ .impl_trait_ref(impl_def_id)
+ .expect("expected impl to correspond to trait")
+ .subst_identity();
let param_env = tcx.param_env(impl_def_id);
let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
)
}
+fn check_is_object_safe(tcx: TyCtxt<'_>, trait_def_id: DefId) -> bool {
+ let violations = tcx.object_safety_violations(trait_def_id);
+
+ if violations.is_empty() {
+ return true;
+ }
+
+ // If the trait contains any other violations, then let the error reporting path
+ // report it instead of emitting a warning here.
+ if violations.iter().all(|violation| {
+ matches!(
+ violation,
+ ObjectSafetyViolation::Method(_, MethodViolationCode::WhereClauseReferencesSelf, _)
+ )
+ }) {
+ for violation in violations {
+ if let ObjectSafetyViolation::Method(
+ _,
+ MethodViolationCode::WhereClauseReferencesSelf,
+ span,
+ ) = violation
+ {
+ lint_object_unsafe_trait(tcx, *span, trait_def_id, &violation);
+ }
+ }
+ return true;
+ }
+
+ false
+}
+
/// We say a method is *vtable safe* if it can be invoked on a trait
/// object. Note that object-safe traits can have some
/// non-vtable-safe methods, so long as they require `Self: Sized` or
object_safety_violation_for_method(tcx, trait_def_id, &item)
.map(|(code, span)| ObjectSafetyViolation::Method(item.name, code, span))
})
- .filter(|violation| {
- if let ObjectSafetyViolation::Method(
- _,
- MethodViolationCode::WhereClauseReferencesSelf,
- span,
- ) = violation
- {
- lint_object_unsafe_trait(tcx, *span, trait_def_id, &violation);
- false
- } else {
- true
- }
- })
.collect();
// Check the trait itself.
trait_def_id: DefId,
method: &ty::AssocItem,
) -> Option<MethodViolationCode> {
- let sig = tcx.fn_sig(method.def_id);
+ let sig = tcx.fn_sig(method.def_id).subst_identity();
// The method's first parameter must be named `self`
if !method.fn_has_self_parameter {
match t.kind() {
ty::Param(_) => {
if t == self.tcx.types.self_param {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
ty::Alias(ty::Projection, ref data)
if self.tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder =>
{
// We'll deny these later in their own pass
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
ty::Alias(ty::Projection, ref data) => {
// This is a projected type `<Foo as SomeTrait>::X`.
// SomeTrait` is in fact a supertrait of the
// current trait. In that case, this type is
// legal, because the type `X` will be specified
- // in the object type. Note that we can just use
+ // in the object type. Note that we can just use
// direct equality here because all of these types
// are part of the formal parameter listing, and
// hence there should be no inference variables.
.contains(&data.trait_ref(self.tcx).def_id);
if is_supertrait_of_current_trait {
- ControlFlow::CONTINUE // do not walk contained types, do not report error, do collect $200
+ ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200
} else {
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
}
}
pub fn provide(providers: &mut ty::query::Providers) {
- *providers = ty::query::Providers { object_safety_violations, ..*providers };
+ *providers =
+ ty::query::Providers { object_safety_violations, check_is_object_safe, ..*providers };
}
use crate::traits::query::NoSolution;
use crate::traits::ObligationCause;
use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir as hir;
-use rustc_hir::HirId;
use rustc_middle::ty::{self, ParamEnv, Ty};
+use rustc_span::def_id::LocalDefId;
pub use rustc_middle::traits::query::OutlivesBound;
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
ty: Ty<'tcx>,
) -> Vec<OutlivesBound<'tcx>>;
fn implied_bounds_tys(
&'a self,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx>;
}
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
ty: Ty<'tcx>,
) -> Vec<OutlivesBound<'tcx>> {
- let span = self.tcx.hir().span(body_id);
+ let span = self.tcx.def_span(body_id);
let result = param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
.fully_perform(self);
fn implied_bounds_tys(
&'a self,
param_env: ParamEnv<'tcx>,
- body_id: HirId,
+ body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx> {
tys.into_iter()
}
// Prefer where-clauses. As in select, if there are multiple
- // candidates, we prefer where-clause candidates over impls. This
+ // candidates, we prefer where-clause candidates over impls. This
// may seem a bit surprising, since impls are the source of
// "truth" in some sense, but in fact some of the impls that SEEM
// applicable are not, because of nested obligations. Where
}
Err(ProjectionCacheEntry::InProgress) => {
// Under lazy normalization, this can arise when
- // bootstrapping. That is, imagine an environment with a
+ // bootstrapping. That is, imagine an environment with a
// where-clause like `A::B == u32`. Now, if we are asked
// to normalize `A::B`, we will want to check the
// where-clauses in scope. So we will try to unify `A::B`
// Check whether the self-type is itself a projection.
// If so, extract what we know from the trait and try to come up with a good answer.
let bounds = match *obligation.predicate.self_ty().kind() {
- ty::Alias(_, ref data) => tcx.bound_item_bounds(data.def_id).subst(tcx, data.substs),
+ ty::Alias(_, ref data) => tcx.item_bounds(data.def_id).subst(tcx, data.substs),
ty::Infer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
// being a projected type, so induce an ambiguity.
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Never
| ty::Tuple(..)
// Integers and floats always have `u8` as their discriminant.
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Never
// Extern types have unit metadata, according to RFC 2850
| ty::Foreign(_)
tcx.predicates_of(impl_fn_def_id).instantiate(tcx, impl_fn_substs),
&mut obligations,
);
- obligations.extend(std::iter::zip(predicates.predicates, predicates.spans).map(
- |(pred, span)| {
- Obligation::with_depth(
- tcx,
- ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- if span.is_dummy() {
- super::ItemObligation(impl_fn_def_id)
- } else {
- super::BindingObligation(impl_fn_def_id, span)
- },
- ),
- obligation.recursion_depth + 1,
- obligation.param_env,
- pred,
- )
- },
- ));
+ obligations.extend(predicates.into_iter().map(|(pred, span)| {
+ Obligation::with_depth(
+ tcx,
+ ObligationCause::new(
+ obligation.cause.span,
+ obligation.cause.body_id,
+ if span.is_dummy() {
+ super::ItemObligation(impl_fn_def_id)
+ } else {
+ super::BindingObligation(impl_fn_def_id, span)
+ },
+ ),
+ obligation.recursion_depth + 1,
+ obligation.param_env,
+ pred,
+ )
+ }));
let ty = normalize_with_depth_to(
selcx,
nested: &mut Vec<PredicateObligation<'tcx>>,
) {
let tcx = selcx.tcx();
- let own = tcx
+ let predicates = tcx
.predicates_of(obligation.predicate.def_id)
.instantiate_own(tcx, obligation.predicate.substs);
- for (predicate, span) in std::iter::zip(own.predicates, own.spans) {
+ for (predicate, span) in predicates {
let normalized = normalize_with_depth_to(
selcx,
obligation.param_env,
| ty::FnPtr(_)
| ty::Char
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::RawPtr(_)
| ty::Ref(..)
| ty::Str
use rustc_middle::ty;
+use rustc_session::config::TraitSolver;
use crate::infer::canonical::OriginalQueryValues;
use crate::infer::InferCtxt;
+use crate::solve::{Certainty, Goal, InferCtxtEvalExt, MaybeCause};
use crate::traits::{EvaluationResult, OverflowError, PredicateObligation, SelectionContext};
pub trait InferCtxtExt<'tcx> {
_ => obligation.param_env.without_const(),
};
- let c_pred = self
- .canonicalize_query_keep_static(param_env.and(obligation.predicate), &mut _orig_values);
- // Run canonical query. If overflow occurs, rerun from scratch but this time
- // in standard trait query mode so that overflow is handled appropriately
- // within `SelectionContext`.
- self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
+ if self.tcx.sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+ let c_pred = self.canonicalize_query_keep_static(
+ param_env.and(obligation.predicate),
+ &mut _orig_values,
+ );
+ self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
+ } else {
+ self.probe(|snapshot| {
+ if let Ok((_, certainty)) =
+ self.evaluate_root_goal(Goal::new(self.tcx, param_env, obligation.predicate))
+ {
+ match certainty {
+ Certainty::Yes => {
+ if self.opaque_types_added_in_snapshot(snapshot) {
+ Ok(EvaluationResult::EvaluatedToOkModuloOpaqueTypes)
+ } else if self.region_constraints_added_in_snapshot(snapshot).is_some()
+ {
+ Ok(EvaluationResult::EvaluatedToOkModuloRegions)
+ } else {
+ Ok(EvaluationResult::EvaluatedToOk)
+ }
+ }
+ Certainty::Maybe(MaybeCause::Ambiguity) => {
+ Ok(EvaluationResult::EvaluatedToAmbig)
+ }
+ Certainty::Maybe(MaybeCause::Overflow) => Err(OverflowError::Canonical),
+ }
+ } else {
+ Ok(EvaluationResult::EvaluatedToErr)
+ }
+ })
+ }
}
// Helper function that canonicalizes and runs the query. If an
&self,
obligation: &PredicateObligation<'tcx>,
) -> EvaluationResult {
+ // Run canonical query. If overflow occurs, rerun from scratch but this time
+ // in standard trait query mode so that overflow is handled appropriately
+ // within `SelectionContext`.
match self.evaluate_obligation(obligation) {
Ok(result) => result,
Err(OverflowError::Canonical) => {
.escaping
.max(t.outer_exclusive_binder().as_usize() - self.outer_index.as_usize());
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
#[inline]
}
_ => {}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => {
self.escaping =
self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => ct.super_visit_with(self),
}
// wait to fold the substs.
// Wrap this in a closure so we don't accidentally return from the outer function
- let res = (|| match *ty.kind() {
+ let res = match *ty.kind() {
// This is really important. While we *can* handle this, this has
// severe performance implications for large opaque types with
// late-bound regions. See `issue-88862` benchmark.
{
// Only normalize `impl Trait` outside of type inference, usually in codegen.
match self.param_env.reveal() {
- Reveal::UserFacing => ty.try_super_fold_with(self),
+ Reveal::UserFacing => ty.try_super_fold_with(self)?,
Reveal::All => {
let substs = substs.try_fold_with(self)?;
let recursion_limit = self.tcx().recursion_limit();
if !recursion_limit.value_within_limit(self.anon_depth) {
- self.infcx.err_ctxt().report_overflow_error(
- &ty,
- self.cause.span,
- true,
- |_| {},
- );
+ // A closure or generator may have itself as in its upvars.
+ // This should be checked handled by the recursion check for opaque
+ // types, but we may end up here before that check can happen.
+ // In that case, we delay a bug to mark the trip, and continue without
+ // revealing the opaque.
+ self.infcx
+ .err_ctxt()
+ .build_overflow_error(&ty, self.cause.span, true)
+ .delay_as_bug();
+ return ty.try_super_fold_with(self);
}
let generic_ty = self.tcx().bound_type_of(def_id);
}
let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));
self.anon_depth -= 1;
- folded_ty
+ folded_ty?
}
}
}
// `tcx.normalize_projection_ty` may normalize to a type that still has
// unevaluated consts, so keep normalizing here if that's the case.
if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
- Ok(res.try_super_fold_with(self)?)
+ res.try_super_fold_with(self)?
} else {
- Ok(res)
+ res
}
}
// `tcx.normalize_projection_ty` may normalize to a type that still has
// unevaluated consts, so keep normalizing here if that's the case.
if res != ty && res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) {
- Ok(res.try_super_fold_with(self)?)
+ res.try_super_fold_with(self)?
} else {
- Ok(res)
+ res
}
}
- _ => ty.try_super_fold_with(self),
- })()?;
+ _ => ty.try_super_fold_with(self)?,
+ };
self.cache.insert(ty, res);
Ok(res)
+++ /dev/null
-use crate::infer::InferCtxt;
-use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::PredicateObligation;
-use rustc_infer::traits::TraitEngine;
-use rustc_middle::ty;
-
-pub(crate) fn update<'tcx, T>(
- engine: &mut T,
- infcx: &InferCtxt<'tcx>,
- obligation: &PredicateObligation<'tcx>,
-) where
- T: TraitEngine<'tcx>,
-{
- // (*) binder skipped
- if let ty::PredicateKind::Clause(ty::Clause::Trait(tpred)) = obligation.predicate.kind().skip_binder()
- && let Some(ty) = infcx.shallow_resolve(tpred.self_ty()).ty_vid().map(|t| infcx.root_var(t))
- && infcx.tcx.lang_items().sized_trait().map_or(false, |st| st != tpred.trait_ref.def_id)
- {
- let new_self_ty = infcx.tcx.types.unit;
-
- // Then construct a new obligation with Self = () added
- // to the ParamEnv, and see if it holds.
- let o = obligation.with(infcx.tcx,
- obligation
- .predicate
- .kind()
- .rebind(
- // (*) binder moved here
- ty::PredicateKind::Clause(ty::Clause::Trait(tpred.with_self_ty(infcx.tcx, new_self_ty)))
- ),
- );
- // Don't report overflow errors. Otherwise equivalent to may_hold.
- if let Ok(result) = infcx.probe(|_| infcx.evaluate_obligation(&o)) && result.may_apply() {
- engine.relationships().entry(ty).or_default().self_in_trait = true;
- }
- }
-
- if let ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) =
- obligation.predicate.kind().skip_binder()
- {
- // If the projection predicate (Foo::Bar == X) has X as a non-TyVid,
- // we need to make it into one.
- if let Some(vid) = predicate.term.ty().and_then(|ty| ty.ty_vid()) {
- debug!("relationship: {:?}.output = true", vid);
- engine.relationships().entry(vid).or_default().output = true;
- }
- }
-}
.param_env
.caller_bounds()
.iter()
- .filter_map(|p| p.to_opt_poly_trait_pred())
- .filter(|p| !p.references_error());
+ .filter(|p| !p.references_error())
+ .filter_map(|p| p.to_opt_poly_trait_pred());
// Micro-optimization: filter out predicates relating to different traits.
let matching_bounds =
// Before we create the substitutions and everything, first
// consider a "quick reject". This avoids creating more types
// and so forth that we need to.
- let impl_trait_ref = self.tcx().bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) {
return;
}
}
ty::Param(..) | ty::Alias(ty::Projection, ..) => {
// In these cases, we don't know what the actual
- // type is. Therefore, we cannot break it down
+ // type is. Therefore, we cannot break it down
// into its constituent types. So we don't
// consider the `..` impl but instead just add no
// candidates: this means that typeck will only
if let Some(principal) = data.principal() {
if !self.infcx.tcx.features().object_safe_for_dispatch {
principal.with_self_ty(self.tcx(), self_ty)
- } else if self.tcx().is_object_safe(principal.def_id()) {
+ } else if self.tcx().check_is_object_safe(principal.def_id()) {
principal.with_self_ty(self.tcx(), self_ty)
} else {
return;
| ty::Closure(..)
| ty::Generator(..)
| ty::Tuple(_)
- | ty::GeneratorWitness(_) => {
+ | ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..) => {
// These are built-in, and cannot have a custom `impl const Destruct`.
candidates.vec.push(ConstDestructCandidate(None));
}
| ty::Closure(_, _)
| ty::Generator(_, _, _)
| ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
| ty::Never
| ty::Alias(..)
| ty::Param(_)
//!
//! Confirmation unifies the output type parameters of the trait
//! with the values found in the obligation, possibly yielding a
-//! type error. See the [rustc dev guide] for more details.
+//! type error. See the [rustc dev guide] for more details.
//!
//! [rustc dev guide]:
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
use rustc_infer::infer::LateBoundRegionConversionTime::HigherRankedType;
use rustc_middle::ty::{
self, Binder, GenericArg, GenericArgKind, GenericParamDefKind, InternalSubsts, SubstsRef,
- ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt,
+ ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeVisitable,
};
use rustc_session::config::TraitSolver;
use rustc_span::def_id::DefId;
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
};
- let candidate_predicate =
- tcx.bound_item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
+ let candidate_predicate = tcx.item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
let candidate = candidate_predicate
.to_opt_poly_trait_pred()
.expect("projection candidate is not a trait predicate")
})?);
if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
- let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates;
- debug!(?predicates, "projection predicates");
- for predicate in predicates {
+ let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
+ for (predicate, _) in predicates {
let normalized = normalize_with_depth_to(
self,
obligation.param_env,
nested,
);
- // Adds the predicates from the trait. Note that this contains a `Self: Trait`
- // predicate as usual. It won't have any effect since auto traits are coinductive.
+ // Adds the predicates from the trait. Note that this contains a `Self: Trait`
+ // predicate as usual. It won't have any effect since auto traits are coinductive.
obligations.extend(trait_obligations);
debug!(?obligations, "vtable_auto_impl");
// This maybe belongs in wf, but that can't (doesn't) handle
// higher-ranked things.
// Prevent, e.g., `dyn Iterator<Item = str>`.
- for bound in self.tcx().bound_item_bounds(assoc_type).transpose_iter() {
+ for bound in self.tcx().item_bounds(assoc_type).transpose_iter() {
let subst_bound =
if defs.count() == 0 {
bound.subst(tcx, trait_predicate.trait_ref.substs)
// `T` -> `Trait`
(_, &ty::Dynamic(ref data, r, ty::Dyn)) => {
let mut object_dids = data.auto_traits().chain(data.principal_def_id());
- if let Some(did) = object_dids.find(|did| !tcx.is_object_safe(*did)) {
+ if let Some(did) = object_dids.find(|did| !tcx.check_is_object_safe(*did)) {
return Err(TraitNotObjectSafe(did));
}
ty::GeneratorWitness(tys) => {
stack.extend(tcx.erase_late_bound_regions(tys).to_vec());
}
+ ty::GeneratorWitnessMIR(def_id, substs) => {
+ let tcx = self.tcx();
+ stack.extend(tcx.generator_hidden_types(def_id).map(|bty| {
+ let ty = bty.subst(tcx, substs);
+ debug_assert!(!ty.has_late_bound_regions());
+ ty
+ }))
+ }
// If we have a projection type, make sure to normalize it so we replace it
// with a fresh infer variable
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_infer::infer::LateBoundRegionConversionTime;
+use rustc_infer::traits::TraitEngine;
+use rustc_infer::traits::TraitEngineExt;
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::SubstsRef;
use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitable};
+use rustc_session::config::TraitSolver;
use rustc_span::symbol::sym;
use std::cell::{Cell, RefCell};
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
//
// and we were to see some code `foo.push_clone()` where `boo`
- // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
+ // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
// we were to winnow, we'd wind up with zero candidates.
// Instead, we select the right impl now but report "`Bar` does
// not implement `Clone`".
obligation: &PredicateObligation<'tcx>,
) -> Result<EvaluationResult, OverflowError> {
self.evaluation_probe(|this| {
- this.evaluate_predicate_recursively(
- TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
- obligation.clone(),
- )
+ if this.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+ this.evaluate_predicate_recursively(
+ TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()),
+ obligation.clone(),
+ )
+ } else {
+ this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])
+ }
})
}
where
I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
{
- let mut result = EvaluatedToOk;
- for obligation in predicates {
- let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
- if let EvaluatedToErr = eval {
- // fast-path - EvaluatedToErr is the top of the lattice,
- // so we don't need to look on the other predicates.
- return Ok(EvaluatedToErr);
- } else {
- result = cmp::max(result, eval);
+ if self.tcx().sess.opts.unstable_opts.trait_solver != TraitSolver::Next {
+ let mut result = EvaluatedToOk;
+ for obligation in predicates {
+ let eval = self.evaluate_predicate_recursively(stack, obligation.clone())?;
+ if let EvaluatedToErr = eval {
+ // fast-path - EvaluatedToErr is the top of the lattice,
+ // so we don't need to look on the other predicates.
+ return Ok(EvaluatedToErr);
+ } else {
+ result = cmp::max(result, eval);
+ }
}
+ Ok(result)
+ } else {
+ self.evaluate_predicates_recursively_in_new_solver(predicates)
}
- Ok(result)
+ }
+
+ /// Evaluates the predicates using the new solver when `-Ztrait-solver=next` is enabled
+ fn evaluate_predicates_recursively_in_new_solver(
+ &mut self,
+ predicates: impl IntoIterator<Item = PredicateObligation<'tcx>>,
+ ) -> Result<EvaluationResult, OverflowError> {
+ let mut fulfill_cx = crate::solve::FulfillmentCtxt::new();
+ fulfill_cx.register_predicate_obligations(self.infcx, predicates);
+ // True errors
+ if !fulfill_cx.select_where_possible(self.infcx).is_empty() {
+ return Ok(EvaluatedToErr);
+ }
+ if !fulfill_cx.select_all_or_error(self.infcx).is_empty() {
+ return Ok(EvaluatedToAmbig);
+ }
+ // Regions and opaques are handled in the `evaluation_probe` by looking at the snapshot
+ Ok(EvaluatedToOk)
}
#[instrument(
}
ty::PredicateKind::ObjectSafe(trait_def_id) => {
- if self.tcx().is_object_safe(trait_def_id) {
+ if self.tcx().check_is_object_safe(trait_def_id) {
Ok(EvaluatedToOk)
} else {
Ok(EvaluatedToErr)
);
}
};
- let bounds = tcx.bound_item_bounds(def_id).subst(tcx, substs);
+ let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
// The bounds returned by `item_bounds` may contain duplicates after
// normalization, so try to deduplicate when possible to avoid
| ty::Ref(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Array(..)
| ty::Closure(..)
| ty::Never
Where(ty::Binder::bind_with_vars(witness_tys.to_vec(), all_vars))
}
+ ty::GeneratorWitnessMIR(def_id, ref substs) => {
+ let hidden_types = bind_generator_hidden_types_above(
+ self.infcx,
+ def_id,
+ substs,
+ obligation.predicate.bound_vars(),
+ );
+ Where(hidden_types)
+ }
+
ty::Closure(_, substs) => {
// (*) binder moved here
let ty = self.infcx.shallow_resolve(substs.as_closure().tupled_upvars_ty());
types.map_bound(|types| types.to_vec())
}
+ ty::GeneratorWitnessMIR(def_id, ref substs) => {
+ bind_generator_hidden_types_above(self.infcx, def_id, substs, t.bound_vars())
+ }
+
// For `PhantomData<T>`, we pass `T`.
ty::Adt(def, substs) if def.is_phantom_data() => t.rebind(substs.types().collect()),
// Matching
//
// Matching is a common path used for both evaluation and
- // confirmation. It basically unifies types that appear in impls
+ // confirmation. It basically unifies types that appear in impls
// and traits. This does affect the surrounding environment;
// therefore, when used during evaluation, match routines must be
// run inside of a `probe()` so that their side-effects are
impl_def_id: DefId,
obligation: &TraitObligation<'tcx>,
) -> Normalized<'tcx, SubstsRef<'tcx>> {
- let impl_trait_ref = self.tcx().bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
Ok(substs) => substs,
Err(()) => {
// obligation will normalize to `<$0 as Iterator>::Item = $1` and
// `$1: Copy`, so we must ensure the obligations are emitted in
// that order.
- let predicates = tcx.bound_predicates_of(def_id);
- debug!(?predicates);
- assert_eq!(predicates.0.parent, None);
- let mut obligations = Vec::with_capacity(predicates.0.predicates.len());
- for (predicate, span) in predicates.0.predicates {
- let span = *span;
+ let predicates = tcx.predicates_of(def_id);
+ assert_eq!(predicates.parent, None);
+ let predicates = predicates.instantiate_own(tcx, substs);
+ let mut obligations = Vec::with_capacity(predicates.len());
+ for (predicate, span) in predicates {
let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
derived,
param_env,
cause.clone(),
recursion_depth,
- predicates.rebind(*predicate).subst(tcx, substs),
+ predicate,
&mut obligations,
);
obligations.push(Obligation { cause, recursion_depth, param_env, predicate });
/// In Issue #60010, we found a bug in rustc where it would cache
/// these intermediate results. This was fixed in #60444 by disabling
/// *all* caching for things involved in a cycle -- in our example,
-/// that would mean we don't cache that `Bar<T>: Send`. But this led
+/// that would mean we don't cache that `Bar<T>: Send`. But this led
/// to large slowdowns.
///
/// Specifically, imagine this scenario, where proving `Baz<T>: Send`
/// a result at `reached_depth`, so it marks the *current* solution as
/// provisional as well. If an error is encountered, we toss out any
/// provisional results added from the subtree that encountered the
-/// error. When we pop the node at `reached_depth` from the stack, we
+/// error. When we pop the node at `reached_depth` from the stack, we
/// can commit all the things that remain in the provisional cache.
struct ProvisionalEvaluationCache<'tcx> {
/// next "depth first number" to issue -- just a counter
}
/// Invoked when the node with dfn `dfn` does not get a successful
- /// result. This will clear out any provisional cache entries
+ /// result. This will clear out any provisional cache entries
/// that were added since `dfn` was created. This is because the
/// provisional entries are things which must assume that the
/// things on the stack at the time of their creation succeeded --
Ambiguous,
No,
}
+
+/// Replace all regions inside the generator interior with late bound regions.
+/// Note that each region slot in the types gets a new fresh late bound region, which means that
+/// none of the regions inside relate to any other, even if typeck had previously found constraints
+/// that would cause them to be related.
+#[instrument(level = "trace", skip(infcx), ret)]
+fn bind_generator_hidden_types_above<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ def_id: DefId,
+ substs: ty::SubstsRef<'tcx>,
+ bound_vars: &ty::List<ty::BoundVariableKind>,
+) -> ty::Binder<'tcx, Vec<Ty<'tcx>>> {
+ let tcx = infcx.tcx;
+ let mut seen_tys = FxHashSet::default();
+
+ let considering_regions = infcx.considering_regions;
+
+ let num_bound_variables = bound_vars.len() as u32;
+ let mut counter = num_bound_variables;
+
+ let hidden_types: Vec<_> = tcx
+ .generator_hidden_types(def_id)
+ // Deduplicate tys to avoid repeated work.
+ .filter(|bty| seen_tys.insert(*bty))
+ .map(|bty| {
+ let mut ty = bty.subst(tcx, substs);
+
+ // Only remap erased regions if we use them.
+ if considering_regions {
+ ty = tcx.fold_regions(ty, |mut r, current_depth| {
+ if let ty::ReErased = r.kind() {
+ let br = ty::BoundRegion {
+ var: ty::BoundVar::from_u32(counter),
+ kind: ty::BrAnon(counter, None),
+ };
+ counter += 1;
+ r = tcx.mk_region(ty::ReLateBound(current_depth, br));
+ }
+ r
+ })
+ }
+
+ ty
+ })
+ .collect();
+ if considering_regions {
+ debug_assert!(!hidden_types.has_erased_regions());
+ }
+ let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.iter().chain(
+ (num_bound_variables..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i, None))),
+ ));
+ ty::Binder::bind_with_vars(hidden_types, bound_vars)
+}
param_env, source_impl, source_substs, target_node
);
let source_trait_ref =
- infcx.tcx.bound_impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
+ infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
// translate the Self and Param parts of the substitution, since those
// vary across impls
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let penv = tcx.param_env(impl1_def_id);
- let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
+ let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap().subst_identity();
// Create an infcx, taking the predicates of impl1 as assumptions:
let infcx = tcx.infer_ctxt().build();
pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
use std::fmt::Write;
- let trait_ref = tcx.impl_trait_ref(impl_def_id)?;
+ let trait_ref = tcx.impl_trait_ref(impl_def_id)?.subst_identity();
let mut w = "impl".to_owned();
let substs = InternalSubsts::identity_for_item(tcx, impl_def_id);
impl<'tcx> ChildrenExt<'tcx> for Children {
/// Insert an impl into this set of children without comparing to any existing impls.
fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer)
{
debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
/// an impl with a parent. The impl must be present in the list of
/// children already.
fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
let vec: &mut Vec<DefId>;
if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer)
{
if le && !ge {
debug!(
"descending as child of TraitRef {:?}",
- tcx.impl_trait_ref(possible_sibling).unwrap()
+ tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
);
// The impl specializes `possible_sibling`.
} else if ge && !le {
debug!(
"placing as parent of TraitRef {:?}",
- tcx.impl_trait_ref(possible_sibling).unwrap()
+ tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
);
replace_children.push(possible_sibling);
) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> {
assert!(impl_def_id.is_local());
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ // FIXME: use `EarlyBinder` in `self.children`
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
let trait_def_id = trait_ref.def_id;
debug!(
impl_def_id: DefId,
assoc_def_id: DefId,
) -> Result<LeafDef, ErrorGuaranteed> {
- let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
+ let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
let trait_def = tcx.trait_def(trait_def_id);
// This function may be called while we are still building the
} else {
// This is saying that neither the trait nor
// the impl contain a definition for this
- // associated type. Normally this situation
+ // associated type. Normally this situation
// could only arise through a compiler bug --
// if the user wrote a bad item name, it
// should have failed in astconv.
ty::Closure(..) => {
return ControlFlow::Break(ty);
}
- ty::Generator(..) | ty::GeneratorWitness(..) => {
+ ty::Generator(..) | ty::GeneratorWitness(..) | ty::GeneratorWitnessMIR(..) => {
return ControlFlow::Break(ty);
}
ty::FnDef(..) => {
// Types of formals and return in `fn(_) -> _` are also irrelevant;
// so we do not recur into them via `super_visit_with`
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::Array(_, n)
if { n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } =>
{
// rust-lang/rust#62336: ignore type of contents
// for empty array.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
// These primitive types are always structural match.
//
// `Never` is kind of special here, but as it is not inhabitable, this should be fine.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::FnPtr(..) => {
if !self.adt_const_param {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
} else {
return ControlFlow::Break(ty);
}
// Even though `NonStructural` does not implement `PartialEq`,
// structural equality on `T` does not recur into the raw
// pointer. Therefore, one can still use `C` in a pattern.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
} else {
return ControlFlow::Break(ty);
}
ty::Float(_) => {
if !self.adt_const_param {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
} else {
return ControlFlow::Break(ty);
}
self.tcx.sess.delay_span_bug(self.span, "ty::Error in structural-match check");
// We still want to check other types after encountering an error,
// as this may still emit relevant errors.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
};
if !self.seen.insert(adt_def.did()) {
debug!("Search already seen adt_def: {:?}", adt_def);
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
if !self.type_marked_structural(ty) {
// Note that this method could then never be called, so we
// do not want to try and codegen it, in that case (see #23435).
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
- if impossible_predicates(tcx, predicates.predicates) {
+ if impossible_predicates(
+ tcx,
+ predicates.map(|(predicate, _)| predicate).collect(),
+ ) {
debug!("vtable_entries: predicates do not hold");
return VtblEntry::Vacant;
}
use crate::infer::InferCtxt;
use crate::traits;
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
-use rustc_span::Span;
+use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
+use rustc_span::{Span, DUMMY_SP};
use std::iter;
/// Returns the set of obligations needed to make `arg` well-formed.
pub fn obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
recursion_depth: usize,
arg: GenericArg<'tcx>,
span: Span,
Some(result)
}
+/// Compute the predicates that are required for a type to be well-formed.
+///
+/// This is only intended to be used in the new solver, since it does not
+/// take into account recursion depth or proper error-reporting spans.
+pub fn unnormalized_obligations<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ arg: GenericArg<'tcx>,
+) -> Option<Vec<traits::PredicateObligation<'tcx>>> {
+ if let ty::GenericArgKind::Lifetime(..) = arg.unpack() {
+ return Some(vec![]);
+ }
+
+ debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg));
+
+ let mut wf = WfPredicates {
+ tcx: infcx.tcx,
+ param_env,
+ body_id: CRATE_DEF_ID,
+ span: DUMMY_SP,
+ out: vec![],
+ recursion_depth: 0,
+ item: None,
+ };
+ wf.compute(arg);
+ Some(wf.out)
+}
+
/// Returns the obligations that make this trait reference
-/// well-formed. For example, if there is a trait `Set` defined like
+/// well-formed. For example, if there is a trait `Set` defined like
/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
/// if `Bar: Eq`.
pub fn trait_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
trait_pred: &ty::TraitPredicate<'tcx>,
span: Span,
item: &'tcx hir::Item<'tcx>,
pub fn predicate_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
predicate: ty::Predicate<'tcx>,
span: Span,
) -> Vec<traits::PredicateObligation<'tcx>> {
struct WfPredicates<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
span: Span,
out: Vec<traits::PredicateObligation<'tcx>>,
recursion_depth: usize,
| ty::Error(_)
| ty::Str
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Never
| ty::Param(_)
| ty::Bound(..)
// All of the requirements on type parameters
// have already been checked for `impl Trait` in
// return position. We do need to check type-alias-impl-trait though.
- if ty::is_impl_trait_defn(self.tcx, def_id).is_none() {
+ if self.tcx.is_type_alias_impl_trait(def_id) {
let obligations = self.nominal_obligations(def_id, substs);
self.out.extend(obligations);
}
trace!("{:#?}", predicates);
debug_assert_eq!(predicates.predicates.len(), origins.len());
- iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev())
+ iter::zip(predicates, origins.into_iter().rev())
.map(|((mut pred, span), origin_def_id)| {
let code = if span.is_dummy() {
traits::ItemObligation(origin_def_id)
//! `crate::chalk::lowering` (to lower rustc types into Chalk types).
use rustc_middle::traits::ChalkRustInterner as RustInterner;
-use rustc_middle::ty::{self, AssocKind, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
+use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
use rustc_target::abi::{Integer, IntegerType};
def_id: DefId,
bound_vars: SubstsRef<'tcx>,
) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
- let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates;
- predicates
- .iter()
- .map(|(wc, _)| EarlyBinder(*wc).subst(self.interner.tcx, bound_vars))
- .filter_map(|wc| LowerInto::<
- Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>
- >::lower_into(wc, self.interner)).collect()
+ self.interner
+ .tcx
+ .predicates_defined_on(def_id)
+ .instantiate_own(self.interner.tcx, bound_vars)
+ .filter_map(|(wc, _)| LowerInto::lower_into(wc, self.interner))
+ .collect()
}
fn bounds_for<T>(&self, def_id: DefId, bound_vars: SubstsRef<'tcx>) -> Vec<T>
let where_clauses = self.where_clauses_for(def_id, bound_vars);
- let sig = self.interner.tcx.bound_fn_sig(def_id);
+ let sig = self.interner.tcx.fn_sig(def_id);
let (inputs_and_output, iobinders, _) = crate::chalk::lowering::collect_bound_vars(
self.interner,
self.interner.tcx,
let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
let binders = binders_for(self.interner, bound_vars);
- let trait_ref = self.interner.tcx.bound_impl_trait_ref(def_id).expect("not an impl");
+ let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl");
let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars);
let where_clauses = self.where_clauses_for(def_id, bound_vars);
let all_impls = self.interner.tcx.all_impls(def_id);
let matched_impls = all_impls.filter(|impl_def_id| {
use chalk_ir::could_match::CouldMatch;
- let trait_ref = self.interner.tcx.bound_impl_trait_ref(*impl_def_id).unwrap();
+ let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap();
let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id);
let self_ty = trait_ref.map_bound(|t| t.self_ty());
let trait_def_id = auto_trait_id.0;
let all_impls = self.interner.tcx.all_impls(trait_def_id);
for impl_def_id in all_impls {
- let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
let self_ty = trait_ref.self_ty();
let provides = match (self_ty.kind(), chalk_ty) {
(&ty::Adt(impl_adt_def, ..), Adt(id, ..)) => impl_adt_def.did() == id.0.did(),
}
fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
- self.interner.tcx.is_object_safe(trait_id.0)
+ self.interner.tcx.check_is_object_safe(trait_id.0)
}
fn hidden_opaque_type(
substs.lower_into(interner),
),
ty::GeneratorWitness(_) => unimplemented!(),
+ ty::GeneratorWitnessMIR(..) => unimplemented!(),
ty::Never => chalk_ir::TyKind::Never,
ty::Tuple(types) => {
chalk_ir::TyKind::Tuple(types.len(), types.as_substs().lower_into(interner))
use rustc_data_structures::fx::FxHashMap;
-use rustc_index::vec::IndexVec;
-
use rustc_middle::infer::canonical::{CanonicalTyVarKind, CanonicalVarKind};
use rustc_middle::traits::ChalkRustInterner;
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::GenericArg;
-use rustc_middle::ty::{self, BoundVar, ParamTy, TyCtxt, TypeFoldable, TypeVisitable};
+use rustc_middle::ty::{self, ParamTy, TyCtxt, TypeFoldable, TypeVisitable};
use rustc_infer::infer::canonical::{
Canonical, CanonicalVarValues, Certainty, QueryRegionConstraints, QueryResponse,
binders: chalk_ir::CanonicalVarKinds<_>| {
use rustc_middle::infer::canonical::CanonicalVarInfo;
- let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new();
let mut reverse_param_substitutor = ReverseParamsSubstitutor::new(tcx, params);
- subst.as_slice(interner).iter().for_each(|p| {
- var_values.push(p.lower_into(interner).fold_with(&mut reverse_param_substitutor));
- });
+ let var_values = tcx.mk_substs(
+ subst
+ .as_slice(interner)
+ .iter()
+ .map(|p| p.lower_into(interner).fold_with(&mut reverse_param_substitutor)),
+ );
let variables: Vec<_> = binders
.iter(interner)
.map(|var| {
max_universe: ty::UniverseIndex::from_usize(0),
variables: obligation.variables,
value: QueryResponse {
- var_values: CanonicalVarValues { var_values: IndexVec::new() }
- .make_identity(tcx),
+ var_values: CanonicalVarValues::dummy(),
region_constraints: QueryRegionConstraints::default(),
certainty: Certainty::Ambiguous,
opaque_types: vec![],
// This file contains various trait resolution methods used by codegen.
-// They all assume regions can be erased and monomorphic types. It
+// They all assume regions can be erased and monomorphic types. It
// seems likely that they should eventually be merged into more
// general routines.
use rustc_infer::infer::{DefiningAnchor, TyCtxtInferExt};
-use rustc_infer::traits::FulfillmentErrorCode;
+use rustc_infer::traits::{FulfillmentErrorCode, TraitEngineExt as _};
use rustc_middle::traits::CodegenObligationError;
use rustc_middle::ty::{self, TyCtxt};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
| ty::Ref(..)
| ty::FnDef(..)
| ty::FnPtr(_)
- | ty::GeneratorWitness(..) => {
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..) => {
// these types never have a destructor
}
//! Do not call this query directory. See
//! [`rustc_trait_selection::traits::query::type_op::implied_outlives_bounds`].
-use rustc_hir as hir;
use rustc_infer::infer::canonical::{self, Canonical};
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::query::OutlivesBound;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
+use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::DUMMY_SP;
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::query::{CanonicalTyGoal, Fallible, NoSolution};
// FIXME(@lcnr): It's not really "always fine", having fewer implied
// bounds can be backward incompatible, e.g. #101951 was caused by
// us not dealing with inference vars in `TypeOutlives` predicates.
- let obligations =
- wf::obligations(ocx.infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP)
- .unwrap_or_default();
+ let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
+ .unwrap_or_default();
// While these predicates should all be implied by other parts of
// the program, they are still relevant as they may constrain
match component {
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
- Component::Projection(p) => Some(OutlivesBound::RegionSubProjection(sub_region, p)),
- Component::Opaque(def_id, substs) => {
- Some(OutlivesBound::RegionSubOpaque(sub_region, def_id, substs))
- }
- Component::EscapingProjection(_) =>
+ Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
+ Component::EscapingAlias(_) =>
// If the projection has escaping regions, don't
// try to infer any implied bounds even for its
// free components. This is conservative, because
use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{ParamEnvAnd, Predicate};
use rustc_middle::ty::{UserSelfTy, UserSubsts, UserType};
+use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::query::{Fallible, NoSolution};
use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt};
use std::fmt;
-use std::iter::zip;
pub(crate) fn provide(p: &mut Providers) {
*p = Providers {
// FIXME(#104764): We should check well-formedness before normalization.
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into()));
ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
-
Ok(())
}
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
debug!(?instantiated_predicates);
- for (instantiated_predicate, predicate_span) in
- zip(instantiated_predicates.predicates, instantiated_predicates.spans)
- {
+ for (instantiated_predicate, predicate_span) in instantiated_predicates {
let span = if span == DUMMY_SP { predicate_span } else { span };
let cause = ObligationCause::new(
span,
- hir::CRATE_HIR_ID,
+ CRATE_DEF_ID,
ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
);
let instantiated_predicate =
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
-
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()));
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
}
-#![feature(alloc_layout_extra, control_flow_enum, decl_macro, iterator_try_reduce, never_type)]
+#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type)]
#![allow(dead_code, unused_variables)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
// We normalize the `fn_sig` again after substituting at a later point.
let mut sig = match *ty.kind() {
ty::FnDef(def_id, substs) => tcx
- .bound_fn_sig(def_id)
+ .fn_sig(def_id)
.map_bound(|fn_sig| {
tcx.normalize_erasing_regions(tcx.param_env(def_id), fn_sig)
})
// `Generator::resume(...) -> GeneratorState` function in case we
// have an ordinary generator, or the `Future::poll(...) -> Poll`
// function in case this is a special generator backing an async construct.
- let ret_ty = if tcx.generator_is_async(did) {
- let state_did = tcx.require_lang_item(LangItem::Poll, None);
- let state_adt_ref = tcx.adt_def(state_did);
- let state_substs = tcx.intern_substs(&[sig.return_ty.into()]);
- tcx.mk_adt(state_adt_ref, state_substs)
+ let (resume_ty, ret_ty) = if tcx.generator_is_async(did) {
+ // The signature should be `Future::poll(_, &mut Context<'_>) -> Poll<Output>`
+ let poll_did = tcx.require_lang_item(LangItem::Poll, None);
+ let poll_adt_ref = tcx.adt_def(poll_did);
+ let poll_substs = tcx.intern_substs(&[sig.return_ty.into()]);
+ let ret_ty = tcx.mk_adt(poll_adt_ref, poll_substs);
+
+ // We have to replace the `ResumeTy` that is used for type and borrow checking
+ // with `&mut Context<'_>` which is used in codegen.
+ #[cfg(debug_assertions)]
+ {
+ if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() {
+ let expected_adt =
+ tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None));
+ assert_eq!(*resume_ty_adt, expected_adt);
+ } else {
+ panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
+ };
+ }
+ let context_mut_ref = tcx.mk_task_context();
+
+ (context_mut_ref, ret_ty)
} else {
+ // The signature should be `Generator::resume(_, Resume) -> GeneratorState<Yield, Return>`
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
- tcx.mk_adt(state_adt_ref, state_substs)
+ let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+
+ (sig.resume_ty, ret_ty)
};
ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
- [env_ty, sig.resume_ty].iter(),
+ [env_ty, resume_ty].iter(),
&ret_ty,
false,
hir::Unsafety::Normal,
return;
}
- // Scalars which have invalid values cannot be undef.
- if !scalar.is_always_valid(&cx) {
+ if !scalar.is_uninit_valid() {
attrs.set(ArgAttribute::NoUndef);
}
// Only pointer types handled below.
- let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
+ let Scalar::Initialized { value: Pointer(_), valid_range} = scalar else { return };
if !valid_range.contains(0) {
attrs.set(ArgAttribute::NonNull);
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
};
- // `Box`, `&T`, and `&mut T` cannot be undef.
- // Note that this only applies to the value of the pointer itself;
- // this attribute doesn't make it UB for the pointed-to data to be undef.
- attrs.set(ArgAttribute::NoUndef);
-
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
// `noalias` for it. This can be turned off using an unstable flag.
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
}
let size = arg.layout.size;
- if arg.layout.is_unsized() || size > Pointer.size(cx) {
+ if arg.layout.is_unsized() || size > Pointer(AddressSpace::DATA).size(cx) {
arg.make_indirect();
} else {
// We want to pass small aggregates as immediates, but using
}
match expr.kind {
- thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(),
+ thir::ExprKind::NamedConst { substs, .. }
+ | thir::ExprKind::ConstBlock { substs, .. } => substs.has_non_region_param(),
thir::ExprKind::ConstParam { .. } => true,
thir::ExprKind::Repeat { value, count } => {
self.visit_expr(&self.thir()[value]);
count.has_non_region_param()
}
- _ => false,
+ thir::ExprKind::Scope { .. }
+ | thir::ExprKind::Box { .. }
+ | thir::ExprKind::If { .. }
+ | thir::ExprKind::Call { .. }
+ | thir::ExprKind::Deref { .. }
+ | thir::ExprKind::Binary { .. }
+ | thir::ExprKind::LogicalOp { .. }
+ | thir::ExprKind::Unary { .. }
+ | thir::ExprKind::Cast { .. }
+ | thir::ExprKind::Use { .. }
+ | thir::ExprKind::NeverToAny { .. }
+ | thir::ExprKind::Pointer { .. }
+ | thir::ExprKind::Loop { .. }
+ | thir::ExprKind::Let { .. }
+ | thir::ExprKind::Match { .. }
+ | thir::ExprKind::Block { .. }
+ | thir::ExprKind::Assign { .. }
+ | thir::ExprKind::AssignOp { .. }
+ | thir::ExprKind::Field { .. }
+ | thir::ExprKind::Index { .. }
+ | thir::ExprKind::VarRef { .. }
+ | thir::ExprKind::UpvarRef { .. }
+ | thir::ExprKind::Borrow { .. }
+ | thir::ExprKind::AddressOf { .. }
+ | thir::ExprKind::Break { .. }
+ | thir::ExprKind::Continue { .. }
+ | thir::ExprKind::Return { .. }
+ | thir::ExprKind::Array { .. }
+ | thir::ExprKind::Tuple { .. }
+ | thir::ExprKind::Adt(_)
+ | thir::ExprKind::PlaceTypeAscription { .. }
+ | thir::ExprKind::ValueTypeAscription { .. }
+ | thir::ExprKind::Closure(_)
+ | thir::ExprKind::Literal { .. }
+ | thir::ExprKind::NonHirLiteral { .. }
+ | thir::ExprKind::ZstLiteral { .. }
+ | thir::ExprKind::StaticRef { .. }
+ | thir::ExprKind::InlineAsm(_)
+ | thir::ExprKind::ThreadLocalRef(_)
+ | thir::ExprKind::Yield { .. } => false,
}
}
fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
match tcx.def_kind(def_id) {
DefKind::Fn => {
- let sig = tcx.fn_sig(def_id);
+ let sig = tcx.fn_sig(def_id).subst_identity();
let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
liberated_sig.inputs_and_output
}
DefKind::AssocFn => {
- let sig = tcx.fn_sig(def_id);
+ let sig = tcx.fn_sig(def_id).subst_identity();
let liberated_sig = tcx.liberate_late_bound_regions(def_id, sig);
let mut assumed_wf_types: Vec<_> =
tcx.assumed_wf_types(tcx.parent(def_id)).as_slice().into();
assumed_wf_types.extend(liberated_sig.inputs_and_output);
tcx.intern_type_list(&assumed_wf_types)
}
- DefKind::Impl => match tcx.impl_trait_ref(def_id) {
- Some(trait_ref) => {
- let types: Vec<_> = trait_ref.substs.types().collect();
- tcx.intern_type_list(&types)
+ DefKind::Impl => {
+ match tcx.impl_trait_ref(def_id) {
+ Some(trait_ref) => {
+ let types: Vec<_> = trait_ref.skip_binder().substs.types().collect();
+ tcx.intern_type_list(&types)
+ }
+ // Only the impl self type
+ None => tcx.intern_type_list(&[tcx.type_of(def_id)]),
}
- // Only the impl self type
- None => tcx.intern_type_list(&[tcx.type_of(def_id)]),
- },
+ }
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
DefKind::Mod
| DefKind::Struct
ty::FloatTy::F64 => F64,
}),
ty::FnPtr(_) => {
- let mut ptr = scalar_unit(Pointer);
+ let mut ptr = scalar_unit(Pointer(dl.instruction_address_space));
ptr.valid_range_mut().start = 1;
tcx.intern_layout(LayoutS::scalar(cx, ptr))
}
// Potentially-wide pointers.
ty::Ref(_, pointee, _) | ty::RawPtr(ty::TypeAndMut { ty: pointee, .. }) => {
- let mut data_ptr = scalar_unit(Pointer);
+ let mut data_ptr = scalar_unit(Pointer(AddressSpace::DATA));
if !ty.is_unsafe_ptr() {
data_ptr.valid_range_mut().start = 1;
}
}
ty::Slice(_) | ty::Str => scalar_unit(Int(dl.ptr_sized_integer(), false)),
ty::Dynamic(..) => {
- let mut vtable = scalar_unit(Pointer);
+ let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
vtable.valid_range_mut().start = 1;
vtable
}
ty::Dynamic(_, _, ty::DynStar) => {
let mut data = scalar_unit(Int(dl.ptr_sized_integer(), false));
data.valid_range_mut().start = 0;
- let mut vtable = scalar_unit(Pointer);
+ let mut vtable = scalar_unit(Pointer(AddressSpace::DATA));
vtable.valid_range_mut().start = 1;
tcx.intern_layout(cx.scalar_pair(data, vtable))
}
return Err(LayoutError::Unknown(ty));
}
- ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Infer(_) => {
+ ty::Placeholder(..)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Infer(_) => {
bug!("Layout::compute: unexpected type `{}`", ty)
}
let promoted_layouts = ineligible_locals
.iter()
- .map(|local| subst_field(info.field_tys[local]))
+ .map(|local| subst_field(info.field_tys[local].ty))
.map(|ty| tcx.mk_maybe_uninit(ty))
.map(|ty| cx.layout_of(ty));
let prefix_layouts = substs
Assigned(_) => bug!("assignment does not match variant"),
Ineligible(_) => false,
})
- .map(|local| subst_field(info.field_tys[*local]));
+ .map(|local| subst_field(info.field_tys[*local].ty));
let mut variant = univariant_uninterned(
cx,
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![feature(let_chains)]
-#![feature(control_flow_enum)]
#![feature(never_type)]
#![feature(box_patterns)]
#![recursion_limit = "256"]
for component in components {
match *component.kind() {
+ // The information required to determine whether a generator has drop is
+ // computed on MIR, while this very method is used to build MIR.
+ // To avoid cycles, we consider that generators always require drop.
+ ty::Generator(..) if tcx.sess.opts.unstable_opts.drop_tracking_mir => {
+ return Some(Err(AlwaysRequiresDrop));
+ }
+
_ if component.is_copy_modulo_regions(tcx, self.param_env) => (),
ty::Closure(_, substs) => {
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt};
use rustc_session::config::TraitSolver;
+use rustc_span::def_id::{DefId, CRATE_DEF_ID};
use rustc_trait_selection::traits;
fn sized_constraint_for_ty<'tcx>(
Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
| FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![],
- Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | GeneratorWitness(..) => {
+ Str
+ | Dynamic(..)
+ | Slice(_)
+ | Foreign(..)
+ | Error(_)
+ | GeneratorWitness(..)
+ | GeneratorWitnessMIR(..) => {
// these are never sized - return the target type
vec![ty]
}
constness,
);
- let body_id =
- local_did.and_then(|id| tcx.hir().maybe_body_owned_by(id).map(|body| body.hir_id));
- let body_id = match body_id {
- Some(id) => id,
- None if hir_id.is_some() => hir_id.unwrap(),
- _ => hir::CRATE_HIR_ID,
- };
-
+ let body_id = local_did.unwrap_or(CRATE_DEF_ID);
let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id);
traits::normalize_param_env_or_error(tcx, unnormalized_env, cause)
}
// In a trait impl, we assume that the header trait ref and all its
// constituents are well-formed.
NodeKind::TraitImpl => {
- let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl");
+ let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl").subst_identity();
// FIXME(chalk): this has problems because of late-bound regions
//inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk()));
// In an fn, we assume that the arguments and all their constituents are
// well-formed.
NodeKind::Fn => {
- let fn_sig = tcx.fn_sig(def_id);
+ let fn_sig = tcx.fn_sig(def_id).subst_identity();
let fn_sig = tcx.liberate_late_bound_regions(def_id, fn_sig);
inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk()));
let trait_ref = tcx
.impl_trait_ref(def_id)
- .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id));
+ .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id))
+ .skip_binder();
debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref);
/// Does this value have `InferConst::Fresh`?
const HAS_CT_FRESH = 1 << 21;
+
+ /// Does this have `Generator` or `GeneratorWitness`?
+ const HAS_TY_GENERATOR = 1 << 22;
}
}
/// ```
GeneratorWitness(I::BinderListTy),
+ /// A type representing the types stored inside a generator.
+ /// This should only appear as part of the `GeneratorSubsts`.
+ ///
+ /// Unlike upvars, the witness can reference lifetimes from
+ /// inside of the generator itself. To deal with them in
+ /// the type of the generator, we convert them to higher ranked
+ /// lifetimes bound by the witness itself.
+ ///
+ /// This variant is only using when `drop_tracking_mir` is set.
+ /// This contains the `DefId` and the `SubstRef` of the generator.
+ /// The actual witness types are computed on MIR by the `mir_generator_witnesses` query.
+ ///
+ /// Looking at the following example, the witness for this generator
+ /// may end up as something like `for<'a> [Vec<i32>, &'a Vec<i32>]`:
+ ///
+ /// ```ignore UNSOLVED (ask @compiler-errors, should this error? can we just swap the yields?)
+ /// #![feature(generators)]
+ /// |a| {
+ /// let x = &vec![3];
+ /// yield a;
+ /// yield x[0];
+ /// }
+ /// # ;
+ /// ```
+ GeneratorWitnessMIR(I::DefId, I::SubstsRef),
+
/// The never type `!`.
Never,
Placeholder(_) => 23,
Infer(_) => 24,
Error(_) => 25,
+ GeneratorWitnessMIR(_, _) => 26,
}
}
Closure(d, s) => Closure(d.clone(), s.clone()),
Generator(d, s, m) => Generator(d.clone(), s.clone(), m.clone()),
GeneratorWitness(g) => GeneratorWitness(g.clone()),
+ GeneratorWitnessMIR(d, s) => GeneratorWitnessMIR(d.clone(), s.clone()),
Never => Never,
Tuple(t) => Tuple(t.clone()),
Alias(k, p) => Alias(*k, p.clone()),
a_d == b_d && a_s == b_s && a_m == b_m
}
(GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g == b_g,
+ (
+ &GeneratorWitnessMIR(ref a_d, ref a_s),
+ &GeneratorWitnessMIR(ref b_d, ref b_s),
+ ) => a_d == b_d && a_s == b_s,
(Tuple(a_t), Tuple(b_t)) => a_t == b_t,
(Alias(a_i, a_p), Alias(b_i, b_p)) => a_i == b_i && a_p == b_p,
(Param(a_p), Param(b_p)) => a_p == b_p,
a_d.cmp(b_d).then_with(|| a_s.cmp(b_s).then_with(|| a_m.cmp(b_m)))
}
(GeneratorWitness(a_g), GeneratorWitness(b_g)) => a_g.cmp(b_g),
+ (
+ &GeneratorWitnessMIR(ref a_d, ref a_s),
+ &GeneratorWitnessMIR(ref b_d, ref b_s),
+ ) => match Ord::cmp(a_d, b_d) {
+ Ordering::Equal => Ord::cmp(a_s, b_s),
+ cmp => cmp,
+ },
(Tuple(a_t), Tuple(b_t)) => a_t.cmp(b_t),
(Alias(a_i, a_p), Alias(b_i, b_p)) => a_i.cmp(b_i).then_with(|| a_p.cmp(b_p)),
(Param(a_p), Param(b_p)) => a_p.cmp(b_p),
m.hash(state)
}
GeneratorWitness(g) => g.hash(state),
+ GeneratorWitnessMIR(d, s) => {
+ d.hash(state);
+ s.hash(state);
+ }
Tuple(t) => t.hash(state),
Alias(i, p) => {
i.hash(state);
Closure(d, s) => f.debug_tuple_field2_finish("Closure", d, s),
Generator(d, s, m) => f.debug_tuple_field3_finish("Generator", d, s, m),
GeneratorWitness(g) => f.debug_tuple_field1_finish("GeneratorWitness", g),
+ GeneratorWitnessMIR(d, s) => f.debug_tuple_field2_finish("GeneratorWitnessMIR", d, s),
Never => f.write_str("Never"),
Tuple(t) => f.debug_tuple_field1_finish("Tuple", t),
Alias(i, a) => f.debug_tuple_field2_finish("Alias", i, a),
GeneratorWitness(b) => e.emit_enum_variant(disc, |e| {
b.encode(e);
}),
+ GeneratorWitnessMIR(def_id, substs) => e.emit_enum_variant(disc, |e| {
+ def_id.encode(e);
+ substs.encode(e);
+ }),
Never => e.emit_enum_variant(disc, |_| {}),
Tuple(substs) => e.emit_enum_variant(disc, |e| {
substs.encode(e);
23 => Placeholder(Decodable::decode(d)),
24 => Infer(Decodable::decode(d)),
25 => Error(Decodable::decode(d)),
+ 26 => GeneratorWitnessMIR(Decodable::decode(d), Decodable::decode(d)),
_ => panic!(
"{}",
format!(
GeneratorWitness(b) => {
b.hash_stable(__hcx, __hasher);
}
+ GeneratorWitnessMIR(def_id, substs) => {
+ def_id.hash_stable(__hcx, __hasher);
+ substs.hash_stable(__hcx, __hasher);
+ }
Never => {}
Tuple(substs) => {
substs.hash_stable(__hcx, __hasher);
///
/// Note that inference variables and bound regions are not included
/// in this diagram. In the case of inference variables, they should
-/// be inferred to some other region from the diagram. In the case of
+/// be inferred to some other region from the diagram. In the case of
/// bound regions, they are excluded because they don't make sense to
/// include -- the diagram indicates the relationship between free
/// regions.
# be built if `extended = true`.
#extended = false
-# Installs chosen set of extended tools if `extended = true`. By default builds
-# all extended tools except `rust-demangler`, unless the target is also being
-# built with `profiler = true`. If chosen tool failed to build the installation
-# fails. If `extended = false`, this option is ignored.
-#tools = ["cargo", "rls", "clippy", "rustfmt", "analysis", "src"] # + "rust-demangler" if `profiler`
+# Set of tools to be included in the installation.
+#
+# If `extended = false`, the only one of these built by default is rustdoc.
+#
+# If `extended = true`, they're all included, with the exception of
+# rust-demangler which additionally requires `profiler = true` to be set.
+#
+# If any enabled tool fails to build, the installation fails.
+#tools = [
+# "cargo",
+# "clippy",
+# "rustdoc",
+# "rustfmt",
+# "rust-analyzer",
+# "analysis",
+# "src",
+# "rust-demangler", # if profiler = true
+#]
# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
#verbose = 0
mod tests;
extern "Rust" {
- // These are the magic symbols to call the global allocator. rustc generates
+ // These are the magic symbols to call the global allocator. rustc generates
// them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute
// (the code expanding that attribute macro generates those functions), or to call
// the default implementations in std (`__rdl_alloc` etc. in `library/std/src/alloc.rs`)
#[cfg(not(no_global_oom_handling))]
extern "Rust" {
- // This is the magic symbol to call the global alloc error handler. rustc generates
+ // This is the magic symbol to call the global alloc error handler. rustc generates
// it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
// default implementations below (`__rdl_oom`) otherwise.
fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
#[unstable(feature = "thin_box", issue = "92791")]
impl<T> ThinBox<T> {
- /// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on
+ /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
/// the stack.
///
/// # Examples
///
/// let five = ThinBox::new(5);
/// ```
+ ///
+ /// [`Metadata`]: core::ptr::Pointee::Metadata
#[cfg(not(no_global_oom_handling))]
pub fn new(value: T) -> Self {
let meta = ptr::metadata(&value);
#[unstable(feature = "thin_box", issue = "92791")]
impl<Dyn: ?Sized> ThinBox<Dyn> {
- /// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on
+ /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
/// the stack.
///
/// # Examples
///
/// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]);
/// ```
+ ///
+ /// [`Metadata`]: core::ptr::Pointee::Metadata
#[cfg(not(no_global_oom_handling))]
pub fn new_unsize<T>(value: T) -> Self
where
use core::fmt;
use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
use core::mem::{self, swap, ManuallyDrop};
+use core::num::NonZeroUsize;
use core::ops::{Deref, DerefMut};
use core::ptr;
/// It is a logic error for an item to be modified in such a way that the
/// item's ordering relative to any other item, as determined by the [`Ord`]
/// trait, changes while it is in the heap. This is normally only possible
-/// through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. The
+/// through interior mutability, global state, I/O, or unsafe code. The
/// behavior resulting from such a logic error is not specified, but will
/// be encapsulated to the `BinaryHeap` that observed the logic error and not
/// result in undefined behavior. This could include panics, incorrect results,
/// aborts, memory leaks, and non-termination.
///
+/// As long as no elements change their relative order while being in the heap
+/// as described above, the API of `BinaryHeap` guarantees that the heap
+/// invariant remains intact i.e. its methods all behave as documented. For
+/// example if a method is documented as iterating in sorted order, that's
+/// guaranteed to work as long as elements in the heap have not changed order,
+/// even in the presence of closures getting unwinded out of, iterators getting
+/// leaked, and similar foolishness.
+///
/// # Examples
///
/// ```
#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
pub struct PeekMut<'a, T: 'a + Ord> {
heap: &'a mut BinaryHeap<T>,
- sift: bool,
+ // If a set_len + sift_down are required, this is Some. If a &mut T has not
+ // yet been exposed to peek_mut()'s caller, it's None.
+ original_len: Option<NonZeroUsize>,
}
#[stable(feature = "collection_debug", since = "1.17.0")]
#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
impl<T: Ord> Drop for PeekMut<'_, T> {
fn drop(&mut self) {
- if self.sift {
+ if let Some(original_len) = self.original_len {
+ // SAFETY: That's how many elements were in the Vec at the time of
+ // the PeekMut::deref_mut call, and therefore also at the time of
+ // the BinaryHeap::peek_mut call. Since the PeekMut did not end up
+ // getting leaked, we are now undoing the leak amplification that
+ // the DerefMut prepared for.
+ unsafe { self.heap.data.set_len(original_len.get()) };
+
// SAFETY: PeekMut is only instantiated for non-empty heaps.
unsafe { self.heap.sift_down(0) };
}
impl<T: Ord> DerefMut for PeekMut<'_, T> {
fn deref_mut(&mut self) -> &mut T {
debug_assert!(!self.heap.is_empty());
- self.sift = true;
+
+ let len = self.heap.len();
+ if len > 1 {
+ // Here we preemptively leak all the rest of the underlying vector
+ // after the currently max element. If the caller mutates the &mut T
+ // we're about to give them, and then leaks the PeekMut, all these
+ // elements will remain leaked. If they don't leak the PeekMut, then
+ // either Drop or PeekMut::pop will un-leak the vector elements.
+ //
+ // This is technique is described throughout several other places in
+ // the standard library as "leak amplification".
+ unsafe {
+ // SAFETY: len > 1 so len != 0.
+ self.original_len = Some(NonZeroUsize::new_unchecked(len));
+ // SAFETY: len > 1 so all this does for now is leak elements,
+ // which is safe.
+ self.heap.data.set_len(1);
+ }
+ }
+
// SAFE: PeekMut is only instantiated for non-empty heaps
unsafe { self.heap.data.get_unchecked_mut(0) }
}
/// Removes the peeked value from the heap and returns it.
#[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")]
pub fn pop(mut this: PeekMut<'a, T>) -> T {
- let value = this.heap.pop().unwrap();
- this.sift = false;
- value
+ if let Some(original_len) = this.original_len.take() {
+ // SAFETY: This is how many elements were in the Vec at the time of
+ // the BinaryHeap::peek_mut call.
+ unsafe { this.heap.data.set_len(original_len.get()) };
+
+ // Unlike in Drop, here we don't also need to do a sift_down even if
+ // the caller could've mutated the element. It is removed from the
+ // heap on the next line and pop() is not sensitive to its value.
+ }
+ this.heap.pop().unwrap()
}
}
/// Returns a mutable reference to the greatest item in the binary heap, or
/// `None` if it is empty.
///
- /// Note: If the `PeekMut` value is leaked, the heap may be in an
- /// inconsistent state.
+ /// Note: If the `PeekMut` value is leaked, some heap elements might get
+ /// leaked along with it, but the remaining elements will remain a valid
+ /// heap.
///
/// # Examples
///
/// otherwise it's *O*(1).
#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
pub fn peek_mut(&mut self) -> Option<PeekMut<'_, T>> {
- if self.is_empty() { None } else { Some(PeekMut { heap: self, sift: false }) }
+ if self.is_empty() { None } else { Some(PeekMut { heap: self, original_len: None }) }
}
/// Removes the greatest item from the binary heap and returns it, or `None` if it
use super::*;
use crate::boxed::Box;
use crate::testing::crash_test::{CrashTestDummy, Panic};
+use core::mem;
use std::iter::TrustedLen;
use std::panic::{catch_unwind, AssertUnwindSafe};
assert_eq!(heap.peek(), Some(&9));
}
+#[test]
+fn test_peek_mut_leek() {
+ let data = vec![4, 2, 7];
+ let mut heap = BinaryHeap::from(data);
+ let mut max = heap.peek_mut().unwrap();
+ *max = -1;
+
+ // The PeekMut object's Drop impl would have been responsible for moving the
+ // -1 out of the max position of the BinaryHeap, but we don't run it.
+ mem::forget(max);
+
+ // Absent some mitigation like leak amplification, the -1 would incorrectly
+ // end up in the last position of the returned Vec, with the rest of the
+ // heap's original contents in front of it in sorted order.
+ let sorted_vec = heap.into_sorted_vec();
+ assert!(sorted_vec.is_sorted(), "{:?}", sorted_vec);
+}
+
#[test]
fn test_peek_mut_pop() {
let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]
+#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
//
// Library features:
#![feature(alloc_layout_extra)]
#![feature(const_size_of_val)]
#![feature(const_align_of_val)]
#![feature(const_ptr_read)]
+#![feature(const_maybe_uninit_zeroed)]
#![feature(const_maybe_uninit_write)]
#![feature(const_maybe_uninit_as_mut_ptr)]
#![feature(const_refs_to_cell)]
#![feature(hasher_prefixfree_extras)]
#![feature(inline_const)]
#![feature(inplace_iteration)]
+#![cfg_attr(test, feature(is_sorted))]
#![feature(iter_advance_by)]
#![feature(iter_next_chunk)]
#![feature(iter_repeat_n)]
#![feature(c_unwind)]
#![feature(with_negative_coherence)]
#![cfg_attr(test, feature(panic_update_hook))]
+#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
//
// Rustdoc features:
#![feature(doc_cfg)]
// This is a `NonNull` to allow optimizing the size of this type in enums,
// but it is not necessarily a valid pointer.
// `Weak::new` sets this to `usize::MAX` so that it doesn’t need
- // to allocate space on the heap. That's not a value a real pointer
+ // to allocate space on the heap. That's not a value a real pointer
// will ever have because RcBox has alignment at least 2.
// This is only possible when `T: Sized`; unsized `T` never dangle.
ptr: NonNull<RcBox<T>>,
use core::mem::{self, SizedTypeProperties};
#[cfg(not(no_global_oom_handling))]
use core::ptr;
+#[cfg(not(no_global_oom_handling))]
+use core::slice::sort;
use crate::alloc::Allocator;
#[cfg(not(no_global_oom_handling))]
-use crate::alloc::Global;
+use crate::alloc::{self, Global};
#[cfg(not(no_global_oom_handling))]
use crate::borrow::ToOwned;
use crate::boxed::Box;
where
T: Ord,
{
- merge_sort(self, T::lt);
+ stable_sort(self, T::lt);
}
/// Sorts the slice with a comparator function.
where
F: FnMut(&T, &T) -> Ordering,
{
- merge_sort(self, |a, b| compare(a, b) == Less);
+ stable_sort(self, |a, b| compare(a, b) == Less);
}
/// Sorts the slice with a key extraction function.
F: FnMut(&T) -> K,
K: Ord,
{
- merge_sort(self, |a, b| f(a).lt(&f(b)));
+ stable_sort(self, |a, b| f(a).lt(&f(b)));
}
/// Sorts the slice with a key extraction function.
// Sorting
////////////////////////////////////////////////////////////////////////////////
-/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.
-///
-/// This is the integral subroutine of insertion sort.
-#[cfg(not(no_global_oom_handling))]
-fn insert_head<T, F>(v: &mut [T], is_less: &mut F)
-where
- F: FnMut(&T, &T) -> bool,
-{
- if v.len() >= 2 && is_less(&v[1], &v[0]) {
- unsafe {
- // There are three ways to implement insertion here:
- //
- // 1. Swap adjacent elements until the first one gets to its final destination.
- // However, this way we copy data around more than is necessary. If elements are big
- // structures (costly to copy), this method will be slow.
- //
- // 2. Iterate until the right place for the first element is found. Then shift the
- // elements succeeding it to make room for it and finally place it into the
- // remaining hole. This is a good method.
- //
- // 3. Copy the first element into a temporary variable. Iterate until the right place
- // for it is found. As we go along, copy every traversed element into the slot
- // preceding it. Finally, copy data from the temporary variable into the remaining
- // hole. This method is very good. Benchmarks demonstrated slightly better
- // performance than with the 2nd method.
- //
- // All methods were benchmarked, and the 3rd showed best results. So we chose that one.
- let tmp = mem::ManuallyDrop::new(ptr::read(&v[0]));
-
- // Intermediate state of the insertion process is always tracked by `hole`, which
- // serves two purposes:
- // 1. Protects integrity of `v` from panics in `is_less`.
- // 2. Fills the remaining hole in `v` in the end.
- //
- // Panic safety:
- //
- // If `is_less` panics at any point during the process, `hole` will get dropped and
- // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it
- // initially held exactly once.
- let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] };
- ptr::copy_nonoverlapping(&v[1], &mut v[0], 1);
-
- for i in 2..v.len() {
- if !is_less(&v[i], &*tmp) {
- break;
- }
- ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1);
- hole.dest = &mut v[i];
- }
- // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`.
- }
- }
-
- // When dropped, copies from `src` into `dest`.
- struct InsertionHole<T> {
- src: *const T,
- dest: *mut T,
- }
-
- impl<T> Drop for InsertionHole<T> {
- fn drop(&mut self) {
- unsafe {
- ptr::copy_nonoverlapping(self.src, self.dest, 1);
- }
- }
- }
-}
-
-/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and
-/// stores the result into `v[..]`.
-///
-/// # Safety
-///
-/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough
-/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type.
-#[cfg(not(no_global_oom_handling))]
-unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F)
-where
- F: FnMut(&T, &T) -> bool,
-{
- let len = v.len();
- let v = v.as_mut_ptr();
- let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) };
-
- // The merge process first copies the shorter run into `buf`. Then it traces the newly copied
- // run and the longer run forwards (or backwards), comparing their next unconsumed elements and
- // copying the lesser (or greater) one into `v`.
- //
- // As soon as the shorter run is fully consumed, the process is done. If the longer run gets
- // consumed first, then we must copy whatever is left of the shorter run into the remaining
- // hole in `v`.
- //
- // Intermediate state of the process is always tracked by `hole`, which serves two purposes:
- // 1. Protects integrity of `v` from panics in `is_less`.
- // 2. Fills the remaining hole in `v` if the longer run gets consumed first.
- //
- // Panic safety:
- //
- // If `is_less` panics at any point during the process, `hole` will get dropped and fill the
- // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every
- // object it initially held exactly once.
- let mut hole;
-
- if mid <= len - mid {
- // The left run is shorter.
- unsafe {
- ptr::copy_nonoverlapping(v, buf, mid);
- hole = MergeHole { start: buf, end: buf.add(mid), dest: v };
- }
-
- // Initially, these pointers point to the beginnings of their arrays.
- let left = &mut hole.start;
- let mut right = v_mid;
- let out = &mut hole.dest;
-
- while *left < hole.end && right < v_end {
- // Consume the lesser side.
- // If equal, prefer the left run to maintain stability.
- unsafe {
- let to_copy = if is_less(&*right, &**left) {
- get_and_increment(&mut right)
- } else {
- get_and_increment(left)
- };
- ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1);
- }
- }
- } else {
- // The right run is shorter.
- unsafe {
- ptr::copy_nonoverlapping(v_mid, buf, len - mid);
- hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid };
- }
-
- // Initially, these pointers point past the ends of their arrays.
- let left = &mut hole.dest;
- let right = &mut hole.end;
- let mut out = v_end;
-
- while v < *left && buf < *right {
- // Consume the greater side.
- // If equal, prefer the right run to maintain stability.
- unsafe {
- let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) {
- decrement_and_get(left)
- } else {
- decrement_and_get(right)
- };
- ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1);
- }
- }
- }
- // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of
- // it will now be copied into the hole in `v`.
-
- unsafe fn get_and_increment<T>(ptr: &mut *mut T) -> *mut T {
- let old = *ptr;
- *ptr = unsafe { ptr.add(1) };
- old
- }
-
- unsafe fn decrement_and_get<T>(ptr: &mut *mut T) -> *mut T {
- *ptr = unsafe { ptr.sub(1) };
- *ptr
- }
-
- // When dropped, copies the range `start..end` into `dest..`.
- struct MergeHole<T> {
- start: *mut T,
- end: *mut T,
- dest: *mut T,
- }
-
- impl<T> Drop for MergeHole<T> {
- fn drop(&mut self) {
- // `T` is not a zero-sized type, and these are pointers into a slice's elements.
- unsafe {
- let len = self.end.sub_ptr(self.start);
- ptr::copy_nonoverlapping(self.start, self.dest, len);
- }
- }
- }
-}
-
-/// This merge sort borrows some (but not all) ideas from TimSort, which is described in detail
-/// [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt).
-///
-/// The algorithm identifies strictly descending and non-descending subsequences, which are called
-/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed
-/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are
-/// satisfied:
-///
-/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len`
-/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len`
-///
-/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case.
+#[inline]
#[cfg(not(no_global_oom_handling))]
-fn merge_sort<T, F>(v: &mut [T], mut is_less: F)
+fn stable_sort<T, F>(v: &mut [T], mut is_less: F)
where
F: FnMut(&T, &T) -> bool,
{
- // Slices of up to this length get sorted using insertion sort.
- const MAX_INSERTION: usize = 20;
- // Very short runs are extended using insertion sort to span at least this many elements.
- const MIN_RUN: usize = 10;
-
- // Sorting has no meaningful behavior on zero-sized types.
if T::IS_ZST {
+ // Sorting has no meaningful behavior on zero-sized types. Do nothing.
return;
}
- let len = v.len();
-
- // Short arrays get sorted in-place via insertion sort to avoid allocations.
- if len <= MAX_INSERTION {
- if len >= 2 {
- for i in (0..len - 1).rev() {
- insert_head(&mut v[i..], &mut is_less);
- }
- }
- return;
- }
-
- // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it
- // shallow copies of the contents of `v` without risking the dtors running on copies if
- // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run,
- // which will always have length at most `len / 2`.
- let mut buf = Vec::with_capacity(len / 2);
+ let elem_alloc_fn = |len: usize| -> *mut T {
+ // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len >
+ // v.len(). Alloc in general will only be used as 'shadow-region' to store temporary swap
+ // elements.
+ unsafe { alloc::alloc(alloc::Layout::array::<T>(len).unwrap_unchecked()) as *mut T }
+ };
- // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a
- // strange decision, but consider the fact that merges more often go in the opposite direction
- // (forwards). According to benchmarks, merging forwards is slightly faster than merging
- // backwards. To conclude, identifying runs by traversing backwards improves performance.
- let mut runs = vec![];
- let mut end = len;
- while end > 0 {
- // Find the next natural run, and reverse it if it's strictly descending.
- let mut start = end - 1;
- if start > 0 {
- start -= 1;
- unsafe {
- if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) {
- while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) {
- start -= 1;
- }
- v[start..end].reverse();
- } else {
- while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1))
- {
- start -= 1;
- }
- }
- }
- }
-
- // Insert some more elements into the run if it's too short. Insertion sort is faster than
- // merge sort on short sequences, so this significantly improves performance.
- while start > 0 && end - start < MIN_RUN {
- start -= 1;
- insert_head(&mut v[start..end], &mut is_less);
+ let elem_dealloc_fn = |buf_ptr: *mut T, len: usize| {
+ // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len >
+ // v.len(). The caller must ensure that buf_ptr was created by elem_alloc_fn with the same
+ // len.
+ unsafe {
+ alloc::dealloc(buf_ptr as *mut u8, alloc::Layout::array::<T>(len).unwrap_unchecked());
}
+ };
- // Push this run onto the stack.
- runs.push(Run { start, len: end - start });
- end = start;
-
- // Merge some pairs of adjacent runs to satisfy the invariants.
- while let Some(r) = collapse(&runs) {
- let left = runs[r + 1];
- let right = runs[r];
- unsafe {
- merge(
- &mut v[left.start..right.start + right.len],
- left.len,
- buf.as_mut_ptr(),
- &mut is_less,
- );
- }
- runs[r] = Run { start: left.start, len: left.len + right.len };
- runs.remove(r + 1);
+ let run_alloc_fn = |len: usize| -> *mut sort::TimSortRun {
+ // SAFETY: Creating the layout is safe as long as merge_sort never calls this with an
+ // obscene length or 0.
+ unsafe {
+ alloc::alloc(alloc::Layout::array::<sort::TimSortRun>(len).unwrap_unchecked())
+ as *mut sort::TimSortRun
}
- }
-
- // Finally, exactly one run must remain in the stack.
- debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len);
+ };
- // Examines the stack of runs and identifies the next pair of runs to merge. More specifically,
- // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the
- // algorithm should continue building a new run instead, `None` is returned.
- //
- // TimSort is infamous for its buggy implementations, as described here:
- // http://envisage-project.eu/timsort-specification-and-verification/
- //
- // The gist of the story is: we must enforce the invariants on the top four runs on the stack.
- // Enforcing them on just top three is not sufficient to ensure that the invariants will still
- // hold for *all* runs in the stack.
- //
- // This function correctly checks invariants for the top four runs. Additionally, if the top
- // run starts at index 0, it will always demand a merge operation until the stack is fully
- // collapsed, in order to complete the sort.
- #[inline]
- fn collapse(runs: &[Run]) -> Option<usize> {
- let n = runs.len();
- if n >= 2
- && (runs[n - 1].start == 0
- || runs[n - 2].len <= runs[n - 1].len
- || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len)
- || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len))
- {
- if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) }
- } else {
- None
+ let run_dealloc_fn = |buf_ptr: *mut sort::TimSortRun, len: usize| {
+ // SAFETY: The caller must ensure that buf_ptr was created by elem_alloc_fn with the same
+ // len.
+ unsafe {
+ alloc::dealloc(
+ buf_ptr as *mut u8,
+ alloc::Layout::array::<sort::TimSortRun>(len).unwrap_unchecked(),
+ );
}
- }
+ };
- #[derive(Clone, Copy)]
- struct Run {
- start: usize,
- len: usize,
- }
+ sort::merge_sort(v, &mut is_less, elem_alloc_fn, elem_dealloc_fn, run_alloc_fn, run_dealloc_fn);
}
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[inline]
pub fn to_ascii_uppercase(&self) -> String {
- let mut bytes = self.as_bytes().to_vec();
- bytes.make_ascii_uppercase();
- // make_ascii_uppercase() preserves the UTF-8 invariant.
- unsafe { String::from_utf8_unchecked(bytes) }
+ let mut s = self.to_owned();
+ s.make_ascii_uppercase();
+ s
}
/// Returns a copy of this string where each character is mapped to its
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[inline]
pub fn to_ascii_lowercase(&self) -> String {
- let mut bytes = self.as_bytes().to_vec();
- bytes.make_ascii_lowercase();
- // make_ascii_lowercase() preserves the UTF-8 invariant.
- unsafe { String::from_utf8_unchecked(bytes) }
+ let mut s = self.to_owned();
+ s.make_ascii_lowercase();
+ s
}
}
// This is a `NonNull` to allow optimizing the size of this type in enums,
// but it is not necessarily a valid pointer.
// `Weak::new` sets this to `usize::MAX` so that it doesn’t need
- // to allocate space on the heap. That's not a value a real pointer
+ // to allocate space on the heap. That's not a value a real pointer
// will ever have because RcBox has alignment at least 2.
// This is only possible when `T: Sized`; unsized `T` never dangle.
ptr: NonNull<ArcInner<T>>,
///
/// This will succeed even if there are outstanding weak references.
///
+ // FIXME: when `Arc::into_inner` is stabilized, add this paragraph:
+ /*
+ /// It is strongly recommended to use [`Arc::into_inner`] instead if you don't
+ /// want to keep the `Arc` in the [`Err`] case.
+ /// Immediately dropping the [`Err`] payload, like in the expression
+ /// `Arc::try_unwrap(this).ok()`, can still cause the strong count to
+ /// drop to zero and the inner value of the `Arc` to be dropped:
+ /// For instance if two threads execute this expression in parallel, then
+ /// there is a race condition. The threads could first both check whether they
+ /// have the last clone of their `Arc` via `Arc::try_unwrap`, and then
+ /// both drop their `Arc` in the call to [`ok`][`Result::ok`],
+ /// taking the strong count from two down to zero.
+ ///
+ */
/// # Examples
///
/// ```
Ok(elem)
}
}
+
+ /// Returns the inner value, if the `Arc` has exactly one strong reference.
+ ///
+ /// Otherwise, [`None`] is returned and the `Arc` is dropped.
+ ///
+ /// This will succeed even if there are outstanding weak references.
+ ///
+ /// If `Arc::into_inner` is called on every clone of this `Arc`,
+ /// it is guaranteed that exactly one of the calls returns the inner value.
+ /// This means in particular that the inner value is not dropped.
+ ///
+ /// The similar expression `Arc::try_unwrap(this).ok()` does not
+ /// offer such a guarantee. See the last example below.
+ //
+ // FIXME: when `Arc::into_inner` is stabilized, add this to end
+ // of the previous sentence:
+ /*
+ /// and the documentation of [`Arc::try_unwrap`].
+ */
+ ///
+ /// # Examples
+ ///
+ /// Minimal example demonstrating the guarantee that `Arc::into_inner` gives.
+ /// ```
+ /// #![feature(arc_into_inner)]
+ ///
+ /// use std::sync::Arc;
+ ///
+ /// let x = Arc::new(3);
+ /// let y = Arc::clone(&x);
+ ///
+ /// // Two threads calling `Arc::into_inner` on both clones of an `Arc`:
+ /// let x_thread = std::thread::spawn(|| Arc::into_inner(x));
+ /// let y_thread = std::thread::spawn(|| Arc::into_inner(y));
+ ///
+ /// let x_inner_value = x_thread.join().unwrap();
+ /// let y_inner_value = y_thread.join().unwrap();
+ ///
+ /// // One of the threads is guaranteed to receive the inner value:
+ /// assert!(matches!(
+ /// (x_inner_value, y_inner_value),
+ /// (None, Some(3)) | (Some(3), None)
+ /// ));
+ /// // The result could also be `(None, None)` if the threads called
+ /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.
+ /// ```
+ ///
+ /// A more practical example demonstrating the need for `Arc::into_inner`:
+ /// ```
+ /// #![feature(arc_into_inner)]
+ ///
+ /// use std::sync::Arc;
+ ///
+ /// // Definition of a simple singly linked list using `Arc`:
+ /// #[derive(Clone)]
+ /// struct LinkedList<T>(Option<Arc<Node<T>>>);
+ /// struct Node<T>(T, Option<Arc<Node<T>>>);
+ ///
+ /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`
+ /// // can cause a stack overflow. To prevent this, we can provide a
+ /// // manual `Drop` implementation that does the destruction in a loop:
+ /// impl<T> Drop for LinkedList<T> {
+ /// fn drop(&mut self) {
+ /// let mut link = self.0.take();
+ /// while let Some(arc_node) = link.take() {
+ /// if let Some(Node(_value, next)) = Arc::into_inner(arc_node) {
+ /// link = next;
+ /// }
+ /// }
+ /// }
+ /// }
+ ///
+ /// // Implementation of `new` and `push` omitted
+ /// impl<T> LinkedList<T> {
+ /// /* ... */
+ /// # fn new() -> Self {
+ /// # LinkedList(None)
+ /// # }
+ /// # fn push(&mut self, x: T) {
+ /// # self.0 = Some(Arc::new(Node(x, self.0.take())));
+ /// # }
+ /// }
+ ///
+ /// // The following code could have still caused a stack overflow
+ /// // despite the manual `Drop` impl if that `Drop` impl had used
+ /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`.
+ ///
+ /// // Create a long list and clone it
+ /// let mut x = LinkedList::new();
+ /// for i in 0..100000 {
+ /// x.push(i); // Adds i to the front of x
+ /// }
+ /// let y = x.clone();
+ ///
+ /// // Drop the clones in parallel
+ /// let x_thread = std::thread::spawn(|| drop(x));
+ /// let y_thread = std::thread::spawn(|| drop(y));
+ /// x_thread.join().unwrap();
+ /// y_thread.join().unwrap();
+ /// ```
+
+ // FIXME: when `Arc::into_inner` is stabilized, adjust above documentation
+ // and the documentation of `Arc::try_unwrap` according to the `FIXME`s. Also
+ // open an issue on rust-lang/rust-clippy, asking for a lint against
+ // `Arc::try_unwrap(...).ok()`.
+ #[inline]
+ #[unstable(feature = "arc_into_inner", issue = "106894")]
+ pub fn into_inner(this: Self) -> Option<T> {
+ // Make sure that the ordinary `Drop` implementation isn’t called as well
+ let mut this = mem::ManuallyDrop::new(this);
+
+ // Following the implementation of `drop` and `drop_slow`
+ if this.inner().strong.fetch_sub(1, Release) != 1 {
+ return None;
+ }
+
+ acquire!(this.inner().strong);
+
+ // SAFETY: This mirrors the line
+ //
+ // unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) };
+ //
+ // in `drop_slow`. Instead of dropping the value behind the pointer,
+ // it is read and eventually returned; `ptr::read` has the same
+ // safety conditions as `ptr::drop_in_place`.
+ let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) };
+
+ drop(Weak { ptr: this.ptr });
+
+ Some(inner)
+ }
}
impl<T> Arc<[T]> {
//
// The acquire label here ensures a happens-before relationship with any
// writes to `strong` (in particular in `Weak::upgrade`) prior to decrements
- // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded
+ // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded
// weak ref was never dropped, the CAS here will fail so we do not care to synchronize.
if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() {
// This needs to be an `Acquire` to synchronize with the decrement of the `strong`
}
// This fence is needed to prevent reordering of use of the data and
- // deletion of the data. Because it is marked `Release`, the decreasing
+ // deletion of the data. Because it is marked `Release`, the decreasing
// of the reference count synchronizes with this `Acquire` fence. This
// means that use of the data happens before decreasing the reference
// count, which happens before this fence, which happens before the
} else {
return Weak { ptr: self.ptr };
};
- // See comments in Arc::clone() for why this is relaxed. This can use a
+ // See comments in Arc::clone() for why this is relaxed. This can use a
// fetch_add (ignoring the lock) because the weak count is only locked
// where are *no other* weak pointers in existence. (So we can't be
// running this code in that case).
assert_eq!(Arc::try_unwrap(x), Ok(5));
}
+#[test]
+fn into_inner() {
+ for _ in 0..100
+ // ^ Increase chances of hitting potential race conditions
+ {
+ let x = Arc::new(3);
+ let y = Arc::clone(&x);
+ let r_thread = std::thread::spawn(|| Arc::into_inner(x));
+ let s_thread = std::thread::spawn(|| Arc::into_inner(y));
+ let r = r_thread.join().expect("r_thread panicked");
+ let s = s_thread.join().expect("s_thread panicked");
+ assert!(
+ matches!((r, s), (None, Some(3)) | (Some(3), None)),
+ "assertion failed: unexpected result `{:?}`\
+ \n expected `(None, Some(3))` or `(Some(3), None)`",
+ (r, s),
+ );
+ }
+
+ let x = Arc::new(3);
+ assert_eq!(Arc::into_inner(x), Some(3));
+
+ let x = Arc::new(4);
+ let y = Arc::clone(&x);
+ assert_eq!(Arc::into_inner(x), None);
+ assert_eq!(Arc::into_inner(y), Some(4));
+
+ let x = Arc::new(5);
+ let _w = Arc::downgrade(&x);
+ assert_eq!(Arc::into_inner(x), Some(5));
+}
+
#[test]
fn into_from_raw() {
let x = Arc::new(Box::new("hello"));
}
// as_slice() must only be called when iter.len() is > 0 because
- // vec::Splice modifies vec::Drain fields and may grow the vec which would invalidate
- // the iterator's internal pointers. Creating a reference to deallocated memory
- // is invalid even when it is zero-length
+ // it also gets touched by vec::Splice which may turn it into a dangling pointer
+ // which would make it and the vec pointer point to different allocations which would
+ // lead to invalid pointer arithmetic below.
let drop_ptr = iter.as_slice().as_ptr();
unsafe {
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
pub(super) alloc: ManuallyDrop<A>,
pub(super) ptr: *const T,
- pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
}
let mut this = ManuallyDrop::new(self);
// SAFETY: This allocation originally came from a `Vec`, so it passes
- // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`,
+ // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`,
// so the `sub_ptr`s below cannot wrap, and will produce a well-formed
- // range. `end` ≤ `buf + cap`, so the range will be in-bounds.
+ // range. `end` ≤ `buf + cap`, so the range will be in-bounds.
// Taking `alloc` is ok because nothing else is going to look at it,
// since our `Drop` impl isn't going to run so there's no more code.
unsafe {
#[rustc_specialization_trait]
pub(super) unsafe trait IsZero {
- /// Whether this value's representation is all zeros
+ /// Whether this value's representation is all zeros,
+ /// or can be represented with all zeroes.
fn is_zero(&self) -> bool;
}
#[inline]
fn is_zero(&self) -> bool {
// Because this is generated as a runtime check, it's not obvious that
- // it's worth doing if the array is really long. The threshold here
+ // it's worth doing if the array is really long. The threshold here
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
// fails to const-fold the check in `vec![[1; 32]; n]`
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
NonZeroIsize,
);
+macro_rules! impl_is_zero_option_of_num {
+ ($($t:ty,)+) => {$(
+ unsafe impl IsZero for Option<$t> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ const {
+ let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+ assert!(none.is_none());
+ }
+ self.is_none()
+ }
+ }
+ )+};
+}
+
+impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,);
+
unsafe impl<T: IsZero> IsZero for Wrapping<T> {
#[inline]
fn is_zero(&self) -> bool {
self.reserve(range.len());
// SAFETY:
- // - `slice::range` guarantees that the given range is valid for indexing self
+ // - `slice::range` guarantees that the given range is valid for indexing self
unsafe {
self.spec_extend_from_within(range);
}
// HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is
// required for this method definition, is not available. Instead use the
- // `slice::to_vec` function which is only available with cfg(test)
+ // `slice::to_vec` function which is only available with cfg(test)
// NB see the slice::hack module in slice.rs for more information
#[cfg(test)]
fn clone(&self) -> Self {
impl<I: Iterator, A: Allocator> Drop for Splice<'_, I, A> {
fn drop(&mut self) {
self.drain.by_ref().for_each(drop);
+ // At this point draining is done and the only remaining tasks are splicing
+ // and moving things into the final place.
+ // Which means we can replace the slice::Iter with pointers that won't point to deallocated
+ // memory, so that Drain::drop is still allowed to call iter.len(), otherwise it would break
+ // the ptr.sub_ptr contract.
+ self.drain.iter = (&[]).iter();
unsafe {
if self.drain.tail_len == 0 {
#![feature(allocator_api)]
#![feature(alloc_layout_extra)]
#![feature(assert_matches)]
-#![feature(box_syntax)]
#![feature(btree_drain_filter)]
#![feature(cow_is_borrowed)]
#![feature(const_box)]
}
// Test that, if we reserved enough space, adding and removing elements does not
- // invalidate references into the vector (such as `v0`). This test also
+ // invalidate references into the vector (such as `v0`). This test also
// runs in Miri, which would detect such problems.
// Note that this test does *not* constitute a stable guarantee that all these functions do not
// reallocate! Only what is explicitly documented at
/// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth
/// noting that the hashes and ordering will vary between Rust releases. Beware
/// of relying on them inside of your code!
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+#[derive(Clone, Copy, Debug, Hash, Eq)]
+#[derive_const(PartialEq, PartialOrd, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct TypeId {
t: u64,
/// use std::array::IntoIter;
/// use std::mem::MaybeUninit;
///
- /// # // Hi! Thanks for reading the code. This is restricted to `Copy` because
- /// # // otherwise it could leak. A fully-general version this would need a drop
+ /// # // Hi! Thanks for reading the code. This is restricted to `Copy` because
+ /// # // otherwise it could leak. A fully-general version this would need a drop
/// # // guard to handle panics from the iterator, but this works for an example.
/// fn next_chunk<T: Copy, const N: usize>(
/// it: &mut impl Iterator<Item = T>,
let initialized = 0..0;
// SAFETY: We're telling it that none of the elements are initialized,
- // which is trivially true. And ∀N: usize, 0 <= N.
+ // which is trivially true. And ∀N: usize, 0 <= N.
unsafe { Self::new_unchecked(buffer, initialized) }
}
use crate::fmt::{self, Debug, Display};
use crate::marker::{PhantomData, Unsize};
use crate::mem;
-use crate::ops::{CoerceUnsized, Deref, DerefMut};
+use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn};
use crate::ptr::{self, NonNull};
mod lazy;
#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<Cell<U>> for Cell<T> {}
+// Allow types that wrap `Cell` to also implement `DispatchFromDyn`
+// and become object safe method receivers.
+// Note that currently `Cell` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: Cell<&Self>` won't work
+// `self: CellWrapper<Self>` becomes possible
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Cell<U>> for Cell<T> {}
+
impl<T> Cell<[T]> {
/// Returns a `&[Cell<T>]` from a `&Cell<[T]>`
///
#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<UnsafeCell<U>> for UnsafeCell<T> {}
+// Allow types that wrap `UnsafeCell` to also implement `DispatchFromDyn`
+// and become object safe method receivers.
+// Note that currently `UnsafeCell` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: UnsafeCell<&Self>` won't work
+// `self: UnsafeCellWrapper<Self>` becomes possible
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<UnsafeCell<U>> for UnsafeCell<T> {}
+
/// [`UnsafeCell`], but [`Sync`].
///
/// This is just an `UnsafeCell`, except it implements `Sync`
//#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<SyncUnsafeCell<U>> for SyncUnsafeCell<T> {}
+// Allow types that wrap `SyncUnsafeCell` to also implement `DispatchFromDyn`
+// and become object safe method receivers.
+// Note that currently `SyncUnsafeCell` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: SyncUnsafeCell<&Self>` won't work
+// `self: SyncUnsafeCellWrapper<Self>` becomes possible
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+//#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<SyncUnsafeCell<U>> for SyncUnsafeCell<T> {}
+
#[allow(unused)]
fn assert_coerce_unsized(
a: UnsafeCell<&i32>,
OnceCell { inner: UnsafeCell::new(Some(value)) }
}
}
+
+// Just like for `Cell<T>` this isn't needed, but results in nicer error messages.
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T> !Sync for OnceCell<T> {}
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "Error")]
#[rustc_has_incoherent_inherent_impls]
+#[cfg_attr(not(bootstrap), allow(multiple_supertrait_upcastable))]
pub trait Error: Debug + Display {
/// The lower-level source of this error, if any.
///
/// family of functions. It contains a function to format the given value. At
/// compile time it is ensured that the function and the value have the correct
/// types, and then this struct is used to canonicalize arguments to one type.
+#[cfg_attr(not(bootstrap), lang = "format_argument")]
#[derive(Copy, Clone)]
#[allow(missing_debug_implementations)]
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
/// This struct represents the unsafety of constructing an `Arguments`.
/// It exists, rather than an unsafe function, in order to simplify the expansion
/// of `format_args!(..)` and reduce the scope of the `unsafe` block.
+#[cfg_attr(not(bootstrap), lang = "format_unsafe_arg")]
#[allow(missing_debug_implementations)]
#[doc(hidden)]
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
/// ```
///
/// [`format()`]: ../../std/fmt/fn.format.html
+#[cfg_attr(not(bootstrap), lang = "format_arguments")]
#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "Arguments")]
#[derive(Copy, Clone)]
pub struct Arguments<'a> {
// Format string pieces to print.
}
impl<'a> Arguments<'a> {
- /// Get the formatted string, if it has no arguments to be formatted.
+ /// Get the formatted string, if it has no arguments to be formatted at runtime.
///
- /// This can be used to avoid allocations in the most trivial case.
+ /// This can be used to avoid allocations in some cases.
+ ///
+ /// # Guarantees
+ ///
+ /// For `format_args!("just a literal")`, this function is guaranteed to
+ /// return `Some("just a literal")`.
+ ///
+ /// For most cases with placeholders, this function will return `None`.
+ ///
+ /// However, the compiler may perform optimizations that can cause this
+ /// function to return `Some(_)` even if the format string contains
+ /// placeholders. For example, `format_args!("Hello, {}!", "world")` may be
+ /// optimized to `format_args!("Hello, world!")`, such that `as_str()`
+ /// returns `Some("Hello, world!")`.
+ ///
+ /// The behavior for anything but the trivial case (without placeholders)
+ /// is not guaranteed, and should not be relied upon for anything other
+ /// than optimization.
///
/// # Examples
///
/// ```rust
/// assert_eq!(format_args!("hello").as_str(), Some("hello"));
/// assert_eq!(format_args!("").as_str(), Some(""));
- /// assert_eq!(format_args!("{}", 1).as_str(), None);
+ /// assert_eq!(format_args!("{:?}", std::env::current_dir()).as_str(), None);
/// ```
#[stable(feature = "fmt_as_str", since = "1.52.0")]
#[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
/// }
/// }
///
- /// assert_eq!(&format!("{}", Foo::new(2)), "2");
- /// assert_eq!(&format!("{}", Foo::new(-1)), "-1");
- /// assert_eq!(&format!("{}", Foo::new(0)), "0");
- /// assert_eq!(&format!("{:#}", Foo::new(-1)), "-Foo 1");
- /// assert_eq!(&format!("{:0>#8}", Foo::new(-1)), "00-Foo 1");
+ /// assert_eq!(format!("{}", Foo::new(2)), "2");
+ /// assert_eq!(format!("{}", Foo::new(-1)), "-1");
+ /// assert_eq!(format!("{}", Foo::new(0)), "0");
+ /// assert_eq!(format!("{:#}", Foo::new(-1)), "-Foo 1");
+ /// assert_eq!(format!("{:0>#8}", Foo::new(-1)), "00-Foo 1");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn pad_integral(&mut self, is_nonnegative: bool, prefix: &str, buf: &str) -> Result {
/// }
/// }
///
- /// assert_eq!(&format!("{Foo:<4}"), "Foo ");
- /// assert_eq!(&format!("{Foo:0>4}"), "0Foo");
+ /// assert_eq!(format!("{Foo:<4}"), "Foo ");
+ /// assert_eq!(format!("{Foo:0>4}"), "0Foo");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn pad(&mut self, s: &str) -> Result {
/// }
/// }
///
- /// assert_eq!(&format!("{Foo}"), "Foo");
- /// assert_eq!(&format!("{Foo:0>8}"), "Foo");
+ /// assert_eq!(format!("{Foo}"), "Foo");
+ /// assert_eq!(format!("{Foo:0>8}"), "Foo");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn write_str(&mut self, data: &str) -> Result {
/// }
/// }
///
- /// assert_eq!(&format!("{}", Foo(-1)), "Foo -1");
- /// assert_eq!(&format!("{:0>8}", Foo(2)), "Foo 2");
+ /// assert_eq!(format!("{}", Foo(-1)), "Foo -1");
+ /// assert_eq!(format!("{:0>8}", Foo(2)), "Foo 2");
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result {
/// }
///
/// // We set alignment to the right with ">".
- /// assert_eq!(&format!("{Foo:G>3}"), "GGG");
- /// assert_eq!(&format!("{Foo:t>6}"), "tttttt");
+ /// assert_eq!(format!("{Foo:G>3}"), "GGG");
+ /// assert_eq!(format!("{Foo:t>6}"), "tttttt");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
/// }
/// }
///
- /// assert_eq!(&format!("{Foo:<}"), "left");
- /// assert_eq!(&format!("{Foo:>}"), "right");
- /// assert_eq!(&format!("{Foo:^}"), "center");
- /// assert_eq!(&format!("{Foo}"), "into the void");
+ /// assert_eq!(format!("{Foo:<}"), "left");
+ /// assert_eq!(format!("{Foo:>}"), "right");
+ /// assert_eq!(format!("{Foo:^}"), "center");
+ /// assert_eq!(format!("{Foo}"), "into the void");
/// ```
#[must_use]
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
/// fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// if let Some(width) = formatter.width() {
/// // If we received a width, we use it
- /// write!(formatter, "{:width$}", &format!("Foo({})", self.0), width = width)
+ /// write!(formatter, "{:width$}", format!("Foo({})", self.0), width = width)
/// } else {
/// // Otherwise we do nothing special
/// write!(formatter, "Foo({})", self.0)
/// }
/// }
///
- /// assert_eq!(&format!("{:10}", Foo(23)), "Foo(23) ");
- /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)");
+ /// assert_eq!(format!("{:10}", Foo(23)), "Foo(23) ");
+ /// assert_eq!(format!("{}", Foo(23)), "Foo(23)");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
/// }
/// }
///
- /// assert_eq!(&format!("{:.4}", Foo(23.2)), "Foo(23.2000)");
- /// assert_eq!(&format!("{}", Foo(23.2)), "Foo(23.20)");
+ /// assert_eq!(format!("{:.4}", Foo(23.2)), "Foo(23.2000)");
+ /// assert_eq!(format!("{}", Foo(23.2)), "Foo(23.20)");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
/// }
/// }
///
- /// assert_eq!(&format!("{:+}", Foo(23)), "Foo(+23)");
- /// assert_eq!(&format!("{:+}", Foo(-23)), "Foo(-23)");
- /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)");
+ /// assert_eq!(format!("{:+}", Foo(23)), "Foo(+23)");
+ /// assert_eq!(format!("{:+}", Foo(-23)), "Foo(-23)");
+ /// assert_eq!(format!("{}", Foo(23)), "Foo(23)");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
/// }
/// }
///
- /// assert_eq!(&format!("{:-}", Foo(23)), "-Foo(23)");
- /// assert_eq!(&format!("{}", Foo(23)), "Foo(23)");
+ /// assert_eq!(format!("{:-}", Foo(23)), "-Foo(23)");
+ /// assert_eq!(format!("{}", Foo(23)), "Foo(23)");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
/// }
/// }
///
- /// assert_eq!(&format!("{:#}", Foo(23)), "Foo(23)");
- /// assert_eq!(&format!("{}", Foo(23)), "23");
+ /// assert_eq!(format!("{:#}", Foo(23)), "Foo(23)");
+ /// assert_eq!(format!("{}", Foo(23)), "23");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
/// }
/// }
///
- /// assert_eq!(&format!("{:04}", Foo(23)), "23");
+ /// assert_eq!(format!("{:04}", Foo(23)), "23");
/// ```
#[must_use]
#[stable(feature = "fmt_flags", since = "1.5.0")]
//! these can be statically allocated and are slightly optimized for the runtime
#![allow(missing_debug_implementations)]
+#[cfg_attr(not(bootstrap), lang = "format_placeholder")]
#[derive(Copy, Clone)]
+// FIXME: Rename this to Placeholder
pub struct Argument {
pub position: usize,
pub format: FormatSpec,
pub width: Count,
}
+impl Argument {
+ #[inline(always)]
+ pub const fn new(
+ position: usize,
+ fill: char,
+ align: Alignment,
+ flags: u32,
+ precision: Count,
+ width: Count,
+ ) -> Self {
+ Self { position, format: FormatSpec { fill, align, flags, precision, width } }
+ }
+}
+
/// Possible alignments that can be requested as part of a formatting directive.
+#[cfg_attr(not(bootstrap), lang = "format_alignment")]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum Alignment {
/// Indication that contents should be left-aligned.
}
/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
+#[cfg_attr(not(bootstrap), lang = "format_count")]
#[derive(Copy, Clone)]
pub enum Count {
/// Specified with a literal number, stores the value
unsafe { &mut *cx.0.as_ptr().cast() }
}
+// FIXME(swatinem): This fn is currently needed to work around shortcomings
+// in type and lifetime inference.
+// See the comment at the bottom of `LoweringContext::make_async_expr` and
+// <https://github.com/rust-lang/rust/issues/104826>.
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[inline]
/// backend used. Programs cannot rely on `black_box` for *correctness* in any way.
///
/// [`std::convert::identity`]: crate::convert::identity
+///
+/// # When is this useful?
+///
+/// First and foremost: `black_box` does _not_ guarantee any exact behavior and, in some cases, may
+/// do nothing at all. As such, it **must not be relied upon to control critical program behavior.**
+/// This _immediately_ precludes any direct use of this function for cryptographic or security
+/// purposes.
+///
+/// While not suitable in those mission-critical cases, `back_box`'s functionality can generally be
+/// relied upon for benchmarking, and should be used there. It will try to ensure that the
+/// compiler doesn't optimize away part of the intended test code based on context. For
+/// example:
+///
+/// ```
+/// fn contains(haystack: &[&str], needle: &str) -> bool {
+/// haystack.iter().any(|x| x == &needle)
+/// }
+///
+/// pub fn benchmark() {
+/// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
+/// let needle = "ghi";
+/// for _ in 0..10 {
+/// contains(&haystack, needle);
+/// }
+/// }
+/// ```
+///
+/// The compiler could theoretically make optimizations like the following:
+///
+/// - `needle` and `haystack` are always the same, move the call to `contains` outside the loop and
+/// delete the loop
+/// - Inline `contains`
+/// - `needle` and `haystack` have values known at compile time, `contains` is always true. Remove
+/// the call and replace with `true`
+/// - Nothing is done with the result of `contains`: delete this function call entirely
+/// - `benchmark` now has no purpose: delete this function
+///
+/// It is not likely that all of the above happens, but the compiler is definitely able to make some
+/// optimizations that could result in a very inaccurate benchmark. This is where `black_box` comes
+/// in:
+///
+/// ```
+/// use std::hint::black_box;
+///
+/// // Same `contains` function
+/// fn contains(haystack: &[&str], needle: &str) -> bool {
+/// haystack.iter().any(|x| x == &needle)
+/// }
+///
+/// pub fn benchmark() {
+/// let haystack = vec!["abc", "def", "ghi", "jkl", "mno"];
+/// let needle = "ghi";
+/// for _ in 0..10 {
+/// // Adjust our benchmark loop contents
+/// black_box(contains(black_box(&haystack), black_box(needle)));
+/// }
+/// }
+/// ```
+///
+/// This essentially tells the compiler to block optimizations across any calls to `black_box`. So,
+/// it now:
+///
+/// - Treats both arguments to `contains` as unpredictable: the body of `contains` can no longer be
+/// optimized based on argument values
+/// - Treats the call to `contains` and its result as volatile: the body of `benchmark` cannot
+/// optimize this away
+///
+/// This makes our benchmark much more realistic to how the function would be used in situ, where
+/// arguments are usually not known at compile time and the result is used in some way.
#[inline]
#[stable(feature = "bench_black_box", since = "1.66.0")]
#[rustc_const_unstable(feature = "const_black_box", issue = "none")]
//!
//! #### Statements
//! - Assign statements work via normal Rust assignment.
-//! - [`Retag`] statements have an associated function.
+//! - [`Retag`], [`StorageLive`], [`StorageDead`], [`Deinit`] statements have an associated function.
//!
//! #### Rvalues
//!
//! - Operands implicitly convert to `Use` rvalues.
//! - `&`, `&mut`, `addr_of!`, and `addr_of_mut!` all work to create their associated rvalue.
-//! - [`Discriminant`] has an associated function.
+//! - [`Discriminant`] and [`Len`] have associated functions.
+//! - Unary and binary operations use their normal Rust syntax - `a * b`, `!c`, etc.
+//! - Checked binary operations are represented by wrapping the associated binop in [`Checked`].
+//! - Array repetition syntax (`[foo; 10]`) creates the associated rvalue.
//!
//! #### Terminators
//!
define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
+define!("mir_storage_live", fn StorageLive<T>(local: T));
+define!("mir_storage_dead", fn StorageDead<T>(local: T));
+define!("mir_deinit", fn Deinit<T>(place: T));
+define!("mir_checked", fn Checked<T>(binop: T) -> (T, bool));
+define!("mir_len", fn Len<T>(place: T) -> usize);
define!("mir_retag", fn Retag<T>(place: T));
define!("mir_move", fn Move<T>(place: T) -> T);
define!("mir_static", fn Static<T>(s: T) -> &'static T);
) -> impl FnMut((), T) -> ControlFlow<B> + '_ {
move |(), x| match f(x) {
Some(x) => ControlFlow::Break(x),
- None => ControlFlow::CONTINUE,
+ None => ControlFlow::Continue(()),
}
}
#[rustc_inherit_overflow_checks]
fn advance<U: Iterator>(n: usize, iter: &mut U) -> ControlFlow<(), usize> {
match iter.advance_by(n) {
- Ok(()) => ControlFlow::BREAK,
+ Ok(()) => ControlFlow::Break(()),
Err(advanced) => ControlFlow::Continue(n - advanced),
}
}
#[rustc_inherit_overflow_checks]
fn advance<U: DoubleEndedIterator>(n: usize, iter: &mut U) -> ControlFlow<(), usize> {
match iter.advance_back_by(n) {
- Ok(()) => ControlFlow::BREAK,
+ Ok(()) => ControlFlow::Break(()),
Err(advanced) => ControlFlow::Continue(n - advanced),
}
}
where
Self: TrustedRandomAccessNoCoerce,
{
- // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index
+ // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index
// that is in bounds.
// Additionally Self: TrustedRandomAccess is only implemented for Copy types
// which means even repeated reads of the same index would be safe.
+use crate::fmt;
use crate::ops::{Generator, GeneratorState};
use crate::pin::Pin;
/// ```
#[inline]
#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
-pub fn from_generator<G: Generator<Return = ()> + Unpin>(
- generator: G,
-) -> impl Iterator<Item = G::Yield> {
+pub fn from_generator<G: Generator<Return = ()> + Unpin>(generator: G) -> FromGenerator<G> {
FromGenerator(generator)
}
-struct FromGenerator<G>(G);
+/// An iterator over the values yielded by an underlying generator.
+///
+/// This `struct` is created by the [`iter::from_generator()`] function. See its documentation for
+/// more.
+///
+/// [`iter::from_generator()`]: from_generator
+#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
+#[derive(Clone)]
+pub struct FromGenerator<G>(G);
+#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
impl<G: Generator<Return = ()> + Unpin> Iterator for FromGenerator<G> {
type Item = G::Yield;
}
}
}
+
+#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
+impl<G> fmt::Debug for FromGenerator<G> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FromGenerator").finish()
+ }
+}
#[inline]
fn check<T>(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut((), T) -> ControlFlow<T> {
move |(), x| {
- if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::CONTINUE }
+ if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::Continue(()) }
}
}
#[inline]
fn check<T>(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> ControlFlow<()> {
move |(), x| {
- if f(x) { ControlFlow::CONTINUE } else { ControlFlow::BREAK }
+ if f(x) { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
}
}
- self.try_fold((), check(f)) == ControlFlow::CONTINUE
+ self.try_fold((), check(f)) == ControlFlow::Continue(())
}
/// Tests if any element of the iterator matches a predicate.
#[inline]
fn check<T>(mut f: impl FnMut(T) -> bool) -> impl FnMut((), T) -> ControlFlow<()> {
move |(), x| {
- if f(x) { ControlFlow::BREAK } else { ControlFlow::CONTINUE }
+ if f(x) { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
}
}
- self.try_fold((), check(f)) == ControlFlow::BREAK
+ self.try_fold((), check(f)) == ControlFlow::Break(())
}
/// Searches for an element of an iterator that satisfies a predicate.
#[inline]
fn check<T>(mut predicate: impl FnMut(&T) -> bool) -> impl FnMut((), T) -> ControlFlow<T> {
move |(), x| {
- if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::CONTINUE }
+ if predicate(&x) { ControlFlow::Break(x) } else { ControlFlow::Continue(()) }
}
}
fn check<T, B>(mut f: impl FnMut(T) -> Option<B>) -> impl FnMut((), T) -> ControlFlow<B> {
move |(), x| match f(x) {
Some(x) => ControlFlow::Break(x),
- None => ControlFlow::CONTINUE,
+ None => ControlFlow::Continue(()),
}
}
R: Residual<Option<I>>,
{
move |(), x| match f(&x).branch() {
- ControlFlow::Continue(false) => ControlFlow::CONTINUE,
+ ControlFlow::Continue(false) => ControlFlow::Continue(()),
ControlFlow::Continue(true) => ControlFlow::Break(Try::from_output(Some(x))),
ControlFlow::Break(r) => ControlFlow::Break(FromResidual::from_residual(r)),
}
F: FnMut(X, Y) -> Ordering,
{
move |x, y| match cmp(x, y) {
- Ordering::Equal => ControlFlow::CONTINUE,
+ Ordering::Equal => ControlFlow::Continue(()),
non_eq => ControlFlow::Break(non_eq),
}
}
F: FnMut(X, Y) -> Option<Ordering>,
{
move |x, y| match partial_cmp(x, y) {
- Some(Ordering::Equal) => ControlFlow::CONTINUE,
+ Some(Ordering::Equal) => ControlFlow::Continue(()),
non_eq => ControlFlow::Break(non_eq),
}
}
F: FnMut(X, Y) -> bool,
{
move |x, y| {
- if eq(x, y) { ControlFlow::CONTINUE } else { ControlFlow::BREAK }
+ if eq(x, y) { ControlFlow::Continue(()) } else { ControlFlow::Break(()) }
}
}
/// Compares two iterators element-wise using the given function.
///
-/// If `ControlFlow::CONTINUE` is returned from the function, the comparison moves on to the next
+/// If `ControlFlow::Continue(())` is returned from the function, the comparison moves on to the next
/// elements of both iterators. Returning `ControlFlow::Break(x)` short-circuits the iteration and
/// returns `ControlFlow::Break(x)`. If one of the iterators runs out of elements,
/// `ControlFlow::Continue(ord)` is returned where `ord` is the result of comparing the lengths of
#![warn(missing_docs)]
#![allow(explicit_outlives_requirements)]
#![allow(incomplete_features)]
+#![cfg_attr(not(bootstrap), warn(multiple_supertrait_upcastable))]
//
// Library features:
#![feature(const_align_offset)]
#![feature(unsized_fn_params)]
#![feature(asm_const)]
#![feature(const_transmute_copy)]
+#![cfg_attr(not(bootstrap), feature(multiple_supertrait_upcastable))]
//
// Target features:
#![feature(arm_target_feature)]
#[cfg_attr(not(test), rustc_diagnostic_item = "Sync")]
#[lang = "sync"]
#[rustc_on_unimplemented(
+ on(
+ _Self = "std::cell::OnceCell<T>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead"
+ ),
+ on(
+ _Self = "std::cell::Cell<u8>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<u16>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU16` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<u32>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<u64>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<usize>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i8>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI8` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i16>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI16` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i32>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i64>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI64` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<isize>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicIsize` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<bool>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<T>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`",
+ ),
+ on(
+ _Self = "std::cell::RefCell<T>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead",
+ ),
message = "`{Self}` cannot be shared between threads safely",
label = "`{Self}` cannot be shared between threads safely"
)]
/// Developer's Manual (Volume 1).
///
/// The only field which is relevant for the following code is PC, Precision Control. This
- /// field determines the precision of the operations performed by the FPU. It can be set to:
+ /// field determines the precision of the operations performed by the FPU. It can be set to:
/// - 0b00, single precision i.e., 32-bits
/// - 0b10, double precision i.e., 64-bits
/// - 0b11, double extended precision i.e., 80-bits (default state)
///
/// ```
/// #![feature(bigint_helper_methods)]
- /// // Only the most significant word is signed.
+ /// // Only the most significant word is signed.
/// //
#[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")]
#[doc = concat!("// + -5 9 (b = -5 × 2^", stringify!($BITS), " + 9)")]
/// overflow.
///
/// Performs "ternary subtraction" by subtracting both an integer
- /// operandand a borrow-in bit from `self`, and returns a tuple of the
+ /// operand and a borrow-in bit from `self`, and returns a tuple of the
/// difference along with a boolean indicating whether an arithmetic
/// overflow would occur. On overflow, the wrapped value is returned.
///
///
/// ```
/// #![feature(bigint_helper_methods)]
- /// // Only the most significant word is signed.
+ /// // Only the most significant word is signed.
/// //
#[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")]
#[doc = concat!("// - -5 9 (b = -5 × 2^", stringify!($BITS), " + 9)")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
+ #[rustc_allow_const_fn_unstable(const_cmp)]
pub const fn signum(self) -> Self {
- match self {
- n if n > 0 => 1,
- 0 => 0,
- _ => -1,
- }
+ // Picking the right way to phrase this is complicated
+ // (<https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign>)
+ // so delegate it to `Ord` which is already producing -1/0/+1
+ // exactly like we need and can be the place to deal with the complexity.
+ self.cmp(&0) as _
}
/// Returns `true` if `self` is positive and `false` if the number is zero or
}
}
}
-
-impl<B> ControlFlow<B, ()> {
- /// It's frequently the case that there's no value needed with `Continue`,
- /// so this provides a way to avoid typing `(())`, if you prefer it.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(control_flow_enum)]
- /// use std::ops::ControlFlow;
- ///
- /// let mut partial_sum = 0;
- /// let last_used = (1..10).chain(20..25).try_for_each(|x| {
- /// partial_sum += x;
- /// if partial_sum > 100 { ControlFlow::Break(x) }
- /// else { ControlFlow::CONTINUE }
- /// });
- /// assert_eq!(last_used.break_value(), Some(22));
- /// ```
- #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
- pub const CONTINUE: Self = ControlFlow::Continue(());
-}
-
-impl<C> ControlFlow<(), C> {
- /// APIs like `try_for_each` don't need values with `Break`,
- /// so this provides a way to avoid typing `(())`, if you prefer it.
- ///
- /// # Examples
- ///
- /// ```
- /// #![feature(control_flow_enum)]
- /// use std::ops::ControlFlow;
- ///
- /// let mut partial_sum = 0;
- /// (1..10).chain(20..25).try_for_each(|x| {
- /// if partial_sum > 100 { ControlFlow::BREAK }
- /// else { partial_sum += x; ControlFlow::CONTINUE }
- /// });
- /// assert_eq!(partial_sum, 108);
- /// ```
- #[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
- pub const BREAK: Self = ControlFlow::Break(());
-}
use crate::panicking::{panic, panic_str};
use crate::pin::Pin;
use crate::{
- convert, hint, mem,
+ cmp, convert, hint, mem,
ops::{self, ControlFlow, Deref, DerefMut},
};
}
}
+/// This specialization trait is a workaround for LLVM not currently (2023-01)
+/// being able to optimize this itself, even though Alive confirms that it would
+/// be legal to do so: <https://github.com/llvm/llvm-project/issues/52622>
+///
+/// Once that's fixed, `Option` should go back to deriving `PartialEq`, as
+/// it used to do before <https://github.com/rust-lang/rust/pull/103556>.
#[unstable(feature = "spec_option_partial_eq", issue = "none", reason = "exposed only for rustc")]
#[doc(hidden)]
pub trait SpecOptionPartialEq: Sized {
}
}
+#[stable(feature = "rust1", since = "1.0.0")]
+impl SpecOptionPartialEq for cmp::Ordering {
+ #[inline]
+ fn eq(l: &Option<Self>, r: &Option<Self>) -> bool {
+ l.map_or(2, |x| x as i8) == r.map_or(2, |x| x as i8)
+ }
+}
+
/////////////////////////////////////////////////////////////////////////////
// The Option Iterators
/////////////////////////////////////////////////////////////////////////////
impl<'a, T: ?Sized> Pin<&'a T> {
/// Constructs a new pin by mapping the interior value.
///
- /// For example, if you wanted to get a `Pin` of a field of something,
+ /// For example, if you wanted to get a `Pin` of a field of something,
/// you could use this to get access to that field in one line of code.
/// However, there are several gotchas with these "pinning projections";
/// see the [`pin` module] documentation for further details on that topic.
/// Construct a new pin by mapping the interior value.
///
- /// For example, if you wanted to get a `Pin` of a field of something,
+ /// For example, if you wanted to get a `Pin` of a field of something,
/// you could use this to get access to that field in one line of code.
/// However, there are several gotchas with these "pinning projections";
/// see the [`pin` module] documentation for further details on that topic.
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
// provenance).
- unsafe { mem::transmute(self) }
+ unsafe { mem::transmute(self.cast::<()>()) }
}
/// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn expose_addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn expose_addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
- self as usize
+ self.cast::<()>() as usize
}
/// Creates a new pointer with the given address.
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn with_addr(self, addr: usize) -> Self
- where
- T: Sized,
- {
+ pub fn with_addr(self, addr: usize) -> Self {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
//
// In the mean-time, this operation is defined to be "as if" it was
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
- where
- T: Sized,
- {
+ pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
self.with_addr(f(self.addr()))
}
/// This computes the same value that [`offset_from`](#method.offset_from)
/// would compute, but with the added precondition that the offset is
/// guaranteed to be non-negative. This method is equivalent to
- /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+ /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
/// but it provides slightly more information to the optimizer, which can
/// sometimes allow it to optimize slightly better with some backends.
///
// offset is not a multiple of `stride`, the input pointer was misaligned and no pointer
// offset will be able to produce a `p` aligned to the specified `a`.
//
- // The naive `-p (mod a)` equation inhibits LLVM's ability to select instructions
+ // The naive `-p (mod a)` equation inhibits LLVM's ability to select instructions
// like `lea`. We compute `(round_up_to_next_alignment(p, a) - p)` instead. This
// redistributes operations around the load-bearing, but pessimizing `and` instruction
// sufficiently for LLVM to be able to utilize the various optimizations it knows about.
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
// provenance).
- unsafe { mem::transmute(self) }
+ unsafe { mem::transmute(self.cast::<()>()) }
}
/// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn expose_addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn expose_addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
- self as usize
+ self.cast::<()>() as usize
}
/// Creates a new pointer with the given address.
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn with_addr(self, addr: usize) -> Self
- where
- T: Sized,
- {
+ pub fn with_addr(self, addr: usize) -> Self {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
//
// In the mean-time, this operation is defined to be "as if" it was
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
- where
- T: Sized,
- {
+ pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
self.with_addr(f(self.addr()))
}
/// This computes the same value that [`offset_from`](#method.offset_from)
/// would compute, but with the added precondition that the offset is
/// guaranteed to be non-negative. This method is equivalent to
- /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+ /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
/// but it provides slightly more information to the optimizer, which can
/// sometimes allow it to optimize slightly better with some backends.
///
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn addr(self) -> NonZeroUsize
- where
- T: Sized,
- {
+ pub fn addr(self) -> NonZeroUsize {
// SAFETY: The pointer is guaranteed by the type to be non-null,
// meaning that the address will be non-zero.
unsafe { NonZeroUsize::new_unchecked(self.pointer.addr()) }
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn with_addr(self, addr: NonZeroUsize) -> Self
- where
- T: Sized,
- {
+ pub fn with_addr(self, addr: NonZeroUsize) -> Self {
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
}
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn map_addr(self, f: impl FnOnce(NonZeroUsize) -> NonZeroUsize) -> Self
- where
- T: Sized,
- {
+ pub fn map_addr(self, f: impl FnOnce(NonZeroUsize) -> NonZeroUsize) -> Self {
self.with_addr(f(self.addr()))
}
use crate::cmp;
use crate::cmp::Ordering;
use crate::fmt;
-use crate::intrinsics::{assume, exact_div, unchecked_sub};
+use crate::intrinsics::assume;
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
use crate::marker::{PhantomData, Send, Sized, Sync};
use crate::mem::{self, SizedTypeProperties};
}
}
-// Macro helper functions
-#[inline(always)]
-fn size_from_ptr<T>(_: *const T) -> usize {
- mem::size_of::<T>()
-}
-
/// Immutable slice iterator
///
/// This struct is created by the [`iter`] method on [slices].
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a, T: 'a> {
ptr: NonNull<T>,
- end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
_marker: PhantomData<&'a T>,
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IterMut<'a, T: 'a> {
ptr: NonNull<T>,
- end: *mut T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ end: *mut T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
_marker: PhantomData<&'a mut T>,
};
}
-// To get rid of some bounds checks (see `position`), we compute the length in a somewhat
-// unexpected way. (Tested by `codegen/slice-position-bounds-check`.)
macro_rules! len {
($self: ident) => {{
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
let start = $self.ptr;
- let size = size_from_ptr(start.as_ptr());
- if size == 0 {
- // This _cannot_ use `unchecked_sub` because we depend on wrapping
+ if T::IS_ZST {
+ // This _cannot_ use `ptr_sub` because we depend on wrapping
// to represent the length of long ZST slice iterators.
$self.end.addr().wrapping_sub(start.as_ptr().addr())
} else {
- // We know that `start <= end`, so can do better than `offset_from`,
- // which needs to deal in signed. By setting appropriate flags here
- // we can tell LLVM this, which helps it remove bounds checks.
- // SAFETY: By the type invariant, `start <= end`
- let diff = unsafe { unchecked_sub($self.end.addr(), start.as_ptr().addr()) };
- // By also telling LLVM that the pointers are apart by an exact
- // multiple of the type size, it can optimize `len() == 0` down to
- // `start == end` instead of `(end - start) < size`.
- // SAFETY: By the type invariant, the pointers are aligned so the
- // distance between them must be a multiple of pointee size
- unsafe { exact_div(diff, size) }
+ // To get rid of some bounds checks (see `position`), we use ptr_sub instead of
+ // offset_from (Tested by `codegen/slice-position-bounds-check`.)
+ // SAFETY: by the type invariant pointers are aligned and `start <= end`
+ unsafe { $self.end.sub_ptr(start.as_ptr()) }
}
}};
}
/// Pure rust memchr implementation, taken from rust-memchr
pub mod memchr;
+#[unstable(
+ feature = "slice_internals",
+ issue = "none",
+ reason = "exposed from core to be reused in std;"
+)]
+pub mod sort;
+
mod ascii;
mod cmp;
mod index;
mod iter;
mod raw;
mod rotate;
-mod sort;
mod specialize;
#[stable(feature = "rust1", since = "1.0.0")]
// Because this function is first compiled in isolation,
// this check tells LLVM that the indexing below is
- // in-bounds. Then after inlining -- once the actual
+ // in-bounds. Then after inlining -- once the actual
// lengths of the slices are known -- it's removed.
let (a, b) = (&mut a[..n], &mut b[..n]);
/// let mut iter = slice.windows(4);
/// assert!(iter.next().is_none());
/// ```
+ ///
+ /// There's no `windows_mut`, as that existing would let safe code violate the
+ /// "only one `&mut` at a time to the same thing" rule. However, you can sometimes
+ /// use [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in
+ /// conjunction with `windows` to accomplish something similar:
+ /// ```
+ /// use std::cell::Cell;
+ ///
+ /// let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+ /// let slice = &mut array[..];
+ /// let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+ /// for w in slice_of_cells.windows(3) {
+ /// Cell::swap(&w[0], &w[2]);
+ /// }
+ /// assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn windows(&self, size: usize) -> Windows<'_, T> {
ArrayChunksMut::new(self)
}
- /// Returns an iterator over overlapping windows of `N` elements of a slice,
+ /// Returns an iterator over overlapping windows of `N` elements of a slice,
/// starting at the beginning of the slice.
///
/// This is the const generic equivalent of [`windows`].
let mid = left + size / 2;
// SAFETY: the while condition means `size` is strictly positive, so
- // `size/2 < size`. Thus `left + size/2 < left + size`, which
+ // `size/2 < size`. Thus `left + size/2 < left + size`, which
// coupled with the `left + size <= self.len()` invariant means
// we have `left + size/2 < self.len()`, and this is in-bounds.
let cmp = f(unsafe { self.get_unchecked(mid) });
//!
//! Unstable sorting is compatible with core because it doesn't allocate memory, unlike our
//! stable sorting implementation.
+//!
+//! In addition it also contains the core logic of the stable sort used by `slice::sort` based on
+//! TimSort.
use crate::cmp;
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
impl<T> Drop for CopyOnDrop<T> {
fn drop(&mut self) {
- // SAFETY: This is a helper class.
- // Please refer to its usage for correctness.
- // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`.
+ // SAFETY: This is a helper class.
+ // Please refer to its usage for correctness.
+ // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`.
unsafe {
ptr::copy_nonoverlapping(self.src, self.dest, 1);
}
) where
F: FnMut(&T, &T) -> bool,
{
+ // Limit the amount of iterations and fall back to heapsort, similarly to `slice::sort_unstable`.
+ // This lowers the worst case running time from O(n^2) to O(n log n).
+ // FIXME: Investigate whether it would be better to use something like Median of Medians
+ // or Fast Deterministic Selection to guarantee O(n) worst case.
+ let mut limit = usize::BITS - v.len().leading_zeros();
+
+ // True if the last partitioning was reasonably balanced.
+ let mut was_balanced = true;
+
loop {
// For slices of up to this length it's probably faster to simply sort them.
const MAX_INSERTION: usize = 10;
return;
}
+ if limit == 0 {
+ heapsort(v, is_less);
+ return;
+ }
+
+ // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling
+ // some elements around. Hopefully we'll choose a better pivot this time.
+ if !was_balanced {
+ break_patterns(v);
+ limit -= 1;
+ }
+
// Choose a pivot
let (pivot, _) = choose_pivot(v, is_less);
}
let (mid, _) = partition(v, pivot, is_less);
+ was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8;
// Split the slice into `left`, `pivot`, and `right`.
let (left, right) = v.split_at_mut(mid);
}
}
+/// Reorder the slice such that the element at `index` is at its final sorted position.
pub fn partition_at_index<T, F>(
v: &mut [T],
index: usize,
let pivot = &mut pivot[0];
(left, pivot, right)
}
+
+/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.
+///
+/// This is the integral subroutine of insertion sort.
+fn insert_head<T, F>(v: &mut [T], is_less: &mut F)
+where
+ F: FnMut(&T, &T) -> bool,
+{
+ if v.len() >= 2 && is_less(&v[1], &v[0]) {
+ // SAFETY: Copy tmp back even if panic, and ensure unique observation.
+ unsafe {
+ // There are three ways to implement insertion here:
+ //
+ // 1. Swap adjacent elements until the first one gets to its final destination.
+ // However, this way we copy data around more than is necessary. If elements are big
+ // structures (costly to copy), this method will be slow.
+ //
+ // 2. Iterate until the right place for the first element is found. Then shift the
+ // elements succeeding it to make room for it and finally place it into the
+ // remaining hole. This is a good method.
+ //
+ // 3. Copy the first element into a temporary variable. Iterate until the right place
+ // for it is found. As we go along, copy every traversed element into the slot
+ // preceding it. Finally, copy data from the temporary variable into the remaining
+ // hole. This method is very good. Benchmarks demonstrated slightly better
+ // performance than with the 2nd method.
+ //
+ // All methods were benchmarked, and the 3rd showed best results. So we chose that one.
+ let tmp = mem::ManuallyDrop::new(ptr::read(&v[0]));
+
+ // Intermediate state of the insertion process is always tracked by `hole`, which
+ // serves two purposes:
+ // 1. Protects integrity of `v` from panics in `is_less`.
+ // 2. Fills the remaining hole in `v` in the end.
+ //
+ // Panic safety:
+ //
+ // If `is_less` panics at any point during the process, `hole` will get dropped and
+ // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it
+ // initially held exactly once.
+ let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] };
+ ptr::copy_nonoverlapping(&v[1], &mut v[0], 1);
+
+ for i in 2..v.len() {
+ if !is_less(&v[i], &*tmp) {
+ break;
+ }
+ ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1);
+ hole.dest = &mut v[i];
+ }
+ // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`.
+ }
+ }
+
+ // When dropped, copies from `src` into `dest`.
+ struct InsertionHole<T> {
+ src: *const T,
+ dest: *mut T,
+ }
+
+ impl<T> Drop for InsertionHole<T> {
+ fn drop(&mut self) {
+ // SAFETY: The caller must ensure that src and dest are correctly set.
+ unsafe {
+ ptr::copy_nonoverlapping(self.src, self.dest, 1);
+ }
+ }
+ }
+}
+
+/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and
+/// stores the result into `v[..]`.
+///
+/// # Safety
+///
+/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough
+/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type.
+unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F)
+where
+ F: FnMut(&T, &T) -> bool,
+{
+ let len = v.len();
+ let v = v.as_mut_ptr();
+
+ // SAFETY: mid and len must be in-bounds of v.
+ let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) };
+
+ // The merge process first copies the shorter run into `buf`. Then it traces the newly copied
+ // run and the longer run forwards (or backwards), comparing their next unconsumed elements and
+ // copying the lesser (or greater) one into `v`.
+ //
+ // As soon as the shorter run is fully consumed, the process is done. If the longer run gets
+ // consumed first, then we must copy whatever is left of the shorter run into the remaining
+ // hole in `v`.
+ //
+ // Intermediate state of the process is always tracked by `hole`, which serves two purposes:
+ // 1. Protects integrity of `v` from panics in `is_less`.
+ // 2. Fills the remaining hole in `v` if the longer run gets consumed first.
+ //
+ // Panic safety:
+ //
+ // If `is_less` panics at any point during the process, `hole` will get dropped and fill the
+ // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every
+ // object it initially held exactly once.
+ let mut hole;
+
+ if mid <= len - mid {
+ // The left run is shorter.
+
+ // SAFETY: buf must have enough capacity for `v[..mid]`.
+ unsafe {
+ ptr::copy_nonoverlapping(v, buf, mid);
+ hole = MergeHole { start: buf, end: buf.add(mid), dest: v };
+ }
+
+ // Initially, these pointers point to the beginnings of their arrays.
+ let left = &mut hole.start;
+ let mut right = v_mid;
+ let out = &mut hole.dest;
+
+ while *left < hole.end && right < v_end {
+ // Consume the lesser side.
+ // If equal, prefer the left run to maintain stability.
+
+ // SAFETY: left and right must be valid and part of v same for out.
+ unsafe {
+ let to_copy = if is_less(&*right, &**left) {
+ get_and_increment(&mut right)
+ } else {
+ get_and_increment(left)
+ };
+ ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1);
+ }
+ }
+ } else {
+ // The right run is shorter.
+
+ // SAFETY: buf must have enough capacity for `v[mid..]`.
+ unsafe {
+ ptr::copy_nonoverlapping(v_mid, buf, len - mid);
+ hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid };
+ }
+
+ // Initially, these pointers point past the ends of their arrays.
+ let left = &mut hole.dest;
+ let right = &mut hole.end;
+ let mut out = v_end;
+
+ while v < *left && buf < *right {
+ // Consume the greater side.
+ // If equal, prefer the right run to maintain stability.
+
+ // SAFETY: left and right must be valid and part of v same for out.
+ unsafe {
+ let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) {
+ decrement_and_get(left)
+ } else {
+ decrement_and_get(right)
+ };
+ ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1);
+ }
+ }
+ }
+ // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of
+ // it will now be copied into the hole in `v`.
+
+ unsafe fn get_and_increment<T>(ptr: &mut *mut T) -> *mut T {
+ let old = *ptr;
+
+ // SAFETY: ptr.add(1) must still be a valid pointer and part of `v`.
+ *ptr = unsafe { ptr.add(1) };
+ old
+ }
+
+ unsafe fn decrement_and_get<T>(ptr: &mut *mut T) -> *mut T {
+ // SAFETY: ptr.sub(1) must still be a valid pointer and part of `v`.
+ *ptr = unsafe { ptr.sub(1) };
+ *ptr
+ }
+
+ // When dropped, copies the range `start..end` into `dest..`.
+ struct MergeHole<T> {
+ start: *mut T,
+ end: *mut T,
+ dest: *mut T,
+ }
+
+ impl<T> Drop for MergeHole<T> {
+ fn drop(&mut self) {
+ // SAFETY: `T` is not a zero-sized type, and these are pointers into a slice's elements.
+ unsafe {
+ let len = self.end.sub_ptr(self.start);
+ ptr::copy_nonoverlapping(self.start, self.dest, len);
+ }
+ }
+ }
+}
+
+/// This merge sort borrows some (but not all) ideas from TimSort, which used to be described in
+/// detail [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt). However Python
+/// has switched to a Powersort based implementation.
+///
+/// The algorithm identifies strictly descending and non-descending subsequences, which are called
+/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed
+/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are
+/// satisfied:
+///
+/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len`
+/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len`
+///
+/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case.
+pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>(
+ v: &mut [T],
+ is_less: &mut CmpF,
+ elem_alloc_fn: ElemAllocF,
+ elem_dealloc_fn: ElemDeallocF,
+ run_alloc_fn: RunAllocF,
+ run_dealloc_fn: RunDeallocF,
+) where
+ CmpF: FnMut(&T, &T) -> bool,
+ ElemAllocF: Fn(usize) -> *mut T,
+ ElemDeallocF: Fn(*mut T, usize),
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+{
+ // Slices of up to this length get sorted using insertion sort.
+ const MAX_INSERTION: usize = 20;
+ // Very short runs are extended using insertion sort to span at least this many elements.
+ const MIN_RUN: usize = 10;
+
+ // The caller should have already checked that.
+ debug_assert!(!T::IS_ZST);
+
+ let len = v.len();
+
+ // Short arrays get sorted in-place via insertion sort to avoid allocations.
+ if len <= MAX_INSERTION {
+ if len >= 2 {
+ for i in (0..len - 1).rev() {
+ insert_head(&mut v[i..], is_less);
+ }
+ }
+ return;
+ }
+
+ // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it
+ // shallow copies of the contents of `v` without risking the dtors running on copies if
+ // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run,
+ // which will always have length at most `len / 2`.
+ let buf = BufGuard::new(len / 2, elem_alloc_fn, elem_dealloc_fn);
+ let buf_ptr = buf.buf_ptr;
+
+ let mut runs = RunVec::new(run_alloc_fn, run_dealloc_fn);
+
+ // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a
+ // strange decision, but consider the fact that merges more often go in the opposite direction
+ // (forwards). According to benchmarks, merging forwards is slightly faster than merging
+ // backwards. To conclude, identifying runs by traversing backwards improves performance.
+ let mut end = len;
+ while end > 0 {
+ // Find the next natural run, and reverse it if it's strictly descending.
+ let mut start = end - 1;
+ if start > 0 {
+ start -= 1;
+
+ // SAFETY: The v.get_unchecked must be fed with correct inbound indicies.
+ unsafe {
+ if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) {
+ while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) {
+ start -= 1;
+ }
+ v[start..end].reverse();
+ } else {
+ while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1))
+ {
+ start -= 1;
+ }
+ }
+ }
+ }
+
+ // Insert some more elements into the run if it's too short. Insertion sort is faster than
+ // merge sort on short sequences, so this significantly improves performance.
+ while start > 0 && end - start < MIN_RUN {
+ start -= 1;
+ insert_head(&mut v[start..end], is_less);
+ }
+
+ // Push this run onto the stack.
+ runs.push(TimSortRun { start, len: end - start });
+ end = start;
+
+ // Merge some pairs of adjacent runs to satisfy the invariants.
+ while let Some(r) = collapse(runs.as_slice()) {
+ let left = runs[r + 1];
+ let right = runs[r];
+ // SAFETY: `buf_ptr` must hold enough capacity for the shorter of the two sides, and
+ // neither side may be on length 0.
+ unsafe {
+ merge(&mut v[left.start..right.start + right.len], left.len, buf_ptr, is_less);
+ }
+ runs[r] = TimSortRun { start: left.start, len: left.len + right.len };
+ runs.remove(r + 1);
+ }
+ }
+
+ // Finally, exactly one run must remain in the stack.
+ debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len);
+
+ // Examines the stack of runs and identifies the next pair of runs to merge. More specifically,
+ // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the
+ // algorithm should continue building a new run instead, `None` is returned.
+ //
+ // TimSort is infamous for its buggy implementations, as described here:
+ // http://envisage-project.eu/timsort-specification-and-verification/
+ //
+ // The gist of the story is: we must enforce the invariants on the top four runs on the stack.
+ // Enforcing them on just top three is not sufficient to ensure that the invariants will still
+ // hold for *all* runs in the stack.
+ //
+ // This function correctly checks invariants for the top four runs. Additionally, if the top
+ // run starts at index 0, it will always demand a merge operation until the stack is fully
+ // collapsed, in order to complete the sort.
+ #[inline]
+ fn collapse(runs: &[TimSortRun]) -> Option<usize> {
+ let n = runs.len();
+ if n >= 2
+ && (runs[n - 1].start == 0
+ || runs[n - 2].len <= runs[n - 1].len
+ || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len)
+ || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len))
+ {
+ if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) }
+ } else {
+ None
+ }
+ }
+
+ // Extremely basic versions of Vec.
+ // Their use is super limited and by having the code here, it allows reuse between the sort
+ // implementations.
+ struct BufGuard<T, ElemDeallocF>
+ where
+ ElemDeallocF: Fn(*mut T, usize),
+ {
+ buf_ptr: *mut T,
+ capacity: usize,
+ elem_dealloc_fn: ElemDeallocF,
+ }
+
+ impl<T, ElemDeallocF> BufGuard<T, ElemDeallocF>
+ where
+ ElemDeallocF: Fn(*mut T, usize),
+ {
+ fn new<ElemAllocF>(
+ len: usize,
+ elem_alloc_fn: ElemAllocF,
+ elem_dealloc_fn: ElemDeallocF,
+ ) -> Self
+ where
+ ElemAllocF: Fn(usize) -> *mut T,
+ {
+ Self { buf_ptr: elem_alloc_fn(len), capacity: len, elem_dealloc_fn }
+ }
+ }
+
+ impl<T, ElemDeallocF> Drop for BufGuard<T, ElemDeallocF>
+ where
+ ElemDeallocF: Fn(*mut T, usize),
+ {
+ fn drop(&mut self) {
+ (self.elem_dealloc_fn)(self.buf_ptr, self.capacity);
+ }
+ }
+
+ struct RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ buf_ptr: *mut TimSortRun,
+ capacity: usize,
+ len: usize,
+ run_alloc_fn: RunAllocF,
+ run_dealloc_fn: RunDeallocF,
+ }
+
+ impl<RunAllocF, RunDeallocF> RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ fn new(run_alloc_fn: RunAllocF, run_dealloc_fn: RunDeallocF) -> Self {
+ // Most slices can be sorted with at most 16 runs in-flight.
+ const START_RUN_CAPACITY: usize = 16;
+
+ Self {
+ buf_ptr: run_alloc_fn(START_RUN_CAPACITY),
+ capacity: START_RUN_CAPACITY,
+ len: 0,
+ run_alloc_fn,
+ run_dealloc_fn,
+ }
+ }
+
+ fn push(&mut self, val: TimSortRun) {
+ if self.len == self.capacity {
+ let old_capacity = self.capacity;
+ let old_buf_ptr = self.buf_ptr;
+
+ self.capacity = self.capacity * 2;
+ self.buf_ptr = (self.run_alloc_fn)(self.capacity);
+
+ // SAFETY: buf_ptr new and old were correctly allocated and old_buf_ptr has
+ // old_capacity valid elements.
+ unsafe {
+ ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr, old_capacity);
+ }
+
+ (self.run_dealloc_fn)(old_buf_ptr, old_capacity);
+ }
+
+ // SAFETY: The invariant was just checked.
+ unsafe {
+ self.buf_ptr.add(self.len).write(val);
+ }
+ self.len += 1;
+ }
+
+ fn remove(&mut self, index: usize) {
+ if index >= self.len {
+ panic!("Index out of bounds");
+ }
+
+ // SAFETY: buf_ptr needs to be valid and len invariant upheld.
+ unsafe {
+ // the place we are taking from.
+ let ptr = self.buf_ptr.add(index);
+
+ // Shift everything down to fill in that spot.
+ ptr::copy(ptr.add(1), ptr, self.len - index - 1);
+ }
+ self.len -= 1;
+ }
+
+ fn as_slice(&self) -> &[TimSortRun] {
+ // SAFETY: Safe as long as buf_ptr is valid and len invariant was upheld.
+ unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr, self.len) }
+ }
+
+ fn len(&self) -> usize {
+ self.len
+ }
+ }
+
+ impl<RunAllocF, RunDeallocF> core::ops::Index<usize> for RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ type Output = TimSortRun;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ if index < self.len {
+ // SAFETY: buf_ptr and len invariant must be upheld.
+ unsafe {
+ return &*(self.buf_ptr.add(index));
+ }
+ }
+
+ panic!("Index out of bounds");
+ }
+ }
+
+ impl<RunAllocF, RunDeallocF> core::ops::IndexMut<usize> for RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ if index < self.len {
+ // SAFETY: buf_ptr and len invariant must be upheld.
+ unsafe {
+ return &mut *(self.buf_ptr.add(index));
+ }
+ }
+
+ panic!("Index out of bounds");
+ }
+ }
+
+ impl<RunAllocF, RunDeallocF> Drop for RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ fn drop(&mut self) {
+ // As long as TimSortRun is Copy we don't need to drop them individually but just the
+ // whole allocation.
+ (self.run_dealloc_fn)(self.buf_ptr, self.capacity);
+ }
+ }
+}
+
+/// Internal type used by merge_sort.
+#[derive(Clone, Copy, Debug)]
+pub struct TimSortRun {
+ len: usize,
+ start: usize,
+}
($_:ident, $($tt:tt)*) => { $($tt)* };
}
-#[cfg(target_has_atomic_load_store = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic_load_store))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic_load_store = "8"))]
macro_rules! atomic_int {
($cfg_cas:meta,
$cfg_align:meta,
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
fn strongest_failure_ordering(order: Ordering) -> Ordering {
match order {
Release => Relaxed,
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_swap<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_swap`.
/// Returns the previous value (like __sync_fetch_and_add).
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_add<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_add`.
/// Returns the previous value (like __sync_fetch_and_sub).
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_sub<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_sub`.
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_compare_exchange<T: Copy>(
dst: *mut T,
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_compare_exchange_weak<T: Copy>(
dst: *mut T,
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_and<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_and`
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_nand<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_nand`
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_or<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_or`
}
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_xor<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_xor`
/// returns the max value (signed comparison)
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_max<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_max`
/// returns the min value (signed comparison)
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_min<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_min`
/// returns the max value (unsigned comparison)
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_umax<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_umax`
/// returns the min value (unsigned comparison)
#[inline]
-#[cfg(target_has_atomic = "8")]
+#[cfg_attr(not(bootstrap), cfg(target_has_atomic))]
+#[cfg_attr(bootstrap, cfg(target_has_atomic = "8"))]
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
unsafe fn atomic_umin<T: Copy>(dst: *mut T, val: T, order: Ordering) -> T {
// SAFETY: the caller must uphold the safety contract for `atomic_umin`
/// Currently, `Context` only serves to provide access to a [`&Waker`](Waker)
/// which can be used to wake the current task.
#[stable(feature = "futures_api", since = "1.36.0")]
+#[cfg_attr(not(bootstrap), lang = "Context")]
pub struct Context<'a> {
waker: &'a Waker,
// Ensure we future-proof against variance changes by forcing
for input in inputs {
assert_eq!(input.parse(), Ok(x64));
assert_eq!(input.parse(), Ok(x32));
- let neg_input = &format!("-{input}");
+ let neg_input = format!("-{input}");
assert_eq!(neg_input.parse(), Ok(-x64));
assert_eq!(neg_input.parse(), Ok(-x32));
}
// optional:
//
// one or more similar inputs for which data[input] succeeds,
- // and the corresponding output as an array. This helps validate
+ // and the corresponding output as an array. This helps validate
// "critical points" where an input range straddles the boundary
// between valid and invalid.
// (such as the input `len..len`, which is just barely valid)
#[cfg(test)]
#[test]
fn test() {
- assert_eq!(&format!("{:.9}", spectral_norm(100)), "1.274219991");
+ assert_eq!(format!("{:.9}", spectral_norm(100)), "1.274219991");
}
fn main() {
///
/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]`
/// and `#[proc_macro_derive]` definitions.
+#[rustc_diagnostic_item = "TokenStream"]
#[stable(feature = "proc_macro_lib", since = "1.15.0")]
#[derive(Clone)]
pub struct TokenStream(Option<bridge::client::TokenStream>);
/// A line-column pair representing the start or end of a `Span`.
#[unstable(feature = "proc_macro_span", issue = "54725")]
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct LineColumn {
/// The 1-indexed line in the source file on which the span starts or ends (inclusive).
#[unstable(feature = "proc_macro_span", issue = "54725")]
}
/// Tests whether this file type represents a regular file.
- /// The result is mutually exclusive to the results of
+ /// The result is mutually exclusive to the results of
/// [`is_dir`] and [`is_symlink`]; only zero or one of these
/// tests may pass.
///
use rand::RngCore;
+#[cfg(target_os = "macos")]
+use crate::ffi::{c_char, c_int};
#[cfg(unix)]
use crate::os::unix::fs::symlink as symlink_dir;
#[cfg(unix)]
use crate::sys::fs::symlink_junction;
#[cfg(target_os = "macos")]
use crate::sys::weak::weak;
-#[cfg(target_os = "macos")]
-use libc::{c_char, c_int};
macro_rules! check {
($e:expr) => {
let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true });
assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..]));
- // The following seek will require two underlying seeks. The first will
- // succeed but the second will fail. This should still invalidate the
+ // The following seek will require two underlying seeks. The first will
+ // succeed but the second will fail. This should still invalidate the
// buffer.
assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err());
assert_eq!(reader.buffer().len(), 0);
static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
-static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE_MESSAGE), TAG_SIMPLE_MESSAGE);
-static_assert!(@usize_eq: (TAG_MASK & TAG_CUSTOM), TAG_CUSTOM);
-static_assert!(@usize_eq: (TAG_MASK & TAG_OS), TAG_OS);
-static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE), TAG_SIMPLE);
+static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE);
+static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM);
+static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS);
+static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE);
// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we
// offset a pointer by this value, and expect it to both be within the same
let io_error = io_error.downcast::<E>().unwrap_err();
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
- assert_eq!(SIMPLE_MESSAGE.message, &*format!("{io_error}"));
+ assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}"));
}
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
#![feature(allow_internal_unstable)]
-#![feature(box_syntax)]
#![feature(c_unwind)]
#![feature(cfg_target_thread_local)]
#![feature(concat_idents)]
#![feature(const_ip)]
#![feature(const_ipv4)]
#![feature(const_ipv6)]
-#![feature(const_socketaddr)]
#![feature(thread_local_internals)]
//
#![default_lib_allocator]
assert_eq!(Ipv4Addr::new(127, 127, 127, 127).to_string(), "127.127.127.127");
// Test padding
- assert_eq!(&format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 ");
- assert_eq!(&format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1");
+ assert_eq!(format!("{:16}", Ipv4Addr::new(1, 1, 1, 1)), "1.1.1.1 ");
+ assert_eq!(format!("{:>16}", Ipv4Addr::new(1, 1, 1, 1)), " 1.1.1.1");
}
#[test]
"1111:2222:3333:4444:5555:6666:7777:8888"
);
// padding
- assert_eq!(&format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 ");
- assert_eq!(&format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8");
+ assert_eq!(format!("{:20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), "1:2:3:4:5:6:7:8 ");
+ assert_eq!(format!("{:>20}", Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), " 1:2:3:4:5:6:7:8");
// reduce a single run of zeros
assert_eq!(
/// ```
#[stable(feature = "ip_addr", since = "1.7.0")]
#[must_use]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn new(ip: IpAddr, port: u16) -> SocketAddr {
match ip {
IpAddr::V4(a) => SocketAddr::V4(SocketAddrV4::new(a, port)),
/// ```
#[must_use]
#[stable(feature = "ip_addr", since = "1.7.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn ip(&self) -> IpAddr {
match *self {
SocketAddr::V4(ref a) => IpAddr::V4(*a.ip()),
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn port(&self) -> u16 {
match *self {
SocketAddr::V4(ref a) => a.port(),
/// ```
#[must_use]
#[stable(feature = "sockaddr_checker", since = "1.16.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn is_ipv4(&self) -> bool {
matches!(*self, SocketAddr::V4(_))
}
/// ```
#[must_use]
#[stable(feature = "sockaddr_checker", since = "1.16.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn is_ipv6(&self) -> bool {
matches!(*self, SocketAddr::V6(_))
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 {
SocketAddrV4 { ip, port }
}
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn ip(&self) -> &Ipv4Addr {
&self.ip
}
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn port(&self) -> u16 {
self.port
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 {
SocketAddrV6 { ip, port, flowinfo, scope_id }
}
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn ip(&self) -> &Ipv6Addr {
&self.ip
}
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn port(&self) -> u16 {
self.port
}
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn flowinfo(&self) -> u32 {
self.flowinfo
}
/// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
- #[rustc_const_unstable(feature = "const_socketaddr", issue = "82485")]
+ #[rustc_const_stable(feature = "const_socketaddr", since = "CURRENT_RUSTC_VERSION")]
pub const fn scope_id(&self) -> u32 {
self.scope_id
}
// Test padding.
assert_eq!(
- &format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
+ format!("{:16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
"1.1.1.1:53 "
);
assert_eq!(
- &format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
+ format!("{:>16}", SocketAddrV4::new(Ipv4Addr::new(1, 1, 1, 1), 53)),
" 1.1.1.1:53"
);
}
// Test padding.
assert_eq!(
- &format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
+ format!("{:22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
"[1:2:3:4:5:6:7:8]:9 "
);
assert_eq!(
- &format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
+ format!("{:>22}", SocketAddrV6::new(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8), 9, 0, 0)),
" [1:2:3:4:5:6:7:8]:9"
);
}
//! This module is supported on Unix platforms and WASI, which both use a
//! similar file descriptor system for referencing OS resources.
-#![stable(feature = "io_safety", since = "1.63.0")]
+#![stable(feature = "os_fd", since = "1.66.0")]
#![deny(unsafe_op_in_unsafe_fn)]
// `RawFd`, `AsRawFd`, etc.
mod tests;
// Export the types and traits for the public API.
-#[unstable(feature = "os_fd", issue = "98699")]
+#[stable(feature = "os_fd", since = "1.66.0")]
pub use owned::*;
-#[unstable(feature = "os_fd", issue = "98699")]
+#[stable(feature = "os_fd", since = "1.66.0")]
pub use raw::*;
// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
// will never be supported, as this is a bare metal framework with
- // no capabilities for multi-process execution. While F_DUPFD is also
+ // no capabilities for multi-process execution. While F_DUPFD is also
// not supported yet, it might be (currently it returns ENOSYS).
#[cfg(target_os = "espidf")]
let cmd = libc::F_DUPFD;
/// Ok(())
/// }
/// ```
- fn from_abstract_name<N>(name: &N) -> crate::io::Result<SocketAddr>
+ fn from_abstract_name<N>(name: N) -> crate::io::Result<SocketAddr>
where
N: AsRef<[u8]>;
//! OS-specific networking functionality.
+// See cfg macros in `library/std/src/os/mod.rs` for why these platforms must
+// be special-cased during rustdoc generation.
+#[cfg(not(all(
+ doc,
+ any(
+ all(target_arch = "wasm32", not(target_os = "wasi")),
+ all(target_vendor = "fortanix", target_env = "sgx")
+ )
+)))]
#[cfg(any(target_os = "linux", target_os = "android", doc))]
pub(super) mod linux_ext;
if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
}
- fn from_abstract_name<N>(name: &N) -> crate::io::Result<Self>
+ fn from_abstract_name<N>(name: N) -> crate::io::Result<Self>
where
N: AsRef<[u8]>,
{
// and after increase and decrease, but not necessarily during their execution.
//
// Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
- // records whether panic::always_abort() has been called. This can only be
+ // records whether panic::always_abort() has been called. This can only be
// set, never cleared.
// panic::always_abort() is usually called to prevent memory allocations done by
// the panic handling in the child created by `libc::fork`.
- // Memory allocations performed in a child created with `libc::fork` are undefined
+ // Memory allocations performed in a child created with `libc::fork` are undefined
// behavior in most operating systems.
// Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory
// allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is
// true if path *physically* has a root separator; for most Windows
// prefixes, it may have a "logical" root separator for the purposes of
- // normalization, e.g., \\server\share == \\server\share\.
+ // normalization, e.g., \\server\share == \\server\share\.
has_physical_root: bool,
// The iterator is double-ended, and these two states keep track of what has
/// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
///
+ /// If `path` is absolute, it replaces the current path.
+ ///
/// See [`PathBuf::push`] for more details on what it means to adjoin a path.
///
/// # Examples
/// use std::path::{Path, PathBuf};
///
/// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd"));
+ /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh"));
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
//! On Emscripten Rust panics are wrapped in C++ exceptions, so we just forward
//! to `__gxx_personality_v0` which is provided by Emscripten.
-use libc::c_int;
+use crate::ffi::c_int;
use unwind as uw;
// This is required by the compiler to exist (e.g., it's a lang item), but it's
//! and the last personality routine transfers control to the catch block.
use super::dwarf::eh::{self, EHAction, EHContext};
-use libc::{c_int, uintptr_t};
+use crate::ffi::c_int;
+use libc::uintptr_t;
use unwind as uw;
// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
) -> Result<(), SendTimeoutError<T>> {
let token = &mut Token::default();
loop {
- // Try sending a message several times.
- let backoff = Backoff::new();
- loop {
- if self.start_send(token) {
- let res = unsafe { self.write(token, msg) };
- return res.map_err(SendTimeoutError::Disconnected);
- }
-
- if backoff.is_completed() {
- break;
- } else {
- backoff.spin_light();
- }
+ // Try sending a message.
+ if self.start_send(token) {
+ let res = unsafe { self.write(token, msg) };
+ return res.map_err(SendTimeoutError::Disconnected);
}
if let Some(d) = deadline {
pub(crate) fn recv(&self, deadline: Option<Instant>) -> Result<T, RecvTimeoutError> {
let token = &mut Token::default();
loop {
+ // Try receiving a message.
if self.start_recv(token) {
let res = unsafe { self.read(token) };
return res.map_err(|_| RecvTimeoutError::Disconnected);
/// Backs off using lightweight spinning.
///
- /// This method should be used for:
- /// - Retrying an operation because another thread made progress. i.e. on CAS failure.
- /// - Waiting for an operation to complete by spinning optimistically for a few iterations
- /// before falling back to parking the thread (see `Backoff::is_completed`).
+ /// This method should be used for retrying an operation because another thread made
+ /// progress. i.e. on CAS failure.
#[inline]
pub fn spin_light(&self) {
let step = self.step.get().min(SPIN_LIMIT);
self.step.set(self.step.get() + 1);
}
-
- /// Returns `true` if quadratic backoff has completed and parking the thread is advised.
- #[inline]
- pub fn is_completed(&self) -> bool {
- self.step.get() > SPIN_LIMIT
- }
}
#[cfg(any(
target_arch = "x86",
target_arch = "arm",
+ target_arch = "m68k",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
p: Box<dyn FnOnce()>,
core_id: isize,
) -> io::Result<Thread> {
- let p = Box::into_raw(box p);
+ let p = Box::into_raw(Box::new(p));
let tid = abi::spawn2(
thread_start,
- p as usize,
+ p.expose_addr(),
abi::Priority::into(abi::NORMAL_PRIO),
stack,
core_id,
// The this solution works like the implementation of macOS and
// doesn't additional OS support
-use crate::cell::Cell;
-use crate::ptr;
+use crate::mem;
#[thread_local]
-static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
-
-type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- if DTORS.get().is_null() {
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v));
- }
-
- let list: &mut List = &mut *DTORS.get();
+ let list = &mut DTORS;
list.push((t, dtor));
}
// every thread call this function to run through all possible destructors
pub unsafe fn run_dtors() {
- let mut ptr = DTORS.replace(ptr::null_mut());
- while !ptr.is_null() {
- let list = Box::from_raw(ptr);
- for (ptr, dtor) in list.into_iter() {
+ let mut list = mem::take(&mut DTORS);
+ while !list.is_empty() {
+ for (ptr, dtor) in list {
dtor(ptr);
}
- ptr = DTORS.replace(ptr::null_mut());
+ list = mem::take(&mut DTORS);
}
}
// Terminate and delete the task
// Safety: `self.task` still represents a task we own (because
// this method or `join_inner` is called only once for
- // each `Thread`). The task indicated that it's safe to
+ // each `Thread`). The task indicated that it's safe to
// delete by entering the `FINISHED` state.
unsafe { terminate_and_delete_task(self.task) };
use super::{abi, itron::task};
use crate::cell::Cell;
-use crate::ptr;
+use crate::mem;
#[thread_local]
-static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+static REGISTERED: Cell<bool> = Cell::new(false);
-type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+#[thread_local]
+static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- if DTORS.get().is_null() {
+ if !REGISTERED.get() {
let tid = task::current_task_id_aborting();
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v));
-
// Register `tls_dtor` to make sure the TLS destructors are called
// for tasks created by other means than `std::thread`
unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) };
+ REGISTERED.set(true);
}
- let list: &mut List = unsafe { &mut *DTORS.get() };
+ let list = unsafe { &mut DTORS };
list.push((t, dtor));
}
pub unsafe fn run_dtors() {
- let ptr = DTORS.get();
- if !ptr.is_null() {
- // Swap the destructor list, call all registered destructors,
- // and repeat this until the list becomes permanently empty.
- while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new()))
- .filter(|list| !list.is_empty())
- {
- for (ptr, dtor) in list.into_iter() {
- unsafe { dtor(ptr) };
- }
+ let mut list = mem::take(unsafe { &mut DTORS });
+ while !list.is_empty() {
+ for (ptr, dtor) in list {
+ unsafe { dtor(ptr) };
}
- // Drop the destructor list
- unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) };
+ list = mem::take(unsafe { &mut DTORS });
}
}
) -> Option<io::Result<FileAttr>> {
use crate::sync::atomic::{AtomicU8, Ordering};
- // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
- // We store the availability in global to avoid unnecessary syscalls.
- // 0: Unknown
- // 1: Not available
- // 2: Available
- static STATX_STATE: AtomicU8 = AtomicU8::new(0);
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
+ // We check for it on first failure and remember availability to avoid having to
+ // do it again.
+ #[repr(u8)]
+ enum STATX_STATE{ Unknown = 0, Present, Unavailable }
+ static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8);
+
syscall! {
fn statx(
fd: c_int,
) -> c_int
}
- match STATX_STATE.load(Ordering::Relaxed) {
- 0 => {
- // It is a trick to call `statx` with null pointers to check if the syscall
- // is available. According to the manual, it is expected to fail with EFAULT.
- // We do this mainly for performance, since it is nearly hundreds times
- // faster than a normal successful call.
- let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
- .err()
- .and_then(|e| e.raw_os_error());
- // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
- // and returns `EPERM`. Listing all possible errors seems not a good idea.
- // See: https://github.com/rust-lang/rust/issues/65662
- if err != Some(libc::EFAULT) {
- STATX_STATE.store(1, Ordering::Relaxed);
- return None;
- }
- STATX_STATE.store(2, Ordering::Relaxed);
- }
- 1 => return None,
- _ => {}
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
+ return None;
}
let mut buf: libc::statx = mem::zeroed();
if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
- return Some(Err(err));
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
+ return Some(Err(err));
+ }
+
+ // Availability not checked yet.
+ //
+ // First try the cheap way.
+ if err.raw_os_error() == Some(libc::ENOSYS) {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
+
+ // Error other than `ENOSYS` is not a good enough indicator -- it is
+ // known that `EPERM` can be returned as a result of using seccomp to
+ // block the syscall.
+ // Availability is checked by performing a call which expects `EFAULT`
+ // if the syscall is usable.
+ // See: https://github.com/rust-lang/rust/issues/65662
+ // FIXME this can probably just do the call if `EPERM` was received, but
+ // previous iteration of the code checked it for all errors and for now
+ // this is retained.
+ // FIXME what about transient conditions like `ENOMEM`?
+ let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
+ .err()
+ .and_then(|e| e.raw_os_error());
+ if err2 == Some(libc::EFAULT) {
+ STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
+ return Some(Err(err));
+ } else {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
}
// We cannot fill `stat64` exhaustively because of private padding fields.
loop {
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
- // with unlimited or variable NAME_MAX. Many modern platforms guarantee
+ // with unlimited or variable NAME_MAX. Many modern platforms guarantee
// thread safety for readdir() as long an individual DIR* is not accessed
// concurrently, which is sufficient for Rust.
super::os::set_errno(0);
let entry_ptr = readdir64(self.inner.dirp.0);
if entry_ptr.is_null() {
- // We either encountered an error, or reached the end. Either way,
+ // We either encountered an error, or reached the end. Either way,
// the next call to next() should return None.
self.end_of_stream = true;
// - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
// - the writer fd was opened with O_APPEND (EBADF²)
- // and no bytes were written successfully yet. (All these errnos should
+ // and no bytes were written successfully yet. (All these errnos should
// not be returned if something was already written, but they happen in
// the wild, see #91152.)
//
// available on Fuchsia.
//
// It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many
- // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
+ // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
// while I don't know if these implementations are actually correct, I think they will do for
// now at least.
pub fn core_dumped(&self) -> bool {
pub fn into_raw(&self) -> c_int {
// We don't know what someone who calls into_raw() will do with this value, but it should
- // have the conventional Unix representation. Despite the fact that this is not
+ // have the conventional Unix representation. Despite the fact that this is not
// standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
- // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
+ // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
// Unix.)
//
// The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may
// different Unix variant.
//
// The other view would be to say that the caller on Fuchsia ought to know that `into_raw`
- // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
+ // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
// not possible here because we must return a c_int because that's what Unix (including
// SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
// necessarily fit.
//
// It seems to me that the right answer would be to provide std::os::fuchsia with its
// own ExitStatusExt, rather that trying to provide a not very convincing imitation of
- // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
+ // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
// fixing this up that is beyond the scope of my efforts now.
let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255.");
let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8;
}
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
- // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
// true for a platform pretending to be Unix, the tests (our doctests, and also
- // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
match NonZero_c_int::try_from(self.0) {
/* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
/* was zero, couldn't convert */ Err(_) => Ok(()),
libc::SIGWINCH => " (SIGWINCH)",
#[cfg(not(target_os = "haiku"))]
libc::SIGIO => " (SIGIO)",
+ #[cfg(target_os = "haiku")]
+ libc::SIGPOLL => " (SIGPOLL)",
libc::SIGSYS => " (SIGSYS)",
// For information on Linux signals, run `man 7 signal`
#[cfg(all(
t(0x00000, "exit status: 0");
t(0x0ff00, "exit status: 255");
- // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
+ // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
// https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956
// The purpose of this test is to test our string formatting, not our understanding of the wait
- // status magic numbers. So restrict these to Linux.
+ // status magic numbers. So restrict these to Linux.
if cfg!(target_os = "linux") {
t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)");
t(0x0ffff, "continued (WIFCONTINUED)");
}
// Testing "unrecognised wait status" is hard because the wait.h macros typically
- // assume that the value came from wait and isn't mad. With the glibc I have here
+ // assume that the value came from wait and isn't mad. With the glibc I have here
// this works:
if cfg!(all(target_os = "linux", target_env = "gnu")) {
t(0x000ff, "unrecognised wait status: 255 0xff");
}
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
- // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
// true for a platform pretending to be Unix, the tests (our doctests, and also
- // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
match NonZero_c_int::try_from(self.0) {
Ok(failure) => Err(ExitStatusError(failure)),
Err(_) => Ok(()),
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
- let p = Box::into_raw(box p);
+ let p = Box::into_raw(Box::new(p));
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
n => {
assert_eq!(n, libc::EINVAL);
// EINVAL means |stack_size| is either too small or not a
- // multiple of the system page size. Because it's definitely
+ // multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = os::page_size();
if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
// Linux doesn't allocate the whole stack right away, and
// the kernel has its own stack-guard mechanism to fault
- // when growing too close to an existing mapping. If we map
+ // when growing too close to an existing mapping. If we map
// our own guard, then the kernel starts enforcing a rather
// large gap above that, rendering much of the possible
- // stack space useless. See #43052.
+ // stack space useless. See #43052.
//
// Instead, we'll just note where we expect rlimit to start
// faulting, so our handler can report "stack overflow", and
None
} else if cfg!(target_os = "freebsd") {
// FreeBSD's stack autogrows, and optionally includes a guard page
- // at the bottom. If we try to remap the bottom of the stack
- // ourselves, FreeBSD's guard page moves upwards. So we'll just use
+ // at the bottom. If we try to remap the bottom of the stack
+ // ourselves, FreeBSD's guard page moves upwards. So we'll just use
// the builtin guard page.
let stackptr = get_stack_start_aligned()?;
let guardaddr = stackptr.addr();
// Technically the number of guard pages is tunable and controlled
// by the security.bsd.stack_guard_page sysctl, but there are
- // few reasons to change it from the default. The default value has
+ // few reasons to change it from the default. The default value has
// been 1 ever since FreeBSD 11.1 and 10.4.
const GUARD_PAGES: usize = 1;
let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
} else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
{
// glibc used to include the guard area within the stack, as noted in the BUGS
- // section of `man pthread_attr_getguardsize`. This has been corrected starting
+ // section of `man pthread_attr_getguardsize`. This has been corrected starting
// with glibc 2.27, and in some distro backports, so the guard is now placed at the
- // end (below) the stack. There's no easy way for us to know which we have at
+ // end (below) the stack. There's no easy way for us to know which we have at
// runtime, so we'll just match any fault in the range right above or below the
// stack base to call that fault a stack overflow.
Some(stackaddr - guardsize..stackaddr + guardsize)
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
-#[cfg(any(
- target_os = "linux",
- target_os = "fuchsia",
- target_os = "redox",
- target_os = "emscripten"
-))]
-#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
+#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox"))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::mem;
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
#[cfg(target_os = "macos")]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::cell::Cell;
+ use crate::mem;
use crate::ptr;
#[thread_local]
static REGISTERED: Cell<bool> = Cell::new(false);
+
+ #[thread_local]
+ static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+
if !REGISTERED.get() {
_tlv_atexit(run_dtors, ptr::null_mut());
REGISTERED.set(true);
}
- type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
-
- #[thread_local]
- static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
- if DTORS.get().is_null() {
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v));
- }
-
extern "C" {
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
}
- let list: &mut List = &mut *DTORS.get();
+ let list = &mut DTORS;
list.push((t, dtor));
unsafe extern "C" fn run_dtors(_: *mut u8) {
- let mut ptr = DTORS.replace(ptr::null_mut());
- while !ptr.is_null() {
- let list = Box::from_raw(ptr);
- for (ptr, dtor) in list.into_iter() {
+ let mut list = mem::take(&mut DTORS);
+ while !list.is_empty() {
+ for (ptr, dtor) in list {
dtor(ptr);
}
- ptr = DTORS.replace(ptr::null_mut());
+ list = mem::take(&mut DTORS);
}
}
}
-#[cfg(any(target_os = "vxworks", target_os = "horizon"))]
+#[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))]
+#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
register_dtor_fallback(t, dtor);
// Double quotes are used as a way of introducing literal semicolons
// (since c:\some;dir is a valid Windows path). Double quotes are not
// themselves permitted in path names, so there is no way to escape a
- // double quote. Quoted regions can appear in arbitrary locations, so
+ // double quote. Quoted regions can appear in arbitrary locations, so
//
// c:\foo;c:\som"e;di"r;c:\bar
//
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
- let p = Box::into_raw(box p);
+ let p = Box::into_raw(Box::new(p));
// FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least
- // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
+ // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
// just that below a certain threshold you can't do anything useful.
// That threshold is application and architecture-specific, however.
let ret = c::CreateThread(
fn keyed_event_handle() -> c::HANDLE {
const INVALID: c::HANDLE = ptr::invalid_mut(!0);
- static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID);
+ static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(INVALID);
match HANDLE.load(Relaxed) {
INVALID => {
let mut handle = c::INVALID_HANDLE_VALUE;
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::Duration;
-use libc::{c_int, c_void};
+use crate::ffi::{c_int, c_void};
cfg_if::cfg_if! {
if #[cfg(any(
target_os = "dragonfly", target_os = "freebsd",
target_os = "openbsd", target_os = "netbsd",
target_os = "solaris", target_os = "illumos"))] {
- use libc::c_uchar;
+ use crate::ffi::c_uchar;
type IpV4MultiCastType = c_uchar;
} else {
type IpV4MultiCastType = c_int;
}
#[cfg(not(target_os = "android"))]
-fn to_ipv6mr_interface(value: u32) -> libc::c_uint {
- value as libc::c_uint
+fn to_ipv6mr_interface(value: u32) -> crate::ffi::c_uint {
+ value as crate::ffi::c_uint
}
////////////////////////////////////////////////////////////////////////////////
static DTORS: StaticKey = StaticKey::new(Some(run_dtors));
type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
if DTORS.get().is_null() {
- let v: Box<List> = box Vec::new();
+ let v: Box<List> = Box::new(Vec::new());
DTORS.set(Box::into_raw(v) as *mut u8);
}
let list: &mut List = &mut *(DTORS.get() as *mut List);
let ptr = if ptr.is_null() {
// If the lookup returned null, we haven't initialized our own
// local copy, so do that now.
- let ptr: Box<Value<T>> = box Value { inner: LazyKeyInner::new(), key: self };
- let ptr = Box::into_raw(ptr);
+ let ptr = Box::into_raw(Box::new(Value { inner: LazyKeyInner::new(), key: self }));
// SAFETY: At this point we are sure there is no value inside
// ptr so setting it will not affect anyone else.
unsafe {
//! Temporal quantification.
//!
-//! # Examples:
+//! # Examples
//!
//! There are multiple ways to create a new [`Duration`]:
//!
println!("avx512dq: {:?}", is_x86_feature_detected!("avx512dq"));
println!("avx512er: {:?}", is_x86_feature_detected!("avx512er"));
println!("avx512f: {:?}", is_x86_feature_detected!("avx512f"));
- println!("avx512gfni: {:?}", is_x86_feature_detected!("avx512gfni"));
println!("avx512ifma: {:?}", is_x86_feature_detected!("avx512ifma"));
println!("avx512pf: {:?}", is_x86_feature_detected!("avx512pf"));
- println!("avx512vaes: {:?}", is_x86_feature_detected!("avx512vaes"));
println!("avx512vbmi2: {:?}", is_x86_feature_detected!("avx512vbmi2"));
println!("avx512vbmi: {:?}", is_x86_feature_detected!("avx512vbmi"));
println!("avx512vl: {:?}", is_x86_feature_detected!("avx512vl"));
println!("avx512vnni: {:?}", is_x86_feature_detected!("avx512vnni"));
println!("avx512vp2intersect: {:?}", is_x86_feature_detected!("avx512vp2intersect"));
- println!("avx512vpclmulqdq: {:?}", is_x86_feature_detected!("avx512vpclmulqdq"));
println!("avx512vpopcntdq: {:?}", is_x86_feature_detected!("avx512vpopcntdq"));
println!("avx: {:?}", is_x86_feature_detected!("avx"));
println!("bmi1: {:?}", is_x86_feature_detected!("bmi1"));
println!("f16c: {:?}", is_x86_feature_detected!("f16c"));
println!("fma: {:?}", is_x86_feature_detected!("fma"));
println!("fxsr: {:?}", is_x86_feature_detected!("fxsr"));
+ println!("gfni: {:?}", is_x86_feature_detected!("gfni"));
println!("lzcnt: {:?}", is_x86_feature_detected!("lzcnt"));
//println!("movbe: {:?}", is_x86_feature_detected!("movbe")); // movbe is unsupported as a target feature
println!("pclmulqdq: {:?}", is_x86_feature_detected!("pclmulqdq"));
println!("sse: {:?}", is_x86_feature_detected!("sse"));
println!("ssse3: {:?}", is_x86_feature_detected!("ssse3"));
println!("tbm: {:?}", is_x86_feature_detected!("tbm"));
+ println!("vaes: {:?}", is_x86_feature_detected!("vaes"));
+ println!("vpclmulqdq: {:?}", is_x86_feature_detected!("vpclmulqdq"));
println!("xsave: {:?}", is_x86_feature_detected!("xsave"));
println!("xsavec: {:?}", is_x86_feature_detected!("xsavec"));
println!("xsaveopt: {:?}", is_x86_feature_detected!("xsaveopt"));
-Subproject commit 790411f93c4b5eada3c23abb4c9a063fb0b24d99
+Subproject commit a0c30f3e3c75adcd6ee7efc94014ebcead61c507
// FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
fn is_nightly() -> bool {
// Whether this is a feature-staged build, i.e., on the beta or stable channel
- let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+ let disable_unstable_features =
+ option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
// Whether we should enable unstable features for bootstrapping
let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
pub metrics: MetricMap,
pub failures: Vec<(TestDesc, Vec<u8>)>,
pub not_failures: Vec<(TestDesc, Vec<u8>)>,
+ pub ignores: Vec<(TestDesc, Vec<u8>)>,
pub time_failures: Vec<(TestDesc, Vec<u8>)>,
pub options: Options,
}
metrics: MetricMap::new(),
failures: Vec::new(),
not_failures: Vec::new(),
+ ignores: Vec::new(),
time_failures: Vec::new(),
options: opts.options,
})
st.passed += 1;
st.not_failures.push((test, stdout));
}
- TestResult::TrIgnored => st.ignored += 1,
+ TestResult::TrIgnored => {
+ st.ignored += 1;
+ st.ignores.push((test, stdout));
+ }
TestResult::TrBench(bs) => {
st.metrics.insert_metric(
test.name.as_slice(),
self.write_plain("\n\n")?;
+ // Custom handling of cases where there is only 1 test to execute and that test was ignored.
+ // We want to show more detailed information(why was the test ignored) for investigation purposes.
+ if self.total_test_count == 1 && state.ignores.len() == 1 {
+ let test_desc = &state.ignores[0].0;
+ if let Some(im) = test_desc.ignore_message {
+ self.write_plain(format!("test: {}, ignore_message: {}\n\n", test_desc.name, im))?;
+ }
+ }
+
Ok(success)
}
}
} else {
if !opts.nocapture {
// If we encounter a non-unwinding panic, flush any captured output from the current test,
- // and stop capturing output to ensure that the non-unwinding panic message is visible.
+ // and stop capturing output to ensure that the non-unwinding panic message is visible.
// We also acquire the locks for both output streams to prevent output from other threads
// from interleaving with the panic message or appearing after it.
let builtin_panic_hook = panic::take_hook();
}
} else {
// Found nothing in TERMINFO_DIRS, use the default paths:
- // According to /etc/terminfo/README, after looking at
+ // According to /etc/terminfo/README, after looking at
// ~/.terminfo, ncurses will search /etc/terminfo, then
// /lib/terminfo, and eventually /usr/share/terminfo.
// On Haiku the database can be found at /boot/system/data/terminfo
failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
options: Options::new(),
not_failures: Vec::new(),
+ ignores: Vec::new(),
time_failures: Vec::new(),
};
let mut build_lock;
let _build_lock_guard;
if cfg!(any(unix, windows)) {
- build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(config.out.join("lock"))));
+ let path = config.out.join("lock");
+ build_lock = fd_lock::RwLock::new(t!(std::fs::File::create(&path)));
_build_lock_guard = match build_lock.try_write() {
Ok(lock) => lock,
err => {
- println!("warning: build directory locked, waiting for lock");
drop(err);
+ if let Some(pid) = get_lock_owner(&path) {
+ println!("warning: build directory locked by process {pid}, waiting for lock");
+ } else {
+ println!("warning: build directory locked, waiting for lock");
+ }
t!(build_lock.write())
}
};
Some(msg)
}
+
+/// Get the PID of the process which took the write lock by
+/// parsing `/proc/locks`.
+#[cfg(target_os = "linux")]
+fn get_lock_owner(f: &std::path::Path) -> Option<u64> {
+ use std::fs::File;
+ use std::io::{BufRead, BufReader};
+ use std::os::unix::fs::MetadataExt;
+
+ let lock_inode = std::fs::metadata(f).ok()?.ino();
+ let lockfile = File::open("/proc/locks").ok()?;
+ BufReader::new(lockfile).lines().find_map(|line| {
+ // pid--vvvvvv vvvvvvv--- inode
+ // 21: FLOCK ADVISORY WRITE 359238 08:02:3719774 0 EOF
+ let line = line.ok()?;
+ let parts = line.split_whitespace().collect::<Vec<_>>();
+ let (pid, inode) = (parts[4].parse::<u64>().ok()?, &parts[5]);
+ let inode = inode.rsplit_once(':')?.1.parse::<u64>().ok()?;
+ if inode == lock_inode { Some(pid) } else { None }
+ })
+}
+
+#[cfg(not(target_os = "linux"))]
+fn get_lock_owner(_: &std::path::Path) -> Option<u64> {
+ // FIXME: Implement on other OS's
+ None
+}
def build_bootstrap(self, color):
"""Build bootstrap"""
- print("Building rustbuild")
+ print("Building bootstrap")
build_dir = os.path.join(self.build_dir, "bootstrap")
if self.clean and os.path.exists(build_dir):
shutil.rmtree(build_dir)
Some(PathBuf::from("ar"))
} else if target.contains("vxworks") {
Some(PathBuf::from("wr-ar"))
+ } else if target.contains("android") {
+ Some(cc.parent().unwrap().join(PathBuf::from("llvm-ar")))
} else {
let parent = cc.parent().unwrap();
let file = cc.file_name().unwrap().to_str().unwrap();
}
pub(crate) fn ndk_compiler(compiler: Language, triple: &str, ndk: &Path) -> PathBuf {
- let triple_translated = triple
- .replace("armv7neon", "arm")
- .replace("armv7", "arm")
- .replace("thumbv7neon", "arm")
- .replace("thumbv7", "arm");
- let compiler = format!("{}-{}", triple_translated, compiler.clang());
+ let mut triple_iter = triple.split("-");
+ let triple_translated = if let Some(arch) = triple_iter.next() {
+ let arch_new = match arch {
+ "arm" | "armv7" | "armv7neon" | "thumbv7" | "thumbv7neon" => "armv7a",
+ other => other,
+ };
+ std::iter::once(arch_new).chain(triple_iter).collect::<Vec<&str>>().join("-")
+ } else {
+ triple.to_string()
+ };
+
+ // API 19 is the earliest API level supported by NDK r25b but AArch64 and x86_64 support
+ // begins at API level 21.
+ let api_level =
+ if triple.contains("aarch64") || triple.contains("x86_64") { "21" } else { "19" };
+ let compiler = format!("{}{}-{}", triple_translated, api_level, compiler.clang());
ndk.join("bin").join(compiler)
}
config.changelog_seen = toml.changelog_seen;
let build = toml.build.unwrap_or_default();
+ if let Some(file_build) = build.build {
+ config.build = TargetSelection::from_user(&file_build);
+ };
set(&mut config.out, flags.build_dir.or_else(|| build.build_dir.map(PathBuf::from)));
// NOTE: Bootstrap spawns various commands with different working directories.
assert_eq!(parse_llvm(""), if_available);
assert_eq!(parse_llvm("rust.channel = \"dev\""), if_available);
assert!(!parse_llvm("rust.channel = \"stable\""));
+ assert!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""));
+ assert!(parse_llvm(
+ "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-available\""
+ ));
+ assert!(!parse_llvm(
+ "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-available\""
+ ));
}
// FIXME: add test for detecting `src` and `out`
t!(fs::create_dir_all(image.join("bin")));
builder.cp_r(&src.join("bin"), &image.join("bin"));
- builder.install(&builder.rustdoc(compiler), &image.join("bin"), 0o755);
+ if builder
+ .config
+ .tools
+ .as_ref()
+ .map_or(true, |tools| tools.iter().any(|tool| tool == "rustdoc"))
+ {
+ let rustdoc = builder.rustdoc(compiler);
+ builder.install(&rustdoc, &image.join("bin"), 0o755);
+ }
- let ra_proc_macro_srv = builder
- .ensure(tool::RustAnalyzerProcMacroSrv {
+ if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
+ tool::RustAnalyzerProcMacroSrv {
compiler: builder.compiler_for(
compiler.stage,
builder.config.build,
compiler.host,
),
target: compiler.host,
- })
- .expect("rust-analyzer-proc-macro-server always builds");
- builder.install(&ra_proc_macro_srv, &image.join("libexec"), 0o755);
+ },
+ builder.kind,
+ ) {
+ builder.install(&ra_proc_macro_srv, &image.join("libexec"), 0o755);
+ }
let libdir_relative = builder.libdir_relative(compiler);
"Cargo.toml",
"Cargo.lock",
];
- let src_dirs = ["src", "compiler", "library"];
+ let src_dirs = ["src", "compiler", "library", "tests"];
copy_src_dirs(builder, &builder.src, &src_dirs, &[], &plain_dst_src);
let compiler = self.compiler;
let target = self.target;
- if target.contains("riscv64") {
- // riscv64 currently has an LLVM bug that makes rust-analyzer unable
- // to build. See #74813 for details.
- return None;
- }
-
let rust_analyzer = builder
.ensure(tool::RustAnalyzer { compiler, target })
.expect("rust-analyzer always builds");
return Vec::new();
}
+ if !stamp.exists() {
+ eprintln!(
+ "Error: Unable to find the stamp file {}, did you try to keep a nonexistent build stage?",
+ stamp.display()
+ );
+ crate::detail_exit(1);
+ }
+
let mut paths = Vec::new();
let contents = t!(fs::read(stamp), &stamp);
// This is the method we use for extracting paths from the stamp file passed to us. See
// https://doc.rust-lang.org/rustc/platform-support.html#tier-1
let supported_platforms = [
// tier 1
- "aarch64-unknown-linux-gnu",
- "i686-pc-windows-gnu",
- "i686-pc-windows-msvc",
- "i686-unknown-linux-gnu",
- "x86_64-unknown-linux-gnu",
- "x86_64-apple-darwin",
- "x86_64-pc-windows-gnu",
- "x86_64-pc-windows-msvc",
+ ("aarch64-unknown-linux-gnu", false),
+ ("i686-pc-windows-gnu", false),
+ ("i686-pc-windows-msvc", false),
+ ("i686-unknown-linux-gnu", false),
+ ("x86_64-unknown-linux-gnu", true),
+ ("x86_64-apple-darwin", true),
+ ("x86_64-pc-windows-gnu", true),
+ ("x86_64-pc-windows-msvc", true),
// tier 2 with host tools
- "aarch64-apple-darwin",
- "aarch64-pc-windows-msvc",
- "aarch64-unknown-linux-musl",
- "arm-unknown-linux-gnueabi",
- "arm-unknown-linux-gnueabihf",
- "armv7-unknown-linux-gnueabihf",
- "mips-unknown-linux-gnu",
- "mips64-unknown-linux-gnuabi64",
- "mips64el-unknown-linux-gnuabi64",
- "mipsel-unknown-linux-gnu",
- "powerpc-unknown-linux-gnu",
- "powerpc64-unknown-linux-gnu",
- "powerpc64le-unknown-linux-gnu",
- "riscv64gc-unknown-linux-gnu",
- "s390x-unknown-linux-gnu",
- "x86_64-unknown-freebsd",
- "x86_64-unknown-illumos",
- "x86_64-unknown-linux-musl",
- "x86_64-unknown-netbsd",
+ ("aarch64-apple-darwin", false),
+ ("aarch64-pc-windows-msvc", false),
+ ("aarch64-unknown-linux-musl", false),
+ ("arm-unknown-linux-gnueabi", false),
+ ("arm-unknown-linux-gnueabihf", false),
+ ("armv7-unknown-linux-gnueabihf", false),
+ ("mips-unknown-linux-gnu", false),
+ ("mips64-unknown-linux-gnuabi64", false),
+ ("mips64el-unknown-linux-gnuabi64", false),
+ ("mipsel-unknown-linux-gnu", false),
+ ("powerpc-unknown-linux-gnu", false),
+ ("powerpc64-unknown-linux-gnu", false),
+ ("powerpc64le-unknown-linux-gnu", false),
+ ("riscv64gc-unknown-linux-gnu", false),
+ ("s390x-unknown-linux-gnu", false),
+ ("x86_64-unknown-freebsd", false),
+ ("x86_64-unknown-illumos", false),
+ ("x86_64-unknown-linux-musl", false),
+ ("x86_64-unknown-netbsd", false),
];
- if !supported_platforms.contains(&&*config.build.triple) {
- return false;
- }
- let triple = &*config.build.triple;
- if (triple == "aarch64-unknown-linux-gnu" || triple.contains("i686")) && asserts {
- // No alt builder for aarch64-unknown-linux-gnu today.
- return false;
+ if !supported_platforms.contains(&(&*config.build.triple, asserts)) {
+ if asserts == true || !supported_platforms.contains(&(&*config.build.triple, true)) {
+ return false;
+ }
}
if CiEnv::is_ci() {
"x86_64-unknown-linux-musl" => {
common_libs("linux", "x86_64", &["asan", "lsan", "msan", "tsan"])
}
+ "s390x-unknown-linux-gnu" => {
+ common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
+ }
+ "s390x-unknown-linux-musl" => {
+ common_libs("linux", "s390x", &["asan", "lsan", "msan", "tsan"])
+ }
_ => Vec::new(),
}
}
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+ let builder = run.builder;
// Allow building `rust-analyzer-proc-macro-srv` both as part of the `rust-analyzer` and as a stand-alone tool.
run.path("src/tools/rust-analyzer")
.path("src/tools/rust-analyzer/crates/proc-macro-srv-cli")
+ .default_condition(builder.config.tools.as_ref().map_or(true, |tools| {
+ tools
+ .iter()
+ .any(|tool| tool == "rust-analyzer" || tool == "rust-analyzer-proc-macro-srv")
+ }))
}
fn make_run(run: RunConfig<'_>) {
-FROM ubuntu:22.04
+FROM ubuntu:22.10
ARG DEBIAN_FRONTEND=noninteractive
COPY scripts/android-base-apt-get.sh /scripts/
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
- download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm 14
+ download_ndk android-ndk-r25b-linux.zip
RUN dpkg --add-architecture i386 && \
apt-get update && \
ENV TARGETS=arm-linux-androideabi
-ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14
+ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/
ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS
-FROM ubuntu:22.04
+FROM ubuntu:22.10
COPY scripts/android-base-apt-get.sh /scripts/
RUN sh /scripts/android-base-apt-get.sh
# ndk
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
- download_ndk android-ndk-r15c-linux-x86_64.zip && \
- make_standalone_toolchain arm 14 && \
- make_standalone_toolchain x86 14 && \
- make_standalone_toolchain arm 21 && \
- make_standalone_toolchain x86 21 && \
- make_standalone_toolchain arm64 21 && \
- make_standalone_toolchain x86_64 21 && \
- remove_ndk
+ download_ndk android-ndk-r25b-linux.zip
# env
ENV TARGETS=arm-linux-androideabi
ENV RUST_CONFIGURE_ARGS \
--enable-extended \
--enable-profiler \
- --arm-linux-androideabi-ndk=/android/ndk/arm-14 \
- --armv7-linux-androideabi-ndk=/android/ndk/arm-14 \
- --thumbv7neon-linux-androideabi-ndk=/android/ndk/arm-14 \
- --i686-linux-android-ndk=/android/ndk/x86-14 \
- --aarch64-linux-android-ndk=/android/ndk/arm64-21 \
- --x86_64-linux-android-ndk=/android/ndk/x86_64-21 \
+ --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --armv7-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --thumbv7neon-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --i686-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --aarch64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --x86_64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
--disable-docs
ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
ENV HOSTS=s390x-unknown-linux-gnu
-ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-profiler --disable-docs
+ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-sanitizers --enable-profiler --disable-docs
ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
git clone https://github.com/WebAssembly/wasi-libc
cd wasi-libc
-git reset --hard 8b7148f69ae241a2749b3defe4606da8143b72e0
+git reset --hard 4362b1885fd369e042a7c0ecd8df3b6cd47fb4e8
make -j$(nproc) \
CC="$bin/clang" \
NM="$bin/llvm-nm" \
-FROM ubuntu:18.04
-# FIXME: when bumping the version, remove the Python 3.6-specific changes in
-# the reuse-requirements.in file, regenerate reuse-requirements.txt and remove
-# this comment.
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
-FROM ubuntu:18.04
-# FIXME: when bumping the version, remove the Python 3.6-specific changes in
-# the reuse-requirements.in file, regenerate reuse-requirements.txt and remove
-# this comment.
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
#
reuse
-
-# Some packages dropped support for Python 3.6, which is the version used in
-# this builder (due to Ubuntu 18.04). This should be removed once we bump the
-# Ubuntu version of the builder.
-jinja2 < 3.1
-markupsafe < 2.1
-requests < 2.28
-setuptools < 59.7
#
-# This file is autogenerated by pip-compile with python 3.10
-# To update, run:
+# This file is autogenerated by pip-compile with Python 3.10
+# by the following command:
#
# pip-compile --allow-unsafe --generate-hashes reuse-requirements.in
#
--hash=sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061 \
--hash=sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4
# via reuse
-boolean-py==3.8 \
- --hash=sha256:cc24e20f985d60cd4a3a5a1c0956dd12611159d32a75081dabd0c9ab981acaa4 \
- --hash=sha256:d75da0fd0354425fa64f6bbc6cec6ae1485d0eec3447b73187ff8cbf9b572e26
+boolean-py==4.0 \
+ --hash=sha256:17b9a181630e43dde1851d42bef546d616d5d9b4480357514597e78b203d06e4 \
+ --hash=sha256:2876f2051d7d6394a531d82dc6eb407faa0b01a0a0b3083817ccd7323b8d96bd
# via
# license-expression
# reuse
-certifi==2022.6.15 \
- --hash=sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d \
- --hash=sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412
- # via requests
-chardet==5.0.0 \
- --hash=sha256:0368df2bfd78b5fc20572bb4e9bb7fb53e2c094f60ae9993339e8671d0afb8aa \
- --hash=sha256:d3e64f022d254183001eccc5db4040520c0f23b1a3f33d6413e099eb7f126557
+chardet==5.1.0 \
+ --hash=sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5 \
+ --hash=sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9
# via
# binaryornot
# python-debian
-charset-normalizer==2.0.12 \
- --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \
- --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df
- # via requests
-idna==3.3 \
- --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \
- --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d
- # via requests
-jinja2==3.0.3 \
- --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \
- --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7
- # via
- # -r reuse-requirements.in
- # reuse
-license-expression==21.6.14 \
- --hash=sha256:324246eed8e138b4139fefdc0e9dc4161d5075e3929e56983966d37298dca30e \
- --hash=sha256:9de87a427c9a449eee7913472fb9ed03b63036295547369fdbf95f76a8b924b2
- # via
- # -r reuse-requirements.in
- # reuse
-markupsafe==2.0.1 \
- --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \
- --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \
- --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \
- --hash=sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194 \
- --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \
- --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \
- --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \
- --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \
- --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \
- --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \
- --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \
- --hash=sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a \
- --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \
- --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \
- --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \
- --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \
- --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \
- --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \
- --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \
- --hash=sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047 \
- --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \
- --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \
- --hash=sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b \
- --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \
- --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \
- --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \
- --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \
- --hash=sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1 \
- --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \
- --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \
- --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \
- --hash=sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee \
- --hash=sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f \
- --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \
- --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \
- --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \
- --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \
- --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \
- --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \
- --hash=sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86 \
- --hash=sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6 \
- --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \
- --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \
- --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \
- --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \
- --hash=sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e \
- --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \
- --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \
- --hash=sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f \
- --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \
- --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \
- --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \
- --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \
- --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \
- --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \
- --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \
- --hash=sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a \
- --hash=sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207 \
- --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \
- --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \
- --hash=sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd \
- --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \
- --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \
- --hash=sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9 \
- --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \
- --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \
- --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \
- --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \
- --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872
- # via
- # -r reuse-requirements.in
- # jinja2
-python-debian==0.1.44 \
- --hash=sha256:11bd6f01c46da57982bdd66dd595e2d240feb32a85de3fd37c452102fd0337ab \
- --hash=sha256:65592fe3b64f6c6c93d94e2d2599db5e0c22831d3bcff07cb7b96d3840b1333e
+jinja2==3.1.2 \
+ --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \
+ --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61
# via reuse
-requests==2.26.0 \
- --hash=sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24 \
- --hash=sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7
- # via
- # -r reuse-requirements.in
- # reuse
-reuse==1.0.0 \
- --hash=sha256:db3022be2d87f69c8f508b928023de3026f454ce17d01e22f770f7147ac1e8d4 \
- --hash=sha256:e2605e796311c424465d741ea2a1e1ad03bbb90b921d74750119c331ca5af46e
+license-expression==30.0.0 \
+ --hash=sha256:ad638292aa8493f84354909b517922cb823582c2ce2c4d880e42544a86bea8dd \
+ --hash=sha256:e95325110110eb2b7539ee7773b97a0724d5371ec563cc718c8cac0e38cc40cc
+ # via reuse
+markupsafe==2.1.1 \
+ --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \
+ --hash=sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88 \
+ --hash=sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5 \
+ --hash=sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7 \
+ --hash=sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a \
+ --hash=sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603 \
+ --hash=sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1 \
+ --hash=sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135 \
+ --hash=sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247 \
+ --hash=sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6 \
+ --hash=sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601 \
+ --hash=sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77 \
+ --hash=sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02 \
+ --hash=sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e \
+ --hash=sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63 \
+ --hash=sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f \
+ --hash=sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980 \
+ --hash=sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b \
+ --hash=sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812 \
+ --hash=sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff \
+ --hash=sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96 \
+ --hash=sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1 \
+ --hash=sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925 \
+ --hash=sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a \
+ --hash=sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6 \
+ --hash=sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e \
+ --hash=sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f \
+ --hash=sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4 \
+ --hash=sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f \
+ --hash=sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3 \
+ --hash=sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c \
+ --hash=sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a \
+ --hash=sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417 \
+ --hash=sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a \
+ --hash=sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a \
+ --hash=sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37 \
+ --hash=sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452 \
+ --hash=sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933 \
+ --hash=sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a \
+ --hash=sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7
+ # via jinja2
+python-debian==0.1.49 \
+ --hash=sha256:880f3bc52e31599f2a9b432bd7691844286825087fccdcf2f6ffd5cd79a26f9f \
+ --hash=sha256:8cf677a30dbcb4be7a99536c17e11308a827a4d22028dc59a67f6c6dd3f0f58c
+ # via reuse
+reuse==1.1.0 \
+ --hash=sha256:7a054f6e372ad02d0b1b07368030fc38746b50ed45f5422a81994e7a88b52f1f \
+ --hash=sha256:b0f3fb9091ff513af04b555d14a4c529ab05f6a575ab192dd9b68244f1e0721d
# via -r reuse-requirements.in
-urllib3==1.26.10 \
- --hash=sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec \
- --hash=sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6
- # via requests
-
-# The following packages are considered to be unsafe in a requirements file:
-setuptools==59.6.0 \
- --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \
- --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e
- # via
- # -r reuse-requirements.in
- # reuse
+setuptools==66.0.0 \
+ --hash=sha256:a78d01d1e2c175c474884671dde039962c9d74c7223db7369771fcf6e29ceeab \
+ --hash=sha256:bd6eb2d6722568de6d14b87c44a96fac54b2a45ff5e940e639979a3d1792adb6
+ # via reuse
cd rust-toolstate
python3 "../../src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" \
"$(git log --format=%s -n1 HEAD)" "" ""
-# Only check maintainers if this build is supposed to publish toolstate.
-# Builds that are not supposed to publish don't have the access token.
-if [ -n "${TOOLSTATE_PUBLISH+is_set}" ]; then
- TOOLSTATE_VALIDATE_MAINTAINERS_REPO=rust-lang/rust python3 \
- "../../src/tools/publish_toolstate.py"
-fi
cd ..
rm -rf rust-toolstate
--- /dev/null
+FROM ubuntu:22.04
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+# NOTE: intentionally installs both python2 and python3 so we can test support for both.
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ g++ \
+ gcc-multilib \
+ make \
+ ninja-build \
+ file \
+ curl \
+ ca-certificates \
+ python2.7 \
+ python3 \
+ git \
+ cmake \
+ sudo \
+ gdb \
+ llvm-14-tools \
+ llvm-14-dev \
+ libedit-dev \
+ libssl-dev \
+ pkg-config \
+ zlib1g-dev \
+ xz-utils \
+ nodejs \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install powershell (universal package) so we can test x.ps1 on Linux
+RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \
+ dpkg -i powershell.deb && \
+ rm -f powershell.deb
+
+COPY scripts/sccache.sh /scripts/
+RUN sh /scripts/sccache.sh
+
+# We are disabling CI LLVM since this builder is intentionally using a host
+# LLVM, rather than the typical src/llvm-project LLVM.
+ENV NO_DOWNLOAD_CI_LLVM 1
+
+# Using llvm-link-shared due to libffi issues -- see #34486
+ENV RUST_CONFIGURE_ARGS \
+ --build=x86_64-unknown-linux-gnu \
+ --llvm-root=/usr/lib/llvm-14 \
+ --enable-llvm-link-shared \
+ --set rust.thin-lto-import-instr-limit=10
+
+# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
+ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \
+ # Run the `mir-opt` tests again but this time for a 32-bit target.
+ # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
+ # both 32-bit and 64-bit outputs updated by the PR author, before
+ # the PR is approved and tested for merging.
+ # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
+ # despite having different output on 32-bit vs 64-bit targets.
+ ../x --stage 2 test tests/mir-opt \
+ --host='' --target=i686-unknown-linux-gnu && \
+ # Run the UI test suite again, but in `--pass=check` mode
+ #
+ # This is intended to make sure that both `--pass=check` continues to
+ # work.
+ #
+ ../x.ps1 --stage 2 test tests/ui --pass=check \
+ --host='' --target=i686-unknown-linux-gnu && \
+ # Run tidy at the very end, after all the other tests.
+ python2.7 ../x.py --stage 2 test src/tools/tidy
--- /dev/null
+FROM ubuntu:22.10
+
+ARG DEBIAN_FRONTEND=noninteractive
+
+# NOTE: intentionally installs both python2 and python3 so we can test support for both.
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ g++ \
+ gcc-multilib \
+ make \
+ ninja-build \
+ file \
+ curl \
+ ca-certificates \
+ python2.7 \
+ python3 \
+ git \
+ cmake \
+ sudo \
+ gdb \
+ llvm-15-tools \
+ llvm-15-dev \
+ libedit-dev \
+ libssl-dev \
+ pkg-config \
+ zlib1g-dev \
+ xz-utils \
+ nodejs \
+ && rm -rf /var/lib/apt/lists/*
+
+# Install powershell (universal package) so we can test x.ps1 on Linux
+RUN curl -sL "https://github.com/PowerShell/PowerShell/releases/download/v7.3.1/powershell_7.3.1-1.deb_amd64.deb" > powershell.deb && \
+ dpkg -i powershell.deb && \
+ rm -f powershell.deb
+
+COPY scripts/sccache.sh /scripts/
+RUN sh /scripts/sccache.sh
+
+# We are disabling CI LLVM since this builder is intentionally using a host
+# LLVM, rather than the typical src/llvm-project LLVM.
+ENV NO_DOWNLOAD_CI_LLVM 1
+
+# Using llvm-link-shared due to libffi issues -- see #34486
+ENV RUST_CONFIGURE_ARGS \
+ --build=x86_64-unknown-linux-gnu \
+ --llvm-root=/usr/lib/llvm-15 \
+ --enable-llvm-link-shared \
+ --set rust.thin-lto-import-instr-limit=10
+
+# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
+ENV SCRIPT ../x.py --stage 2 test --exclude src/tools/tidy && \
+ # Run the `mir-opt` tests again but this time for a 32-bit target.
+ # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
+ # both 32-bit and 64-bit outputs updated by the PR author, before
+ # the PR is approved and tested for merging.
+ # It will also detect tests lacking `// EMIT_MIR_FOR_EACH_BIT_WIDTH`,
+ # despite having different output on 32-bit vs 64-bit targets.
+ ../x --stage 2 test tests/mir-opt \
+ --host='' --target=i686-unknown-linux-gnu && \
+ # Run the UI test suite again, but in `--pass=check` mode
+ #
+ # This is intended to make sure that both `--pass=check` continues to
+ # work.
+ #
+ ../x.ps1 --stage 2 test tests/ui --pass=check \
+ --host='' --target=i686-unknown-linux-gnu && \
+ # Run tidy at the very end, after all the other tests.
+ python2.7 ../x.py --stage 2 test src/tools/tidy
URL=https://dl.google.com/android/repository
download_ndk() {
- mkdir -p /android/ndk
- cd /android/ndk
+ mkdir /android/
+ cd /android
curl -fO $URL/$1
unzip -q $1
rm $1
mv android-ndk-* ndk
}
-
-make_standalone_toolchain() {
- # See https://developer.android.com/ndk/guides/standalone_toolchain.htm
- python3 /android/ndk/ndk/build/tools/make_standalone_toolchain.py \
- --install-dir /android/ndk/$1-$2 \
- --arch $1 \
- --api $2
-}
-
-remove_ndk() {
- rm -rf /android/ndk/ndk
-}
-
-download_and_make_toolchain() {
- download_ndk $1 && \
- make_standalone_toolchain $2 $3 && \
- remove_ndk
-}
- name: x86_64-gnu-distcheck
<<: *job-linux-xl
+ - name: x86_64-gnu-llvm-15
+ env:
+ RUST_BACKTRACE: 1
+ <<: *job-linux-xl
+
+ - name: x86_64-gnu-llvm-14
+ env:
+ RUST_BACKTRACE: 1
+ <<: *job-linux-xl
+
- name: x86_64-gnu-llvm-13
env:
RUST_BACKTRACE: 1
- name: i686-mingw-1
env:
- RUST_CONFIGURE_ARGS: >-
- --build=i686-pc-windows-gnu
- --set llvm.allow-old-toolchain
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
SCRIPT: make ci-mingw-subset-1
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
- name: i686-mingw-2
env:
- RUST_CONFIGURE_ARGS: >-
- --build=i686-pc-windows-gnu
- --set llvm.allow-old-toolchain
+ RUST_CONFIGURE_ARGS: --build=i686-pc-windows-gnu
SCRIPT: make ci-mingw-subset-2
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
RUST_CONFIGURE_ARGS: >-
--build=x86_64-pc-windows-gnu
--enable-profiler
- --set llvm.allow-old-toolchain
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1
RUST_CONFIGURE_ARGS: >-
--build=x86_64-pc-windows-gnu
--enable-profiler
- --set llvm.allow-old-toolchain
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1
--build=i686-pc-windows-gnu
--enable-full-tools
--enable-profiler
- --set llvm.allow-old-toolchain
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1
--build=x86_64-pc-windows-gnu
--enable-full-tools
--enable-profiler
- --set llvm.allow-old-toolchain
# We are intentionally allowing an old toolchain on this builder (and that's
# incompatible with LLVM downloads today).
NO_DOWNLOAD_CI_LLVM: 1
# 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.
-#
# Otherwise install MinGW through `pacman`
set -euo pipefail
source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
-MINGW_ARCHIVE_32="i686-6.3.0-release-posix-dwarf-rt_v5-rev2.7z"
-MINGW_ARCHIVE_64="x86_64-6.3.0-release-posix-seh-rt_v5-rev2.7z"
+MINGW_ARCHIVE_32="i686-12.2.0-release-posix-dwarf-rt_v10-rev0.7z"
+MINGW_ARCHIVE_64="x86_64-12.2.0-release-posix-seh-rt_v10-rev0.7z"
if isWindows; then
case "${CI_JOB_NAME}" in
curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}"
7z x -y mingw.7z > /dev/null
- curl -o "${mingw_dir}/bin/gdborig.exe" "${MIRRORS_BASE}/2017-04-20-${bits}bit-gdborig.exe"
ciCommandAddPath "$(pwd)/${mingw_dir}/bin"
fi
fi
-Subproject commit 2bd5d42c9956369132228da6409f0e68da56c51a
+Subproject commit 2cd1b5593d26dc6a03c20f8619187ad4b2485552
-Subproject commit 8ca261268068d80c0969260fff15199bad87b587
+Subproject commit 960d610e7f33889a2577f5f17c26f0d5c82b30df
-Subproject commit 3ae62681ff236d5528ef7c8c28ba7c6b2ecc6731
+Subproject commit 2cb0ed9ba56360949f492f9866afe8c293f9f9da
-Subproject commit 8888f9428fe9a48f31de6bd2cef9b9bf80791edc
+Subproject commit a9fb7d13eadfcc5f457962731f105b97f9a7474a
-Subproject commit b3e2a6e6c8a3aae5b5d950c63046f23bae07096d
+Subproject commit 7352353ae91c48b136d2ca7d03822e1448165e1e
cargo test --tests
```
+> **Note**: The default for `LLVM_PROFILE_FILE` is `default_%m_%p.profraw`. Versions prior to 1.65 had a default of `default.profraw`, so if using those earlier versions, it is recommended to explicitly set `LLVM_PROFILE_FILE="default_%m_%p.profraw"` to avoid having multiple tests overwrite the `.profraw` files.
+
Make note of the test binary file paths, displayed after the word "`Running`" in the test output:
```text
--- /dev/null
+# `tiny-const-eval-limit`
+
+--------------------
+
+The `-Ztiny-const-eval-limit` compiler flag sets a tiny, non-configurable limit for const eval.
+This flag should only be used by const eval tests in the rustc test suite.
echo "Running pre-push script $ROOT_DIR/x test tidy"
cd "$ROOT_DIR"
-./x test tidy
+CARGOFLAGS="--locked" ./x test tidy
bound_params: Vec::new(),
})
})
- .chain(
- lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map(
- |(lifetime, bounds)| {
- let mut bounds_vec = bounds.into_iter().collect();
- self.sort_where_bounds(&mut bounds_vec);
- WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec }
- },
- ),
- )
+ .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map(
+ |(lifetime, bounds)| {
+ let mut bounds_vec = bounds.into_iter().collect();
+ self.sort_where_bounds(&mut bounds_vec);
+ WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec }
+ },
+ ))
.collect()
}
trait_def_id,
impl_def_id
);
- let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap();
if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) {
continue;
}
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId};
use rustc_hir::Mutability;
use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty::{self, TyCtxt};
res: Res,
name: Symbol,
attrs: Option<&[ast::Attribute]>,
- visited: &mut FxHashSet<DefId>,
+ visited: &mut DefIdSet,
) -> Option<Vec<clean::Item>> {
let did = res.opt_def_id()?;
if did.is_local() {
pub(crate) fn try_inline_glob(
cx: &mut DocContext<'_>,
res: Res,
- visited: &mut FxHashSet<DefId>,
+ current_mod: LocalDefId,
+ visited: &mut DefIdSet,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
) -> Option<Vec<clean::Item>> {
let did = res.opt_def_id()?;
match res {
Res::Def(DefKind::Mod, did) => {
- let mut items = build_module_items(cx, did, visited, inlined_names);
+ // Use the set of module reexports to filter away names that are not actually
+ // reexported by the glob, e.g. because they are shadowed by something else.
+ let reexports = cx
+ .tcx
+ .module_reexports(current_mod)
+ .unwrap_or_default()
+ .iter()
+ .filter_map(|child| child.res.opt_def_id())
+ .collect();
+ let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports));
items.drain_filter(|item| {
if let Some(name) = item.name {
// If an item with the same type and name already exists,
}
fn build_external_function<'tcx>(cx: &mut DocContext<'tcx>, did: DefId) -> Box<clean::Function> {
- let sig = cx.tcx.fn_sig(did);
+ let sig = cx.tcx.fn_sig(did).subst_identity();
let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name)) if name != kw::UnderscoreLifetime => {
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl");
let tcx = cx.tcx;
- let associated_trait = tcx.impl_trait_ref(did);
+ let associated_trait = tcx.impl_trait_ref(did).map(ty::EarlyBinder::skip_binder);
// Only inline impl if the implemented trait is
// reachable in rustdoc generated documentation
));
}
-fn build_module(
- cx: &mut DocContext<'_>,
- did: DefId,
- visited: &mut FxHashSet<DefId>,
-) -> clean::Module {
- let items = build_module_items(cx, did, visited, &mut FxHashSet::default());
+fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module {
+ let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None);
let span = clean::Span::new(cx.tcx.def_span(did));
clean::Module { items, span }
fn build_module_items(
cx: &mut DocContext<'_>,
did: DefId,
- visited: &mut FxHashSet<DefId>,
+ visited: &mut DefIdSet,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
+ allowed_def_ids: Option<&DefIdSet>,
) -> Vec<clean::Item> {
let mut items = Vec::new();
for &item in cx.tcx.module_children(did).iter() {
if item.vis.is_public() {
let res = item.res.expect_non_local();
+ if let Some(def_id) = res.opt_def_id()
+ && let Some(allowed_def_ids) = allowed_def_ids
+ && !allowed_def_ids.contains(&def_id) {
+ continue;
+ }
if let Some(def_id) = res.mod_def_id() {
// If we're inlining a glob import, it's possible to have
// two distinct modules with the same name. We don't want to
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, LOCAL_CRATE};
use rustc_hir::PredicateOrigin;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
}
});
- Item::from_hir_id_and_parts(doc.id, Some(doc.name), ModuleItem(Module { items, span }), cx)
+ let kind = ModuleItem(Module { items, span });
+ Item::from_def_id_and_parts(doc.def_id.to_def_id(), Some(doc.name), kind, cx)
}
fn clean_generic_bound<'tcx>(
fn clean_hir_term<'tcx>(term: &hir::Term<'tcx>, cx: &mut DocContext<'tcx>) -> Term {
match term {
hir::Term::Ty(ty) => Term::Type(clean_ty(ty, cx)),
- hir::Term::Const(c) => {
- let def_id = cx.tcx.hir().local_def_id(c.hir_id);
- Term::Constant(clean_middle_const(
- ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, def_id)),
- cx,
- ))
- }
+ hir::Term::Const(c) => Term::Constant(clean_middle_const(
+ ty::Binder::dummy(ty::Const::from_anon_const(cx.tcx, c.def_id)),
+ cx,
+ )),
}
}
Some(def.def_id),
)),
default: match has_default {
- true => Some(Box::new(cx.tcx.const_param_default(def.def_id).to_string())),
+ true => Some(Box::new(
+ cx.tcx.const_param_default(def.def_id).subst_identity().to_string(),
+ )),
false => None,
},
},
generics: Option<&hir::Generics<'tcx>>,
param: &hir::GenericParam<'tcx>,
) -> GenericParamDef {
- let did = cx.tcx.hir().local_def_id(param.hir_id);
let (name, kind) = match param.kind {
hir::GenericParamKind::Lifetime { .. } => {
let outlives = if let Some(generics) = generics {
generics
- .outlives_for_param(did)
+ .outlives_for_param(param.def_id)
.filter(|bp| !bp.in_where_clause)
.flat_map(|bp| bp.bounds)
.map(|bound| match bound {
hir::GenericParamKind::Type { ref default, synthetic } => {
let bounds = if let Some(generics) = generics {
generics
- .bounds_for_param(did)
+ .bounds_for_param(param.def_id)
.filter(|bp| bp.origin != PredicateOrigin::WhereClause)
.flat_map(|bp| bp.bounds)
.filter_map(|x| clean_generic_bound(x, cx))
(
param.name.ident().name,
GenericParamDefKind::Type {
- did: did.to_def_id(),
+ did: param.def_id.to_def_id(),
bounds,
default: default.map(|t| clean_ty(t, cx)).map(Box::new),
synthetic,
hir::GenericParamKind::Const { ty, default } => (
param.name.ident().name,
GenericParamDefKind::Const {
- did: did.to_def_id(),
+ did: param.def_id.to_def_id(),
ty: Box::new(clean_ty(ty, cx)),
- default: default.map(|ct| {
- let def_id = cx.tcx.hir().local_def_id(ct.hir_id);
- Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string())
- }),
+ default: default
+ .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
},
),
};
}
}
ty::AssocKind::Fn => {
- let sig = tcx.fn_sig(assoc_item.def_id);
+ let sig = tcx.fn_sig(assoc_item.def_id).subst_identity();
let late_bound_regions = sig.bound_vars().into_iter().filter_map(|var| match var {
ty::BoundVariableKind::Region(ty::BrNamed(_, name))
let hir::ItemKind::TyAlias(ty, generics) = alias else { return None };
let provided_params = &path.segments.last().expect("segments were empty");
- let mut substs = FxHashMap::default();
+ let mut substs = DefIdMap::default();
let generic_args = provided_params.args();
let mut indices: hir::GenericParamCount = Default::default();
_ => None,
});
if let Some(lt) = lifetime {
- let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id);
let cleaned = if !lt.is_anonymous() {
clean_lifetime(lt, cx)
} else {
Lifetime::elided()
};
- substs.insert(lt_def_id.to_def_id(), SubstParam::Lifetime(cleaned));
+ substs.insert(param.def_id.to_def_id(), SubstParam::Lifetime(cleaned));
}
indices.lifetimes += 1;
}
hir::GenericParamKind::Type { ref default, .. } => {
- let ty_param_def_id = cx.tcx.hir().local_def_id(param.hir_id);
let mut j = 0;
let type_ = generic_args.args.iter().find_map(|arg| match arg {
hir::GenericArg::Type(ty) => {
_ => None,
});
if let Some(ty) = type_ {
- substs.insert(ty_param_def_id.to_def_id(), SubstParam::Type(clean_ty(ty, cx)));
+ substs.insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(ty, cx)));
} else if let Some(default) = *default {
- substs.insert(
- ty_param_def_id.to_def_id(),
- SubstParam::Type(clean_ty(default, cx)),
- );
+ substs
+ .insert(param.def_id.to_def_id(), SubstParam::Type(clean_ty(default, cx)));
}
indices.types += 1;
}
hir::GenericParamKind::Const { .. } => {
- let const_param_def_id = cx.tcx.hir().local_def_id(param.hir_id);
let mut j = 0;
let const_ = generic_args.args.iter().find_map(|arg| match arg {
hir::GenericArg::Const(ct) => {
});
if let Some(ct) = const_ {
substs.insert(
- const_param_def_id.to_def_id(),
+ param.def_id.to_def_id(),
SubstParam::Constant(clean_const(ct, cx)),
);
}
let length = match length {
hir::ArrayLen::Infer(_, _) => "_".to_string(),
hir::ArrayLen::Body(anon_const) => {
- let def_id = cx.tcx.hir().local_def_id(anon_const.hir_id);
// NOTE(min_const_generics): We can't use `const_eval_poly` for constants
// as we currently do not supply the parent generics to anonymous constants
// but do allow `ConstKind::Param`.
// `const_eval_poly` tries to first substitute generic parameters which
// results in an ICE while manually constructing the constant and using `eval`
// does nothing for `ConstKind::Param`.
- let ct = ty::Const::from_anon_const(cx.tcx, def_id);
- let param_env = cx.tcx.param_env(def_id);
+ let ct = ty::Const::from_anon_const(cx.tcx, anon_const.def_id);
+ let param_env = cx.tcx.param_env(anon_const.def_id);
print_const(cx, ct.eval(cx.tcx, param_env))
}
};
ty::Bound(..) => panic!("Bound"),
ty::Placeholder(..) => panic!("Placeholder"),
ty::GeneratorWitness(..) => panic!("GeneratorWitness"),
+ ty::GeneratorWitnessMIR(..) => panic!("GeneratorWitnessMIR"),
ty::Infer(..) => panic!("Infer"),
ty::Error(_) => rustc_errors::FatalError.raise(),
}
}
pub(crate) fn clean_field<'tcx>(field: &hir::FieldDef<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
- let def_id = cx.tcx.hir().local_def_id(field.hir_id).to_def_id();
- clean_field_with_def_id(def_id, field.ident.name, clean_ty(field.ty, cx), cx)
+ clean_field_with_def_id(field.def_id.to_def_id(), field.ident.name, clean_ty(field.ty, cx), cx)
}
pub(crate) fn clean_middle_field<'tcx>(field: &ty::FieldDef, cx: &mut DocContext<'tcx>) -> Item {
disr_expr: &Option<hir::AnonConst>,
cx: &mut DocContext<'tcx>,
) -> Variant {
- let discriminant = disr_expr.map(|disr| Discriminant {
- expr: Some(disr.body),
- value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(),
- });
+ let discriminant = disr_expr
+ .map(|disr| Discriminant { expr: Some(disr.body), value: disr.def_id.to_def_id() });
let kind = match variant {
hir::VariantData::Struct(..) => VariantKind::Struct(VariantStruct {
map: rustc_middle::hir::map::Map<'hir>,
item: Option<&'hir hir::Item<'hir>>,
looking_for: Ident,
- target_hir_id: hir::HirId,
+ target_def_id: LocalDefId,
}
impl<'hir> OneLevelVisitor<'hir> {
- fn new(map: rustc_middle::hir::map::Map<'hir>, target_hir_id: hir::HirId) -> Self {
- Self { map, item: None, looking_for: Ident::empty(), target_hir_id }
+ fn new(map: rustc_middle::hir::map::Map<'hir>, target_def_id: LocalDefId) -> Self {
+ Self { map, item: None, looking_for: Ident::empty(), target_def_id }
}
fn reset(&mut self, looking_for: Ident) {
if self.item.is_none()
&& item.ident == self.looking_for
&& matches!(item.kind, hir::ItemKind::Use(_, _))
- || item.hir_id() == self.target_hir_id
+ || item.owner_id.def_id == self.target_def_id
{
self.item = Some(item);
}
fn get_all_import_attributes<'hir>(
mut item: &hir::Item<'hir>,
tcx: TyCtxt<'hir>,
- target_hir_id: hir::HirId,
+ target_def_id: LocalDefId,
attributes: &mut Vec<ast::Attribute>,
) {
let hir_map = tcx.hir();
- let mut visitor = OneLevelVisitor::new(hir_map, target_hir_id);
+ let mut visitor = OneLevelVisitor::new(hir_map, target_def_id);
+ let mut visited = FxHashSet::default();
// If the item is an import and has at least a path with two parts, we go into it.
while let hir::ItemKind::Use(path, _) = item.kind &&
path.segments.len() > 1 &&
- let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res
+ let hir::def::Res::Def(_, def_id) = path.segments[path.segments.len() - 2].res &&
+ visited.insert(def_id)
{
if let Some(hir::Node::Item(parent_item)) = hir_map.get_if_local(def_id) {
// We add the attributes from this import into the list.
cx: &mut DocContext<'tcx>,
item: &hir::Item<'tcx>,
renamed: Option<Symbol>,
- import_id: Option<hir::HirId>,
+ import_id: Option<LocalDefId>,
) -> Vec<Item> {
use hir::ItemKind;
generics: clean_generics(generics, cx),
fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
}),
- ItemKind::Impl(impl_) => return clean_impl(impl_, item.hir_id(), cx),
+ ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
// proc macros can have a name set by attributes
ItemKind::Fn(ref sig, generics, body_id) => {
clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
let mut extra_attrs = Vec::new();
if let Some(hir::Node::Item(use_node)) =
- import_id.and_then(|hir_id| cx.tcx.hir().find(hir_id))
+ import_id.and_then(|def_id| cx.tcx.hir().find_by_def_id(def_id))
{
// We get all the various imports' attributes.
- get_all_import_attributes(use_node, cx.tcx, item.hir_id(), &mut extra_attrs);
+ get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs);
}
if !extra_attrs.is_empty() {
fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx));
- Item::from_hir_id_and_parts(variant.hir_id, Some(variant.ident.name), kind, cx)
+ Item::from_def_id_and_parts(variant.def_id.to_def_id(), Some(variant.ident.name), kind, cx)
}
fn clean_impl<'tcx>(
impl_: &hir::Impl<'tcx>,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
cx: &mut DocContext<'tcx>,
) -> Vec<Item> {
let tcx = cx.tcx;
.iter()
.map(|ii| clean_impl_item(tcx.hir().impl_item(ii.id), cx))
.collect::<Vec<_>>();
- let def_id = tcx.hir().local_def_id(hir_id);
// If this impl block is an implementation of the Deref trait, then we
// need to try inlining the target's inherent impl blocks as well.
ImplKind::Normal
},
}));
- Item::from_hir_id_and_parts(hir_id, None, kind, cx)
+ Item::from_def_id_and_parts(def_id.to_def_id(), None, kind, cx)
};
if let Some(type_alias) = type_alias {
ret.push(make_item(trait_.clone(), type_alias, items.clone()));
let krate_owner_def_id = krate.owner_id.to_def_id();
if please_inline {
- let mut visited = FxHashSet::default();
+ let mut visited = DefIdSet::default();
let res = Res::Def(DefKind::Mod, crate_def_id);
let path = clean_path(path, cx);
let inner = if kind == hir::UseKind::Glob {
if !denied {
- let mut visited = FxHashSet::default();
- if let Some(items) = inline::try_inline_glob(cx, path.res, &mut visited, inlined_names)
+ let mut visited = DefIdSet::default();
+ if let Some(items) =
+ inline::try_inline_glob(cx, path.res, current_mod, &mut visited, inlined_names)
{
return items;
}
}
}
if !denied {
- let mut visited = FxHashSet::default();
+ let mut visited = DefIdSet::default();
let import_def_id = import.owner_id.to_def_id();
if let Some(mut items) = inline::try_inline(
hir::ForeignItemKind::Type => ForeignTypeItem,
};
- Item::from_hir_id_and_parts(
- item.hir_id(),
+ Item::from_def_id_and_parts(
+ item.owner_id.def_id.to_def_id(),
Some(renamed.unwrap_or(item.ident.name)),
kind,
cx,
// Look for equality predicates on associated types that can be merged into
// general bound predicates.
- equalities.retain(|&(ref lhs, ref rhs, ref bound_params)| {
+ equalities.retain(|(lhs, rhs, bound_params)| {
let Some((ty, trait_did, name)) = lhs.projection() else { return true; };
let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
let bound_params = bound_params
self.attrs.doc_value()
}
- /// Convenience wrapper around [`Self::from_def_id_and_parts`] which converts
- /// `hir_id` to a [`DefId`]
- pub(crate) fn from_hir_id_and_parts(
- hir_id: hir::HirId,
- name: Option<Symbol>,
- kind: ItemKind,
- cx: &mut DocContext<'_>,
- ) -> Item {
- Item::from_def_id_and_parts(cx.tcx.hir().local_def_id(hir_id).to_def_id(), name, kind, cx)
- }
-
pub(crate) fn from_def_id_and_parts(
def_id: DefId,
name: Option<Symbol>,
tcx: TyCtxt<'_>,
asyncness: hir::IsAsync,
) -> hir::FnHeader {
- let sig = tcx.fn_sig(def_id);
+ let sig = tcx.fn_sig(def_id).skip_binder();
let constness =
if tcx.is_const_fn(def_id) && is_unstable_const_fn(tcx, def_id).is_none() {
hir::Constness::Const
let header = match *self.kind {
ItemKind::ForeignFunctionItem(_) => {
let def_id = self.item_id.as_def_id().unwrap();
- let abi = tcx.fn_sig(def_id).abi();
+ let abi = tcx.fn_sig(def_id).skip_binder().abi();
hir::FnHeader {
unsafety: if abi == Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap())
pub(crate) fn is_literal(&self, tcx: TyCtxt<'_>) -> bool {
match *self {
- ConstantKind::TyConst { .. } => false,
- ConstantKind::Extern { def_id } => def_id.as_local().map_or(false, |def_id| {
- is_literal_expr(tcx, tcx.hir().local_def_id_to_hir_id(def_id))
- }),
+ ConstantKind::TyConst { .. } | ConstantKind::Extern { .. } => false,
ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
is_literal_expr(tcx, body.hir_id)
}
}
pub(crate) fn imported_item_is_doc_hidden(&self, tcx: TyCtxt<'_>) -> bool {
- match self.source.did {
- Some(did) => tcx
- .get_attrs(did, sym::doc)
- .filter_map(ast::Attribute::meta_item_list)
- .flatten()
- .has_word(sym::hidden),
- None => false,
- }
+ self.source.did.map_or(false, |did| tcx.is_doc_hidden(did))
}
}
pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
let module = crate::visit_ast::RustdocVisitor::new(cx).visit();
- for &cnum in cx.tcx.crates(()) {
- // Analyze doc-reachability for extern items
- crate::visit_lib::lib_embargo_visit_item(cx, cnum.as_def_id());
- }
-
// Clean the crate, translating the entire librustc_ast AST to one that is
// understood by rustdoc.
let mut module = clean_doc_module(&module, cx);
use rustc_errors::json::JsonEmitter;
use rustc_feature::UnstableFeatures;
use rustc_hir::def::{Namespace, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{HirId, Path, TraitCandidate};
use rustc_interface::interface;
pub(crate) traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
pub(crate) all_trait_impls: Option<Vec<DefId>>,
pub(crate) all_macro_rules: FxHashMap<Symbol, Res<NodeId>>,
+ pub(crate) extern_doc_reachable: DefIdSet,
}
pub(crate) struct DocContext<'tcx> {
pub(crate) external_traits: Rc<RefCell<FxHashMap<DefId, clean::Trait>>>,
/// Used while populating `external_traits` to ensure we don't process the same trait twice at
/// the same time.
- pub(crate) active_extern_traits: FxHashSet<DefId>,
+ pub(crate) active_extern_traits: DefIdSet,
// The current set of parameter substitutions,
// for expanding type aliases at the HIR level:
/// Table `DefId` of type, lifetime, or const parameter -> substituted type, lifetime, or const
- pub(crate) substs: FxHashMap<DefId, clean::SubstParam>,
+ pub(crate) substs: DefIdMap<clean::SubstParam>,
/// Table synthetic type parameter for `impl Trait` in argument position -> bounds
pub(crate) impl_trait_bounds: FxHashMap<ImplTraitParam, Vec<clean::GenericBound>>,
/// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`.
/// Call the closure with the given parameters set as
/// the substitutions for a type alias' RHS.
- pub(crate) fn enter_alias<F, R>(
- &mut self,
- substs: FxHashMap<DefId, clean::SubstParam>,
- f: F,
- ) -> R
+ pub(crate) fn enter_alias<F, R>(&mut self, substs: DefIdMap<clean::SubstParam>, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
// Add the doc cfg into the doc build.
cfgs.push("doc".to_string());
- let cpath = Some(input.clone());
let input = Input::File(input);
// By default, rustdoc ignores all lints.
crate_cfg: interface::parse_cfgspecs(cfgs),
crate_check_cfg: interface::parse_check_cfg(check_cfgs),
input,
- input_path: cpath,
output_file: None,
output_dir: None,
file_loader: None,
show_coverage,
};
+ ctxt.cache
+ .effective_visibilities
+ .init(mem::take(&mut ctxt.resolver_caches.extern_doc_reachable));
+
// Small hack to force the Sized trait to be present.
//
// Note that in case of `#![no_core]`, the trait is not available.
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
-use rustc_hir as hir;
-use rustc_hir::def_id::LOCAL_CRATE;
-use rustc_hir::intravisit;
-use rustc_hir::{HirId, CRATE_HIR_ID};
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID};
use rustc_interface::interface;
use rustc_middle::hir::map::Map;
use rustc_middle::hir::nested_filter;
crate_cfg: interface::parse_cfgspecs(cfgs),
crate_check_cfg: interface::parse_check_cfg(options.check_cfgs.clone()),
input,
- input_path: None,
output_file: None,
output_dir: None,
file_loader: None,
};
hir_collector.visit_testable(
"".to_string(),
- CRATE_HIR_ID,
+ CRATE_DEF_ID,
tcx.hir().span(CRATE_HIR_ID),
|this| tcx.hir().walk_toplevel_module(this),
);
fn visit_testable<F: FnOnce(&mut Self)>(
&mut self,
name: String,
- hir_id: HirId,
+ def_id: LocalDefId,
sp: Span,
nested: F,
) {
- let ast_attrs = self.tcx.hir().attrs(hir_id);
+ let ast_attrs = self.tcx.hir().attrs(self.tcx.hir().local_def_id_to_hir_id(def_id));
if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
if !cfg.matches(&self.sess.parse_sess, Some(self.tcx.features())) {
return;
self.collector.enable_per_target_ignores,
Some(&crate::html::markdown::ExtraInfo::new(
self.tcx,
- hir_id,
+ def_id.to_def_id(),
span_of_attrs(&attrs).unwrap_or(sp),
)),
);
_ => item.ident.to_string(),
};
- self.visit_testable(name, item.hir_id(), item.span, |this| {
+ self.visit_testable(name, item.owner_id.def_id, item.span, |this| {
intravisit::walk_item(this, item);
});
}
fn visit_trait_item(&mut self, item: &'hir hir::TraitItem<'_>) {
- self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| {
+ self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
intravisit::walk_trait_item(this, item);
});
}
fn visit_impl_item(&mut self, item: &'hir hir::ImplItem<'_>) {
- self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| {
+ self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
intravisit::walk_impl_item(this, item);
});
}
fn visit_foreign_item(&mut self, item: &'hir hir::ForeignItem<'_>) {
- self.visit_testable(item.ident.to_string(), item.hir_id(), item.span, |this| {
+ self.visit_testable(item.ident.to_string(), item.owner_id.def_id, item.span, |this| {
intravisit::walk_foreign_item(this, item);
});
}
fn visit_variant(&mut self, v: &'hir hir::Variant<'_>) {
- self.visit_testable(v.ident.to_string(), v.hir_id, v.span, |this| {
+ self.visit_testable(v.ident.to_string(), v.def_id, v.span, |this| {
intravisit::walk_variant(this, v);
});
}
fn visit_field_def(&mut self, f: &'hir hir::FieldDef<'_>) {
- self.visit_testable(f.ident.to_string(), f.hir_id, f.span, |this| {
+ self.visit_testable(f.ident.to_string(), f.def_id, f.span, |this| {
intravisit::walk_field_def(this, f);
});
}
use std::mem;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Symbol;
///
/// The values of the map are a list of implementations and documentation
/// found on that implementation.
- pub(crate) impls: FxHashMap<DefId, Vec<Impl>>,
+ pub(crate) impls: DefIdMap<Vec<Impl>>,
/// Maintains a mapping of local crate `DefId`s to the fully qualified name
/// and "short type description" of that node. This is used when generating
/// to the path used if the corresponding type is inlined. By
/// doing this, we can detect duplicate impls on a trait page, and only display
/// the impl for the inlined type.
- pub(crate) exact_paths: FxHashMap<DefId, Vec<Symbol>>,
+ pub(crate) exact_paths: DefIdMap<Vec<Symbol>>,
/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
struct CacheBuilder<'a, 'tcx> {
cache: &'a mut Cache,
/// This field is used to prevent duplicated impl blocks.
- impl_ids: FxHashMap<DefId, FxHashSet<DefId>>,
+ impl_ids: DefIdMap<DefIdSet>,
tcx: TyCtxt<'tcx>,
}
let (krate, mut impl_ids) = {
let mut cache_builder =
- CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: FxHashMap::default() };
+ CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() };
krate = cache_builder.fold_crate(krate);
(krate, cache_builder.impl_ids)
};
}
// Index this method for searching later on.
- if let Some(ref s) = item.name.or_else(|| {
+ if let Some(s) = item.name.or_else(|| {
if item.is_stripped() {
None
} else if let clean::ImportItem(ref i) = *item.kind &&
// for where the type was defined. On the other
// hand, `paths` always has the right
// information if present.
- Some(&(ref fqp, _)) => Some(&fqp[..fqp.len() - 1]),
+ Some((fqp, _)) => Some(&fqp[..fqp.len() - 1]),
None => None,
};
((did, path), true)
short_markdown_summary(x.as_str(), &item.link_names(self.cache))
});
let ty = item.type_();
- let name = s.to_string();
- if ty != ItemType::StructField || u16::from_str_radix(&name, 10).is_err() {
+ if ty != ItemType::StructField
+ || u16::from_str_radix(s.as_str(), 10).is_err()
+ {
// In case this is a field from a tuple struct, we don't add it into
// the search index because its name is something like "0", which is
// not useful for rustdoc search.
self.cache.search_index.push(IndexItem {
ty,
- name,
+ name: s,
path: join_with_double_colon(path),
desc,
parent,
root_path: Option<&str>,
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
let tcx = cx.shared.tcx;
- let crate_name = tcx.crate_name(def_id.krate).to_string();
+ let crate_name = tcx.crate_name(def_id.krate);
let cache = cx.cache();
let fqp: Vec<Symbol> = tcx
}
})
.collect();
- let mut relative = fqp.iter().map(|elem| elem.to_string());
+ let mut relative = fqp.iter().copied();
let cstore = CStore::from_tcx(tcx);
// We need this to prevent a `panic` when this function is used from intra doc links...
if !cstore.has_crate_data(def_id.krate) {
};
let mut path = if is_macro_2 {
- once(crate_name.clone()).chain(relative).collect()
+ once(crate_name).chain(relative).collect()
} else {
- vec![crate_name.clone(), relative.next_back().unwrap()]
+ vec![crate_name, relative.next_back().unwrap()]
};
if path.len() < 2 {
// The minimum we can have is the crate name followed by the macro name. If shorter, then
}
if let Some(last) = path.last_mut() {
- *last = format!("macro.{}.html", last);
+ *last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
}
let url = match cache.extern_locations[&def_id.krate] {
ExternalLocation::Remote(ref s) => {
// `ExternalLocation::Remote` always end with a `/`.
- format!("{}{}", s, path.join("/"))
+ format!("{}{}", s, path.iter().map(|p| p.as_str()).join("/"))
}
ExternalLocation::Local => {
// `root_path` always end with a `/`.
- format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/"))
+ format!(
+ "{}{}/{}",
+ root_path.unwrap_or(""),
+ crate_name,
+ path.iter().map(|p| p.as_str()).join("/")
+ )
}
ExternalLocation::Unknown => {
debug!("crate {} not in cache when linkifying macros", crate_name);
clean::Tuple(ref typs) => {
match &typs[..] {
&[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
- &[ref one] => {
+ [one] => {
if let clean::Generic(name) = one {
primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
} else {
_ => String::new(),
};
let m = mutability.print_with_space();
- let amp = if f.alternate() { "&".to_string() } else { "&".to_string() };
+ let amp = if f.alternate() { "&" } else { "&" };
match **ty {
clean::DynTrait(ref bounds, ref trait_lt)
if bounds.len() > 1 || trait_lt.is_some() =>
fmt_type(ty, f, use_absolute, cx)?;
write!(f, ")")
}
- clean::Generic(..) => {
- primitive_link(
- f,
- PrimitiveType::Reference,
- &format!("{}{}{}", amp, lt, m),
- cx,
- )?;
- fmt_type(ty, f, use_absolute, cx)
+ clean::Generic(name) => {
+ primitive_link(f, PrimitiveType::Reference, &format!("{amp}{lt}{m}{name}"), cx)
}
_ => {
write!(f, "{}{}{}", amp, lt, m)?;
/// and returns [`ControlFlow::Break`].
pub(super) fn push(&mut self, text: &str) -> ControlFlow<(), ()> {
if self.len + text.len() > self.limit {
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
self.flush_queue();
write!(self.buf, "{}", Escape(text)).unwrap();
self.len += text.len();
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
/// Open an HTML tag.
buf.push("word#")?;
buf.push(&n.to_string())?;
buf.close_tag();
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
});
buf.close_tag();
assert_eq!(
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
-use rustc_hir::HirId;
use rustc_middle::ty::TyCtxt;
use rustc_span::edition::Edition;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
use once_cell::sync::Lazy;
use std::borrow::Cow;
#[derive(Clone, Debug)]
pub struct Playground {
- pub crate_name: Option<String>,
+ pub crate_name: Option<Symbol>,
pub url: String,
}
.map(|l| map_line(l).for_code())
.intersperse("\n".into())
.collect::<String>();
- let krate = krate.as_ref().map(|s| &**s);
+ let krate = krate.as_ref().map(|s| s.as_str());
let (test, _, _) =
doctest::make_test(&test, krate, false, &Default::default(), edition, None);
let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" };
// These characters don't need to be escaped in a URI.
- // FIXME: use a library function for percent encoding.
+ // See https://url.spec.whatwg.org/#query-percent-encode-set
+ // and https://url.spec.whatwg.org/#urlencoded-parsing
+ // and https://url.spec.whatwg.org/#url-code-points
fn dont_escape(c: u8) -> bool {
(b'a' <= c && c <= b'z')
|| (b'A' <= c && c <= b'Z')
|| c == b'-'
|| c == b'_'
|| c == b'.'
+ || c == b','
|| c == b'~'
|| c == b'!'
|| c == b'\''
|| c == b'('
|| c == b')'
|| c == b'*'
+ || c == b'/'
+ || c == b';'
+ || c == b':'
+ || c == b'?'
+ // As described in urlencoded-parsing, the
+ // first `=` is the one that separates key from
+ // value. Following `=`s are part of the value.
+ || c == b'='
}
let mut test_escaped = String::new();
for b in test.bytes() {
if dont_escape(b) {
test_escaped.push(char::from(b));
+ } else if b == b' ' {
+ // URL queries are decoded with + replaced with SP
+ test_escaped.push('+');
+ } else if b == b'%' {
+ test_escaped.push('%');
+ test_escaped.push('%');
} else {
write!(test_escaped, "%{:02X}", b).unwrap();
}
}
pub(crate) struct ExtraInfo<'tcx> {
- id: ExtraInfoId,
+ def_id: DefId,
sp: Span,
tcx: TyCtxt<'tcx>,
}
-enum ExtraInfoId {
- Hir(HirId),
- Def(DefId),
-}
-
impl<'tcx> ExtraInfo<'tcx> {
- pub(crate) fn new(tcx: TyCtxt<'tcx>, hir_id: HirId, sp: Span) -> ExtraInfo<'tcx> {
- ExtraInfo { id: ExtraInfoId::Hir(hir_id), sp, tcx }
- }
-
- pub(crate) fn new_did(tcx: TyCtxt<'tcx>, did: DefId, sp: Span) -> ExtraInfo<'tcx> {
- ExtraInfo { id: ExtraInfoId::Def(did), sp, tcx }
+ pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: DefId, sp: Span) -> ExtraInfo<'tcx> {
+ ExtraInfo { def_id, sp, tcx }
}
fn error_invalid_codeblock_attr(&self, msg: &str, help: &str) {
- let hir_id = match self.id {
- ExtraInfoId::Hir(hir_id) => hir_id,
- ExtraInfoId::Def(item_did) => {
- match item_did.as_local() {
- Some(item_did) => self.tcx.hir().local_def_id_to_hir_id(item_did),
- None => {
- // If non-local, no need to check anything.
- return;
- }
- }
- }
- };
- self.tcx.struct_span_lint_hir(
- crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
- hir_id,
- self.sp,
- msg,
- |lint| lint.help(help),
- );
+ if let Some(def_id) = self.def_id.as_local() {
+ self.tcx.struct_span_lint_hir(
+ crate::lint::INVALID_CODEBLOCK_ATTRIBUTES,
+ self.tcx.hir().local_def_id_to_hir_id(def_id),
+ self.sp,
+ msg,
+ |lint| lint.help(help),
+ );
+ }
}
}
Event::Start(tag) => match tag {
Tag::Emphasis => buf.open_tag("em"),
Tag::Strong => buf.open_tag("strong"),
- Tag::CodeBlock(..) => return ControlFlow::BREAK,
+ Tag::CodeBlock(..) => return ControlFlow::Break(()),
_ => {}
},
Event::End(tag) => match tag {
Tag::Emphasis | Tag::Strong => buf.close_tag(),
- Tag::Paragraph | Tag::Heading(..) => return ControlFlow::BREAK,
+ Tag::Paragraph | Tag::Heading(..) => return ControlFlow::Break(()),
_ => {}
},
Event::HardBreak | Event::SoftBreak => buf.push(" ")?,
_ => {}
};
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
});
(buf.finish(), stopped_early)
use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::edition::Edition;
pub(super) render_redirect_pages: bool,
/// Tracks section IDs for `Deref` targets so they match in both the main
/// body and the sidebar.
- pub(super) deref_id_map: FxHashMap<DefId, String>,
+ pub(super) deref_id_map: DefIdMap<String>,
/// The map used to ensure all generated 'id=' attributes are unique.
pub(super) id_map: IdMap,
/// Shared mutable state.
// If user passed in `--playground-url` arg, we fill in crate name here
let mut playground = None;
if let Some(url) = playground_url {
- playground =
- Some(markdown::Playground { crate_name: Some(krate.name(tcx).to_string()), url });
+ playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
}
let mut layout = layout::Layout {
logo: String::new(),
}
(sym::html_playground_url, Some(s)) => {
playground = Some(markdown::Playground {
- crate_name: Some(krate.name(tcx).to_string()),
+ crate_name: Some(krate.name(tcx)),
url: s.to_string(),
});
}
dst,
render_redirect_pages: false,
id_map,
- deref_id_map: FxHashMap::default(),
+ deref_id_map: Default::default(),
shared: Rc::new(scx),
include_sources,
types_with_notable_traits: FxHashSet::default(),
current: self.current.clone(),
dst: self.dst.clone(),
render_redirect_pages: self.render_redirect_pages,
- deref_id_map: FxHashMap::default(),
+ deref_id_map: Default::default(),
id_map: IdMap::new(),
shared: Rc::clone(&self.shared),
include_sources: self.include_sources,
use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::CtorKind;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
use rustc_middle::middle::stability;
use rustc_middle::ty;
#[derive(Debug)]
pub(crate) struct IndexItem {
pub(crate) ty: ItemType,
- pub(crate) name: String,
+ pub(crate) name: Symbol,
pub(crate) path: String,
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
it: DefId,
what: AssocItemRender<'_>,
) {
- let mut derefs = FxHashSet::default();
+ let mut derefs = DefIdSet::default();
derefs.insert(it);
render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
}
containing_item: &clean::Item,
it: DefId,
what: AssocItemRender<'_>,
- derefs: &mut FxHashSet<DefId>,
+ derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
impl_: &Impl,
container_item: &clean::Item,
deref_mut: bool,
- derefs: &mut FxHashSet<DefId>,
+ derefs: &mut DefIdSet,
) {
let cache = cx.cache();
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
write!(
&mut out,
"<h3>Notable traits for <code>{}</code></h3>\
- <pre class=\"content\"><code>",
+ <pre><code>",
impl_.for_.print(cx)
);
}
})
})
.map(|item| format!("{}.{}", item.type_(), name));
- write!(
- w,
- "<section id=\"{}\" class=\"{}{} has-srclink\">",
- id, item_type, in_trait_class,
- );
+ write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
render_rightside(w, cx, item, containing_item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(
- w,
- "<section id=\"{}\" class=\"{}{} has-srclink\">",
- id, item_type, in_trait_class
- );
+ write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
render_rightside(w, cx, item, containing_item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
clean::AssocTypeItem(tydef, _bounds) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(
- w,
- "<section id=\"{}\" class=\"{}{} has-srclink\">",
- id, item_type, in_trait_class
- );
+ write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
if trait_.is_some() {
// Anchors are only used on trait impls.
write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
} else {
format!(" data-aliases=\"{}\"", aliases.join(","))
};
- write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
+ write!(w, "<section id=\"{}\" class=\"impl\"{}>", id, aliases);
render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
write!(w, "<a href=\"#{}\" class=\"anchor\">§</a>", id);
write!(w, "<h3 class=\"code-header\">");
if let Some(impl_) =
v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
{
- let mut derefs = FxHashSet::default();
+ let mut derefs = DefIdSet::default();
derefs.insert(did);
sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
}
out: &mut Buffer,
impl_: &Impl,
v: &[Impl],
- derefs: &mut FxHashSet<DefId>,
+ derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
let c = cx.cache();
let mut work = VecDeque::new();
let mut process_path = |did: DefId| {
- let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
- let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
+ let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
+ let fqp = cache.exact_paths.get(&did).or_else(get_extern);
if let Some(path) = fqp {
out.push(join_with_double_colon(&path));
// Look for the example file in the source map if it exists, otherwise return a dummy span
let file_span = (|| {
let source_map = tcx.sess.source_map();
- let crate_src = tcx.sess.local_crate_source_file.as_ref()?;
+ let crate_src = tcx.sess.local_crate_source_file()?;
let abs_crate_src = crate_src.canonicalize().ok()?;
let crate_root = abs_crate_src.parent()?.parent()?;
let rel_path = path.strip_prefix(crate_root).ok()?;
};
write!(
w,
- "<div class=\"item-left {stab}{add}import-item\"{id}>\
+ "<div class=\"item-left{add}{stab}\"{id}>\
<code>{vis}{imp}</code>\
</div>\
{stab_tags_before}{stab_tags}{stab_tags_after}",
};
write!(
w,
- "<div class=\"item-left {stab}{add}module-item\">\
+ "<div class=\"item-left{add}{stab}\">\
<a class=\"{class}\" href=\"{href}\" title=\"{title}\">{name}</a>\
{visibility_emoji}\
{unsafety_flag}\
stab = stab.unwrap_or_default(),
unsafety_flag = unsafety_flag,
href = item_path(myitem.type_(), myitem.name.unwrap().as_str()),
- title = [full_path(cx, myitem), myitem.type_().to_string()]
+ title = [myitem.type_().to_string(), full_path(cx, myitem)]
.iter()
.filter_map(|s| if !s.is_empty() { Some(s.as_str()) } else { None })
.collect::<Vec<_>>()
f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
wrap_into_item_decl(w, |w| {
- wrap_item(w, "fn", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
w.reserve(header_len);
write!(
// Output the trait definition
wrap_into_item_decl(w, |w| {
- wrap_item(w, "trait", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
}
- write!(w, "<section id=\"{}\" class=\"method has-srclink\">", id);
+ write!(w, "<section id=\"{}\" class=\"method\">", id);
render_rightside(w, cx, m, t, RenderMode::Normal);
write!(w, "<h4 class=\"code-header\">");
render_assoc_item(
.chain(std::iter::once("implementors"))
.collect();
if let Some(did) = it.item_id.as_def_id() &&
- let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } &&
- let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) {
+ let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } &&
+ let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) {
js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
} else {
fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "trait-alias", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "opaque", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
- wrap_item(w, "typedef", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx));
write!(
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "union", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
render_union(w, it, Some(&s.generics), &s.fields, "", cx);
});
let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_into_item_decl(w, |w| {
- wrap_item(w, "enum", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
let name = it.name.expect("proc-macros always have names");
match m.kind {
MacroKind::Bang => {
- wrap_item(w, "macro", |w| {
+ wrap_item(w, |w| {
write!(w, "{}!() {{ /* proc-macro */ }}", name);
});
}
MacroKind::Attr => {
- wrap_item(w, "attr", |w| {
+ wrap_item(w, |w| {
write!(w, "#[{}]", name);
});
}
MacroKind::Derive => {
- wrap_item(w, "derive", |w| {
+ wrap_item(w, |w| {
write!(w, "#[derive({})]", name);
if !m.helpers.is_empty() {
w.push_str("\n{\n");
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "const", |w| {
+ wrap_item(w, |w| {
let tcx = cx.tcx();
render_attributes_in_code(w, it);
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "struct", |w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it);
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "static", |w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it);
write!(
w,
fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "foreigntype", |w| {
+ wrap_item(w, |w| {
w.write_str("extern {\n");
render_attributes_in_code(w, it);
write!(
w.write_str("</div>")
}
-fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F)
+fn wrap_item<F>(w: &mut Buffer, f: F)
where
F: FnOnce(&mut Buffer),
{
- w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name));
+ w.write_str(r#"<pre class="rust"><code>"#);
f(w);
w.write_str("</code></pre>");
}
// Attach all orphan items to the type's definition if the type
// has since been learned.
for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
- if let Some(&(ref fqp, _)) = cache.paths.get(&parent) {
+ if let Some((fqp, _)) = cache.paths.get(&parent) {
let desc = item
.doc_value()
.map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
cache.search_index.push(IndexItem {
ty: item.type_(),
- name: item.name.unwrap().to_string(),
+ name: item.name.unwrap(),
path: join_with_double_colon(&fqp[..fqp.len() - 1]),
desc,
parent: Some(parent),
// Sort search index items. This improves the compressibility of the search index.
cache.search_index.sort_unstable_by(|k1, k2| {
// `sort_unstable_by_key` produces lifetime errors
- let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
- let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
+ let k1 = (&k1.path, k1.name.as_str(), &k1.ty, &k1.parent);
+ let k2 = (&k2.path, k2.name.as_str(), &k2.ty, &k2.parent);
std::cmp::Ord::cmp(&k1, &k2)
});
)?;
crate_data.serialize_field(
"n",
- &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
+ &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
)?;
crate_data.serialize_field(
"q",
)?;
crate_data.serialize_field(
"p",
- &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(),
+ &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
let decl = &func.decl;
let combined_generics;
- let (self_, generics) = if let Some(&(ref impl_self, ref impl_generics)) = impl_generics {
+ let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics {
match (impl_generics.is_empty(), func.generics.is_empty()) {
(true, _) => (Some(impl_self), &func.generics),
(_, true) => (Some(impl_self), impl_generics),
Ok((ret, krates))
}
- /// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
+ /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
/// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
///
/// This forms the payload of files that look like this:
padding-left: 24px;
}
-.sidebar a, .sidebar .current {
+.sidebar a {
color: var(--sidebar-link-color);
}
.sidebar .current,
.rustdoc .example-wrap > pre {
margin: 0;
flex-grow: 1;
- overflow-x: auto;
+ overflow: auto hidden;
}
.rustdoc .example-wrap > pre.example-line-numbers,
0 -1px 0 black;
}
-.module-item.unstable,
-.import-item.unstable {
+.item-left.unstable {
opacity: 0.65;
}
content: "\00a0";
}
-.notable .docblock {
+.notable .content {
margin: 0.25em 0.5em;
}
-.notable .docblock pre, .notable .docblock code {
+.notable .content pre, .notable .content code {
background: transparent;
margin: 0;
padding: 0;
white-space: pre-wrap;
}
+.notable .content > h3:first-child {
+ margin: 0 0 5px 0;
+}
+
.search-failed {
text-align: center;
margin-top: 20px;
right: 0.25em;
}
-.scraped-example:not(.expanded) .code-wrapper:before,
-.scraped-example:not(.expanded) .code-wrapper:after {
+.scraped-example:not(.expanded) .code-wrapper::before,
+.scraped-example:not(.expanded) .code-wrapper::after {
content: " ";
width: 100%;
height: 5px;
position: absolute;
z-index: 1;
}
-.scraped-example:not(.expanded) .code-wrapper:before {
+.scraped-example:not(.expanded) .code-wrapper::before {
top: 0;
+ background: linear-gradient(to bottom,
+ var(--scrape-example-code-wrapper-background-start),
+ var(--scrape-example-code-wrapper-background-end));
}
-.scraped-example:not(.expanded) .code-wrapper:after {
+.scraped-example:not(.expanded) .code-wrapper::after {
bottom: 0;
+ background: linear-gradient(to top,
+ var(--scrape-example-code-wrapper-background-start),
+ var(--scrape-example-code-wrapper-background-end));
}
.scraped-example .code-wrapper .example-wrap {
.setting-line {
- margin: 0.6em 0 0.6em 0.3em;
+ margin: 1.2em 0.6em;
position: relative;
}
-.setting-line .choices {
- display: flex;
- flex-wrap: wrap;
-}
-
-.setting-line .radio-line input,
-.setting-line .settings-toggle input {
+.setting-radio input, .setting-check input {
margin-right: 0.3em;
height: 1.2rem;
width: 1.2rem;
-webkit-appearance: none;
cursor: pointer;
}
-.setting-line .radio-line input {
+.setting-radio input {
border-radius: 50%;
}
-.setting-line .settings-toggle input:checked {
+.setting-check input:checked {
content: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">\
<path d="M7,25L17,32L33,12" fill="none" stroke="black" stroke-width="5"/>\
<path d="M7,23L17,30L33,10" fill="none" stroke="white" stroke-width="5"/></svg>');
}
-.setting-line .radio-line input + span,
-.setting-line .settings-toggle span {
+.setting-radio span, .setting-check span {
padding-bottom: 1px;
}
-.radio-line .setting-name {
- width: 100%;
-}
-
-.radio-line .choice {
+.setting-radio {
margin-top: 0.1em;
margin-bottom: 0.1em;
min-width: 3.8em;
padding: 0.3em;
- display: flex;
+ display: inline-flex;
align-items: center;
cursor: pointer;
}
-.radio-line .choice + .choice {
+.setting-radio + .setting-radio {
margin-left: 0.5em;
}
-.settings-toggle {
+.setting-check {
position: relative;
width: 100%;
margin-right: 20px;
cursor: pointer;
}
-#settings .setting-line {
- margin: 1.2em 0.6em;
-}
-
-.setting-line .radio-line input:checked {
+.setting-radio input:checked {
box-shadow: inset 0 0 0 3px var(--main-background-color);
background-color: var(--settings-input-color);
}
-.setting-line .settings-toggle input:checked {
+.setting-check input:checked {
background-color: var(--settings-input-color);
}
-.setting-line .radio-line input:focus,
-.setting-line .settings-toggle input:focus {
+.setting-radio input:focus, .setting-check input:focus {
box-shadow: 0 0 1px 1px var(--settings-input-color);
}
/* In here we combine both `:focus` and `:checked` properties. */
-.setting-line .radio-line input:checked:focus {
+.setting-radio input:checked:focus {
box-shadow: inset 0 0 0 3px var(--main-background-color),
0 0 2px 2px var(--settings-input-color);
}
-.setting-line .radio-line input:hover,
-.setting-line .settings-toggle input:hover {
+.setting-radio input:hover, .setting-check input:hover {
border-color: var(--settings-input-color) !important;
}
--scrape-example-help-color: #eee;
--scrape-example-help-hover-border-color: #fff;
--scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(15, 20, 25, 1);
+ --scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
}
h1, h2, h3, h4 {
#source-sidebar div.files > a.selected {
color: #ffb44c;
}
-
-.scraped-example:not(.expanded) .code-wrapper::before {
- background: linear-gradient(to bottom, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
-}
-.scraped-example:not(.expanded) .code-wrapper::after {
- background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
-}
--scrape-example-help-color: #eee;
--scrape-example-help-hover-border-color: #fff;
--scrape-example-help-hover-color: #fff;
+ --scrape-example-code-wrapper-background-start: rgba(53, 53, 53, 1);
+ --scrape-example-code-wrapper-background-end: rgba(53, 53, 53, 0);
}
#search-tabs > button:not(.selected) {
border-top-color: #0089ff;
background-color: #353535;
}
-
-.scraped-example:not(.expanded) .code-wrapper::before {
- background: linear-gradient(to bottom, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
-}
-.scraped-example:not(.expanded) .code-wrapper::after {
- background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
-}
--scrape-example-help-color: #333;
--scrape-example-help-hover-border-color: #000;
--scrape-example-help-hover-color: #000;
+ --scrape-example-code-wrapper-background-start: rgba(255, 255, 255, 1);
+ --scrape-example-code-wrapper-background-end: rgba(255, 255, 255, 0);
}
#search-tabs > button:not(.selected) {
background-color: #ffffff;
border-top-color: #0089ff;
}
-
-.scraped-example:not(.expanded) .code-wrapper::before {
- background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
-}
-.scraped-example:not(.expanded) .code-wrapper::after {
- background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
-}
}
if (document.activeElement.tagName === "INPUT" &&
- document.activeElement.type !== "checkbox") {
+ document.activeElement.type !== "checkbox" &&
+ document.activeElement.type !== "radio") {
switch (getVirtualKey(ev)) {
case "Escape":
handleEscape(ev);
}
});
- function handleClick(id, f) {
- const elem = document.getElementById(id);
- if (elem) {
- elem.addEventListener("click", f);
- }
+ const mainElem = document.getElementById(MAIN_ID);
+ if (mainElem) {
+ mainElem.addEventListener("click", hideSidebar);
}
- handleClick(MAIN_ID, () => {
- hideSidebar();
- });
onEachLazy(document.querySelectorAll("a[href^='#']"), el => {
// For clicks on internal links (<A> tags with a hash property), we expand the section we're
window.hideAllModals(false);
const ty = e.getAttribute("data-ty");
const wrapper = document.createElement("div");
- wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
+ wrapper.innerHTML = "<div class=\"content\">" + window.NOTABLE_TRAITS[ty] + "</div>";
wrapper.className = "notable popover";
const focusCatcher = document.createElement("div");
focusCatcher.setAttribute("tabindex", "0");
return;
}
if (!this.NOTABLE_FORCE_VISIBLE &&
- !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
+ !elemIsInParent(ev.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
hideNotable(true);
}
};
* Show the help popup menu.
*/
function showHelp() {
+ // Prevent `blur` events from being dispatched as a result of closing
+ // other modals.
+ getHelpButton().querySelector("a").focus();
const menu = getHelpMenu(true);
if (menu.style.display === "none") {
window.hideAllModals();
(function() {
let reset_button_timeout = null;
- window.copy_path = but => {
+ const but = document.getElementById("copy-path");
+ if (!but) {
+ return;
+ }
+ but.onclick = () => {
const parent = but.parentElement;
const path = [];
return a - b;
}
- // Sort by non levenshtein results and then levenshtein results by the distance
+ // sort by index of keyword in item name (no literal occurrence goes later)
+ a = (aaa.index < 0);
+ b = (bbb.index < 0);
+ if (a !== b) {
+ return a - b;
+ }
+
+ // Sort by distance in the path part, if specified
+ // (less changes required to match means higher rankings)
+ a = aaa.path_lev;
+ b = bbb.path_lev;
+ if (a !== b) {
+ return a - b;
+ }
+
+ // (later literal occurrence, if any, goes later)
+ a = aaa.index;
+ b = bbb.index;
+ if (a !== b) {
+ return a - b;
+ }
+
+ // Sort by distance in the name part, the last part of the path
// (less changes required to match means higher rankings)
a = (aaa.lev);
b = (bbb.lev);
return (a > b ? +1 : -1);
}
- // sort by index of keyword in item name (no literal occurrence goes later)
- a = (aaa.index < 0);
- b = (bbb.index < 0);
- if (a !== b) {
- return a - b;
- }
- // (later literal occurrence, if any, goes later)
- a = aaa.index;
- b = bbb.index;
- if (a !== b) {
- return a - b;
- }
-
// special precedence for primitive and keyword pages
if ((aaa.item.ty === TY_PRIMITIVE && bbb.item.ty !== TY_KEYWORD) ||
(aaa.item.ty === TY_KEYWORD && bbb.item.ty !== TY_PRIMITIVE)) {
* * `id` is the index in both `searchWords` and `searchIndex` arrays for this element.
* * `index` is an `integer`` used to sort by the position of the word in the item's name.
* * `lev` is the main metric used to sort the search results.
+ * * `path_lev` is zero if a single-component search query is used, otherwise it's the
+ * distance computed for everything other than the last path component.
*
* @param {Results} results
* @param {string} fullId
* @param {integer} id
* @param {integer} index
* @param {integer} lev
+ * @param {integer} path_lev
*/
- function addIntoResults(results, fullId, id, index, lev) {
- if (lev === 0 || (!parsedQuery.literalSearch && lev <= MAX_LEV_DISTANCE)) {
+ function addIntoResults(results, fullId, id, index, lev, path_lev) {
+ const inBounds = lev <= MAX_LEV_DISTANCE || index !== -1;
+ if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) {
if (results[fullId] !== undefined) {
const result = results[fullId];
if (result.dontValidate || result.lev <= lev) {
index: index,
dontValidate: parsedQuery.literalSearch,
lev: lev,
+ path_lev: path_lev,
};
}
}
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
return;
}
- let lev, lev_add = 0, index = -1;
+ let lev, index = -1, path_lev = 0;
const fullId = row.id;
+ const searchWord = searchWords[pos];
const in_args = findArg(row, elem, parsedQuery.typeFilter);
const returned = checkReturned(row, elem, parsedQuery.typeFilter);
- addIntoResults(results_in_args, fullId, pos, index, in_args);
- addIntoResults(results_returned, fullId, pos, index, returned);
+ // path_lev is 0 because no parent path information is currently stored
+ // in the search index
+ addIntoResults(results_in_args, fullId, pos, -1, in_args, 0);
+ addIntoResults(results_returned, fullId, pos, -1, returned, 0);
if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
return;
}
- const searchWord = searchWords[pos];
- if (parsedQuery.literalSearch) {
- if (searchWord === elem.name) {
- addIntoResults(results_others, fullId, pos, -1, 0);
- }
- return;
+ const row_index = row.normalizedName.indexOf(elem.pathLast);
+ const word_index = searchWord.indexOf(elem.pathLast);
+
+ // lower indexes are "better" matches
+ // rank based on the "best" match
+ if (row_index === -1) {
+ index = word_index;
+ } else if (word_index === -1) {
+ index = row_index;
+ } else if (word_index < row_index) {
+ index = word_index;
+ } else {
+ index = row_index;
}
// No need to check anything else if it's a "pure" generics search.
if (elem.name.length === 0) {
if (row.type !== null) {
lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1);
- addIntoResults(results_others, fullId, pos, index, lev);
+ // path_lev is 0 because we know it's empty
+ addIntoResults(results_others, fullId, pos, index, lev, 0);
}
return;
}
if (elem.fullPath.length > 1) {
- lev = checkPath(elem.pathWithoutLast, row);
- if (lev > MAX_LEV_DISTANCE || (parsedQuery.literalSearch && lev !== 0)) {
+ path_lev = checkPath(elem.pathWithoutLast, row);
+ if (path_lev > MAX_LEV_DISTANCE) {
return;
- } else if (lev > 0) {
- lev_add = lev / 10;
}
}
- if (searchWord.indexOf(elem.pathLast) > -1 ||
- row.normalizedName.indexOf(elem.pathLast) > -1
- ) {
- index = row.normalizedName.indexOf(elem.pathLast);
- }
- lev = levenshtein(searchWord, elem.pathLast);
- if (lev > 0 && elem.pathLast.length > 2 && searchWord.indexOf(elem.pathLast) > -1) {
- if (elem.pathLast.length < 6) {
- lev = 1;
- } else {
- lev = 0;
+ if (parsedQuery.literalSearch) {
+ if (searchWord === elem.name) {
+ addIntoResults(results_others, fullId, pos, index, 0, path_lev);
}
- }
- lev += lev_add;
- if (lev > MAX_LEV_DISTANCE) {
return;
- } else if (index !== -1 && elem.fullPath.length < 2) {
- lev -= 1;
}
- if (lev < 0) {
- lev = 0;
+
+ lev = levenshtein(searchWord, elem.pathLast);
+
+ if (index === -1 && lev + path_lev > MAX_LEV_DISTANCE) {
+ return;
}
- addIntoResults(results_others, fullId, pos, index, lev);
+
+ addIntoResults(results_others, fullId, pos, index, lev, path_lev);
}
/**
return;
}
const lev = Math.round(totalLev / nbLev);
- addIntoResults(results, row.id, pos, 0, lev);
+ addIntoResults(results, row.id, pos, 0, lev, 0);
}
function innerRunQuery() {
}
function showLightAndDark() {
- removeClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
- removeClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
+ removeClass(document.getElementById("preferred-light-theme"), "hidden");
+ removeClass(document.getElementById("preferred-dark-theme"), "hidden");
}
function hideLightAndDark() {
- addClass(document.getElementById("preferred-light-theme").parentElement, "hidden");
- addClass(document.getElementById("preferred-dark-theme").parentElement, "hidden");
+ addClass(document.getElementById("preferred-light-theme"), "hidden");
+ addClass(document.getElementById("preferred-dark-theme"), "hidden");
}
function updateLightAndDark() {
toggle.onkeyup = handleKey;
toggle.onkeyrelease = handleKey;
});
- onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), elem => {
- const select = elem.getElementsByTagName("select")[0];
- const settingId = select.id;
- const settingValue = getSettingValue(settingId);
- if (settingValue !== null) {
- select.value = settingValue;
- }
- select.onchange = function() {
- changeSetting(this.id, this.value);
- };
- });
onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), elem => {
const settingId = elem.name;
let settingValue = getSettingValue(settingId);
let output = "";
for (const setting of settings) {
- output += "<div class=\"setting-line\">";
const js_data_name = setting["js_name"];
const setting_name = setting["name"];
if (setting["options"] !== undefined) {
// This is a select setting.
output += `\
-<div class="radio-line" id="${js_data_name}">
- <span class="setting-name">${setting_name}</span>
-<div class="choices">`;
+<div class="setting-line" id="${js_data_name}">
+ <div class="setting-radio-name">${setting_name}</div>
+ <div class="setting-radio-choices">`;
onEach(setting["options"], option => {
const checked = option === setting["default"] ? " checked" : "";
const full = `${js_data_name}-${option.replace(/ /g,"-")}`;
output += `\
-<label for="${full}" class="choice">
- <input type="radio" name="${js_data_name}"
- id="${full}" value="${option}"${checked}>
- <span>${option}</span>
-</label>`;
+ <label for="${full}" class="setting-radio">
+ <input type="radio" name="${js_data_name}"
+ id="${full}" value="${option}"${checked}>
+ <span>${option}</span>
+ </label>`;
});
- output += "</div></div>";
+ output += `\
+ </div>
+</div>`;
} else {
// This is a checkbox toggle.
const checked = setting["default"] === true ? " checked" : "";
output += `\
-<label class="settings-toggle">\
- <input type="checkbox" id="${js_data_name}"${checked}>\
- <span class="label">${setting_name}</span>\
-</label>`;
+<div class="setting-line">\
+ <label class="setting-check">\
+ <input type="checkbox" id="${js_data_name}"${checked}>\
+ <span>${setting_name}</span>\
+ </label>\
+</div>`;
}
- output += "</div>";
}
return output;
}
<a href="{{component.path|safe}}index.html">{{component.name}}</a>::<wbr>
{%- endfor -%}
<a class="{{item_type}}" href="#">{{name}}</a> {#- -#}
- <button id="copy-path" onclick="copy_path(this)" title="Copy item path to clipboard"> {#- -#}
+ <button id="copy-path" title="Copy item path to clipboard"> {#- -#}
<img src="{{static_root_path|safe}}{{clipboard_svg}}" {# -#}
width="19" height="18" {# -#}
alt="Copy item path"> {#- -#}
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::DefIdSet;
use crate::{
clean::{self, Import, ImportSource, Item},
/// See [#100973](https://github.com/rust-lang/rust/issues/100973) and
/// [#101103](https://github.com/rust-lang/rust/issues/101103) for times when
/// this information is needed.
-pub(crate) fn get_imports(krate: clean::Crate) -> (clean::Crate, FxHashSet<DefId>) {
- let mut finder = ImportFinder { imported: FxHashSet::default() };
+pub(crate) fn get_imports(krate: clean::Crate) -> (clean::Crate, DefIdSet) {
+ let mut finder = ImportFinder::default();
let krate = finder.fold_crate(krate);
(krate, finder.imported)
}
+#[derive(Default)]
struct ImportFinder {
- imported: FxHashSet<DefId>,
+ imported: DefIdSet,
}
impl DocFolder for ImportFinder {
use std::path::PathBuf;
use std::rc::Rc;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::DefId;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::def_id::LOCAL_CRATE;
/// The directory where the blob will be written to.
out_path: PathBuf,
cache: Rc<Cache>,
- imported_items: FxHashSet<DefId>,
+ imported_items: DefIdSet,
}
impl<'tcx> JsonRenderer<'tcx> {
#![feature(array_methods)]
#![feature(assert_matches)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(drain_filter)]
#![feature(is_terminal)]
#![feature(let_chains)]
sess.fatal("Compilation failed, aborting rustdoc");
}
- let global_ctxt = abort_on_err(queries.global_ctxt(), sess);
+ let mut global_ctxt = abort_on_err(queries.global_ctxt(), sess);
global_ctxt.enter(|tcx| {
let (krate, render_opts, mut cache) = sess.time("run_global_ctxt", || {
);
let has_doc_example = tests.found_tests != 0;
- // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
- // would presumably panic if a fake `DefIndex` were passed.
- let hir_id = self
- .ctx
- .tcx
- .hir()
- .local_def_id_to_hir_id(i.item_id.expect_def_id().expect_local());
+ let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
// In case we have:
use crate::visit_ast::inherits_doc_hidden;
use rustc_hir as hir;
use rustc_middle::lint::LintLevelSource;
+use rustc_middle::ty::DefIdTree;
use rustc_session::lint;
-use rustc_span::symbol::sym;
pub(crate) const CHECK_DOC_TEST_VISIBILITY: Pass = Pass {
name: "check_doc_test_visibility",
// The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
// would presumably panic if a fake `DefIndex` were passed.
- let hir_id = cx.tcx.hir().local_def_id_to_hir_id(item.item_id.expect_def_id().expect_local());
+ let def_id = item.item_id.expect_def_id().expect_local();
// check if parent is trait impl
- if let Some(parent_hir_id) = cx.tcx.hir().opt_parent_id(hir_id) {
- if let Some(parent_node) = cx.tcx.hir().find(parent_hir_id) {
+ if let Some(parent_def_id) = cx.tcx.opt_local_parent(def_id) {
+ if let Some(parent_node) = cx.tcx.hir().find_by_def_id(parent_def_id) {
if matches!(
parent_node,
hir::Node::Item(hir::Item {
}
}
- if cx.tcx.hir().attrs(hir_id).lists(sym::doc).has_word(sym::hidden)
- || inherits_doc_hidden(cx.tcx, hir_id)
- || cx.tcx.hir().span(hir_id).in_derive_expansion()
+ if cx.tcx.is_doc_hidden(def_id.to_def_id())
+ || inherits_doc_hidden(cx.tcx, def_id)
+ || cx.tcx.def_span(def_id.to_def_id()).in_derive_expansion()
{
return false;
}
- let (level, source) = cx.tcx.lint_level_at_node(crate::lint::MISSING_DOC_CODE_EXAMPLES, hir_id);
+ let (level, source) = cx.tcx.lint_level_at_node(
+ crate::lint::MISSING_DOC_CODE_EXAMPLES,
+ cx.tcx.hir().local_def_id_to_hir_id(def_id),
+ );
level != lint::Level::Allow || matches!(source, LintLevelSource::Default)
}
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(_)
+ | ty::GeneratorWitnessMIR(..)
| ty::Dynamic(..)
| ty::Param(_)
| ty::Bound(..)
tcx.find_map_relevant_impl(trait_, ty, |impl_| {
let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
// Check if these are the same type.
- let impl_type = trait_ref.self_ty();
+ let impl_type = trait_ref.skip_binder().self_ty();
trace!(
"comparing type {} with kind {:?} against type {:?}",
impl_type,
}
// item can be non-local e.g. when using #[doc(primitive = "pointer")]
- if let Some((src_id, dst_id)) = id
- .as_local()
- // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
- // would presumably panic if a fake `DefIndex` were passed.
- .and_then(|dst_id| {
- item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
- })
- {
+ if let Some((src_id, dst_id)) = id.as_local().and_then(|dst_id| {
+ item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
+ }) {
if self.cx.tcx.effective_visibilities(()).is_exported(src_id)
&& !self.cx.tcx.effective_visibilities(()).is_exported(dst_id)
{
use crate::core::ResolverCaches;
use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
+use crate::visit_lib::early_lib_embargo_visit_item;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{self as ast, ItemKind};
traits_in_scope: Default::default(),
all_trait_impls: Default::default(),
all_macro_rules: Default::default(),
+ extern_doc_reachable: Default::default(),
+ local_doc_reachable: Default::default(),
document_private_items,
};
traits_in_scope: link_resolver.traits_in_scope,
all_trait_impls: Some(link_resolver.all_trait_impls),
all_macro_rules: link_resolver.all_macro_rules,
+ extern_doc_reachable: link_resolver.extern_doc_reachable,
}
}
traits_in_scope: DefIdMap<Vec<TraitCandidate>>,
all_trait_impls: Vec<DefId>,
all_macro_rules: FxHashMap<Symbol, Res<ast::NodeId>>,
+ /// This set is used as a seed for `effective_visibilities`, which are then extended by some
+ /// more items using `lib_embargo_visit_item` during doc inlining.
+ extern_doc_reachable: DefIdSet,
+ /// This is an easily identifiable superset of items added to `effective_visibilities`
+ /// using `lib_embargo_visit_item` during doc inlining.
+ /// The union of `(extern,local)_doc_reachable` is therefore a superset of
+ /// `effective_visibilities` and can be used for pruning extern impls here
+ /// in early doc link resolution.
+ local_doc_reachable: DefIdSet,
document_private_items: bool,
}
}
}
+ fn is_doc_reachable(&self, def_id: DefId) -> bool {
+ self.extern_doc_reachable.contains(&def_id) || self.local_doc_reachable.contains(&def_id)
+ }
+
/// Add traits in scope for links in impls collected by the `collect-intra-doc-links` pass.
/// That pass filters impls using type-based information, but we don't yet have such
/// information here, so we just conservatively calculate traits in scope for *all* modules
let mut start_cnum = 0;
loop {
let crates = Vec::from_iter(self.resolver.cstore().crates_untracked());
+ for cnum in &crates[start_cnum..] {
+ early_lib_embargo_visit_item(
+ self.resolver,
+ &mut self.extern_doc_reachable,
+ cnum.as_def_id(),
+ true,
+ );
+ }
for &cnum in &crates[start_cnum..] {
let all_trait_impls =
Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
// privacy, private traits and impls from other crates are never documented in
// the current crate, and links in their doc comments are not resolved.
for &(trait_def_id, impl_def_id, simplified_self_ty) in &all_trait_impls {
- if self.resolver.cstore().visibility_untracked(trait_def_id).is_public()
- && simplified_self_ty.and_then(|ty| ty.def()).map_or(true, |ty_def_id| {
- self.resolver.cstore().visibility_untracked(ty_def_id).is_public()
- })
+ if self.is_doc_reachable(trait_def_id)
+ && simplified_self_ty
+ .and_then(|ty| ty.def())
+ .map_or(true, |ty_def_id| self.is_doc_reachable(ty_def_id))
{
if self.visited_mods.insert(trait_def_id) {
self.resolve_doc_links_extern_impl(trait_def_id, false);
}
self.resolve_doc_links_extern_impl(impl_def_id, false);
}
+ self.all_trait_impls.push(impl_def_id);
}
for (ty_def_id, impl_def_id) in all_inherent_impls {
- if self.resolver.cstore().visibility_untracked(ty_def_id).is_public() {
+ if self.is_doc_reachable(ty_def_id) {
self.resolve_doc_links_extern_impl(impl_def_id, true);
}
}
for impl_def_id in all_incoherent_impls {
self.resolve_doc_links_extern_impl(impl_def_id, true);
}
-
- self.all_trait_impls
- .extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
}
if crates.len() > start_cnum {
&& module_id.is_local()
{
if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
+ self.local_doc_reachable.insert(def_id);
let scope_id = match child.res {
Res::Def(
DefKind::Variant
use crate::formats::cache::Cache;
use crate::visit::DocVisitor;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
use rustc_middle::ty::{self, DefIdTree};
use rustc_span::symbol::sym;
});
let mut cleaner = BadImplStripper { prims, items: crate_items, cache: &cx.cache };
- let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
+ let mut type_did_to_deref_target: DefIdMap<&Type> = DefIdMap::default();
// Follow all `Deref` targets of included items and recursively add them as valid
fn add_deref_target(
cx: &DocContext<'_>,
- map: &FxHashMap<DefId, &Type>,
+ map: &DefIdMap<&Type>,
cleaner: &mut BadImplStripper<'_>,
- targets: &mut FxHashSet<DefId>,
+ targets: &mut DefIdSet,
type_did: DefId,
) {
if let Some(target) = map.get(&type_did) {
// `Deref` target type and the impl for type positions, this map of types is keyed by
// `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
if cleaner.keep_impl_with_def_id(for_did.into()) {
- let mut targets = FxHashSet::default();
+ let mut targets = DefIdSet::default();
targets.insert(for_did);
add_deref_target(
cx,
pub(crate) fn visit_item(cx: &DocContext<'_>, item: &clean::Item) {
if let Some(dox) = &item.attrs.collapsed_doc_value() {
let sp = item.attr_span(cx.tcx);
- let extra =
- crate::html::markdown::ExtraInfo::new_did(cx.tcx, item.item_id.expect_def_id(), sp);
+ let extra = crate::html::markdown::ExtraInfo::new(cx.tcx, item.item_id.expect_def_id(), sp);
for code_block in markdown::rust_code_blocks(dox, &extra) {
check_rust_syntax(cx, item, dox, code_block);
}
return;
};
- let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
let empty_block = code_block.lang_string == Default::default() && code_block.is_fenced;
let is_ignore = code_block.lang_string.ignore != markdown::Ignore::None;
// Finally build and emit the completed diagnostic.
// All points of divergence have been handled earlier so this can be
// done the same way whether the span is precise or not.
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
cx.tcx.struct_span_lint_hir(crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, msg, |lint| {
let explanation = if is_ignore {
"`ignore` code blocks require valid Rust code for syntax highlighting; \
use crate::passes::Pass;
use rustc_hir::def_id::LocalDefId;
+use rustc_middle::ty::DefIdTree;
pub(crate) const PROPAGATE_DOC_CFG: Pass = Pass {
name: "propagate-doc-cfg",
let Some(def_id) = item.item_id.as_def_id().and_then(|def_id| def_id.as_local())
else { return };
- let hir = self.cx.tcx.hir();
- let hir_id = hir.local_def_id_to_hir_id(def_id);
-
if check_parent {
- let expected_parent = hir.get_parent_item(hir_id);
+ let expected_parent = self.cx.tcx.opt_local_parent(def_id);
// If parents are different, it means that `item` is a reexport and we need
// to compute the actual `cfg` by iterating through its "real" parents.
- if self.parent == Some(expected_parent.def_id) {
+ if self.parent.is_some() && self.parent == expected_parent {
return;
}
}
let mut attrs = Vec::new();
- for (parent_hir_id, _) in hir.parent_iter(hir_id) {
- if let Some(def_id) = hir.opt_local_def_id(parent_hir_id) {
- attrs.extend_from_slice(load_attrs(self.cx, def_id.to_def_id()));
- }
+ let mut next_def_id = def_id;
+ while let Some(parent_def_id) = self.cx.tcx.opt_local_parent(next_def_id) {
+ attrs.extend_from_slice(load_attrs(self.cx, parent_def_id.to_def_id()));
+ next_def_id = parent_def_id;
}
+
let (_, cfg) = merge_attrs(self.cx, None, item.attrs.other_attrs.as_slice(), Some(&attrs));
item.cfg = cfg;
}
//! The Rust AST Visitor. Extracts useful information and massages it into a form
//! usable for `clean`.
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{walk_item, Visitor};
-use rustc_hir::Node;
-use rustc_hir::CRATE_HIR_ID;
-use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::TyCtxt;
+use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet};
+use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_middle::ty::{DefIdTree, TyCtxt};
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
pub(crate) name: Symbol,
pub(crate) where_inner: Span,
pub(crate) mods: Vec<Module<'hir>>,
- pub(crate) id: hir::HirId,
+ pub(crate) def_id: LocalDefId,
// (item, renamed, import_id)
- pub(crate) items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>, Option<hir::HirId>)>,
+ pub(crate) items: Vec<(&'hir hir::Item<'hir>, Option<Symbol>, Option<LocalDefId>)>,
pub(crate) foreigns: Vec<(&'hir hir::ForeignItem<'hir>, Option<Symbol>)>,
}
impl Module<'_> {
- pub(crate) fn new(name: Symbol, id: hir::HirId, where_inner: Span) -> Self {
- Module { name, id, where_inner, mods: Vec::new(), items: Vec::new(), foreigns: Vec::new() }
+ pub(crate) fn new(name: Symbol, def_id: LocalDefId, where_inner: Span) -> Self {
+ Module {
+ name,
+ def_id,
+ where_inner,
+ mods: Vec::new(),
+ items: Vec::new(),
+ foreigns: Vec::new(),
+ }
}
pub(crate) fn where_outer(&self, tcx: TyCtxt<'_>) -> Span {
- tcx.hir().span(self.id)
+ tcx.def_span(self.def_id)
}
}
std::iter::once(crate_name).chain(relative).collect()
}
-pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool {
- while let Some(id) = tcx.hir().get_enclosing_scope(node) {
+pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: LocalDefId) -> bool {
+ while let Some(id) = tcx.opt_local_parent(node) {
node = id;
- if tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) {
+ if tcx.is_doc_hidden(node.to_def_id()) {
return true;
}
}
false
}
+// Also, is there some reason that this doesn't use the 'visit'
+// framework from syntax?.
+
pub(crate) struct RustdocVisitor<'a, 'tcx> {
cx: &'a mut core::DocContext<'tcx>,
- view_item_stack: FxHashSet<hir::HirId>,
+ view_item_stack: LocalDefIdSet,
inlining: bool,
/// Are the current module and all of its parents public?
inside_public_path: bool,
- exact_paths: FxHashMap<DefId, Vec<Symbol>>,
- modules: Vec<Module<'tcx>>,
+ exact_paths: DefIdMap<Vec<Symbol>>,
}
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
pub(crate) fn new(cx: &'a mut core::DocContext<'tcx>) -> RustdocVisitor<'a, 'tcx> {
// If the root is re-exported, terminate all recursion.
- let mut stack = FxHashSet::default();
- stack.insert(hir::CRATE_HIR_ID);
- let om = Module::new(
- cx.tcx.crate_name(LOCAL_CRATE),
- hir::CRATE_HIR_ID,
- cx.tcx.hir().root_module().spans.inner_span,
- );
-
+ let mut stack = LocalDefIdSet::default();
+ stack.insert(CRATE_DEF_ID);
RustdocVisitor {
cx,
view_item_stack: stack,
inlining: false,
inside_public_path: true,
- exact_paths: FxHashMap::default(),
- modules: vec![om],
+ exact_paths: Default::default(),
}
}
}
pub(crate) fn visit(mut self) -> Module<'tcx> {
- let root_module = self.cx.tcx.hir().root_module();
- self.visit_mod_contents(CRATE_HIR_ID, root_module);
-
- let mut top_level_module = self.modules.pop().unwrap();
+ let mut top_level_module = self.visit_mod_contents(
+ CRATE_DEF_ID,
+ self.cx.tcx.hir().root_module(),
+ self.cx.tcx.crate_name(LOCAL_CRATE),
+ None,
+ );
// `#[macro_export] macro_rules!` items are reexported at the top level of the
// crate, regardless of where they're defined. We want to document the
// macro in the same module.
let mut inserted = FxHashSet::default();
for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) {
- if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
- let Some(local_def_id) = def_id.as_local() &&
- self.cx.tcx.has_attr(def_id, sym::macro_export) &&
- inserted.insert(def_id)
- {
- let item = self.cx.tcx.hir().expect_item(local_def_id);
- top_level_module.items.push((item, None, None));
+ if let Res::Def(DefKind::Macro(_), def_id) = export.res {
+ if let Some(local_def_id) = def_id.as_local() {
+ if self.cx.tcx.has_attr(def_id, sym::macro_export) {
+ if inserted.insert(def_id) {
+ let item = self.cx.tcx.hir().expect_item(local_def_id);
+ top_level_module.items.push((item, None, None));
+ }
+ }
+ }
}
}
top_level_module
}
- /// This method will go through the given module items in two passes:
- /// 1. The items which are not glob imports/reexports.
- /// 2. The glob imports/reexports.
- fn visit_mod_contents(&mut self, id: hir::HirId, m: &'tcx hir::Mod<'tcx>) {
- debug!("Going through module {:?}", m);
- let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
+ fn visit_mod_contents(
+ &mut self,
+ def_id: LocalDefId,
+ m: &'tcx hir::Mod<'tcx>,
+ name: Symbol,
+ parent_id: Option<LocalDefId>,
+ ) -> Module<'tcx> {
+ let mut om = Module::new(name, def_id, m.spans.inner_span);
// Keep track of if there were any private modules in the path.
let orig_inside_public_path = self.inside_public_path;
- self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
-
- // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in
- // the second loop):
+ self.inside_public_path &= self.cx.tcx.local_visibility(def_id).is_public();
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
- if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- self.visit_item(item);
+ if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
+ continue;
}
+ self.visit_item(item, None, &mut om, parent_id);
}
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
// Later passes in rustdoc will de-duplicate by name and kind, so if glob-
// imported items appear last, then they'll be the ones that get discarded.
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- self.visit_item(item);
+ self.visit_item(item, None, &mut om, parent_id);
}
}
self.inside_public_path = orig_inside_public_path;
+ om
}
/// Tries to resolve the target of a `pub use` statement and inlines the
/// Returns `true` if the target has been inlined.
fn maybe_inline_local(
&mut self,
- id: hir::HirId,
+ def_id: LocalDefId,
res: Res,
renamed: Option<Symbol>,
glob: bool,
+ om: &mut Module<'tcx>,
please_inline: bool,
) -> bool {
debug!("maybe_inline_local res: {:?}", res);
return false;
};
- let use_attrs = tcx.hir().attrs(id);
+ let use_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
// Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
- || use_attrs.lists(sym::doc).has_word(sym::hidden);
+ || tcx.is_doc_hidden(def_id.to_def_id());
// For cross-crate impl inlining we need to know whether items are
// reachable in documentation -- a previously unreachable item can be
return false;
}
- let res_hir_id = match res_did.as_local() {
- Some(n) => tcx.hir().local_def_id_to_hir_id(n),
- None => return false,
+ let Some(res_did) = res_did.as_local() else {
+ return false;
};
- let is_private =
- !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, res_did);
- let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);
+ let is_private = !self
+ .cx
+ .cache
+ .effective_visibilities
+ .is_directly_public(self.cx.tcx, res_did.to_def_id());
+ let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did);
// Only inline if requested or if the item would otherwise be stripped.
if (!please_inline && !is_private && !is_hidden) || is_no_inline {
return false;
}
- if !self.view_item_stack.insert(res_hir_id) {
+ if !self.view_item_stack.insert(res_did) {
return false;
}
- let ret = match tcx.hir().get(res_hir_id) {
+ let ret = match tcx.hir().get_by_def_id(res_did) {
Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
let prev = mem::replace(&mut self.inlining, true);
for &i in m.item_ids {
let i = self.cx.tcx.hir().item(i);
- self.visit_item_inner(i, None, Some(id));
+ self.visit_item(i, None, om, Some(def_id));
}
self.inlining = prev;
true
}
Node::Item(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_item_inner(it, renamed, Some(id));
+ self.visit_item(it, renamed, om, Some(def_id));
self.inlining = prev;
true
}
Node::ForeignItem(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_foreign_item_inner(it, renamed);
+ self.visit_foreign_item(it, renamed, om);
self.inlining = prev;
true
}
_ => false,
};
- self.view_item_stack.remove(&res_hir_id);
+ self.view_item_stack.remove(&res_did);
ret
}
- #[inline]
- fn add_to_current_mod(
+ fn visit_item(
&mut self,
item: &'tcx hir::Item<'_>,
renamed: Option<Symbol>,
- parent_id: Option<hir::HirId>,
+ om: &mut Module<'tcx>,
+ parent_id: Option<LocalDefId>,
) {
- self.modules.last_mut().unwrap().items.push((item, renamed, parent_id))
- }
-
- fn visit_item_inner(
- &mut self,
- item: &'tcx hir::Item<'_>,
- renamed: Option<Symbol>,
- parent_id: Option<hir::HirId>,
- ) -> bool {
debug!("visiting item {:?}", item);
let name = renamed.unwrap_or(item.ident.name);
hir::ItemKind::ForeignMod { items, .. } => {
for item in items {
let item = self.cx.tcx.hir().foreign_item(item.id);
- self.visit_foreign_item_inner(item, None);
+ self.visit_foreign_item(item, None, om);
}
}
// If we're inlining, skip private items or item reexported as "_".
let is_glob = kind == hir::UseKind::Glob;
let ident = if is_glob { None } else { Some(name) };
if self.maybe_inline_local(
- item.hir_id(),
+ item.owner_id.def_id,
res,
ident,
is_glob,
+ om,
please_inline,
) {
continue;
}
}
- self.add_to_current_mod(item, renamed, parent_id);
+ om.items.push((item, renamed, parent_id))
}
}
hir::ItemKind::Macro(ref macro_def, _) => {
let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export);
if is_macro_2_0 || nonexported || self.inlining {
- self.add_to_current_mod(item, renamed, None);
+ om.items.push((item, renamed, None));
}
}
hir::ItemKind::Mod(ref m) => {
- self.enter_mod(item.hir_id(), m, name);
+ om.mods.push(self.visit_mod_contents(item.owner_id.def_id, m, name, parent_id));
}
hir::ItemKind::Fn(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)
- | hir::ItemKind::TraitAlias(..) => {
- self.add_to_current_mod(item, renamed, parent_id);
- }
+ | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed, parent_id)),
hir::ItemKind::Const(..) => {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if name != kw::Underscore {
- self.add_to_current_mod(item, renamed, parent_id);
+ om.items.push((item, renamed, parent_id));
}
}
hir::ItemKind::Impl(impl_) => {
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
// them up regardless of where they're located.
if !self.inlining && impl_.of_trait.is_none() {
- self.add_to_current_mod(item, None, None);
+ om.items.push((item, None, None));
}
}
}
- true
}
- fn visit_foreign_item_inner(
+ fn visit_foreign_item(
&mut self,
item: &'tcx hir::ForeignItem<'_>,
renamed: Option<Symbol>,
+ om: &mut Module<'tcx>,
) {
// If inlining we only want to include public functions.
if !self.inlining || self.cx.tcx.visibility(item.owner_id).is_public() {
- self.modules.last_mut().unwrap().foreigns.push((item, renamed));
+ om.foreigns.push((item, renamed));
}
}
-
- /// This method will create a new module and push it onto the "modules stack" then call
- /// `visit_mod_contents`. Once done, it'll remove it from the "modules stack" and instead
- /// add into the list of modules of the current module.
- fn enter_mod(&mut self, id: hir::HirId, m: &'tcx hir::Mod<'tcx>, name: Symbol) {
- self.modules.push(Module::new(name, id, m.spans.inner_span));
-
- self.visit_mod_contents(id, m);
-
- let last = self.modules.pop().unwrap();
- self.modules.last_mut().unwrap().mods.push(last);
- }
-}
-
-// We need to implement this visitor so it'll go everywhere and retrieve items we're interested in
-// such as impl blocks in const blocks.
-impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
- type NestedFilter = nested_filter::All;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.cx.tcx.hir()
- }
-
- fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
- let parent_id = if self.modules.len() > 1 {
- Some(self.modules[self.modules.len() - 2].id)
- } else {
- None
- };
- if self.visit_item_inner(i, None, parent_id) {
- walk_item(self, i);
- }
- }
-
- fn visit_mod(&mut self, _: &hir::Mod<'tcx>, _: Span, _: hir::HirId) {
- // Handled in `visit_item_inner`
- }
-
- fn visit_use(&mut self, _: &hir::UsePath<'tcx>, _: hir::HirId) {
- // Handled in `visit_item_inner`
- }
-
- fn visit_path(&mut self, _: &hir::Path<'tcx>, _: hir::HirId) {
- // Handled in `visit_item_inner`
- }
-
- fn visit_label(&mut self, _: &rustc_ast::Label) {
- // Unneeded.
- }
-
- fn visit_infer(&mut self, _: &hir::InferArg) {
- // Unneeded.
- }
-
- fn visit_lifetime(&mut self, _: &hir::Lifetime) {
- // Unneeded.
- }
}
use crate::core::DocContext;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def::DefKind;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_middle::ty::TyCtxt;
+use rustc_resolve::Resolver;
// FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses
#[derive(Default)]
pub(crate) struct RustdocEffectiveVisibilities {
- extern_public: FxHashSet<DefId>,
+ extern_public: DefIdSet,
}
macro_rules! define_method {
define_method!(is_directly_public);
define_method!(is_exported);
define_method!(is_reachable);
+
+ pub(crate) fn init(&mut self, extern_public: DefIdSet) {
+ self.extern_public = extern_public;
+ }
}
pub(crate) fn lib_embargo_visit_item(cx: &mut DocContext<'_>, def_id: DefId) {
.visit_item(def_id)
}
+pub(crate) fn early_lib_embargo_visit_item(
+ resolver: &Resolver<'_>,
+ extern_public: &mut DefIdSet,
+ def_id: DefId,
+ is_mod: bool,
+) {
+ assert!(!def_id.is_local());
+ EarlyLibEmbargoVisitor { resolver, extern_public, visited_mods: Default::default() }
+ .visit_item(def_id, is_mod)
+}
+
/// Similar to `librustc_privacy::EmbargoVisitor`, but also takes
/// specific rustdoc annotations into account (i.e., `doc(hidden)`)
struct LibEmbargoVisitor<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
// Effective visibilities for reachable nodes
- extern_public: &'a mut FxHashSet<DefId>,
+ extern_public: &'a mut DefIdSet,
+ // Keeps track of already visited modules, in case a module re-exports its parent
+ visited_mods: DefIdSet,
+}
+
+struct EarlyLibEmbargoVisitor<'r, 'ra> {
+ resolver: &'r Resolver<'ra>,
+ // Effective visibilities for reachable nodes
+ extern_public: &'r mut DefIdSet,
// Keeps track of already visited modules, in case a module re-exports its parent
- visited_mods: FxHashSet<DefId>,
+ visited_mods: DefIdSet,
}
impl LibEmbargoVisitor<'_, '_> {
}
}
}
+
+impl EarlyLibEmbargoVisitor<'_, '_> {
+ fn visit_mod(&mut self, def_id: DefId) {
+ if !self.visited_mods.insert(def_id) {
+ return;
+ }
+
+ for item in self.resolver.cstore().module_children_untracked(def_id, self.resolver.sess()) {
+ if let Some(def_id) = item.res.opt_def_id() {
+ if item.vis.is_public() {
+ self.visit_item(def_id, matches!(item.res, Res::Def(DefKind::Mod, _)));
+ }
+ }
+ }
+ }
+
+ fn visit_item(&mut self, def_id: DefId, is_mod: bool) {
+ if !self.resolver.cstore().is_doc_hidden_untracked(def_id) {
+ self.extern_public.insert(def_id);
+ if is_mod {
+ self.visit_mod(def_id);
+ }
+ }
+ }
+}
-Subproject commit 9ad24035fea8d309753f5e39e6eb53d1d0eb39ce
+Subproject commit 477e7285b12f876ad105188cfcfc8adda7dc29aa
-Subproject commit 1cd6d3803dfb0b342272862a8590f5dfc9f72573
+Subproject commit 3c5af6bed9a1a243a693e8e22ee2486bd5b82a6c
- name: Test metadata collection
run: cargo collect-metadata
+ - name: Test lint_configuration.md is up-to-date
+ run: |
+ echo "run \`cargo collect-metadata\` if this fails"
+ git update-index --refresh
+
integration_build:
needs: changelog
runs-on: ubuntu-latest
## Unreleased / Beta / In Rust Nightly
-[4f142aa1...master](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...master)
+[d822110d...master](https://github.com/rust-lang/rust-clippy/compare/d822110d...master)
+
+## Rust 1.67
+
+Current stable, released 2023-01-26
+
+[4f142aa1...d822110d](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...d822110d)
+
+### New Lints
+
+* [`seek_from_current`]
+ [#9681](https://github.com/rust-lang/rust-clippy/pull/9681)
+* [`from_raw_with_void_ptr`]
+ [#9690](https://github.com/rust-lang/rust-clippy/pull/9690)
+* [`misnamed_getters`]
+ [#9770](https://github.com/rust-lang/rust-clippy/pull/9770)
+* [`seek_to_start_instead_of_rewind`]
+ [#9667](https://github.com/rust-lang/rust-clippy/pull/9667)
+* [`suspicious_xor_used_as_pow`]
+ [#9506](https://github.com/rust-lang/rust-clippy/pull/9506)
+* [`unnecessary_safety_doc`]
+ [#9822](https://github.com/rust-lang/rust-clippy/pull/9822)
+* [`unchecked_duration_subtraction`]
+ [#9570](https://github.com/rust-lang/rust-clippy/pull/9570)
+* [`manual_is_ascii_check`]
+ [#9765](https://github.com/rust-lang/rust-clippy/pull/9765)
+* [`unnecessary_safety_comment`]
+ [#9851](https://github.com/rust-lang/rust-clippy/pull/9851)
+* [`let_underscore_future`]
+ [#9760](https://github.com/rust-lang/rust-clippy/pull/9760)
+* [`manual_let_else`]
+ [#8437](https://github.com/rust-lang/rust-clippy/pull/8437)
+
+### Moves and Deprecations
+
+* Moved [`uninlined_format_args`] to `style` (Now warn-by-default)
+ [#9865](https://github.com/rust-lang/rust-clippy/pull/9865)
+* Moved [`needless_collect`] to `nursery` (Now allow-by-default)
+ [#9705](https://github.com/rust-lang/rust-clippy/pull/9705)
+* Moved [`or_fun_call`] to `nursery` (Now allow-by-default)
+ [#9829](https://github.com/rust-lang/rust-clippy/pull/9829)
+* Uplifted [`let_underscore_lock`] into rustc
+ [#9697](https://github.com/rust-lang/rust-clippy/pull/9697)
+* Uplifted [`let_underscore_drop`] into rustc
+ [#9697](https://github.com/rust-lang/rust-clippy/pull/9697)
+* Moved [`bool_to_int_with_if`] to `pedantic` (Now allow-by-default)
+ [#9830](https://github.com/rust-lang/rust-clippy/pull/9830)
+* Move `index_refutable_slice` to `pedantic` (Now warn-by-default)
+ [#9975](https://github.com/rust-lang/rust-clippy/pull/9975)
+* Moved [`manual_clamp`] to `nursery` (Now allow-by-default)
+ [#10101](https://github.com/rust-lang/rust-clippy/pull/10101)
+
+### Enhancements
+
+* The scope of `#![clippy::msrv]` is now tracked correctly
+ [#9924](https://github.com/rust-lang/rust-clippy/pull/9924)
+* `#[clippy::msrv]` can now be used as an outer attribute
+ [#9860](https://github.com/rust-lang/rust-clippy/pull/9860)
+* Clippy will now avoid Cargo's cache, if `Cargo.toml` or `clippy.toml` have changed
+ [#9707](https://github.com/rust-lang/rust-clippy/pull/9707)
+* [`uninlined_format_args`]: Added a new config `allow-mixed-uninlined-format-args` to allow the
+ lint, if only some arguments can be inlined
+ [#9865](https://github.com/rust-lang/rust-clippy/pull/9865)
+* [`needless_lifetimes`]: Now provides suggests for individual lifetimes
+ [#9743](https://github.com/rust-lang/rust-clippy/pull/9743)
+* [`needless_collect`]: Now detects needless `is_empty` and `contains` calls
+ [#8744](https://github.com/rust-lang/rust-clippy/pull/8744)
+* [`blanket_clippy_restriction_lints`]: Now lints, if `clippy::restriction` is enabled via the
+ command line arguments
+ [#9755](https://github.com/rust-lang/rust-clippy/pull/9755)
+* [`mutable_key_type`]: Now has the `ignore-interior-mutability` configuration, to add types which
+ should be ignored by the lint
+ [#9692](https://github.com/rust-lang/rust-clippy/pull/9692)
+* [`uninlined_format_args`]: Now works for multiline `format!` expressions
+ [#9945](https://github.com/rust-lang/rust-clippy/pull/9945)
+* [`cognitive_complexity`]: Now works for async functions
+ [#9828](https://github.com/rust-lang/rust-clippy/pull/9828)
+ [#9836](https://github.com/rust-lang/rust-clippy/pull/9836)
+* [`vec_box`]: Now avoids an off-by-one error when using the `vec-box-size-threshold` configuration
+ [#9848](https://github.com/rust-lang/rust-clippy/pull/9848)
+* [`never_loop`]: Now correctly handles breaks in nested labeled blocks
+ [#9858](https://github.com/rust-lang/rust-clippy/pull/9858)
+ [#9837](https://github.com/rust-lang/rust-clippy/pull/9837)
+* [`disallowed_methods`], [`disallowed_types`], [`disallowed_macros`]: Now correctly resolve
+ paths, if a crate is used multiple times with different versions
+ [#9800](https://github.com/rust-lang/rust-clippy/pull/9800)
+* [`disallowed_methods`]: Can now be used for local methods
+ [#9800](https://github.com/rust-lang/rust-clippy/pull/9800)
+* [`print_stdout`], [`print_stderr`]: Can now be enabled in test with the `allow-print-in-tests`
+ config value
+ [#9797](https://github.com/rust-lang/rust-clippy/pull/9797)
+* [`from_raw_with_void_ptr`]: Now works for `Rc`, `Arc`, `alloc::rc::Weak` and
+ `alloc::sync::Weak` types.
+ [#9700](https://github.com/rust-lang/rust-clippy/pull/9700)
+* [`needless_borrowed_reference`]: Now works for struct and tuple patterns with wildcards
+ [#9855](https://github.com/rust-lang/rust-clippy/pull/9855)
+* [`or_fun_call`]: Now supports `map_or` methods
+ [#9689](https://github.com/rust-lang/rust-clippy/pull/9689)
+* [`unwrap_used`], [`expect_used`]: No longer lints in test code
+ [#9686](https://github.com/rust-lang/rust-clippy/pull/9686)
+* [`fn_params_excessive_bools`]: Is now emitted with the lint level at the linted function
+ [#9698](https://github.com/rust-lang/rust-clippy/pull/9698)
+
+### False Positive Fixes
+
+* [`new_ret_no_self`]: No longer lints when `impl Trait<Self>` is returned
+ [#9733](https://github.com/rust-lang/rust-clippy/pull/9733)
+* [`unnecessary_lazy_evaluations`]: No longer lints, if the type has a significant drop
+ [#9750](https://github.com/rust-lang/rust-clippy/pull/9750)
+* [`option_if_let_else`]: No longer lints, if any arm has guard
+ [#9747](https://github.com/rust-lang/rust-clippy/pull/9747)
+* [`explicit_auto_deref`]: No longer lints, if the target type is a projection with generic
+ arguments
+ [#9813](https://github.com/rust-lang/rust-clippy/pull/9813)
+* [`unnecessary_to_owned`]: No longer lints, if the suggestion effects types
+ [#9796](https://github.com/rust-lang/rust-clippy/pull/9796)
+* [`needless_borrow`]: No longer lints, if the suggestion is affected by `Deref`
+ [#9674](https://github.com/rust-lang/rust-clippy/pull/9674)
+* [`unused_unit`]: No longer lints, if lifetimes are bound to the return type
+ [#9849](https://github.com/rust-lang/rust-clippy/pull/9849)
+* [`mut_mut`]: No longer lints cases with unsized mutable references
+ [#9835](https://github.com/rust-lang/rust-clippy/pull/9835)
+* [`bool_to_int_with_if`]: No longer lints in const context
+ [#9738](https://github.com/rust-lang/rust-clippy/pull/9738)
+* [`use_self`]: No longer lints in macros
+ [#9704](https://github.com/rust-lang/rust-clippy/pull/9704)
+* [`unnecessary_operation`]: No longer lints, if multiple macros are involved
+ [#9981](https://github.com/rust-lang/rust-clippy/pull/9981)
+* [`allow_attributes_without_reason`]: No longer lints inside external macros
+ [#9630](https://github.com/rust-lang/rust-clippy/pull/9630)
+* [`question_mark`]: No longer lints for `if let Err()` with an `else` branch
+ [#9722](https://github.com/rust-lang/rust-clippy/pull/9722)
+* [`unnecessary_cast`]: No longer lints if the identifier and cast originate from different macros
+ [#9980](https://github.com/rust-lang/rust-clippy/pull/9980)
+* [`arithmetic_side_effects`]: Now detects operations with associated constants
+ [#9592](https://github.com/rust-lang/rust-clippy/pull/9592)
+* [`explicit_auto_deref`]: No longer lints, if the initial value is not a reference or reference
+ receiver
+ [#9997](https://github.com/rust-lang/rust-clippy/pull/9997)
+* [`module_name_repetitions`], [`single_component_path_imports`]: Now handle `#[allow]`
+ attributes correctly
+ [#9879](https://github.com/rust-lang/rust-clippy/pull/9879)
+* [`bool_to_int_with_if`]: No longer lints `if let` statements
+ [#9714](https://github.com/rust-lang/rust-clippy/pull/9714)
+* [`needless_borrow`]: No longer lints, `if`-`else`-statements that require the borrow
+ [#9791](https://github.com/rust-lang/rust-clippy/pull/9791)
+* [`needless_borrow`]: No longer lints borrows, if moves were illegal
+ [#9711](https://github.com/rust-lang/rust-clippy/pull/9711)
+* [`manual_swap`]: No longer lints in const context
+ [#9871](https://github.com/rust-lang/rust-clippy/pull/9871)
+
+### Suggestion Fixes/Improvements
+
+* [`missing_safety_doc`], [`missing_errors_doc`], [`missing_panics_doc`]: No longer show the
+ entire item in the lint emission.
+ [#9772](https://github.com/rust-lang/rust-clippy/pull/9772)
+* [`needless_lifetimes`]: Only suggests `'_` when it's applicable
+ [#9743](https://github.com/rust-lang/rust-clippy/pull/9743)
+* [`use_self`]: Now suggests full paths correctly
+ [#9726](https://github.com/rust-lang/rust-clippy/pull/9726)
+* [`redundant_closure_call`]: Now correctly deals with macros during suggestion creation
+ [#9987](https://github.com/rust-lang/rust-clippy/pull/9987)
+* [`unnecessary_cast`]: Suggestions now correctly deal with references
+ [#9996](https://github.com/rust-lang/rust-clippy/pull/9996)
+* [`unnecessary_join`]: Suggestions now correctly use [turbofish] operators
+ [#9779](https://github.com/rust-lang/rust-clippy/pull/9779)
+* [`equatable_if_let`]: Can now suggest `matches!` replacements
+ [#9368](https://github.com/rust-lang/rust-clippy/pull/9368)
+* [`string_extend_chars`]: Suggestions now correctly work for `str` slices
+ [#9741](https://github.com/rust-lang/rust-clippy/pull/9741)
+* [`redundant_closure_for_method_calls`]: Suggestions now include angle brackets and generic
+ arguments if needed
+ [#9745](https://github.com/rust-lang/rust-clippy/pull/9745)
+* [`manual_let_else`]: Suggestions no longer expand macro calls
+ [#9943](https://github.com/rust-lang/rust-clippy/pull/9943)
+* [`infallible_destructuring_match`]: Suggestions now preserve references
+ [#9850](https://github.com/rust-lang/rust-clippy/pull/9850)
+* [`result_large_err`]: The error now shows the largest enum variant
+ [#9662](https://github.com/rust-lang/rust-clippy/pull/9662)
+* [`needless_return`]: Suggestions are now formatted better
+ [#9967](https://github.com/rust-lang/rust-clippy/pull/9967)
+* [`unused_rounding`]: The suggestion now preserves the original float literal notation
+ [#9870](https://github.com/rust-lang/rust-clippy/pull/9870)
+
+[turbofish]: https://turbo.fish/::%3CClippy%3E
+
+### ICE Fixes
+
+* [`result_large_err`]: Fixed ICE for empty enums
+ [#10007](https://github.com/rust-lang/rust-clippy/pull/10007)
+* [`redundant_allocation`]: Fixed ICE for types with bounded variables
+ [#9773](https://github.com/rust-lang/rust-clippy/pull/9773)
+* [`unused_rounding`]: Fixed ICE, if `_` was used as a separator
+ [#10001](https://github.com/rust-lang/rust-clippy/pull/10001)
## Rust 1.66
-Current stable, released 2022-12-15
+Released 2022-12-15
[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1)
* [`unnecessary_to_owned`]: Avoid ICEs in favor of false negatives if information is missing
[#9505](https://github.com/rust-lang/rust-clippy/pull/9505)
+ [#10027](https://github.com/rust-lang/rust-clippy/pull/10027)
* [`manual_range_contains`]: No longer ICEs on values behind references
[#9627](https://github.com/rust-lang/rust-clippy/pull/9627)
* [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments
[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
+[`multiple_unsafe_ops_per_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_unsafe_ops_per_block
[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
[package]
name = "clippy"
-version = "0.1.68"
+version = "0.1.69"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
```toml
avoid-breaking-exported-api = false
disallowed-names = ["toto", "tata", "titi"]
-cognitive-complexity-threshold = 30
```
-See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
-the lint descriptions contain the names and meanings of these configuration variables.
+The [table of configurations](https://doc.rust-lang.org/nightly/clippy/lint_configuration.html)
+contains all config values, their default, and a list of lints they affect.
+Each [configurable lint](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration)
+, also contains information about these values.
+
+For configurations that are a list type with default values such as
+[disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names),
+you can use the unique value `".."` to extend the default values instead of replacing them.
+
+```toml
+# default of disallowed-names is ["foo", "baz", "quux"]
+disallowed-names = ["bar", ".."] # -> ["bar", "foo", "baz", "quux"]
+```
> **Note**
>
- [Installation](installation.md)
- [Usage](usage.md)
- [Configuration](configuration.md)
+ - [Lint Configuration](lint_configuration.md)
- [Clippy's Lints](lints.md)
- [Continuous Integration](continuous_integration/README.md)
- [GitHub Actions](continuous_integration/github_actions.md)
```toml
avoid-breaking-exported-api = false
disallowed-names = ["toto", "tata", "titi"]
-cognitive-complexity-threshold = 30
```
-See the [list of configurable lints](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration),
-the lint descriptions contain the names and meanings of these configuration variables.
+The [table of configurations](./lint_configuration.md)
+contains all config values, their default, and a list of lints they affect.
+Each [configurable lint](https://rust-lang.github.io/rust-clippy/master/index.html#Configuration)
+, also contains information about these values.
+
+For configurations that are a list type with default values such as
+[disallowed-names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names),
+you can use the unique value `".."` to extend the default values instead of replacing them.
+
+```toml
+# default of disallowed-names is ["foo", "baz", "quux"]
+disallowed-names = ["bar", ".."] # -> ["bar", "foo", "baz", "quux"]
+```
To deactivate the "for further information visit *lint-link*" message you can define the `CLIPPY_DISABLE_DOCS_LINKS`
environment variable.
manifest.
If our new lint is named e.g. `foo_categories`, after running `cargo dev
-new_lint` we will find by default two new crates, each with its manifest file:
+new_lint --name=foo_categories --type=cargo --category=cargo` we will find by
+default two new crates, each with its manifest file:
* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the
new lint to raise an error.
`clippy.toml` file with the configuration value and a rust file that
should be linted by Clippy. The test can otherwise be written as usual.
+5. Update [Lint Configuration](../lint_configuration.md)
+
+ Run `cargo collect-metadata` to generate documentation changes for the book.
+
[`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs
[`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
[`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui
This document explains how to make additions and changes to the Clippy book, the
guide to Clippy that you're reading right now. The Clippy book is formatted with
[Markdown](https://www.markdownguide.org) and generated by
-[mdbook](https://github.com/rust-lang/mdBook).
+[mdBook](https://github.com/rust-lang/mdBook).
-- [Get mdbook](#get-mdbook)
+- [Get mdBook](#get-mdbook)
- [Make changes](#make-changes)
-## Get mdbook
+## Get mdBook
While not strictly necessary since the book source is simply Markdown text
-files, having mdbook locally will allow you to build, test and serve the book
+files, having mdBook locally will allow you to build, test and serve the book
locally to view changes before you commit them to the repository. You likely
already have `cargo` installed, so the easiest option is to simply:
cargo install mdbook
```
-See the mdbook [installation](https://github.com/rust-lang/mdBook#installation)
+See the mdBook [installation](https://github.com/rust-lang/mdBook#installation)
instructions for other options.
## Make changes
The book's
[src](https://github.com/rust-lang/rust-clippy/tree/master/book/src)
directory contains all of the markdown files used to generate the book. If you
-want to see your changes in real time, you can use the mdbook `serve` command to
+want to see your changes in real time, you can use the mdBook `serve` command to
run a web server locally that will automatically update changes as they are
made. From the top level of your `rust-clippy` directory:
Then navigate to `http://localhost:3000` to see the generated book. While the
server is running, changes you make will automatically be updated.
-For more information, see the mdbook
+For more information, see the mdBook
[guide](https://rust-lang.github.io/mdBook/).
Please also be sure to update the Beta/Unreleased sections at the top with the
relevant commit ranges.
-If you have the time, it would be appreciated if you double-check, that the
-`#[clippy::version]` attributes for the added lints contains the correct version.
+#### 3.1 Include `beta-accepted` PRs
+
+Look for the [`beta-accepted`] label and make sure to also include the PRs with
+that label in the changelog. If you can, remove the `beta-accepted` labels
+**after** the changelog PR was merged.
+
+> _Note:_ Some of those PRs might even got backported to the previous `beta`.
+> Those have to be included in the changelog of the _previous_ release.
+
+### 4. Update `clippy::version` attributes
+
+Next, make sure to check that the `#[clippy::version]` attributes for the added
+lints contain the correct version.
[changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
[forge]: https://forge.rust-lang.org/
[rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy
[rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy
[rust_stable_tools]: https://github.com/rust-lang/rust/releases
+[`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+
--- /dev/null
+<!--
+This file is generated by `cargo collect-metadata`.
+Please use that command to update the file and do not edit it by hand.
+-->
+
+## Lint Configuration Options
+| <div style="width:290px">Option</div> | Default Value |
+|--|--|
+| [arithmetic-side-effects-allowed](#arithmetic-side-effects-allowed) | `{}` |
+| [arithmetic-side-effects-allowed-binary](#arithmetic-side-effects-allowed-binary) | `[]` |
+| [arithmetic-side-effects-allowed-unary](#arithmetic-side-effects-allowed-unary) | `{}` |
+| [avoid-breaking-exported-api](#avoid-breaking-exported-api) | `true` |
+| [msrv](#msrv) | `None` |
+| [cognitive-complexity-threshold](#cognitive-complexity-threshold) | `25` |
+| [disallowed-names](#disallowed-names) | `["foo", "baz", "quux"]` |
+| [doc-valid-idents](#doc-valid-idents) | `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` |
+| [too-many-arguments-threshold](#too-many-arguments-threshold) | `7` |
+| [type-complexity-threshold](#type-complexity-threshold) | `250` |
+| [single-char-binding-names-threshold](#single-char-binding-names-threshold) | `4` |
+| [too-large-for-stack](#too-large-for-stack) | `200` |
+| [enum-variant-name-threshold](#enum-variant-name-threshold) | `3` |
+| [enum-variant-size-threshold](#enum-variant-size-threshold) | `200` |
+| [verbose-bit-mask-threshold](#verbose-bit-mask-threshold) | `1` |
+| [literal-representation-threshold](#literal-representation-threshold) | `16384` |
+| [trivial-copy-size-limit](#trivial-copy-size-limit) | `None` |
+| [pass-by-value-size-limit](#pass-by-value-size-limit) | `256` |
+| [too-many-lines-threshold](#too-many-lines-threshold) | `100` |
+| [array-size-threshold](#array-size-threshold) | `512000` |
+| [vec-box-size-threshold](#vec-box-size-threshold) | `4096` |
+| [max-trait-bounds](#max-trait-bounds) | `3` |
+| [max-struct-bools](#max-struct-bools) | `3` |
+| [max-fn-params-bools](#max-fn-params-bools) | `3` |
+| [warn-on-all-wildcard-imports](#warn-on-all-wildcard-imports) | `false` |
+| [disallowed-macros](#disallowed-macros) | `[]` |
+| [disallowed-methods](#disallowed-methods) | `[]` |
+| [disallowed-types](#disallowed-types) | `[]` |
+| [unreadable-literal-lint-fractions](#unreadable-literal-lint-fractions) | `true` |
+| [upper-case-acronyms-aggressive](#upper-case-acronyms-aggressive) | `false` |
+| [matches-for-let-else](#matches-for-let-else) | `WellKnownTypes` |
+| [cargo-ignore-publish](#cargo-ignore-publish) | `false` |
+| [standard-macro-braces](#standard-macro-braces) | `[]` |
+| [enforced-import-renames](#enforced-import-renames) | `[]` |
+| [allowed-scripts](#allowed-scripts) | `["Latin"]` |
+| [enable-raw-pointer-heuristic-for-send](#enable-raw-pointer-heuristic-for-send) | `true` |
+| [max-suggested-slice-pattern-length](#max-suggested-slice-pattern-length) | `3` |
+| [max-include-file-size](#max-include-file-size) | `1000000` |
+| [allow-expect-in-tests](#allow-expect-in-tests) | `false` |
+| [allow-unwrap-in-tests](#allow-unwrap-in-tests) | `false` |
+| [allow-dbg-in-tests](#allow-dbg-in-tests) | `false` |
+| [allow-print-in-tests](#allow-print-in-tests) | `false` |
+| [large-error-threshold](#large-error-threshold) | `128` |
+| [ignore-interior-mutability](#ignore-interior-mutability) | `["bytes::Bytes"]` |
+| [allow-mixed-uninlined-format-args](#allow-mixed-uninlined-format-args) | `true` |
+| [suppress-restriction-lint-in-const](#suppress-restriction-lint-in-const) | `false` |
+
+### arithmetic-side-effects-allowed
+Suppress checking of the passed type names in all types of operations.
+
+If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
+
+#### Example
+
+```toml
+arithmetic-side-effects-allowed = ["SomeType", "AnotherType"]
+```
+
+#### Noteworthy
+
+A type, say `SomeType`, listed in this configuration has the same behavior of
+`["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
+
+**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
+
+* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
+
+
+### arithmetic-side-effects-allowed-binary
+Suppress checking of the passed type pair names in binary operations like addition or
+multiplication.
+
+Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless
+of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`.
+
+Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as
+`["AnotherType", "SomeType"]`.
+
+#### Example
+
+```toml
+arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
+```
+
+**Default Value:** `[]` (`Vec<[String; 2]>`)
+
+* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
+
+
+### arithmetic-side-effects-allowed-unary
+Suppress checking of the passed type names in unary operations like "negation" (`-`).
+
+#### Example
+
+```toml
+arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
+```
+
+**Default Value:** `{}` (`rustc_data_structures::fx::FxHashSet<String>`)
+
+* [arithmetic_side_effects](https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects)
+
+
+### avoid-breaking-exported-api
+Suppress lints whenever the suggested change would cause breakage for other crates.
+
+**Default Value:** `true` (`bool`)
+
+* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
+* [large_types_passed_by_value](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
+* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
+* [unnecessary_wraps](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps)
+* [unused_self](https://rust-lang.github.io/rust-clippy/master/index.html#unused_self)
+* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
+* [wrong_self_convention](https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention)
+* [box_collection](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection)
+* [redundant_allocation](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation)
+* [rc_buffer](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
+* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
+* [option_option](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
+* [linkedlist](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
+* [rc_mutex](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
+
+
+### msrv
+The minimum rust version that the project supports
+
+**Default Value:** `None` (`Option<String>`)
+
+* [manual_split_once](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once)
+* [manual_str_repeat](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat)
+* [cloned_instead_of_copied](https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied)
+* [redundant_field_names](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
+* [redundant_static_lifetimes](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes)
+* [filter_map_next](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next)
+* [checked_conversions](https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions)
+* [manual_range_contains](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains)
+* [use_self](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
+* [mem_replace_with_default](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
+* [manual_non_exhaustive](https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive)
+* [option_as_ref_deref](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
+* [map_unwrap_or](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
+* [match_like_matches_macro](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
+* [manual_strip](https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip)
+* [missing_const_for_fn](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
+* [unnested_or_patterns](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
+* [from_over_into](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
+* [ptr_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
+* [if_then_some_else_none](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
+* [approx_constant](https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant)
+* [deprecated_cfg_attr](https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr)
+* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
+* [map_clone](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
+* [borrow_as_ptr](https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr)
+* [manual_bits](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
+* [err_expect](https://rust-lang.github.io/rust-clippy/master/index.html#err_expect)
+* [cast_abs_to_unsigned](https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned)
+* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
+* [manual_clamp](https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp)
+* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
+* [unchecked_duration_subtraction](https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction)
+
+
+### cognitive-complexity-threshold
+The maximum cognitive complexity a function can have
+
+**Default Value:** `25` (`u64`)
+
+* [cognitive_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
+
+
+### disallowed-names
+The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
+`".."` can be used as part of the list to indicate, that the configured values should be appended to the
+default configuration of Clippy. By default any configuration will replace the default value.
+
+**Default Value:** `["foo", "baz", "quux"]` (`Vec<String>`)
+
+* [disallowed_names](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names)
+
+
+### doc-valid-idents
+The list of words this lint should not consider as identifiers needing ticks. The value
+`".."` can be used as part of the list to indicate, that the configured values should be appended to the
+default configuration of Clippy. By default any configuraction will replace the default value. For example:
+* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
+* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
+
+Default list:
+
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS", "WebGL", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]` (`Vec<String>`)
+
+* [doc_markdown](https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown)
+
+
+### too-many-arguments-threshold
+The maximum number of argument a function or method can have
+
+**Default Value:** `7` (`u64`)
+
+* [too_many_arguments](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments)
+
+
+### type-complexity-threshold
+The maximum complexity a type can have
+
+**Default Value:** `250` (`u64`)
+
+* [type_complexity](https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity)
+
+
+### single-char-binding-names-threshold
+The maximum number of single char bindings a scope may have
+
+**Default Value:** `4` (`u64`)
+
+* [many_single_char_names](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names)
+
+
+### too-large-for-stack
+The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+
+**Default Value:** `200` (`u64`)
+
+* [boxed_local](https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local)
+* [useless_vec](https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec)
+
+
+### enum-variant-name-threshold
+The minimum number of enum variants for the lints about variant names to trigger
+
+**Default Value:** `3` (`u64`)
+
+* [enum_variant_names](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
+
+
+### enum-variant-size-threshold
+The maximum size of an enum's variant to avoid box suggestion
+
+**Default Value:** `200` (`u64`)
+
+* [large_enum_variant](https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant)
+
+
+### verbose-bit-mask-threshold
+The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
+
+**Default Value:** `1` (`u64`)
+
+* [verbose_bit_mask](https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask)
+
+
+### literal-representation-threshold
+The lower bound for linting decimal literals
+
+**Default Value:** `16384` (`u64`)
+
+* [decimal_literal_representation](https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation)
+
+
+### trivial-copy-size-limit
+The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
+
+**Default Value:** `None` (`Option<u64>`)
+
+* [trivially_copy_pass_by_ref](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
+
+
+### pass-by-value-size-limit
+The minimum size (in bytes) to consider a type for passing by reference instead of by value.
+
+**Default Value:** `256` (`u64`)
+
+* [large_type_pass_by_move](https://rust-lang.github.io/rust-clippy/master/index.html#large_type_pass_by_move)
+
+
+### too-many-lines-threshold
+The maximum number of lines a function or method can have
+
+**Default Value:** `100` (`u64`)
+
+* [too_many_lines](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
+
+
+### array-size-threshold
+The maximum allowed size for arrays on the stack
+
+**Default Value:** `512000` (`u128`)
+
+* [large_stack_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays)
+* [large_const_arrays](https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays)
+
+
+### vec-box-size-threshold
+The size of the boxed type in bytes, where boxing in a `Vec` is allowed
+
+**Default Value:** `4096` (`u64`)
+
+* [vec_box](https://rust-lang.github.io/rust-clippy/master/index.html#vec_box)
+
+
+### max-trait-bounds
+The maximum number of bounds a trait can have to be linted
+
+**Default Value:** `3` (`u64`)
+
+* [type_repetition_in_bounds](https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds)
+
+
+### max-struct-bools
+The maximum number of bool fields a struct can have
+
+**Default Value:** `3` (`u64`)
+
+* [struct_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools)
+
+
+### max-fn-params-bools
+The maximum number of bool parameters a function can have
+
+**Default Value:** `3` (`u64`)
+
+* [fn_params_excessive_bools](https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools)
+
+
+### warn-on-all-wildcard-imports
+Whether to allow certain wildcard imports (prelude, super in tests).
+
+**Default Value:** `false` (`bool`)
+
+* [wildcard_imports](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
+
+
+### disallowed-macros
+The list of disallowed macros, written as fully qualified paths.
+
+**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+
+* [disallowed_macros](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros)
+
+
+### disallowed-methods
+The list of disallowed methods, written as fully qualified paths.
+
+**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+
+* [disallowed_methods](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods)
+
+
+### disallowed-types
+The list of disallowed types, written as fully qualified paths.
+
+**Default Value:** `[]` (`Vec<crate::utils::conf::DisallowedPath>`)
+
+* [disallowed_types](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types)
+
+
+### unreadable-literal-lint-fractions
+Should the fraction of a decimal be linted to include separators.
+
+**Default Value:** `true` (`bool`)
+
+* [unreadable_literal](https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal)
+
+
+### upper-case-acronyms-aggressive
+Enables verbose mode. Triggers if there is more than one uppercase char next to each other
+
+**Default Value:** `false` (`bool`)
+
+* [upper_case_acronyms](https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms)
+
+
+### matches-for-let-else
+Whether the matches should be considered by the lint, and whether there should
+be filtering for common types.
+
+**Default Value:** `WellKnownTypes` (`crate::manual_let_else::MatchLintBehaviour`)
+
+* [manual_let_else](https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else)
+
+
+### cargo-ignore-publish
+For internal testing only, ignores the current `publish` settings in the Cargo manifest.
+
+**Default Value:** `false` (`bool`)
+
+* [_cargo_common_metadata](https://rust-lang.github.io/rust-clippy/master/index.html#_cargo_common_metadata)
+
+
+### standard-macro-braces
+Enforce the named macros always use the braces specified.
+
+A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
+is could be used with a full path two `MacroMatcher`s have to be added one with the full path
+`crate_name::macro_name` and one with just the macro name.
+
+**Default Value:** `[]` (`Vec<crate::nonstandard_macro_braces::MacroMatcher>`)
+
+* [nonstandard_macro_braces](https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces)
+
+
+### enforced-import-renames
+The list of imports to always rename, a fully qualified path followed by the rename.
+
+**Default Value:** `[]` (`Vec<crate::utils::conf::Rename>`)
+
+* [missing_enforced_import_renames](https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames)
+
+
+### allowed-scripts
+The list of unicode scripts allowed to be used in the scope.
+
+**Default Value:** `["Latin"]` (`Vec<String>`)
+
+* [disallowed_script_idents](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents)
+
+
+### enable-raw-pointer-heuristic-for-send
+Whether to apply the raw pointer heuristic to determine if a type is `Send`.
+
+**Default Value:** `true` (`bool`)
+
+* [non_send_fields_in_send_ty](https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty)
+
+
+### max-suggested-slice-pattern-length
+When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
+the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
+
+**Default Value:** `3` (`u64`)
+
+* [index_refutable_slice](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
+
+
+### max-include-file-size
+The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
+
+**Default Value:** `1000000` (`u64`)
+
+* [large_include_file](https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file)
+
+
+### allow-expect-in-tests
+Whether `expect` should be allowed within `#[cfg(test)]`
+
+**Default Value:** `false` (`bool`)
+
+* [expect_used](https://rust-lang.github.io/rust-clippy/master/index.html#expect_used)
+
+
+### allow-unwrap-in-tests
+Whether `unwrap` should be allowed in test cfg
+
+**Default Value:** `false` (`bool`)
+
+* [unwrap_used](https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used)
+
+
+### allow-dbg-in-tests
+Whether `dbg!` should be allowed in test functions
+
+**Default Value:** `false` (`bool`)
+
+* [dbg_macro](https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro)
+
+
+### allow-print-in-tests
+Whether print macros (ex. `println!`) should be allowed in test functions
+
+**Default Value:** `false` (`bool`)
+
+* [print_stdout](https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout)
+* [print_stderr](https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr)
+
+
+### large-error-threshold
+The maximum size of the `Err`-variant in a `Result` returned from a function
+
+**Default Value:** `128` (`u64`)
+
+* [result_large_err](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err)
+
+
+### ignore-interior-mutability
+A list of paths to types that should be treated like `Arc`, i.e. ignored but
+for the generic parameters for determining interior mutability
+
+**Default Value:** `["bytes::Bytes"]` (`Vec<String>`)
+
+* [mutable_key](https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key)
+
+
+### allow-mixed-uninlined-format-args
+Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
+
+**Default Value:** `true` (`bool`)
+
+* [uninlined_format_args](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args)
+
+
+### suppress-restriction-lint-in-const
+In same
+cases the restructured operation might not be unavoidable, as the
+suggested counterparts are unavailable in constant code. This
+configuration will cause restriction lints to trigger even
+if no suggestion can be made.
+
+**Default Value:** `false` (`bool`)
+
+* [indexing_slicing](https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing)
+
+
+
[package]
name = "clippy_lints"
-version = "0.1.68"
+version = "0.1.69"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
+use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
-use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait};
+use clippy_utils::ty::{implements_trait, is_copy};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Lit};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Ident;
) && !e.span.from_expansion()
}
-fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
- let ty = cx.typeck_results().expr_ty(e);
-
+fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
cx.tcx
.lang_items()
.not_trait()
return;
}
let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return };
- if !(is_bool_lit(a) ^ is_bool_lit(b)) {
+
+ let a_span = a.span.source_callsite();
+ let b_span = b.span.source_callsite();
+
+ let (lit_span, non_lit_expr) = match (is_bool_lit(a), is_bool_lit(b)) {
+ // assert_eq!(true, b)
+ // ^^^^^^
+ (true, false) => (a_span.until(b_span), b),
+ // assert_eq!(a, true)
+ // ^^^^^^
+ (false, true) => (b_span.with_lo(a_span.hi()), a),
// If there are two boolean arguments, we definitely don't understand
// what's going on, so better leave things as is...
//
// Or there is simply no boolean and then we can leave things as is!
- return;
- }
+ _ => return,
+ };
- if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) {
+ let non_lit_ty = cx.typeck_results().expr_ty(non_lit_expr);
+
+ if !is_impl_not_trait_with_bool_out(cx, non_lit_ty) {
// At this point the expression which is not a boolean
// literal does not implement Not trait with a bool output,
// so we cannot suggest to rewrite our code
return;
}
+ if !is_copy(cx, non_lit_ty) {
+ // Only lint with types that are `Copy` because `assert!(x)` takes
+ // ownership of `x` whereas `assert_eq(x, true)` does not
+ return;
+ }
+
let macro_name = macro_name.as_str();
let non_eq_mac = ¯o_name[..macro_name.len() - 3];
- span_lint_and_sugg(
+ span_lint_and_then(
cx,
BOOL_ASSERT_COMPARISON,
macro_call.span,
&format!("used `{macro_name}!` with a literal bool"),
- "replace it with",
- format!("{non_eq_mac}!(..)"),
- Applicability::MaybeIncorrect,
+ |diag| {
+ // assert_eq!(...)
+ // ^^^^^^^^^
+ let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
+
+ diag.multipart_suggestion(
+ format!("replace it with `{non_eq_mac}!(..)`"),
+ vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())],
+ Applicability::MachineApplicable,
+ );
+ },
);
}
}
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
-use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp};
+use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::sym;
_: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
- _: HirId,
+ _: LocalDefId,
) {
NonminimalBoolVisitor { cx }.visit_body(body);
}
&& let ExprKind::MethodCall(method_name, receiver, [], _) = cast_expr.peel_blocks().kind
&& method_name.ident.name == rustc_span::sym::as_ptr
&& let Some(as_ptr_did) = cx.typeck_results().type_dependent_def_id(cast_expr.peel_blocks().hir_id)
- && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did)
+ && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).subst_identity()
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
&& let Some(recv) = snippet_opt(cx, receiver.span)
use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::expr_or_init;
+use clippy_utils::source::snippet;
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
+use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_span::Span;
use rustc_target::abi::IntegerType;
use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
}
}
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_expr: &Expr<'_>,
+ cast_from: Ty<'_>,
+ cast_to: Ty<'_>,
+ cast_to_span: Span,
+) {
let msg = match (cast_from.kind(), cast_to.is_integral()) {
(ty::Int(_) | ty::Uint(_), true) => {
let from_nbits = apply_reductions(
);
return;
}
- format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
+ format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
},
(ty::Float(_), true) => {
_ => return,
};
- span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
+ let name_of_cast_from = snippet(cx, cast_expr.span, "..");
+ let cast_to_snip = snippet(cx, cast_to_span, "..");
+ let suggestion = format!("{cast_to_snip}::try_from({name_of_cast_from})");
+
+ span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
+ diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...");
+ diag.span_suggestion_with_style(
+ expr.span,
+ "... or use `try_from` and handle the error accordingly",
+ suggestion,
+ Applicability::Unspecified,
+ // always show the suggestion in a separate line
+ SuggestionStyle::ShowAlways,
+ );
+ });
}
/// ### What it does
/// Checks for casts between numerical types that may
/// truncate large values. This is expected behavior, so the cast is `Allow` by
- /// default.
+ /// default. It suggests user either explicitly ignore the lint,
+ /// or use `try_from()` and handle the truncation, default, or panic explicitly.
///
/// ### Why is this bad?
/// In some problem domains, it is good practice to avoid
/// x as u8
/// }
/// ```
+ /// Use instead:
+ /// ```
+ /// fn as_u8(x: u64) -> u8 {
+ /// if let Ok(x) = u8::try_from(x) {
+ /// x
+ /// } else {
+ /// todo!();
+ /// }
+ /// }
+ /// // Or
+ /// #[allow(clippy::cast_possible_truncation)]
+ /// fn as_u16(x: u64) -> u16 {
+ /// x as u16
+ /// }
+ /// ```
#[clippy::version = "pre 1.29.0"]
pub CAST_POSSIBLE_TRUNCATION,
pedantic,
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
- cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
+ cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
if cast_from.is_numeric() {
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
cast_precision_loss::check(cx, expr, cast_from, cast_to);
use core::ops::ControlFlow;
use rustc_ast::ast::Attribute;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::{sym, BytePos};
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
- let def_id = cx.tcx.hir().local_def_id(hir_id);
if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) {
let expr = if is_async_fn(kind) {
match get_async_fn_body(cx.tcx, body) {
crate::module_style::MOD_MODULE_FILES_INFO,
crate::module_style::SELF_NAMED_MODULE_FILES_INFO,
crate::multi_assignments::MULTI_ASSIGNMENTS_INFO,
+ crate::multiple_unsafe_ops_per_block::MULTIPLE_UNSAFE_OPS_PER_BLOCK_INFO,
crate::mut_key::MUTABLE_KEY_TYPE_INFO,
crate::mut_mut::MUT_MUT_INFO,
crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO,
ExprKind::MethodCall(_, receiver, args, _) => {
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
- let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
+ let fn_sig = self.cx.tcx.fn_sig(def_id).subst_identity().skip_binder();
for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
self.ty_bounds.push((*bound).into());
self.visit_expr(expr);
let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
// We can't use `Ty::fn_sig` because it automatically performs substs, this may result in FNs.
match node_ty.kind() {
- ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
+ ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).subst_identity()),
ty::FnPtr(fn_sig) => Some(*fn_sig),
_ => None,
}
}) if span.ctxt() == ctxt => {
let output = cx
.tcx
- .erase_late_bound_regions(cx.tcx.fn_sig(owner_id.to_def_id()).output());
+ .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
},
Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
ExprKind::Ret(_) => {
- let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
+ let owner_id = cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap());
Some(
if let Node::Expr(
closure_expr @ Expr {
kind: ExprKind::Closure(closure),
..
},
- ) = cx.tcx.hir().get(owner_id)
+ ) = cx.tcx.hir().get_by_def_id(owner_id)
{
closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
} else {
let output = cx
.tcx
- .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
+ .erase_late_bound_regions(cx.tcx.fn_sig(owner_id).subst_identity().output());
ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
},
)
&& let subs = cx
.typeck_results()
.node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default()
- && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
+ && let impl_ty = if cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[0].is_ref() {
// Trait methods taking `&self`
sub_ty
} else {
return Some(Position::MethodReceiver);
}
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
- let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
+ let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i + 1];
// `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
// `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782
if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() {
} else {
ty_auto_deref_stability(
cx,
- cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
+ cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().input(i + 1)),
precedence,
)
.position_for_arg()
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
- let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+ let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder();
let substs_with_expr_ty = cx
.typeck_results()
.node_substs(if let ExprKind::Call(callee, _) = parent.kind {
.in_definition_order()
.any(|assoc_item| {
if assoc_item.fn_has_self_parameter {
- let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
+ let self_ty = cx.tcx.fn_sig(assoc_item.def_id).subst_identity().skip_binder().inputs()[0];
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
} else {
false
| ty::FnDef(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::Closure(..)
| ty::Never
| ty::Tuple(_)
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
use rustc_hir::{
- self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
+ self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource,
Unsafety,
};
use rustc_lint::{LateContext, LateLintPass};
TraitPredicate, Ty, TyCtxt,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::sym;
// Only care about `impl PartialEq<Foo> for Foo`
// For `impl PartialEq<B> for A, input_types is [A, B]
- if trait_ref.substs.type_at(1) == ty {
+ if trait_ref.subst_identity().substs.type_at(1) == ty {
span_lint_and_then(
cx,
DERIVED_HASH_WITH_MANUAL_EQ,
// Only care about `impl PartialOrd<Foo> for Foo`
// For `impl PartialOrd<B> for A, input_types is [A, B]
- if trait_ref.substs.type_at(1) == ty {
+ if trait_ref.subst_identity().substs.type_at(1) == ty {
let mess = if partial_ord_is_automatically_derived {
"you are implementing `Ord` explicitly but have derived `PartialOrd`"
} else {
impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
- fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: HirId) {
+ fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) {
if self.has_unsafe {
return;
}
use rustc_parse::parser::ForceCollect;
use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::def_id::LocalDefId;
use rustc_span::edition::Edition;
use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
use rustc_span::{sym, FileName, Pos};
/// unimplemented!();
/// }
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub UNNECESSARY_SAFETY_DOC,
restriction,
"`pub fn` or `pub trait` with `# Safety` docs"
panic_span: None,
};
fpu.visit_expr(body.value);
- lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
+ lint_for_missing_headers(cx, item.owner_id, sig, headers, Some(body_id), fpu.panic_span);
}
},
hir::ItemKind::Impl(impl_) => {
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
if !in_external_macro(cx.tcx.sess, item.span) {
- lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None);
+ lint_for_missing_headers(cx, item.owner_id, sig, headers, None, None);
}
}
}
panic_span: None,
};
fpu.visit_expr(body.value);
- lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
+ lint_for_missing_headers(cx, item.owner_id, sig, headers, Some(body_id), fpu.panic_span);
}
}
}
fn lint_for_missing_headers(
cx: &LateContext<'_>,
- def_id: LocalDefId,
+ owner_id: hir::OwnerId,
sig: &hir::FnSig<'_>,
headers: DocHeaders,
body_id: Option<hir::BodyId>,
panic_span: Option<Span>,
) {
- if !cx.effective_visibilities.is_exported(def_id) {
+ if !cx.effective_visibilities.is_exported(owner_id.def_id) {
return; // Private functions do not require doc comments
}
if cx
.tcx
.hir()
- .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
+ .parent_iter(owner_id.into())
.any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
{
return;
}
- let span = cx.tcx.def_span(def_id);
+ let span = cx.tcx.def_span(owner_id);
match (headers.safety, sig.header.unsafety) {
(false, hir::Unsafety::Unsafe) => span_lint(
cx,
);
}
if !headers.errors {
- let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
- if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
+ if is_type_diagnostic_item(cx, return_ty(cx, owner_id), sym::Result) {
span_lint(
cx,
MISSING_ERRORS_DOC,
Some(c) if is_word_beginning(c) => span_lint(
cx,
MODULE_NAME_REPETITIONS,
- item.span,
+ item.ident.span,
"item name starts with its containing module's name",
),
_ => (),
span_lint(
cx,
MODULE_NAME_REPETITIONS,
- item.span,
+ item.ident.span,
"item name ends with its containing module's name",
);
}
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, TraitRef, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::symbol::kw;
use rustc_target::spec::abi::Abi;
_: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
- hir_id: HirId,
+ fn_def_id: LocalDefId,
) {
if let Some(header) = fn_kind.header() {
if header.abi != Abi::Rust {
}
}
- let parent_id = cx.tcx.hir().get_parent_item(hir_id).def_id;
+ let parent_id = cx
+ .tcx
+ .hir()
+ .get_parent_item(cx.tcx.hir().local_def_id_to_hir_id(fn_def_id))
+ .def_id;
let parent_node = cx.tcx.hir().find_by_def_id(parent_id);
let mut trait_self_ty = None;
// find `self` ty for this trait if relevant
if let ItemKind::Trait(_, _, _, _, items) = item.kind {
for trait_item in items {
- if trait_item.id.hir_id() == hir_id {
+ if trait_item.id.owner_id.def_id == fn_def_id {
// be sure we have `self` parameter in this function
if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
trait_self_ty = Some(
too_large_for_stack: self.too_large_for_stack,
};
- let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
let infcx = cx.tcx.infer_ctxt().build();
ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
+use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
use rustc_target::spec::abi::Abi;
declare_clippy_lint! {
fn_decl: &'tcx FnDecl<'tcx>,
_: &'tcx Body<'tcx>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
if let Some(fn_header) = fn_kind.header()
&& fn_header.abi == Abi::Rust
&& get_parent_as_impl(cx.tcx, hir_id)
then {
let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
if v.fields().iter().any(|f| {
- let def_id = cx.tcx.hir().local_def_id(f.hir_id);
- !cx.tcx.visibility(def_id).is_public()
+ !cx.tcx.visibility(f.def_id).is_public()
}) {
// skip structs with private fields
return;
if_chain! {
if let hir::ItemKind::Impl(impl_) = &item.kind;
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
- if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id);
+ if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id);
then {
lint_impl_body(cx, item.span, impl_.items);
}
};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::ty::{implements_trait, is_type_lang_item};
use if_chain::if_chain;
use itertools::Itertools;
use rustc_errors::{
Applicability,
SuggestionStyle::{CompletelyHidden, ShowCode},
};
-use rustc_hir::{Expr, ExprKind, HirId, QPath};
+use rustc_hir::{Expr, ExprKind, HirId, LangItem, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
);
}
- if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() {
+ if is_type_lang_item(cx, param_ty, LangItem::FormatArguments) && !arg.format.is_default_for_trait() {
span_lint_and_then(
cx,
UNUSED_FORMAT_SPECS,
// in those cases, make the code suggestion hidden
let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span));
+ // Suggest removing each argument only once, for example in `format!("{0} {0}", arg)`.
+ fixes.sort_unstable_by_key(|(span, _)| *span);
+ fixes.dedup_by_key(|(span, _)| *span);
+
span_lint_and_then(
cx,
UNINLINED_FORMAT_ARGS,
&& let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
// `impl Into<target_ty> for self_ty`
&& let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
- && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+ && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity)
&& cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
&& !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _))
{
/// let _ = unsafe { Box::from_raw(ptr as *mut usize) };
/// ```
///
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub FROM_RAW_WITH_VOID_PTR,
suspicious,
"creating a `Box` from a void raw pointer"
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
-use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety};
+use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, ImplicitSelfKind, Unsafety};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;
decl: &FnDecl<'_>,
body: &Body<'_>,
span: Span,
- _hir_id: HirId,
) {
let FnKind::Method(ref ident, sig) = kind else {
return;
use rustc_hir::intravisit;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
declare_clippy_lint! {
decl: &'tcx hir::FnDecl<'_>,
body: &'tcx hir::Body<'_>,
span: Span,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
) {
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
- not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
- misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id);
+ not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id);
+ misnamed_getters::check_fn(cx, kind, decl, body, span);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
-use rustc_hir::def_id::{DefIdSet, LocalDefId};
+use rustc_hir::def_id::DefIdSet;
use rustc_hir::{self as hir, def::Res, QPath};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::{
let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
+ check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
} else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
check_must_use_candidate(
cx,
sig.decl,
cx.tcx.hir().body(*body_id),
item.span,
- item.owner_id.def_id,
+ item.owner_id,
item.span.with_hi(sig.decl.output.span().hi()),
"this function could have a `#[must_use]` attribute",
);
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
+ check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
} else if is_public
&& !is_proc_macro(cx.sess(), attrs)
&& trait_ref_of_method(cx, item.owner_id.def_id).is_none()
sig.decl,
cx.tcx.hir().body(*body_id),
item.span,
- item.owner_id.def_id,
+ item.owner_id,
item.span.with_hi(sig.decl.output.span().hi()),
"this method could have a `#[must_use]` attribute",
);
let attrs = cx.tcx.hir().attrs(item.hir_id());
let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
if let Some(attr) = attr {
- check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
+ check_needless_must_use(cx, sig.decl, item.owner_id, item.span, fn_header_span, attr);
} else if let hir::TraitFn::Provided(eid) = *eid {
let body = cx.tcx.hir().body(eid);
if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
sig.decl,
body,
item.span,
- item.owner_id.def_id,
+ item.owner_id,
item.span.with_hi(sig.decl.output.span().hi()),
"this method could have a `#[must_use]` attribute",
);
fn check_needless_must_use(
cx: &LateContext<'_>,
decl: &hir::FnDecl<'_>,
- item_id: hir::HirId,
+ item_id: hir::OwnerId,
item_span: Span,
fn_header_span: Span,
attr: &Attribute,
decl: &'tcx hir::FnDecl<'_>,
body: &'tcx hir::Body<'_>,
item_span: Span,
- item_id: LocalDefId,
+ item_id: hir::OwnerId,
fn_span: Span,
msg: &str,
) {
|| mutates_static(cx, body)
|| in_external_macro(cx.sess(), item_span)
|| returns_unit(decl)
- || !cx.effective_visibilities.is_exported(item_id)
- || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
+ || !cx.effective_visibilities.is_exported(item_id.def_id)
+ || is_must_use_ty(cx, return_ty(cx, item_id))
{
return;
}
kind: intravisit::FnKind<'tcx>,
decl: &'tcx hir::FnDecl<'tcx>,
body: &'tcx hir::Body<'tcx>,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
) {
let unsafety = match kind {
intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }) => unsafety,
intravisit::FnKind::Closure => return,
};
- check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id));
+ check_raw_ptr(cx, unsafety, decl, body, def_id)
}
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
},
hir::ExprKind::MethodCall(_, recv, args, _) => {
let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap();
- if cx.tcx.fn_sig(def_id).skip_binder().unsafety == hir::Unsafety::Unsafe {
+ if cx.tcx.fn_sig(def_id).skip_binder().skip_binder().unsafety == hir::Unsafety::Unsafe {
check_arg(cx, &raw_ptrs, recv);
for arg in args {
check_arg(cx, &raw_ptrs, arg);
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
if !in_external_macro(cx.sess(), item_span)
&& let hir::FnRetTy::Return(hir_ty) = decl.output
- && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).output())
+ && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).subst_identity().output())
&& is_type_diagnostic_item(cx, ty, sym::Result)
&& let ty::Adt(_, substs) = ty.kind()
{
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::return_ty;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, FnDecl, HirId};
+use rustc_hir::{Body, FnDecl};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AliasTy, Clause, EarlyBinder, PredicateKind};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, Span};
use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::{self, FulfillmentError};
decl: &'tcx FnDecl<'tcx>,
_: &'tcx Body<'tcx>,
_: Span,
- hir_id: HirId,
+ fn_def_id: LocalDefId,
) {
if let FnKind::Closure = kind {
return;
}
- let ret_ty = return_ty(cx, hir_id);
+ let ret_ty = return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(fn_def_id).expect_owner());
if let ty::Alias(ty::Opaque, AliasTy { def_id, substs, .. }) = *ret_ty.kind() {
let preds = cx.tcx.explicit_item_bounds(def_id);
let mut is_future = false;
let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
let span = decl.output.span();
let infcx = cx.tcx.infer_ctxt().build();
- let cause = traits::ObligationCause::misc(span, hir_id);
+ let cause = traits::ObligationCause::misc(span, fn_def_id);
let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait);
if !send_errors.is_empty() {
span_lint_and_then(
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::{Span, SyntaxContext};
declare_clippy_lint! {
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- _: HirId,
+ _: LocalDefId,
) {
if (!matches!(kind, FnKind::Closure) && matches!(decl.output, FnRetTy::DefaultReturn(_)))
|| span.ctxt() != body.value.span.ctxt()
// List of spans to lint. (lint_span, first_span)
let mut lint_spans = Vec::new();
- for (_, impl_ids) in cx
+ let inherent_impls = cx
.tcx
- .crate_inherent_impls(())
- .inherent_impls
- .iter()
- .filter(|(&id, impls)| {
- impls.len() > 1
- // Check for `#[allow]` on the type definition
- && !is_lint_allowed(
- cx,
- MULTIPLE_INHERENT_IMPL,
- cx.tcx.hir().local_def_id_to_hir_id(id),
- )
- })
- {
+ .with_stable_hashing_context(|hcx| cx.tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true));
+
+ for (_, impl_ids) in inherent_impls.into_iter().filter(|(&id, impls)| {
+ impls.len() > 1
+ // Check for `#[allow]` on the type definition
+ && !is_lint_allowed(
+ cx,
+ MULTIPLE_INHERENT_IMPL,
+ cx.tcx.hir().local_def_id_to_hir_id(id),
+ )
+ }) {
for impl_id in impl_ids.iter().map(|id| id.expect_local()) {
match type_map.entry(cx.tcx.type_of(impl_id)) {
Entry::Vacant(e) => {
if impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }));
// Check if return type is String
- if is_type_lang_item(cx, return_ty(cx, impl_item.hir_id()), LangItem::String);
+ if is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String);
// Filters instances of to_string which are required by a trait
if trait_ref_of_method(cx, impl_item.owner_id.def_id).is_none();
.expect("Failed to get trait ID of `Display`!");
// Get the real type of 'self'
- let self_type = cx.tcx.fn_sig(item.owner_id).input(0);
+ let self_type = cx.tcx.fn_sig(item.owner_id).skip_binder().input(0);
let self_type = self_type.skip_binder().peel_refs();
// Emit either a warning or an error
///
/// [`Duration`]: std::time::Duration
/// [`Instant::now()`]: std::time::Instant::now;
- #[clippy::version = "1.65.0"]
+ #[clippy::version = "1.67.0"]
pub UNCHECKED_DURATION_SUBTRACTION,
- suspicious,
+ pedantic,
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
}
fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) {
if sig.decl.implicit_self.has_implicit_self() {
- let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).output());
+ let ret_ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(fn_id).subst_identity().output());
let ret_ty = cx
.tcx
.try_normalize_erasing_regions(cx.param_env, ret_ty)
if let Some(local_id) = ty_id.as_local();
let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
- if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).skip_binder());
+ if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder());
then {
let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
item.ident.name == name
&& if let AssocItemKind::Fn { has_self } = item.kind {
- has_self && { cx.tcx.fn_sig(item.id.owner_id).inputs().skip_binder().len() == 1 }
+ has_self && { cx.tcx.fn_sig(item.id.owner_id).skip_binder().inputs().skip_binder().len() == 1 }
} else {
false
}
let is_empty = sym!(is_empty);
let is_empty_method_found = current_and_super_traits
- .iter()
+ .items()
.flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty))
.any(|i| {
i.kind == ty::AssocKind::Fn
&& i.fn_has_self_parameter
- && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
+ && cx.tcx.fn_sig(i.def_id).skip_binder().inputs().skip_binder().len() == 1
});
if !is_empty_method_found {
),
Some(is_empty)
if !(is_empty.fn_has_self_parameter
- && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) =>
+ && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).subst_identity().skip_binder(), self_kind, output)) =>
{
(
format!(
/// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
if item.kind == ty::AssocKind::Fn {
- let sig = cx.tcx.fn_sig(item.def_id);
+ let sig = cx.tcx.fn_sig(item.def_id).skip_binder();
let ty = sig.skip_binder();
ty.inputs().len() == 1
} else {
/// let _ = foo().await;
/// # }
/// ```
- #[clippy::version = "1.66"]
+ #[clippy::version = "1.67.0"]
pub LET_UNDERSCORE_FUTURE,
suspicious,
"non-binding `let` on a future"
#![feature(array_windows)]
#![feature(binary_heap_into_iter_sorted)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(drain_filter)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
mod mixed_read_write_in_expression;
mod module_style;
mod multi_assignments;
+mod multiple_unsafe_ops_per_block;
mod mut_key;
mod mut_mut;
mod mut_reference;
store.register_late_pass(|_| Box::new(fn_null_check::FnNullCheck));
store.register_late_pass(|_| Box::new(permissions_set_readonly_false::PermissionsSetReadonlyFalse));
store.register_late_pass(|_| Box::new(size_of_ref::SizeOfRef));
+ store.register_late_pass(|_| Box::new(multiple_unsafe_ops_per_block::MultipleUnsafeOpsPerBlock));
// add lints here, do not remove this comment, it's used in `new_lint`
}
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter as middle_nested_filter;
-use rustc_middle::ty::TyCtxt;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
for typ in types {
- for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
+ for pred in generics.bounds_for_param(typ.def_id) {
if pred.origin == PredicateOrigin::WhereClause {
// has_where_lifetimes checked that this predicate contains no lifetime.
continue;
// level of the current item.
// check named LTs
- let allowed_lts = allowed_lts_from(cx.tcx, named_generics);
+ let allowed_lts = allowed_lts_from(named_generics);
// these will collect all the lifetimes for references in arg/return types
let mut input_visitor = RefVisitor::new(cx);
}
}
-fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
+fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
let mut allowed_lts = FxHashSet::default();
for par in named_generics.iter() {
if let GenericParamKind::Lifetime { .. } = par.kind {
- allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id)));
+ allowed_lts.insert(RefLt::Named(par.def_id));
}
}
allowed_lts.insert(RefLt::Unnamed);
return true;
}
// if the bounds define new lifetimes, they are fine to occur
- let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params);
+ let allowed_lts = allowed_lts_from(pred.bound_generic_params);
// now walk the bounds
for bound in pred.bounds.iter() {
walk_param_bound(&mut visitor, bound);
ExprKind::MethodCall(_, receiver, args, _) => {
let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
for (ty, expr) in iter::zip(
- self.cx.tcx.fn_sig(def_id).inputs().skip_binder(),
+ self.cx.tcx.fn_sig(def_id).subst_identity().inputs().skip_binder(),
std::iter::once(receiver).chain(args.iter()),
) {
self.prefer_mutable = false;
} else {
return;
};
- let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
+ let mutable_static_in_cond = var_visitor.def_ids.items().any(|(_, v)| *v);
let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
has_break_or_return: false,
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
- HirId, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
+ ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, Span};
declare_clippy_lint! {
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- _: HirId,
+ _: LocalDefId,
) {
if_chain! {
if let Some(header) = kind.header();
/// 'A'.is_ascii_uppercase();
/// }
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub MANUAL_IS_ASCII_CHECK,
style,
"use dedicated method to check ascii range"
&& def.variants.len() > 1
{
let mut iter = def.variants.iter().filter_map(|v| {
- let id = cx.tcx.hir().local_def_id(v.hir_id);
- (matches!(v.data, hir::VariantData::Unit(..))
+ (matches!(v.data, hir::VariantData::Unit(_, _))
&& v.ident.as_str().starts_with('_')
&& is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
- .then_some((id, v.span))
+ .then_some((v.def_id, v.span))
});
if let Some((id, span)) = iter.next()
&& iter.next().is_none()
let ty = cx.typeck_results().expr_ty(expr);
if let ty::FnDef(id, _) = *ty.kind() {
- if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
+ if let Some(fn_type) = cx.tcx.fn_sig(id).subst_identity().no_bound_vars() {
return is_unit_type(fn_type.output());
}
}
from_args.push_front(from);
ControlFlow::Continue(())
} else {
- ControlFlow::BREAK
+ ControlFlow::Break(())
}
} else {
ControlFlow::Continue(())
if let hir::ExprKind::Path(ref p) = fun.kind {
match cx.qpath_res(p, fun.hir_id) {
hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
- cx.tcx.fn_sig(def_id).output().skip_binder().kind(),
+ cx.tcx.fn_sig(def_id).subst_identity().output().skip_binder().kind(),
ty::Ref(re, ..) if re.is_static(),
),
_ => false,
.type_dependent_def_id(arg.hir_id)
.map_or(false, |method_id| {
matches!(
- cx.tcx.fn_sig(method_id).output().skip_binder().kind(),
+ cx.tcx.fn_sig(method_id).subst_identity().output().skip_binder().kind(),
ty::Ref(re, ..) if re.is_static()
)
})
/// Ok(())
/// }
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub SEEK_FROM_CURRENT,
complexity,
"use dedicated method for seek from current position"
/// t.rewind();
/// }
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub SEEK_TO_START_INSTEAD_OF_REWIND,
complexity,
"jumping to the start of stream using `seek` method"
let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind {
- let method_sig = cx.tcx.fn_sig(impl_item.owner_id);
+ let method_sig = cx.tcx.fn_sig(impl_item.owner_id).subst_identity();
let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
let first_arg_ty_opt = method_sig.inputs().iter().next().copied();
// if this impl block implements a trait, lint in trait definition instead
}
if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
- let ret_ty = return_ty(cx, impl_item.hir_id());
+ let ret_ty = return_ty(cx, impl_item.owner_id);
if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
return;
if_chain! {
if item.ident.name == sym::new;
if let TraitItemKind::Fn(_, _) = item.kind;
- let ret_ty = return_ty(cx, item.hir_id());
+ let ret_ty = return_ty(cx, item.owner_id);
let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id())
.self_ty()
.skip_binder();
/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
- let sig = cx.tcx.fn_sig(id).skip_binder();
+ let sig = cx.tcx.fn_sig(id).subst_identity().skip_binder();
sig.inputs().len() == 1 && sig.output().is_bool()
})
}
fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool {
let typeck = cx.typeck_results();
if let Some(id) = typeck.type_dependent_def_id(call_id)
- && let sig = cx.tcx.fn_sig(id)
+ && let sig = cx.tcx.fn_sig(id).subst_identity()
&& sig.skip_binder().output().is_bool()
&& let [_, search_ty] = *sig.skip_binder().inputs()
&& let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
if_chain! {
if is_trait_method(cx, count_recv, sym::Iterator);
- let closure = expr_or_init(cx, map_arg);
- if let Some(def_id) = cx.tcx.hir().opt_local_def_id(closure.hir_id);
- if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id);
- let closure_body = cx.tcx.hir().body(body_id);
+ if let hir::ExprKind::Closure(closure) = expr_or_init(cx, map_arg).kind;
+ let closure_body = cx.tcx.hir().body(closure.body);
if !cx.typeck_results().expr_ty(closure_body.value).is_unit();
then {
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
if_chain! {
if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
- let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+ let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder();
if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id);
if let Some(input) = fn_sig.inputs().get(i);
let (input, n_refs) = peel_mid_ty_refs(*input);
Node::Block(..) => continue,
Node::Item(item) => {
if let ItemKind::Fn(_, _, body_id) = &item.kind
- && let output_ty = return_ty(cx, item.hir_id())
- && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
- && Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
- let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id());
+ && let output_ty = return_ty(cx, item.owner_id)
+ && Inherited::build(cx.tcx, item.owner_id.def_id).enter(|inherited| {
+ let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.owner_id.def_id);
fn_ctxt.can_coerce(ty, output_ty)
}) {
if has_lifetime(output_ty) && has_lifetime(ty) {
Node::Expr(parent_expr) => {
if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
{
- let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
+ let fn_sig = cx.tcx.fn_sig(callee_def_id).subst_identity().skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
&& let Some(param_ty) = fn_sig.inputs().get(arg_index)
&& let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
- self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind,
- Stmt, StmtKind, TyKind,
+ self as hir, def, BinOpKind, BindingAnnotation, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, Stmt,
+ StmtKind, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::{ExpnKind, Span};
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- _: HirId,
+ _: LocalDefId,
) {
if let FnKind::Closure = k {
// Does not apply to closures
use rustc_hir as hir;
use rustc_hir::def_id::CRATE_DEF_ID;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
+use rustc_hir::{Body, Constness, FnDecl, GenericParamKind};
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
declare_clippy_lint! {
_: &FnDecl<'_>,
body: &Body<'tcx>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
return;
}
- let def_id = cx.tcx.hir().local_def_id(hir_id);
-
if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
return;
}
FnKind::Closure => return,
}
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
+
// Const fns are not allowed as methods in a trait.
{
let parent = cx.tcx.hir().get_parent_item(hir_id).def_id;
let container_id = assoc_item.container_id(cx.tcx);
let trait_def_id = match assoc_item.container {
TraitContainer => Some(container_id),
- ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.def_id),
+ ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
};
if let Some(trait_def_id) = trait_def_id {
}
}
- for assoc in provided.values() {
- let source_map = cx.tcx.sess.source_map();
- let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
+ cx.tcx.with_stable_hashing_context(|hcx| {
+ for assoc in provided.values_sorted(&hcx, true) {
+ let source_map = cx.tcx.sess.source_map();
+ let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
- span_lint_and_help(
- cx,
- MISSING_TRAIT_METHODS,
- source_map.guess_head_span(item.span),
- &format!("missing trait method provided by default: `{}`", assoc.name),
- Some(definition_span),
- "implement the method",
- );
- }
+ span_lint_and_help(
+ cx,
+ MISSING_TRAIT_METHODS,
+ source_map.guess_head_span(item.span),
+ &format!("missing trait method provided by default: `{}`", assoc.name),
+ Some(definition_span),
+ "implement the method",
+ );
+ }
+ });
}
}
}
--- /dev/null
+use clippy_utils::{
+ diagnostics::span_lint_and_then,
+ visitors::{for_each_expr_with_closures, Descend, Visitable},
+};
+use core::ops::ControlFlow::Continue;
+use hir::{
+ def::{DefKind, Res},
+ BlockCheckMode, ExprKind, QPath, UnOp, Unsafety,
+};
+use rustc_ast::Mutability;
+use rustc_hir as hir;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `unsafe` blocks that contain more than one unsafe operation.
+ ///
+ /// ### Why is this bad?
+ /// Combined with `undocumented_unsafe_blocks`,
+ /// this lint ensures that each unsafe operation must be independently justified.
+ /// Combined with `unused_unsafe`, this lint also ensures
+ /// elimination of unnecessary unsafe blocks through refactoring.
+ ///
+ /// ### Example
+ /// ```rust
+ /// /// Reads a `char` from the given pointer.
+ /// ///
+ /// /// # Safety
+ /// ///
+ /// /// `ptr` must point to four consecutive, initialized bytes which
+ /// /// form a valid `char` when interpreted in the native byte order.
+ /// fn read_char(ptr: *const u8) -> char {
+ /// // SAFETY: The caller has guaranteed that the value pointed
+ /// // to by `bytes` is a valid `char`.
+ /// unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// /// Reads a `char` from the given pointer.
+ /// ///
+ /// /// # Safety
+ /// ///
+ /// /// - `ptr` must be 4-byte aligned, point to four consecutive
+ /// /// initialized bytes, and be valid for reads of 4 bytes.
+ /// /// - The bytes pointed to by `ptr` must represent a valid
+ /// /// `char` when interpreted in the native byte order.
+ /// fn read_char(ptr: *const u8) -> char {
+ /// // SAFETY: `ptr` is 4-byte aligned, points to four consecutive
+ /// // initialized bytes, and is valid for reads of 4 bytes.
+ /// let int_value = unsafe { *ptr.cast::<u32>() };
+ ///
+ /// // SAFETY: The caller has guaranteed that the four bytes
+ /// // pointed to by `bytes` represent a valid `char`.
+ /// unsafe { char::from_u32_unchecked(int_value) }
+ /// }
+ /// ```
+ #[clippy::version = "1.68.0"]
+ pub MULTIPLE_UNSAFE_OPS_PER_BLOCK,
+ restriction,
+ "more than one unsafe operation per `unsafe` block"
+}
+declare_lint_pass!(MultipleUnsafeOpsPerBlock => [MULTIPLE_UNSAFE_OPS_PER_BLOCK]);
+
+impl<'tcx> LateLintPass<'tcx> for MultipleUnsafeOpsPerBlock {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
+ if !matches!(block.rules, BlockCheckMode::UnsafeBlock(_)) {
+ return;
+ }
+ let mut unsafe_ops = vec![];
+ collect_unsafe_exprs(cx, block, &mut unsafe_ops);
+ if unsafe_ops.len() > 1 {
+ span_lint_and_then(
+ cx,
+ MULTIPLE_UNSAFE_OPS_PER_BLOCK,
+ block.span,
+ &format!(
+ "this `unsafe` block contains {} unsafe operations, expected only one",
+ unsafe_ops.len()
+ ),
+ |diag| {
+ for (msg, span) in unsafe_ops {
+ diag.span_note(span, msg);
+ }
+ },
+ );
+ }
+ }
+}
+
+fn collect_unsafe_exprs<'tcx>(
+ cx: &LateContext<'tcx>,
+ node: impl Visitable<'tcx>,
+ unsafe_ops: &mut Vec<(&'static str, Span)>,
+) {
+ for_each_expr_with_closures(cx, node, |expr| {
+ match expr.kind {
+ ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
+
+ ExprKind::Field(e, _) => {
+ if cx.typeck_results().expr_ty(e).is_union() {
+ unsafe_ops.push(("union field access occurs here", expr.span));
+ }
+ },
+
+ ExprKind::Path(QPath::Resolved(
+ _,
+ hir::Path {
+ res: Res::Def(DefKind::Static(Mutability::Mut), _),
+ ..
+ },
+ )) => {
+ unsafe_ops.push(("access of a mutable static occurs here", expr.span));
+ },
+
+ ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty_adjusted(e).is_unsafe_ptr() => {
+ unsafe_ops.push(("raw pointer dereference occurs here", expr.span));
+ },
+
+ ExprKind::Call(path_expr, _) => match path_expr.kind {
+ ExprKind::Path(QPath::Resolved(
+ _,
+ hir::Path {
+ res: Res::Def(kind, def_id),
+ ..
+ },
+ )) if kind.is_fn_like() => {
+ let sig = cx.tcx.fn_sig(*def_id);
+ if sig.0.unsafety() == Unsafety::Unsafe {
+ unsafe_ops.push(("unsafe function call occurs here", expr.span));
+ }
+ },
+
+ ExprKind::Path(QPath::TypeRelative(..)) => {
+ if let Some(sig) = cx
+ .typeck_results()
+ .type_dependent_def_id(path_expr.hir_id)
+ .map(|def_id| cx.tcx.fn_sig(def_id))
+ {
+ if sig.0.unsafety() == Unsafety::Unsafe {
+ unsafe_ops.push(("unsafe function call occurs here", expr.span));
+ }
+ }
+ },
+
+ _ => {},
+ },
+
+ ExprKind::MethodCall(..) => {
+ if let Some(sig) = cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map(|def_id| cx.tcx.fn_sig(def_id))
+ {
+ if sig.0.unsafety() == Unsafety::Unsafe {
+ unsafe_ops.push(("unsafe method call occurs here", expr.span));
+ }
+ }
+ },
+
+ ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
+ if matches!(
+ lhs.kind,
+ ExprKind::Path(QPath::Resolved(
+ _,
+ hir::Path {
+ res: Res::Def(DefKind::Static(Mutability::Mut), _),
+ ..
+ }
+ ))
+ ) {
+ unsafe_ops.push(("modification of a mutable static occurs here", expr.span));
+ collect_unsafe_exprs(cx, rhs, unsafe_ops);
+ return Continue(Descend::No);
+ }
+ },
+
+ _ => {},
+ };
+
+ Continue::<(), _>(Descend::Yes)
+ });
+}
use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::symbol::sym;
use std::iter;
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
- self.check_sig(cx, item.hir_id(), sig.decl);
+ self.check_sig(cx, item.owner_id.def_id, sig.decl);
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
if trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
- self.check_sig(cx, item.hir_id(), sig.decl);
+ self.check_sig(cx, item.owner_id.def_id, sig.decl);
}
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
- self.check_sig(cx, item.hir_id(), sig.decl);
+ self.check_sig(cx, item.owner_id.def_id, sig.decl);
}
}
}
}
- fn check_sig(&self, cx: &LateContext<'_>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
- let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
- let fn_sig = cx.tcx.fn_sig(fn_def_id);
+ fn check_sig(&self, cx: &LateContext<'_>, fn_def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
+ let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity();
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
self.check_ty_(cx, hir_ty.span, *ty);
}
use rustc_middle::mir::FakeReadCause;
use rustc_middle::ty::{self, TypeVisitable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::kw;
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::misc::can_type_implement_copy;
+use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy;
use std::borrow::Cow;
declare_clippy_lint! {
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- hir_id: HirId,
+ fn_def_id: LocalDefId,
) {
if span.from_expansion() {
return;
}
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def_id);
+
match kind {
FnKind::ItemFn(.., header) => {
let attrs = cx.tcx.hir().attrs(hir_id);
let sized_trait = need!(cx.tcx.lang_items().sized_trait());
- let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
-
let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
.filter(|p| !p.is_global())
.filter_map(|obligation| {
ctx
};
- let fn_sig = cx.tcx.fn_sig(fn_def_id);
+ let fn_sig = cx.tcx.fn_sig(fn_def_id).subst_identity();
let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
let sugg = |diag: &mut Diagnostic| {
if let ty::Adt(def, ..) = ty.kind() {
if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
- if can_type_implement_copy(
+ if type_allowed_to_implement_copy(
cx.tcx,
cx.param_env,
ty,
}
if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
let name = impl_item.ident.name;
- let id = impl_item.hir_id();
+ let id = impl_item.owner_id;
if sig.header.constness == hir::Constness::Const {
// can't be implemented by default
return;
if sig.decl.inputs.is_empty();
if name == sym::new;
if cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id);
- let self_def_id = cx.tcx.hir().get_parent_item(id);
+ let self_def_id = cx.tcx.hir().get_parent_item(id.into());
let self_ty = cx.tcx.type_of(self_def_id);
if self_ty == return_ty(cx, id);
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
span_lint_hir_and_then(
cx,
NEW_WITHOUT_DEFAULT,
- id,
+ id.into(),
impl_item.span,
&format!(
"you should consider adding a `Default` implementation for `{self_type_snip}`"
if send_trait == trait_id;
if hir_impl.polarity == ImplPolarity::Positive;
if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
- if let self_ty = ty_trait_ref.self_ty();
+ if let self_ty = ty_trait_ref.subst_identity().self_ty();
if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
then {
let mut non_send_fields = Vec::new();
use rustc_hir::hir_id::HirIdMap;
use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::ty::subst::{EarlyBinder, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, ConstKind};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Ident};
})) => {
#[allow(trivial_casts)]
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
- && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+ && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(EarlyBinder::subst_identity)
&& let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
{
(
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner(body.id());
- let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
+ let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id());
+
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind {
let body_span = cx.tcx.hir().span_with_body(body_owner);
pub fn enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner(body.id());
- let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
+ let body_owner_def_id = cx.tcx.hir().body_owner_def_id(body.id());
match cx.tcx.hir().body_owner_kind(body_owner_def_id) {
hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::{sym, Span};
declare_clippy_lint! {
_: &'tcx hir::FnDecl<'tcx>,
body: &'tcx hir::Body<'tcx>,
span: Span,
- hir_id: hir::HirId,
+ def_id: LocalDefId,
) {
- if !matches!(fn_kind, FnKind::Closure) && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
+ if matches!(fn_kind, FnKind::Closure) {
+ return;
+ }
+ let owner = cx.tcx.hir().local_def_id_to_hir_id(def_id).expect_owner();
+ if is_type_diagnostic_item(cx, return_ty(cx, owner), sym::Result) {
lint_impl_body(cx, span, body);
}
}
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
+use rustc_hir::{BindingAnnotation, Body, FnDecl, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, PointerCast};
use rustc_middle::ty::layout::LayoutOf;
return;
}
- let fn_sig = cx.tcx.fn_sig(def_id);
+ let fn_sig = cx.tcx.fn_sig(def_id).subst_identity();
let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
// Gather all the lifetimes found in the output type which may affect whether
// Don't lint if an unsafe pointer is created.
// TODO: Limit the check only to unsafe pointers to the argument (or part of the argument)
// which escape the current function.
- if typeck.node_types().iter().any(|(_, &ty)| ty.is_unsafe_ptr())
+ if typeck.node_types().items().any(|(_, &ty)| ty.is_unsafe_ptr())
|| typeck
.adjustments()
- .iter()
+ .items()
.flat_map(|(_, a)| a)
.any(|a| matches!(a.kind, Adjust::Pointer(PointerCast::UnsafeFnPointer)))
{
decl: &'tcx FnDecl<'_>,
_body: &'tcx Body<'_>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
if span.from_expansion() {
return;
}
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
match kind {
FnKind::ItemFn(.., header) => {
if header.abi != Abi::Rust {
}
}
- self.check_poly_fn(cx, cx.tcx.hir().local_def_id(hir_id), decl, Some(span));
+ self.check_poly_fn(cx, def_id, decl, Some(span));
}
}
use clippy_utils::diagnostics::span_lint_and_help;
-use rustc_hir::{
- intravisit, Body, Expr, ExprKind, FnDecl, HirId, Let, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
-};
+use rustc_hir::{intravisit, Body, Expr, ExprKind, FnDecl, Let, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
declare_clippy_lint! {
_: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
_: Span,
- _: HirId,
+ _: LocalDefId,
) {
for param in body.params {
apply_lint(cx, param.pat, DerefPossible::Impossible);
check_mut_from_ref(cx, sig, None);
for arg in check_fn_args(
cx,
- cx.tcx.fn_sig(item.owner_id).skip_binder().inputs(),
+ cx.tcx.fn_sig(item.owner_id).subst_identity().skip_binder().inputs(),
sig.decl.inputs,
&[],
)
check_mut_from_ref(cx, sig, Some(body));
let decl = sig.decl;
- let sig = cx.tcx.fn_sig(item_id).skip_binder();
+ let sig = cx.tcx.fn_sig(item_id).subst_identity().skip_binder();
let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
.filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
.collect();
return;
};
- match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
+ match *self.cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i].peel_refs().kind() {
ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
set_skip_flag();
},
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{def_id, Body, FnDecl, HirId, LangItem};
+use rustc_hir::{def_id, Body, FnDecl, LangItem};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::{BytePos, Span};
use rustc_span::sym;
cx: &LateContext<'tcx>,
_: FnKind<'tcx>,
_: &'tcx FnDecl<'_>,
- body: &'tcx Body<'_>,
+ _: &'tcx Body<'_>,
_: Span,
- _: HirId,
+ def_id: LocalDefId,
) {
- let def_id = cx.tcx.hir().body_owner_def_id(body.id());
-
// Building MIR for `fn`s with unsatisfiable preds results in ICE.
if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
return;
use clippy_utils::{nth_arg, return_ty};
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind};
+use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]);
-fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) {
+fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, owner_id: OwnerId) {
if_chain! {
// If it comes from an external macro, better ignore it.
if !in_external_macro(cx.sess(), span);
// We only show this warning for public exported methods.
if cx.effective_visibilities.is_exported(fn_def);
// We don't want to emit this lint if the `#[must_use]` attribute is already there.
- if !cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::must_use));
+ if !cx.tcx.hir().attrs(owner_id.into()).iter().any(|attr| attr.has_name(sym::must_use));
if cx.tcx.visibility(fn_def.to_def_id()).is_public();
- let ret_ty = return_ty(cx, hir_id);
- let self_arg = nth_arg(cx, hir_id, 0);
+ let ret_ty = return_ty(cx, owner_id.into());
+ let self_arg = nth_arg(cx, owner_id.into(), 0);
// If `Self` has the same type as the returned type, then we want to warn.
//
// For this check, we don't want to remove the reference on the returned type because if
decl: &'tcx FnDecl<'tcx>,
_: &'tcx Body<'tcx>,
span: Span,
- hir_id: HirId,
+ fn_def: LocalDefId,
) {
if_chain! {
// We are only interested in methods, not in functions or associated functions.
if matches!(kind, FnKind::Method(_, _));
- if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id);
if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id());
// We don't want this method to be te implementation of a trait because the
// `#[must_use]` should be put on the trait definition directly.
if cx.tcx.trait_id_of_impl(impl_def).is_none();
then {
- check_method(cx, decl, fn_def, span, hir_id);
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(fn_def);
+ check_method(cx, decl, fn_def, span, hir_id.expect_owner());
}
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
if let TraitItemKind::Fn(ref sig, _) = item.kind {
- check_method(cx, sig.decl, item.owner_id.def_id, item.span, item.hir_id());
+ check_method(cx, sig.decl, item.owner_id.def_id, item.span, item.owner_id);
}
}
}
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::visitors::{for_each_expr, Descend};
-use clippy_utils::{fn_def_id, path_to_local_id};
+use clippy_utils::{fn_def_id, path_to_local_id, span_find_starting_semi};
use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, LangItem, MatchSource, PatKind, QPath, StmtKind};
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem, MatchSource, PatKind, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::{BytePos, Pos};
kind: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
- _: Span,
- _: HirId,
+ sp: Span,
+ _: LocalDefId,
) {
match kind {
FnKind::Closure => {
check_final_expr(cx, body.value, vec![], replacement);
},
FnKind::ItemFn(..) | FnKind::Method(..) => {
- check_block_return(cx, &body.value.kind, vec![]);
+ check_block_return(cx, &body.value.kind, sp, vec![]);
},
}
}
}
// if `expr` is a block, check if there are needless returns in it
-fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec<Span>) {
+fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
if let ExprKind::Block(block, _) = expr_kind {
if let Some(block_expr) = block.expr {
check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
},
StmtKind::Semi(semi_expr) => {
- let mut semi_spans_and_this_one = semi_spans;
- // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382`
- if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) {
- semi_spans_and_this_one.push(semicolon_span);
- check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty);
+ // Remove ending semicolons and any whitespace ' ' in between.
+ // Without `return`, the suggestion might not compile if the semicolon is retained
+ if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) {
+ let semi_span_to_remove =
+ span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
+ semi_spans.push(semi_span_to_remove);
}
+ check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty);
},
_ => (),
}
emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
},
ExprKind::If(_, then, else_clause_opt) => {
- check_block_return(cx, &then.kind, semi_spans.clone());
+ check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
if let Some(else_clause) = else_clause_opt {
- check_block_return(cx, &else_clause.kind, semi_spans);
+ check_block_return(cx, &else_clause.kind, peeled_drop_expr.span, semi_spans);
}
},
// a match expr, check all arms
}
},
// if it's a whole block, check it
- other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans),
+ other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans),
}
}
&& cx
.tcx
.fn_sig(def_id)
+ .subst_identity()
.skip_binder()
.output()
.walk()
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
let item = cx.tcx.hir().expect_item(parent);
let self_ty = cx.tcx.type_of(item.owner_id);
- let ret_ty = return_ty(cx, impl_item.hir_id());
+ let ret_ty = return_ty(cx, impl_item.owner_id);
// Do not check trait impls
if matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. })) {
/// ### What it does
/// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal.
/// ### Why is this bad?
- /// It's most probably a typo and may lead to unexpected behaviours.
+ /// It's most probably a typo and may lead to unexpected behaviours.
/// ### Example
/// ```rust
/// let x = 3_i32 ^ 4_i32;
/// ```rust
/// let x = 3_i32.pow(4);
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub SUSPICIOUS_XOR_USED_AS_POW,
restriction,
"XOR (`^`) operator possibly used as exponentiation operator"
if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind;
// Then check if that that array zero-sized
- let length_ldid = cx.tcx.hir().local_def_id(length.hir_id);
- let length = Const::from_anon_const(cx.tcx, length_ldid);
+ let length = Const::from_anon_const(cx.tcx, length.def_id);
let length = length.try_eval_usize(cx.tcx, cx.param_env);
if let Some(length) = length;
then {
// - char conversions (https://github.com/rust-lang/rust/issues/89259)
let const_context = in_constant(cx, e.hir_id);
- let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
+ let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) {
+ [] => (cx.typeck_results().expr_ty(arg), false),
+ [.., a] => (a.target, true),
+ };
// Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
let to_ty = cx.typeck_results().expr_ty(e);
);
if !linted {
- transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
+ transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, from_ty_adjusted, to_ty, arg);
}
}
}
-use super::utils::can_be_expressed_as_pointer_cast;
+use super::utils::check_cast;
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{cast::CastKind, Ty};
/// Checks for `transmutes_expressible_as_ptr_casts` lint.
/// Returns `true` if it's triggered, otherwise returns `false`.
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
+ from_ty_adjusted: bool,
to_ty: Ty<'tcx>,
arg: &'tcx Expr<'_>,
) -> bool {
- if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) {
- span_lint_and_then(
- cx,
- TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
- e.span,
- &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"),
- |diag| {
- if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
- let sugg = arg.as_ty(to_ty.to_string()).to_string();
- diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
- }
- },
- );
- true
- } else {
- false
- }
+ use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
+ let mut app = Applicability::MachineApplicable;
+ let sugg = match check_cast(cx, e, from_ty, to_ty) {
+ Some(PtrPtrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast) => {
+ Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)
+ .as_ty(to_ty.to_string())
+ .to_string()
+ },
+ Some(PtrAddrCast) if !from_ty_adjusted => Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app)
+ .as_ty(to_ty.to_string())
+ .to_string(),
+
+ // The only adjustments here would be ref-to-ptr and unsize coercions. The result of an unsize coercions can't
+ // be transmuted to a usize. For ref-to-ptr coercions, borrows need to be cast to a pointer before being cast to
+ // a usize.
+ Some(PtrAddrCast) => format!(
+ "{} as {to_ty}",
+ Sugg::hir_with_context(cx, arg, e.span.ctxt(), "..", &mut app).as_ty(from_ty)
+ ),
+ _ => return false,
+ };
+
+ span_lint_and_sugg(
+ cx,
+ TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
+ e.span,
+ &format!("transmute from `{from_ty}` to `{to_ty}` which could be expressed as a pointer cast instead"),
+ "try",
+ sugg,
+ app,
+ );
+ true
}
}
}
-/// Check if the type conversion can be expressed as a pointer cast, instead of
-/// a transmute. In certain cases, including some invalid casts from array
-/// references to pointers, this may cause additional errors to be emitted and/or
-/// ICE error messages. This function will panic if that occurs.
-pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
- cx: &LateContext<'tcx>,
- e: &'tcx Expr<'_>,
- from_ty: Ty<'tcx>,
- to_ty: Ty<'tcx>,
-) -> bool {
- use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
- matches!(
- check_cast(cx, e, from_ty, to_ty),
- Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
- )
-}
-
/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
/// the cast. In certain cases, including some invalid casts from array references
/// to pointers, this may cause additional errors to be emitted and/or ICE error
/// messages. This function will panic if that occurs.
-fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
+pub(super) fn check_cast<'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx Expr<'_>,
+ from_ty: Ty<'tcx>,
+ to_ty: Ty<'tcx>,
+) -> Option<CastKind> {
let hir_id = e.hir_id;
let local_def_id = hir_id.owner.def_id;
Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
- let fn_ctxt = FnCtxt::new(inherited, cx.param_env, hir_id);
+ let fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_def_id);
// If we already have errors, we can't be sure we can pointer cast.
assert!(
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
- Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
+ Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
TraitItemKind, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
declare_clippy_lint! {
impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
impl<'tcx> LateLintPass<'tcx> for Types {
- fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
- let is_in_trait_impl =
- if let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(id).def_id) {
- matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
- } else {
- false
- };
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'_>,
+ _: FnKind<'_>,
+ decl: &FnDecl<'_>,
+ _: &Body<'_>,
+ _: Span,
+ def_id: LocalDefId,
+ ) {
+ let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(
+ cx.tcx
+ .hir()
+ .get_parent_item(cx.tcx.hir().local_def_id_to_hir_id(def_id))
+ .def_id,
+ ) {
+ matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
+ } else {
+ false
+ };
- let is_exported = cx.effective_visibilities.is_exported(cx.tcx.hir().local_def_id(id));
+ let is_exported = cx.effective_visibilities.is_exported(def_id);
self.check_fn_decl(
cx,
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
let is_exported = cx
.effective_visibilities
- .is_exported(cx.tcx.hir().local_def_id(field.hir_id));
+ .is_exported(field.def_id);
self.check_ty(
cx,
expr: &'tcx hir::Expr<'tcx>,
comment_pos: BytePos,
) -> Option<Span> {
+ if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| {
+ matches!(
+ node,
+ Node::Block(&Block {
+ rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+ ..
+ }),
+ )
+ }) {
+ return None;
+ }
+
// this should roughly be the reverse of `block_parents_have_safety_comment`
if for_each_expr_with_closures(cx, expr, |expr| match expr.kind {
hir::ExprKind::Block(
fn get_args_to_check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Vec<(usize, String)> {
let mut args_to_check = Vec::new();
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
- let fn_sig = cx.tcx.fn_sig(def_id);
+ let fn_sig = cx.tcx.fn_sig(def_id).subst_identity();
let generics = cx.tcx.predicates_of(def_id);
let fn_mut_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().fn_mut_trait());
let ord_preds = get_trait_predicates_for_trait_id(cx, generics, cx.tcx.get_diagnostic_item(sym::Ord));
},
_ => return false,
};
- let sig = cx.tcx.fn_sig(id).skip_binder();
+ let sig = cx.tcx.fn_sig(id).subst_identity().skip_binder();
if let ty::Param(output_ty) = *sig.output().kind() {
let args: Vec<&Expr<'_>> = if let Some(receiver) = receiver {
std::iter::once(receiver).chain(args.iter()).collect()
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::LangItem::{OptionSome, ResultOk};
-use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
+use rustc_hir::{Body, ExprKind, FnDecl, Impl, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::sym;
use rustc_span::Span;
fn_decl: &FnDecl<'tcx>,
body: &Body<'tcx>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
// Abort if public function/method or closure.
match fn_kind {
FnKind::ItemFn(..) | FnKind::Method(..) => {
- let def_id = cx.tcx.hir().local_def_id(hir_id);
if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
return;
}
}
// Abort if the method is implementing a trait or of it a trait method.
+ let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
if let Some(Node::Item(item)) = cx.tcx.hir().find_parent(hir_id) {
if matches!(
item.kind,
}
// Get the wrapper and inner types, if can't, abort.
- let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
- if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
- ("Option", OptionSome, subst.type_at(0))
- } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
- ("Result", ResultOk, subst.type_at(0))
+ let (return_type_label, lang_item, inner_type) =
+ if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() {
+ if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
+ ("Option", OptionSome, subst.type_at(0))
+ } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
+ ("Result", ResultOk, subst.type_at(0))
+ } else {
+ return;
+ }
} else {
return;
- }
- } else {
- return;
- };
+ };
// Check if all return expression respect the following condition and collect them.
let mut suggs = Vec::new();
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, YieldSource};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
declare_clippy_lint! {
fn_decl: &'tcx FnDecl<'tcx>,
body: &Body<'tcx>,
span: Span,
- hir_id: HirId,
+ def_id: LocalDefId,
) {
if !span.from_expansion() && fn_kind.asyncness().is_async() {
let mut visitor = AsyncFnVisitor { cx, found_await: false };
- walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), hir_id);
+ walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
if !visitor.found_await {
span_lint_and_help(
cx,
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_span::sym;
decl: &'tcx FnDecl<'_>,
body: &'tcx Body<'_>,
span: Span,
- fn_id: HirId,
+ fn_id: LocalDefId,
) {
if span.from_expansion() {
return;
// first check if it's a method or function
if let hir::ImplItemKind::Fn(ref _signature, _) = impl_item.kind;
// checking if its return type is `result` or `option`
- if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Result)
- || is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::Option);
+ if is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result)
+ || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option);
then {
lint_impl_body(cx, impl_item.span, impl_item);
}
then {
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
// `Self`.
- let self_ty = impl_trait_ref.self_ty();
+ let self_ty = impl_trait_ref.subst_identity().self_ty();
// `trait_method_sig` is the signature of the function, how it is declared in the
// trait, not in the impl of the trait.
.associated_item(impl_item.owner_id)
.trait_item_def_id
.expect("impl method matches a trait method");
- let trait_method_sig = cx.tcx.fn_sig(trait_method);
+ let trait_method_sig = cx.tcx.fn_sig(trait_method).subst_identity();
let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
// `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
///
/// #### Noteworthy
///
- /// A type, say `SomeType`, listed in this configuration has the same behavior of `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
+ /// A type, say `SomeType`, listed in this configuration has the same behavior of
+ /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
(arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
/// Lint: ARITHMETIC_SIDE_EFFECTS.
///
use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
use if_chain::if_chain;
+use itertools::Itertools;
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{
use std::path::PathBuf;
use std::process::Command;
-/// This is the output file of the lint collector.
-const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
+/// This is the json output file of the lint collector.
+const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
+/// This is the markdown output file of the lint collector.
+const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md";
/// These lints are excluded from the export.
const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
/// These groups will be ignored by the lint group matcher. This is useful for collections like
)
})
}
+
+ fn configs_to_markdown(&self, map_fn: fn(&ClippyConfiguration) -> String) -> String {
+ self.config
+ .iter()
+ .filter(|config| config.deprecation_reason.is_none())
+ .filter(|config| !config.lints.is_empty())
+ .map(map_fn)
+ .join("\n")
+ }
+
+ fn get_markdown_docs(&self) -> String {
+ format!(
+ "## Lint Configuration Options\n| <div style=\"width:290px\">Option</div> | Default Value |\n|--|--|\n{}\n\n{}\n",
+ self.configs_to_markdown(ClippyConfiguration::to_markdown_table_entry),
+ self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph),
+ )
+ }
}
impl Drop for MetadataCollector {
collect_renames(&mut lints);
- // Outputting
- if Path::new(OUTPUT_FILE).exists() {
- fs::remove_file(OUTPUT_FILE).unwrap();
+ // Outputting json
+ if Path::new(JSON_OUTPUT_FILE).exists() {
+ fs::remove_file(JSON_OUTPUT_FILE).unwrap();
}
- let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
+ let mut file = OpenOptions::new()
+ .write(true)
+ .create(true)
+ .open(JSON_OUTPUT_FILE)
+ .unwrap();
writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
+
+ // Outputting markdown
+ if Path::new(MARKDOWN_OUTPUT_FILE).exists() {
+ fs::remove_file(MARKDOWN_OUTPUT_FILE).unwrap();
+ }
+ let mut file = OpenOptions::new()
+ .write(true)
+ .create(true)
+ .open(MARKDOWN_OUTPUT_FILE)
+ .unwrap();
+ writeln!(
+ file,
+ "<!--
+This file is generated by `cargo collect-metadata`.
+Please use that command to update the file and do not edit it by hand.
+-->
+
+{}",
+ self.get_markdown_docs(),
+ )
+ .unwrap();
}
}
deprecation_reason,
}
}
+
+ fn to_markdown_paragraph(&self) -> String {
+ format!(
+ "### {}\n{}\n\n**Default Value:** `{}` (`{}`)\n\n{}\n\n",
+ self.name,
+ self.doc
+ .lines()
+ .map(|line| line.strip_prefix(" ").unwrap_or(line))
+ .join("\n"),
+ self.default,
+ self.config_type,
+ self.lints
+ .iter()
+ .map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
+ .map(|name| format!("* [{name}](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
+ .join("\n"),
+ )
+ }
+
+ fn to_markdown_table_entry(&self) -> String {
+ format!("| [{}](#{}) | `{}` |", self.name, self.name, self.default)
+ }
}
fn collect_configs() -> Vec<ClippyConfiguration> {
[package]
name = "clippy_utils"
-version = "0.1.68"
+version = "0.1.69"
edition = "2021"
publish = false
&& subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
{
// Limit the function to either `(self) -> bool` or `(&self) -> bool`
- match &**cx.tcx.fn_sig(fn_id).skip_binder().inputs_and_output {
+ match &**cx.tcx.fn_sig(fn_id).subst_identity().skip_binder().inputs_and_output {
[arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange,
_ => Lazy,
}
#![feature(array_chunks)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(let_chains)]
#![feature(lint_reasons)]
#![feature(never_type)]
self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
}
},
- ExprKind::Closure { .. } => {
- let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
- for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
+ ExprKind::Closure(closure) => {
+ for capture in self.cx.typeck_results().closure_min_captures_flattened(closure.def_id) {
let local_id = match capture.place.base {
PlaceBase::Local(id) => id,
PlaceBase::Upvar(var) => var.var_path.hir_id,
.chain(args.iter())
.position(|arg| arg.hir_id == id)?;
let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
- let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+ let ty = cx.tcx.fn_sig(id).subst_identity().skip_binder().inputs()[i];
ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
},
_ => None,
}
/// Convenience function to get the return type of a function.
-pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
- let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
- let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
+pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId) -> Ty<'tcx> {
+ let ret_ty = cx.tcx.fn_sig(fn_def_id).subst_identity().output();
cx.tcx.erase_late_bound_regions(ret_ty)
}
/// Convenience function to get the nth argument type of a function.
-pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
- let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
- let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
+pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_def_id: hir::OwnerId, nth: usize) -> Ty<'tcx> {
+ let arg = cx.tcx.fn_sig(fn_def_id).subst_identity().input(nth);
cx.tcx.erase_late_bound_regions(arg)
}
comments_buf.join("\n")
}
+pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span {
+ sm.span_take_while(span, |&ch| ch == ' ' || ch == ';')
+}
+
macro_rules! op_utils {
($($name:ident $assign:ident)*) => {
/// Binary operation traits like `LangItem::Add`
#![allow(clippy::similar_names)] // `expr` and `expn`
-use crate::is_path_diagnostic_item;
use crate::source::snippet_opt;
use crate::visitors::{for_each_expr, Descend};
use itertools::{izip, Either, Itertools};
use rustc_ast::ast::LitKind;
use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, Node, QPath};
+use rustc_hir::{self as hir, Expr, ExprField, ExprKind, HirId, LangItem, Node, QPath, TyKind};
use rustc_lexer::unescape::unescape_literal;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use rustc_lint::LateContext;
} else {
match cx.tcx.item_name(macro_call.def_id) {
// `cfg!(debug_assertions)` in `debug_assert!`
- sym::cfg => ControlFlow::CONTINUE,
+ sym::cfg => ControlFlow::Continue(()),
// assert!(other_macro!(..))
_ => ControlFlow::Break(true),
}
// ArgumentV1::from_usize(<val>)
if let ExprKind::Call(callee, [val]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
- && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
- && path.segments.last().unwrap().ident.name == sym::ArgumentV1
+ && let TyKind::Path(QPath::LangItem(LangItem::FormatArgument, _, _)) = ty.kind
{
let val_idx = if val.span.ctxt() == expr.span.ctxt()
&& let ExprKind::Field(_, field) = val.kind
impl<'tcx> Visitor<'tcx> for ParamPosition {
fn visit_expr_field(&mut self, field: &'tcx ExprField<'tcx>) {
- fn parse_count(expr: &Expr<'_>) -> Option<usize> {
- // ::core::fmt::rt::v1::Count::Param(1usize),
- if let ExprKind::Call(ctor, [val]) = expr.kind
- && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
- && path.segments.last()?.ident.name == sym::Param
- && let ExprKind::Lit(lit) = &val.kind
- && let LitKind::Int(pos, _) = lit.node
- {
- Some(pos as usize)
- } else {
- None
- }
- }
-
match field.ident.name {
sym::position => {
if let ExprKind::Lit(lit) = &field.expr.kind
}
}
+fn parse_count(expr: &Expr<'_>) -> Option<usize> {
+ // <::core::fmt::rt::v1::Count>::Param(1usize),
+ if let ExprKind::Call(ctor, [val]) = expr.kind
+ && let ExprKind::Path(QPath::TypeRelative(_, path)) = ctor.kind
+ && path.ident.name == sym::Param
+ && let ExprKind::Lit(lit) = &val.kind
+ && let LitKind::Int(pos, _) = lit.node
+ {
+ Some(pos as usize)
+ } else {
+ None
+ }
+}
+
/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
if let ExprKind::AddrOf(.., array) = fmt_arg.kind
&& let ExprKind::Array(specs) = array.kind
{
Some(specs.iter().map(|spec| {
- let mut position = ParamPosition::default();
- position.visit_expr(spec);
- position
+ if let ExprKind::Call(f, args) = spec.kind
+ && let ExprKind::Path(QPath::TypeRelative(ty, f)) = f.kind
+ && let TyKind::Path(QPath::LangItem(LangItem::FormatPlaceholder, _, _)) = ty.kind
+ && f.ident.name == sym::new
+ && let [position, _fill, _align, _flags, precision, width] = args
+ && let ExprKind::Lit(position) = &position.kind
+ && let LitKind::Int(position, _) = position.node {
+ ParamPosition {
+ value: position as usize,
+ width: parse_count(width),
+ precision: parse_count(precision),
+ }
+ } else {
+ ParamPosition::default()
+ }
}))
} else {
None
pub fill: Option<char>,
/// Optionally specified alignment.
pub align: Alignment,
- /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
- pub flags: u32,
+ /// Whether all flag options are set to default (no flags specified).
+ pub no_flags: bool,
/// Represents either the maximum width or the integer precision.
pub precision: Count<'tcx>,
/// The minimum width, will be padded according to `width`/`align`
Some(Self {
fill: spec.fill,
align: spec.align,
- flags: spec.flags,
+ no_flags: spec.sign.is_none() && !spec.alternate && !spec.zero_pad && spec.debug_hex.is_none(),
precision: Count::new(
FormatParamUsage::Precision,
spec.precision,
self.width.is_implied()
&& self.precision.is_implied()
&& self.align == Alignment::AlignUnknown
- && self.flags == 0
+ && self.no_flags
}
}
// ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
&& let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
- && is_path_diagnostic_item(cx, ty, sym::Arguments)
+ && let TyKind::Path(QPath::LangItem(LangItem::FormatArguments, _, _)) = ty.kind
&& matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
{
let format_string = FormatString::new(cx, pieces)?;
type BreakTy = ();
fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::BREAK
+ ControlFlow::Break(())
}
}
// impl trait is gone in MIR, so check the return type manually
check_ty(
tcx,
- tcx.fn_sig(def_id).output().skip_binder(),
+ tcx.fn_sig(def_id).subst_identity().output().skip_binder(),
body.local_decls.iter().next().unwrap().source_info.span,
)?;
| StatementKind::Retag { .. }
| StatementKind::AscribeUserType(..)
| StatementKind::Coverage(..)
+ | StatementKind::ConstEvalCounter
| StatementKind::Nop => Ok(()),
}
}
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
| ast::ExprKind::Yeet(..)
+ | ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Struct(..)
| ast::ExprKind::Try(..)
| ast::ExprKind::TryBlock(..)
///
/// note: this only works on single line immutable closures with exactly one input parameter.
pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option<DerefClosure> {
- if let hir::ExprKind::Closure(&Closure { fn_decl, body, .. }) = closure.kind {
+ if let hir::ExprKind::Closure(&Closure { fn_decl, def_id, body, .. }) = closure.kind {
let closure_body = cx.tcx.hir().body(body);
// is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
// a type annotation is present if param `kind` is different from `TyKind::Infer`
applicability: Applicability::MachineApplicable,
};
- let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
let infcx = cx.tcx.infer_ctxt().build();
- ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
- .consume_body(closure_body);
+ ExprUseVisitor::new(&mut visitor, &infcx, def_id, cx.param_env, cx.typeck_results()).consume_body(closure_body);
if !visitor.suggestion_start.is_empty() {
return Some(DerefClosure {
.cx
.typeck_results()
.type_dependent_def_id(parent_expr.hir_id)
- .map(|did| self.cx.tcx.fn_sig(did).skip_binder())
+ .map(|did| self.cx.tcx.fn_sig(did).subst_identity().skip_binder())
{
std::iter::once(receiver)
.chain(call_args.iter())
/// If the expression is function like, get the signature for it.
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
- Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
+ Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst_identity(), Some(id)))
} else {
ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
}
.and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
},
- ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
- sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id))
- },
+ ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs), Some(id))),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => sig_from_bounds(
+ cx,
+ ty,
+ cx.tcx.item_bounds(def_id).subst(cx.tcx, substs),
+ cx.tcx.opt_parent(def_id),
+ ),
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
ty::Dynamic(bounds, _, _) => {
let lang_items = cx.tcx.lang_items();
.cx
.typeck_results()
.type_dependent_def_id(e.hir_id)
- .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe) =>
{
self.is_unsafe = true;
},
ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
- ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+ ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
_ => walk_expr(self, e),
},
[package]
name = "declare_clippy_lint"
-version = "0.1.68"
+version = "0.1.69"
edition = "2021"
publish = false
[toolchain]
-channel = "nightly-2023-01-12"
+channel = "nightly-2023-01-27"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
-D --deny OPT Set lint denied
-F --forbid OPT Set lint forbidden
-You can use tool lints to allow or deny lints from your code, eg.:
+You can use tool lints to allow or deny lints from your code, e.g.:
#[allow(clippy::needless_lifetimes)]
"#;
= help: convert all references to use `sym::Deref`
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
-error: hardcoded path to a language item
- --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
- |
-LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: convert all references to use `LangItem::DerefMut`
-
error: hardcoded path to a diagnostic item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
|
|
= help: convert all references to use `sym::deref_method`
+error: hardcoded path to a language item
+ --> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
+ |
+LL | const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: convert all references to use `LangItem::DerefMut`
+
error: aborting due to 3 previous errors
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::assertions_on_constants)]
+#![warn(clippy::bool_assert_comparison)]
+
+use std::ops::Not;
+
+macro_rules! a {
+ () => {
+ true
+ };
+}
+macro_rules! b {
+ () => {
+ true
+ };
+}
+
+// Implements the Not trait but with an output type
+// that's not bool. Should not suggest a rewrite
+#[derive(Debug, Clone, Copy)]
+enum ImplNotTraitWithoutBool {
+ VariantX(bool),
+ VariantY(u32),
+}
+
+impl PartialEq<bool> for ImplNotTraitWithoutBool {
+ fn eq(&self, other: &bool) -> bool {
+ match *self {
+ ImplNotTraitWithoutBool::VariantX(b) => b == *other,
+ _ => false,
+ }
+ }
+}
+
+impl Not for ImplNotTraitWithoutBool {
+ type Output = Self;
+
+ fn not(self) -> Self::Output {
+ match self {
+ ImplNotTraitWithoutBool::VariantX(b) => ImplNotTraitWithoutBool::VariantX(!b),
+ ImplNotTraitWithoutBool::VariantY(0) => ImplNotTraitWithoutBool::VariantY(1),
+ ImplNotTraitWithoutBool::VariantY(_) => ImplNotTraitWithoutBool::VariantY(0),
+ }
+ }
+}
+
+// This type implements the Not trait with an Output of
+// type bool. Using assert!(..) must be suggested
+#[derive(Debug, Clone, Copy)]
+struct ImplNotTraitWithBool;
+
+impl PartialEq<bool> for ImplNotTraitWithBool {
+ fn eq(&self, other: &bool) -> bool {
+ false
+ }
+}
+
+impl Not for ImplNotTraitWithBool {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ true
+ }
+}
+
+#[derive(Debug)]
+struct NonCopy;
+
+impl PartialEq<bool> for NonCopy {
+ fn eq(&self, other: &bool) -> bool {
+ false
+ }
+}
+
+impl Not for NonCopy {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ true
+ }
+}
+
+fn main() {
+ let a = ImplNotTraitWithoutBool::VariantX(true);
+ let b = ImplNotTraitWithBool;
+
+ assert_eq!("a".len(), 1);
+ assert!("a".is_empty());
+ assert!("".is_empty());
+ assert!("".is_empty());
+ assert_eq!(a!(), b!());
+ assert_eq!(a!(), "".is_empty());
+ assert_eq!("".is_empty(), b!());
+ assert_eq!(a, true);
+ assert!(b);
+
+ assert_ne!("a".len(), 1);
+ assert!("a".is_empty());
+ assert!("".is_empty());
+ assert!("".is_empty());
+ assert_ne!(a!(), b!());
+ assert_ne!(a!(), "".is_empty());
+ assert_ne!("".is_empty(), b!());
+ assert_ne!(a, true);
+ assert!(b);
+
+ debug_assert_eq!("a".len(), 1);
+ debug_assert!("a".is_empty());
+ debug_assert!("".is_empty());
+ debug_assert!("".is_empty());
+ debug_assert_eq!(a!(), b!());
+ debug_assert_eq!(a!(), "".is_empty());
+ debug_assert_eq!("".is_empty(), b!());
+ debug_assert_eq!(a, true);
+ debug_assert!(b);
+
+ debug_assert_ne!("a".len(), 1);
+ debug_assert!("a".is_empty());
+ debug_assert!("".is_empty());
+ debug_assert!("".is_empty());
+ debug_assert_ne!(a!(), b!());
+ debug_assert_ne!(a!(), "".is_empty());
+ debug_assert_ne!("".is_empty(), b!());
+ debug_assert_ne!(a, true);
+ debug_assert!(b);
+
+ // assert with error messages
+ assert_eq!("a".len(), 1, "tadam {}", 1);
+ assert_eq!("a".len(), 1, "tadam {}", true);
+ assert!("a".is_empty(), "tadam {}", 1);
+ assert!("a".is_empty(), "tadam {}", true);
+ assert!("a".is_empty(), "tadam {}", true);
+ assert_eq!(a, true, "tadam {}", false);
+
+ debug_assert_eq!("a".len(), 1, "tadam {}", 1);
+ debug_assert_eq!("a".len(), 1, "tadam {}", true);
+ debug_assert!("a".is_empty(), "tadam {}", 1);
+ debug_assert!("a".is_empty(), "tadam {}", true);
+ debug_assert!("a".is_empty(), "tadam {}", true);
+ debug_assert_eq!(a, true, "tadam {}", false);
+
+ assert!(a!());
+ assert!(b!());
+
+ use debug_assert_eq as renamed;
+ renamed!(a, true);
+ debug_assert!(b);
+
+ let non_copy = NonCopy;
+ assert_eq!(non_copy, true);
+ // changing the above to `assert!(non_copy)` would cause a `borrow of moved value`
+ println!("{non_copy:?}");
+
+ macro_rules! in_macro {
+ ($v:expr) => {{
+ assert_eq!($v, true);
+ }};
+ }
+ in_macro!(a);
+}
+// run-rustfix
+
+#![allow(unused, clippy::assertions_on_constants)]
#![warn(clippy::bool_assert_comparison)]
use std::ops::Not;
// Implements the Not trait but with an output type
// that's not bool. Should not suggest a rewrite
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
enum ImplNotTraitWithoutBool {
VariantX(bool),
VariantY(u32),
// This type implements the Not trait with an Output of
// type bool. Using assert!(..) must be suggested
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
struct ImplNotTraitWithBool;
impl PartialEq<bool> for ImplNotTraitWithBool {
}
}
+#[derive(Debug)]
+struct NonCopy;
+
+impl PartialEq<bool> for NonCopy {
+ fn eq(&self, other: &bool) -> bool {
+ false
+ }
+}
+
+impl Not for NonCopy {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ true
+ }
+}
+
fn main() {
let a = ImplNotTraitWithoutBool::VariantX(true);
let b = ImplNotTraitWithBool;
debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
debug_assert_eq!(a, true, "tadam {}", false);
+
+ assert_eq!(a!(), true);
+ assert_eq!(true, b!());
+
+ use debug_assert_eq as renamed;
+ renamed!(a, true);
+ renamed!(b, true);
+
+ let non_copy = NonCopy;
+ assert_eq!(non_copy, true);
+ // changing the above to `assert!(non_copy)` would cause a `borrow of moved value`
+ println!("{non_copy:?}");
+
+ macro_rules! in_macro {
+ ($v:expr) => {{
+ assert_eq!($v, true);
+ }};
+ }
+ in_macro!(a);
}
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:69:5
+ --> $DIR/bool_assert_comparison.rs:89:5
|
LL | assert_eq!("a".is_empty(), false);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::bool-assert-comparison` implied by `-D warnings`
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!("a".is_empty(), false);
+LL + assert!("a".is_empty());
+ |
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:70:5
+ --> $DIR/bool_assert_comparison.rs:90:5
|
LL | assert_eq!("".is_empty(), true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!("".is_empty(), true);
+LL + assert!("".is_empty());
+ |
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:71:5
+ --> $DIR/bool_assert_comparison.rs:91:5
|
LL | assert_eq!(true, "".is_empty());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!(true, "".is_empty());
+LL + assert!("".is_empty());
+ |
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:76:5
+ --> $DIR/bool_assert_comparison.rs:96:5
|
LL | assert_eq!(b, true);
- | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!(b, true);
+LL + assert!(b);
+ |
error: used `assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:79:5
+ --> $DIR/bool_assert_comparison.rs:99:5
|
LL | assert_ne!("a".is_empty(), false);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_ne!("a".is_empty(), false);
+LL + assert!("a".is_empty());
+ |
error: used `assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:80:5
+ --> $DIR/bool_assert_comparison.rs:100:5
|
LL | assert_ne!("".is_empty(), true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_ne!("".is_empty(), true);
+LL + assert!("".is_empty());
+ |
error: used `assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:81:5
+ --> $DIR/bool_assert_comparison.rs:101:5
|
LL | assert_ne!(true, "".is_empty());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_ne!(true, "".is_empty());
+LL + assert!("".is_empty());
+ |
error: used `assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:86:5
+ --> $DIR/bool_assert_comparison.rs:106:5
|
LL | assert_ne!(b, true);
- | ^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_ne!(b, true);
+LL + assert!(b);
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:89:5
+ --> $DIR/bool_assert_comparison.rs:109:5
|
LL | debug_assert_eq!("a".is_empty(), false);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!("a".is_empty(), false);
+LL + debug_assert!("a".is_empty());
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:90:5
+ --> $DIR/bool_assert_comparison.rs:110:5
|
LL | debug_assert_eq!("".is_empty(), true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!("".is_empty(), true);
+LL + debug_assert!("".is_empty());
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:91:5
+ --> $DIR/bool_assert_comparison.rs:111:5
|
LL | debug_assert_eq!(true, "".is_empty());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!(true, "".is_empty());
+LL + debug_assert!("".is_empty());
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:96:5
+ --> $DIR/bool_assert_comparison.rs:116:5
|
LL | debug_assert_eq!(b, true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!(b, true);
+LL + debug_assert!(b);
+ |
error: used `debug_assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:99:5
+ --> $DIR/bool_assert_comparison.rs:119:5
|
LL | debug_assert_ne!("a".is_empty(), false);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_ne!("a".is_empty(), false);
+LL + debug_assert!("a".is_empty());
+ |
error: used `debug_assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:100:5
+ --> $DIR/bool_assert_comparison.rs:120:5
|
LL | debug_assert_ne!("".is_empty(), true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_ne!("".is_empty(), true);
+LL + debug_assert!("".is_empty());
+ |
error: used `debug_assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:101:5
+ --> $DIR/bool_assert_comparison.rs:121:5
|
LL | debug_assert_ne!(true, "".is_empty());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_ne!(true, "".is_empty());
+LL + debug_assert!("".is_empty());
+ |
error: used `debug_assert_ne!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:106:5
+ --> $DIR/bool_assert_comparison.rs:126:5
|
LL | debug_assert_ne!(b, true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_ne!(b, true);
+LL + debug_assert!(b);
+ |
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:111:5
+ --> $DIR/bool_assert_comparison.rs:131:5
|
LL | assert_eq!("a".is_empty(), false, "tadam {}", 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!("a".is_empty(), false, "tadam {}", 1);
+LL + assert!("a".is_empty(), "tadam {}", 1);
+ |
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:112:5
+ --> $DIR/bool_assert_comparison.rs:132:5
|
LL | assert_eq!("a".is_empty(), false, "tadam {}", true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!("a".is_empty(), false, "tadam {}", true);
+LL + assert!("a".is_empty(), "tadam {}", true);
+ |
error: used `assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:113:5
+ --> $DIR/bool_assert_comparison.rs:133:5
|
LL | assert_eq!(false, "a".is_empty(), "tadam {}", true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!(false, "a".is_empty(), "tadam {}", true);
+LL + assert!("a".is_empty(), "tadam {}", true);
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:118:5
+ --> $DIR/bool_assert_comparison.rs:138:5
|
LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
+LL + debug_assert!("a".is_empty(), "tadam {}", 1);
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:119:5
+ --> $DIR/bool_assert_comparison.rs:139:5
|
LL | debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
+LL + debug_assert!("a".is_empty(), "tadam {}", true);
+ |
error: used `debug_assert_eq!` with a literal bool
- --> $DIR/bool_assert_comparison.rs:120:5
+ --> $DIR/bool_assert_comparison.rs:140:5
|
LL | debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `debug_assert!(..)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
+LL + debug_assert!("a".is_empty(), "tadam {}", true);
+ |
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:143:5
+ |
+LL | assert_eq!(a!(), true);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!(a!(), true);
+LL + assert!(a!());
+ |
+
+error: used `assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:144:5
+ |
+LL | assert_eq!(true, b!());
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `assert!(..)`
+ |
+LL - assert_eq!(true, b!());
+LL + assert!(b!());
+ |
+
+error: used `debug_assert_eq!` with a literal bool
+ --> $DIR/bool_assert_comparison.rs:148:5
+ |
+LL | renamed!(b, true);
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: replace it with `debug_assert!(..)`
+ |
+LL - renamed!(b, true);
+LL + debug_assert!(b);
+ |
-error: aborting due to 22 previous errors
+error: aborting due to 25 previous errors
1i32 as u8;
1f64 as isize;
1f64 as usize;
+ 1f32 as u32 as u16;
// Test clippy::cast_possible_wrap
1u8 as i8;
1u16 as i16;
LL | 1f32 as i32;
| ^^^^^^^^^^^
|
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i32::try_from(1f32);
+ | ~~~~~~~~~~~~~~~~~~~
error: casting `f32` to `u32` may truncate the value
--> $DIR/cast.rs:25:5
|
LL | 1f32 as u32;
| ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u32::try_from(1f32);
+ | ~~~~~~~~~~~~~~~~~~~
error: casting `f32` to `u32` may lose the sign of the value
--> $DIR/cast.rs:25:5
|
LL | 1f64 as f32;
| ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | f32::try_from(1f64);
+ | ~~~~~~~~~~~~~~~~~~~
error: casting `i32` to `i8` may truncate the value
--> $DIR/cast.rs:27:5
|
LL | 1i32 as i8;
| ^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i8::try_from(1i32);
+ | ~~~~~~~~~~~~~~~~~~
error: casting `i32` to `u8` may truncate the value
--> $DIR/cast.rs:28:5
|
LL | 1i32 as u8;
| ^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u8::try_from(1i32);
+ | ~~~~~~~~~~~~~~~~~~
error: casting `f64` to `isize` may truncate the value
--> $DIR/cast.rs:29:5
|
LL | 1f64 as isize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | isize::try_from(1f64);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `f64` to `usize` may truncate the value
--> $DIR/cast.rs:30:5
|
LL | 1f64 as usize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | usize::try_from(1f64);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `f64` to `usize` may lose the sign of the value
--> $DIR/cast.rs:30:5
LL | 1f64 as usize;
| ^^^^^^^^^^^^^
+error: casting `u32` to `u16` may truncate the value
+ --> $DIR/cast.rs:31:5
+ |
+LL | 1f32 as u32 as u16;
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u16::try_from(1f32 as u32);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: casting `f32` to `u32` may truncate the value
+ --> $DIR/cast.rs:31:5
+ |
+LL | 1f32 as u32 as u16;
+ | ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u32::try_from(1f32) as u16;
+ | ~~~~~~~~~~~~~~~~~~~
+
+error: casting `f32` to `u32` may lose the sign of the value
+ --> $DIR/cast.rs:31:5
+ |
+LL | 1f32 as u32 as u16;
+ | ^^^^^^^^^^^
+
error: casting `u8` to `i8` may wrap around the value
- --> $DIR/cast.rs:32:5
+ --> $DIR/cast.rs:33:5
|
LL | 1u8 as i8;
| ^^^^^^^^^
= note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
error: casting `u16` to `i16` may wrap around the value
- --> $DIR/cast.rs:33:5
+ --> $DIR/cast.rs:34:5
|
LL | 1u16 as i16;
| ^^^^^^^^^^^
error: casting `u32` to `i32` may wrap around the value
- --> $DIR/cast.rs:34:5
+ --> $DIR/cast.rs:35:5
|
LL | 1u32 as i32;
| ^^^^^^^^^^^
error: casting `u64` to `i64` may wrap around the value
- --> $DIR/cast.rs:35:5
+ --> $DIR/cast.rs:36:5
|
LL | 1u64 as i64;
| ^^^^^^^^^^^
error: casting `usize` to `isize` may wrap around the value
- --> $DIR/cast.rs:36:5
+ --> $DIR/cast.rs:37:5
|
LL | 1usize as isize;
| ^^^^^^^^^^^^^^^
error: casting `i32` to `u32` may lose the sign of the value
- --> $DIR/cast.rs:39:5
+ --> $DIR/cast.rs:40:5
|
LL | -1i32 as u32;
| ^^^^^^^^^^^^
error: casting `isize` to `usize` may lose the sign of the value
- --> $DIR/cast.rs:41:5
+ --> $DIR/cast.rs:42:5
|
LL | -1isize as usize;
| ^^^^^^^^^^^^^^^^
error: casting `i64` to `i8` may truncate the value
- --> $DIR/cast.rs:108:5
+ --> $DIR/cast.rs:109:5
|
LL | (-99999999999i64).min(1) as i8; // should be linted because signed
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i8::try_from((-99999999999i64).min(1)); // should be linted because signed
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `u8` may truncate the value
- --> $DIR/cast.rs:120:5
+ --> $DIR/cast.rs:121:5
|
LL | 999999u64.clamp(0, 256) as u8; // should still be linted
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u8::try_from(999999u64.clamp(0, 256)); // should still be linted
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E2` to `u8` may truncate the value
- --> $DIR/cast.rs:141:21
+ --> $DIR/cast.rs:142:21
|
LL | let _ = self as u8;
| ^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let _ = u8::try_from(self);
+ | ~~~~~~~~~~~~~~~~~~
error: casting `main::E2::B` to `u8` will truncate the value
- --> $DIR/cast.rs:142:21
+ --> $DIR/cast.rs:143:21
|
LL | let _ = Self::B as u8;
| ^^^^^^^^^^^^^
= note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
error: casting `main::E5` to `i8` may truncate the value
- --> $DIR/cast.rs:178:21
+ --> $DIR/cast.rs:179:21
|
LL | let _ = self as i8;
| ^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let _ = i8::try_from(self);
+ | ~~~~~~~~~~~~~~~~~~
error: casting `main::E5::A` to `i8` will truncate the value
- --> $DIR/cast.rs:179:21
+ --> $DIR/cast.rs:180:21
|
LL | let _ = Self::A as i8;
| ^^^^^^^^^^^^^
error: casting `main::E6` to `i16` may truncate the value
- --> $DIR/cast.rs:193:21
+ --> $DIR/cast.rs:194:21
|
LL | let _ = self as i16;
| ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let _ = i16::try_from(self);
+ | ~~~~~~~~~~~~~~~~~~~
error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
- --> $DIR/cast.rs:208:21
+ --> $DIR/cast.rs:209:21
|
LL | let _ = self as usize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let _ = usize::try_from(self);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `main::E10` to `u16` may truncate the value
- --> $DIR/cast.rs:249:21
+ --> $DIR/cast.rs:250:21
|
LL | let _ = self as u16;
| ^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let _ = u16::try_from(self);
+ | ~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value
- --> $DIR/cast.rs:257:13
+ --> $DIR/cast.rs:258:13
|
LL | let c = (q >> 16) as u8;
| ^^^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let c = u8::try_from((q >> 16));
+ | ~~~~~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `u8` may truncate the value
- --> $DIR/cast.rs:260:13
+ --> $DIR/cast.rs:261:13
|
LL | let c = (q / 1000) as u8;
| ^^^^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | let c = u8::try_from((q / 1000));
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
-error: aborting due to 33 previous errors
+error: aborting due to 36 previous errors
LL | 1isize as i8;
| ^^^^^^^^^^^^
|
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
= note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i8::try_from(1isize);
+ | ~~~~~~~~~~~~~~~~~~~~
error: casting `isize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`isize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
--> $DIR/cast_size.rs:15:5
|
LL | 1isize as i32;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i32::try_from(1isize);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `isize` to `u32` may truncate the value on targets with 64-bit wide pointers
--> $DIR/cast_size.rs:20:5
|
LL | 1isize as u32;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u32::try_from(1isize);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `usize` to `u32` may truncate the value on targets with 64-bit wide pointers
--> $DIR/cast_size.rs:21:5
|
LL | 1usize as u32;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | u32::try_from(1usize);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `usize` to `i32` may truncate the value on targets with 64-bit wide pointers
--> $DIR/cast_size.rs:22:5
|
LL | 1usize as i32;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | i32::try_from(1usize);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `usize` to `i32` may wrap around the value on targets with 32-bit wide pointers
--> $DIR/cast_size.rs:22:5
|
LL | 1i64 as isize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | isize::try_from(1i64);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `i64` to `usize` may truncate the value on targets with 32-bit wide pointers
--> $DIR/cast_size.rs:25:5
|
LL | 1i64 as usize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | usize::try_from(1i64);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `isize` may truncate the value on targets with 32-bit wide pointers
--> $DIR/cast_size.rs:26:5
|
LL | 1u64 as isize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | isize::try_from(1u64);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `u64` to `isize` may wrap around the value on targets with 64-bit wide pointers
--> $DIR/cast_size.rs:26:5
|
LL | 1u64 as usize;
| ^^^^^^^^^^^^^
+ |
+ = help: if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...
+help: ... or use `try_from` and handle the error accordingly
+ |
+LL | usize::try_from(1u64);
+ | ~~~~~~~~~~~~~~~~~~~~~
error: casting `u32` to `isize` may wrap around the value on targets with 32-bit wide pointers
--> $DIR/cast_size.rs:28:5
error: item name starts with its containing module's name
- --> $DIR/module_name_repetitions.rs:8:5
+ --> $DIR/module_name_repetitions.rs:8:12
|
LL | pub fn foo_bar() {}
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^
|
= note: `-D clippy::module-name-repetitions` implied by `-D warnings`
error: item name ends with its containing module's name
- --> $DIR/module_name_repetitions.rs:9:5
+ --> $DIR/module_name_repetitions.rs:9:12
|
LL | pub fn bar_foo() {}
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^
error: item name starts with its containing module's name
- --> $DIR/module_name_repetitions.rs:10:5
+ --> $DIR/module_name_repetitions.rs:10:16
|
LL | pub struct FooCake;
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^
error: item name ends with its containing module's name
- --> $DIR/module_name_repetitions.rs:11:5
+ --> $DIR/module_name_repetitions.rs:11:14
|
LL | pub enum CakeFoo {}
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^
error: item name starts with its containing module's name
- --> $DIR/module_name_repetitions.rs:12:5
+ --> $DIR/module_name_repetitions.rs:12:16
|
LL | pub struct Foo7Bar;
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^
error: aborting due to 5 previous errors
--- /dev/null
+#![allow(unused)]
+#![allow(deref_nullptr)]
+#![allow(clippy::unnecessary_operation)]
+#![allow(clippy::drop_copy)]
+#![warn(clippy::multiple_unsafe_ops_per_block)]
+
+use core::arch::asm;
+
+fn raw_ptr() -> *const () {
+ core::ptr::null()
+}
+
+unsafe fn not_very_safe() {}
+
+struct Sample;
+
+impl Sample {
+ unsafe fn not_very_safe(&self) {}
+}
+
+#[allow(non_upper_case_globals)]
+const sample: Sample = Sample;
+
+union U {
+ i: i32,
+ u: u32,
+}
+
+static mut STATIC: i32 = 0;
+
+fn test1() {
+ unsafe {
+ STATIC += 1;
+ not_very_safe();
+ }
+}
+
+fn test2() {
+ let u = U { i: 0 };
+
+ unsafe {
+ drop(u.u);
+ *raw_ptr();
+ }
+}
+
+fn test3() {
+ unsafe {
+ asm!("nop");
+ sample.not_very_safe();
+ STATIC = 0;
+ }
+}
+
+fn test_all() {
+ let u = U { i: 0 };
+ unsafe {
+ drop(u.u);
+ drop(STATIC);
+ sample.not_very_safe();
+ not_very_safe();
+ *raw_ptr();
+ asm!("nop");
+ }
+}
+
+// no lint
+fn correct1() {
+ unsafe {
+ STATIC += 1;
+ }
+}
+
+// no lint
+fn correct2() {
+ unsafe {
+ STATIC += 1;
+ }
+
+ unsafe {
+ *raw_ptr();
+ }
+}
+
+// no lint
+fn correct3() {
+ let u = U { u: 0 };
+
+ unsafe {
+ not_very_safe();
+ }
+
+ unsafe {
+ drop(u.i);
+ }
+}
+
+// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064)
+
+unsafe fn read_char_bad(ptr: *const u8) -> char {
+ unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+}
+
+// no lint
+unsafe fn read_char_good(ptr: *const u8) -> char {
+ let int_value = unsafe { *ptr.cast::<u32>() };
+ unsafe { core::char::from_u32_unchecked(int_value) }
+}
+
+fn main() {}
--- /dev/null
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:32:5
+ |
+LL | / unsafe {
+LL | | STATIC += 1;
+LL | | not_very_safe();
+LL | | }
+ | |_____^
+ |
+note: modification of a mutable static occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:33:9
+ |
+LL | STATIC += 1;
+ | ^^^^^^^^^^^
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:34:9
+ |
+LL | not_very_safe();
+ | ^^^^^^^^^^^^^^^
+ = note: `-D clippy::multiple-unsafe-ops-per-block` implied by `-D warnings`
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:41:5
+ |
+LL | / unsafe {
+LL | | drop(u.u);
+LL | | *raw_ptr();
+LL | | }
+ | |_____^
+ |
+note: union field access occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:42:14
+ |
+LL | drop(u.u);
+ | ^^^
+note: raw pointer dereference occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:43:9
+ |
+LL | *raw_ptr();
+ | ^^^^^^^^^^
+
+error: this `unsafe` block contains 3 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:48:5
+ |
+LL | / unsafe {
+LL | | asm!("nop");
+LL | | sample.not_very_safe();
+LL | | STATIC = 0;
+LL | | }
+ | |_____^
+ |
+note: inline assembly used here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:49:9
+ |
+LL | asm!("nop");
+ | ^^^^^^^^^^^
+note: unsafe method call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:50:9
+ |
+LL | sample.not_very_safe();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+note: modification of a mutable static occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:51:9
+ |
+LL | STATIC = 0;
+ | ^^^^^^^^^^
+
+error: this `unsafe` block contains 6 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:57:5
+ |
+LL | / unsafe {
+LL | | drop(u.u);
+LL | | drop(STATIC);
+LL | | sample.not_very_safe();
+... |
+LL | | asm!("nop");
+LL | | }
+ | |_____^
+ |
+note: union field access occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:58:14
+ |
+LL | drop(u.u);
+ | ^^^
+note: access of a mutable static occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:59:14
+ |
+LL | drop(STATIC);
+ | ^^^^^^
+note: unsafe method call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:60:9
+ |
+LL | sample.not_very_safe();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:61:9
+ |
+LL | not_very_safe();
+ | ^^^^^^^^^^^^^^^
+note: raw pointer dereference occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:62:9
+ |
+LL | *raw_ptr();
+ | ^^^^^^^^^^
+note: inline assembly used here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:63:9
+ |
+LL | asm!("nop");
+ | ^^^^^^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+ --> $DIR/multiple_unsafe_ops_per_block.rs:101:5
+ |
+LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: unsafe function call occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:101:14
+ |
+LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: raw pointer dereference occurs here
+ --> $DIR/multiple_unsafe_ops_per_block.rs:101:39
+ |
+LL | unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+ | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 5 previous errors
+
true
}
+#[rustfmt::skip]
+fn test_multiple_semicolon() -> bool {
+ true
+}
+
+#[rustfmt::skip]
+fn test_multiple_semicolon_with_spaces() -> bool {
+ true
+}
+
fn test_if_block() -> bool {
if true {
true
return true;
}
+#[rustfmt::skip]
+fn test_multiple_semicolon() -> bool {
+ return true;;;
+}
+
+#[rustfmt::skip]
+fn test_multiple_semicolon_with_spaces() -> bool {
+ return true;; ; ;
+}
+
fn test_if_block() -> bool {
if true {
return true;
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:36:9
+ --> $DIR/needless_return.rs:36:5
+ |
+LL | return true;;;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:41:5
+ |
+LL | return true;; ; ;
+ | ^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:46:9
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:38:9
+ --> $DIR/needless_return.rs:48:9
|
LL | return false;
| ^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:44:17
+ --> $DIR/needless_return.rs:54:17
|
LL | true => return false,
| ^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:46:13
+ --> $DIR/needless_return.rs:56:13
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:53:9
+ --> $DIR/needless_return.rs:63:9
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:55:16
+ --> $DIR/needless_return.rs:65:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:59:5
+ --> $DIR/needless_return.rs:69:5
|
LL | return the_answer!();
| ^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:62:21
+ --> $DIR/needless_return.rs:72:21
|
LL | fn test_void_fun() {
| _____________________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:67:11
+ --> $DIR/needless_return.rs:77:11
|
LL | if b {
| ___________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:69:13
+ --> $DIR/needless_return.rs:79:13
|
LL | } else {
| _____________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:77:14
+ --> $DIR/needless_return.rs:87:14
|
LL | _ => return,
| ^^^^^^
= help: replace `return` with a unit value
error: unneeded `return` statement
- --> $DIR/needless_return.rs:85:24
+ --> $DIR/needless_return.rs:95:24
|
LL | let _ = 42;
| ________________________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:88:14
+ --> $DIR/needless_return.rs:98:14
|
LL | _ => return,
| ^^^^^^
= help: replace `return` with a unit value
error: unneeded `return` statement
- --> $DIR/needless_return.rs:101:9
+ --> $DIR/needless_return.rs:111:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:103:9
+ --> $DIR/needless_return.rs:113:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:125:32
+ --> $DIR/needless_return.rs:135:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^
= help: replace `return` with an empty block
error: unneeded `return` statement
- --> $DIR/needless_return.rs:129:21
+ --> $DIR/needless_return.rs:139:21
|
LL | let _ = || {
| _____________________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:132:20
+ --> $DIR/needless_return.rs:142:20
|
LL | let _ = || return;
| ^^^^^^
= help: replace `return` with an empty block
error: unneeded `return` statement
- --> $DIR/needless_return.rs:138:32
+ --> $DIR/needless_return.rs:148:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:147:5
+ --> $DIR/needless_return.rs:157:5
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:151:5
+ --> $DIR/needless_return.rs:161:5
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:156:9
+ --> $DIR/needless_return.rs:166:9
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:158:9
+ --> $DIR/needless_return.rs:168:9
|
LL | return false;
| ^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:164:17
+ --> $DIR/needless_return.rs:174:17
|
LL | true => return false,
| ^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:166:13
+ --> $DIR/needless_return.rs:176:13
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:173:9
+ --> $DIR/needless_return.rs:183:9
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:175:16
+ --> $DIR/needless_return.rs:185:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:179:5
+ --> $DIR/needless_return.rs:189:5
|
LL | return the_answer!();
| ^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:182:33
+ --> $DIR/needless_return.rs:192:33
|
LL | async fn async_test_void_fun() {
| _________________________________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:187:11
+ --> $DIR/needless_return.rs:197:11
|
LL | if b {
| ___________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:189:13
+ --> $DIR/needless_return.rs:199:13
|
LL | } else {
| _____________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:197:14
+ --> $DIR/needless_return.rs:207:14
|
LL | _ => return,
| ^^^^^^
= help: replace `return` with a unit value
error: unneeded `return` statement
- --> $DIR/needless_return.rs:210:9
+ --> $DIR/needless_return.rs:220:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:212:9
+ --> $DIR/needless_return.rs:222:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:228:5
+ --> $DIR/needless_return.rs:238:5
|
LL | return format!("Hello {}", "world!");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:239:9
+ --> $DIR/needless_return.rs:249:9
|
LL | return true;
| ^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:241:9
+ --> $DIR/needless_return.rs:251:9
|
LL | return false;
| ^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:248:13
+ --> $DIR/needless_return.rs:258:13
|
LL | return 10;
| ^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:251:13
+ --> $DIR/needless_return.rs:261:13
|
LL | return 100;
| ^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:259:9
+ --> $DIR/needless_return.rs:269:9
|
LL | return 0;
| ^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:266:13
+ --> $DIR/needless_return.rs:276:13
|
LL | return *(x as *const isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:268:13
+ --> $DIR/needless_return.rs:278:13
|
LL | return !*(x as *const isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:275:20
+ --> $DIR/needless_return.rs:285:20
|
LL | let _ = 42;
| ____________________^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:282:20
+ --> $DIR/needless_return.rs:292:20
|
LL | let _ = 42; return;
| ^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:294:9
+ --> $DIR/needless_return.rs:304:9
|
LL | return Ok(format!("ok!"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:296:9
+ --> $DIR/needless_return.rs:306:9
|
LL | return Err(format!("err!"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: remove `return`
-error: aborting due to 48 previous errors
+error: aborting due to 50 previous errors
// e is a function pointer type and U is an integer; fptr-addr-cast
let _usize_from_fn_ptr_transmute = unsafe { foo as usize };
let _usize_from_fn_ptr = foo as *const usize;
+
+ let _usize_from_ref = unsafe { &1u32 as *const u32 as usize };
}
// If a ref-to-ptr cast of this form where the pointer type points to a type other
// e is a function pointer type and U is an integer; fptr-addr-cast
let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
let _usize_from_fn_ptr = foo as *const usize;
+
+ let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) };
}
// If a ref-to-ptr cast of this form where the pointer type points to a type other
LL | let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
+error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:55:36
+ |
+LL | let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize`
+
error: transmute from a reference to a pointer
- --> $DIR/transmutes_expressible_as_ptr_casts.rs:64:14
+ --> $DIR/transmutes_expressible_as_ptr_casts.rs:66:14
|
LL | unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
24
}
+mod issue_10084 {
+ unsafe fn bar() -> i32 {
+ 42
+ }
+
+ macro_rules! foo {
+ () => {
+ // SAFETY: This is necessary
+ unsafe { bar() }
+ };
+ }
+
+ fn main() {
+ foo!();
+ }
+}
+
fn main() {}
pub enum CompareMode {
Polonius,
Chalk,
+ NextSolver,
SplitDwarf,
SplitDwarfSingle,
}
match *self {
CompareMode::Polonius => "polonius",
CompareMode::Chalk => "chalk",
+ CompareMode::NextSolver => "next-solver",
CompareMode::SplitDwarf => "split-dwarf",
CompareMode::SplitDwarfSingle => "split-dwarf-single",
}
match s.as_str() {
"polonius" => CompareMode::Polonius,
"chalk" => CompareMode::Chalk,
+ "next-solver" => CompareMode::NextSolver,
"split-dwarf" => CompareMode::SplitDwarf,
"split-dwarf-single" => CompareMode::SplitDwarfSingle,
x => panic!("unknown --compare-mode option: {}", x),
pub stderr_per_bitwidth: bool,
// The MIR opt to unit test, if any
pub mir_unit_test: Option<String>,
+ // Whether to tell `rustc` to remap the "src base" directory to a fake
+ // directory.
+ pub remap_src_base: bool,
}
mod directives {
pub const INCREMENTAL: &'static str = "incremental";
pub const KNOWN_BUG: &'static str = "known-bug";
pub const MIR_UNIT_TEST: &'static str = "unit-test";
+ pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
// This isn't a real directive, just one that is probably mistyped often
pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
}
should_ice: false,
stderr_per_bitwidth: false,
mir_unit_test: None,
+ remap_src_base: false,
}
}
/// `//[foo]`), then the property is ignored unless `cfg` is
/// `Some("foo")`.
fn load_from(&mut self, testfile: &Path, cfg: Option<&str>, config: &Config) {
+ // Mode-dependent defaults.
+ self.remap_src_base = config.mode == Mode::Ui && !config.suite.contains("rustdoc");
+
let mut has_edition = false;
if !testfile.is_dir() {
let file = File::open(testfile).unwrap();
self.known_bug = true;
} else {
panic!(
- "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `unknown`."
+ "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
);
}
+ } else if config.parse_name_directive(ln, KNOWN_BUG) {
+ panic!(
+ "Invalid known-bug attribute, requires comma-separated issue references (`#000` or `chalk#000`) or `known-bug: unknown`."
+ );
}
+
config.set_name_value_directive(ln, MIR_UNIT_TEST, &mut self.mir_unit_test, |s| {
s.trim().to_string()
});
+ config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
});
}
match self.compare_mode {
Some(CompareMode::Polonius) => name == "compare-mode-polonius",
Some(CompareMode::Chalk) => name == "compare-mode-chalk",
+ Some(CompareMode::NextSolver) => name == "compare-mode-next-solver",
Some(CompareMode::SplitDwarf) => name == "compare-mode-split-dwarf",
Some(CompareMode::SplitDwarfSingle) => name == "compare-mode-split-dwarf-single",
None => false,
&& matches!(line.as_bytes().get(directive.len()), None | Some(&b' ') | Some(&b':'))
}
+ fn parse_negative_name_directive(&self, line: &str, directive: &str) -> bool {
+ line.starts_with("no-") && self.parse_name_directive(&line[3..], directive)
+ }
+
pub fn parse_name_value_directive(&self, line: &str, directive: &str) -> Option<String> {
let colon = directive.len();
if line.starts_with(directive) && line.as_bytes().get(colon) == Some(&b':') {
}
fn set_name_directive(&self, line: &str, directive: &str, value: &mut bool) {
- if !*value {
- *value = self.parse_name_directive(line, directive)
+ match value {
+ true => {
+ if self.parse_negative_name_directive(line, directive) {
+ *value = false;
+ }
+ }
+ false => {
+ if self.parse_name_directive(line, directive) {
+ *value = true;
+ }
+ }
}
}
cfg: Option<&str>,
) -> test::TestDesc {
let mut ignore = false;
- let ignore_message = None;
+ let mut ignore_message = None;
let mut should_fail = false;
let rustc_has_profiler_support = env::var_os("RUSTC_PROFILER_SUPPORT").is_some();
if revision.is_some() && revision != cfg {
return;
}
+ macro_rules! reason {
+ ($e:expr) => {
+ ignore |= match $e {
+ true => {
+ ignore_message = Some(stringify!($e));
+ true
+ }
+ false => ignore,
+ }
+ };
+ }
ignore = match config.parse_cfg_name_directive(ln, "ignore") {
- ParsedNameDirective::Match => true,
+ ParsedNameDirective::Match => {
+ ignore_message = Some("cfg -> ignore => Match");
+ true
+ }
ParsedNameDirective::NoMatch => ignore,
};
+
if config.has_cfg_prefix(ln, "only") {
ignore = match config.parse_cfg_name_directive(ln, "only") {
ParsedNameDirective::Match => ignore,
- ParsedNameDirective::NoMatch => true,
+ ParsedNameDirective::NoMatch => {
+ ignore_message = Some("cfg -> only => NoMatch");
+ true
+ }
};
}
- ignore |= ignore_llvm(config, ln);
- ignore |=
- config.run_clang_based_tests_with.is_none() && config.parse_needs_matching_clang(ln);
- ignore |= !has_asm_support && config.parse_name_directive(ln, "needs-asm-support");
- ignore |= !rustc_has_profiler_support && config.parse_needs_profiler_support(ln);
- ignore |= !config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled");
- ignore |= !rustc_has_sanitizer_support
- && config.parse_name_directive(ln, "needs-sanitizer-support");
- ignore |= !has_asan && config.parse_name_directive(ln, "needs-sanitizer-address");
- ignore |= !has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi");
- ignore |= !has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi");
- ignore |= !has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak");
- ignore |= !has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory");
- ignore |= !has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread");
- ignore |= !has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress");
- ignore |= !has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag");
- ignore |= !has_shadow_call_stack
- && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack");
- ignore |= !config.can_unwind() && config.parse_name_directive(ln, "needs-unwind");
- ignore |= config.target == "wasm32-unknown-unknown"
- && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS);
- ignore |= config.debugger == Some(Debugger::Cdb) && ignore_cdb(config, ln);
- ignore |= config.debugger == Some(Debugger::Gdb) && ignore_gdb(config, ln);
- ignore |= config.debugger == Some(Debugger::Lldb) && ignore_lldb(config, ln);
- ignore |= !has_rust_lld && config.parse_name_directive(ln, "needs-rust-lld");
+
+ reason!(ignore_llvm(config, ln));
+ reason!(
+ config.run_clang_based_tests_with.is_none() && config.parse_needs_matching_clang(ln)
+ );
+ reason!(!has_asm_support && config.parse_name_directive(ln, "needs-asm-support"));
+ reason!(!rustc_has_profiler_support && config.parse_needs_profiler_support(ln));
+ reason!(!config.run_enabled() && config.parse_name_directive(ln, "needs-run-enabled"));
+ reason!(
+ !rustc_has_sanitizer_support
+ && config.parse_name_directive(ln, "needs-sanitizer-support")
+ );
+ reason!(!has_asan && config.parse_name_directive(ln, "needs-sanitizer-address"));
+ reason!(!has_cfi && config.parse_name_directive(ln, "needs-sanitizer-cfi"));
+ reason!(!has_kcfi && config.parse_name_directive(ln, "needs-sanitizer-kcfi"));
+ reason!(!has_lsan && config.parse_name_directive(ln, "needs-sanitizer-leak"));
+ reason!(!has_msan && config.parse_name_directive(ln, "needs-sanitizer-memory"));
+ reason!(!has_tsan && config.parse_name_directive(ln, "needs-sanitizer-thread"));
+ reason!(!has_hwasan && config.parse_name_directive(ln, "needs-sanitizer-hwaddress"));
+ reason!(!has_memtag && config.parse_name_directive(ln, "needs-sanitizer-memtag"));
+ reason!(
+ !has_shadow_call_stack
+ && config.parse_name_directive(ln, "needs-sanitizer-shadow-call-stack")
+ );
+ reason!(!config.can_unwind() && config.parse_name_directive(ln, "needs-unwind"));
+ reason!(
+ config.target == "wasm32-unknown-unknown"
+ && config.parse_name_directive(ln, directives::CHECK_RUN_RESULTS)
+ );
+ reason!(config.debugger == Some(Debugger::Cdb) && ignore_cdb(config, ln));
+ reason!(config.debugger == Some(Debugger::Gdb) && ignore_gdb(config, ln));
+ reason!(config.debugger == Some(Debugger::Lldb) && ignore_lldb(config, ln));
+ reason!(!has_rust_lld && config.parse_name_directive(ln, "needs-rust-lld"));
should_fail |= config.parse_name_directive(ln, "should-fail");
});
) -> test::TestName {
// Print the name of the file, relative to the repository root.
// `src_base` looks like `/path/to/rust/tests/ui`
- let root_directory = config.src_base.parent().unwrap().parent().unwrap().parent().unwrap();
+ let root_directory = config.src_base.parent().unwrap().parent().unwrap();
let path = testpaths.file.strip_prefix(root_directory).unwrap();
let debugger = match config.debugger {
Some(d) => format!("-{}", d),
fn into_bytes(self) -> Vec<u8> {
match self {
ProcOutput::Full { bytes, .. } => bytes,
- ProcOutput::Abbreviated { mut head, skipped, tail } => {
+ ProcOutput::Abbreviated { mut head, mut skipped, tail } => {
+ let mut tail = &*tail;
+
+ // Skip over '{' at the start of the tail, so we don't later wrongfully consider this as json.
+ // See <https://rust-lang.zulipchat.com/#narrow/stream/182449-t-compiler.2Fhelp/topic/Weird.20CI.20failure/near/321797811>
+ while tail.get(0) == Some(&b'{') {
+ tail = &tail[1..];
+ skipped += 1;
+ }
+
write!(&mut head, "\n\n<<<<<< SKIPPED {} BYTES >>>>>>\n\n", skipped).unwrap();
- head.extend_from_slice(&tail);
+ head.extend_from_slice(tail);
head
}
}
#[cfg(test)]
mod tests;
+const FAKE_SRC_BASE: &str = "fake-test-src-base";
+
#[cfg(windows)]
fn disable_error_reporting<F: FnOnce() -> R, R>(f: F) -> R {
use std::sync::Mutex;
return;
}
+ // On Windows, translate all '\' path separators to '/'
+ let file_name = format!("{}", self.testpaths.file.display()).replace(r"\", "/");
+
// On Windows, keep all '\' path separators to match the paths reported in the JSON output
// from the compiler
- let os_file_name = self.testpaths.file.display().to_string();
-
- // on windows, translate all '\' path separators to '/'
- let file_name = format!("{}", self.testpaths.file.display()).replace(r"\", "/");
+ let diagnostic_file_name = if self.props.remap_src_base {
+ let mut p = PathBuf::from(FAKE_SRC_BASE);
+ p.push(&self.testpaths.relative_dir);
+ p.push(self.testpaths.file.file_name().unwrap());
+ p.display().to_string()
+ } else {
+ self.testpaths.file.display().to_string()
+ };
// If the testcase being checked contains at least one expected "help"
// message, then we'll ensure that all "help" messages are expected.
let expect_note = expected_errors.iter().any(|ee| ee.kind == Some(ErrorKind::Note));
// Parse the JSON output from the compiler and extract out the messages.
- let actual_errors = json::parse_output(&os_file_name, &proc_res.stderr, proc_res);
+ let actual_errors = json::parse_output(&diagnostic_file_name, &proc_res.stderr, proc_res);
let mut unexpected = Vec::new();
let mut found = vec![false; expected_errors.len()];
for actual_error in &actual_errors {
}
}
+ if self.props.remap_src_base {
+ rustc.arg(format!(
+ "--remap-path-prefix={}={}",
+ self.config.src_base.display(),
+ FAKE_SRC_BASE,
+ ));
+ }
+
match emit {
Emit::None => {}
Emit::Metadata if is_rustdoc => {}
Some(CompareMode::Chalk) => {
rustc.args(&["-Ztrait-solver=chalk"]);
}
+ Some(CompareMode::NextSolver) => {
+ rustc.args(&["-Ztrait-solver=next"]);
+ }
Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
}
let parent_dir = self.testpaths.file.parent().unwrap();
normalize_path(parent_dir, "$DIR");
+ if self.props.remap_src_base {
+ let mut remapped_parent_dir = PathBuf::from(FAKE_SRC_BASE);
+ if self.testpaths.relative_dir != Path::new("") {
+ remapped_parent_dir.push(&self.testpaths.relative_dir);
+ }
+ normalize_path(&remapped_parent_dir, "$DIR");
+ }
+
let source_bases = &[
// Source base on the current filesystem (calculated as parent of `tests/$suite`):
Some(self.config.src_base.parent().unwrap().parent().unwrap().into()),
"x86_64-linux-android",
"x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu",
+ "s390x-unknown-linux-gnu",
];
// FIXME(rcvalle): More targets are likely supported.
"aarch64-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-unknown-linux-gnu",
+ "s390x-unknown-linux-gnu",
];
-pub const MSAN_SUPPORTED_TARGETS: &[&str] =
- &["aarch64-unknown-linux-gnu", "x86_64-unknown-freebsd", "x86_64-unknown-linux-gnu"];
+pub const MSAN_SUPPORTED_TARGETS: &[&str] = &[
+ "aarch64-unknown-linux-gnu",
+ "x86_64-unknown-freebsd",
+ "x86_64-unknown-linux-gnu",
+ "s390x-unknown-linux-gnu",
+];
pub const TSAN_SUPPORTED_TARGETS: &[&str] = &[
"aarch64-apple-darwin",
"x86_64-apple-darwin",
"x86_64-unknown-freebsd",
"x86_64-unknown-linux-gnu",
+ "s390x-unknown-linux-gnu",
];
pub const HWASAN_SUPPORTED_TARGETS: &[&str] =
fn after_analysis<'tcx>(
&mut self,
- compiler: &rustc_interface::interface::Compiler,
+ _: &rustc_interface::interface::Compiler,
queries: &'tcx rustc_interface::Queries<'tcx>,
) -> Compilation {
- compiler.session().abort_if_errors();
-
queries.global_ctxt().unwrap().enter(|tcx| {
+ tcx.sess.abort_if_errors();
+
init_late_loggers(tcx);
if !tcx.sess.crate_types().contains(&CrateType::Executable) {
tcx.sess.fatal("miri only makes sense on bin crates");
let mut config = self.miri_config.clone();
// Add filename to `miri` arguments.
- config.args.insert(0, compiler.input().filestem().to_string());
+ config.args.insert(0, tcx.sess.io.input.filestem().to_string());
// Adjust working directory for interpretation.
if let Some(cwd) = env::var_os("MIRI_CWD") {
i32::try_from(return_code).expect("Return value was too large!"),
);
}
+ tcx.sess.abort_if_errors();
});
- compiler.session().abort_if_errors();
-
Compilation::Stop
}
}
match entry_type {
EntryFnType::Main { .. } => {
let start_id = tcx.lang_items().start_fn().unwrap();
- let main_ret_ty = tcx.fn_sig(entry_id).output();
+ let main_ret_ty = tcx.fn_sig(entry_id).no_bound_vars().unwrap().output();
let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
let start_instance = ty::Instance::resolve(
tcx,
}
// test `stat`
- assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
+ let err = fs::metadata("foo.txt").unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::PermissionDenied);
// check that it is the right kind of `PermissionDenied`
- assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
+ assert_eq!(err.raw_os_error(), Some(libc::EACCES));
}
assert!(v[0].0 == 49);
}
+fn miri_issue_2759() {
+ let mut input = "1".to_string();
+ input.replace_range(0..0, "0");
+}
+
fn main() {
assert_eq!(vec_reallocate().len(), 5);
swap();
swap_remove();
reverse();
+ miri_issue_2759();
}
print("Refusing to decode " + str(type(content)) + " to str")
return json.loads(content_str)
-def validate_maintainers(repo, github_token):
- # type: (str, str) -> None
- '''Ensure all maintainers are assignable on a GitHub repo'''
- next_link_re = re.compile(r'<([^>]+)>; rel="next"')
-
- # Load the list of assignable people in the GitHub repo
- assignable = [] # type: typing.List[str]
- url = 'https://api.github.com/repos/' \
- + '%s/collaborators?per_page=100' % repo # type: typing.Optional[str]
- while url is not None:
- response = urllib2.urlopen(urllib2.Request(url, headers={
- 'Authorization': 'token ' + github_token,
- # Properly load nested teams.
- 'Accept': 'application/vnd.github.hellcat-preview+json',
- }))
- assignable.extend(user['login'] for user in load_json_from_response(response))
- # Load the next page if available
- url = None
- link_header = response.headers.get('Link')
- if link_header:
- matches = next_link_re.match(link_header)
- if matches is not None:
- url = matches.group(1)
-
- errors = False
- for tool, maintainers in MAINTAINERS.items():
- for maintainer in maintainers:
- if maintainer not in assignable:
- errors = True
- print(
- "error: %s maintainer @%s is not assignable in the %s repo"
- % (tool, maintainer, repo),
- )
-
- if errors:
- print()
- print(" To be assignable, a person needs to be explicitly listed as a")
- print(" collaborator in the repository settings. The simple way to")
- print(" fix this is to ask someone with 'admin' privileges on the repo")
- print(" to add the person or whole team as a collaborator with 'read'")
- print(" privileges. Those privileges don't grant any extra permissions")
- print(" so it's safe to apply them.")
- print()
- print("The build will fail due to this.")
- exit(1)
-
def read_current_status(current_commit, path):
# type: (str, str) -> typing.Mapping[str, typing.Any]
try:
if __name__ != '__main__':
exit(0)
- repo = os.environ.get('TOOLSTATE_VALIDATE_MAINTAINERS_REPO')
- if repo:
- github_token = os.environ.get('TOOLSTATE_REPO_ACCESS_TOKEN')
- if github_token:
- # FIXME: This is currently broken. Starting on 2021-09-15, GitHub
- # seems to have changed it so that to list the collaborators
- # requires admin permissions. I think this will probably just need
- # to be removed since we are probably not going to use an admin
- # token, and I don't see another way to do this.
- print('maintainer validation disabled')
- # validate_maintainers(repo, github_token)
- else:
- print('skipping toolstate maintainers validation since no GitHub token is present')
- # When validating maintainers don't run the full script.
- exit(0)
cur_commit = sys.argv[1]
cur_datetime = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
--- /dev/null
+name: Diff Check
+on:
+ workflow_dispatch:
+ inputs:
+ clone_url:
+ description: 'Git url of a rustfmt fork to compare against the latest master rustfmt'
+ required: true
+ branch_name:
+ description: 'Name of the feature branch on the forked repo'
+ required: true
+ commit_hash:
+ description: 'Optional commit hash from the feature branch'
+ required: false
+ rustfmt_configs:
+ description: 'Optional comma separated list of rustfmt config options to pass when running the feature branch'
+ required: false
+
+jobs:
+ diff_check:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: checkout
+ uses: actions/checkout@v3
+
+ - name: install rustup
+ run: |
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh
+ sh rustup-init.sh -y --default-toolchain none
+ rustup target add x86_64-unknown-linux-gnu
+
+ - name: check diff
+ run: bash ${GITHUB_WORKSPACE}/ci/check_diff.sh ${{ github.event.inputs.clone_url }} ${{ github.event.inputs.branch_name }} ${{ github.event.inputs.commit_hash }} ${{ github.event.inputs.rustfmt_configs }}
tempdir,
futures-rs,
rust-clippy,
- failure,
]
include:
# Allowed Failures
# Original comment was: temporal build failure due to breaking changes in the nightly compiler
- integration: rust-semverver
allow-failure: true
- # Can be moved back to include section after https://github.com/rust-lang-nursery/failure/pull/298 is merged
- - integration: failure
- allow-failure: true
steps:
- name: checkout
## [Unreleased]
+## [1.5.2] 2023-01-24
+
+### Fixed
+
+- Resolve issue when comments are found within const generic defaults in unit structs [#5668](https://github.com/rust-lang/rustfmt/issues/5668)
+- Resolve issue when block comments are found within trait generics [#5358](https://github.com/rust-lang/rustfmt/issues/5358)
+- Correctly handle alignment of comments containing unicode characters [#5504](https://github.com/rust-lang/rustfmt/issues/5504)
+- Properly indent a single generic bound that requires being written across multiple lines [#4689](https://github.com/rust-lang/rustfmt/issues/4689) (n.b. this change is version gated and will only appear when the `version` configuration option is set to `Two`)
+
+### Changed
+
+- Renamed `fn_args_layout` configuration option to `fn_params_layout` [#4149](https://github.com/rust-lang/rustfmt/issues/4149). Note that `fn_args_layout` has only been soft deprecated: `fn_args_layout` will continue to work without issue, but rustfmt will display a warning to encourage users to switch to the new name
+
+### Added
+
+- New configuration option (`skip_macro_invocations`)[https://rust-lang.github.io/rustfmt/?version=master&search=#skip_macro_invocations] [#5347](https://github.com/rust-lang/rustfmt/pull/5347) that can be used to globally define a single enumerated list of macro calls that rustfmt should skip formatting. rustfmt [currently also supports this via a custom tool attribute](https://github.com/rust-lang/rustfmt#tips), however, these cannot be used in all contexts because [custom inner attributes are unstable](https://github.com/rust-lang/rust/issues/54726)
+
+### Misc
+
+- rustfmt now internally supports the ability to have both stable and unstable variants of a configuration option [#5378](https://github.com/rust-lang/rustfmt/issues/5378). This ability will allow the rustfmt team to make certain configuration options available on stable toolchains more quickly because we no longer have to wait for _every_ variant to be stable-ready before stabilizing _any_ variant.
+
+### Install/Download Options
+- **rustup (nightly)** - nightly-2023-01-24
+- **GitHub Release Binaries** - [Release v1.5.2](https://github.com/rust-lang/rustfmt/releases/tag/v1.5.2)
+- **Build from source** - [Tag v1.5.2](https://github.com/rust-lang/rustfmt/tree/v1.5.2), see instructions for how to [install rustfmt from source][install-from-source]
+
## [1.5.1] 2022-06-24
**N.B** A bug was introduced in v1.5.0/nightly-2022-06-15 which modified formatting. If you happened to run rustfmt over your code with one of those ~10 nightlies it's possible you may have seen formatting changes, and you may see additional changes after this fix since that bug has now been reverted.
- Fix formatting of raw string literals #2983
- Handle chain with try operators with spaces #2986
- Use correct shape in Visual tuple rewriting #2987
-- Impove formatting of arguments with `visual_style = "Visual"` option #2988
+- Improve formatting of arguments with `visual_style = "Visual"` option #2988
- Change `print_diff` to output the correct line number 992b179
- Propagate errors about failing to rewrite a macro 6f318e3
- Handle formatting of long function signature #3010
[[package]]
name = "rustfmt-config_proc_macro"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"proc-macro2",
"quote",
[[package]]
name = "rustfmt-nightly"
-version = "1.5.1"
+version = "1.5.2"
dependencies = [
"annotate-snippets",
"anyhow",
[package]
name = "rustfmt-nightly"
-version = "1.5.1"
+version = "1.5.2"
description = "Tool to find and fix Rust formatting issues"
repository = "https://github.com/rust-lang/rustfmt"
readme = "README.md"
unicode-width = "0.1"
unicode_categories = "0.1"
-rustfmt-config_proc_macro = { version = "0.2", path = "config_proc_macro" }
+rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" }
# A noop dependency that changes in the Rust repository, it's a bit of a hack.
# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
# Configuring Rustfmt
-Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/1.0.4/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well.
+Rustfmt is designed to be very configurable. You can create a TOML file called `rustfmt.toml` or `.rustfmt.toml`, place it in the project or any other parent directory and it will apply the options in that file. If none of these directories contain such a file, both your home directory and a directory called `rustfmt` in your [global config directory](https://docs.rs/dirs/4.0.0/dirs/fn.config_dir.html) (e.g. `.config/rustfmt/`) are checked as well.
A possible content of `rustfmt.toml` or `.rustfmt.toml` might look like this:
## `comment_width`
-Maximum length of comments. No effect unless`wrap_comments = true`.
+Maximum length of comments. No effect unless `wrap_comments = true`.
- **Default value**: `80`
- **Possible values**: any positive integer
#### `0` (default):
```rust
-enum Bar {
+enum Foo {
A = 0,
Bb = 1,
RandomLongVariantGoesHere = 10,
## `fn_args_layout`
-Control the layout of arguments in a function
+This option is deprecated and has been renamed to `fn_params_layout` to better communicate that
+it affects the layout of parameters in function signatures.
- **Default value**: `"Tall"`
- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"`
}
```
+See also [`fn_params_layout`](#fn_params_layout)
+
## `fn_call_width`
Maximum width of the args of a function call before falling back to vertical formatting.
See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics)
+## `fn_params_layout`
+
+Control the layout of parameters in function signatures.
+
+- **Default value**: `"Tall"`
+- **Possible values**: `"Compressed"`, `"Tall"`, `"Vertical"`
+- **Stable**: Yes
+
+#### `"Tall"` (default):
+
+```rust
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: Consectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: Consectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ ) {
+ // body
+ }
+}
+```
+
+#### `"Compressed"`:
+
+```rust
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur,
+ adipiscing: Adipiscing, elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: Consectetur,
+ adipiscing: Adipiscing, elit: Elit,
+ ) {
+ // body
+ }
+}
+```
+
+#### `"Vertical"`:
+
+```rust
+trait Lorem {
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ ) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: Consectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: Consectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ ) {
+ // body
+ }
+}
+```
+
+
## `fn_single_line`
Put single-expression functions on a single line
See also [`format_macro_matchers`](#format_macro_matchers).
+## `skip_macro_invocations`
+
+Skip formatting the bodies of macro invocations with the following names.
+
+rustfmt will not format any macro invocation for macros with names set in this list.
+Including the special value "*" will prevent any macro invocations from being formatted.
+
+Note: This option does not have any impact on how rustfmt formats macro definitions.
+
+- **Default value**: `[]`
+- **Possible values**: a list of macro name idents, `["name_0", "name_1", ..., "*"]`
+- **Stable**: No (tracking issue: [#5346](https://github.com/rust-lang/rustfmt/issues/5346))
+
+#### `[]` (default):
+
+rustfmt will follow its standard approach to formatting macro invocations.
+
+No macro invocations will be skipped based on their name. More information about rustfmt's standard macro invocation formatting behavior can be found in [#5437](https://github.com/rust-lang/rustfmt/discussions/5437).
+
+```rust
+lorem!(
+ const _: u8 = 0;
+);
+
+ipsum!(
+ const _: u8 = 0;
+);
+```
+
+#### `["lorem"]`:
+
+The named macro invocations will be skipped.
+
+```rust
+lorem!(
+ const _: u8 = 0;
+);
+
+ipsum!(
+ const _: u8 = 0;
+);
+```
+
+#### `["*"]`:
+
+The special selector `*` will skip all macro invocations.
+
+```rust
+lorem!(
+ const _: u8 = 0;
+);
+
+ipsum!(
+ const _: u8 = 0;
+);
+```
## `format_strings`
## `imports_granularity`
-How imports should be grouped into `use` statements. Imports will be merged or split to the configured level of granularity.
+Controls how imports are structured in `use` statements. Imports will be merged or split to the configured level of granularity.
+
+Similar to other `import` related configuration options, this option operates within the bounds of user-defined groups of imports. See [`group_imports`](#group_imports) for more information on import groups.
+
+Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments.
- **Default value**: `Preserve`
- **Possible values**: `Preserve`, `Crate`, `Module`, `Item`, `One`
- **Stable**: No (tracking issue: [#4991](https://github.com/rust-lang/rustfmt/issues/4991))
-Note that rustfmt will not modify the granularity of imports containing comments if doing so could potentially lose or misplace said comments.
#### `Preserve` (default):
# Stabilising an Option
-In this Section, we describe how to stabilise an option of the rustfmt's configration.
+In this Section, we describe how to stabilise an option of the rustfmt's configuration.
## Conditions
set "RUSTFLAGS=-D warnings"
+set "RUSTFMT_CI=1"
:: Print version information
rustc -Vv || exit /b 1
set -euo pipefail
export RUSTFLAGS="-D warnings"
+export RUSTFMT_CI=1
# Print version information
rustc -Vv
--- /dev/null
+#!/bin/bash
+
+function print_usage() {
+ echo "usage check_diff REMOTE_REPO FEATURE_BRANCH [COMMIT_HASH] [OPTIONAL_RUSTFMT_CONFIGS]"
+}
+
+if [ $# -le 1 ]; then
+ print_usage
+ exit 1
+fi
+
+REMOTE_REPO=$1
+FEATURE_BRANCH=$2
+OPTIONAL_COMMIT_HASH=$3
+OPTIONAL_RUSTFMT_CONFIGS=$4
+
+# OUTPUT array used to collect all the status of running diffs on various repos
+STATUSES=()
+
+# Clone a git repository and cd into it.
+#
+# Parameters:
+# $1: git clone url
+# $2: directory where the repo should be cloned
+function clone_repo() {
+ GIT_TERMINAL_PROMPT=0 git clone --quiet $1 --depth 1 $2 && cd $2
+}
+
+# Initialize Git submoduels for the repo.
+#
+# Parameters
+# $1: list of directories to initialize
+function init_submodules() {
+ git submodule update --init $1
+}
+
+# Run rusfmt with the --check flag to see if a diff is produced.
+#
+# Parameters:
+# $1: Path to a rustfmt binary
+# $2: Output file path for the diff
+# $3: Any additional configuration options to pass to rustfmt
+#
+# Globlas:
+# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4
+function create_diff() {
+ local config;
+ if [ -z "$3" ]; then
+ config="--config=error_on_line_overflow=false,error_on_unformatted=false"
+ else
+ config="--config=error_on_line_overflow=false,error_on_unformatted=false,$OPTIONAL_RUSTFMT_CONFIGS"
+ fi
+
+ for i in `find . | grep "\.rs$"`
+ do
+ $1 --unstable-features --skip-children --check --color=always $config $i >> $2 2>/dev/null
+ done
+}
+
+# Run the master rustfmt binary and the feature branch binary in the current directory and compare the diffs
+#
+# Parameters
+# $1: Name of the repository (used for logging)
+#
+# Globlas:
+# $RUSFMT_BIN: Path to the rustfmt master binary. Created when running `compile_rustfmt`
+# $FEATURE_BIN: Path to the rustfmt feature binary. Created when running `compile_rustfmt`
+# $OPTIONAL_RUSTFMT_CONFIGS: Optional configs passed to the script from $4
+function check_diff() {
+ echo "running rustfmt (master) on $1"
+ create_diff $RUSFMT_BIN rustfmt_diff.txt
+
+ echo "running rustfmt (feature) on $1"
+ create_diff $FEATURE_BIN feature_diff.txt $OPTIONAL_RUSTFMT_CONFIGS
+
+ echo "checking diff"
+ local diff;
+ # we don't add color to the diff since we added color when running rustfmt --check.
+ # tail -n + 6 removes the git diff header info
+ # cut -c 2- removes the leading diff characters("+","-"," ") from running git diff.
+ # Again, the diff output we care about was already added when we ran rustfmt --check
+ diff=$(
+ git --no-pager diff --color=never \
+ --unified=0 --no-index rustfmt_diff.txt feature_diff.txt 2>&1 | tail -n +6 | cut -c 2-
+ )
+
+ if [ -z "$diff" ]; then
+ echo "no diff detected between rustfmt and the feture branch"
+ return 0
+ else
+ echo "$diff"
+ return 1
+ fi
+}
+
+# Compiles and produces two rustfmt binaries.
+# One for the current master, and another for the feature branch
+#
+# Parameters:
+# $1: Directory where rustfmt will be cloned
+#
+# Globlas:
+# $REMOTE_REPO: Clone URL to the rustfmt fork that we want to test
+# $FEATURE_BRANCH: Name of the feature branch
+# $OPTIONAL_COMMIT_HASH: Optional commit hash that will be checked out if provided
+function compile_rustfmt() {
+ RUSTFMT_REPO="https://github.com/rust-lang/rustfmt.git"
+ clone_repo $RUSTFMT_REPO $1
+ git remote add feature $REMOTE_REPO
+ git fetch feature $FEATURE_BRANCH
+
+ cargo build --release --bin rustfmt && cp target/release/rustfmt $1/rustfmt
+ if [ -z "$OPTIONAL_COMMIT_HASH" ]; then
+ git switch $FEATURE_BRANCH
+ else
+ git switch $OPTIONAL_COMMIT_HASH --detach
+ fi
+ cargo build --release --bin rustfmt && cp target/release/rustfmt $1/feature_rustfmt
+ RUSFMT_BIN=$1/rustfmt
+ FEATURE_BIN=$1/feature_rustfmt
+}
+
+# Check the diff for running rustfmt and the feature branch on all the .rs files in the repo.
+#
+# Parameters
+# $1: Clone URL for the repo
+# $2: Name of the repo (mostly used for logging)
+# $3: Path to any submodules that should be initialized
+function check_repo() {
+ WORKDIR=$(pwd)
+ REPO_URL=$1
+ REPO_NAME=$2
+ SUBMODULES=$3
+
+ local tmp_dir;
+ tmp_dir=$(mktemp -d -t $REPO_NAME-XXXXXXXX)
+ clone_repo $REPO_URL $tmp_dir
+
+ if [ ! -z "$SUBMODULES" ]; then
+ init_submodules $SUBMODULES
+ fi
+
+ check_diff $REPO_NAME
+ # append the status of running `check_diff` to the STATUSES array
+ STATUSES+=($?)
+
+ echo "removing tmp_dir $tmp_dir"
+ rm -rf $tmp_dir
+ cd $WORKDIR
+}
+
+function main() {
+ tmp_dir=$(mktemp -d -t rustfmt-XXXXXXXX)
+ echo Created tmp_dir $tmp_dir
+
+ compile_rustfmt $tmp_dir
+
+ # run checks
+ check_repo "https://github.com/rust-lang/rust.git" rust-lang-rust
+ check_repo "https://github.com/rust-lang/cargo.git" cargo
+ check_repo "https://github.com/rust-lang/miri.git" miri
+ check_repo "https://github.com/rust-lang/rust-analyzer.git" rust-analyzer
+ check_repo "https://github.com/bitflags/bitflags.git" bitflags
+ check_repo "https://github.com/rust-lang/log.git" log
+ check_repo "https://github.com/rust-lang/mdBook.git" mdBook
+ check_repo "https://github.com/rust-lang/packed_simd.git" packed_simd
+ check_repo "https://github.com/rust-lang/rust-semverver.git" check_repo
+ check_repo "https://github.com/Stebalien/tempfile.git" tempfile
+ check_repo "https://github.com/rust-lang/futures-rs.git" futures-rs
+ check_repo "https://github.com/dtolnay/anyhow.git" anyhow
+ check_repo "https://github.com/dtolnay/thiserror.git" thiserror
+ check_repo "https://github.com/dtolnay/syn.git" syn
+ check_repo "https://github.com/serde-rs/serde.git" serde
+ check_repo "https://github.com/rust-lang/rustlings.git" rustlings
+ check_repo "https://github.com/rust-lang/rustup.git" rustup
+ check_repo "https://github.com/SergioBenitez/Rocket.git" Rocket
+ check_repo "https://github.com/rustls/rustls.git" rustls
+ check_repo "https://github.com/rust-lang/rust-bindgen.git" rust-bindgen
+ check_repo "https://github.com/hyperium/hyper.git" hyper
+ check_repo "https://github.com/actix/actix.git" actix
+ check_repo "https://github.com/denoland/deno.git" denoland_deno
+
+ # cleanup temp dir
+ echo removing tmp_dir $tmp_dir
+ rm -rf $tmp_dir
+
+ # figure out the exit code
+ for status in ${STATUSES[@]}
+ do
+ if [ $status -eq 1 ]; then
+ echo "formatting diff found 💔"
+ return 1
+ fi
+ done
+
+ echo "no diff found 😊"
+}
+
+main
cd -
;;
crater)
- git clone --depth=1 https://github.com/rust-lang-nursery/${INTEGRATION}.git
+ git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git
cd ${INTEGRATION}
show_head
check_fmt_with_lib_tests
cd -
;;
+ bitflags)
+ git clone --depth=1 https://github.com/bitflags/${INTEGRATION}.git
+ cd ${INTEGRATION}
+ show_head
+ check_fmt_with_all_tests
+ cd -
+ ;;
+ error-chain | tempdir)
+ git clone --depth=1 https://github.com/rust-lang-deprecated/${INTEGRATION}.git
+ cd ${INTEGRATION}
+ show_head
+ check_fmt_with_all_tests
+ cd -
+ ;;
*)
- git clone --depth=1 https://github.com/rust-lang-nursery/${INTEGRATION}.git
+ git clone --depth=1 https://github.com/rust-lang/${INTEGRATION}.git
cd ${INTEGRATION}
show_head
check_fmt_with_all_tests
[[package]]
name = "rustfmt-config_proc_macro"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"proc-macro2",
"quote",
[package]
name = "rustfmt-config_proc_macro"
-version = "0.2.0"
+version = "0.3.0"
edition = "2018"
description = "A collection of procedural macros for rustfmt"
license = "Apache-2.0/MIT"
//! This module provides utilities for handling attributes on variants
-//! of `config_type` enum. Currently there are two types of attributes
-//! that could appear on the variants of `config_type` enum: `doc_hint`
-//! and `value`. Both comes in the form of name-value pair whose value
-//! is string literal.
+//! of `config_type` enum. Currently there are the following attributes
+//! that could appear on the variants of `config_type` enum:
+//!
+//! - `doc_hint`: name-value pair whose value is string literal
+//! - `value`: name-value pair whose value is string literal
+//! - `unstable_variant`: name only
/// Returns the value of the first `doc_hint` attribute in the given slice or
/// `None` if `doc_hint` attribute is not available.
attrs.iter().filter_map(config_value).next()
}
+/// Returns `true` if the there is at least one `unstable` attribute in the given slice.
+pub fn any_unstable_variant(attrs: &[syn::Attribute]) -> bool {
+ attrs.iter().any(is_unstable_variant)
+}
+
/// Returns a string literal value if the given attribute is `value`
/// attribute or `None` otherwise.
pub fn config_value(attr: &syn::Attribute) -> Option<String> {
is_attr_name_value(attr, "value")
}
+/// Returns `true` if the given attribute is an `unstable` attribute.
+pub fn is_unstable_variant(attr: &syn::Attribute) -> bool {
+ is_attr_path(attr, "unstable_variant")
+}
+
fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
attr.parse_meta().ok().map_or(false, |meta| match meta {
syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident(name) => true,
})
}
+fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool {
+ attr.parse_meta().ok().map_or(false, |meta| match meta {
+ syn::Meta::Path(path) if path.is_ident(name) => true,
+ _ => false,
+ })
+}
+
fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
attr.parse_meta().ok().and_then(|meta| match meta {
syn::Meta::NameValue(syn::MetaNameValue {
use proc_macro2::TokenStream;
-use quote::quote;
+use quote::{quote, quote_spanned};
+use syn::spanned::Spanned;
use crate::attrs::*;
use crate::utils::*;
let metas = variant
.attrs
.iter()
- .filter(|attr| !is_doc_hint(attr) && !is_config_value(attr));
+ .filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr));
let attrs = fold_quote(metas, |meta| quote!(#meta));
let syn::Variant { ident, fields, .. } = variant;
quote!(#attrs #ident #fields)
}
+/// Return the correct syntax to pattern match on the enum variant, discarding all
+/// internal field data.
+fn fields_in_variant(variant: &syn::Variant) -> TokenStream {
+ // With thanks to https://stackoverflow.com/a/65182902
+ match &variant.fields {
+ syn::Fields::Unnamed(_) => quote_spanned! { variant.span() => (..) },
+ syn::Fields::Unit => quote_spanned! { variant.span() => },
+ syn::Fields::Named(_) => quote_spanned! { variant.span() => {..} },
+ }
+}
+
fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream {
let doc_hint = variants
.iter()
.collect::<Vec<_>>()
.join("|");
let doc_hint = format!("[{}]", doc_hint);
+
+ let variant_stables = variants
+ .iter()
+ .map(|v| (&v.ident, fields_in_variant(&v), !unstable_of_variant(v)));
+ let match_patterns = fold_quote(variant_stables, |(v, fields, stable)| {
+ quote! {
+ #ident::#v #fields => #stable,
+ }
+ });
quote! {
use crate::config::ConfigType;
impl ConfigType for #ident {
fn doc_hint() -> String {
#doc_hint.to_owned()
}
+ fn stable_variant(&self) -> bool {
+ match self {
+ #match_patterns
+ }
+ }
}
}
}
}
fn doc_hint_of_variant(variant: &syn::Variant) -> String {
- find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string())
+ let mut text = find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string());
+ if unstable_of_variant(&variant) {
+ text.push_str(" (unstable)")
+ };
+ text
}
fn config_value_of_variant(variant: &syn::Variant) -> String {
find_config_value(&variant.attrs).unwrap_or(variant.ident.to_string())
}
+fn unstable_of_variant(variant: &syn::Variant) -> bool {
+ any_unstable_variant(&variant.attrs)
+}
+
fn impl_serde(ident: &syn::Ident, variants: &Variants) -> TokenStream {
let arms = fold_quote(variants.iter(), |v| {
let v_ident = &v.ident;
TokenStream::from_str("").unwrap()
}
}
+
+/// Used to conditionally output the TokenStream for tests that should be run as part of rustfmts
+/// test suite, but should be ignored when running in the rust-lang/rust test suite.
+#[proc_macro_attribute]
+pub fn rustfmt_only_ci_test(_args: TokenStream, input: TokenStream) -> TokenStream {
+ if option_env!("RUSTFMT_CI").is_some() {
+ input
+ } else {
+ let mut token_stream = TokenStream::from_str("#[ignore]").unwrap();
+ token_stream.extend(input);
+ token_stream
+ }
+}
pub mod config {
pub trait ConfigType: Sized {
fn doc_hint() -> String;
+ fn stable_variant(&self) -> bool;
}
}
[toolchain]
-channel = "nightly-2022-06-21"
-components = ["rustc-dev"]
+channel = "nightly-2023-01-24"
+components = ["llvm-tools", "rustc-dev"]
} else {
let should_skip = self
.ident()
- .map(|s| context.skip_context.skip_attribute(s.name.as_str()))
+ .map(|s| context.skip_context.attributes.skip(s.name.as_str()))
.unwrap_or(false);
let prefix = attr_prefix(self);
// Determine if the source text is annotated with `#[rustfmt::skip::attributes(derive)]`
// or `#![rustfmt::skip::attributes(derive)]`
- let skip_derives = context.skip_context.skip_attribute("derive");
+ let skip_derives = context.skip_context.attributes.skip("derive");
// This is not just a simple map because we need to handle doc comments
// (where we take as many doc comment attributes as possible) and possibly
"l",
"files-with-diff",
"Prints the names of mismatched files that were formatted. Prints the names of \
- files that would be formated when used with `--check` mode. ",
+ files that would be formatted when used with `--check` mode. ",
);
opts.optmulti(
"",
Ok(())
}
"human" => Ok(()),
- _ => {
- return Err(format!(
- "invalid --message-format value: {}. Allowed values are: short|json|human",
- message_format
- ));
- }
+ _ => Err(format!(
+ "invalid --message-format value: {}. Allowed values are: short|json|human",
+ message_format
+ )),
}
}
.expect("failed to write to stderr");
}
-#[derive(Debug, Clone, Copy, PartialEq)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Verbosity {
Verbose,
Normal,
.is_err()
);
assert!(
- !Opts::command()
+ Opts::command()
.try_get_matches_from(&["test", "--", "--emit"])
- .is_err()
+ .is_ok()
);
}
use crate::shape::Shape;
use crate::source_map::SpanUtils;
use crate::utils::{
- self, first_line_width, last_line_extendable, last_line_width, mk_sp, rewrite_ident,
- trimmed_last_line_width, wrap_str,
+ self, filtered_str_fits, first_line_width, last_line_extendable, last_line_width, mk_sp,
+ rewrite_ident, trimmed_last_line_width, wrap_str,
};
+/// Provides the original input contents from the span
+/// of a chain element with trailing spaces trimmed.
+fn format_overflow_style(span: Span, context: &RewriteContext<'_>) -> Option<String> {
+ context.snippet_provider.span_to_snippet(span).map(|s| {
+ s.lines()
+ .map(|l| l.trim_end())
+ .collect::<Vec<_>>()
+ .join("\n")
+ })
+}
+
+fn format_chain_item(
+ item: &ChainItem,
+ context: &RewriteContext<'_>,
+ rewrite_shape: Shape,
+ allow_overflow: bool,
+) -> Option<String> {
+ if allow_overflow {
+ item.rewrite(context, rewrite_shape)
+ .or_else(|| format_overflow_style(item.span, context))
+ } else {
+ item.rewrite(context, rewrite_shape)
+ }
+}
+
+fn get_block_child_shape(
+ prev_ends_with_block: bool,
+ context: &RewriteContext<'_>,
+ shape: Shape,
+) -> Shape {
+ if prev_ends_with_block {
+ shape.block_indent(0)
+ } else {
+ shape.block_indent(context.config.tab_spaces())
+ }
+ .with_max_width(context.config)
+}
+
+fn get_visual_style_child_shape(
+ context: &RewriteContext<'_>,
+ shape: Shape,
+ offset: usize,
+ parent_overflowing: bool,
+) -> Option<Shape> {
+ if !parent_overflowing {
+ shape
+ .with_max_width(context.config)
+ .offset_left(offset)
+ .map(|s| s.visual_indent(0))
+ } else {
+ Some(shape.visual_indent(offset))
+ }
+}
+
pub(crate) fn rewrite_chain(
expr: &ast::Expr,
context: &RewriteContext<'_>,
// The number of children in the chain. This is not equal to `self.children.len()`
// because `self.children` will change size as we process the chain.
child_count: usize,
+ // Whether elements are allowed to overflow past the max_width limit
+ allow_overflow: bool,
}
impl<'a> ChainFormatterShared<'a> {
rewrites: Vec::with_capacity(chain.children.len() + 1),
fits_single_line: false,
child_count: chain.children.len(),
+ // TODO(calebcartwright)
+ allow_overflow: false,
}
}
}
}
+ fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
+ for item in &self.children[..self.children.len() - 1] {
+ let rewrite = format_chain_item(item, context, child_shape, self.allow_overflow)?;
+ self.rewrites.push(rewrite);
+ }
+ Some(())
+ }
+
// Rewrite the last child. The last child of a chain requires special treatment. We need to
// know whether 'overflowing' the last child make a better formatting:
//
}
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
- Some(
- if self.root_ends_with_block {
- shape.block_indent(0)
- } else {
- shape.block_indent(context.config.tab_spaces())
- }
- .with_max_width(context.config),
- )
+ let block_end = self.root_ends_with_block;
+ Some(get_block_child_shape(block_end, context, shape))
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
- for item in &self.shared.children[..self.shared.children.len() - 1] {
- let rewrite = item.rewrite(context, child_shape)?;
- self.shared.rewrites.push(rewrite);
- }
- Some(())
+ self.shared.format_children(context, child_shape)
}
fn format_last_child(
.visual_indent(self.offset)
.sub_width(self.offset)?;
let rewrite = item.rewrite(context, child_shape)?;
- match wrap_str(rewrite, context.config.max_width(), shape) {
- Some(rewrite) => root_rewrite.push_str(&rewrite),
- None => {
- // We couldn't fit in at the visual indent, try the last
- // indent.
- let rewrite = item.rewrite(context, parent_shape)?;
- root_rewrite.push_str(&rewrite);
- self.offset = 0;
- }
+ if filtered_str_fits(&rewrite, context.config.max_width(), shape) {
+ root_rewrite.push_str(&rewrite);
+ } else {
+ // We couldn't fit in at the visual indent, try the last
+ // indent.
+ let rewrite = item.rewrite(context, parent_shape)?;
+ root_rewrite.push_str(&rewrite);
+ self.offset = 0;
}
self.shared.children = &self.shared.children[1..];
}
fn child_shape(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<Shape> {
- shape
- .with_max_width(context.config)
- .offset_left(self.offset)
- .map(|s| s.visual_indent(0))
+ get_visual_style_child_shape(
+ context,
+ shape,
+ self.offset,
+ // TODO(calebcartwright): self.shared.permissibly_overflowing_parent,
+ false,
+ )
}
fn format_children(&mut self, context: &RewriteContext<'_>, child_shape: Shape) -> Option<()> {
- for item in &self.shared.children[..self.shared.children.len() - 1] {
- let rewrite = item.rewrite(context, child_shape)?;
- self.shared.rewrites.push(rewrite);
- }
- Some(())
+ self.shared.format_children(context, child_shape)
}
fn format_last_child(
use crate::config::file_lines::FileLines;
+use crate::config::macro_names::MacroSelectors;
use crate::config::options::{IgnoreList, WidthHeuristics};
/// Trait for types that can be used in `Config`.
/// Returns hint text for use in `Config::print_docs()`. For enum types, this is a
/// pipe-separated list of variants; for other types it returns `<type>`.
fn doc_hint() -> String;
+
+ /// Return `true` if the variant (i.e. value of this type) is stable.
+ ///
+ /// By default, return true for all values. Enums annotated with `#[config_type]`
+ /// are automatically implemented, based on the `#[unstable_variant]` annotation.
+ fn stable_variant(&self) -> bool {
+ true
+ }
}
impl ConfigType for bool {
}
}
+impl ConfigType for MacroSelectors {
+ fn doc_hint() -> String {
+ String::from("[<string>, ...]")
+ }
+}
+
impl ConfigType for WidthHeuristics {
fn doc_hint() -> String {
String::new()
}
macro_rules! create_config {
+ // Options passed in to the macro.
+ //
+ // - $i: the ident name of the option
+ // - $ty: the type of the option value
+ // - $def: the default value of the option
+ // - $stb: true if the option is stable
+ // - $dstring: description of the option
($($i:ident: $ty:ty, $def:expr, $stb:expr, $( $dstring:expr ),+ );+ $(;)*) => (
#[cfg(test)]
use std::collections::HashSet;
#[derive(Clone)]
#[allow(unreachable_pub)]
pub struct Config {
- // For each config item, we store a bool indicating whether it has
- // been accessed and the value, and a bool whether the option was
- // manually initialised, or taken from the default,
+ // For each config item, we store:
+ //
+ // - 0: true if the value has been access
+ // - 1: true if the option was manually initialized
+ // - 2: the option value
+ // - 3: true if the option is unstable
$($i: (Cell<bool>, bool, $ty, bool)),+
}
| "array_width"
| "chain_width" => self.0.set_heuristics(),
"merge_imports" => self.0.set_merge_imports(),
+ "fn_args_layout" => self.0.set_fn_args_layout(),
&_ => (),
}
}
fn fill_from_parsed_config(mut self, parsed: PartialConfig, dir: &Path) -> Config {
$(
- if let Some(val) = parsed.$i {
- if self.$i.3 {
+ if let Some(option_value) = parsed.$i {
+ let option_stable = self.$i.3;
+ if $crate::config::config_type::is_stable_option_and_value(
+ stringify!($i), option_stable, &option_value
+ ) {
self.$i.1 = true;
- self.$i.2 = val;
- } else {
- if crate::is_nightly_channel!() {
- self.$i.1 = true;
- self.$i.2 = val;
- } else {
- eprintln!("Warning: can't set `{} = {:?}`, unstable features are only \
- available in nightly channel.", stringify!($i), val);
- }
+ self.$i.2 = option_value;
}
}
)+
self.set_heuristics();
self.set_ignore(dir);
self.set_merge_imports();
+ self.set_fn_args_layout();
self
}
match key {
$(
stringify!($i) => {
- self.$i.1 = true;
- self.$i.2 = val.parse::<$ty>()
+ let option_value = val.parse::<$ty>()
.expect(&format!("Failed to parse override for {} (\"{}\") as a {}",
stringify!($i),
val,
stringify!($ty)));
+
+ // Users are currently allowed to set unstable
+ // options/variants via the `--config` options override.
+ //
+ // There is ongoing discussion about how to move forward here:
+ // https://github.com/rust-lang/rustfmt/pull/5379
+ //
+ // For now, do not validate whether the option or value is stable,
+ // just always set it.
+ self.$i.1 = true;
+ self.$i.2 = option_value;
}
)+
_ => panic!("Unknown config key in override: {}", key)
| "array_width"
| "chain_width" => self.set_heuristics(),
"merge_imports" => self.set_merge_imports(),
+ "fn_args_layout" => self.set_fn_args_layout(),
&_ => (),
}
}
#[allow(unreachable_pub)]
pub fn is_hidden_option(name: &str) -> bool {
- const HIDE_OPTIONS: [&str; 5] =
- ["verbose", "verbose_diff", "file_lines", "width_heuristics", "merge_imports"];
+ const HIDE_OPTIONS: [&str; 6] = [
+ "verbose",
+ "verbose_diff",
+ "file_lines",
+ "width_heuristics",
+ "merge_imports",
+ "fn_args_layout"
+ ];
HIDE_OPTIONS.contains(&name)
}
}
}
+ fn set_fn_args_layout(&mut self) {
+ if self.was_set().fn_args_layout() {
+ eprintln!(
+ "Warning: the `fn_args_layout` option is deprecated. \
+ Use `fn_params_layout`. instead"
+ );
+ if !self.was_set().fn_params_layout() {
+ self.fn_params_layout.2 = self.fn_args_layout();
+ }
+ }
+ }
+
#[allow(unreachable_pub)]
/// Returns `true` if the config key was explicitly set and is the default value.
pub fn is_default(&self, key: &str) -> bool {
}
)
}
+
+pub(crate) fn is_stable_option_and_value<T>(
+ option_name: &str,
+ option_stable: bool,
+ option_value: &T,
+) -> bool
+where
+ T: PartialEq + std::fmt::Debug + ConfigType,
+{
+ let nightly = crate::is_nightly_channel!();
+ let variant_stable = option_value.stable_variant();
+ match (nightly, option_stable, variant_stable) {
+ // Stable with an unstable option
+ (false, false, _) => {
+ eprintln!(
+ "Warning: can't set `{} = {:?}`, unstable features are only \
+ available in nightly channel.",
+ option_name, option_value
+ );
+ false
+ }
+ // Stable with a stable option, but an unstable variant
+ (false, true, false) => {
+ eprintln!(
+ "Warning: can't set `{} = {:?}`, unstable variants are only \
+ available in nightly channel.",
+ option_name, option_value
+ );
+ false
+ }
+ // Nightly: everything allowed
+ // Stable with stable option and variant: allowed
+ (true, _, _) | (false, true, true) => true,
+ }
+}
--- /dev/null
+//! This module contains types and functions to support formatting specific macros.
+
+use itertools::Itertools;
+use std::{fmt, str};
+
+use serde::{Deserialize, Serialize};
+use serde_json as json;
+use thiserror::Error;
+
+/// Defines the name of a macro.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
+pub struct MacroName(String);
+
+impl MacroName {
+ pub fn new(other: String) -> Self {
+ Self(other)
+ }
+}
+
+impl fmt::Display for MacroName {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl From<MacroName> for String {
+ fn from(other: MacroName) -> Self {
+ other.0
+ }
+}
+
+/// Defines a selector to match against a macro.
+#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
+pub enum MacroSelector {
+ Name(MacroName),
+ All,
+}
+
+impl fmt::Display for MacroSelector {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Self::Name(name) => name.fmt(f),
+ Self::All => write!(f, "*"),
+ }
+ }
+}
+
+impl str::FromStr for MacroSelector {
+ type Err = std::convert::Infallible;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(match s {
+ "*" => MacroSelector::All,
+ name => MacroSelector::Name(MacroName(name.to_owned())),
+ })
+ }
+}
+
+/// A set of macro selectors.
+#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
+pub struct MacroSelectors(pub Vec<MacroSelector>);
+
+impl fmt::Display for MacroSelectors {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.0.iter().format(", "))
+ }
+}
+
+#[derive(Error, Debug)]
+pub enum MacroSelectorsError {
+ #[error("{0}")]
+ Json(json::Error),
+}
+
+// This impl is needed for `Config::override_value` to work for use in tests.
+impl str::FromStr for MacroSelectors {
+ type Err = MacroSelectorsError;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let raw: Vec<&str> = json::from_str(s).map_err(MacroSelectorsError::Json)?;
+ Ok(Self(
+ raw.into_iter()
+ .map(|raw| {
+ MacroSelector::from_str(raw).expect("MacroSelector from_str is infallible")
+ })
+ .collect(),
+ ))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn macro_names_from_str() {
+ let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap();
+ assert_eq!(
+ macro_names,
+ MacroSelectors(
+ [
+ MacroSelector::Name(MacroName("foo".to_owned())),
+ MacroSelector::All,
+ MacroSelector::Name(MacroName("bar".to_owned()))
+ ]
+ .into_iter()
+ .collect()
+ )
+ );
+ }
+
+ #[test]
+ fn macro_names_display() {
+ let macro_names = MacroSelectors::from_str(r#"["foo", "*", "bar"]"#).unwrap();
+ assert_eq!(format!("{}", macro_names), "foo, *, bar");
+ }
+}
#[allow(unreachable_pub)]
pub use crate::config::lists::*;
#[allow(unreachable_pub)]
+pub use crate::config::macro_names::{MacroSelector, MacroSelectors};
+#[allow(unreachable_pub)]
pub use crate::config::options::*;
#[macro_use]
pub(crate) mod config_type;
#[macro_use]
+#[allow(unreachable_pub)]
pub(crate) mod options;
pub(crate) mod file_lines;
+#[allow(unreachable_pub)]
pub(crate) mod lists;
+pub(crate) mod macro_names;
// This macro defines configuration options used in rustfmt. Each option
// is defined as follows:
format_macro_matchers: bool, false, false,
"Format the metavariable matching patterns in macros";
format_macro_bodies: bool, true, false, "Format the bodies of macros";
+ skip_macro_invocations: MacroSelectors, MacroSelectors::default(), false,
+ "Skip formatting the bodies of macros invoked with the following names.";
hex_literal_case: HexLiteralCase, HexLiteralCase::Preserve, false,
"Format hexadecimal integer literals";
force_multiline_blocks: bool, false, false,
"Force multiline closure bodies and match arms to be wrapped in a block";
fn_args_layout: Density, Density::Tall, true,
- "Control the layout of arguments in a function";
+ "(deprecated: use fn_params_layout instead)";
+ fn_params_layout: Density, Density::Tall, true,
+ "Control the layout of parameters in function signatures.";
brace_style: BraceStyle, BraceStyle::SameLineWhere, false, "Brace style for items";
control_brace_style: ControlBraceStyle, ControlBraceStyle::AlwaysSameLine, false,
"Brace style for control flow constructs";
make_backup: bool, false, false, "Backup changed files";
print_misformatted_file_names: bool, false, true,
"Prints the names of mismatched files that were formatted. Prints the names of \
- files that would be formated when used with `--check` mode. ";
+ files that would be formatted when used with `--check` mode. ";
}
#[derive(Error, Debug)]
cloned.width_heuristics = None;
cloned.print_misformatted_file_names = None;
cloned.merge_imports = None;
+ cloned.fn_args_layout = None;
::toml::to_string(&cloned).map_err(ToTomlError)
}
use super::*;
use std::str;
+ use crate::config::macro_names::MacroName;
use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
#[allow(dead_code)]
mod mock {
use super::super::*;
+ use rustfmt_config_proc_macro::config_type;
+
+ #[config_type]
+ pub(crate) enum PartiallyUnstableOption {
+ V1,
+ V2,
+ #[unstable_variant]
+ V3,
+ }
create_config! {
// Options that are used by the generated functions
"Merge imports";
merge_imports: bool, false, false, "(deprecated: use imports_granularity instead)";
+ // fn_args_layout renamed to fn_params_layout
+ fn_args_layout: Density, Density::Tall, true,
+ "(deprecated: use fn_params_layout instead)";
+ fn_params_layout: Density, Density::Tall, true,
+ "Control the layout of parameters in a function signatures.";
+
// Width Heuristics
use_small_heuristics: Heuristics, Heuristics::Default, true,
"Whether to use different formatting for items and \
// Options that are used by the tests
stable_option: bool, false, true, "A stable option";
unstable_option: bool, false, false, "An unstable option";
+ partially_unstable_option: PartiallyUnstableOption, PartiallyUnstableOption::V1, true,
+ "A partially unstable option";
+ }
+
+ #[cfg(test)]
+ mod partially_unstable_option {
+ use super::{Config, PartialConfig, PartiallyUnstableOption};
+ use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
+ use std::path::Path;
+
+ /// From the config file, we can fill with a stable variant
+ #[test]
+ fn test_from_toml_stable_value() {
+ let toml = r#"
+ partially_unstable_option = "V2"
+ "#;
+ let partial_config: PartialConfig = toml::from_str(toml).unwrap();
+ let config = Config::default();
+ let config = config.fill_from_parsed_config(partial_config, Path::new(""));
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V2
+ );
+ }
+
+ /// From the config file, we cannot fill with an unstable variant (stable only)
+ #[stable_only_test]
+ #[test]
+ fn test_from_toml_unstable_value_on_stable() {
+ let toml = r#"
+ partially_unstable_option = "V3"
+ "#;
+ let partial_config: PartialConfig = toml::from_str(toml).unwrap();
+ let config = Config::default();
+ let config = config.fill_from_parsed_config(partial_config, Path::new(""));
+ assert_eq!(
+ config.partially_unstable_option(),
+ // default value from config, i.e. fill failed
+ PartiallyUnstableOption::V1
+ );
+ }
+
+ /// From the config file, we can fill with an unstable variant (nightly only)
+ #[nightly_only_test]
+ #[test]
+ fn test_from_toml_unstable_value_on_nightly() {
+ let toml = r#"
+ partially_unstable_option = "V3"
+ "#;
+ let partial_config: PartialConfig = toml::from_str(toml).unwrap();
+ let config = Config::default();
+ let config = config.fill_from_parsed_config(partial_config, Path::new(""));
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V3
+ );
+ }
}
}
assert_eq!(config.was_set().verbose(), false);
}
+ const PRINT_DOCS_STABLE_OPTION: &str = "stable_option <boolean> Default: false";
+ const PRINT_DOCS_UNSTABLE_OPTION: &str = "unstable_option <boolean> Default: false (unstable)";
+ const PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION: &str =
+ "partially_unstable_option [V1|V2|V3 (unstable)] Default: V1";
+
#[test]
fn test_print_docs_exclude_unstable() {
use self::mock::Config;
Config::print_docs(&mut output, false);
let s = str::from_utf8(&output).unwrap();
-
- assert_eq!(s.contains("stable_option"), true);
- assert_eq!(s.contains("unstable_option"), false);
- assert_eq!(s.contains("(unstable)"), false);
+ assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
+ assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), false);
+ assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}
#[test]
Config::print_docs(&mut output, true);
let s = str::from_utf8(&output).unwrap();
- assert_eq!(s.contains("stable_option"), true);
- assert_eq!(s.contains("unstable_option"), true);
- assert_eq!(s.contains("(unstable)"), true);
+ assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
+ assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), true);
+ assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
}
#[test]
format_strings = false
format_macro_matchers = false
format_macro_bodies = true
+skip_macro_invocations = []
hex_literal_case = "Preserve"
empty_item_single_line = true
struct_lit_single_line = true
match_arm_blocks = true
match_arm_leading_pipes = "Never"
force_multiline_blocks = false
-fn_args_layout = "Tall"
+fn_params_layout = "Tall"
brace_style = "SameLineWhere"
control_brace_style = "AlwaysSameLine"
trailing_semicolon = true
assert_eq!(config.single_line_if_else_max_width(), 100);
}
}
+
+ #[cfg(test)]
+ mod partially_unstable_option {
+ use super::mock::{Config, PartiallyUnstableOption};
+ use super::*;
+
+ /// From the command line, we can override with a stable variant.
+ #[test]
+ fn test_override_stable_value() {
+ let mut config = Config::default();
+ config.override_value("partially_unstable_option", "V2");
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V2
+ );
+ }
+
+ /// From the command line, we can override with an unstable variant.
+ #[test]
+ fn test_override_unstable_value() {
+ let mut config = Config::default();
+ config.override_value("partially_unstable_option", "V3");
+ assert_eq!(
+ config.partially_unstable_option(),
+ PartiallyUnstableOption::V3
+ );
+ }
+ }
+
+ #[test]
+ fn test_override_skip_macro_invocations() {
+ let mut config = Config::default();
+ config.override_value("skip_macro_invocations", r#"["*", "println"]"#);
+ assert_eq!(
+ config.skip_macro_invocations(),
+ MacroSelectors(vec![
+ MacroSelector::All,
+ MacroSelector::Name(MacroName::new("println".to_owned()))
+ ])
+ );
+ }
}
use crate::string::{rewrite_string, StringFormat};
use crate::types::{rewrite_path, PathContext};
use crate::utils::{
- colon_spaces, contains_skip, count_newlines, first_line_ends_with, inner_attributes,
- last_line_extendable, last_line_width, mk_sp, outer_attributes, semicolon_for_expr,
- unicode_str_width, wrap_str,
+ colon_spaces, contains_skip, count_newlines, filtered_str_fits, first_line_ends_with,
+ inner_attributes, last_line_extendable, last_line_width, mk_sp, outer_attributes,
+ semicolon_for_expr, unicode_str_width, wrap_str,
};
use crate::vertical::rewrite_with_alignment;
use crate::visitor::FmtVisitor;
}
}
ast::ExprKind::Underscore => Some("_".to_owned()),
- ast::ExprKind::IncludedBytes(..) => unreachable!(),
+ ast::ExprKind::FormatArgs(..) | ast::ExprKind::IncludedBytes(..) => {
+ // These do not occur in the AST because macros aren't expanded.
+ unreachable!()
+ }
ast::ExprKind::Err => None,
};
match (orig_rhs, new_rhs) {
(Some(ref orig_rhs), Some(ref new_rhs))
- if wrap_str(new_rhs.clone(), context.config.max_width(), new_shape)
- .is_none() =>
+ if !filtered_str_fits(&new_rhs, context.config.max_width(), new_shape) =>
{
Some(format!("{}{}", before_space_str, orig_rhs))
}
use_trees: Vec<UseTree>,
import_granularity: ImportGranularity,
) -> Vec<UseTree> {
- // Return non-sorted single occurance of the use-trees text string;
- // order is by first occurance of the use-tree.
+ // Return non-sorted single occurrence of the use-trees text string;
+ // order is by first occurrence of the use-tree.
use_trees
.into_iter()
.flat_map(|tree| tree.flatten(import_granularity))
let item_snippet = context.snippet(item.span);
if let Some(lo) = item_snippet.find('/') {
// 1 = `{`
- let comment_hi = body_lo - BytePos(1);
+ let comment_hi = if generics.params.len() > 0 {
+ generics.span.lo() - BytePos(1)
+ } else {
+ body_lo - BytePos(1)
+ };
let comment_lo = item.span.lo() + BytePos(lo as u32);
if comment_lo < comment_hi {
match recover_missing_comment_in_span(
) -> Option<String> {
let header_str = format_header(context, p.prefix, p.ident, p.vis, offset);
let generics_str = if let Some(generics) = p.generics {
- let hi = context.snippet_provider.span_before(p.span, ";");
+ let hi = context.snippet_provider.span_before_last(p.span, ";");
format_generics(
context,
generics,
¶m_items,
context
.config
- .fn_args_layout()
+ .fn_params_layout()
.to_list_tactic(param_items.len()),
Separator::Comma,
one_line_budget,
} else {
inner_item.as_ref()
};
- let mut item_last_line_width = item_last_line.len() + item_sep_len;
+ let mut item_last_line_width = unicode_str_width(item_last_line) + item_sep_len;
if item_last_line.starts_with(&**indent_str) {
- item_last_line_width -= indent_str.len();
+ item_last_line_width -= unicode_str_width(indent_str);
}
if !item.is_substantial() {
} else if starts_with_newline(comment) {
false
} else {
- comment.trim().contains('\n') || comment.trim().len() > width
+ comment.trim().contains('\n') || unicode_str_width(comment.trim()) > width
};
rewrite_comment(
if !starts_with_newline(comment) {
if formatting.align_comments {
let mut comment_alignment =
- post_comment_alignment(item_max_width, inner_item.len());
+ post_comment_alignment(item_max_width, unicode_str_width(inner_item));
if first_line_width(&formatted_comment)
+ last_line_width(&result)
+ comment_alignment
item_max_width = None;
formatted_comment = rewrite_post_comment(&mut item_max_width)?;
comment_alignment =
- post_comment_alignment(item_max_width, inner_item.len());
+ post_comment_alignment(item_max_width, unicode_str_width(inner_item));
}
for _ in 0..=comment_alignment {
result.push(' ');
let mut first = true;
for item in items.clone().into_iter().skip(i) {
let item = item.as_ref();
- let inner_item_width = item.inner_as_ref().len();
+ let inner_item_width = unicode_str_width(item.inner_as_ref());
if !first
&& (item.is_different_group()
|| item.post_comment.is_none()
max_width
}
-fn post_comment_alignment(item_max_width: Option<usize>, inner_item_len: usize) -> usize {
- item_max_width.unwrap_or(0).saturating_sub(inner_item_len)
+fn post_comment_alignment(item_max_width: Option<usize>, inner_item_width: usize) -> usize {
+ item_max_width.unwrap_or(0).saturating_sub(inner_item_width)
}
pub(crate) struct ListItems<'a, I, F1, F2, F3>
use crate::source_map::SpanUtils;
use crate::spanned::Spanned;
use crate::utils::{
- format_visibility, indent_next_line, is_empty_line, mk_sp, remove_trailing_white_spaces,
- rewrite_ident, trim_left_preserve_layout, wrap_str, NodeIdExt,
+ filtered_str_fits, format_visibility, indent_next_line, is_empty_line, mk_sp,
+ remove_trailing_white_spaces, rewrite_ident, trim_left_preserve_layout, NodeIdExt,
};
use crate::visitor::FmtVisitor;
) -> Option<String> {
let should_skip = context
.skip_context
- .skip_macro(context.snippet(mac.path.span));
+ .macros
+ .skip(context.snippet(mac.path.span));
if should_skip {
None
} else {
}
}
};
- let new_body = wrap_str(
- new_body_snippet.snippet.to_string(),
- config.max_width(),
- shape,
- )?;
+
+ if !filtered_str_fits(&new_body_snippet.snippet, config.max_width(), shape) {
+ return None;
+ }
// Indent the body since it is in a block.
let indent_str = body_indent.to_string(&config);
- let mut new_body = LineClasses::new(new_body.trim_end())
+ let mut new_body = LineClasses::new(new_body_snippet.snippet.trim_end())
.enumerate()
.fold(
(String::new(), true),
use rustc_ast::ast;
use rustc_ast_pretty::pprust;
+use std::collections::HashSet;
-/// Take care of skip name stack. You can update it by attributes slice or
-/// by other context. Query this context to know if you need skip a block.
+/// Track which blocks of code are to be skipped when formatting.
+///
+/// You can update it by:
+///
+/// - attributes slice
+/// - manually feeding values into the underlying contexts
+///
+/// Query this context to know if you need to skip a block.
#[derive(Default, Clone)]
pub(crate) struct SkipContext {
- macros: Vec<String>,
- attributes: Vec<String>,
+ pub(crate) macros: SkipNameContext,
+ pub(crate) attributes: SkipNameContext,
}
impl SkipContext {
pub(crate) fn update_with_attrs(&mut self, attrs: &[ast::Attribute]) {
- self.macros.append(&mut get_skip_names("macros", attrs));
- self.attributes
- .append(&mut get_skip_names("attributes", attrs));
+ self.macros.extend(get_skip_names("macros", attrs));
+ self.attributes.extend(get_skip_names("attributes", attrs));
}
- pub(crate) fn update(&mut self, mut other: SkipContext) {
- self.macros.append(&mut other.macros);
- self.attributes.append(&mut other.attributes);
+ pub(crate) fn update(&mut self, other: SkipContext) {
+ let SkipContext { macros, attributes } = other;
+ self.macros.update(macros);
+ self.attributes.update(attributes);
+ }
+}
+
+/// Track which names to skip.
+///
+/// Query this context with a string to know whether to skip it.
+#[derive(Clone)]
+pub(crate) enum SkipNameContext {
+ All,
+ Values(HashSet<String>),
+}
+
+impl Default for SkipNameContext {
+ fn default() -> Self {
+ Self::Values(Default::default())
+ }
+}
+
+impl Extend<String> for SkipNameContext {
+ fn extend<T: IntoIterator<Item = String>>(&mut self, iter: T) {
+ match self {
+ Self::All => {}
+ Self::Values(values) => values.extend(iter),
+ }
+ }
+}
+
+impl SkipNameContext {
+ pub(crate) fn update(&mut self, other: Self) {
+ match (self, other) {
+ // If we're already skipping everything, nothing more can be added
+ (Self::All, _) => {}
+ // If we want to skip all, set it
+ (this, Self::All) => {
+ *this = Self::All;
+ }
+ // If we have some new values to skip, add them
+ (Self::Values(existing_values), Self::Values(new_values)) => {
+ existing_values.extend(new_values)
+ }
+ }
}
- pub(crate) fn skip_macro(&self, name: &str) -> bool {
- self.macros.iter().any(|n| n == name)
+ pub(crate) fn skip(&self, name: &str) -> bool {
+ match self {
+ Self::All => true,
+ Self::Values(values) => values.contains(name),
+ }
}
- pub(crate) fn skip_attribute(&self, name: &str) -> bool {
- self.attributes.iter().any(|n| n == name)
+ pub(crate) fn skip_all(&mut self) {
+ *self = Self::All;
}
}
lazy_static! {
static ref CONFIG_NAME_REGEX: regex::Regex =
regex::Regex::new(r"^## `([^`]+)`").expect("failed creating configuration pattern");
+ // Configuration values, which will be passed to `from_str`:
+ //
+ // - must be prefixed with `####`
+ // - must be wrapped in backticks
+ // - may by wrapped in double quotes (which will be stripped)
static ref CONFIG_VALUE_REGEX: regex::Regex =
- regex::Regex::new(r#"^#### `"?([^`"]+)"?`"#)
+ regex::Regex::new(r#"^#### `"?([^`]+?)"?`"#)
.expect("failed creating configuration value pattern");
}
assert!(
me.is_file() || me.with_extension("exe").is_file(),
"{}",
- if cfg!(release) {
- "no rustfmt bin, try running `cargo build --release` before testing"
- } else {
- "no rustfmt bin, try running `cargo build` before testing"
- }
+ "no rustfmt bin, try running `cargo build` or `cargo build --release` before testing"
);
me
}
ast::GenericBound::Trait(..) => last_line_extendable(s),
};
+ // Whether a GenericBound item is a PathSegment segment that includes internal array
+ // that contains more than one item
+ let is_item_with_multi_items_array = |item: &ast::GenericBound| match item {
+ ast::GenericBound::Trait(ref poly_trait_ref, ..) => {
+ let segments = &poly_trait_ref.trait_ref.path.segments;
+ if segments.len() > 1 {
+ true
+ } else {
+ if let Some(args_in) = &segments[0].args {
+ matches!(
+ args_in.deref(),
+ ast::GenericArgs::AngleBracketed(bracket_args)
+ if bracket_args.args.len() > 1
+ )
+ } else {
+ false
+ }
+ }
+ }
+ _ => false,
+ };
+
let result = items.iter().enumerate().try_fold(
(String::new(), None, false),
|(strs, prev_trailing_span, prev_extendable), (i, item)| {
},
)?;
- if !force_newline
- && items.len() > 1
- && (result.0.contains('\n') || result.0.len() > shape.width)
- {
+ // Whether to retry with a forced newline:
+ // Only if result is not already multiline and did not exceed line width,
+ // and either there is more than one item;
+ // or the single item is of type `Trait`,
+ // and any of the internal arrays contains more than one item;
+ let retry_with_force_newline = match context.config.version() {
+ Version::One => {
+ !force_newline
+ && items.len() > 1
+ && (result.0.contains('\n') || result.0.len() > shape.width)
+ }
+ Version::Two if force_newline => false,
+ Version::Two if (!result.0.contains('\n') && result.0.len() <= shape.width) => false,
+ Version::Two if items.len() > 1 => true,
+ Version::Two => is_item_with_multi_items_array(&items[0]),
+ };
+
+ if retry_with_force_newline {
join_bounds_inner(context, shape, items, need_indent, true)
} else {
Some(result.0)
// Wraps String in an Option. Returns Some when the string adheres to the
// Rewrite constraints defined for the Rewrite trait and None otherwise.
pub(crate) fn wrap_str(s: String, max_width: usize, shape: Shape) -> Option<String> {
- if is_valid_str(&filter_normal_code(&s), max_width, shape) {
+ if filtered_str_fits(&s, max_width, shape) {
Some(s)
} else {
None
}
}
-fn is_valid_str(snippet: &str, max_width: usize, shape: Shape) -> bool {
+pub(crate) fn filtered_str_fits(snippet: &str, max_width: usize, shape: Shape) -> bool {
+ let snippet = &filter_normal_code(snippet);
if !snippet.is_empty() {
// First line must fits with `shape.width`.
if first_line_width(snippet) > shape.width {
pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr: &str) -> bool {
match expr.kind {
ast::ExprKind::MacCall(..)
+ | ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Call(..)
| ast::ExprKind::MethodCall(..)
| ast::ExprKind::Array(..)
use crate::attr::*;
use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices};
use crate::config::Version;
-use crate::config::{BraceStyle, Config};
+use crate::config::{BraceStyle, Config, MacroSelector};
use crate::coverage::transform_missing_snippet;
use crate::items::{
format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate,
snippet_provider: &'a SnippetProvider,
report: FormatReport,
) -> FmtVisitor<'a> {
+ let mut skip_context = SkipContext::default();
+ let mut macro_names = Vec::new();
+ for macro_selector in config.skip_macro_invocations().0 {
+ match macro_selector {
+ MacroSelector::Name(name) => macro_names.push(name.to_string()),
+ MacroSelector::All => skip_context.macros.skip_all(),
+ }
+ }
+ skip_context.macros.extend(macro_names);
FmtVisitor {
parent_context: None,
parse_sess: parse_session,
is_macro_def: false,
macro_rewrite_failure: false,
report,
- skip_context: Default::default(),
+ skip_context,
}
}
use std::path::Path;
use std::process::Command;
+use rustfmt_config_proc_macro::rustfmt_only_ci_test;
+
/// Run the cargo-fmt executable and return its output.
fn cargo_fmt(args: &[&str]) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
};
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn version() {
assert_that!(&["--version"], starts_with("rustfmt "));
assert_that!(&["--", "--version"], starts_with("rustfmt "));
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn print_config() {
assert_that!(
);
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn rustfmt_help() {
assert_that!(&["--", "--help"], contains("Format Rust code"));
assert_that!(&["--", "--help=config"], contains("Configuration Options:"));
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn cargo_fmt_out_of_line_test_modules() {
// See also https://github.com/rust-lang/rustfmt/issues/5119
assert!(stdout.contains(&format!("Diff in {}", path.display())))
}
}
+
+#[rustfmt_only_ci_test]
+#[test]
+fn cargo_fmt_emits_error_on_line_overflow_true() {
+ // See also https://github.com/rust-lang/rustfmt/issues/3164
+ let args = [
+ "--check",
+ "--manifest-path",
+ "tests/cargo-fmt/source/issue_3164/Cargo.toml",
+ "--",
+ "--config",
+ "error_on_line_overflow=true",
+ ];
+
+ let (_stdout, stderr) = cargo_fmt(&args);
+ assert!(stderr.contains(
+ "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
+ ))
+}
--- /dev/null
+[package]
+name = "issue_3164"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
--- /dev/null
+#[allow(unused_macros)]
+macro_rules! foo {
+ ($id:ident) => {
+ macro_rules! bar {
+ ($id2:tt) => {
+ #[cfg(any(target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2))]
+ fn $id() {}
+ };
+ }
+ };
+}
+
+fn main() {}
tab_spaces = 2
newline_style = "Unix"
brace_style = "SameLineWhere"
-fn_args_layout = "Tall"
+fn_params_layout = "Tall"
trailing_comma = "Vertical"
indent_style = "Block"
reorder_imports = false
* mod g;
Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs',
-so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that
+so we should fall back to looking for './lib/c/e.rs', which correctly finds the module, that
rustfmt should format.
'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able
* mod c;
Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs',
-so we should fall back to looking for './a.rs', which correctly finds the modlue that
+so we should fall back to looking for './a.rs', which correctly finds the module that
rustfmt should format.
'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able
use std::path::Path;
use std::process::Command;
+use rustfmt_config_proc_macro::rustfmt_only_ci_test;
+
/// Run the rustfmt executable and return its output.
fn rustfmt(args: &[&str]) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
};
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn print_config() {
assert_that!(
remove_file("minimal-config").unwrap();
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn inline_config() {
// single invocation
// The path attribute points to a file that does not exist
assert!(stderr.contains("does_not_exist.rs does not exist"));
}
+
+#[test]
+fn rustfmt_emits_error_on_line_overflow_true() {
+ // See also https://github.com/rust-lang/rustfmt/issues/3164
+ let args = [
+ "--config",
+ "error_on_line_overflow=true",
+ "tests/cargo-fmt/source/issue_3164/src/main.rs",
+ ];
+
+ let (_stdout, stderr) = rustfmt(&args);
+ assert!(stderr.contains(
+ "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
+ ))
+}
tbm,
/// POPCNT (Population Count)
popcnt,
- /// FXSR (Floating-point context fast save and restor)
+ /// FXSR (Floating-point context fast save and restore)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,
--- /dev/null
+impl Default for WhitespaceCharacters {
+ fn default() -> Self {
+ Self {
+ space: '·', // U+00B7
+ nbsp: '⍽', // U+237D
+ tab: '→', // U+2192
+ newline: '⏎', // U+23CE
+ }
+ }
+}
+
+const RAINBOWS: &[&str] = &[
+ "rаinЬοѡ", // hue: 0
+ "raіnЬοw", // hue: 2
+ "rаіɴЬow", // hue: 2
+ "raіɴЬoѡ", // hue: 8
+ "ʀainЬow", // hue: 8
+ "ʀaіɴboѡ", // hue: 8
+ "ʀаіnbοw", // hue: 11
+ "rainЬoѡ", // hue: 14
+ "raіɴbow", // hue: 14
+ "rаiɴЬow", // hue: 20
+ "raіnЬow", // hue: 26
+ "ʀaiɴbοw", // hue: 32
+ "raіɴboѡ", // hue: 35
+ "rаiɴbow", // hue: 35
+ "rаіnbοw", // hue: 38
+ "rаinЬow", // hue: 47
+ "ʀaіnboѡ", // hue: 47
+ "ʀaіnЬoѡ", // hue: 47
+ "ʀаіɴbοw", // hue: 53
+ "ʀaіnЬοѡ", // hue: 57
+ "raiɴЬoѡ", // hue: 68
+ "ʀainbοѡ", // hue: 68
+ "ʀаinboѡ", // hue: 68
+ "ʀаiɴbοw", // hue: 68
+ "ʀаіnbow", // hue: 68
+ "rаіnЬοѡ", // hue: 69
+ "ʀainЬοw", // hue: 71
+ "raiɴbow", // hue: 73
+ "raіnЬoѡ", // hue: 74
+ "rаіɴbοw", // hue: 77
+ "raіnЬοѡ", // hue: 81
+ "raiɴЬow", // hue: 83
+ "ʀainbοw", // hue: 83
+ "ʀаinbow", // hue: 83
+ "ʀаiɴbοѡ", // hue: 83
+ "ʀаіnboѡ", // hue: 83
+ "ʀаіɴЬοѡ", // hue: 84
+ "rainЬow", // hue: 85
+ "ʀаiɴЬοw", // hue: 86
+ "ʀаіnbοѡ", // hue: 89
+ "ʀаіnЬοw", // hue: 92
+ "rаiɴbοw", // hue: 95
+ "ʀаіɴbοѡ", // hue: 98
+ "ʀаiɴЬοѡ", // hue: 99
+ "raіnbοw", // hue: 101
+ "ʀаіɴЬοw", // hue: 101
+ "ʀaiɴboѡ", // hue: 104
+ "ʀаinbοѡ", // hue: 104
+ "rаiɴbοѡ", // hue: 107
+ "ʀаinЬοw", // hue: 107
+ "rаiɴЬοw", // hue: 110
+ "rаіnboѡ", // hue: 110
+ "rаіnbοѡ", // hue: 113
+ "ʀainЬοѡ", // hue: 114
+ "rаіnЬοw", // hue: 116
+ "ʀaіɴЬow", // hue: 116
+ "rаinbοw", // hue: 122
+ "ʀаіɴboѡ", // hue: 125
+ "rаinbοѡ", // hue: 131
+ "rainbow", // hue: 134
+ "rаinЬοw", // hue: 134
+ "ʀаiɴboѡ", // hue: 140
+ "rainЬοѡ", // hue: 141
+ "raіɴЬow", // hue: 143
+ "ʀainЬoѡ", // hue: 143
+ "ʀaіɴbow", // hue: 143
+ "ʀainbow", // hue: 148
+ "rаіɴboѡ", // hue: 149
+ "ʀainboѡ", // hue: 155
+ "ʀaіnbow", // hue: 155
+ "ʀaіnЬow", // hue: 155
+ "raiɴbοw", // hue: 158
+ "ʀаiɴЬoѡ", // hue: 158
+ "rainbοw", // hue: 160
+ "rаinbow", // hue: 160
+ "ʀaіɴbοѡ", // hue: 164
+ "ʀаiɴbow", // hue: 164
+ "ʀаіnЬoѡ", // hue: 164
+ "ʀaiɴЬοѡ", // hue: 165
+ "rаiɴboѡ", // hue: 167
+ "ʀaіɴЬοw", // hue: 167
+ "ʀaіɴЬοѡ", // hue: 171
+ "raіnboѡ", // hue: 173
+ "ʀаіɴЬoѡ", // hue: 173
+ "rаіɴbοѡ", // hue: 176
+ "ʀаinЬow", // hue: 176
+ "rаiɴЬοѡ", // hue: 177
+ "rаіɴЬοw", // hue: 179
+ "ʀаinЬoѡ", // hue: 179
+ "ʀаіɴbow", // hue: 179
+ "rаiɴЬoѡ", // hue: 182
+ "raіɴbοѡ", // hue: 188
+ "rаіnЬoѡ", // hue: 188
+ "raiɴЬοѡ", // hue: 189
+ "raіɴЬοw", // hue: 191
+ "ʀaіɴbοw", // hue: 191
+ "ʀаіnЬow", // hue: 191
+ "rainbοѡ", // hue: 194
+ "rаinboѡ", // hue: 194
+ "rаіnbow", // hue: 194
+ "rainЬοw", // hue: 197
+ "rаinЬoѡ", // hue: 206
+ "rаіɴbow", // hue: 206
+ "rаіɴЬοѡ", // hue: 210
+ "ʀaiɴЬow", // hue: 212
+ "raіɴbοw", // hue: 218
+ "rаіnЬow", // hue: 218
+ "ʀaiɴbοѡ", // hue: 221
+ "ʀaiɴЬοw", // hue: 224
+ "ʀaіnbοѡ", // hue: 227
+ "raiɴboѡ", // hue: 230
+ "ʀaіnbοw", // hue: 230
+ "ʀaіnЬοw", // hue: 230
+ "ʀаinЬοѡ", // hue: 231
+ "rainboѡ", // hue: 232
+ "raіnbow", // hue: 232
+ "ʀаіɴЬow", // hue: 233
+ "ʀaіɴЬoѡ", // hue: 239
+ "ʀаіnЬοѡ", // hue: 246
+ "raiɴbοѡ", // hue: 248
+ "ʀаiɴЬow", // hue: 248
+ "raіɴЬοѡ", // hue: 249
+ "raiɴЬοw", // hue: 251
+ "rаіɴЬoѡ", // hue: 251
+ "ʀaiɴbow", // hue: 251
+ "ʀаinbοw", // hue: 251
+ "raіnbοѡ", // hue: 254
+];
+++ /dev/null
-// rustfmt-fn_args_layout: Compressed
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Tall
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Vertical
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
- // body
- }
-}
--- /dev/null
+// rustfmt-fn_params_layout: Compressed
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Tall
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Vertical
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
+ // body
+ }
+}
Normal(u32, String, ),
StructLike { x: i32, // Test comment
// Pre-comment
- #[Attr50] y: SomeType, // Aanother Comment
+ #[Attr50] y: SomeType, // Another Comment
}, SL { a: A }
}
// rustfmt-normalize_comments: true
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// rustfmt-brace_style: AlwaysNextLine
// Case with only one variable.
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
// Test some of the ways function signatures can be customised.
// Test compressed layout of args.
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// Empty list should stay on one line.
fn do_bar(
--- /dev/null
+// rustfmt-format_macro_bodies: true
+
+// with comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 + //comment 1
+ 42
+ //comment 2
+ ),
+ };
+ }};
+}
+
+// without comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 +
+ 42
+ ),
+ };
+ }};
+}
--- /dev/null
+// output doesn't get corrupted when using comments within generic type parameters of a trait
+
+pub trait Something<
+ A,
+ // some comment
+ B,
+ C
+> {
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
+
+pub trait SomethingElse<
+ A,
+ /* some comment */
+ B,
+ C
+> {
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
--- /dev/null
+// rustfmt-version: One
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write1 + fmt::Write2
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer1<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + Printer2<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+>,
+> {
+}
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+> + fmt::Write1
++ fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+)
+{
+}
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+) + fmt::Write
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+mut entries: entryyyyyyyy,
+) -> (
+impl Fn(
+AlphabeticalTraversal,
+Seconddddddddddddddddddddddddddddddddddd
+) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
++ Sendddddddddddddddddddddddddddddddddddddddddddd
+) {
+}
+
+pub trait SomeTrait:
+Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+for<'b> &'b Self: Send
++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
--- /dev/null
+// rustfmt-version: Two
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write1 + fmt::Write2
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer1<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + Printer2<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+>,
+> {
+}
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+> + fmt::Write1
++ fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+)
+{
+}
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+) + fmt::Write
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+mut entries: entryyyyyyyy,
+) -> (
+impl Fn(
+AlphabeticalTraversal,
+Seconddddddddddddddddddddddddddddddddddd
+) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
++ Sendddddddddddddddddddddddddddddddddddddddddddd
+) {
+}
+
+pub trait SomeTrait:
+Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+for<'b> &'b Self: Send
++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
--- /dev/null
+// rustfmt-max_width: 160
+// rustfmt-fn_call_width: 96
+// rustfmt-fn_args_layout: Compressed
+// rustfmt-trailing_comma: Always
+// rustfmt-wrap_comments: true
+
+fn foo() {
+ for elem in try!(gen_epub_book::ops::parse_descriptor_file(&mut try!(File::open(&opts.source_file.1).map_err(|_| {
+ gen_epub_book::Error::Io {
+ desc: "input file",
+ op: "open",
+ more: None,
+ }
+ })),
+ "input file")) {
+ println!("{}", elem);
+ }
+}
+
+fn write_content() {
+ io::copy(try!(File::open(in_f).map_err(|_| {
+ Error::Io {
+ desc: "Content",
+ op: "open",
+ more: None,
+ }
+ })),
+ w);
+}
--- /dev/null
+fn main() {
+ let x = 1;
+ ;let y = 3;
+}
--- /dev/null
+fn main() {;7
+}
+
+fn main() {
+ ;7
+}
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*","items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should also skip this invocation, as the wildcard covers it
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: []
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["unknown"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["foo","bar"]
+
+// Should skip this invocation
+foo!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+bar!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+baz!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should not skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["aaa","ccc"]
+
+// These tests demonstrate a realistic use case with use aliases.
+// The use statements should not impact functionality in any way.
+
+use crate::{aaa, bbb, ddd};
+
+// No use alias, invocation in list
+// Should skip this invocation
+aaa!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation in list
+// Should skip this invocation
+use crate::bbb as ccc;
+ccc!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation not in list
+// Should not skip this invocation
+use crate::ddd as eee;
+eee!(
+ const _: u8 = 0;
+);
+
+// No use alias, invocation not in list
+// Should not skip this invocation
+fff!(
+ const _: u8 = 0;
+);
-// Test tuple litterals
+// Test tuple literals
fn foo() {
let a = (a, a, a, a, a);
///
fn foo() {}
-/// A long commment for wrapping
+/// A long comment for wrapping
/// This is a long long long long long long long long long long long long long long long long long long long long sentence.
fn bar() {}
tbm,
/// POPCNT (Population Count)
popcnt,
- /// FXSR (Floating-point context fast save and restor)
+ /// FXSR (Floating-point context fast save and restore)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,
--- /dev/null
+impl Default for WhitespaceCharacters {
+ fn default() -> Self {
+ Self {
+ space: '·', // U+00B7
+ nbsp: '⍽', // U+237D
+ tab: '→', // U+2192
+ newline: '⏎', // U+23CE
+ }
+ }
+}
+
+const RAINBOWS: &[&str] = &[
+ "rаinЬοѡ", // hue: 0
+ "raіnЬοw", // hue: 2
+ "rаіɴЬow", // hue: 2
+ "raіɴЬoѡ", // hue: 8
+ "ʀainЬow", // hue: 8
+ "ʀaіɴboѡ", // hue: 8
+ "ʀаіnbοw", // hue: 11
+ "rainЬoѡ", // hue: 14
+ "raіɴbow", // hue: 14
+ "rаiɴЬow", // hue: 20
+ "raіnЬow", // hue: 26
+ "ʀaiɴbοw", // hue: 32
+ "raіɴboѡ", // hue: 35
+ "rаiɴbow", // hue: 35
+ "rаіnbοw", // hue: 38
+ "rаinЬow", // hue: 47
+ "ʀaіnboѡ", // hue: 47
+ "ʀaіnЬoѡ", // hue: 47
+ "ʀаіɴbοw", // hue: 53
+ "ʀaіnЬοѡ", // hue: 57
+ "raiɴЬoѡ", // hue: 68
+ "ʀainbοѡ", // hue: 68
+ "ʀаinboѡ", // hue: 68
+ "ʀаiɴbοw", // hue: 68
+ "ʀаіnbow", // hue: 68
+ "rаіnЬοѡ", // hue: 69
+ "ʀainЬοw", // hue: 71
+ "raiɴbow", // hue: 73
+ "raіnЬoѡ", // hue: 74
+ "rаіɴbοw", // hue: 77
+ "raіnЬοѡ", // hue: 81
+ "raiɴЬow", // hue: 83
+ "ʀainbοw", // hue: 83
+ "ʀаinbow", // hue: 83
+ "ʀаiɴbοѡ", // hue: 83
+ "ʀаіnboѡ", // hue: 83
+ "ʀаіɴЬοѡ", // hue: 84
+ "rainЬow", // hue: 85
+ "ʀаiɴЬοw", // hue: 86
+ "ʀаіnbοѡ", // hue: 89
+ "ʀаіnЬοw", // hue: 92
+ "rаiɴbοw", // hue: 95
+ "ʀаіɴbοѡ", // hue: 98
+ "ʀаiɴЬοѡ", // hue: 99
+ "raіnbοw", // hue: 101
+ "ʀаіɴЬοw", // hue: 101
+ "ʀaiɴboѡ", // hue: 104
+ "ʀаinbοѡ", // hue: 104
+ "rаiɴbοѡ", // hue: 107
+ "ʀаinЬοw", // hue: 107
+ "rаiɴЬοw", // hue: 110
+ "rаіnboѡ", // hue: 110
+ "rаіnbοѡ", // hue: 113
+ "ʀainЬοѡ", // hue: 114
+ "rаіnЬοw", // hue: 116
+ "ʀaіɴЬow", // hue: 116
+ "rаinbοw", // hue: 122
+ "ʀаіɴboѡ", // hue: 125
+ "rаinbοѡ", // hue: 131
+ "rainbow", // hue: 134
+ "rаinЬοw", // hue: 134
+ "ʀаiɴboѡ", // hue: 140
+ "rainЬοѡ", // hue: 141
+ "raіɴЬow", // hue: 143
+ "ʀainЬoѡ", // hue: 143
+ "ʀaіɴbow", // hue: 143
+ "ʀainbow", // hue: 148
+ "rаіɴboѡ", // hue: 149
+ "ʀainboѡ", // hue: 155
+ "ʀaіnbow", // hue: 155
+ "ʀaіnЬow", // hue: 155
+ "raiɴbοw", // hue: 158
+ "ʀаiɴЬoѡ", // hue: 158
+ "rainbοw", // hue: 160
+ "rаinbow", // hue: 160
+ "ʀaіɴbοѡ", // hue: 164
+ "ʀаiɴbow", // hue: 164
+ "ʀаіnЬoѡ", // hue: 164
+ "ʀaiɴЬοѡ", // hue: 165
+ "rаiɴboѡ", // hue: 167
+ "ʀaіɴЬοw", // hue: 167
+ "ʀaіɴЬοѡ", // hue: 171
+ "raіnboѡ", // hue: 173
+ "ʀаіɴЬoѡ", // hue: 173
+ "rаіɴbοѡ", // hue: 176
+ "ʀаinЬow", // hue: 176
+ "rаiɴЬοѡ", // hue: 177
+ "rаіɴЬοw", // hue: 179
+ "ʀаinЬoѡ", // hue: 179
+ "ʀаіɴbow", // hue: 179
+ "rаiɴЬoѡ", // hue: 182
+ "raіɴbοѡ", // hue: 188
+ "rаіnЬoѡ", // hue: 188
+ "raiɴЬοѡ", // hue: 189
+ "raіɴЬοw", // hue: 191
+ "ʀaіɴbοw", // hue: 191
+ "ʀаіnЬow", // hue: 191
+ "rainbοѡ", // hue: 194
+ "rаinboѡ", // hue: 194
+ "rаіnbow", // hue: 194
+ "rainЬοw", // hue: 197
+ "rаinЬoѡ", // hue: 206
+ "rаіɴbow", // hue: 206
+ "rаіɴЬοѡ", // hue: 210
+ "ʀaiɴЬow", // hue: 212
+ "raіɴbοw", // hue: 218
+ "rаіnЬow", // hue: 218
+ "ʀaiɴbοѡ", // hue: 221
+ "ʀaiɴЬοw", // hue: 224
+ "ʀaіnbοѡ", // hue: 227
+ "raiɴboѡ", // hue: 230
+ "ʀaіnbοw", // hue: 230
+ "ʀaіnЬοw", // hue: 230
+ "ʀаinЬοѡ", // hue: 231
+ "rainboѡ", // hue: 232
+ "raіnbow", // hue: 232
+ "ʀаіɴЬow", // hue: 233
+ "ʀaіɴЬoѡ", // hue: 239
+ "ʀаіnЬοѡ", // hue: 246
+ "raiɴbοѡ", // hue: 248
+ "ʀаiɴЬow", // hue: 248
+ "raіɴЬοѡ", // hue: 249
+ "raiɴЬοw", // hue: 251
+ "rаіɴЬoѡ", // hue: 251
+ "ʀaiɴbow", // hue: 251
+ "ʀаinbοw", // hue: 251
+ "raіnbοѡ", // hue: 254
+];
+++ /dev/null
-// rustfmt-fn_args_layout: Compressed
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(
- ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
- adipiscing: Adipiscing, elit: Elit,
- );
-
- fn lorem(
- ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
- adipiscing: Adipiscing, elit: Elit,
- ) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Tall
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- );
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- ) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Vertical
-// Function arguments density
-
-trait Lorem {
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- );
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- ) {
- // body
- }
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- );
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- ) {
- // body
- }
-}
--- /dev/null
+// rustfmt-fn_params_layout: Compressed
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
+ adipiscing: Adipiscing, elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
+ adipiscing: Adipiscing, elit: Elit,
+ ) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Tall
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ ) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Vertical
+// Function arguments density
+
+trait Lorem {
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ ) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ ) {
+ // body
+ }
+}
x: i32, // Test comment
// Pre-comment
#[Attr50]
- y: SomeType, // Aanother Comment
+ y: SomeType, // Another Comment
},
SL {
a: A,
// rustfmt-normalize_comments: true
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// rustfmt-brace_style: AlwaysNextLine
// Case with only one variable.
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
// Test some of the ways function signatures can be customised.
// Test compressed layout of args.
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// Empty list should stay on one line.
fn do_bar() -> u8 {
--- /dev/null
+// rustfmt-format_macro_matchers: false
+
+macro_rules! foo {
+ ($a:ident : $b:ty) => {};
+ ($a:ident $b:ident $c:ident) => {};
+}
--- /dev/null
+// rustfmt-format_macro_matchers: true
+
+macro_rules! foo {
+ ($a:ident : $b:ty) => {};
+ ($a:ident $b:ident $c:ident) => {};
+}
--- /dev/null
+// rustfmt-format_macro_bodies: false
+
+// with comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 + //comment 1
+ 42
+ //comment 2
+ ),
+ };
+ }};
+}
+
+// without comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 +
+ 42
+ ),
+ };
+ }};
+}
--- /dev/null
+// rustfmt-format_macro_bodies: true
+
+// with comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 + //comment 1
+ 42
+ //comment 2
+ ),
+ };
+ }};
+}
+
+// without comments
+macro_rules! macros {
+ () => {{
+ Struct { field: (42 + 42) };
+ }};
+}
--- /dev/null
+// output doesn't get corrupted when using comments within generic type parameters of a trait
+
+pub trait Something<
+ A,
+ // some comment
+ B,
+ C,
+>
+{
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
+
+pub trait SomethingElse<A, /* some comment */ B, C> {
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
--- /dev/null
+// rustfmt-version: One
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+>
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write1
+ + fmt::Write2
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer1<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + Printer2<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ >,
+> {
+}
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ > + fmt::Write1
+ + fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ),
+{
+}
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ) + fmt::Write,
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+ mut entries: entryyyyyyyy,
+) -> (impl Fn(
+ AlphabeticalTraversal,
+ Seconddddddddddddddddddddddddddddddddddd,
+) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ + Sendddddddddddddddddddddddddddddddddddddddddddd) {
+}
+
+pub trait SomeTrait:
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+ for<'b> &'b Self: Send
+ + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
--- /dev/null
+// rustfmt-version: Two
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write1
+ + fmt::Write2
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer1<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + Printer2<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ >,
+> {
+}
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ > + fmt::Write1
+ + fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ),
+{
+}
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ) + fmt::Write,
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+ mut entries: entryyyyyyyy,
+) -> (
+ impl Fn(
+ AlphabeticalTraversal,
+ Seconddddddddddddddddddddddddddddddddddd,
+ ) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ + Sendddddddddddddddddddddddddddddddddddddddddddd
+) {
+}
+
+pub trait SomeTrait:
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+ for<'b> &'b Self: Send
+ + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
// rustfmt-brace_style: SameLineWhere
// rustfmt-comment_width: 100
// rustfmt-edition: 2018
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
// rustfmt-hard_tabs: false
// rustfmt-match_block_trailing_comma: true
// rustfmt-max_width: 100
--- /dev/null
+// Test /* comment */ inside trait generics does not get duplicated.
+trait Test</* comment */ T> {}
+
+trait TestTwo</* comment */ T, /* comment */ V> {}
--- /dev/null
+// rustfmt-max_width: 160
+// rustfmt-fn_call_width: 96
+// rustfmt-fn_args_layout: Compressed
+// rustfmt-trailing_comma: Always
+// rustfmt-wrap_comments: true
+
+fn foo() {
+ for elem in try!(gen_epub_book::ops::parse_descriptor_file(
+ &mut try!(File::open(&opts.source_file.1).map_err(|_| {
+ gen_epub_book::Error::Io {
+ desc: "input file",
+ op: "open",
+ more: None,
+ }
+ })),
+ "input file"
+ )) {
+ println!("{}", elem);
+ }
+}
+
+fn write_content() {
+ io::copy(
+ try!(File::open(in_f).map_err(|_| {
+ Error::Io {
+ desc: "Content",
+ op: "open",
+ more: None,
+ }
+ })),
+ w,
+ );
+}
--- /dev/null
+use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding::
+ BluetoothRemoteGATTServerMethods;
--- /dev/null
+fn main() {
+ let x = 1;
+ let y = 3;
+}
--- /dev/null
+fn main() {
+ 7
+}
+
+fn main() {
+ 7
+}
--- /dev/null
+//rustfmt-format_macro_bodies: true
+
+macro_rules! mto_text_left {
+ ($buf:ident, $n:ident, $pos:ident, $state:ident) => {{
+ let cursor = loop {
+ state = match iter.next() {
+ None if $pos == DP::Start => break last_char_idx($buf),
+ None /*some comment */ => break 0,
+ };
+ };
+ Ok(saturate_cursor($buf, cursor))
+ }};
+}
--- /dev/null
+type Foo = impl Send;
+struct Struct<
+ const C: usize = {
+ let _: Foo = ();
+ //~^ ERROR: mismatched types
+ 0
+ },
+>;
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*","items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should also skip this invocation, as the wildcard covers it
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: []
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["unknown"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["foo","bar"]
+
+// Should skip this invocation
+foo!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+bar!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+baz!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should not skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["aaa","ccc"]
+
+// These tests demonstrate a realistic use case with use aliases.
+// The use statements should not impact functionality in any way.
+
+use crate::{aaa, bbb, ddd};
+
+// No use alias, invocation in list
+// Should skip this invocation
+aaa!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation in list
+// Should skip this invocation
+use crate::bbb as ccc;
+ccc!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation not in list
+// Should not skip this invocation
+use crate::ddd as eee;
+eee!(
+ const _: u8 = 0;
+);
+
+// No use alias, invocation not in list
+// Should not skip this invocation
+fff!(
+ const _: u8 = 0;
+);
-// Test tuple litterals
+// Test tuple literals
fn foo() {
let a = (a, a, a, a, a);
/// ```
fn foo() {}
-/// A long commment for wrapping
+/// A long comment for wrapping
/// This is a long long long long long long long long long long long long long
/// long long long long long long long sentence.
fn bar() {}
[dependencies]
cargo_metadata = "0.14"
+cargo-platform = "0.1.2"
regex = "1"
miropt-test-tools = { path = "../miropt-test-tools" }
lazy_static = "1"
walkdir = "2"
ignore = "0.4.18"
+semver = "1.0"
termcolor = "1.1.3"
[[bin]]
//! Checks the licenses of third-party dependencies.
-use cargo_metadata::{Metadata, Package, PackageId, Resolve};
-use std::collections::{BTreeSet, HashSet};
+use cargo_metadata::{DepKindInfo, Metadata, Package, PackageId};
+use std::collections::HashSet;
use std::path::Path;
/// These are licenses that are allowed for all crates, including the runtime,
("cranelift-codegen", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen-meta", "Apache-2.0 WITH LLVM-exception"),
("cranelift-codegen-shared", "Apache-2.0 WITH LLVM-exception"),
- ("cranelift-egraph", "Apache-2.0 WITH LLVM-exception"),
("cranelift-entity", "Apache-2.0 WITH LLVM-exception"),
("cranelift-frontend", "Apache-2.0 WITH LLVM-exception"),
("cranelift-isle", "Apache-2.0 WITH LLVM-exception"),
"autocfg",
"bitflags",
"block-buffer",
- "bumpalo", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
"cc",
"cfg-if",
"chalk-derive",
"chalk-engine",
"chalk-ir",
"chalk-solve",
- "chrono",
"convert_case", // dependency of derive_more
"compiler_builtins",
"cpufeatures",
"dlmalloc",
"either",
"ena",
- "env_logger",
"expect-test",
"fallible-iterator", // dependency of `thorin`
"fastrand",
- "filetime",
"fixedbitset",
"flate2",
"fluent-bundle",
"gsgdt",
"hashbrown",
"hermit-abi",
- "humantime",
"icu_list",
"icu_locid",
"icu_provider",
"icu_provider_adapters",
"icu_provider_macros",
- "if_chain",
"indexmap",
"instant",
"intl-memoizer",
"itertools",
"itoa",
"jobserver",
- "js-sys", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
"lazy_static",
"libc",
"libloading",
"memmap2",
"memoffset",
"miniz_oxide",
- "num-integer",
- "num-traits",
"num_cpus",
"object",
"odht",
"proc-macro2",
"psm",
"punycode",
- "quick-error",
"quote",
"rand",
"rand_chacha",
"thiserror-impl",
"thorin-dwp",
"thread_local",
- "time",
"tinystr",
"tinyvec",
"tinyvec_macros",
"valuable",
"version_check",
"wasi",
- // vvv Included in Cargo's dep graph but only activated on wasm32-*-unknown.
- "wasm-bindgen",
- "wasm-bindgen-backend",
- "wasm-bindgen-macro",
- "wasm-bindgen-macro-support",
- "wasm-bindgen-shared",
- // ^^^ Included in Cargo's dep graph but only activated on wasm32-*-unknown.
"winapi",
"winapi-i686-pc-windows-gnu",
"winapi-util",
"cranelift-codegen",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
- "cranelift-egraph",
"cranelift-entity",
"cranelift-frontend",
"cranelift-isle",
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
"windows-sys",
+ "windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
"windows_x86_64_msvc",
];
restricted_dependency_crates: &[&'static str],
bad: &mut bool,
) {
+ let mut deps = HashSet::new();
+ for to_check in restricted_dependency_crates {
+ let to_check = pkg_from_name(metadata, to_check);
+ use cargo_platform::Cfg;
+ use std::str::FromStr;
+ // We don't expect the compiler to ever run on wasm32, so strip
+ // out those dependencies to avoid polluting the permitted list.
+ deps_of_filtered(metadata, &to_check.id, &mut deps, &|dep_kinds| {
+ dep_kinds.iter().any(|dep_kind| {
+ dep_kind
+ .target
+ .as_ref()
+ .map(|target| {
+ !target.matches(
+ "wasm32-unknown-unknown",
+ &[
+ Cfg::from_str("target_arch=\"wasm32\"").unwrap(),
+ Cfg::from_str("target_os=\"unknown\"").unwrap(),
+ ],
+ )
+ })
+ .unwrap_or(true)
+ })
+ });
+ }
+
// Check that the PERMITTED_DEPENDENCIES does not have unused entries.
- for name in permitted_dependencies {
- if !metadata.packages.iter().any(|p| p.name == *name) {
+ for permitted in permitted_dependencies {
+ if !deps.iter().any(|dep_id| &pkg_from_id(metadata, dep_id).name == permitted) {
tidy_error!(
bad,
- "could not find allowed package `{}`\n\
+ "could not find allowed package `{permitted}`\n\
Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
- name
);
}
}
- // Get the list in a convenient form.
- let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
-
- // Check dependencies.
- let mut visited = BTreeSet::new();
- let mut unapproved = BTreeSet::new();
- for &krate in restricted_dependency_crates.iter() {
- let pkg = pkg_from_name(metadata, krate);
- let mut bad =
- check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
- unapproved.append(&mut bad);
- }
-
- if !unapproved.is_empty() {
- tidy_error!(bad, "Dependencies for {} not explicitly permitted:", descr);
- for dep in unapproved {
- println!("* {dep}");
- }
- }
-}
-
-/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
-/// the list of permitted dependencies. Returns a list of disallowed dependencies.
-fn check_crate_dependencies<'a>(
- permitted_dependencies: &'a HashSet<&'static str>,
- metadata: &'a Metadata,
- visited: &mut BTreeSet<&'a PackageId>,
- krate: &'a Package,
-) -> BTreeSet<&'a PackageId> {
- // This will contain bad deps.
- let mut unapproved = BTreeSet::new();
-
- // Check if we have already visited this crate.
- if visited.contains(&krate.id) {
- return unapproved;
- }
- visited.insert(&krate.id);
+ // Get in a convenient form.
+ let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
- // If this path is in-tree, we don't require it to be explicitly permitted.
- if krate.source.is_some() {
- // If this dependency is not on `PERMITTED_DEPENDENCIES`, add to bad set.
- if !permitted_dependencies.contains(krate.name.as_str()) {
- unapproved.insert(&krate.id);
+ for dep in deps {
+ let dep = pkg_from_id(metadata, dep);
+ // If this path is in-tree, we don't require it to be explicitly permitted.
+ if dep.source.is_some() {
+ if !permitted_dependencies.contains(dep.name.as_str()) {
+ tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id);
+ }
}
}
-
- // Do a DFS in the crate graph.
- let to_check = deps_of(metadata, &krate.id);
-
- for dep in to_check {
- let mut bad = check_crate_dependencies(permitted_dependencies, metadata, visited, dep);
- unapproved.append(&mut bad);
- }
-
- unapproved
}
/// Prevents multiple versions of some expensive crates.
}
}
-/// Returns a list of dependencies for the given package.
-fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
- let resolve = metadata.resolve.as_ref().unwrap();
- let node = resolve
- .nodes
- .iter()
- .find(|n| &n.id == pkg_id)
- .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
- node.deps
- .iter()
- .map(|dep| {
- metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| {
- panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id)
- })
- })
- .collect()
-}
-
/// Finds a package with the given name.
fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
let mut i = metadata.packages.iter().filter(|p| p.name == name);
result
}
+fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
+ metadata.packages.iter().find(|p| &p.id == id).unwrap()
+}
+
/// Finds all the packages that are in the rust runtime.
fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> {
- let resolve = metadata.resolve.as_ref().unwrap();
let mut result = HashSet::new();
for name in RUNTIME_CRATES {
let id = &pkg_from_name(metadata, name).id;
- normal_deps_of_r(resolve, id, &mut result);
+ deps_of_filtered(metadata, id, &mut result, &|_| true);
}
result
}
-/// Recursively find all normal dependencies.
-fn normal_deps_of_r<'a>(
- resolve: &'a Resolve,
+/// Recursively find all dependencies.
+fn deps_of_filtered<'a>(
+ metadata: &'a Metadata,
pkg_id: &'a PackageId,
result: &mut HashSet<&'a PackageId>,
+ filter: &dyn Fn(&[DepKindInfo]) -> bool,
) {
if !result.insert(pkg_id) {
return;
}
- let node = resolve
+ let node = metadata
+ .resolve
+ .as_ref()
+ .unwrap()
.nodes
.iter()
.find(|n| &n.id == pkg_id)
.unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
for dep in &node.deps {
- normal_deps_of_r(resolve, &dep.pkg, result);
+ if !filter(&dep.dep_kinds) {
+ continue;
+ }
+ deps_of_filtered(metadata, &dep.pkg, result, filter);
}
}
+fn direct_deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
+ let resolve = metadata.resolve.as_ref().unwrap();
+ let node = resolve.nodes.iter().find(|n| &n.id == pkg_id).unwrap();
+ node.deps.iter().map(|dep| pkg_from_id(metadata, &dep.pkg)).collect()
+}
+
fn check_rustfix(metadata: &Metadata, bad: &mut bool) {
let cargo = pkg_from_name(metadata, "cargo");
let compiletest = pkg_from_name(metadata, "compiletest");
- let cargo_deps = deps_of(metadata, &cargo.id);
- let compiletest_deps = deps_of(metadata, &compiletest.id);
+ let cargo_deps = direct_deps_of(metadata, &cargo.id);
+ let compiletest_deps = direct_deps_of(metadata, &compiletest.id);
let cargo_rustfix = cargo_deps.iter().find(|p| p.name == "rustfix").unwrap();
let compiletest_rustfix = compiletest_deps.iter().find(|p| p.name == "rustfix").unwrap();
if cargo_rustfix.version != compiletest_rustfix.version {
const ERROR_TESTS_PATH: &str = "tests/ui/error-codes/";
// Error codes that (for some reason) can't have a doctest in their explanation. Error codes are still expected to provide a code example, even if untested.
-const IGNORE_DOCTEST_CHECK: &[&str] =
- &["E0208", "E0464", "E0570", "E0601", "E0602", "E0640", "E0717"];
+const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E0640", "E0717"];
// Error codes that don't yet have a UI test. This list will eventually be removed.
const IGNORE_UI_TEST_CHECK: &[&str] =
- &["E0461", "E0465", "E0476", "E0514", "E0523", "E0554", "E0640", "E0717", "E0729", "E0789"];
+ &["E0461", "E0465", "E0476", "E0514", "E0523", "E0554", "E0640", "E0717", "E0729"];
macro_rules! verbose_print {
($verbose:expr, $($fmt:tt)*) => {
pub mod mir_opt_tests;
pub mod pal;
pub mod primitive_docs;
+pub mod rustdoc_gui_tests;
pub mod style;
pub mod target_specific_tests;
pub mod tests_placement;
pub mod unit_tests;
pub mod unstable_book;
pub mod walk;
+pub mod x_version;
let handle = s.spawn(|| {
let mut flag = false;
- $p::check($($args),* , &mut flag);
+ $p::check($($args, )* &mut flag);
if (flag) {
bad.store(true, Ordering::Relaxed);
}
check!(debug_artifacts, &tests_path);
check!(ui_tests, &tests_path);
check!(mir_opt_tests, &tests_path, bless);
+ check!(rustdoc_gui_tests, &tests_path);
// Checks that only make sense for the compiler.
check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose);
check!(alphabetical, &compiler_path);
check!(alphabetical, &library_path);
+ check!(x_version, &root_path, &cargo);
+
let collected = {
drain_handles(&mut handles);
--- /dev/null
+//! Tidy check to ensure that rustdoc GUI tests start with a small description.
+
+use std::path::Path;
+
+pub fn check(path: &Path, bad: &mut bool) {
+ crate::walk::walk(
+ &path.join("rustdoc-gui"),
+ &mut |p| {
+ // If there is no extension, it's very likely a folder and we want to go into it.
+ p.extension().map(|e| e != "goml").unwrap_or(false)
+ },
+ &mut |entry, content| {
+ for line in content.lines() {
+ if !line.starts_with("// ") {
+ tidy_error!(
+ bad,
+ "{}: rustdoc-gui tests must start with a small description",
+ entry.path().display(),
+ );
+ return;
+ } else if line.starts_with("// ") {
+ let parts = line[2..].trim();
+ // We ignore tidy comments.
+ if parts.starts_with("// tidy-") {
+ continue;
+ }
+ // All good!
+ return;
+ }
+ }
+ },
+ );
+}
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";
+const DOUBLE_SPACE_AFTER_DOT: &str = r"\
+Use a single space after dots in comments.";
+
const ANNOTATIONS_TO_IGNORE: &[&str] = &[
"// @!has",
"// @has",
if filename.contains("ignore-tidy") {
return;
}
+ // apfloat shouldn't be changed because of license problems
+ if is_in(file, "compiler", "rustc_apfloat") {
+ return;
+ }
let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
let mut skip_undocumented_unsafe =
contains_ignore_directive(can_contain, &contents, "undocumented-unsafe");
if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
err(LLVM_UNREACHABLE_INFO);
}
+
+ // For now only enforce in compiler
+ let is_compiler = || file.components().any(|c| c.as_os_str() == "compiler");
+ if is_compiler()
+ && line.contains("//")
+ && line
+ .chars()
+ .collect::<Vec<_>>()
+ .windows(4)
+ .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
+ {
+ err(DOUBLE_SPACE_AFTER_DOT)
+ }
}
if leading_new_lines {
let mut err = |_| {
--- /dev/null
+use semver::Version;
+use std::path::Path;
+use std::process::{Command, Stdio};
+
+pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
+ let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();
+
+ let child = match cargo_list {
+ Ok(child) => child,
+ Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e),
+ };
+
+ let cargo_list = child.wait_with_output().unwrap();
+
+ if cargo_list.status.success() {
+ let exe_list = String::from_utf8_lossy(&cargo_list.stdout);
+ let exe_list = exe_list.lines();
+
+ let mut installed: Option<Version> = None;
+
+ for line in exe_list {
+ let mut iter = line.split_whitespace();
+ if iter.next() == Some("x") {
+ if let Some(version) = iter.next() {
+ // Check this is the rust-lang/rust x tool installation since it should be
+ // installed at a path containing `src/tools/x`.
+ if let Some(path) = iter.next() {
+ if path.contains(&"src/tools/x") {
+ let version = version.strip_prefix("v").unwrap();
+ installed = Some(Version::parse(version).unwrap());
+ break;
+ }
+ };
+ }
+ } else {
+ continue;
+ }
+ }
+ // Unwrap the some if x is installed, otherwise return because it's fine if x isn't installed.
+ let installed = if let Some(i) = installed { i } else { return };
+
+ if let Some(expected) = get_x_wrapper_version(root, cargo) {
+ if installed < expected {
+ return println!(
+ "Current version of x is {installed}, but the latest version is {expected}\nConsider updating to the newer version of x by running `cargo install --path src/tools/x`"
+ );
+ }
+ } else {
+ return tidy_error!(
+ bad,
+ "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`"
+ );
+ }
+ } else {
+ return tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status);
+ }
+}
+
+// Parse latest version out of `x` Cargo.toml
+fn get_x_wrapper_version(root: &Path, cargo: &Path) -> Option<Version> {
+ let mut cmd = cargo_metadata::MetadataCommand::new();
+ cmd.cargo_path(cargo)
+ .manifest_path(root.join("src/tools/x/Cargo.toml"))
+ .no_deps()
+ .features(cargo_metadata::CargoOpt::AllFeatures);
+ let mut metadata = t!(cmd.exec());
+ metadata.packages.pop().map(|x| x.version)
+}
// assembly-output: emit-asm
-// min-llvm-version: 14.0
+// min-llvm-version: 15.0
// only-x86_64
// revisions: opt-speed opt-size
// [opt-speed] compile-flags: -Copt-level=1
// of the sysv64 abi.
//
// needs-llvm-components: x86
-// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu
+// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu -Copt-level=0
#![crate_type = "lib"]
#![no_core]
// of the x86-interrupt abi.
// needs-llvm-components: x86
-// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu
+// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu -Copt-level=0
#![crate_type = "lib"]
#![no_core]
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
// as long as it doesn't cause a verifier error by using `bitcast`.
transmute(x)
}
+
+pub enum Either<T, U> { A(T), B(U) }
+
+// Previously, we would codegen this as passing/returning a scalar pair of `{ i8, ptr }`,
+// with the `ptr` field representing both `&i32` and `fn()` depending on the variant.
+// This is incorrect, because `fn()` should be `ptr addrspace(1)`, not `ptr`.
+
+// CHECK: define{{.+}}void @should_not_combine_addrspace({{.+\*|ptr}}{{.+}}sret{{.+}}%0, {{.+\*|ptr}}{{.+}}%x)
+#[no_mangle]
+#[inline(never)]
+pub fn should_not_combine_addrspace(x: Either<&i32, fn()>) -> Either<&i32, fn()> {
+ x
+}
+
+// The incorrectness described above would result in us producing (after optimizations)
+// a `ptrtoint`/`inttoptr` roundtrip to convert from `ptr` to `ptr addrspace(1)`.
+
+// CHECK-LABEL: @call_with_fn_ptr
+#[no_mangle]
+pub fn call_with_fn_ptr<'a>(f: fn()) -> Either<&'a i32, fn()> {
+ // CHECK-NOT: ptrtoint
+ // CHECK-NOT: inttoptr
+ // CHECK: call addrspace(1) void @should_not_combine_addrspace
+ should_not_combine_addrspace(Either::B(f))
+}
// Hide the LLVM 15+ `allocalign` attribute in the declaration of __rust_alloc
// from the CHECK-NOT above. We don't check the attributes here because we can't rely
// on all of them being set until LLVM 15.
-// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+.*}})
+// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef)
// Hide the `allocalign` attribute in the declaration of __rust_alloc
// from the CHECK-NOT above, and also verify the attributes got set reasonably.
-// CHECK: declare noalias ptr @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+}} allocalign) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
+// CHECK: declare noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} }
// ignore-wasm32-bare compiled with panic=abort by default
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
//
#![crate_type = "lib"]
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
// ignore-riscv64
pub struct Foo(u16);
// CHECK-LABEL: @check_lt
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_lt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ult i16 %[[A]], %[[B]]
}
// CHECK-LABEL: @check_le
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_le(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ule i16 %[[A]], %[[B]]
}
// CHECK-LABEL: @check_gt
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_gt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ugt i16 %[[A]], %[[B]]
}
// CHECK-LABEL: @check_ge
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_ge(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp uge i16 %[[A]], %[[B]]
// compile-flags: -Cdebuginfo=2 -Copt-level=0 -Csymbol-mangling-version=v0
// ignore-tidy-linelength
+// Make sure that vtables don't have the unnamed_addr attribute when debuginfo is enabled.
+// This helps debuggers more reliably map from dyn pointer to concrete type.
+// CHECK: @vtable.0 = private constant <{
+// CHECK: @vtable.1 = private constant <{
+// CHECK: @vtable.2 = private constant <{
+// CHECK: @vtable.3 = private constant <{
+// CHECK: @vtable.4 = private constant <{
+
// NONMSVC: ![[USIZE:[0-9]+]] = !DIBasicType(name: "usize"
// MSVC: ![[USIZE:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "usize"
// NONMSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()"
-// This test is for *-windows-msvc only.
+ // This test is for *-windows-msvc only.
// only-windows
// ignore-gnu
// CHECK: @static_global1 = external local_unnamed_addr global i32
// CHECK: @static_global2 = external local_unnamed_addr global i32
-// CHECK: declare dllimport i32 @dylib_func1(i32)
-// CHECK: declare dllimport i32 @dylib_func2(i32)
-// CHECK: declare i32 @static_func1(i32)
-// CHECK: declare i32 @static_func2(i32)
+// CHECK: declare dllimport noundef i32 @dylib_func1(i32 noundef)
+// CHECK: declare dllimport noundef i32 @dylib_func2(i32 noundef)
+// CHECK: declare noundef i32 @static_func1(i32 noundef)
+// CHECK: declare noundef i32 @static_func2(i32 noundef)
#[link(name = "dummy", kind="dylib")]
extern "C" {
B,
}
-// CHECK: define i8 @match0{{.*}}
+// CHECK: define noundef i8 @match0{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: %1 = icmp eq i8 %0, 2
// CHECK-NEXT: %2 = and i8 %0, 1
C,
}
-// CHECK: define i8 @match1{{.*}}
+// CHECK: define noundef i8 @match1{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: [[DISCR:%.*]] = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
// CHECK-NEXT: switch i8 [[DISCR]], label {{.*}} [
E,
}
-// CHECK: define i8 @match2{{.*}}
+// CHECK: define noundef i8 @match2{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: %1 = add i8 %0, 2
// CHECK-NEXT: %2 = zext i8 %1 to i64
trait Copy {}
pub mod tests {
- // CHECK: @f1(i32 inreg %_1, i32 inreg %_2, i32 %_3)
+ // CHECK: @f1(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {}
- // CHECK: @f2({{i32\*|ptr}} inreg %_1, {{i32\*|ptr}} inreg %_2, {{i32\*|ptr}} %_3)
+ // CHECK: @f2({{i32\*|ptr}} inreg noundef %_1, {{i32\*|ptr}} inreg noundef %_2, {{i32\*|ptr}} noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {}
- // CHECK: @f3(float %_1, i32 inreg %_2, i32 inreg %_3, i32 %_4)
+ // CHECK: @f3(float noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3, i32 noundef %_4)
#[no_mangle]
pub extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {}
- // CHECK: @f4(i32 inreg %_1, float %_2, i32 inreg %_3, i32 %_4)
+ // CHECK: @f4(i32 inreg noundef %_1, float noundef %_2, i32 inreg noundef %_3, i32 noundef %_4)
#[no_mangle]
pub extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {}
- // CHECK: @f5(i64 %_1, i32 %_2)
+ // CHECK: @f5(i64 noundef %_1, i32 noundef %_2)
#[no_mangle]
pub extern "fastcall" fn f5(_: i64, _: i32) {}
- // CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg %_2, i32 %_3)
+ // CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f6(_: bool, _: i32, _: i32) {}
}
#[no_mangle]
pub fn sum(x: u32, y: u32) -> u32 {
-// YES-LABEL: define{{.*}}i32 @sum(i32 %0, i32 %1)
+// YES-LABEL: define{{.*}}i32 @sum(i32 noundef %0, i32 noundef %1)
// YES-NEXT: %3 = add i32 %1, %0
// YES-NEXT: ret i32 %3
-// NO-LABEL: define{{.*}}i32 @sum(i32 %x, i32 %y)
+// NO-LABEL: define{{.*}}i32 @sum(i32 noundef %x, i32 noundef %y)
// NO-NEXT: start:
// NO-NEXT: %z = add i32 %y, %x
// NO-NEXT: ret i32 %z
-// compile-flags: --crate-type=rlib
+// compile-flags: --crate-type=rlib -Copt-level=0
// revisions: aarch64-apple aarch64-linux force x64-apple x64-linux
// [aarch64-apple] needs-llvm-components: aarch64
// [aarch64-apple] compile-flags: --target=aarch64-apple-darwin
// compile-flags: -O -C no-prepopulate-passes
#![crate_type = "lib"]
-#![feature(rustc_attrs)]
use std::mem::MaybeUninit;
use std::num::NonZeroU64;
use std::marker::PhantomPinned;
+use std::ptr::NonNull;
pub struct S {
_field: [i32; 8],
x
}
-// CHECK: i64 @int(i64 %x)
+// CHECK: noundef i64 @int(i64 noundef %x)
#[no_mangle]
pub fn int(x: u64) -> u64 {
x
x
}
-// CHECK: i64 @option_nonzero_int(i64 %x)
+// CHECK: noundef i64 @option_nonzero_int(i64 noundef %x)
#[no_mangle]
pub fn option_nonzero_int(x: Option<NonZeroU64>) -> Option<NonZeroU64> {
x
pub fn borrowed_struct(_: &S) {
}
-// CHECK: @raw_struct({{%S\*|ptr}} %_1)
+// CHECK: @option_borrow({{i32\*|ptr}} noalias noundef readonly align 4 dereferenceable_or_null(4) %x)
+#[no_mangle]
+pub fn option_borrow(x: Option<&i32>) {
+}
+
+// CHECK: @option_borrow_mut({{i32\*|ptr}} noalias noundef align 4 dereferenceable_or_null(4) %x)
+#[no_mangle]
+pub fn option_borrow_mut(x: Option<&mut i32>) {
+}
+
+// CHECK: @raw_struct({{%S\*|ptr}} noundef %_1)
#[no_mangle]
pub fn raw_struct(_: *const S) {
}
+// CHECK: @raw_option_nonnull_struct({{i32\*|ptr}} noundef %_1)
+#[no_mangle]
+pub fn raw_option_nonnull_struct(_: Option<NonNull<S>>) {
+}
+
+
// `Box` can get deallocated during execution of the function, so it should
// not get `dereferenceable`.
// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @_box({{i32\*|ptr}} noalias noundef nonnull align 4 %x)
}
// Hack to get the correct size for the length part in slices
-// CHECK: @helper([[USIZE:i[0-9]+]] %_1)
+// CHECK: @helper([[USIZE:i[0-9]+]] noundef %_1)
#[no_mangle]
pub fn helper(_: usize) {
}
-// CHECK: @slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {
}
-// CHECK: @mutable_slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @mutable_slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_slice(_: &mut [u8]) {
}
-// CHECK: @unsafe_slice({{\[0 x i16\]\*|ptr}} noundef nonnull align 2 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @unsafe_slice({{\[0 x i16\]\*|ptr}} noundef nonnull align 2 %_1.0, [[USIZE]] noundef %_1.1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {
}
-// CHECK: @raw_slice({{\[0 x i8\]\*|ptr}} %_1.0, [[USIZE]] %_1.1)
+// CHECK: @raw_slice({{\[0 x i8\]\*|ptr}} noundef %_1.0, [[USIZE]] noundef %_1.1)
#[no_mangle]
pub fn raw_slice(_: *const [u8]) {
}
-// CHECK: @str({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @str({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {
// CHECK: @trait_borrow({{\{\}\*|ptr}} noundef nonnull align 1 %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
-pub fn trait_borrow(_: &Drop) {
+pub fn trait_borrow(_: &dyn Drop) {
+}
+
+// CHECK: @option_trait_borrow({{i8\*|ptr}} noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
+#[no_mangle]
+pub fn option_trait_borrow(x: Option<&dyn Drop>) {
+}
+
+// CHECK: @option_trait_borrow_mut({{i8\*|ptr}} noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
+#[no_mangle]
+pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) {
}
-// CHECK: @trait_raw({{\{\}\*|ptr}} %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
+// CHECK: @trait_raw({{\{\}\*|ptr}} noundef %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
#[no_mangle]
-pub fn trait_raw(_: *const Drop) {
+pub fn trait_raw(_: *const dyn Drop) {
}
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
-pub fn trait_box(_: Box<Drop>) {
+pub fn trait_box(_: Box<dyn Drop>) {
}
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle]
-pub fn trait_option(x: Option<Box<Drop>>) -> Option<Box<Drop>> {
+pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> {
x
}
-// CHECK: { {{\[0 x i16\]\*|ptr}}, [[USIZE]] } @return_slice({{\[0 x i16\]\*|ptr}} noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
+// CHECK: { {{\[0 x i16\]\*|ptr}}, [[USIZE]] } @return_slice({{\[0 x i16\]\*|ptr}} noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] noundef %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
#![feature(const_eval_select)]
+// compile-flags: -Copt-level=0
#![crate_type = "lib"]
#![feature(core_intrinsics)]
#[no_mangle]
pub fn mask_ptr(ptr: *const u16, mask: usize) -> *const u16 {
// CHECK: call
- // CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%0}}, [[WORD]] %mask)
+ // CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%1}}, [[WORD]] %mask)
core::intrinsics::ptr_mask(ptr, mask)
}
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
// prevent optimizing away bounds checks
// compile-flags: -O
+// ignore-debug: the debug assertions get in the way
#![crate_type="rlib"]
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
//
// only-x86_64
// ignore-windows
// in some situations, see https://github.com/rust-lang/rust/issues/96497#issuecomment-1112865218
// compile-flags: -O
-// min-llvm-version: 14.0
+// min-llvm-version: 15.0
#![crate_type="lib"]
#[no_mangle]
// CHECK-LABEL: @vec_extend_via_iter_repeat_n
pub fn vec_extend_via_iter_repeat_n() -> Vec<u8> {
- // CHECK: %[[ADDR:.+]] = tail call dereferenceable_or_null(1234) ptr @__rust_alloc(i64 1234, i64 1)
+ // CHECK: %[[ADDR:.+]] = tail call noundef dereferenceable_or_null(1234) ptr @__rust_alloc(i64 noundef 1234, i64 noundef 1)
// CHECK: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1234) %[[ADDR]], i8 42, i64 1234,
let n = 1234_usize;
// CHECK-LABEL: @load_raw_pointer
#[no_mangle]
pub fn load_raw_pointer<'a>(x: &*const i32) -> *const i32 {
- // loaded raw pointer should not have !nonnull, !align, or !noundef metadata
- // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %x, align [[PTR_ALIGNMENT]]{{$}}
+ // loaded raw pointer should not have !nonnull or !align metadata
+ // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %x, align [[PTR_ALIGNMENT]], !noundef ![[NOUNDEF:[0-9]+]]{{$}}
*x
}
// CHECK-LABEL: @load_int
#[no_mangle]
pub fn load_int(x: &u16) -> u16 {
- // CHECK: load i16, {{i16\*|ptr}} %x, align 2{{$}}
+ // CHECK: load i16, {{i16\*|ptr}} %x, align 2, !noundef ![[NOUNDEF]]{{$}}
*x
}
// CHECK-LABEL: @load_option_nonzero_int
#[no_mangle]
pub fn load_option_nonzero_int(x: &Option<NonZeroU16>) -> Option<NonZeroU16> {
- // CHECK: load i16, {{i16\*|ptr}} %x, align 2{{$}}
+ // CHECK: load i16, {{i16\*|ptr}} %x, align 2, !noundef ![[NOUNDEF]]{{$}}
*x
}
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
// needs-asm-support
// only-x86_64
#![crate_type = "lib"]
extern crate core;
+use core::cmp::Ordering;
use core::num::{NonZeroU32, NonZeroI64};
use core::ptr::NonNull;
// CHECK-NEXT: ret i1
l == r
}
+
+// CHECK-lABEL: @ordering_eq
+#[no_mangle]
+pub fn ordering_eq(l: Option<Ordering>, r: Option<Ordering>) -> bool {
+ // CHECK: start:
+ // CHECK-NEXT: icmp eq i8
+ // CHECK-NEXT: ret i1
+ l == r
+}
-// compile-flags: -C relocation-model=pic
+// compile-flags: -C relocation-model=pic -Copt-level=0
#![crate_type = "rlib"]
-// compile-flags: -C relocation-model=pie
+// compile-flags: -C relocation-model=pie -Copt-level=0
// only-x86_64-unknown-linux-gnu
#![crate_type = "rlib"]
-// compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0
+// compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0 -Copt-level=0
#![crate_type = "lib"]
#[repr(transparent)]
pub struct F32(f32);
-// CHECK: define{{.*}}float @test_F32(float %_1)
+// CHECK: define{{.*}}float @test_F32(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_F32(_: F32) -> F32 { loop {} }
#[repr(transparent)]
pub struct Ptr(*mut u8);
-// CHECK: define{{.*}}{{i8\*|ptr}} @test_Ptr({{i8\*|ptr}} %_1)
+// CHECK: define{{.*}}{{i8\*|ptr}} @test_Ptr({{i8\*|ptr}} noundef %_1)
#[no_mangle]
pub extern "C" fn test_Ptr(_: Ptr) -> Ptr { loop {} }
#[repr(transparent)]
pub struct WithZst(u64, Zst1);
-// CHECK: define{{.*}}i64 @test_WithZst(i64 %_1)
+// CHECK: define{{.*}}i64 @test_WithZst(i64 noundef %_1)
#[no_mangle]
pub extern "C" fn test_WithZst(_: WithZst) -> WithZst { loop {} }
pub struct WithZeroSizedArray(*const f32, [i8; 0]);
// Apparently we use i32* when newtype-unwrapping f32 pointers. Whatever.
-// CHECK: define{{.*}}{{i32\*|ptr}} @test_WithZeroSizedArray({{i32\*|ptr}} %_1)
+// CHECK: define{{.*}}{{i32\*|ptr}} @test_WithZeroSizedArray({{i32\*|ptr}} noundef %_1)
#[no_mangle]
pub extern "C" fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { loop {} }
#[repr(transparent)]
pub struct Generic<T>(T);
-// CHECK: define{{.*}}double @test_Generic(double %_1)
+// CHECK: define{{.*}}double @test_Generic(double noundef %_1)
#[no_mangle]
pub extern "C" fn test_Generic(_: Generic<f64>) -> Generic<f64> { loop {} }
#[repr(transparent)]
pub struct LifetimePhantom<'a, T: 'a>(*const T, PhantomData<&'a T>);
-// CHECK: define{{.*}}{{i16\*|ptr}} @test_LifetimePhantom({{i16\*|ptr}} %_1)
+// CHECK: define{{.*}}{{i16\*|ptr}} @test_LifetimePhantom({{i16\*|ptr}} noundef %_1)
#[no_mangle]
pub extern "C" fn test_LifetimePhantom(_: LifetimePhantom<i16>) -> LifetimePhantom<i16> { loop {} }
pub struct Px;
-// CHECK: define{{.*}}float @test_UnitPhantom(float %_1)
+// CHECK: define{{.*}}float @test_UnitPhantom(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_UnitPhantom(_: UnitPhantom<f32, Px>) -> UnitPhantom<f32, Px> { loop {} }
#[repr(transparent)]
pub struct TwoZsts(Zst1, i8, Zst2);
-// CHECK: define{{( dso_local)?}}{{( signext)?}} i8 @test_TwoZsts(i8{{( signext)?}} %_1)
+// CHECK: define{{( dso_local)?}} noundef{{( signext)?}} i8 @test_TwoZsts(i8 noundef{{( signext)?}} %_1)
#[no_mangle]
pub extern "C" fn test_TwoZsts(_: TwoZsts) -> TwoZsts { loop {} }
#[repr(transparent)]
pub struct Nested1(Zst2, Generic<f64>);
-// CHECK: define{{.*}}double @test_Nested1(double %_1)
+// CHECK: define{{.*}}double @test_Nested1(double noundef %_1)
#[no_mangle]
pub extern "C" fn test_Nested1(_: Nested1) -> Nested1 { loop {} }
#[repr(transparent)]
pub struct Nested2(Nested1, Zst1);
-// CHECK: define{{.*}}double @test_Nested2(double %_1)
+// CHECK: define{{.*}}double @test_Nested2(double noundef %_1)
#[no_mangle]
pub extern "C" fn test_Nested2(_: Nested2) -> Nested2 { loop {} }
#[repr(transparent)]
pub struct StructWithProjection(<f32 as Mirror>::It);
-// CHECK: define{{.*}}float @test_Projection(float %_1)
+// CHECK: define{{.*}}float @test_Projection(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_Projection(_: StructWithProjection) -> StructWithProjection { loop {} }
Variant(F32)
}
-// CHECK: define{{.*}}float @test_EnumF32(float %_1)
+// CHECK: define{{.*}}float @test_EnumF32(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_EnumF32(_: EnumF32) -> EnumF32 { loop {} }
Variant(Zst1, F32, Zst2)
}
-// CHECK: define{{.*}}float @test_EnumF32WithZsts(float %_1)
+// CHECK: define{{.*}}float @test_EnumF32WithZsts(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_EnumF32WithZsts(_: EnumF32WithZsts) -> EnumF32WithZsts { loop {} }
field: F32,
}
-// CHECK: define{{.*}}float @test_UnionF32(float %_1)
+// CHECK: define{{.*}} float @test_UnionF32(float %_1)
#[no_mangle]
pub extern "C" fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} }
a
}
-// CHECK: define signext i8 @f_scalar_1(i8 signext %x)
+// CHECK: define noundef signext i8 @f_scalar_1(i8 noundef signext %x)
#[no_mangle]
pub extern "C" fn f_scalar_1(x: i8) -> i8 {
x
}
-// CHECK: define zeroext i8 @f_scalar_2(i8 zeroext %x)
+// CHECK: define noundef zeroext i8 @f_scalar_2(i8 noundef zeroext %x)
#[no_mangle]
pub extern "C" fn f_scalar_2(x: u8) -> u8 {
x
}
-// CHECK: define signext i32 @f_scalar_3(i32 signext %x)
+// CHECK: define noundef signext i32 @f_scalar_3(i32 noundef signext %x)
#[no_mangle]
pub extern "C" fn f_scalar_3(x: i32) -> u32 {
x as u32
}
-// CHECK: define i64 @f_scalar_4(i64 %x)
+// CHECK: define noundef i64 @f_scalar_4(i64 noundef %x)
#[no_mangle]
pub extern "C" fn f_scalar_4(x: i64) -> i64 {
x
pub extern "C" fn f_agg_large(mut x: Large) {
}
-// CHECK: define void @f_agg_large_ret({{%Large\*|ptr}} {{.*}}sret{{.*}}, i32 signext %i, i8 signext %j)
+// CHECK: define void @f_agg_large_ret({{%Large\*|ptr}} {{.*}}sret{{.*}}, i32 noundef signext %i, i8 noundef signext %j)
#[no_mangle]
pub extern "C" fn f_agg_large_ret(i: i32, j: i8) -> Large {
Large { a: 1, b: 2, c: 3, d: 4 }
}
-// CHECK: define void @f_scalar_stack_1(i64 %0, [2 x i64] %1, i128 %2, {{%Large\*|ptr}} {{.*}}%d, i8 zeroext %e, i8 signext %f, i8 %g, i8 %h)
+// CHECK: define void @f_scalar_stack_1(i64 %0, [2 x i64] %1, i128 %2, {{%Large\*|ptr}} {{.*}}%d, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef %g, i8 noundef %h)
#[no_mangle]
pub extern "C" fn f_scalar_stack_1(
a: Tiny,
) {
}
-// CHECK: define void @f_scalar_stack_2({{%Large\*|ptr}} {{.*}}sret{{.*}} %0, i64 %a, i128 %1, i128 %2, i64 %d, i8 zeroext %e, i8 %f, i8 %g)
+// CHECK: define void @f_scalar_stack_2({{%Large\*|ptr}} {{.*}}sret{{.*}} %0, i64 noundef %a, i128 %1, i128 %2, i64 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g)
#[no_mangle]
pub extern "C" fn f_scalar_stack_2(
a: u64,
#[no_mangle]
pub unsafe extern "C" fn f_va_caller() {
- // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 signext 1, i32 signext 2, i64 3, double {{.*}}, double {{.*}}, i64 {{.*}}, [2 x i64] {{.*}}, i128 {{.*}}, {{%Large\*|ptr}} {{.*}})
+ // CHECK: call noundef signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i64 noundef 3, double {{.*}}, double {{.*}}, i64 {{.*}}, [2 x i64] {{.*}}, i128 {{.*}}, {{%Large\*|ptr}} {{.*}})
f_va_callee(
1,
2i32,
SmallAligned { a: 11 },
Large { a: 12, b: 13, c: 14, d: 15 },
);
- // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 signext 1, i32 signext 2, i32 signext 3, i32 signext 4, i128 {{.*}}, i32 signext 6, i32 signext 7, i32 8, i32 9)
+ // CHECK: call noundef signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i128 {{.*}}, i32 noundef signext 6, i32 noundef signext 7, i32 noundef 8, i32 noundef 9)
f_va_callee(1, 2i32, 3i32, 4i32, SmallAligned { a: 5 }, 6i32, 7i32, 8i32, 9i32);
}
// Verifies that pointer type membership tests for indirect calls are emitted.
//
// needs-sanitizer-cfi
-// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
+// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0
#![crate_type="lib"]
// [aarch64] needs-llvm-components: aarch64
// [x86_64] compile-flags: --target x86_64-unknown-none
// [x86_64] needs-llvm-components:
-// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi
+// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
#![crate_type="lib"]
#![feature(no_core, lang_items)]
// revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO
// no-prefer-dynamic
//
-//[ASAN] compile-flags: -Zsanitizer=address
-//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address
+//[ASAN] compile-flags: -Zsanitizer=address -Copt-level=0
+//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address -Copt-level=0
//[MSAN] compile-flags: -Zsanitizer=memory
//[MSAN-RECOVER] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory
//[MSAN-RECOVER-LTO] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory -C lto=fat
// ASAN-RECOVER-NOT: unreachable
// ASAN: }
//
-// MSAN-LABEL: define dso_local i32 @penguin(
+// MSAN-LABEL: define dso_local noundef i32 @penguin(
// MSAN: call void @__msan_warning{{(_with_origin_noreturn\(i32 0\)|_noreturn\(\))}}
// MSAN: unreachable
// MSAN: }
//
-// MSAN-RECOVER-LABEL: define dso_local i32 @penguin(
+// MSAN-RECOVER-LABEL: define dso_local noundef i32 @penguin(
// MSAN-RECOVER: call void @__msan_warning{{(_with_origin\(i32 0\)|\(\))}}
// MSAN-RECOVER-NOT: unreachable
// MSAN-RECOVER: }
//
-// MSAN-RECOVER-LTO-LABEL: define dso_local i32 @penguin(
+// MSAN-RECOVER-LTO-LABEL: define dso_local noundef i32 @penguin(
// MSAN-RECOVER-LTO: call void @__msan_warning{{(_with_origin\(i32 0\)|\(\))}}
// MSAN-RECOVER-LTO-NOT: unreachable
// MSAN-RECOVER-LTO: }
pair
}
-// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 %pair.1)
+// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 noundef %pair.1)
#[no_mangle]
pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) {
pair
}
-// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 %pair.0, i1 noundef zeroext %pair.1)
+// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 noundef %pair.0, i1 noundef zeroext %pair.1)
#[no_mangle]
pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) {
pair
-// compile-flags: -Cno-prepopulate-passes
+// compile-flags: -Cno-prepopulate-passes -Copt-level=0
// revisions:x86_64 i686 aarch64-apple aarch64-windows aarch64-linux arm riscv
// it to be marked `dso_local` as well, given the static relocation model.
//
// CHECK: @extern_static = external dso_local local_unnamed_addr global i8
-// CHECK: define dso_local i8 @access_extern() {{.*}}
-// CHECK: declare dso_local i8 @extern_fn() {{.*}}
+// CHECK: define dso_local noundef i8 @access_extern() {{.*}}
+// CHECK: declare dso_local noundef i8 @extern_fn() {{.*}}
#[no_mangle]
pub fn access_extern() -> u8 {
// FIXME(eddyb) all of these tests show memory stores and loads, even after a
// scalar `bitcast`, more special-casing is required to remove `alloca` usage.
-// CHECK-LABEL: define{{.*}}i32 @f32_to_bits(float %x)
+// CHECK-LABEL: define{{.*}}i32 @f32_to_bits(float noundef %x)
// CHECK: store i32 %{{.*}}, {{.*}} %0
// CHECK-NEXT: %[[RES:.*]] = load i32, {{.*}} %0
// CHECK: ret i32 %[[RES]]
unsafe { std::mem::transmute(b) }
}
-// CHECK-LABEL: define{{.*}}noundef zeroext i1 @byte_to_bool(i8 %byte)
+// CHECK-LABEL: define{{.*}}noundef zeroext i1 @byte_to_bool(i8 noundef %byte)
// CHECK: %1 = trunc i8 %byte to i1
// CHECK-NEXT: %2 = zext i1 %1 to i8
// CHECK-NEXT: store i8 %2, {{.*}} %0
std::mem::transmute(byte)
}
-// CHECK-LABEL: define{{.*}}{{i8\*|ptr}} @ptr_to_ptr({{i16\*|ptr}} %p)
+// CHECK-LABEL: define{{.*}}{{i8\*|ptr}} @ptr_to_ptr({{i16\*|ptr}} noundef %p)
// CHECK: store {{i8\*|ptr}} %{{.*}}, {{.*}} %0
// CHECK-NEXT: %[[RES:.*]] = load {{i8\*|ptr}}, {{.*}} %0
// CHECK: ret {{i8\*|ptr}} %[[RES]]
// Tests below show the non-special-cased behavior (with the possible
// future special-cased instructions in the "NOTE(eddyb)" comments).
-// CHECK: define{{.*}}[[USIZE:i[0-9]+]] @ptr_to_int({{i16\*|ptr}} %p)
+// CHECK: define{{.*}}[[USIZE:i[0-9]+]] @ptr_to_int({{i16\*|ptr}} noundef %p)
// NOTE(eddyb) see above, the following two CHECK lines should ideally be this:
// %2 = ptrtoint i16* %p to [[USIZE]]
unsafe { std::mem::transmute(p) }
}
-// CHECK: define{{.*}}{{i16\*|ptr}} @int_to_ptr([[USIZE]] %i)
+// CHECK: define{{.*}}{{i16\*|ptr}} @int_to_ptr([[USIZE]] noundef %i)
// NOTE(eddyb) see above, the following two CHECK lines should ideally be this:
// %2 = inttoptr [[USIZE]] %i to i16*
// ignore-emscripten
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
// Test that tuples get optimized layout, in particular with a ZST in the last field (#63244)
#![crate_type = "lib"]
-// CHECK-LABEL: define{{.*}}i32 @test(i32 %a, i32 %b)
+// CHECK-LABEL: define{{.*}}i32 @test(i32 noundef %a, i32 noundef %b)
#[no_mangle]
pub fn test(a: u32, b: u32) -> u32 {
let c = a + b;
vec![Some(false); n]
}
+// CHECK-LABEL: @vec_option_i32
+#[no_mangle]
+pub fn vec_option_i32(n: usize) -> Vec<Option<i32>> {
+ // CHECK-NOT: call {{.*}}alloc::vec::from_elem
+ // CHECK-NOT: call {{.*}}reserve
+ // CHECK-NOT: call {{.*}}__rust_alloc(
+
+ // CHECK: call {{.*}}__rust_alloc_zeroed(
+
+ // CHECK-NOT: call {{.*}}alloc::vec::from_elem
+ // CHECK-NOT: call {{.*}}reserve
+ // CHECK-NOT: call {{.*}}__rust_alloc(
+
+ // CHECK: ret void
+ vec![None; n]
+}
+
// Ensure that __rust_alloc_zeroed gets the right attributes for LLVM to optimize it away.
-// CHECK: declare noalias ptr @__rust_alloc_zeroed(i64, i64 allocalign) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]]
+// CHECK: declare noalias noundef ptr @__rust_alloc_zeroed(i64 noundef, i64 allocalign noundef) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]]
// CHECK-DAG: attributes [[RUST_ALLOC_ZEROED_ATTRS]] = { {{.*}} allockind("alloc,zeroed,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} }
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
#![feature(repr_simd)]
// compile-flags:-g
// min-gdb-version: 8.1
// ignore-lldb
+// ignore-windows-gnu // emit_debug_gdb_scripts is disabled on Windows
// === CDB TESTS ==================================================================================
// compile-flags:-g
// min-gdb-version: 8.1
+// ignore-windows-gnu // emit_debug_gdb_scripts is disabled on Windows
// Tests the visualizations for `NonZero{I,U}{8,16,32,64,128,size}`, `Wrapping<T>` and
// `Atomic{Bool,I8,I16,I32,I64,Isize,U8,U16,U32,U64,Usize}` located in `libcore.natvis`.
-// ignore-windows
// min-lldb-version: 310
// compile-flags:-g
+++ /dev/null
-- // MIR for `encode` before SimplifyBranchSame
-+ // MIR for `encode` after SimplifyBranchSame
-
- fn encode(_1: Type) -> Type {
- debug v => _1; // in scope 0 at $DIR/76803_regression.rs:+0:15: +0:16
- let mut _0: Type; // return place in scope 0 at $DIR/76803_regression.rs:+0:27: +0:31
- let mut _2: isize; // in scope 0 at $DIR/76803_regression.rs:+2:9: +2:16
-
- bb0: {
- _2 = discriminant(_1); // scope 0 at $DIR/76803_regression.rs:+1:11: +1:12
- switchInt(move _2) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/76803_regression.rs:+1:5: +1:12
- }
-
- bb1: {
- _0 = move _1; // scope 0 at $DIR/76803_regression.rs:+3:14: +3:15
- goto -> bb3; // scope 0 at $DIR/76803_regression.rs:+3:14: +3:15
- }
-
- bb2: {
- Deinit(_0); // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27
- discriminant(_0) = 1; // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27
- goto -> bb3; // scope 0 at $DIR/76803_regression.rs:+2:20: +2:27
- }
-
- bb3: {
- return; // scope 0 at $DIR/76803_regression.rs:+5:2: +5:2
- }
- }
-
+++ /dev/null
-// compile-flags: -Z mir-opt-level=1
-// EMIT_MIR 76803_regression.encode.SimplifyBranchSame.diff
-
-#[derive(Debug, Eq, PartialEq)]
-pub enum Type {
- A,
- B,
-}
-
-pub fn encode(v: Type) -> Type {
- match v {
- Type::A => Type::B,
- _ => v,
- }
-}
-
-fn main() {
- assert_eq!(Type::B, encode(Type::A));
-}
--- /dev/null
+// MIR for `a::{closure#0}` 0 generator_resume
+/* generator_layout = GeneratorLayout {
+ field_tys: {},
+ variant_fields: {
+ Unresumed(0): [],
+ Returned (1): [],
+ Panicked (2): [],
+ },
+ storage_conflicts: BitMatrix(0x0) {},
+} */
+
+fn a::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:11:14: 11:16]>, _2: &mut Context<'_>) -> Poll<()> {
+ debug _task_context => _4; // in scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ let mut _0: std::task::Poll<()>; // return place in scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ let mut _3: (); // in scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ let mut _4: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ let mut _5: u32; // in scope 0 at $DIR/async_await.rs:+0:14: +0:16
+
+ bb0: {
+ _5 = discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:11:14: 11:16]))); // scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ switchInt(move _5) -> [0: bb1, 1: bb2, otherwise: bb3]; // scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ }
+
+ bb1: {
+ _4 = move _2; // scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ _3 = const (); // scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ Deinit(_0); // scope 0 at $DIR/async_await.rs:+0:16: +0:16
+ ((_0 as Ready).0: ()) = move _3; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
+ discriminant(_0) = 0; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
+ discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:11:14: 11:16]))) = 1; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
+ return; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
+ }
+
+ bb2: {
+ assert(const false, "`async fn` resumed after completion") -> bb2; // scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ }
+
+ bb3: {
+ unreachable; // scope 0 at $DIR/async_await.rs:+0:14: +0:16
+ }
+}
--- /dev/null
+// MIR for `b::{closure#0}` 0 generator_resume
+/* generator_layout = GeneratorLayout {
+ field_tys: {
+ _0: GeneratorSavedTy {
+ ty: impl std::future::Future<Output = ()>,
+ source_info: SourceInfo {
+ span: $DIR/async_await.rs:15:8: 15:14 (#9),
+ scope: scope[0],
+ },
+ ignore_for_traits: false,
+ },
+ _1: GeneratorSavedTy {
+ ty: impl std::future::Future<Output = ()>,
+ source_info: SourceInfo {
+ span: $DIR/async_await.rs:16:8: 16:14 (#12),
+ scope: scope[0],
+ },
+ ignore_for_traits: false,
+ },
+ },
+ variant_fields: {
+ Unresumed(0): [],
+ Returned (1): [],
+ Panicked (2): [],
+ Suspend0 (3): [_0],
+ Suspend1 (4): [_1],
+ },
+ storage_conflicts: BitMatrix(2x2) {
+ (_0, _0),
+ (_1, _1),
+ },
+} */
+
+fn b::{closure#0}(_1: Pin<&mut [async fn body@$DIR/async_await.rs:14:18: 17:2]>, _2: &mut Context<'_>) -> Poll<()> {
+ debug _task_context => _38; // in scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ let mut _0: std::task::Poll<()>; // return place in scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ let _3: (); // in scope 0 at $DIR/async_await.rs:+1:5: +1:14
+ let mut _4: impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _5: impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+1:5: +1:8
+ let mut _6: impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _7: (); // in scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ let _8: (); // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _9: std::task::Poll<()>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _10: std::pin::Pin<&mut impl std::future::Future<Output = ()>>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _11: &mut impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _12: &mut impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _13: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+1:5: +1:14
+ let mut _14: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+1:5: +1:14
+ let mut _15: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _16: isize; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _18: !; // in scope 0 at $DIR/async_await.rs:+1:5: +1:14
+ let mut _19: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _20: (); // in scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ let mut _21: impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _22: impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+2:5: +2:8
+ let mut _23: impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let _24: (); // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _25: std::task::Poll<()>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _26: std::pin::Pin<&mut impl std::future::Future<Output = ()>>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _27: &mut impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _28: &mut impl std::future::Future<Output = ()>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _29: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+2:5: +2:14
+ let mut _30: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+2:5: +2:14
+ let mut _31: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _32: isize; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _34: !; // in scope 0 at $DIR/async_await.rs:+2:5: +2:14
+ let mut _35: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _36: (); // in scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ let mut _37: (); // in scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ let mut _38: &mut std::task::Context<'_>; // in scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ let mut _39: u32; // in scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ scope 1 {
+ debug __awaitee => (((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2])) as variant#3).0: impl std::future::Future<Output = ()>); // in scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ let _17: (); // in scope 1 at $DIR/async_await.rs:+1:5: +1:14
+ scope 2 {
+ }
+ scope 3 {
+ debug result => _17; // in scope 3 at $DIR/async_await.rs:+1:5: +1:14
+ }
+ }
+ scope 4 {
+ debug __awaitee => (((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2])) as variant#4).0: impl std::future::Future<Output = ()>); // in scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ let _33: (); // in scope 4 at $DIR/async_await.rs:+2:5: +2:14
+ scope 5 {
+ }
+ scope 6 {
+ debug result => _33; // in scope 6 at $DIR/async_await.rs:+2:5: +2:14
+ }
+ }
+
+ bb0: {
+ _39 = discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ switchInt(move _39) -> [0: bb1, 1: bb29, 3: bb27, 4: bb28, otherwise: bb30]; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ }
+
+ bb1: {
+ _38 = move _2; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ StorageLive(_3); // scope 0 at $DIR/async_await.rs:+1:5: +1:14
+ StorageLive(_4); // scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ StorageLive(_5); // scope 0 at $DIR/async_await.rs:+1:5: +1:8
+ _5 = a() -> bb2; // scope 0 at $DIR/async_await.rs:+1:5: +1:8
+ // mir::Constant
+ // + span: $DIR/async_await.rs:15:5: 15:6
+ // + literal: Const { ty: fn() -> impl Future<Output = ()> {a}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ _4 = <impl Future<Output = ()> as IntoFuture>::into_future(move _5) -> bb3; // scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ // mir::Constant
+ // + span: $DIR/async_await.rs:15:8: 15:14
+ // + literal: Const { ty: fn(impl Future<Output = ()>) -> <impl Future<Output = ()> as IntoFuture>::IntoFuture {<impl Future<Output = ()> as IntoFuture>::into_future}, val: Value(<ZST>) }
+ }
+
+ bb3: {
+ StorageDead(_5); // scope 0 at $DIR/async_await.rs:+1:13: +1:14
+ nop; // scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ (((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2])) as variant#3).0: impl std::future::Future<Output = ()>) = move _4; // scope 0 at $DIR/async_await.rs:+1:8: +1:14
+ goto -> bb4; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ }
+
+ bb4: {
+ StorageLive(_8); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ StorageLive(_9); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ StorageLive(_10); // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ StorageLive(_11); // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ StorageLive(_12); // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ _12 = &mut (((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2])) as variant#3).0: impl std::future::Future<Output = ()>); // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ _11 = &mut (*_12); // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ _10 = Pin::<&mut impl Future<Output = ()>>::new_unchecked(move _11) -> bb5; // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ // mir::Constant
+ // + span: $DIR/async_await.rs:15:8: 15:14
+ // + literal: Const { ty: unsafe fn(&mut impl Future<Output = ()>) -> Pin<&mut impl Future<Output = ()>> {Pin::<&mut impl Future<Output = ()>>::new_unchecked}, val: Value(<ZST>) }
+ }
+
+ bb5: {
+ StorageDead(_11); // scope 2 at $DIR/async_await.rs:+1:13: +1:14
+ StorageLive(_13); // scope 2 at $DIR/async_await.rs:+1:5: +1:14
+ StorageLive(_14); // scope 2 at $DIR/async_await.rs:+1:5: +1:14
+ StorageLive(_15); // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ _15 = _38; // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ _14 = move _15; // scope 2 at $DIR/async_await.rs:+1:5: +1:14
+ goto -> bb6; // scope 2 at $DIR/async_await.rs:+1:5: +1:14
+ }
+
+ bb6: {
+ _13 = &mut (*_14); // scope 2 at $DIR/async_await.rs:+1:5: +1:14
+ StorageDead(_15); // scope 2 at $DIR/async_await.rs:+1:13: +1:14
+ _9 = <impl Future<Output = ()> as Future>::poll(move _10, move _13) -> bb7; // scope 2 at $DIR/async_await.rs:+1:8: +1:14
+ // mir::Constant
+ // + span: $DIR/async_await.rs:15:8: 15:14
+ // + literal: Const { ty: for<'a, 'b, 'c> fn(Pin<&'a mut impl Future<Output = ()>>, &'b mut Context<'c>) -> Poll<<impl Future<Output = ()> as Future>::Output> {<impl Future<Output = ()> as Future>::poll}, val: Value(<ZST>) }
+ }
+
+ bb7: {
+ StorageDead(_13); // scope 2 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_10); // scope 2 at $DIR/async_await.rs:+1:13: +1:14
+ _16 = discriminant(_9); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ switchInt(move _16) -> [0: bb10, 1: bb8, otherwise: bb9]; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ }
+
+ bb8: {
+ _8 = const (); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ StorageDead(_14); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_12); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_9); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_8); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageLive(_19); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ StorageLive(_20); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ _20 = (); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ Deinit(_0); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ discriminant(_0) = 1; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 3; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ return; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ }
+
+ bb9: {
+ unreachable; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ }
+
+ bb10: {
+ StorageLive(_17); // scope 1 at $DIR/async_await.rs:+1:5: +1:14
+ _17 = ((_9 as Ready).0: ()); // scope 1 at $DIR/async_await.rs:+1:5: +1:14
+ _3 = _17; // scope 3 at $DIR/async_await.rs:+1:5: +1:14
+ StorageDead(_17); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_14); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_12); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_9); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ StorageDead(_8); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ goto -> bb12; // scope 0 at $DIR/async_await.rs:+1:13: +1:14
+ }
+
+ bb11: {
+ StorageDead(_20); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ _38 = move _19; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ StorageDead(_19); // scope 1 at $DIR/async_await.rs:+1:13: +1:14
+ _7 = const (); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ goto -> bb4; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ }
+
+ bb12: {
+ nop; // scope 0 at $DIR/async_await.rs:+1:13: +1:14
+ goto -> bb13; // scope 0 at $DIR/async_await.rs:+1:14: +1:15
+ }
+
+ bb13: {
+ StorageDead(_4); // scope 0 at $DIR/async_await.rs:+1:14: +1:15
+ StorageDead(_3); // scope 0 at $DIR/async_await.rs:+1:14: +1:15
+ StorageLive(_21); // scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ StorageLive(_22); // scope 0 at $DIR/async_await.rs:+2:5: +2:8
+ _22 = a() -> bb14; // scope 0 at $DIR/async_await.rs:+2:5: +2:8
+ // mir::Constant
+ // + span: $DIR/async_await.rs:16:5: 16:6
+ // + literal: Const { ty: fn() -> impl Future<Output = ()> {a}, val: Value(<ZST>) }
+ }
+
+ bb14: {
+ _21 = <impl Future<Output = ()> as IntoFuture>::into_future(move _22) -> bb15; // scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ // mir::Constant
+ // + span: $DIR/async_await.rs:16:8: 16:14
+ // + literal: Const { ty: fn(impl Future<Output = ()>) -> <impl Future<Output = ()> as IntoFuture>::IntoFuture {<impl Future<Output = ()> as IntoFuture>::into_future}, val: Value(<ZST>) }
+ }
+
+ bb15: {
+ StorageDead(_22); // scope 0 at $DIR/async_await.rs:+2:13: +2:14
+ nop; // scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ (((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2])) as variant#4).0: impl std::future::Future<Output = ()>) = move _21; // scope 0 at $DIR/async_await.rs:+2:8: +2:14
+ goto -> bb16; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ }
+
+ bb16: {
+ StorageLive(_24); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ StorageLive(_25); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ StorageLive(_26); // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ StorageLive(_27); // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ StorageLive(_28); // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ _28 = &mut (((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2])) as variant#4).0: impl std::future::Future<Output = ()>); // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ _27 = &mut (*_28); // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ _26 = Pin::<&mut impl Future<Output = ()>>::new_unchecked(move _27) -> bb17; // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ // mir::Constant
+ // + span: $DIR/async_await.rs:16:8: 16:14
+ // + literal: Const { ty: unsafe fn(&mut impl Future<Output = ()>) -> Pin<&mut impl Future<Output = ()>> {Pin::<&mut impl Future<Output = ()>>::new_unchecked}, val: Value(<ZST>) }
+ }
+
+ bb17: {
+ StorageDead(_27); // scope 5 at $DIR/async_await.rs:+2:13: +2:14
+ StorageLive(_29); // scope 5 at $DIR/async_await.rs:+2:5: +2:14
+ StorageLive(_30); // scope 5 at $DIR/async_await.rs:+2:5: +2:14
+ StorageLive(_31); // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ _31 = _38; // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ _30 = move _31; // scope 5 at $DIR/async_await.rs:+2:5: +2:14
+ goto -> bb18; // scope 5 at $DIR/async_await.rs:+2:5: +2:14
+ }
+
+ bb18: {
+ _29 = &mut (*_30); // scope 5 at $DIR/async_await.rs:+2:5: +2:14
+ StorageDead(_31); // scope 5 at $DIR/async_await.rs:+2:13: +2:14
+ _25 = <impl Future<Output = ()> as Future>::poll(move _26, move _29) -> bb19; // scope 5 at $DIR/async_await.rs:+2:8: +2:14
+ // mir::Constant
+ // + span: $DIR/async_await.rs:16:8: 16:14
+ // + literal: Const { ty: for<'a, 'b, 'c> fn(Pin<&'a mut impl Future<Output = ()>>, &'b mut Context<'c>) -> Poll<<impl Future<Output = ()> as Future>::Output> {<impl Future<Output = ()> as Future>::poll}, val: Value(<ZST>) }
+ }
+
+ bb19: {
+ StorageDead(_29); // scope 5 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_26); // scope 5 at $DIR/async_await.rs:+2:13: +2:14
+ _32 = discriminant(_25); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ switchInt(move _32) -> [0: bb22, 1: bb20, otherwise: bb21]; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ }
+
+ bb20: {
+ _24 = const (); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ StorageDead(_30); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_28); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_25); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_24); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageLive(_35); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ StorageLive(_36); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ _36 = (); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ Deinit(_0); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ discriminant(_0) = 1; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 4; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ return; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ }
+
+ bb21: {
+ unreachable; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ }
+
+ bb22: {
+ StorageLive(_33); // scope 4 at $DIR/async_await.rs:+2:5: +2:14
+ _33 = ((_25 as Ready).0: ()); // scope 4 at $DIR/async_await.rs:+2:5: +2:14
+ _37 = _33; // scope 6 at $DIR/async_await.rs:+2:5: +2:14
+ StorageDead(_33); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_30); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_28); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_25); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ StorageDead(_24); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ goto -> bb24; // scope 0 at $DIR/async_await.rs:+2:13: +2:14
+ }
+
+ bb23: {
+ StorageDead(_36); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ _38 = move _35; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ StorageDead(_35); // scope 4 at $DIR/async_await.rs:+2:13: +2:14
+ _7 = const (); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ goto -> bb16; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ }
+
+ bb24: {
+ nop; // scope 0 at $DIR/async_await.rs:+2:13: +2:14
+ goto -> bb25; // scope 0 at $DIR/async_await.rs:+3:1: +3:2
+ }
+
+ bb25: {
+ StorageDead(_21); // scope 0 at $DIR/async_await.rs:+3:1: +3:2
+ goto -> bb26; // scope 0 at $DIR/async_await.rs:+3:1: +3:2
+ }
+
+ bb26: {
+ Deinit(_0); // scope 0 at $DIR/async_await.rs:+3:2: +3:2
+ ((_0 as Ready).0: ()) = move _37; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
+ discriminant(_0) = 0; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
+ discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 1; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
+ return; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
+ }
+
+ bb27: {
+ StorageLive(_3); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ StorageLive(_4); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ StorageLive(_19); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ StorageLive(_20); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ _19 = move _2; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ goto -> bb11; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ }
+
+ bb28: {
+ StorageLive(_21); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ StorageLive(_35); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ StorageLive(_36); // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ _35 = move _2; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ goto -> bb23; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ }
+
+ bb29: {
+ assert(const false, "`async fn` resumed after completion") -> bb29; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ }
+
+ bb30: {
+ unreachable; // scope 0 at $DIR/async_await.rs:+0:18: +3:2
+ }
+}
--- /dev/null
+// This test makes sure that the generator MIR pass eliminates all calls to
+// `get_context`, and that the MIR argument type for an async fn and all locals
+// related to `yield` are `&mut Context`, and its return type is `Poll`.
+
+// edition:2018
+// compile-flags: -C panic=abort
+
+#![crate_type = "lib"]
+
+// EMIT_MIR async_await.a-{closure#0}.generator_resume.0.mir
+async fn a() {}
+
+// EMIT_MIR async_await.b-{closure#0}.generator_resume.0.mir
+pub async fn b() {
+ a().await;
+ a().await
+}
--- /dev/null
+// MIR for `arrays` after built
+
+fn arrays() -> usize {
+ let mut _0: usize; // return place in scope 0 at $DIR/arrays.rs:+0:32: +0:37
+ let mut _1: [i32; C]; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _2: usize; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _1 = [const 5_i32; C]; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _2 = Len(_1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _0 = _2; // scope 0 at $DIR/arrays.rs:+4:9: +4:16
+ return; // scope 0 at $DIR/arrays.rs:+5:9: +5:17
+ }
+}
--- /dev/null
+#![feature(custom_mir, core_intrinsics, inline_const)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+// EMIT_MIR arrays.arrays.built.after.mir
+#[custom_mir(dialect = "built")]
+fn arrays<const C: usize>() -> usize {
+ mir!({
+ let x = [5_i32; C];
+ let c = Len(x);
+ RET = c;
+ Return()
+ })
+}
+
+fn main() {
+ assert_eq!(arrays::<20>(), 20);
+}
})
}
-static S: i32 = 5;
-static mut T: i32 = 10;
+static S: i32 = 0x05050505;
+static mut T: i32 = 0x0a0a0a0a;
// EMIT_MIR consts.statics.built.after.mir
#[custom_mir(dialect = "built")]
fn statics() {
}
alloc2 (static: T, size: 4, align: 4) {
- 0a 00 00 00 │ ....
+ 0a 0a 0a 0a │ ....
}
alloc1 (static: S, size: 4, align: 4) {
- 05 00 00 00 │ ....
+ 05 05 05 05 │ ....
}
#[custom_mir(dialect = "runtime", phase = "initial")]
fn set_discr(option: &mut Option<()>) {
mir!({
+ Deinit(*option);
SetDiscriminant(*option, 0);
Return()
})
let mut _0: (); // return place in scope 0 at $DIR/enums.rs:+0:39: +0:39
bb0: {
- discriminant((*_1)) = 0; // scope 0 at $DIR/enums.rs:+2:9: +2:36
- return; // scope 0 at $DIR/enums.rs:+3:9: +3:17
+ Deinit((*_1)); // scope 0 at $DIR/enums.rs:+2:9: +2:24
+ discriminant((*_1)) = 0; // scope 0 at $DIR/enums.rs:+3:9: +3:36
+ return; // scope 0 at $DIR/enums.rs:+4:9: +4:17
}
}
--- /dev/null
+// MIR for `f` after built
+
+fn f(_1: i32, _2: bool) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/operators.rs:+0:30: +0:33
+ let mut _3: (i32, bool); // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _1 = Neg(_1); // scope 0 at $DIR/operators.rs:+2:9: +2:15
+ _2 = Not(_2); // scope 0 at $DIR/operators.rs:+3:9: +3:15
+ _1 = Add(_1, _1); // scope 0 at $DIR/operators.rs:+4:9: +4:18
+ _1 = Sub(_1, _1); // scope 0 at $DIR/operators.rs:+5:9: +5:18
+ _1 = Mul(_1, _1); // scope 0 at $DIR/operators.rs:+6:9: +6:18
+ _1 = Div(_1, _1); // scope 0 at $DIR/operators.rs:+7:9: +7:18
+ _1 = Rem(_1, _1); // scope 0 at $DIR/operators.rs:+8:9: +8:18
+ _1 = BitXor(_1, _1); // scope 0 at $DIR/operators.rs:+9:9: +9:18
+ _1 = BitAnd(_1, _1); // scope 0 at $DIR/operators.rs:+10:9: +10:18
+ _1 = Shl(_1, _1); // scope 0 at $DIR/operators.rs:+11:9: +11:19
+ _1 = Shr(_1, _1); // scope 0 at $DIR/operators.rs:+12:9: +12:19
+ _2 = Eq(_1, _1); // scope 0 at $DIR/operators.rs:+13:9: +13:19
+ _2 = Lt(_1, _1); // scope 0 at $DIR/operators.rs:+14:9: +14:18
+ _2 = Le(_1, _1); // scope 0 at $DIR/operators.rs:+15:9: +15:19
+ _2 = Ge(_1, _1); // scope 0 at $DIR/operators.rs:+16:9: +16:19
+ _2 = Gt(_1, _1); // scope 0 at $DIR/operators.rs:+17:9: +17:18
+ _3 = CheckedAdd(_1, _1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _2 = (_3.1: bool); // scope 0 at $DIR/operators.rs:+19:9: +19:18
+ _1 = (_3.0: i32); // scope 0 at $DIR/operators.rs:+20:9: +20:18
+ _0 = _1; // scope 0 at $DIR/operators.rs:+21:9: +21:16
+ return; // scope 0 at $DIR/operators.rs:+22:9: +22:17
+ }
+}
--- /dev/null
+// compile-flags: --crate-type=lib
+#![feature(custom_mir, core_intrinsics, inline_const)]
+use std::intrinsics::mir::*;
+
+// EMIT_MIR operators.f.built.after.mir
+#[custom_mir(dialect = "built")]
+pub fn f(a: i32, b: bool) -> i32 {
+ mir!({
+ a = -a;
+ b = !b;
+ a = a + a;
+ a = a - a;
+ a = a * a;
+ a = a / a;
+ a = a % a;
+ a = a ^ a;
+ a = a & a;
+ a = a << a;
+ a = a >> a;
+ b = a == a;
+ b = a < a;
+ b = a <= a;
+ b = a >= a;
+ b = a > a;
+ let res = Checked(a + a);
+ b = res.1;
+ a = res.0;
+ RET = a;
+ Return()
+ })
+}
let temp2: _;
{
+ StorageLive(temp1);
temp1 = x;
Goto(exit)
}
exit = {
temp2 = Move(temp1);
+ StorageDead(temp1);
RET = temp2;
Return()
}
let mut _3: i32; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
bb0: {
- _2 = _1; // scope 0 at $DIR/simple_assign.rs:+6:13: +6:22
- goto -> bb1; // scope 0 at $DIR/simple_assign.rs:+7:13: +7:23
+ StorageLive(_2); // scope 0 at $DIR/simple_assign.rs:+6:13: +6:31
+ _2 = _1; // scope 0 at $DIR/simple_assign.rs:+7:13: +7:22
+ goto -> bb1; // scope 0 at $DIR/simple_assign.rs:+8:13: +8:23
}
bb1: {
- _3 = move _2; // scope 0 at $DIR/simple_assign.rs:+11:13: +11:32
- _0 = _3; // scope 0 at $DIR/simple_assign.rs:+12:13: +12:24
- return; // scope 0 at $DIR/simple_assign.rs:+13:13: +13:21
+ _3 = move _2; // scope 0 at $DIR/simple_assign.rs:+12:13: +12:32
+ StorageDead(_2); // scope 0 at $DIR/simple_assign.rs:+13:13: +13:31
+ _0 = _3; // scope 0 at $DIR/simple_assign.rs:+14:13: +14:24
+ return; // scope 0 at $DIR/simple_assign.rs:+15:13: +15:21
}
}
_3 = const 3_u8; // scope 2 at $DIR/const_debuginfo.rs:+3:13: +3:16
StorageLive(_4); // scope 3 at $DIR/const_debuginfo.rs:+4:9: +4:12
StorageLive(_5); // scope 3 at $DIR/const_debuginfo.rs:+4:15: +4:20
- StorageLive(_6); // scope 3 at $DIR/const_debuginfo.rs:+4:15: +4:16
- _6 = const 1_u8; // scope 3 at $DIR/const_debuginfo.rs:+4:15: +4:16
- StorageLive(_7); // scope 3 at $DIR/const_debuginfo.rs:+4:19: +4:20
- _7 = const 2_u8; // scope 3 at $DIR/const_debuginfo.rs:+4:19: +4:20
_5 = const 3_u8; // scope 3 at $DIR/const_debuginfo.rs:+4:15: +4:20
- StorageDead(_7); // scope 3 at $DIR/const_debuginfo.rs:+4:19: +4:20
- StorageDead(_6); // scope 3 at $DIR/const_debuginfo.rs:+4:19: +4:20
- StorageLive(_8); // scope 3 at $DIR/const_debuginfo.rs:+4:23: +4:24
- _8 = const 3_u8; // scope 3 at $DIR/const_debuginfo.rs:+4:23: +4:24
_4 = const 6_u8; // scope 3 at $DIR/const_debuginfo.rs:+4:15: +4:24
- StorageDead(_8); // scope 3 at $DIR/const_debuginfo.rs:+4:23: +4:24
StorageDead(_5); // scope 3 at $DIR/const_debuginfo.rs:+4:23: +4:24
StorageLive(_9); // scope 4 at $DIR/const_debuginfo.rs:+6:9: +6:10
_9 = const "hello, world!"; // scope 4 at $DIR/const_debuginfo.rs:+6:13: +6:28
StorageDead(_16); // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_9); // scope 4 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_4); // scope 3 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_3); // scope 2 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_2); // scope 1 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_1); // scope 0 at $DIR/const_debuginfo.rs:+14:1: +14:2
return; // scope 0 at $DIR/const_debuginfo.rs:+14:2: +14:2
}
}
StorageLive(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:+1:9: +1:10
_1 = const 0_i32; // scope 0 at $DIR/bad_op_mod_by_zero.rs:+1:13: +1:14
StorageLive(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:9: +2:11
- StorageLive(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:18: +2:19
-- _3 = _1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:18: +2:19
-- _4 = Eq(_3, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
+- _4 = Eq(_1, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
- assert(!move _4, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
-+ _3 = const 0_i32; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:18: +2:19
+ _4 = const true; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
+ assert(!const true, "attempt to calculate the remainder of `{}` with a divisor of zero", const 1_i32) -> bb1; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
}
bb1: {
-- _5 = Eq(_3, const -1_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
+ _5 = Eq(_1, const -1_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
- _6 = Eq(const 1_i32, const i32::MIN); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
- _7 = BitAnd(move _5, move _6); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
-- assert(!move _7, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _3) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
-+ _5 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
+- assert(!move _7, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _1) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
+ _6 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
+ _7 = const false; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
-+ assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, const 0_i32) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
++ assert(!const false, "attempt to compute the remainder of `{} % {}`, which would overflow", const 1_i32, _1) -> bb2; // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
}
bb2: {
-- _2 = Rem(const 1_i32, move _3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
-+ _2 = Rem(const 1_i32, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
- StorageDead(_3); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:18: +2:19
+ _2 = Rem(const 1_i32, _1); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
StorageDead(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+3:1: +3:2
- StorageDead(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:+3:1: +3:2
return; // scope 0 at $DIR/bad_op_mod_by_zero.rs:+3:2: +3:2
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:9: +1:10
StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
- StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
_8 = const _; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
// mir::Constant
// + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
// + literal: Const { ty: &[i32; 3], val: Unevaluated(main, [], Some(promoted[0])) }
- _3 = _8; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
- _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
+ _2 = &raw const (*_8); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
_1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:34: +1:35
- StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:35: +1:36
StorageLive(_4); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:13: +3:15
StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
_5 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
bb0: {
StorageLive(_1); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:9: +1:10
StorageLive(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
- StorageLive(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
_8 = const _; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
// mir::Constant
// + span: $DIR/bad_op_unsafe_oob_for_slices.rs:5:25: 5:35
// + literal: Const { ty: &[i32; 3], val: Unevaluated(main, [], Some(promoted[0])) }
- _3 = _8; // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
- _2 = &raw const (*_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
+ _2 = &raw const (*_8); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
_1 = move _2 as *const [i32] (Pointer(Unsize)); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:25: +1:35
StorageDead(_2); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:34: +1:35
- StorageDead(_3); // scope 0 at $DIR/bad_op_unsafe_oob_for_slices.rs:+1:35: +1:36
StorageLive(_4); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:13: +3:15
StorageLive(_5); // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
_5 = const 3_usize; // scope 2 at $DIR/bad_op_unsafe_oob_for_slices.rs:+3:23: +3:24
bb0: {
StorageLive(_3); // scope 0 at $DIR/boolean_identities.rs:+1:5: +1:15
- StorageLive(_4); // scope 0 at $DIR/boolean_identities.rs:+1:6: +1:7
- _4 = _2; // scope 0 at $DIR/boolean_identities.rs:+1:6: +1:7
-- _3 = BitOr(move _4, const true); // scope 0 at $DIR/boolean_identities.rs:+1:5: +1:15
+- _3 = BitOr(_2, const true); // scope 0 at $DIR/boolean_identities.rs:+1:5: +1:15
+ _3 = const true; // scope 0 at $DIR/boolean_identities.rs:+1:5: +1:15
- StorageDead(_4); // scope 0 at $DIR/boolean_identities.rs:+1:14: +1:15
StorageLive(_5); // scope 0 at $DIR/boolean_identities.rs:+1:18: +1:29
- StorageLive(_6); // scope 0 at $DIR/boolean_identities.rs:+1:19: +1:20
- _6 = _1; // scope 0 at $DIR/boolean_identities.rs:+1:19: +1:20
-- _5 = BitAnd(move _6, const false); // scope 0 at $DIR/boolean_identities.rs:+1:18: +1:29
-+ _5 = const false; // scope 0 at $DIR/boolean_identities.rs:+1:18: +1:29
- StorageDead(_6); // scope 0 at $DIR/boolean_identities.rs:+1:28: +1:29
+- _5 = BitAnd(_1, const false); // scope 0 at $DIR/boolean_identities.rs:+1:18: +1:29
- _0 = BitAnd(move _3, move _5); // scope 0 at $DIR/boolean_identities.rs:+1:5: +1:29
++ _5 = const false; // scope 0 at $DIR/boolean_identities.rs:+1:18: +1:29
+ _0 = const false; // scope 0 at $DIR/boolean_identities.rs:+1:5: +1:29
StorageDead(_5); // scope 0 at $DIR/boolean_identities.rs:+1:28: +1:29
StorageDead(_3); // scope 0 at $DIR/boolean_identities.rs:+1:28: +1:29
let mut _2: i32; // in scope 0 at $DIR/mult_by_zero.rs:+1:3: +1:4
bb0: {
- StorageLive(_2); // scope 0 at $DIR/mult_by_zero.rs:+1:3: +1:4
- _2 = _1; // scope 0 at $DIR/mult_by_zero.rs:+1:3: +1:4
-- _0 = Mul(move _2, const 0_i32); // scope 0 at $DIR/mult_by_zero.rs:+1:3: +1:8
+- _0 = Mul(_1, const 0_i32); // scope 0 at $DIR/mult_by_zero.rs:+1:3: +1:8
+ _0 = const 0_i32; // scope 0 at $DIR/mult_by_zero.rs:+1:3: +1:8
- StorageDead(_2); // scope 0 at $DIR/mult_by_zero.rs:+1:7: +1:8
return; // scope 0 at $DIR/mult_by_zero.rs:+2:2: +2:2
}
}
}
alloc1 (static: STATIC, size: 4, align: 4) {
- 2a 00 00 00 │ *...
+ 42 42 42 42 │ BBBB
}
// unit-test
// compile-flags: -O
-static mut STATIC: u32 = 42;
+static mut STATIC: u32 = 0x42424242;
// EMIT_MIR mutable_variable_no_prop.main.ConstProp.diff
fn main() {
StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
_4 = const _; // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
// mir::Constant
- // + span: $DIR/ref_deref.rs:6:6: 6:10
+ // + span: $DIR/ref_deref.rs:5:6: 5:10
// + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
- _2 = _4; // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
-- _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:+1:5: +1:10
-+ _1 = const 4_i32; // scope 0 at $DIR/ref_deref.rs:+1:5: +1:10
+ _2 = &(*_4); // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
+ _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:+1:5: +1:10
StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:+1:10: +1:11
StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:+1:10: +1:11
- nop; // scope 0 at $DIR/ref_deref.rs:+0:11: +2:2
+ _0 = const (); // scope 0 at $DIR/ref_deref.rs:+0:11: +2:2
return; // scope 0 at $DIR/ref_deref.rs:+2:2: +2:2
}
}
+++ /dev/null
-- // MIR for `main` before PromoteTemps
-+ // MIR for `main` after PromoteTemps
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/ref_deref.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/ref_deref.rs:+1:5: +1:10
- let mut _2: &i32; // in scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
- let _3: i32; // in scope 0 at $DIR/ref_deref.rs:+1:8: +1:9
-+ let mut _4: &i32; // in scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/ref_deref.rs:+1:5: +1:10
- StorageLive(_2); // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
-- StorageLive(_3); // scope 0 at $DIR/ref_deref.rs:+1:8: +1:9
-- _3 = const 4_i32; // scope 0 at $DIR/ref_deref.rs:+1:8: +1:9
-- _2 = &_3; // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
-+ _4 = const _; // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
-+ // mir::Constant
-+ // + span: $DIR/ref_deref.rs:6:6: 6:10
-+ // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
-+ _2 = &(*_4); // scope 0 at $DIR/ref_deref.rs:+1:6: +1:10
- _1 = (*_2); // scope 0 at $DIR/ref_deref.rs:+1:5: +1:10
-- StorageDead(_3); // scope 0 at $DIR/ref_deref.rs:+1:10: +1:11
- StorageDead(_2); // scope 0 at $DIR/ref_deref.rs:+1:10: +1:11
- StorageDead(_1); // scope 0 at $DIR/ref_deref.rs:+1:10: +1:11
- _0 = const (); // scope 0 at $DIR/ref_deref.rs:+0:11: +2:2
- return; // scope 0 at $DIR/ref_deref.rs:+2:2: +2:2
- }
- }
-
-// compile-flags: -Zmir-enable-passes=-SimplifyLocals-before-const-prop
-// EMIT_MIR ref_deref.main.PromoteTemps.diff
+// unit-test: ConstProp
// EMIT_MIR ref_deref.main.ConstProp.diff
fn main() {
StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
_4 = const _; // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
// mir::Constant
- // + span: $DIR/ref_deref_project.rs:6:6: 6:17
+ // + span: $DIR/ref_deref_project.rs:5:6: 5:17
// + literal: Const { ty: &(i32, i32), val: Unevaluated(main, [], Some(promoted[0])) }
_2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
_1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:+1:5: +1:17
StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:+1:17: +1:18
StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:+1:17: +1:18
- nop; // scope 0 at $DIR/ref_deref_project.rs:+0:11: +2:2
+ _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:+0:11: +2:2
return; // scope 0 at $DIR/ref_deref_project.rs:+2:2: +2:2
}
}
+++ /dev/null
-- // MIR for `main` before PromoteTemps
-+ // MIR for `main` after PromoteTemps
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/ref_deref_project.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/ref_deref_project.rs:+1:5: +1:17
- let mut _2: &i32; // in scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
- let _3: (i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:+1:8: +1:14
-+ let mut _4: &(i32, i32); // in scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/ref_deref_project.rs:+1:5: +1:17
- StorageLive(_2); // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
-- StorageLive(_3); // scope 0 at $DIR/ref_deref_project.rs:+1:8: +1:14
-- _3 = (const 4_i32, const 5_i32); // scope 0 at $DIR/ref_deref_project.rs:+1:8: +1:14
-- _2 = &(_3.1: i32); // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
-+ _4 = const _; // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
-+ // mir::Constant
-+ // + span: $DIR/ref_deref_project.rs:6:6: 6:17
-+ // + literal: Const { ty: &(i32, i32), val: Unevaluated(main, [], Some(promoted[0])) }
-+ _2 = &((*_4).1: i32); // scope 0 at $DIR/ref_deref_project.rs:+1:6: +1:17
- _1 = (*_2); // scope 0 at $DIR/ref_deref_project.rs:+1:5: +1:17
-- StorageDead(_3); // scope 0 at $DIR/ref_deref_project.rs:+1:17: +1:18
- StorageDead(_2); // scope 0 at $DIR/ref_deref_project.rs:+1:17: +1:18
- StorageDead(_1); // scope 0 at $DIR/ref_deref_project.rs:+1:17: +1:18
- _0 = const (); // scope 0 at $DIR/ref_deref_project.rs:+0:11: +2:2
- return; // scope 0 at $DIR/ref_deref_project.rs:+2:2: +2:2
- }
- }
-
-// compile-flags: -Zmir-enable-passes=-SimplifyLocals-before-const-prop
-// EMIT_MIR ref_deref_project.main.PromoteTemps.diff
+// unit-test: ConstProp
// EMIT_MIR ref_deref_project.main.ConstProp.diff
fn main() {
StorageLive(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:+1:9: +1:10
_1 = const 1_u32; // scope 0 at $DIR/scalar_literal_propagation.rs:+1:13: +1:14
StorageLive(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:+2:5: +2:15
- StorageLive(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:+2:13: +2:14
-- _3 = _1; // scope 1 at $DIR/scalar_literal_propagation.rs:+2:13: +2:14
-- _2 = consume(move _3) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:+2:5: +2:15
-+ _3 = const 1_u32; // scope 1 at $DIR/scalar_literal_propagation.rs:+2:13: +2:14
+- _2 = consume(_1) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:+2:5: +2:15
+ _2 = consume(const 1_u32) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:+2:5: +2:15
// mir::Constant
// + span: $DIR/scalar_literal_propagation.rs:4:5: 4:12
}
bb1: {
- StorageDead(_3); // scope 1 at $DIR/scalar_literal_propagation.rs:+2:14: +2:15
StorageDead(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:+2:15: +2:16
- StorageDead(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:+3:1: +3:2
return; // scope 0 at $DIR/scalar_literal_propagation.rs:+3:2: +3:2
}
}
StorageLive(_4); // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
_9 = const _; // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
// mir::Constant
- // + span: $DIR/slice_len.rs:6:6: 6:19
+ // + span: $DIR/slice_len.rs:7:6: 7:19
// + literal: Const { ty: &[u32; 3], val: Unevaluated(main, [], Some(promoted[0])) }
_4 = _9; // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
_3 = _4; // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
+ _7 = const 3_usize; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
+ _8 = const true; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
-+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> bb1; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
++ assert(const true, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
}
bb1: {
StorageDead(_4); // scope 0 at $DIR/slice_len.rs:+1:33: +1:34
StorageDead(_2); // scope 0 at $DIR/slice_len.rs:+1:33: +1:34
StorageDead(_1); // scope 0 at $DIR/slice_len.rs:+1:33: +1:34
- nop; // scope 0 at $DIR/slice_len.rs:+0:11: +2:2
+ _0 = const (); // scope 0 at $DIR/slice_len.rs:+0:11: +2:2
return; // scope 0 at $DIR/slice_len.rs:+2:2: +2:2
}
}
StorageLive(_4); // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
_9 = const _; // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
// mir::Constant
- // + span: $DIR/slice_len.rs:6:6: 6:19
+ // + span: $DIR/slice_len.rs:7:6: 7:19
// + literal: Const { ty: &[u32; 3], val: Unevaluated(main, [], Some(promoted[0])) }
_4 = _9; // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
_3 = _4; // scope 0 at $DIR/slice_len.rs:+1:6: +1:19
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
+ _7 = const 3_usize; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
+ _8 = const true; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
-+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> bb1; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
++ assert(const true, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> bb1; // scope 0 at $DIR/slice_len.rs:+1:5: +1:33
}
bb1: {
StorageDead(_4); // scope 0 at $DIR/slice_len.rs:+1:33: +1:34
StorageDead(_2); // scope 0 at $DIR/slice_len.rs:+1:33: +1:34
StorageDead(_1); // scope 0 at $DIR/slice_len.rs:+1:33: +1:34
- nop; // scope 0 at $DIR/slice_len.rs:+0:11: +2:2
+ _0 = const (); // scope 0 at $DIR/slice_len.rs:+0:11: +2:2
return; // scope 0 at $DIR/slice_len.rs:+2:2: +2:2
}
}
-// compile-flags: -Zmir-enable-passes=-SimplifyLocals-before-const-prop
+// unit-test: ConstProp
+// compile-flags: -Zmir-enable-passes=+InstCombine
// EMIT_MIR_FOR_EACH_BIT_WIDTH
// EMIT_MIR slice_len.main.ConstProp.diff
fn bar() -> () {
let mut _0: (); // return place in scope 0 at $DIR/const_prop_miscompile.rs:+0:10: +0:10
let mut _1: (i32,); // in scope 0 at $DIR/const_prop_miscompile.rs:+1:9: +1:14
- let mut _2: *mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
- let mut _4: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:+5:13: +5:20
+ let _2: (); // in scope 0 at $DIR/const_prop_miscompile.rs:+2:5: +4:6
+ let mut _3: *mut i32; // in scope 0 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
+ let mut _5: i32; // in scope 0 at $DIR/const_prop_miscompile.rs:+5:13: +5:20
scope 1 {
debug v => _1; // in scope 1 at $DIR/const_prop_miscompile.rs:+1:9: +1:14
- let _3: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:+5:9: +5:10
+ let _4: bool; // in scope 1 at $DIR/const_prop_miscompile.rs:+5:9: +5:10
scope 2 {
}
scope 3 {
- debug y => _3; // in scope 3 at $DIR/const_prop_miscompile.rs:+5:9: +5:10
+ debug y => _4; // in scope 3 at $DIR/const_prop_miscompile.rs:+5:9: +5:10
}
}
StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+1:9: +1:14
Deinit(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
(_1.0: i32) = const 1_i32; // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
- StorageLive(_2); // scope 2 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
- _2 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
- (*_2) = const 5_i32; // scope 2 at $DIR/const_prop_miscompile.rs:+3:9: +3:26
- StorageDead(_2); // scope 2 at $DIR/const_prop_miscompile.rs:+3:26: +3:27
- StorageLive(_3); // scope 1 at $DIR/const_prop_miscompile.rs:+5:9: +5:10
- StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:+5:13: +5:20
- _4 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:+5:15: +5:18
- _3 = Eq(move _4, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:+5:13: +5:25
- StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:+5:24: +5:25
- StorageDead(_3); // scope 1 at $DIR/const_prop_miscompile.rs:+6:1: +6:2
+ StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:+2:5: +4:6
+ StorageLive(_3); // scope 2 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
+ _3 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
+ (*_3) = const 5_i32; // scope 2 at $DIR/const_prop_miscompile.rs:+3:9: +3:26
+ StorageDead(_3); // scope 2 at $DIR/const_prop_miscompile.rs:+3:26: +3:27
+ _2 = const (); // scope 2 at $DIR/const_prop_miscompile.rs:+2:5: +4:6
+ StorageDead(_2); // scope 1 at $DIR/const_prop_miscompile.rs:+4:5: +4:6
+ StorageLive(_4); // scope 1 at $DIR/const_prop_miscompile.rs:+5:9: +5:10
+ StorageLive(_5); // scope 1 at $DIR/const_prop_miscompile.rs:+5:13: +5:20
+ _5 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:+5:15: +5:18
+ _4 = Eq(move _5, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:+5:13: +5:25
+ StorageDead(_5); // scope 1 at $DIR/const_prop_miscompile.rs:+5:24: +5:25
+ _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:+0:10: +6:2
+ StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:+6:1: +6:2
StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+6:1: +6:2
return; // scope 0 at $DIR/const_prop_miscompile.rs:+6:2: +6:2
}
_4 = (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:+3:15: +3:18
_3 = Eq(move _4, const 5_i32); // scope 1 at $DIR/const_prop_miscompile.rs:+3:13: +3:25
StorageDead(_4); // scope 1 at $DIR/const_prop_miscompile.rs:+3:24: +3:25
+ _0 = const (); // scope 0 at $DIR/const_prop_miscompile.rs:+0:10: +4:2
StorageDead(_3); // scope 1 at $DIR/const_prop_miscompile.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+4:1: +4:2
return; // scope 0 at $DIR/const_prop_miscompile.rs:+4:2: +4:2
+// unit-test: ConstProp
#![feature(raw_ref_op)]
// EMIT_MIR const_prop_miscompile.foo.ConstProp.diff
--- /dev/null
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f() -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/borrowed_local.rs:+0:11: +0:15
+ let mut _1: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _2: &u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _4: &u8; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _1 = const 5_u8; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _2 = &_1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _3 = _1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _4 = &_3; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _0 = cmp_ref(_2, _4) -> bb1; // scope 0 at $DIR/borrowed_local.rs:+8:13: +8:45
+ // mir::Constant
+ // + span: $DIR/borrowed_local.rs:23:29: 23:36
+ // + literal: Const { ty: for<'a, 'b> fn(&'a u8, &'b u8) -> bool {cmp_ref}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+- _0 = opaque::<u8>(_3) -> bb2; // scope 0 at $DIR/borrowed_local.rs:+12:13: +12:38
++ _0 = opaque::<u8>(_1) -> bb2; // scope 0 at $DIR/borrowed_local.rs:+12:13: +12:38
+ // mir::Constant
+ // + span: $DIR/borrowed_local.rs:27:28: 27:34
+ // + literal: Const { ty: fn(u8) -> bool {opaque::<u8>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ return; // scope 0 at $DIR/borrowed_local.rs:+15:13: +15:21
+ }
+ }
+
--- /dev/null
+// unit-test: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+fn opaque(_: impl Sized) -> bool { true }
+
+fn cmp_ref(a: &u8, b: &u8) -> bool {
+ std::ptr::eq(a as *const u8, b as *const u8)
+}
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f() -> bool {
+ mir!(
+ {
+ let a = 5_u8;
+ let r1 = &a;
+ let b = a;
+ // We cannot propagate the place `a`.
+ let r2 = &b;
+ Call(RET, next, cmp_ref(r1, r2))
+ }
+ next = {
+ // But we can propagate the value `a`.
+ Call(RET, ret, opaque(b))
+ }
+ ret = {
+ Return()
+ }
+ )
+}
+
+fn main() {
+ assert!(!f());
+}
+
+// EMIT_MIR borrowed_local.f.CopyProp.diff
--- /dev/null
+- // MIR for `foo` before CopyProp
++ // MIR for `foo` after CopyProp
+
+ fn foo() -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/branch.rs:+0:13: +0:16
+ let _1: i32; // in scope 0 at $DIR/branch.rs:+1:9: +1:10
+ let mut _3: bool; // in scope 0 at $DIR/branch.rs:+3:16: +3:22
+ let _4: i32; // in scope 0 at $DIR/branch.rs:+6:9: +6:14
+ scope 1 {
+ debug x => _1; // in scope 1 at $DIR/branch.rs:+1:9: +1:10
+ let _2: i32; // in scope 1 at $DIR/branch.rs:+3:9: +3:10
+ scope 2 {
+ debug y => _2; // in scope 2 at $DIR/branch.rs:+3:9: +3:10
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/branch.rs:+1:9: +1:10
+ _1 = val() -> bb1; // scope 0 at $DIR/branch.rs:+1:13: +1:18
+ // mir::Constant
+ // + span: $DIR/branch.rs:13:13: 13:16
+ // + literal: Const { ty: fn() -> i32 {val}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageLive(_2); // scope 1 at $DIR/branch.rs:+3:9: +3:10
+ StorageLive(_3); // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ _3 = cond() -> bb2; // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ // mir::Constant
+ // + span: $DIR/branch.rs:15:16: 15:20
+ // + literal: Const { ty: fn() -> bool {cond}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ switchInt(move _3) -> [0: bb4, otherwise: bb3]; // scope 1 at $DIR/branch.rs:+3:16: +3:22
+ }
+
+ bb3: {
+ _2 = _1; // scope 1 at $DIR/branch.rs:+4:9: +4:10
+ goto -> bb6; // scope 1 at $DIR/branch.rs:+3:13: +8:6
+ }
+
+ bb4: {
+ StorageLive(_4); // scope 1 at $DIR/branch.rs:+6:9: +6:14
+ _4 = val() -> bb5; // scope 1 at $DIR/branch.rs:+6:9: +6:14
+ // mir::Constant
+ // + span: $DIR/branch.rs:18:9: 18:12
+ // + literal: Const { ty: fn() -> i32 {val}, val: Value(<ZST>) }
+ }
+
+ bb5: {
+ StorageDead(_4); // scope 1 at $DIR/branch.rs:+6:14: +6:15
+ _2 = _1; // scope 1 at $DIR/branch.rs:+7:9: +7:10
+ goto -> bb6; // scope 1 at $DIR/branch.rs:+3:13: +8:6
+ }
+
+ bb6: {
+ StorageDead(_3); // scope 1 at $DIR/branch.rs:+8:5: +8:6
+ _0 = _2; // scope 2 at $DIR/branch.rs:+10:5: +10:6
+ StorageDead(_2); // scope 1 at $DIR/branch.rs:+11:1: +11:2
+ StorageDead(_1); // scope 0 at $DIR/branch.rs:+11:1: +11:2
+ return; // scope 0 at $DIR/branch.rs:+11:2: +11:2
+ }
+ }
+
--- /dev/null
+//! Tests that we bail out when there are multiple assignments to the same local.
+// unit-test: CopyProp
+fn val() -> i32 {
+ 1
+}
+
+fn cond() -> bool {
+ true
+}
+
+// EMIT_MIR branch.foo.CopyProp.diff
+fn foo() -> i32 {
+ let x = val();
+
+ let y = if cond() {
+ x
+ } else {
+ val();
+ x
+ };
+
+ y
+}
+
+fn main() {
+ foo();
+}
--- /dev/null
+- // MIR for `arg_src` before CopyProp
++ // MIR for `arg_src` after CopyProp
+
+ fn arg_src(_1: i32) -> i32 {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:12: +0:17
+ let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:27: +0:30
+ let _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:9: +1:10
+ scope 1 {
+ debug y => _2; // in scope 1 at $DIR/copy_propagation_arg.rs:+1:9: +1:10
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:9: +1:10
+ _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14
+ _1 = const 123_i32; // scope 1 at $DIR/copy_propagation_arg.rs:+2:5: +2:12
+ _0 = _2; // scope 1 at $DIR/copy_propagation_arg.rs:+3:5: +3:6
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+4:1: +4:2
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+- // MIR for `bar` before CopyProp
++ // MIR for `bar` after CopyProp
+
+ fn bar(_1: u8) -> () {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13
+ let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +0:19
+ let _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13
+ let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13
+ StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12
+ _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:11: +1:12
+ _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+1:5: +1:13
+ // mir::Constant
+ // + span: $DIR/copy_propagation_arg.rs:16:5: 16:10
+ // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+1:12: +1:13
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+1:13: +1:14
+ _1 = const 5_u8; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10
+ _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2
+ }
+ }
+
--- /dev/null
+- // MIR for `baz` before CopyProp
++ // MIR for `baz` after CopyProp
+
+ fn baz(_1: i32) -> i32 {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13
+ let mut _0: i32; // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:23: +0:26
+ let mut _2: i32; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+ _2 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+ _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:10
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:10
+ _0 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+3:5: +3:6
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+- // MIR for `foo` before CopyProp
++ // MIR for `foo` after CopyProp
+
+ fn foo(_1: u8) -> () {
+ debug x => _1; // in scope 0 at $DIR/copy_propagation_arg.rs:+0:8: +0:13
+ let mut _0: (); // return place in scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +0:19
+ let mut _2: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17
+ let mut _3: u8; // in scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17
+ StorageLive(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16
+ _3 = _1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:15: +2:16
+ _2 = dummy(move _3) -> bb1; // scope 0 at $DIR/copy_propagation_arg.rs:+2:9: +2:17
+ // mir::Constant
+ // + span: $DIR/copy_propagation_arg.rs:11:9: 11:14
+ // + literal: Const { ty: fn(u8) -> u8 {dummy}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_3); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17
+ _1 = move _2; // scope 0 at $DIR/copy_propagation_arg.rs:+2:5: +2:17
+ StorageDead(_2); // scope 0 at $DIR/copy_propagation_arg.rs:+2:16: +2:17
+ _0 = const (); // scope 0 at $DIR/copy_propagation_arg.rs:+0:19: +3:2
+ return; // scope 0 at $DIR/copy_propagation_arg.rs:+3:2: +3:2
+ }
+ }
+
--- /dev/null
+// Check that CopyProp does not propagate an assignment to a function argument
+// (doing so can break usages of the original argument value)
+// unit-test: CopyProp
+fn dummy(x: u8) -> u8 {
+ x
+}
+
+// EMIT_MIR copy_propagation_arg.foo.CopyProp.diff
+fn foo(mut x: u8) {
+ // calling `dummy` to make a use of `x` that copyprop cannot eliminate
+ x = dummy(x); // this will assign a local to `x`
+}
+
+// EMIT_MIR copy_propagation_arg.bar.CopyProp.diff
+fn bar(mut x: u8) {
+ dummy(x);
+ x = 5;
+}
+
+// EMIT_MIR copy_propagation_arg.baz.CopyProp.diff
+fn baz(mut x: i32) -> i32 {
+ // self-assignment to a function argument should be eliminated
+ x = x;
+ x
+}
+
+// EMIT_MIR copy_propagation_arg.arg_src.CopyProp.diff
+fn arg_src(mut x: i32) -> i32 {
+ let y = x;
+ x = 123; // Don't propagate this assignment to `y`
+ y
+}
+
+fn main() {
+ // Make sure the function actually gets instantiated.
+ foo(0);
+ bar(0);
+ baz(0);
+ arg_src(0);
+}
--- /dev/null
+- // MIR for `main` before CopyProp
++ // MIR for `main` after CopyProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/cycle.rs:+0:11: +0:11
+ let mut _1: i32; // in scope 0 at $DIR/cycle.rs:+1:9: +1:14
+ let mut _4: i32; // in scope 0 at $DIR/cycle.rs:+4:9: +4:10
+ let _5: (); // in scope 0 at $DIR/cycle.rs:+6:5: +6:12
+ let mut _6: i32; // in scope 0 at $DIR/cycle.rs:+6:10: +6:11
+ scope 1 {
+ debug x => _1; // in scope 1 at $DIR/cycle.rs:+1:9: +1:14
+ let _2: i32; // in scope 1 at $DIR/cycle.rs:+2:9: +2:10
+ scope 2 {
+ debug y => _2; // in scope 2 at $DIR/cycle.rs:+2:9: +2:10
+ let _3: i32; // in scope 2 at $DIR/cycle.rs:+3:9: +3:10
+ scope 3 {
+- debug z => _3; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10
++ debug z => _2; // in scope 3 at $DIR/cycle.rs:+3:9: +3:10
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/cycle.rs:+1:9: +1:14
+ _1 = val() -> bb1; // scope 0 at $DIR/cycle.rs:+1:17: +1:22
+ // mir::Constant
+ // + span: $DIR/cycle.rs:9:17: 9:20
+ // + literal: Const { ty: fn() -> i32 {val}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageLive(_2); // scope 1 at $DIR/cycle.rs:+2:9: +2:10
+ _2 = _1; // scope 1 at $DIR/cycle.rs:+2:13: +2:14
+- StorageLive(_3); // scope 2 at $DIR/cycle.rs:+3:9: +3:10
+- _3 = _2; // scope 2 at $DIR/cycle.rs:+3:13: +3:14
+- StorageLive(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10
+- _4 = _3; // scope 3 at $DIR/cycle.rs:+4:9: +4:10
+- _1 = move _4; // scope 3 at $DIR/cycle.rs:+4:5: +4:10
+- StorageDead(_4); // scope 3 at $DIR/cycle.rs:+4:9: +4:10
++ _1 = _2; // scope 3 at $DIR/cycle.rs:+4:5: +4:10
+ StorageLive(_5); // scope 3 at $DIR/cycle.rs:+6:5: +6:12
+ StorageLive(_6); // scope 3 at $DIR/cycle.rs:+6:10: +6:11
+ _6 = _1; // scope 3 at $DIR/cycle.rs:+6:10: +6:11
+ _5 = std::mem::drop::<i32>(move _6) -> bb2; // scope 3 at $DIR/cycle.rs:+6:5: +6:12
+ // mir::Constant
+ // + span: $DIR/cycle.rs:14:5: 14:9
+ // + literal: Const { ty: fn(i32) {std::mem::drop::<i32>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_6); // scope 3 at $DIR/cycle.rs:+6:11: +6:12
+ StorageDead(_5); // scope 3 at $DIR/cycle.rs:+6:12: +6:13
+ _0 = const (); // scope 0 at $DIR/cycle.rs:+0:11: +7:2
+- StorageDead(_3); // scope 2 at $DIR/cycle.rs:+7:1: +7:2
+- StorageDead(_2); // scope 1 at $DIR/cycle.rs:+7:1: +7:2
+ StorageDead(_1); // scope 0 at $DIR/cycle.rs:+7:1: +7:2
+ return; // scope 0 at $DIR/cycle.rs:+7:2: +7:2
+ }
+ }
+
--- /dev/null
+//! Tests that cyclic assignments don't hang CopyProp, and result in reasonable code.
+// unit-test: CopyProp
+fn val() -> i32 {
+ 1
+}
+
+// EMIT_MIR cycle.main.CopyProp.diff
+fn main() {
+ let mut x = val();
+ let y = x;
+ let z = y;
+ x = z;
+
+ drop(x);
+}
--- /dev/null
+// MIR for `f` after CopyProp
+
+fn f(_1: usize) -> usize {
+ debug a => _1; // in scope 0 at $DIR/dead_stores_79191.rs:+0:6: +0:11
+ let mut _0: usize; // return place in scope 0 at $DIR/dead_stores_79191.rs:+0:23: +0:28
+ let _2: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+1:9: +1:10
+ let mut _3: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+3:9: +3:10
+ let mut _4: usize; // in scope 0 at $DIR/dead_stores_79191.rs:+4:8: +4:9
+ scope 1 {
+ debug b => _2; // in scope 1 at $DIR/dead_stores_79191.rs:+1:9: +1:10
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/dead_stores_79191.rs:+1:9: +1:10
+ _2 = _1; // scope 0 at $DIR/dead_stores_79191.rs:+1:13: +1:14
+ _1 = const 5_usize; // scope 1 at $DIR/dead_stores_79191.rs:+2:5: +2:10
+ _1 = _2; // scope 1 at $DIR/dead_stores_79191.rs:+3:5: +3:10
+ StorageLive(_4); // scope 1 at $DIR/dead_stores_79191.rs:+4:8: +4:9
+ _4 = _1; // scope 1 at $DIR/dead_stores_79191.rs:+4:8: +4:9
+ _0 = id::<usize>(move _4) -> bb1; // scope 1 at $DIR/dead_stores_79191.rs:+4:5: +4:10
+ // mir::Constant
+ // + span: $DIR/dead_stores_79191.rs:12:5: 12:7
+ // + literal: Const { ty: fn(usize) -> usize {id::<usize>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_4); // scope 1 at $DIR/dead_stores_79191.rs:+4:9: +4:10
+ return; // scope 0 at $DIR/dead_stores_79191.rs:+5:2: +5:2
+ }
+}
--- /dev/null
+// unit-test: CopyProp
+
+fn id<T>(x: T) -> T {
+ x
+}
+
+// EMIT_MIR dead_stores_79191.f.CopyProp.after.mir
+fn f(mut a: usize) -> usize {
+ let b = a;
+ a = 5;
+ a = b;
+ id(a)
+}
+
+fn main() {
+ f(0);
+}
--- /dev/null
+// MIR for `f` after CopyProp
+
+fn f(_1: usize) -> usize {
+ debug a => _1; // in scope 0 at $DIR/dead_stores_better.rs:+0:10: +0:15
+ let mut _0: usize; // return place in scope 0 at $DIR/dead_stores_better.rs:+0:27: +0:32
+ let _2: usize; // in scope 0 at $DIR/dead_stores_better.rs:+1:9: +1:10
+ let mut _3: usize; // in scope 0 at $DIR/dead_stores_better.rs:+3:9: +3:10
+ let mut _4: usize; // in scope 0 at $DIR/dead_stores_better.rs:+4:8: +4:9
+ scope 1 {
+ debug b => _2; // in scope 1 at $DIR/dead_stores_better.rs:+1:9: +1:10
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/dead_stores_better.rs:+1:9: +1:10
+ _2 = _1; // scope 0 at $DIR/dead_stores_better.rs:+1:13: +1:14
+ _1 = const 5_usize; // scope 1 at $DIR/dead_stores_better.rs:+2:5: +2:10
+ _1 = _2; // scope 1 at $DIR/dead_stores_better.rs:+3:5: +3:10
+ StorageLive(_4); // scope 1 at $DIR/dead_stores_better.rs:+4:8: +4:9
+ _4 = _1; // scope 1 at $DIR/dead_stores_better.rs:+4:8: +4:9
+ _0 = id::<usize>(move _4) -> bb1; // scope 1 at $DIR/dead_stores_better.rs:+4:5: +4:10
+ // mir::Constant
+ // + span: $DIR/dead_stores_better.rs:16:5: 16:7
+ // + literal: Const { ty: fn(usize) -> usize {id::<usize>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_4); // scope 1 at $DIR/dead_stores_better.rs:+4:9: +4:10
+ return; // scope 0 at $DIR/dead_stores_better.rs:+5:2: +5:2
+ }
+}
--- /dev/null
+// This is a copy of the `dead_stores_79191` test, except that we turn on DSE. This demonstrates
+// that that pass enables this one to do more optimizations.
+
+// unit-test: CopyProp
+// compile-flags: -Zmir-enable-passes=+DeadStoreElimination
+
+fn id<T>(x: T) -> T {
+ x
+}
+
+// EMIT_MIR dead_stores_better.f.CopyProp.after.mir
+pub fn f(mut a: usize) -> usize {
+ let b = a;
+ a = 5;
+ a = b;
+ id(a)
+}
+
+fn main() {
+ f(0);
+}
--- /dev/null
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: T) -> () {
+ debug a => _1; // in scope 0 at $DIR/move_arg.rs:+0:19: +0:20
+ let mut _0: (); // return place in scope 0 at $DIR/move_arg.rs:+0:25: +0:25
+ let _2: T; // in scope 0 at $DIR/move_arg.rs:+1:9: +1:10
+ let _3: (); // in scope 0 at $DIR/move_arg.rs:+2:5: +2:12
+ let mut _4: T; // in scope 0 at $DIR/move_arg.rs:+2:7: +2:8
+ let mut _5: T; // in scope 0 at $DIR/move_arg.rs:+2:10: +2:11
+ scope 1 {
+- debug b => _2; // in scope 1 at $DIR/move_arg.rs:+1:9: +1:10
++ debug b => _1; // in scope 1 at $DIR/move_arg.rs:+1:9: +1:10
+ }
+
+ bb0: {
+- StorageLive(_2); // scope 0 at $DIR/move_arg.rs:+1:9: +1:10
+- _2 = _1; // scope 0 at $DIR/move_arg.rs:+1:13: +1:14
+ StorageLive(_3); // scope 1 at $DIR/move_arg.rs:+2:5: +2:12
+- StorageLive(_4); // scope 1 at $DIR/move_arg.rs:+2:7: +2:8
+- _4 = _1; // scope 1 at $DIR/move_arg.rs:+2:7: +2:8
+- StorageLive(_5); // scope 1 at $DIR/move_arg.rs:+2:10: +2:11
+- _5 = _2; // scope 1 at $DIR/move_arg.rs:+2:10: +2:11
+- _3 = g::<T>(move _4, move _5) -> bb1; // scope 1 at $DIR/move_arg.rs:+2:5: +2:12
++ _3 = g::<T>(_1, _1) -> bb1; // scope 1 at $DIR/move_arg.rs:+2:5: +2:12
+ // mir::Constant
+ // + span: $DIR/move_arg.rs:7:5: 7:6
+ // + literal: Const { ty: fn(T, T) {g::<T>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+- StorageDead(_5); // scope 1 at $DIR/move_arg.rs:+2:11: +2:12
+- StorageDead(_4); // scope 1 at $DIR/move_arg.rs:+2:11: +2:12
+ StorageDead(_3); // scope 1 at $DIR/move_arg.rs:+2:12: +2:13
+ _0 = const (); // scope 0 at $DIR/move_arg.rs:+0:25: +3:2
+- StorageDead(_2); // scope 0 at $DIR/move_arg.rs:+3:1: +3:2
+ return; // scope 0 at $DIR/move_arg.rs:+3:2: +3:2
+ }
+ }
+
--- /dev/null
+// Test that we do not move multiple times from the same local.
+// unit-test: CopyProp
+
+// EMIT_MIR move_arg.f.CopyProp.diff
+pub fn f<T: Copy>(a: T) {
+ let b = a;
+ g(a, b);
+}
+
+#[inline(never)]
+pub fn g<T: Copy>(_: T, _: T) {}
+
+fn main() {
+ f(5)
+}
--- /dev/null
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: bool) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/mutate_through_pointer.rs:+0:18: +0:22
+ let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: *const bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _4: *mut bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ _2 = _1; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _3 = &raw const _2; // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ _4 = &raw mut (*_3); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ (*_4) = const false; // scope 0 at $DIR/mutate_through_pointer.rs:+5:9: +5:20
+ _0 = _1; // scope 0 at $DIR/mutate_through_pointer.rs:+6:9: +6:16
+ return; // scope 0 at $DIR/mutate_through_pointer.rs:+7:9: +7:17
+ }
+ }
+
--- /dev/null
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f(c: bool) -> bool {
+ mir!({
+ let a = c;
+ let p = core::ptr::addr_of!(a);
+ let p2 = core::ptr::addr_of_mut!(*p);
+ *p2 = false;
+ RET = c;
+ Return()
+ })
+}
+
+fn main() {
+ assert_eq!(true, f(true));
+}
+
+// EMIT_MIR mutate_through_pointer.f.CopyProp.diff
--- /dev/null
+- // MIR for `f` before CopyProp
++ // MIR for `f` after CopyProp
+
+ fn f(_1: bool) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/non_dominate.rs:+0:18: +0:22
+ let mut _2: bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+ let mut _3: bool; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
+
+ bb0: {
+ goto -> bb1; // scope 0 at $DIR/non_dominate.rs:+4:11: +4:20
+ }
+
+ bb1: {
+ _3 = _1; // scope 0 at $DIR/non_dominate.rs:+5:17: +5:22
+ switchInt(_3) -> [0: bb3, otherwise: bb2]; // scope 0 at $DIR/non_dominate.rs:+5:24: +5:58
+ }
+
+ bb2: {
+ _2 = _3; // scope 0 at $DIR/non_dominate.rs:+8:17: +8:22
+ _1 = const false; // scope 0 at $DIR/non_dominate.rs:+8:24: +8:33
+ goto -> bb1; // scope 0 at $DIR/non_dominate.rs:+8:35: +8:44
+ }
+
+ bb3: {
+ _0 = _2; // scope 0 at $DIR/non_dominate.rs:+9:17: +9:24
+ return; // scope 0 at $DIR/non_dominate.rs:+9:26: +9:34
+ }
+ }
+
--- /dev/null
+// unit-test: CopyProp
+
+#![feature(custom_mir, core_intrinsics)]
+#![allow(unused_assignments)]
+extern crate core;
+use core::intrinsics::mir::*;
+
+#[custom_mir(dialect = "analysis", phase = "post-cleanup")]
+fn f(c: bool) -> bool {
+ mir!(
+ let a: bool;
+ let b: bool;
+ { Goto(bb1) }
+ bb1 = { b = c; match b { false => bb3, _ => bb2 }}
+ // This assignment to `a` does not dominate the use in `bb3`.
+ // It should not be replaced by `b`.
+ bb2 = { a = b; c = false; Goto(bb1) }
+ bb3 = { RET = a; Return() }
+ )
+}
+
+fn main() {
+ assert_eq!(true, f(true));
+}
+
+// EMIT_MIR non_dominate.f.CopyProp.diff
_1 = const u8::MAX; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
StorageLive(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
_2 = const 1_u8; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
- StorageLive(_3); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
- _3 = const u8::MAX; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
- StorageLive(_4); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
- _4 = const 1_u8; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
_5 = CheckedAdd(const u8::MAX, const 1_u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
assert(!move (_5.1: bool), "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
}
bb1: {
- StorageDead(_4); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
- StorageDead(_3); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
- StorageDead(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
- StorageDead(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
return; // scope 0 at $DIR/inherit_overflow.rs:+4:2: +4:2
}
}
}
bb1: {
- _0 = Div(const 256_i32, move _1); // scope 0 at $DIR/div_overflow.rs:+1:5: +1:12
+ _0 = Div(const 256_i32, _1); // scope 0 at $DIR/div_overflow.rs:+1:5: +1:12
return; // scope 0 at $DIR/div_overflow.rs:+2:2: +2:2
}
}
let mut _0: i32; // return place in scope 0 at $DIR/div_overflow.rs:+0:33: +0:36
bb0: {
- _0 = Div(move _1, const 256_i32); // scope 0 at $DIR/div_overflow.rs:+1:5: +1:12
+ _0 = Div(_1, const 256_i32); // scope 0 at $DIR/div_overflow.rs:+1:5: +1:12
return; // scope 0 at $DIR/div_overflow.rs:+2:2: +2:2
}
}
_10 = ((_7 as Some).0: usize); // scope 3 at $DIR/funky_arms.rs:+13:17: +13:26
StorageLive(_11); // scope 3 at $DIR/funky_arms.rs:+15:43: +15:46
_11 = &mut (*_1); // scope 3 at $DIR/funky_arms.rs:+15:43: +15:46
- StorageLive(_12); // scope 3 at $DIR/funky_arms.rs:+15:48: +15:51
- _12 = _2; // scope 3 at $DIR/funky_arms.rs:+15:48: +15:51
StorageLive(_13); // scope 3 at $DIR/funky_arms.rs:+15:53: +15:57
_13 = _6; // scope 3 at $DIR/funky_arms.rs:+15:53: +15:57
StorageLive(_14); // scope 3 at $DIR/funky_arms.rs:+15:59: +15:79
StorageLive(_15); // scope 3 at $DIR/funky_arms.rs:+15:59: +15:75
- StorageLive(_16); // scope 3 at $DIR/funky_arms.rs:+15:59: +15:68
- _16 = _10; // scope 3 at $DIR/funky_arms.rs:+15:59: +15:68
- _15 = move _16 as u32 (IntToInt); // scope 3 at $DIR/funky_arms.rs:+15:59: +15:75
- StorageDead(_16); // scope 3 at $DIR/funky_arms.rs:+15:74: +15:75
+ _15 = _10 as u32 (IntToInt); // scope 3 at $DIR/funky_arms.rs:+15:59: +15:75
_14 = Add(move _15, const 1_u32); // scope 3 at $DIR/funky_arms.rs:+15:59: +15:79
StorageDead(_15); // scope 3 at $DIR/funky_arms.rs:+15:78: +15:79
- StorageLive(_17); // scope 3 at $DIR/funky_arms.rs:+15:81: +15:86
- _17 = _3; // scope 3 at $DIR/funky_arms.rs:+15:81: +15:86
- _0 = float_to_exponential_common_exact::<T>(move _11, move _12, move _13, move _14, move _17) -> bb7; // scope 3 at $DIR/funky_arms.rs:+15:9: +15:87
+ _0 = float_to_exponential_common_exact::<T>(move _11, _2, move _13, move _14, _3) -> bb7; // scope 3 at $DIR/funky_arms.rs:+15:9: +15:87
// mir::Constant
// + span: $DIR/funky_arms.rs:26:9: 26:42
// + literal: Const { ty: for<'a, 'b, 'c> fn(&'a mut Formatter<'b>, &'c T, Sign, u32, bool) -> Result<(), std::fmt::Error> {float_to_exponential_common_exact::<T>}, val: Value(<ZST>) }
}
bb7: {
- StorageDead(_17); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87
StorageDead(_14); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87
StorageDead(_13); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87
- StorageDead(_12); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87
StorageDead(_11); // scope 3 at $DIR/funky_arms.rs:+15:86: +15:87
- StorageDead(_10); // scope 2 at $DIR/funky_arms.rs:+16:5: +16:6
goto -> bb10; // scope 2 at $DIR/funky_arms.rs:+13:5: +18:6
}
bb8: {
StorageLive(_18); // scope 2 at $DIR/funky_arms.rs:+17:46: +17:49
_18 = &mut (*_1); // scope 2 at $DIR/funky_arms.rs:+17:46: +17:49
- StorageLive(_19); // scope 2 at $DIR/funky_arms.rs:+17:51: +17:54
- _19 = _2; // scope 2 at $DIR/funky_arms.rs:+17:51: +17:54
StorageLive(_20); // scope 2 at $DIR/funky_arms.rs:+17:56: +17:60
_20 = _6; // scope 2 at $DIR/funky_arms.rs:+17:56: +17:60
- StorageLive(_21); // scope 2 at $DIR/funky_arms.rs:+17:62: +17:67
- _21 = _3; // scope 2 at $DIR/funky_arms.rs:+17:62: +17:67
- _0 = float_to_exponential_common_shortest::<T>(move _18, move _19, move _20, move _21) -> bb9; // scope 2 at $DIR/funky_arms.rs:+17:9: +17:68
+ _0 = float_to_exponential_common_shortest::<T>(move _18, _2, move _20, _3) -> bb9; // scope 2 at $DIR/funky_arms.rs:+17:9: +17:68
// mir::Constant
// + span: $DIR/funky_arms.rs:28:9: 28:45
// + literal: Const { ty: for<'a, 'b, 'c> fn(&'a mut Formatter<'b>, &'c T, Sign, bool) -> Result<(), std::fmt::Error> {float_to_exponential_common_shortest::<T>}, val: Value(<ZST>) }
}
bb9: {
- StorageDead(_21); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68
StorageDead(_20); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68
- StorageDead(_19); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68
StorageDead(_18); // scope 2 at $DIR/funky_arms.rs:+17:67: +17:68
goto -> bb10; // scope 2 at $DIR/funky_arms.rs:+13:5: +18:6
}
// MIR for `main::{closure#0}` 0 generator_drop
/* generator_layout = GeneratorLayout {
field_tys: {
- _0: std::string::String,
+ _0: GeneratorSavedTy {
+ ty: std::string::String,
+ source_info: SourceInfo {
+ span: $DIR/generator_drop_cleanup.rs:11:13: 11:15 (#0),
+ scope: scope[0],
+ },
+ ignore_for_traits: false,
+ },
},
variant_fields: {
Unresumed(0): [],
// MIR for `main::{closure#0}` 0 generator_resume
/* generator_layout = GeneratorLayout {
field_tys: {
- _0: HasDrop,
+ _0: GeneratorSavedTy {
+ ty: HasDrop,
+ source_info: SourceInfo {
+ span: $DIR/generator_tiny.rs:20:13: 20:15 (#0),
+ scope: scope[0],
+ },
+ ignore_for_traits: false,
+ },
},
variant_fields: {
Unresumed(0): [],
_4 = &(*_2); // scope 1 at $DIR/dyn_trait.rs:+2:23: +2:24
- _0 = try_execute_query::<<Q as Query>::C>(move _4) -> bb2; // scope 1 at $DIR/dyn_trait.rs:+2:5: +2:25
+ StorageLive(_5); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15
-+ _5 = move _4 as &dyn Cache<V = <Q as Query>::V> (Pointer(Unsize)); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15
-+ _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(move _5) -> bb2; // scope 3 at $DIR/dyn_trait.rs:21:5: 21:22
++ _5 = _4 as &dyn Cache<V = <Q as Query>::V> (Pointer(Unsize)); // scope 2 at $DIR/dyn_trait.rs:27:14: 27:15
++ _0 = <dyn Cache<V = <Q as Query>::V> as Cache>::store_nocache(_5) -> bb2; // scope 3 at $DIR/dyn_trait.rs:21:5: 21:22
// mir::Constant
- // + span: $DIR/dyn_trait.rs:34:5: 34:22
- // + literal: Const { ty: for<'a> fn(&'a <Q as Query>::C) {try_execute_query::<<Q as Query>::C>}, val: Value(<ZST>) }
_2 = move _3 as &dyn Cache<V = <C as Cache>::V> (Pointer(Unsize)); // scope 0 at $DIR/dyn_trait.rs:+1:14: +1:15
StorageDead(_3); // scope 0 at $DIR/dyn_trait.rs:+1:14: +1:15
- _0 = mk_cycle::<<C as Cache>::V>(move _2) -> bb1; // scope 0 at $DIR/dyn_trait.rs:+1:5: +1:16
-+ _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(move _2) -> bb1; // scope 1 at $DIR/dyn_trait.rs:21:5: 21:22
++ _0 = <dyn Cache<V = <C as Cache>::V> as Cache>::store_nocache(_2) -> bb1; // scope 1 at $DIR/dyn_trait.rs:21:5: 21:22
// mir::Constant
- // + span: $DIR/dyn_trait.rs:27:5: 27:13
- // + literal: Const { ty: for<'a> fn(&'a (dyn Cache<V = <C as Cache>::V> + 'a)) {mk_cycle::<<C as Cache>::V>}, val: Value(<ZST>) }
_3 = const 1_i32; // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13
StorageLive(_4); // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13
_4 = const -1_i32; // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13
- _0 = Eq(move _3, move _4); // scope 2 at $DIR/inline_any_operand.rs:17:5: 17:11
+ _0 = Eq(_3, _4); // scope 2 at $DIR/inline_any_operand.rs:17:5: 17:11
StorageDead(_4); // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13
StorageDead(_3); // scope 1 at $DIR/inline_any_operand.rs:+2:5: +2:13
StorageDead(_2); // scope 1 at $DIR/inline_any_operand.rs:+2:12: +2:13
+
+ bb3: {
+ StorageLive(_8); // scope 6 at $DIR/inline_generator.rs:15:17: 15:39
-+ switchInt(move _7) -> [0: bb5, otherwise: bb4]; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21
++ switchInt(_7) -> [0: bb5, otherwise: bb4]; // scope 6 at $DIR/inline_generator.rs:15:20: 15:21
+ }
+
+ bb4: {
+ StorageLive(_8); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41
+ StorageDead(_8); // scope 6 at $DIR/inline_generator.rs:15:38: 15:39
+ Deinit(_1); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
-+ ((_1 as Complete).0: bool) = move _7; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
++ ((_1 as Complete).0: bool) = _7; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
+ discriminant(_1) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
+ _12 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
+ discriminant((*_12)) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
let mut _6: (); // in scope 0 at $DIR/inline_into_box_place.rs:+1:42: +1:43
let mut _7: *const std::vec::Vec<u32>; // in scope 0 at $DIR/inline_into_box_place.rs:+1:29: +1:43
+ let mut _8: &mut std::vec::Vec<u32>; // in scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
++ let mut _9: std::vec::Vec<u32>; // in scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
scope 1 {
debug _x => _1; // in scope 1 at $DIR/inline_into_box_place.rs:+1:9: +1:11
}
scope 2 {
}
+ scope 3 (inlined Vec::<u32>::new) { // at $DIR/inline_into_box_place.rs:8:33: 8:43
-+ let mut _9: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ let mut _10: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ }
bb0: {
- (*_7) = Vec::<u32>::new() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ StorageLive(_8); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ _8 = &mut (*_7); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
-+ StorageLive(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ _9 = const _; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageLive(_9); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
++ StorageLive(_10); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ _10 = const _; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
// mir::Constant
- // + span: $DIR/inline_into_box_place.rs:8:33: 8:41
- // + user_ty: UserType(1)
+ // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Unevaluated(alloc::raw_vec::RawVec::<T>::NEW, [u32], None) }
-+ Deinit((*_8)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ ((*_8).0: alloc::raw_vec::RawVec<u32>) = move _9; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ ((*_8).1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ StorageDead(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ Deinit(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ (_9.0: alloc::raw_vec::RawVec<u32>) = move _10; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ (_9.1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageDead(_10); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ (*_8) = move _9; // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
++ StorageDead(_9); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ StorageDead(_8); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
_1 = move _5; // scope 0 at $DIR/inline_into_box_place.rs:+1:29: +1:43
StorageDead(_5); // scope 0 at $DIR/inline_into_box_place.rs:+1:42: +1:43
_3 = &(*_1); // scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11
_2 = move _3 as &dyn X (Pointer(Unsize)); // scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11
StorageDead(_3); // scope 0 at $DIR/inline_trait_method_2.rs:+1:10: +1:11
- _0 = <dyn X as X>::y(move _2) -> bb1; // scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10
+ _0 = <dyn X as X>::y(_2) -> bb1; // scope 1 at $DIR/inline_trait_method_2.rs:10:5: 10:10
// mir::Constant
// + span: $DIR/inline_trait_method_2.rs:10:7: 10:8
// + literal: Const { ty: for<'a> fn(&'a dyn X) -> bool {<dyn X as X>::y}, val: Value(<ZST>) }
--- /dev/null
+- // MIR for `outer` before Inline
++ // MIR for `outer` after Inline
+
+ fn outer() -> usize {
+ let mut _0: usize; // return place in scope 0 at $DIR/issue_106141.rs:+0:19: +0:24
++ scope 1 (inlined inner) { // at $DIR/issue_106141.rs:2:5: 2:12
++ let mut _1: bool; // in scope 1 at $DIR/issue_106141.rs:13:8: 13:21
++ let mut _2: bool; // in scope 1 at $DIR/issue_106141.rs:13:8: 13:21
++ let mut _3: &[bool; 1]; // in scope 1 at $DIR/issue_106141.rs:11:18: 11:25
++ scope 2 {
++ debug buffer => _3; // in scope 2 at $DIR/issue_106141.rs:11:9: 11:15
++ scope 3 {
++ debug index => _0; // in scope 3 at $DIR/issue_106141.rs:12:9: 12:14
++ }
++ }
++ }
+
+ bb0: {
+- _0 = inner() -> bb1; // scope 0 at $DIR/issue_106141.rs:+1:5: +1:12
++ StorageLive(_3); // scope 0 at $DIR/issue_106141.rs:+1:5: +1:12
++ _3 = const _; // scope 1 at $DIR/issue_106141.rs:11:18: 11:25
+ // mir::Constant
+- // + span: $DIR/issue_106141.rs:2:5: 2:10
+- // + literal: Const { ty: fn() -> usize {inner}, val: Value(<ZST>) }
++ // + span: $DIR/issue_106141.rs:11:18: 11:25
++ // + literal: Const { ty: &[bool; 1], val: Unevaluated(inner, [], Some(promoted[0])) }
++ _0 = index() -> bb1; // scope 2 at $DIR/issue_106141.rs:12:17: 12:24
++ // mir::Constant
++ // + span: $DIR/issue_106141.rs:12:17: 12:22
++ // + literal: Const { ty: fn() -> usize {index}, val: Value(<ZST>) }
+ }
+
+ bb1: {
++ StorageLive(_1); // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ _2 = Lt(_0, const 1_usize); // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ assert(move _2, "index out of bounds: the length is {} but the index is {}", const 1_usize, _0) -> bb2; // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ }
++
++ bb2: {
++ _1 = (*_3)[_0]; // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ switchInt(move _1) -> [0: bb3, otherwise: bb4]; // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ }
++
++ bb3: {
++ _0 = const 0_usize; // scope 3 at $DIR/issue_106141.rs:16:9: 16:10
++ goto -> bb4; // scope 3 at $DIR/issue_106141.rs:13:5: 17:6
++ }
++
++ bb4: {
++ StorageDead(_1); // scope 3 at $DIR/issue_106141.rs:17:5: 17:6
++ StorageDead(_3); // scope 0 at $DIR/issue_106141.rs:+1:5: +1:12
+ return; // scope 0 at $DIR/issue_106141.rs:+2:2: +2:2
+ }
+ }
+
--- /dev/null
+pub fn outer() -> usize {
+ inner()
+}
+
+fn index() -> usize {
+ loop {}
+}
+
+#[inline]
+fn inner() -> usize {
+ let buffer = &[true];
+ let index = index();
+ if buffer[index] {
+ index
+ } else {
+ 0
+ }
+}
+
+fn main() {
+ outer();
+}
+
+// EMIT_MIR issue_106141.outer.Inline.diff
--- /dev/null
+- // MIR for `generic` before InstCombine
++ // MIR for `generic` after InstCombine
+
+ fn generic() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/intrinsic_asserts.rs:+0:21: +0:21
+ let _1: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:46
+ let _2: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:47
+ let _3: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:60
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:46
+ _1 = assert_inhabited::<T>() -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:46
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:25:5: 25:44
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_inhabited::<T>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:46: +1:47
+ StorageLive(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:47
+ _2 = assert_zero_valid::<T>() -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:47
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:26:5: 26:45
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_zero_valid::<T>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:47: +2:48
+ StorageLive(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:60
+ _3 = assert_mem_uninitialized_valid::<T>() -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:60
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:27:5: 27:58
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<T>}, val: Value(<ZST>) }
+ }
+
+ bb3: {
+ StorageDead(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:60: +3:61
+ nop; // scope 0 at $DIR/intrinsic_asserts.rs:+0:21: +4:2
+ return; // scope 0 at $DIR/intrinsic_asserts.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+- // MIR for `panics` before InstCombine
++ // MIR for `panics` after InstCombine
+
+ fn panics() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/intrinsic_asserts.rs:+0:17: +0:17
+ let _1: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
+ let _2: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
+ let _3: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
+- _1 = assert_inhabited::<Never>() -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
++ _1 = assert_inhabited::<Never>(); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:17:5: 17:48
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_inhabited::<Never>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:50: +1:51
+ StorageLive(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
+- _2 = assert_zero_valid::<&u8>() -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
++ _2 = assert_zero_valid::<&u8>(); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:18:5: 18:47
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_zero_valid::<&u8>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:49: +2:50
+ StorageLive(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
+- _3 = assert_mem_uninitialized_valid::<&u8>() -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
++ _3 = assert_mem_uninitialized_valid::<&u8>(); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:19:5: 19:60
+ // + user_ty: UserType(1)
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<&u8>}, val: Value(<ZST>) }
+ }
+
+ bb3: {
+ StorageDead(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:62: +3:63
+ nop; // scope 0 at $DIR/intrinsic_asserts.rs:+0:17: +4:2
+ return; // scope 0 at $DIR/intrinsic_asserts.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+- // MIR for `removable` before InstCombine
++ // MIR for `removable` after InstCombine
+
+ fn removable() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/intrinsic_asserts.rs:+0:20: +0:20
+ let _1: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+ let _2: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+ let _3: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+- _1 = assert_inhabited::<()>() -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+- // mir::Constant
+- // + span: $DIR/intrinsic_asserts.rs:7:5: 7:45
+- // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_inhabited::<()>}, val: Value(<ZST>) }
++ goto -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:47: +1:48
+ StorageLive(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+- _2 = assert_zero_valid::<u8>() -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+- // mir::Constant
+- // + span: $DIR/intrinsic_asserts.rs:8:5: 8:46
+- // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_zero_valid::<u8>}, val: Value(<ZST>) }
++ goto -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+ }
+
+ bb2: {
+ StorageDead(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:48: +2:49
+ StorageLive(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+- _3 = assert_mem_uninitialized_valid::<u8>() -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+- // mir::Constant
+- // + span: $DIR/intrinsic_asserts.rs:9:5: 9:59
+- // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<u8>}, val: Value(<ZST>) }
++ goto -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+ }
+
+ bb3: {
+ StorageDead(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:61: +3:62
+ nop; // scope 0 at $DIR/intrinsic_asserts.rs:+0:20: +4:2
+ return; // scope 0 at $DIR/intrinsic_asserts.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+// All these assertions pass, so all the intrinsic calls should be deleted.
+// EMIT_MIR intrinsic_asserts.removable.InstCombine.diff
+pub fn removable() {
+ core::intrinsics::assert_inhabited::<()>();
+ core::intrinsics::assert_zero_valid::<u8>();
+ core::intrinsics::assert_mem_uninitialized_valid::<u8>();
+}
+
+enum Never {}
+
+// These assertions all diverge, so their target blocks should become None.
+// EMIT_MIR intrinsic_asserts.panics.InstCombine.diff
+pub fn panics() {
+ core::intrinsics::assert_inhabited::<Never>();
+ core::intrinsics::assert_zero_valid::<&u8>();
+ core::intrinsics::assert_mem_uninitialized_valid::<&u8>();
+}
+
+// Whether or not these asserts pass isn't known, so they shouldn't be modified.
+// EMIT_MIR intrinsic_asserts.generic.InstCombine.diff
+pub fn generic<T>() {
+ core::intrinsics::assert_inhabited::<T>();
+ core::intrinsics::assert_zero_valid::<T>();
+ core::intrinsics::assert_mem_uninitialized_valid::<T>();
+}
let mut _10: (u32, bool); // in scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
let mut _11: (u32, bool); // in scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
scope 1 (inlined imm8) { // at $DIR/issue_101973.rs:14:5: 14:17
- debug x => _5; // in scope 1 at $DIR/issue_101973.rs:5:13: 5:14
+ debug x => _1; // in scope 1 at $DIR/issue_101973.rs:5:13: 5:14
let mut _12: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:27
let mut _13: u32; // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20
let mut _14: (u32, bool); // in scope 1 at $DIR/issue_101973.rs:7:12: 7:20
StorageLive(_2); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:65
StorageLive(_3); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:58
StorageLive(_4); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:17
- StorageLive(_5); // scope 0 at $DIR/issue_101973.rs:+1:10: +1:16
- _5 = _1; // scope 0 at $DIR/issue_101973.rs:+1:10: +1:16
StorageLive(_12); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
StorageLive(_13); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
- _14 = CheckedShr(_5, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
+ _14 = CheckedShr(_1, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
assert(!move (_14.1: bool), "attempt to shift right by `{}`, which would overflow", const 0_i32) -> bb3; // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
}
bb1: {
_8 = move (_10.0: u32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
- StorageDead(_9); // scope 0 at $DIR/issue_101973.rs:+1:44: +1:45
_7 = BitAnd(move _8, const 15_u32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
StorageDead(_8); // scope 0 at $DIR/issue_101973.rs:+1:51: +1:52
_11 = CheckedShl(_7, const 1_i32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
bb2: {
_6 = move (_11.0: u32); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
StorageDead(_7); // scope 0 at $DIR/issue_101973.rs:+1:56: +1:57
- StorageLive(_15); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
- _15 = _4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
- StorageLive(_16); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
- _16 = _6; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
- _3 = rotate_right::<u32>(move _15, move _16) -> bb4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
+ _3 = rotate_right::<u32>(_4, _6) -> bb4; // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
// mir::Constant
// + span: $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
// + literal: Const { ty: extern "rust-intrinsic" fn(u32, u32) -> u32 {rotate_right::<u32>}, val: Value(<ZST>) }
StorageDead(_13); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
_4 = BitOr(const 0_u32, move _12); // scope 2 at $DIR/issue_101973.rs:7:5: 7:27
StorageDead(_12); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
- StorageDead(_5); // scope 0 at $DIR/issue_101973.rs:+1:16: +1:17
StorageLive(_6); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
StorageLive(_7); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
StorageLive(_8); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
- StorageLive(_9); // scope 0 at $DIR/issue_101973.rs:+1:33: +1:39
- _9 = _1; // scope 0 at $DIR/issue_101973.rs:+1:33: +1:39
- _10 = CheckedShr(_9, const 8_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
+ _10 = CheckedShr(_1, const 8_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
assert(!move (_10.1: bool), "attempt to shift right by `{}`, which would overflow", const 8_i32) -> bb1; // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
}
bb4: {
- StorageDead(_16); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
- StorageDead(_15); // scope 3 at $SRC_DIR/core/src/num/uint_macros.rs:LL:COL
- StorageDead(_6); // scope 0 at $DIR/issue_101973.rs:+1:57: +1:58
- StorageDead(_4); // scope 0 at $DIR/issue_101973.rs:+1:57: +1:58
_2 = move _3 as i32 (IntToInt); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:65
StorageDead(_3); // scope 0 at $DIR/issue_101973.rs:+1:64: +1:65
_0 = move _2 as i64 (IntToInt); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:72
+++ /dev/null
-- // MIR for `main` before SimplifyArmIdentity
-+ // MIR for `main` after SimplifyArmIdentity
-
- fn main() -> () {
- let mut _0: (); // return place in scope 0 at $DIR/issue_73223.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/issue_73223.rs:+1:9: +1:14
- let mut _2: std::option::Option<i32>; // in scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- let mut _3: isize; // in scope 0 at $DIR/issue_73223.rs:+2:9: +2:16
- let _4: i32; // in scope 0 at $DIR/issue_73223.rs:+2:14: +2:15
- let mut _6: i32; // in scope 0 at $DIR/issue_73223.rs:+6:22: +6:27
- let mut _7: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _8: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _11: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _12: bool; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _13: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _14: i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _16: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _17: core::panicking::AssertKind; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _18: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _19: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _20: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _21: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _22: std::option::Option<std::fmt::Arguments<'_>>; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _24: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _25: &i32; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 1 {
- debug split => _1; // in scope 1 at $DIR/issue_73223.rs:+1:9: +1:14
- let _5: std::option::Option<i32>; // in scope 1 at $DIR/issue_73223.rs:+6:9: +6:14
- scope 3 {
- debug _prev => _5; // in scope 3 at $DIR/issue_73223.rs:+6:9: +6:14
- let _9: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _10: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let mut _23: &i32; // in scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 4 {
- debug left_val => _9; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- debug right_val => _10; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- let _15: core::panicking::AssertKind; // in scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- scope 5 {
- debug kind => _15; // in scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
- }
- }
- }
- scope 2 {
- debug v => _4; // in scope 2 at $DIR/issue_73223.rs:+2:14: +2:15
- }
-
- bb0: {
- StorageLive(_1); // scope 0 at $DIR/issue_73223.rs:+1:9: +1:14
- StorageLive(_2); // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- Deinit(_2); // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- ((_2 as Some).0: i32) = const 1_i32; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- discriminant(_2) = 1; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- _3 = const 1_isize; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- goto -> bb3; // scope 0 at $DIR/issue_73223.rs:+1:17: +1:30
- }
-
- bb1: {
- StorageDead(_2); // scope 0 at $DIR/issue_73223.rs:+4:6: +4:7
- StorageDead(_1); // scope 0 at $DIR/issue_73223.rs:+8:1: +8:2
- return; // scope 0 at $DIR/issue_73223.rs:+8:2: +8:2
- }
-
- bb2: {
- unreachable; // scope 0 at $DIR/issue_73223.rs:+1:23: +1:30
- }
-
- bb3: {
- StorageLive(_4); // scope 0 at $DIR/issue_73223.rs:+2:14: +2:15
- _4 = ((_2 as Some).0: i32); // scope 0 at $DIR/issue_73223.rs:+2:14: +2:15
- _1 = _4; // scope 2 at $DIR/issue_73223.rs:+2:20: +2:21
- StorageDead(_4); // scope 0 at $DIR/issue_73223.rs:+2:20: +2:21
- StorageDead(_2); // scope 0 at $DIR/issue_73223.rs:+4:6: +4:7
- StorageLive(_5); // scope 1 at $DIR/issue_73223.rs:+6:9: +6:14
- StorageLive(_6); // scope 1 at $DIR/issue_73223.rs:+6:22: +6:27
- _6 = _1; // scope 1 at $DIR/issue_73223.rs:+6:22: +6:27
- Deinit(_5); // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28
- ((_5 as Some).0: i32) = move _6; // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28
- discriminant(_5) = 1; // scope 1 at $DIR/issue_73223.rs:+6:17: +6:28
- StorageDead(_6); // scope 1 at $DIR/issue_73223.rs:+6:27: +6:28
- StorageLive(_24); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_25); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _7 = &_1; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _23 = const _; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: &i32, val: Unevaluated(main, [], Some(promoted[0])) }
- _8 = _23; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_24); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_25); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _24 = move _7; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _25 = move _8; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_8); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_7); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _9 = _24; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _10 = _25; // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _13 = (*_9); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_14); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _14 = const 1_i32; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _12 = Eq(move _13, const 1_i32); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_14); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_13); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _11 = Not(move _12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_12); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- switchInt(move _11) -> [0: bb5, otherwise: bb4]; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- }
-
- bb4: {
- StorageLive(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_15); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- discriminant(_15) = 0; // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_16); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_17); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _17 = const core::panicking::AssertKind::Eq; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) }
- StorageLive(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_19); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _19 = _9; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _18 = _19; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_20); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_21); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _21 = _10; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _20 = _21; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageLive(_22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- discriminant(_22) = 0; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- _16 = core::panicking::assert_failed::<i32, i32>(const core::panicking::AssertKind::Eq, move _18, move _20, move _22); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: for<'a, 'b, 'c> fn(core::panicking::AssertKind, &'a i32, &'b i32, Option<Arguments<'c>>) -> ! {core::panicking::assert_failed::<i32, i32>}, val: Value(<ZST>) }
- // mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
- // + literal: Const { ty: core::panicking::AssertKind, val: Value(Scalar(0x00)) }
- }
-
- bb5: {
- StorageDead(_11); // scope 4 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_10); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_9); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_24); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_25); // scope 3 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- StorageDead(_5); // scope 1 at $DIR/issue_73223.rs:+8:1: +8:2
- StorageDead(_1); // scope 0 at $DIR/issue_73223.rs:+8:1: +8:2
- return; // scope 0 at $DIR/issue_73223.rs:+8:2: +8:2
- }
- }
-
+++ /dev/null
-fn main() {
- let split = match Some(1) {
- Some(v) => v,
- None => return,
- };
-
- let _prev = Some(split);
- assert_eq!(split, 1);
-}
-
-
-// EMIT_MIR issue_73223.main.SimplifyArmIdentity.diff
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue_76432.rs:+1:9: +1:10
- StorageLive(_3); // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
StorageLive(_4); // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
StorageLive(_5); // scope 0 at $DIR/issue_76432.rs:+1:20: +1:29
- StorageLive(_6); // scope 0 at $DIR/issue_76432.rs:+1:21: +1:22
- _6 = _1; // scope 0 at $DIR/issue_76432.rs:+1:21: +1:22
- StorageLive(_7); // scope 0 at $DIR/issue_76432.rs:+1:24: +1:25
- _7 = _1; // scope 0 at $DIR/issue_76432.rs:+1:24: +1:25
- StorageLive(_8); // scope 0 at $DIR/issue_76432.rs:+1:27: +1:28
- _8 = _1; // scope 0 at $DIR/issue_76432.rs:+1:27: +1:28
- _5 = [move _6, move _7, move _8]; // scope 0 at $DIR/issue_76432.rs:+1:20: +1:29
- StorageDead(_8); // scope 0 at $DIR/issue_76432.rs:+1:28: +1:29
- StorageDead(_7); // scope 0 at $DIR/issue_76432.rs:+1:28: +1:29
- StorageDead(_6); // scope 0 at $DIR/issue_76432.rs:+1:28: +1:29
+ _5 = [_1, _1, _1]; // scope 0 at $DIR/issue_76432.rs:+1:20: +1:29
_4 = &_5; // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
- _3 = _4; // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
- _2 = move _3 as &[T] (Pointer(Unsize)); // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
- StorageDead(_3); // scope 0 at $DIR/issue_76432.rs:+1:28: +1:29
- StorageDead(_4); // scope 0 at $DIR/issue_76432.rs:+1:29: +1:30
+ _2 = _4 as &[T] (Pointer(Unsize)); // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
_9 = Len((*_2)); // scope 1 at $DIR/issue_76432.rs:+3:9: +3:33
_10 = const 3_usize; // scope 1 at $DIR/issue_76432.rs:+3:9: +3:33
- _11 = Eq(move _9, const 3_usize); // scope 1 at $DIR/issue_76432.rs:+3:9: +3:33
_3 = _1; // scope 2 at $DIR/issue_75439.rs:+2:47: +2:52
_2 = transmute::<[u8; 16], [u32; 4]>(move _3) -> bb1; // scope 2 at $DIR/issue_75439.rs:+2:37: +2:53
// mir::Constant
- // + span: $DIR/issue_75439.rs:7:37: 7:46
+ // + span: $DIR/issue_75439.rs:8:37: 8:46
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn([u8; 16]) -> [u32; 4] {transmute::<[u8; 16], [u32; 4]>}, val: Value(<ZST>) }
}
_6 = _4; // scope 4 at $DIR/issue_75439.rs:+5:33: +5:35
_5 = transmute::<u32, [u8; 4]>(move _6) -> bb7; // scope 4 at $DIR/issue_75439.rs:+5:23: +5:36
// mir::Constant
- // + span: $DIR/issue_75439.rs:10:23: 10:32
+ // + span: $DIR/issue_75439.rs:11:23: 11:32
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) -> [u8; 4] {transmute::<u32, [u8; 4]>}, val: Value(<ZST>) }
}
// EMIT_MIR issue_75439.foo.MatchBranchSimplification.diff
+// ignore-endian-big
use std::mem::transmute;
debug slice => _2; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:50: +0:55
let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:70: +0:72
let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
- let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
- let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
- let mut _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- let mut _7: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let mut _6: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
bb0: {
StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
- StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
- _4 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
- StorageLive(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
- _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
- _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
- StorageDead(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
+ StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _4 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _3 = Lt(_1, move _4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
switchInt(move _3) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
}
bb1: {
- _6 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- _7 = Lt(_1, _6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ _6 = Lt(_1, _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
}
bb2: {
debug slice => _2; // in scope 0 at $DIR/lower_array_len_e2e.rs:+0:54: +0:59
let mut _0: u8; // return place in scope 0 at $DIR/lower_array_len_e2e.rs:+0:78: +0:80
let mut _3: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
- let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
- let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
- let mut _6: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- let mut _7: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- let _8: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
- let mut _9: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
- let mut _10: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ let mut _4: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ let mut _5: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let mut _6: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ let _7: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
+ let mut _8: usize; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ let mut _9: bool; // in scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
bb0: {
StorageLive(_3); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
- StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
- _4 = _1; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:13
- StorageLive(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
- _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
- _3 = Lt(move _4, move _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
- StorageDead(_5); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
+ StorageLive(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _4 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:16: +1:27
+ _3 = Lt(_1, move _4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
StorageDead(_4); // scope 0 at $DIR/lower_array_len_e2e.rs:+1:26: +1:27
switchInt(move _3) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:8: +1:27
}
bb1: {
- _6 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- _7 = Lt(_1, _6); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
- assert(move _7, "index out of bounds: the length is {} but the index is {}", move _6, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ _5 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ _6 = Lt(_1, _5); // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
+ assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _1) -> bb2; // scope 0 at $DIR/lower_array_len_e2e.rs:+2:9: +2:21
}
bb2: {
}
bb3: {
- StorageLive(_8); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
- _8 = const 0_usize; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
- _9 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
- _10 = Lt(const 0_usize, _9); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
- assert(move _10, "index out of bounds: the length is {} but the index is {}", move _9, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ StorageLive(_7); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
+ _7 = const 0_usize; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:15: +4:16
+ _8 = const N; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ _9 = Lt(const 0_usize, _8); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
+ assert(move _9, "index out of bounds: the length is {} but the index is {}", move _8, const 0_usize) -> bb4; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:17
}
bb4: {
- (*_2)[_8] = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:22
- StorageDead(_8); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:22: +4:23
+ (*_2)[_7] = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+4:9: +4:22
+ StorageDead(_7); // scope 0 at $DIR/lower_array_len_e2e.rs:+4:22: +4:23
_0 = const 42_u8; // scope 0 at $DIR/lower_array_len_e2e.rs:+6:9: +6:11
goto -> bb5; // scope 0 at $DIR/lower_array_len_e2e.rs:+1:5: +7:6
}
|
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/region_subtyping_basic.rs:+0:11: +0:11
- let mut _1: [usize; Const { ty: usize, kind: Value(Leaf(0x00000003)) }]; // in scope 0 at $DIR/region_subtyping_basic.rs:+1:9: +1:14
+ let mut _1: [usize; Const(Value(Leaf(0x00000003)): usize)]; // in scope 0 at $DIR/region_subtyping_basic.rs:+1:9: +1:14
let _3: usize; // in scope 0 at $DIR/region_subtyping_basic.rs:+2:16: +2:17
let mut _4: usize; // in scope 0 at $DIR/region_subtyping_basic.rs:+2:14: +2:18
let mut _5: bool; // in scope 0 at $DIR/region_subtyping_basic.rs:+2:14: +2:18
|
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/region_subtyping_basic.rs:+0:11: +0:11
- let mut _1: [usize; Const { ty: usize, kind: Value(Leaf(0x0000000000000003)) }]; // in scope 0 at $DIR/region_subtyping_basic.rs:+1:9: +1:14
+ let mut _1: [usize; Const(Value(Leaf(0x0000000000000003)): usize)]; // in scope 0 at $DIR/region_subtyping_basic.rs:+1:9: +1:14
let _3: usize; // in scope 0 at $DIR/region_subtyping_basic.rs:+2:16: +2:17
let mut _4: usize; // in scope 0 at $DIR/region_subtyping_basic.rs:+2:14: +2:18
let mut _5: bool; // in scope 0 at $DIR/region_subtyping_basic.rs:+2:14: +2:18
bb3: {
_5 = move ((_1 as Some).0: i32); // scope 1 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
StorageLive(_4); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- _4 = Add(move _5, const 1_i32); // scope 3 at $DIR/simple_option_map_e2e.rs:+1:16: +1:21
+ _4 = Add(_5, const 1_i32); // scope 3 at $DIR/simple_option_map_e2e.rs:+1:16: +1:21
Deinit(_0); // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
((_0 as Some).0: i32) = move _4; // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
discriminant(_0) = 1; // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/simplify_match.rs:+1:11: +1:31
StorageLive(_2); // scope 0 at $DIR/simplify_match.rs:+1:17: +1:18
_2 = const false; // scope 0 at $DIR/simplify_match.rs:+1:21: +1:26
-- _1 = _2; // scope 1 at $DIR/simplify_match.rs:+1:28: +1:29
-+ _1 = const false; // scope 1 at $DIR/simplify_match.rs:+1:28: +1:29
- StorageDead(_2); // scope 0 at $DIR/simplify_match.rs:+1:30: +1:31
-- switchInt(_1) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
+- switchInt(_2) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
+ switchInt(const false) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
}
}
bb3: {
- StorageDead(_1); // scope 0 at $DIR/simplify_match.rs:+5:1: +5:2
return; // scope 0 at $DIR/simplify_match.rs:+5:2: +5:2
}
}
--- /dev/null
+fn main() {
+ let input = vec![];
+ let _variant_a_result = variant_a(&input);
+ let _variant_b_result = variant_b(&input);
+}
+
+pub fn variant_a(input: &[(usize, usize, usize, usize)]) -> usize {
+ input.iter().filter(|(a, b, c, d)| a <= c && d <= b || c <= a && b <= d).count()
+}
+
+pub fn variant_b(input: &[(usize, usize, usize, usize)]) -> usize {
+ input.iter().filter(|&&(a, b, c, d)| a <= c && d <= b || c <= a && b <= d).count()
+}
+
+// EMIT_MIR slice_filter.variant_a-{closure#0}.CopyProp.diff
+// EMIT_MIR slice_filter.variant_a-{closure#0}.DestinationPropagation.diff
+// EMIT_MIR slice_filter.variant_b-{closure#0}.CopyProp.diff
+// EMIT_MIR slice_filter.variant_b-{closure#0}.DestinationPropagation.diff
--- /dev/null
+- // MIR for `variant_a::{closure#0}` before CopyProp
++ // MIR for `variant_a::{closure#0}` after CopyProp
+
+ fn variant_a::{closure#0}(_1: &mut [closure@$DIR/slice_filter.rs:8:25: 8:39], _2: &&(usize, usize, usize, usize)) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/slice_filter.rs:+0:40: +0:40
+ let _3: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ let _4: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ let _5: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ let _6: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ let mut _7: bool; // in scope 0 at $DIR/slice_filter.rs:+0:40: +0:56
+ let mut _8: bool; // in scope 0 at $DIR/slice_filter.rs:+0:40: +0:46
+ let mut _9: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:40: +0:41
+ let mut _10: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:45: +0:46
+ let _11: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:45: +0:46
+ let mut _12: bool; // in scope 0 at $DIR/slice_filter.rs:+0:50: +0:56
+ let mut _13: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:50: +0:51
+ let mut _14: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:55: +0:56
+ let _15: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:55: +0:56
+ let mut _16: bool; // in scope 0 at $DIR/slice_filter.rs:+0:60: +0:76
+ let mut _17: bool; // in scope 0 at $DIR/slice_filter.rs:+0:60: +0:66
+ let mut _18: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:60: +0:61
+ let mut _19: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:65: +0:66
+ let _20: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:65: +0:66
+ let mut _21: bool; // in scope 0 at $DIR/slice_filter.rs:+0:70: +0:76
+ let mut _22: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:70: +0:71
+ let mut _23: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+ let _24: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+ let mut _25: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ let mut _26: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ let mut _27: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ let mut _28: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ scope 1 {
+ debug a => _3; // in scope 1 at $DIR/slice_filter.rs:+0:27: +0:28
+ debug b => _4; // in scope 1 at $DIR/slice_filter.rs:+0:30: +0:31
+ debug c => _5; // in scope 1 at $DIR/slice_filter.rs:+0:33: +0:34
+ debug d => _6; // in scope 1 at $DIR/slice_filter.rs:+0:36: +0:37
+ scope 2 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:40: 8:46
+ debug self => _9; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _10; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _29: &usize; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _30: &usize; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _31: &usize; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _32: &usize; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 3 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug self => _29; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug other => _30; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug self => _31; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug other => _32; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _33: usize; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _34: usize; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ scope 4 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:60: 8:66
+ debug self => _18; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _19; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _35: &usize; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _36: &usize; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _37: &usize; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _38: &usize; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 5 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug self => _35; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug other => _36; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug self => _37; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug other => _38; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _39: usize; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _40: usize; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ scope 6 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:50: 8:56
+ debug self => _13; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _14; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _41: &usize; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _42: &usize; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _43: &usize; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _44: &usize; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 7 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug self => _41; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug other => _42; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug self => _43; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug other => _44; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _45: usize; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _46: usize; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ scope 8 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:70: 8:76
+ debug self => _22; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _23; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _47: &usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _48: &usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _49: &usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _50: &usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 9 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug self => _47; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- debug other => _48; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug self => _49; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ debug other => _50; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _51: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _52: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ _25 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ _3 = &((*_25).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ _26 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ _4 = &((*_26).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ _27 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ _5 = &((*_27).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ _28 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ _6 = &((*_28).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ StorageLive(_8); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:46
+ StorageLive(_9); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:41
+ _9 = &_3; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:41
+ StorageLive(_10); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ StorageLive(_11); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ _11 = _5; // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ _10 = &_11; // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+- StorageLive(_29); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _31 = deref_copy (*_9); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _29 = _31; // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageLive(_30); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _32 = deref_copy (*_10); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _30 = _32; // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_33); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _33 = (*_29); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _33 = (*_31); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_34); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _34 = (*_30); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _34 = (*_32); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _8 = Le(move _33, move _34); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_34); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_33); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_30); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_29); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_11); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ StorageDead(_10); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ StorageDead(_9); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ switchInt(move _8) -> [0: bb4, otherwise: bb5]; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ }
+
+ bb1: {
+ _0 = const true; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ }
+
+ bb2: {
+ StorageLive(_16); // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ StorageLive(_17); // scope 1 at $DIR/slice_filter.rs:+0:60: +0:66
+ StorageLive(_18); // scope 1 at $DIR/slice_filter.rs:+0:60: +0:61
+ _18 = &_5; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:61
+ StorageLive(_19); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ StorageLive(_20); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ _20 = _3; // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ _19 = &_20; // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+- StorageLive(_35); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _37 = deref_copy (*_18); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _35 = _37; // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageLive(_36); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _38 = deref_copy (*_19); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _36 = _38; // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_39); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _39 = (*_35); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _39 = (*_37); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_40); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _40 = (*_36); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _40 = (*_38); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _17 = Le(move _39, move _40); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_40); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_39); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_36); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_35); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_20); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ StorageDead(_19); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ StorageDead(_18); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ switchInt(move _17) -> [0: bb6, otherwise: bb7]; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ }
+
+ bb3: {
+ StorageDead(_16); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_7); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+- StorageDead(_6); // scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+- StorageDead(_5); // scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+- StorageDead(_4); // scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+- StorageDead(_3); // scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+ return; // scope 0 at $DIR/slice_filter.rs:+0:76: +0:76
+ }
+
+ bb4: {
+ _7 = const false; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ StorageDead(_12); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ goto -> bb2; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ }
+
+ bb5: {
+ StorageLive(_12); // scope 1 at $DIR/slice_filter.rs:+0:50: +0:56
+ StorageLive(_13); // scope 1 at $DIR/slice_filter.rs:+0:50: +0:51
+ _13 = &_6; // scope 1 at $DIR/slice_filter.rs:+0:50: +0:51
+ StorageLive(_14); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageLive(_15); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ _15 = _4; // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ _14 = &_15; // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+- StorageLive(_41); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _43 = deref_copy (*_13); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _41 = _43; // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageLive(_42); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _44 = deref_copy (*_14); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _42 = _44; // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_45); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _45 = (*_41); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _45 = (*_43); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_46); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _46 = (*_42); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _46 = (*_44); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _12 = Le(move _45, move _46); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_46); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_45); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_42); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_41); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_15); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_14); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_13); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ _7 = move _12; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ StorageDead(_12); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ switchInt(move _7) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ }
+
+ bb6: {
+ _16 = const false; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ }
+
+ bb7: {
+ StorageLive(_21); // scope 1 at $DIR/slice_filter.rs:+0:70: +0:76
+ StorageLive(_22); // scope 1 at $DIR/slice_filter.rs:+0:70: +0:71
+ _22 = &_4; // scope 1 at $DIR/slice_filter.rs:+0:70: +0:71
+ StorageLive(_23); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageLive(_24); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _24 = _6; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _23 = &_24; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+- StorageLive(_47); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _49 = deref_copy (*_22); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _47 = _49; // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageLive(_48); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _50 = deref_copy (*_23); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _48 = _50; // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_51); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _51 = (*_47); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _51 = (*_49); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_52); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _52 = (*_48); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _52 = (*_50); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _21 = Le(move _51, move _52); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_52); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_51); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_48); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- StorageDead(_47); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_24); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_23); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_22); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _16 = move _21; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ }
+
+ bb8: {
+ StorageDead(_21); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_17); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _0 = move _16; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ }
+ }
+
--- /dev/null
+- // MIR for `variant_a::{closure#0}` before DestinationPropagation
++ // MIR for `variant_a::{closure#0}` after DestinationPropagation
+
+ fn variant_a::{closure#0}(_1: &mut [closure@$DIR/slice_filter.rs:8:25: 8:39], _2: &&(usize, usize, usize, usize)) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/slice_filter.rs:+0:40: +0:40
+ let _3: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ let _4: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ let _5: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ let _6: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ let mut _7: bool; // in scope 0 at $DIR/slice_filter.rs:+0:40: +0:56
+ let mut _8: bool; // in scope 0 at $DIR/slice_filter.rs:+0:40: +0:46
+ let mut _9: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:40: +0:41
+ let mut _10: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:45: +0:46
+ let _11: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:45: +0:46
+ let mut _12: bool; // in scope 0 at $DIR/slice_filter.rs:+0:50: +0:56
+ let mut _13: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:50: +0:51
+ let mut _14: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:55: +0:56
+ let _15: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:55: +0:56
+ let mut _16: bool; // in scope 0 at $DIR/slice_filter.rs:+0:60: +0:76
+ let mut _17: bool; // in scope 0 at $DIR/slice_filter.rs:+0:60: +0:66
+ let mut _18: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:60: +0:61
+ let mut _19: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:65: +0:66
+ let _20: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:65: +0:66
+ let mut _21: bool; // in scope 0 at $DIR/slice_filter.rs:+0:70: +0:76
+ let mut _22: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:70: +0:71
+ let mut _23: &&usize; // in scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+ let _24: &usize; // in scope 0 at $DIR/slice_filter.rs:+0:75: +0:76
+ let mut _25: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ let mut _26: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ let mut _27: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ let mut _28: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:38
+ scope 1 {
+ debug a => _3; // in scope 1 at $DIR/slice_filter.rs:+0:27: +0:28
+ debug b => _4; // in scope 1 at $DIR/slice_filter.rs:+0:30: +0:31
+ debug c => _5; // in scope 1 at $DIR/slice_filter.rs:+0:33: +0:34
+ debug d => _6; // in scope 1 at $DIR/slice_filter.rs:+0:36: +0:37
+ scope 2 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:40: 8:46
+ debug self => _9; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _10; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _29: &usize; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _30: &usize; // in scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 3 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug self => _29; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _30; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _31: usize; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _32: usize; // in scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ scope 4 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:60: 8:66
+ debug self => _18; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _19; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _33: &usize; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _34: &usize; // in scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 5 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug self => _33; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _34; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _35: usize; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _36: usize; // in scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ scope 6 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:50: 8:56
+ debug self => _13; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _14; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _37: &usize; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _38: &usize; // in scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 7 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug self => _37; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _38; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _39: usize; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _40: usize; // in scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ scope 8 (inlined cmp::impls::<impl PartialOrd for &usize>::le) { // at $DIR/slice_filter.rs:8:70: 8:76
+ debug self => _22; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _23; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _41: &usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _42: &usize; // in scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ scope 9 (inlined cmp::impls::<impl PartialOrd for usize>::le) { // at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug self => _41; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ debug other => _42; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _43: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ let mut _44: usize; // in scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ _25 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ _3 = &((*_25).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+ StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ _26 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ _4 = &((*_26).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+ StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ _27 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ _5 = &((*_27).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+ StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ _28 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+ _6 = &((*_28).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+- StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ StorageLive(_8); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:46
+ StorageLive(_9); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:41
+ _9 = &_3; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:41
+ StorageLive(_10); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ StorageLive(_11); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ _11 = _5; // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ _10 = &_11; // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ _29 = deref_copy (*_9); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _30 = deref_copy (*_10); // scope 2 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_31); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _31 = (*_29); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_32); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _32 = (*_30); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _8 = Le(move _31, move _32); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_32); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_31); // scope 3 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_11); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ StorageDead(_10); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ StorageDead(_9); // scope 1 at $DIR/slice_filter.rs:+0:45: +0:46
+ switchInt(move _8) -> [0: bb4, otherwise: bb5]; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ }
+
+ bb1: {
+ _0 = const true; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ }
+
+ bb2: {
+- StorageLive(_16); // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ StorageLive(_17); // scope 1 at $DIR/slice_filter.rs:+0:60: +0:66
+ StorageLive(_18); // scope 1 at $DIR/slice_filter.rs:+0:60: +0:61
+ _18 = &_5; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:61
+ StorageLive(_19); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ StorageLive(_20); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ _20 = _3; // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ _19 = &_20; // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ _33 = deref_copy (*_18); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _34 = deref_copy (*_19); // scope 4 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_35); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _35 = (*_33); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_36); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _36 = (*_34); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _17 = Le(move _35, move _36); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_36); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_35); // scope 5 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_20); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ StorageDead(_19); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ StorageDead(_18); // scope 1 at $DIR/slice_filter.rs:+0:65: +0:66
+ switchInt(move _17) -> [0: bb6, otherwise: bb7]; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ }
+
+ bb3: {
+- StorageDead(_16); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+- StorageDead(_7); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ return; // scope 0 at $DIR/slice_filter.rs:+0:76: +0:76
+ }
+
+ bb4: {
+- StorageDead(_12); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ goto -> bb2; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+ }
+
+ bb5: {
+- StorageLive(_12); // scope 1 at $DIR/slice_filter.rs:+0:50: +0:56
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:50: +0:56
+ StorageLive(_13); // scope 1 at $DIR/slice_filter.rs:+0:50: +0:51
+ _13 = &_6; // scope 1 at $DIR/slice_filter.rs:+0:50: +0:51
+ StorageLive(_14); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageLive(_15); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ _15 = _4; // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ _14 = &_15; // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ _37 = deref_copy (*_13); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _38 = deref_copy (*_14); // scope 6 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_39); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _39 = (*_37); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_40); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _40 = (*_38); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _12 = Le(move _39, move _40); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_40); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_39); // scope 7 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_15); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_14); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_13); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+- _7 = move _12; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
+- StorageDead(_12); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:55: +0:56
+- switchInt(move _7) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
++ switchInt(move _12) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ }
+
+ bb6: {
+- _16 = const false; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
++ _0 = const false; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ }
+
+ bb7: {
+- StorageLive(_21); // scope 1 at $DIR/slice_filter.rs:+0:70: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:70: +0:76
+ StorageLive(_22); // scope 1 at $DIR/slice_filter.rs:+0:70: +0:71
+ _22 = &_4; // scope 1 at $DIR/slice_filter.rs:+0:70: +0:71
+ StorageLive(_23); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageLive(_24); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _24 = _6; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _23 = &_24; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ _41 = deref_copy (*_22); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _42 = deref_copy (*_23); // scope 8 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_43); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _43 = (*_41); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageLive(_44); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ _44 = (*_42); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+- _21 = Le(move _43, move _44); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
++ _0 = Le(move _43, move _44); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_44); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_43); // scope 9 at $SRC_DIR/core/src/cmp.rs:LL:COL
+ StorageDead(_24); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_23); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_22); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+- _16 = move _21; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:60: +0:76
+ }
+
+ bb8: {
+- StorageDead(_21); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+ StorageDead(_17); // scope 1 at $DIR/slice_filter.rs:+0:75: +0:76
+- _0 = move _16; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:40: +0:76
+ }
+ }
+
--- /dev/null
+- // MIR for `variant_b::{closure#0}` before CopyProp
++ // MIR for `variant_b::{closure#0}` after CopyProp
+
+ fn variant_b::{closure#0}(_1: &mut [closure@$DIR/slice_filter.rs:12:25: 12:41], _2: &&(usize, usize, usize, usize)) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/slice_filter.rs:+0:42: +0:42
+ let _3: usize; // in scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ let _4: usize; // in scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ let _5: usize; // in scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ let _6: usize; // in scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ let mut _7: bool; // in scope 0 at $DIR/slice_filter.rs:+0:42: +0:58
+ let mut _8: bool; // in scope 0 at $DIR/slice_filter.rs:+0:42: +0:48
+ let mut _9: usize; // in scope 0 at $DIR/slice_filter.rs:+0:42: +0:43
+ let mut _10: usize; // in scope 0 at $DIR/slice_filter.rs:+0:47: +0:48
+ let mut _11: bool; // in scope 0 at $DIR/slice_filter.rs:+0:52: +0:58
+ let mut _12: usize; // in scope 0 at $DIR/slice_filter.rs:+0:52: +0:53
+ let mut _13: usize; // in scope 0 at $DIR/slice_filter.rs:+0:57: +0:58
+ let mut _14: bool; // in scope 0 at $DIR/slice_filter.rs:+0:62: +0:78
+ let mut _15: bool; // in scope 0 at $DIR/slice_filter.rs:+0:62: +0:68
+ let mut _16: usize; // in scope 0 at $DIR/slice_filter.rs:+0:62: +0:63
+ let mut _17: usize; // in scope 0 at $DIR/slice_filter.rs:+0:67: +0:68
+ let mut _18: bool; // in scope 0 at $DIR/slice_filter.rs:+0:72: +0:78
+ let mut _19: usize; // in scope 0 at $DIR/slice_filter.rs:+0:72: +0:73
+ let mut _20: usize; // in scope 0 at $DIR/slice_filter.rs:+0:77: +0:78
+ let mut _21: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ let mut _22: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ let mut _23: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ let mut _24: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ scope 1 {
+ debug a => _3; // in scope 1 at $DIR/slice_filter.rs:+0:29: +0:30
+ debug b => _4; // in scope 1 at $DIR/slice_filter.rs:+0:32: +0:33
+ debug c => _5; // in scope 1 at $DIR/slice_filter.rs:+0:35: +0:36
+ debug d => _6; // in scope 1 at $DIR/slice_filter.rs:+0:38: +0:39
+ }
+
+ bb0: {
+ StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ _21 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ _3 = ((*_21).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ _22 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ _4 = ((*_22).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ _23 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ _5 = ((*_23).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ _24 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ _6 = ((*_24).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ StorageLive(_8); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:48
+- StorageLive(_9); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:43
+- _9 = _3; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:43
+- StorageLive(_10); // scope 1 at $DIR/slice_filter.rs:+0:47: +0:48
+- _10 = _5; // scope 1 at $DIR/slice_filter.rs:+0:47: +0:48
+- _8 = Le(move _9, move _10); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:48
+- StorageDead(_10); // scope 1 at $DIR/slice_filter.rs:+0:47: +0:48
+- StorageDead(_9); // scope 1 at $DIR/slice_filter.rs:+0:47: +0:48
++ _8 = Le(_3, _5); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:48
+ switchInt(move _8) -> [0: bb4, otherwise: bb5]; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ }
+
+ bb1: {
+ _0 = const true; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ }
+
+ bb2: {
+ StorageLive(_14); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ StorageLive(_15); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:68
+- StorageLive(_16); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:63
+- _16 = _5; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:63
+- StorageLive(_17); // scope 1 at $DIR/slice_filter.rs:+0:67: +0:68
+- _17 = _3; // scope 1 at $DIR/slice_filter.rs:+0:67: +0:68
+- _15 = Le(move _16, move _17); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:68
+- StorageDead(_17); // scope 1 at $DIR/slice_filter.rs:+0:67: +0:68
+- StorageDead(_16); // scope 1 at $DIR/slice_filter.rs:+0:67: +0:68
++ _15 = Le(_5, _3); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:68
+ switchInt(move _15) -> [0: bb6, otherwise: bb7]; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ }
+
+ bb3: {
+ StorageDead(_14); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+ StorageDead(_7); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+- StorageDead(_6); // scope 0 at $DIR/slice_filter.rs:+0:77: +0:78
+- StorageDead(_5); // scope 0 at $DIR/slice_filter.rs:+0:77: +0:78
+- StorageDead(_4); // scope 0 at $DIR/slice_filter.rs:+0:77: +0:78
+- StorageDead(_3); // scope 0 at $DIR/slice_filter.rs:+0:77: +0:78
+ return; // scope 0 at $DIR/slice_filter.rs:+0:78: +0:78
+ }
+
+ bb4: {
+ _7 = const false; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ StorageDead(_11); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ goto -> bb2; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ }
+
+ bb5: {
+ StorageLive(_11); // scope 1 at $DIR/slice_filter.rs:+0:52: +0:58
+- StorageLive(_12); // scope 1 at $DIR/slice_filter.rs:+0:52: +0:53
+- _12 = _6; // scope 1 at $DIR/slice_filter.rs:+0:52: +0:53
+- StorageLive(_13); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+- _13 = _4; // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+- _11 = Le(move _12, move _13); // scope 1 at $DIR/slice_filter.rs:+0:52: +0:58
+- StorageDead(_13); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+- StorageDead(_12); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
++ _11 = Le(_6, _4); // scope 1 at $DIR/slice_filter.rs:+0:52: +0:58
+ _7 = move _11; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ StorageDead(_11); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ switchInt(move _7) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ }
+
+ bb6: {
+ _14 = const false; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ }
+
+ bb7: {
+ StorageLive(_18); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
+- StorageLive(_19); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:73
+- _19 = _4; // scope 1 at $DIR/slice_filter.rs:+0:72: +0:73
+- StorageLive(_20); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+- _20 = _6; // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+- _18 = Le(move _19, move _20); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
+- StorageDead(_20); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+- StorageDead(_19); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
++ _18 = Le(_4, _6); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
+ _14 = move _18; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ }
+
+ bb8: {
+ StorageDead(_18); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+ StorageDead(_15); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+ _0 = move _14; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ }
+ }
+
--- /dev/null
+- // MIR for `variant_b::{closure#0}` before DestinationPropagation
++ // MIR for `variant_b::{closure#0}` after DestinationPropagation
+
+ fn variant_b::{closure#0}(_1: &mut [closure@$DIR/slice_filter.rs:12:25: 12:41], _2: &&(usize, usize, usize, usize)) -> bool {
+ let mut _0: bool; // return place in scope 0 at $DIR/slice_filter.rs:+0:42: +0:42
+ let _3: usize; // in scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ let _4: usize; // in scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ let _5: usize; // in scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ let _6: usize; // in scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ let mut _7: bool; // in scope 0 at $DIR/slice_filter.rs:+0:42: +0:58
+ let mut _8: bool; // in scope 0 at $DIR/slice_filter.rs:+0:42: +0:48
+ let mut _9: bool; // in scope 0 at $DIR/slice_filter.rs:+0:52: +0:58
+ let mut _10: bool; // in scope 0 at $DIR/slice_filter.rs:+0:62: +0:78
+ let mut _11: bool; // in scope 0 at $DIR/slice_filter.rs:+0:62: +0:68
+ let mut _12: bool; // in scope 0 at $DIR/slice_filter.rs:+0:72: +0:78
+ let mut _13: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ let mut _14: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ let mut _15: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ let mut _16: &(usize, usize, usize, usize); // in scope 0 at $DIR/slice_filter.rs:+0:26: +0:40
+ scope 1 {
+ debug a => _3; // in scope 1 at $DIR/slice_filter.rs:+0:29: +0:30
+ debug b => _4; // in scope 1 at $DIR/slice_filter.rs:+0:32: +0:33
+ debug c => _5; // in scope 1 at $DIR/slice_filter.rs:+0:35: +0:36
+ debug d => _6; // in scope 1 at $DIR/slice_filter.rs:+0:38: +0:39
+ }
+
+ bb0: {
+ StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ _13 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ _3 = ((*_13).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+ StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ _14 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ _4 = ((*_14).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+ StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ _15 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ _5 = ((*_15).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+ StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ _16 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+ _6 = ((*_16).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+- StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ StorageLive(_8); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:48
+ _8 = Le(_3, _5); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:48
+ switchInt(move _8) -> [0: bb4, otherwise: bb5]; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ }
+
+ bb1: {
+ _0 = const true; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ }
+
+ bb2: {
+- StorageLive(_10); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ StorageLive(_11); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:68
+ _11 = Le(_5, _3); // scope 1 at $DIR/slice_filter.rs:+0:62: +0:68
+ switchInt(move _11) -> [0: bb6, otherwise: bb7]; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ }
+
+ bb3: {
+- StorageDead(_10); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+- StorageDead(_7); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+ return; // scope 0 at $DIR/slice_filter.rs:+0:78: +0:78
+ }
+
+ bb4: {
+- StorageDead(_9); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ goto -> bb2; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+ }
+
+ bb5: {
+- StorageLive(_9); // scope 1 at $DIR/slice_filter.rs:+0:52: +0:58
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:52: +0:58
+ _9 = Le(_6, _4); // scope 1 at $DIR/slice_filter.rs:+0:52: +0:58
+- _7 = move _9; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
+- StorageDead(_9); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+ StorageDead(_8); // scope 1 at $DIR/slice_filter.rs:+0:57: +0:58
+- switchInt(move _7) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
++ switchInt(move _9) -> [0: bb2, otherwise: bb1]; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ }
+
+ bb6: {
+- _10 = const false; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
++ _0 = const false; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ }
+
+ bb7: {
+- StorageLive(_12); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
+- _12 = Le(_4, _6); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
+- _10 = move _12; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
++ _0 = Le(_4, _6); // scope 1 at $DIR/slice_filter.rs:+0:72: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ goto -> bb8; // scope 1 at $DIR/slice_filter.rs:+0:62: +0:78
+ }
+
+ bb8: {
+- StorageDead(_12); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+ StorageDead(_11); // scope 1 at $DIR/slice_filter.rs:+0:77: +0:78
+- _0 = move _10; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
++ nop; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ goto -> bb3; // scope 1 at $DIR/slice_filter.rs:+0:42: +0:78
+ }
+ }
+
let mut _0: std::result::Result<T, E>; // return place in scope 0 at $DIR/try_identity_e2e.rs:+0:34: +0:46
let mut _2: std::ops::ControlFlow<E, T>; // in scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
let mut _3: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:22
- let mut _4: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:48: +4:49
- let mut _5: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:46: +5:47
+ let _4: T; // in scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
+ let _5: E; // in scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
let mut _6: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+8:13: +8:37
let _7: T; // in scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
- let mut _8: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:49: +9:50
+ let _8: E; // in scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
scope 1 {
debug v => _4; // in scope 1 at $DIR/try_identity_e2e.rs:+4:20: +4:21
}
}
bb1: {
+ StorageLive(_5); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
_5 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
Deinit(_2); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
((_2 as Break).0: E) = move _5; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
}
bb2: {
+ StorageLive(_4); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
_4 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
Deinit(_2); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
((_2 as Continue).0: T) = move _4; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
}
bb3: {
+ StorageLive(_8); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
_8 = move ((_2 as Break).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
Deinit(_0); // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
((_0 as Err).0: E) = move _8; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
}
bb5: {
+ StorageLive(_7); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
_7 = move ((_2 as Continue).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
((_0 as Ok).0: T) = move _7; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
let mut _0: std::result::Result<T, E>; // return place in scope 0 at $DIR/try_identity_e2e.rs:+0:34: +0:46
let mut _2: isize; // in scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:18
let _3: T; // in scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
- let mut _4: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:34: +4:35
+ let _4: E; // in scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
scope 1 {
debug v => _3; // in scope 1 at $DIR/try_identity_e2e.rs:+3:16: +3:17
}
}
bb1: {
+ StorageLive(_4); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
_4 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
Deinit(_0); // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
((_0 as Err).0: E) = move _4; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
}
bb3: {
+ StorageLive(_3); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
_3 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
((_0 as Ok).0: T) = move _3; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
debug c => _1; // in scope 0 at $DIR/while_storage.rs:+0:15: +0:16
let mut _0: (); // return place in scope 0 at $DIR/while_storage.rs:+0:24: +0:24
let mut _2: bool; // in scope 0 at $DIR/while_storage.rs:+1:11: +1:22
- let mut _3: bool; // in scope 0 at $DIR/while_storage.rs:+1:20: +1:21
- let mut _4: bool; // in scope 0 at $DIR/while_storage.rs:+2:12: +2:23
- let mut _5: bool; // in scope 0 at $DIR/while_storage.rs:+2:21: +2:22
+ let mut _3: bool; // in scope 0 at $DIR/while_storage.rs:+2:12: +2:23
bb0: {
goto -> bb1; // scope 0 at $DIR/while_storage.rs:+1:5: +5:6
bb1: {
StorageLive(_2); // scope 0 at $DIR/while_storage.rs:+1:11: +1:22
- StorageLive(_3); // scope 0 at $DIR/while_storage.rs:+1:20: +1:21
- _3 = _1; // scope 0 at $DIR/while_storage.rs:+1:20: +1:21
- _2 = get_bool(move _3) -> bb2; // scope 0 at $DIR/while_storage.rs:+1:11: +1:22
+ _2 = get_bool(_1) -> bb2; // scope 0 at $DIR/while_storage.rs:+1:11: +1:22
// mir::Constant
// + span: $DIR/while_storage.rs:10:11: 10:19
// + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(<ZST>) }
}
bb2: {
- StorageDead(_3); // scope 0 at $DIR/while_storage.rs:+1:21: +1:22
switchInt(move _2) -> [0: bb7, otherwise: bb3]; // scope 0 at $DIR/while_storage.rs:+1:11: +1:22
}
bb3: {
- StorageLive(_4); // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
- StorageLive(_5); // scope 0 at $DIR/while_storage.rs:+2:21: +2:22
- _5 = _1; // scope 0 at $DIR/while_storage.rs:+2:21: +2:22
- _4 = get_bool(move _5) -> bb4; // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
+ StorageLive(_3); // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
+ _3 = get_bool(_1) -> bb4; // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
// mir::Constant
// + span: $DIR/while_storage.rs:11:12: 11:20
// + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(<ZST>) }
}
bb4: {
- StorageDead(_5); // scope 0 at $DIR/while_storage.rs:+2:22: +2:23
- switchInt(move _4) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
+ switchInt(move _3) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/while_storage.rs:+2:12: +2:23
}
bb5: {
- StorageDead(_4); // scope 0 at $DIR/while_storage.rs:+4:9: +4:10
+ StorageDead(_3); // scope 0 at $DIR/while_storage.rs:+4:9: +4:10
goto -> bb7; // scope 0 at no-location
}
bb6: {
- StorageDead(_4); // scope 0 at $DIR/while_storage.rs:+4:9: +4:10
+ StorageDead(_3); // scope 0 at $DIR/while_storage.rs:+4:9: +4:10
StorageDead(_2); // scope 0 at $DIR/while_storage.rs:+5:5: +5:6
goto -> bb1; // scope 0 at $DIR/while_storage.rs:+1:5: +5:6
}
// pretty-mode:expanded
// pp-exact:dollar-crate.pp
-fn main() {
- { ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); };
-}
+fn main() { { ::std::io::_print(format_args!("rust\n")); }; }
({
let res =
((::alloc::fmt::format as
- for<'a> fn(Arguments<'a>) -> String {format})(((::core::fmt::Arguments::new_v1
+ for<'a> fn(Arguments<'a>) -> String {format})(((<#[lang = "format_arguments"]>::new_v1
as
fn(&[&'static str], &[ArgumentV1<'_>]) -> Arguments<'_> {Arguments::<'_>::new_v1})((&([("test"
as &str)] as [&str; 1]) as &[&str; 1]),
#![feature(rustc_private)]
-extern crate rustc_interface;
extern crate rustc_driver;
+extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_span;
-use rustc_session::config::{Input, Options, OutputType, OutputTypes};
use rustc_interface::interface;
+use rustc_session::config::{Input, Options, OutputType, OutputTypes};
use rustc_span::source_map::FileName;
use std::path::PathBuf;
crate_cfg: Default::default(),
crate_check_cfg: Default::default(),
input,
- input_path: None,
output_file: Some(output),
output_dir: None,
file_loader: None,
interface::run_compiler(config, |compiler| {
// This runs all the passes prior to linking, too.
- let linker = compiler.enter(|queries| {
- queries.linker()
- });
+ let linker = compiler.enter(|queries| queries.linker());
if let Ok(linker) = linker {
linker.link();
}
$(RUSTC) $(UNSTABLEOPTS) -C split-debuginfo=unpacked -C debuginfo=2 \
-Z split-dwarf-kind=single --remap-path-prefix $(TMPDIR)=/a foo.rs -g
objdump -Wi $(TMPDIR)/foo | grep DW_AT_GNU_dwo_name | (! grep $(TMPDIR)) || exit 1
- ls $(TMPDIR)/*.o
+ rm $(TMPDIR)/*.o
ls $(TMPDIR)/*.dwo && exit 1 || exit 0
ls $(TMPDIR)/*.dwp && exit 1 || exit 0
rm $(TMPDIR)/$(call BIN,foo)
-unpacked-crosscrate: packed-crosscrate-split packed-crosscrate-single
+unpacked-crosscrate: unpacked-crosscrate-split unpacked-crosscrate-single
# - Debuginfo in `.dwo` files
# - (bar) `.rlib` file created, contains `.dwo`
# We're using the llvm-nm instead of the system nm to ensure it is compatible
# with the LLVM bitcode generated by rustc.
+# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
+ifndef IS_WINDOWS
NM = "$(LLVM_BIN_DIR)"/llvm-nm
+else
+NM = nm
+endif
all: $(call NATIVE_STATICLIB,native-staticlib)
# Build a staticlib and a rlib, the `native_func` symbol will be bundled into them
--- /dev/null
+include ../../run-make-fulldeps/tools.mk
+
+all:
+ $(RUSTC) main.rs -o main.rs 2> $(TMPDIR)/file.stderr || echo "failed successfully"
+ $(RUSTC) main.rs -o . 2> $(TMPDIR)/folder.stderr || echo "failed successfully"
+
+ifdef RUSTC_BLESS_TEST
+ cp "$(TMPDIR)"/file.stderr file.stderr
+ cp "$(TMPDIR)"/folder.stderr folder.stderr
+else
+ $(DIFF) file.stderr "$(TMPDIR)"/file.stderr
+ $(DIFF) folder.stderr "$(TMPDIR)"/folder.stderr
+endif
--- /dev/null
+warning: ignoring --out-dir flag due to -o flag
+
+error: the input file "main.rs" would be overwritten by the generated executable
+
+error: aborting due to previous error; 1 warning emitted
+
--- /dev/null
+warning: ignoring --out-dir flag due to -o flag
+
+error: the generated executable for the input file "main.rs" conflicts with the existing directory "."
+
+error: aborting due to previous error; 1 warning emitted
+
--- /dev/null
+fn main() {}
--- /dev/null
+warning: ignoring --out-dir flag due to -o flag
+
+error: the input file "main.rs" would be overwritten by the generated executable
+
+error: aborting due to previous error; 1 warning emitted
+
include ../../run-make-fulldeps/tools.mk
+# We'd be using the llvm-objdump instead of the system objdump to ensure compatibility
+# with the LLVM bitcode generated by rustc but on Windows piping/IO redirection under MSYS2 is wonky with llvm-objdump.
+OBJDUMP = objdump
+
all:
$(RUSTC) --crate-type dylib --crate-name raw_dylib_test lib.rs -C prefer-dynamic
$(RUSTC) --crate-type dylib --crate-name raw_dylib_test_wrapper lib_wrapper.rs -C prefer-dynamic
$(RUSTC) --crate-type bin driver.rs -L "$(TMPDIR)" -C prefer-dynamic
# Make sure we don't find an import to the functions we expect to be inlined.
- "$(LLVM_BIN_DIR)"/llvm-objdump -p $(TMPDIR)/driver.exe | $(CGREP) -v -e "inline_library_function"
- "$(LLVM_BIN_DIR)"/llvm-objdump -p $(TMPDIR)/driver.exe | $(CGREP) -v -e "inline_library_function_calls_inline"
+ $(OBJDUMP) -p $(TMPDIR)/driver.exe | $(CGREP) -v -e "inline_library_function"
+ $(OBJDUMP) -p $(TMPDIR)/driver.exe | $(CGREP) -v -e "inline_library_function_calls_inline"
# Make sure we do find an import to the functions we expect to be imported.
- "$(LLVM_BIN_DIR)"/llvm-objdump -p $(TMPDIR)/driver.exe | $(CGREP) -e "library_function"
+ $(OBJDUMP) -p $(TMPDIR)/driver.exe | $(CGREP) -e "library_function"
$(call COMPILE_OBJ,"$(TMPDIR)"/extern_1.obj,extern_1.c)
$(call COMPILE_OBJ,"$(TMPDIR)"/extern_2.obj,extern_2.c)
ifdef IS_MSVC
# We're using the llvm-nm instead of the system nm to ensure it is compatible
# with the LLVM bitcode generated by rustc.
+# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
+ifndef IS_WINDOWS
NM = "$(LLVM_BIN_DIR)"/llvm-nm
+else
+NM = nm
+endif
all:
# Build strange-named dep.
# We're using the llvm-nm instead of the system nm to ensure it is compatible
# with the LLVM bitcode generated by rustc.
+# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
+ifndef IS_WINDOWS
NM = "$(LLVM_BIN_DIR)"/llvm-nm
+else
+NM = nm
+endif
all: $(call NATIVE_STATICLIB,native_dep_1) $(call NATIVE_STATICLIB,native_dep_2) $(call NATIVE_STATICLIB,native_dep_3)
$(RUSTC) rust_dep_up.rs --crate-type=rlib -Zpacked_bundled_libs
+// Small test to ensure the "src-line-numbers" element is only present once on
+// the page.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: ".srclink"
wait-for: ".src-line-numbers"
// Check that their content is inside <pre><code>
assert-count: (".example-wrap pre > code", 4)
// Check that function signature is inside <pre><code>
-assert: "pre.rust.fn > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
-assert: "pre.rust.struct > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/enum.AnEnum.html"
-assert: "pre.rust.enum > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/trait.AnotherOne.html"
-assert: "pre.rust.trait > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/type.SomeType.html"
-assert: "pre.rust.typedef > code"
+assert: ".item-decl pre.rust > code"
// This is a complex selector, so here's how it works:
//
// * //*[@class='item-decl'] — selects element of any tag with classes docblock and item-decl
-// * /pre[@class='rust trait'] — selects immediate child with tag pre and classes rust and trait
+// * /pre[@class='rust'] — selects immediate child with tag pre and class rust
// * /code — selects immediate child with tag code
// * /a[@class='constant'] — selects immediate child with tag a and class constant
// * //text() — selects child that is text node
// This uses '/parent::*' as a proxy for the style of the text node.
// We can't just select the '<a>' because intermediate tags could be added.
assert-count: (
- "//*[@class='item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*",
+ "//*[@class='item-decl']/pre[@class='rust']/code/a[@class='constant']//text()/parent::*",
1,
)
assert-css: (
- "//*[@class='item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*",
+ "//*[@class='item-decl']/pre[@class='rust']/code/a[@class='constant']//text()/parent::*",
{"font-weight": "400"},
)
// table like view
assert-css: (".item-right.docblock-short", { "padding-left": "0px" })
compare-elements-position-near: (
- "//*[@class='item-left module-item']//a[text()='replaced_function']",
+ "//*[@class='item-left']//a[text()='replaced_function']",
".item-left .stab.deprecated",
{"y": 2},
)
// Ensure no wrap
compare-elements-position: (
- "//*[@class='item-left module-item']//a[text()='replaced_function']/..",
+ "//*[@class='item-left']//a[text()='replaced_function']/..",
"//*[@class='item-right docblock-short'][text()='a thing with a label']",
("y"),
)
// staggered layout with 2em spacing
assert-css: (".item-right.docblock-short", { "padding-left": "32px" })
compare-elements-position-near: (
- "//*[@class='item-left module-item']//a[text()='replaced_function']",
+ "//*[@class='item-left']//a[text()='replaced_function']",
".item-left .stab.deprecated",
{"y": 2},
)
// Ensure wrap
compare-elements-position-false: (
- "//*[@class='item-left module-item']//a[text()='replaced_function']/..",
+ "//*[@class='item-left']//a[text()='replaced_function']/..",
"//*[@class='item-right docblock-short'][text()='a thing with a label']",
("y"),
)
// This test checks that code blocks in list are supported.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
goto: "./fn.check_list_code_block.html"
-assert: ("pre.rust.fn")
+assert: (".item-decl pre.rust")
size: (400, 600)
// Ignored for now https://github.com/rust-lang/rust/issues/93784.
// compare-elements-position-near-false: (
-// "#preferred-light-theme .setting-name",
-// "#preferred-light-theme .choice",
+// "#preferred-light-theme .setting-radio-name",
+// "#preferred-light-theme .setting-radio",
// {"y": 16},
// )
// This test checks that the correct font is used on module items (in index.html pages).
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
assert-css: (
- ".item-table .module-item a",
+ ".item-table .item-left > a",
{"font-family": '"Fira Sans", Arial, NanumBarunGothic, sans-serif'},
ALL,
)
+// This test ensures that the scraped examples buttons are working as expecting
+// when 'Enter' key is pressed when they're focused.
goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
// The next/prev buttons vertically scroll the code viewport between examples
"help_hover_border": "rgb(0, 0, 0)",
"help_hover_color": "rgb(0, 0, 0)",
})
+
+// Now testing the top and bottom background in case there is only one scraped examples.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
+
+define-function: (
+ "check-background",
+ (theme, background_color_start, background_color_end),
+ block {
+ local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false", }
+ reload:
+ assert-css: (".scraped-example:not(.expanded) .code-wrapper::before", {
+ "background-image": "linear-gradient(" + |background_color_start| + ", " +
+ |background_color_end| + ")",
+ })
+ assert-css: (".scraped-example:not(.expanded) .code-wrapper::after", {
+ "background-image": "linear-gradient(to top, " + |background_color_start| + ", " +
+ |background_color_end| + ")",
+ })
+ },
+)
+
+call-function: ("check-background", {
+ "theme": "ayu",
+ "background_color_start": "rgb(15, 20, 25)",
+ "background_color_end": "rgba(15, 20, 25, 0)",
+})
+call-function: ("check-background", {
+ "theme": "dark",
+ "background_color_start": "rgb(53, 53, 53)",
+ "background_color_end": "rgba(53, 53, 53, 0)",
+})
+call-function: ("check-background", {
+ "theme": "light",
+ "background_color_start": "rgb(255, 255, 255)",
+ "background_color_end": "rgba(255, 255, 255, 0)",
+})
click: "#settings-menu"
wait-for: "#settings"
assert-css: ("#settings", {"display": "block"})
+
+// Store the line margin to compare with the settings.html later.
+store-css: (setting_line_margin, ".setting-line", "margin")
+
// Let's close it by clicking on the same button.
click: "#settings-menu"
wait-for-css: ("#settings", {"display": "none"})
// We check that the "Use system theme" is disabled.
assert-property: ("#theme-system-preference", {"checked": "false"})
// Meaning that only the "theme" menu is showing up.
-assert: ".setting-line:not(.hidden) #theme"
-assert: ".setting-line.hidden #preferred-dark-theme"
-assert: ".setting-line.hidden #preferred-light-theme"
+assert: "#theme.setting-line:not(.hidden)"
+assert: "#preferred-dark-theme.setting-line.hidden"
+assert: "#preferred-light-theme.setting-line.hidden"
// We check that the correct theme is selected.
-assert-property: ("#theme .choices #theme-dark", {"checked": "true"})
+assert-property: ("#theme .setting-radio-choices #theme-dark", {"checked": "true"})
// Some style checks...
move-cursor-to: "#settings-menu > a"
"box-shadow": "rgb(33, 150, 243) 0px 0px 1px 1px",
},
)
+// Now we check the setting-radio-name is on a different line than the label.
+compare-elements-position-near: (
+ "#theme .setting-radio-name",
+ "#theme .setting-radio-choices",
+ {"x": 1}
+)
+compare-elements-position-near-false: (
+ "#theme .setting-radio-name",
+ "#theme .setting-radio-choices",
+ {"y": 1}
+)
+// Now we check that the label positions are all on the same line.
+compare-elements-position-near: (
+ "#theme .setting-radio-choices #theme-light",
+ "#theme .setting-radio-choices #theme-dark",
+ {"y": 1}
+)
+compare-elements-position-near: (
+ "#theme .setting-radio-choices #theme-dark",
+ "#theme .setting-radio-choices #theme-ayu",
+ {"y": 1}
+)
+compare-elements-position-near: (
+ "#theme .setting-radio-choices #theme-ayu",
+ "#theme .setting-radio-choices #theme-system-preference",
+ {"y": 1}
+)
// First we check the "default" display for toggles.
assert-css: (
// We now switch the display.
click: "#theme-system-preference"
// Wait for the hidden element to show up.
-wait-for: ".setting-line:not(.hidden) #preferred-dark-theme"
-assert: ".setting-line:not(.hidden) #preferred-light-theme"
+wait-for: "#preferred-dark-theme.setting-line:not(.hidden)"
+assert: "#preferred-light-theme.setting-line:not(.hidden)"
// We check their text as well.
-assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
-assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
+assert-text: ("#preferred-dark-theme .setting-radio-name", "Preferred dark theme")
+assert-text: ("#preferred-light-theme .setting-radio-name", "Preferred light theme")
// We now check that clicking on the toggles' text is like clicking on the checkbox.
// To test it, we use the "Disable keyboard shortcuts".
local-storage: {"rustdoc-disable-shortcuts": "false"}
-click: ".setting-line:last-child .settings-toggle .label"
+click: ".setting-line:last-child .setting-check span"
assert-local-storage: {"rustdoc-disable-shortcuts": "true"}
// Make sure that "Disable keyboard shortcuts" actually took effect.
wait-for-css: ("#settings-menu .popover", {"display": "block"})
// Now turn keyboard shortcuts back on, and see if they work.
-click: ".setting-line:last-child .settings-toggle .label"
+click: ".setting-line:last-child .setting-check span"
assert-local-storage: {"rustdoc-disable-shortcuts": "false"}
press-key: "Escape"
press-key: "?"
wait-for-css: ("#help-button .popover", {"display": "block"})
assert-css: ("#settings-menu .popover", {"display": "none"})
+// Now switch back to the settings popover, and make sure the keyboard
+// shortcut works when a check box is selected.
+click: "#settings-menu > a"
+wait-for-css: ("#settings-menu .popover", {"display": "block"})
+focus: "#auto-hide-large-items"
+press-key: "?"
+wait-for-css: ("#settings-menu .popover", {"display": "none"})
+wait-for-css: ("#help-button .popover", {"display": "block"})
+
+// Now switch back to the settings popover, and make sure the keyboard
+// shortcut works when a check box is selected.
+click: "#settings-menu > a"
+wait-for-css: ("#settings-menu .popover", {"display": "block"})
+wait-for-css: ("#help-button .popover", {"display": "none"})
+focus: "#theme-system-preference"
+press-key: "?"
+wait-for-css: ("#settings-menu .popover", {"display": "none"})
+wait-for-css: ("#help-button .popover", {"display": "block"})
+
// Now we go to the settings page to check that the CSS is loaded as expected.
goto: "file://" + |DOC_PATH| + "/settings.html"
wait-for: "#settings"
assert-attribute-false: ("#settings", {"class": "popover"}, CONTAINS)
compare-elements-position: (".sub form", "#settings", ("x"))
+// Check that setting-line has the same margin in this mode as in the popover.
+assert-css: (".setting-line", {"margin": |setting_line_margin|})
+
// We now check the display with JS disabled.
assert-false: "noscript section"
javascript: false
goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
show-text: true
// Check the impl headers.
-assert-css: (".impl.has-srclink .srclink", {"font-size": "16px", "font-weight": 400}, ALL)
-assert-css: (".impl.has-srclink .code-header", {"font-size": "18px", "font-weight": 600}, ALL)
+assert-css: (".impl .srclink", {"font-size": "16px", "font-weight": 400}, ALL)
+assert-css: (".impl .code-header", {"font-size": "18px", "font-weight": 600}, ALL)
// Check the impl items.
-assert-css: (".impl-items .has-srclink .srclink", {"font-size": "16px", "font-weight": 400}, ALL)
-assert-css: (".impl-items .has-srclink .code-header", {"font-size": "16px", "font-weight": 600}, ALL)
+assert-css: (".impl-items .srclink", {"font-size": "16px", "font-weight": 400}, ALL)
+assert-css: (".impl-items .code-header", {"font-size": "16px", "font-weight": 600}, ALL)
// Check that we can click on source link
store-document-property: (url, "URL")
-click: ".impl-items .has-srclink .srclink"
+click: ".impl-items .srclink"
assert-document-property-false: {"URL": |url|}
assert-local-storage-false: { "rustdoc-use-system-theme": "true" }
click: "#theme-system-preference"
-wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
+wait-for: "#preferred-light-theme.setting-line:not(.hidden)"
assert-local-storage: { "rustdoc-use-system-theme": "true" }
// We click on both preferred light and dark themes to be sure that there is a change.
click: "#preferred-light-theme-dark"
reload:
// Ensure that the "preferred themes" are still displayed.
-wait-for: ".setting-line:not(.hidden) #preferred-light-theme"
+wait-for: "#preferred-light-theme.setting-line:not(.hidden)"
click: "#theme-light"
wait-for-css: ("body", { "background-color": |background_light| })
assert-local-storage: { "rustdoc-theme": "light" }
// Ensure it's now hidden again
-wait-for: ".setting-line.hidden #preferred-light-theme"
+wait-for: "#preferred-light-theme.setting-line.hidden"
// And ensure the theme was rightly set.
wait-for-css: ("body", { "background-color": |background_light| })
assert-local-storage: { "rustdoc-theme": "light" }
reload:
wait-for: "#settings"
-assert: ".setting-line.hidden #preferred-light-theme"
+assert: "#preferred-light-theme.setting-line.hidden"
show-text: true
compare-elements-property: (
- "//a[@title='test_docs::safe_fn fn']/..",
- "//a[@title='test_docs::unsafe_fn fn']/..",
+ "//a[@title='fn test_docs::safe_fn']/..",
+ "//a[@title='fn test_docs::unsafe_fn']/..",
["clientHeight"]
)
const EXPECTED = {
'others': [
{ 'path': 'std', 'name': 'print' },
- { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'println' },
+ { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'eprintln' },
],
};
const EXPECTED = {
'others': [
{ 'path': 'std', 'name': 'print' },
- { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'println' },
+ { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'eprintln' },
{ 'path': 'std::pin', 'name': 'pin' },
{ 'path': 'std::future', 'name': 'join' },
const EXPECTED = {
'others': [
{ 'path': 'std::vec::Vec', 'name': 'new' },
- { 'path': 'std::vec::Vec', 'name': 'ne' },
- { 'path': 'alloc::vec::Vec', 'name': 'ne' },
+ { 'path': 'alloc::vec::Vec', 'name': 'new' },
+ { 'path': 'std::vec::Vec', 'name': 'new_in' },
+ { 'path': 'alloc::vec::Vec', 'name': 'new_in' },
],
};
'others': [
{ 'path': 'search_short_types', 'name': 'P' },
{ 'path': 'search_short_types::VeryLongTypeName', 'name': 'p' },
- { 'path': 'search_short_types', 'name': 'Ap' },
- { 'path': 'search_short_types::VeryLongTypeName', 'name': 'ap' },
+ { 'path': 'search_short_types', 'name': 'Pa' },
],
};
+// check-pass
// normalize-stderr-test: "`.*`" -> "`DEF_ID`"
// normalize-stdout-test: "`.*`" -> "`DEF_ID`"
// edition:2018
pub async fn f() -> impl std::fmt::Debug {
+ // rustdoc doesn't care that this is infinitely sized
#[derive(Debug)]
- enum E { //~ ERROR
+ enum E {
This(E),
Unit,
}
+++ /dev/null
-error[E0072]: recursive type `DEF_ID` has infinite size
- --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5
- |
-LL | enum E {
- | ^^^^^^
-LL | This(E),
- | - recursive without indirection
- |
-help: insert some indirection (e.g., a `DEF_ID`) to break the cycle
- |
-LL | This(Box<E>),
- | ++++ +
-
-error: aborting due to previous error
-
-For more information about this error, try `DEF_ID`.
+// check-pass
+
fn f() -> impl Sized {
- enum E { //~ ERROR
+ // rustdoc doesn't care that this is infinitely sized
+ enum E {
V(E),
}
unimplemented!()
+++ /dev/null
-error[E0072]: recursive type `f::E` has infinite size
- --> $DIR/infinite-recursive-type-impl-trait.rs:2:5
- |
-LL | enum E {
- | ^^^^^^
-LL | V(E),
- | - recursive without indirection
- |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
- |
-LL | V(Box<E>),
- | ++++ +
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0072`.
| ^^^
|
= note: error from rustc: unknown start of token: `
- = note: error from rustc: unknown start of token: `
- = note: error from rustc: unknown start of token: `
warning: could not parse code block as Rust code
--> $DIR/invalid-syntax.rs:64:5
-Z dlltool=val -- import library generation tool (windows-gnu only)
-Z dont-buffer-diagnostics=val -- emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) (default: no)
-Z drop-tracking=val -- enables drop tracking in generators (default: no)
+ -Z drop-tracking-mir=val -- enables drop tracking on MIR in generators (default: no)
-Z dual-proc-macros=val -- load proc macros for both target and host, but only link to the target (default: no)
-Z dump-dep-graph=val -- dump the dependency graph to $RUST_DEP_GRAPH (default: /tmp/dep_graph.gv) (default: no)
-Z dump-drop-tracking-cfg=val -- dump drop-tracking control-flow graph as a `.dot` file (default: no)
-Z threads=val -- use a thread pool with N threads
-Z time-llvm-passes=val -- measure time of each LLVM pass (default: no)
-Z time-passes=val -- measure time of each rustc pass (default: no)
+ -Z tiny-const-eval-limit=val -- sets a tiny, non-configurable limit for const eval; useful for compiler tests
-Z tls-model=val -- choose the TLS model to use (`rustc --print tls-models` for details)
-Z trace-macros=val -- for every macro invocation, print its name and arguments (default: no)
-Z track-diagnostics=val -- tracks where in rustc a diagnostic was emitted
-<section id="associatedconstant.YOLO" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#16">source</a><h4 class="code-header">const <a href="#associatedconstant.YOLO" class="constant">YOLO</a>: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></section>
\ No newline at end of file
+<section id="associatedconstant.YOLO" class="method"><a class="srclink rightside" href="../src/foo/anchors.rs.html#16">source</a><h4 class="code-header">const <a href="#associatedconstant.YOLO" class="constant">YOLO</a>: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></section>
\ No newline at end of file
-<section id="associatedconstant.X" class="associatedconstant has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#42">source</a><h4 class="code-header">pub const <a href="#associatedconstant.X" class="constant">X</a>: <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a> = 0i32</h4></section>
\ No newline at end of file
+<section id="associatedconstant.X" class="associatedconstant"><a class="srclink rightside" href="../src/foo/anchors.rs.html#42">source</a><h4 class="code-header">pub const <a href="#associatedconstant.X" class="constant">X</a>: <a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a> = 0i32</h4></section>
\ No newline at end of file
-<section id="method.new" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#48">source</a><h4 class="code-header">pub fn <a href="#method.new" class="fn">new</a>() -> Self</h4></section>
\ No newline at end of file
+<section id="method.new" class="method"><a class="srclink rightside" href="../src/foo/anchors.rs.html#48">source</a><h4 class="code-header">pub fn <a href="#method.new" class="fn">new</a>() -> Self</h4></section>
\ No newline at end of file
-<section id="method.bar" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#23">source</a><h4 class="code-header">fn <a href="#method.bar" class="fn">bar</a>()</h4></section>
\ No newline at end of file
+<section id="method.bar" class="method"><a class="srclink rightside" href="../src/foo/anchors.rs.html#23">source</a><h4 class="code-header">fn <a href="#method.bar" class="fn">bar</a>()</h4></section>
\ No newline at end of file
-<section id="tymethod.foo" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#20">source</a><h4 class="code-header">fn <a href="#tymethod.foo" class="fn">foo</a>()</h4></section>
\ No newline at end of file
+<section id="tymethod.foo" class="method"><a class="srclink rightside" href="../src/foo/anchors.rs.html#20">source</a><h4 class="code-header">fn <a href="#tymethod.foo" class="fn">foo</a>()</h4></section>
\ No newline at end of file
-<section id="associatedtype.T" class="method has-srclink"><a class="srclink rightside" href="../src/foo/anchors.rs.html#13">source</a><h4 class="code-header">type <a href="#associatedtype.T" class="associatedtype">T</a></h4></section>
\ No newline at end of file
+<section id="associatedtype.T" class="method"><a class="srclink rightside" href="../src/foo/anchors.rs.html#13">source</a><h4 class="code-header">type <a href="#associatedtype.T" class="associatedtype">T</a></h4></section>
\ No newline at end of file
-<section id="associatedtype.Y" class="associatedtype has-srclink"><h4 class="code-header">type <a href="#associatedtype.Y" class="associatedtype">Y</a> = <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></section>
+<section id="associatedtype.Y" class="associatedtype"><h4 class="code-header">type <a href="#associatedtype.Y" class="associatedtype">Y</a> = <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a></h4></section>
\ No newline at end of file
pub struct MyBox<T: ?Sized>(*const T);
// @has 'foo/fn.alpha.html'
-// @snapshot link_slice_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn alpha() -> &'static [u32; 1] {
loop {}
}
// @has 'foo/fn.beta.html'
-// @snapshot link_slice_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn beta<T>() -> &'static [T; 1] {
loop {}
}
// @has 'foo/fn.gamma.html'
-// @snapshot link_box_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn gamma() -> MyBox<[u32; 1]> {
loop {}
}
// @has 'foo/fn.delta.html'
-// @snapshot link_box_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn delta<T>() -> MyBox<[T; 1]> {
loop {}
}
pub trait Foo {
- // @has assoc_consts/trait.Foo.html '//*[@class="rust trait"]' \
+ // @has assoc_consts/trait.Foo.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'const FOO: usize = 13usize;'
// @has - '//*[@id="associatedconstant.FOO"]' 'const FOO: usize'
const FOO: usize = 12 + 1;
}
// @has foo/type.AsExprOf.html
-// @has - '//pre[@class="rust typedef"]' 'type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;'
pub type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;
}
// @has assoc_types/fn.use_output.html
-// @has - '//*[@class="rust fn"]' '-> &T::Output'
-// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' '-> &T::Output'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output'
pub fn use_output<T: Index<usize>>(obj: &T, index: usize) -> &T::Output {
obj.index(index)
}
}
// @has assoc_types/fn.use_input.html
-// @has - '//*[@class="rust fn"]' 'T::Input'
-// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'T::Input'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
pub fn use_input<T: Feed>(_feed: &T, _element: T::Input) { }
// @has assoc_types/fn.cmp_input.html
-// @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq<U::Input>'
-// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where T::Input: PartialEq<U::Input>'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
pub fn cmp_input<T: Feed, U: Feed>(a: &T::Input, b: &U::Input) -> bool
where T::Input: PartialEq<U::Input>
{
// edition:2018
-// @has async_fn/fn.foo.html '//pre[@class="rust fn"]' 'pub async fn foo() -> Option<Foo>'
+// @has async_fn/fn.foo.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn foo() -> Option<Foo>'
pub async fn foo() -> Option<Foo> {
None
}
-// @has async_fn/fn.bar.html '//pre[@class="rust fn"]' 'pub async fn bar(a: i32, b: i32) -> i32'
+// @has async_fn/fn.bar.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn bar(a: i32, b: i32) -> i32'
pub async fn bar(a: i32, b: i32) -> i32 {
0
}
-// @has async_fn/fn.baz.html '//pre[@class="rust fn"]' 'pub async fn baz<T>(a: T) -> T'
+// @has async_fn/fn.baz.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn baz<T>(a: T) -> T'
pub async fn baz<T>(a: T) -> T {
a
}
-// @has async_fn/fn.qux.html '//pre[@class="rust fn"]' 'pub async unsafe fn qux() -> char'
+// @has async_fn/fn.qux.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async unsafe fn qux() -> char'
pub async unsafe fn qux() -> char {
'⚠'
}
-// @has async_fn/fn.mut_args.html '//pre[@class="rust fn"]' 'pub async fn mut_args(a: usize)'
+// @has async_fn/fn.mut_args.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn mut_args(a: usize)'
pub async fn mut_args(mut a: usize) {}
-// @has async_fn/fn.mut_ref.html '//pre[@class="rust fn"]' 'pub async fn mut_ref(x: i32)'
+// @has async_fn/fn.mut_ref.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn mut_ref(x: i32)'
pub async fn mut_ref(ref mut x: i32) {}
trait Bar {}
impl Bar for () {}
-// @has async_fn/fn.quux.html '//pre[@class="rust fn"]' 'pub async fn quux() -> impl Bar'
+// @has async_fn/fn.quux.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn quux() -> impl Bar'
pub async fn quux() -> impl Bar {
()
}
pub trait Trait<const N: usize> {}
// @has async_fn/fn.const_generics.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)'
pub async fn const_generics<const N: usize>(_: impl Trait<N>) {}
// test that elided lifetimes are properly elided and not displayed as `'_`
// regression test for #63037
// @has async_fn/fn.elided.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn elided(foo: &str) -> &str'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn elided(foo: &str) -> &str'
pub async fn elided(foo: &str) -> &str {}
// This should really be shown as written, but for implementation reasons it's difficult.
// See `impl Clean for TyKind::Ref`.
// @has async_fn/fn.user_elided.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn user_elided(foo: &str) -> &str'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn user_elided(foo: &str) -> &str'
pub async fn user_elided(foo: &'_ str) -> &str {}
// @has async_fn/fn.static_trait.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>'
pub async fn static_trait(foo: &str) -> Box<dyn Bar> {}
// @has async_fn/fn.lifetime_for_trait.html
-// @has - '//pre[@class="rust fn"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>"
pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_> {}
// @has async_fn/fn.elided_in_input_trait.html
-// @has - '//pre[@class="rust fn"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)"
pub async fn elided_in_input_trait(t: impl Pattern<'_>) {}
struct AsyncFdReadyGuard<'a, T> { x: &'a T }
impl Foo {
// @has async_fn/struct.Foo.html
- // @has - '//*[@class="method has-srclink"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator<Item = &usize>'
+ // @has - '//*[@class="method"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator<Item = &usize>'
pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator<Item = &usize> {}
// taken from `tokio` as an example of a method that was particularly bad before
- // @has - '//*[@class="method has-srclink"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>"
+ // @has - '//*[@class="method"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>"
pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()> {}
- // @has - '//*[@class="method has-srclink"]' "pub async fn mut_self(&mut self)"
+ // @has - '//*[@class="method"]' "pub async fn mut_self(&mut self)"
pub async fn mut_self(&mut self) {}
}
// test named lifetimes, just in case
// @has async_fn/fn.named.html
-// @has - '//pre[@class="rust fn"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str"
pub async fn named<'a, 'b>(foo: &'a str) -> &'b str {}
// @has async_fn/fn.named_trait.html
-// @has - '//pre[@class="rust fn"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>"
pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b> {}
#![crate_name = "foo"]
-// @has foo/fn.f.html '//*[@class="rust fn"]' '#[no_mangle]'
+// @has foo/fn.f.html '//div[@class="item-decl"]/pre[@class="rust"]' '#[no_mangle]'
#[no_mangle]
pub extern "C" fn f() {}
-// @has foo/fn.g.html '//*[@class="rust fn"]' '#[export_name = "bar"]'
+// @has foo/fn.g.html '//div[@class="item-decl"]/pre[@class="rust"]' '#[export_name = "bar"]'
#[export_name = "bar"]
pub extern "C" fn g() {}
-// @has foo/struct.Repr.html '//*[@class="item-decl"]' '#[repr(C, align(8))]'
+// @has foo/struct.Repr.html '//div[@class="item-decl"]' '#[repr(C, align(8))]'
#[repr(C, align(8))]
pub struct Repr;
// @has issue_85454/trait.FromResidual.html
-// @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
pub trait FromResidual<R = <Self as Try>::Residual> {
fn from_residual(residual: R) -> Self;
}
#![no_core]
// @has 'foo/index.html'
-// @has - '//*[@class="item-left module-item"]/*[@class="stab portability"]' 'foobar'
-// @has - '//*[@class="item-left module-item"]/*[@class="stab portability"]' 'bar'
+// @has - '//*[@class="item-left"]/*[@class="stab portability"]' 'foobar'
+// @has - '//*[@class="item-left"]/*[@class="stab portability"]' 'bar'
#[doc(cfg(feature = "foobar"))]
mod imp_priv {
#![crate_name = "foo"]
// @has foo/fn.bar.html
-// @has - '//*[@class="rust fn"]' 'pub const fn bar() -> '
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub const fn bar() -> '
/// foo
pub const fn bar() -> usize {
2
}
// @has foo/struct.Foo.html
-// @has - '//*[@class="method has-srclink"]' 'const fn new()'
+// @has - '//*[@class="method"]' 'const fn new()'
pub struct Foo(usize);
impl Foo {
use std::ops::Add;
-// @has foo/struct.Simd.html '//pre[@class="rust struct"]' 'pub struct Simd<T, const WIDTH: usize>'
+// @has foo/struct.Simd.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Simd<T, const WIDTH: usize>'
pub struct Simd<T, const WIDTH: usize> {
inner: T,
}
#![crate_name = "foo"]
-// @has foo/struct.Foo.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Foo.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>(_);'
pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>(T);
}
// @has foo/trait.Array.html
-// @has - '//*[@class="impl has-srclink"]' 'impl<T, const N: usize> Array for [T; N]'
+// @has - '//*[@class="impl"]' 'impl<T, const N: usize> Array for [T; N]'
impl<T, const N: usize> Array for [T; N] {
type Item = T;
}
#![crate_name = "foo"]
extern crate extern_crate;
-// @has foo/fn.extern_fn.html '//pre[@class="rust fn"]' \
+// @has foo/fn.extern_fn.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn extern_fn<const N: usize>() -> impl Iterator<Item = [u8; N]>'
pub use extern_crate::extern_fn;
-// @has foo/struct.ExternTy.html '//pre[@class="rust struct"]' \
+// @has foo/struct.ExternTy.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct ExternTy<const N: usize> {'
pub use extern_crate::ExternTy;
-// @has foo/type.TyAlias.html '//pre[@class="rust typedef"]' \
+// @has foo/type.TyAlias.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'type TyAlias<const N: usize> = ExternTy<N>;'
pub use extern_crate::TyAlias;
-// @has foo/trait.WTrait.html '//pre[@class="rust trait"]' \
+// @has foo/trait.WTrait.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub trait WTrait<const N: usize, const M: usize>'
-// @has - '//*[@class="rust trait"]' 'fn hey<const P: usize>() -> usize'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn hey<const P: usize>() -> usize'
pub use extern_crate::WTrait;
-// @has foo/trait.Trait.html '//pre[@class="rust trait"]' \
+// @has foo/trait.Trait.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub trait Trait<const N: usize>'
// @has - '//*[@id="impl-Trait%3C1%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<1> for u8'
// @has - '//*[@id="impl-Trait%3C2%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<2> for u8'
impl Trait<{1 + 2}> for u8 {}
impl<const N: usize> Trait<N> for [u8; N] {}
-// @has foo/struct.Foo.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Foo.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Foo<const N: usize>where u8: Trait<N>'
pub struct Foo<const N: usize> where u8: Trait<N>;
-// @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar<T, const N: usize>(_)'
+// @has foo/struct.Bar.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Bar<T, const N: usize>(_)'
pub struct Bar<T, const N: usize>([T; N]);
// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Foo<M>where u8: Trait<M>'
}
}
-// @has foo/fn.test.html '//pre[@class="rust fn"]' \
+// @has foo/fn.test.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn test<const N: usize>() -> impl Trait<N>where u8: Trait<N>'
pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N> {
2u8
}
-// @has foo/fn.a_sink.html '//pre[@class="rust fn"]' \
+// @has foo/fn.a_sink.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub async fn a_sink<const N: usize>(v: [u8; N]) -> impl Trait<N>'
pub async fn a_sink<const N: usize>(v: [u8; N]) -> impl Trait<N> {
v
}
-// @has foo/fn.b_sink.html '//pre[@class="rust fn"]' \
+// @has foo/fn.b_sink.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub async fn b_sink<const N: usize>(_: impl Trait<N>)'
pub async fn b_sink<const N: usize>(_: impl Trait<N>) {}
-// @has foo/fn.concrete.html '//pre[@class="rust fn"]' \
+// @has foo/fn.concrete.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn concrete() -> [u8; 22]'
pub fn concrete() -> [u8; 3 + std::mem::size_of::<u64>() << 1] {
Default::default()
}
-// @has foo/type.Faz.html '//pre[@class="rust typedef"]' \
+// @has foo/type.Faz.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'type Faz<const N: usize> = [u8; N];'
pub type Faz<const N: usize> = [u8; N];
-// @has foo/type.Fiz.html '//pre[@class="rust typedef"]' \
+// @has foo/type.Fiz.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'type Fiz<const N: usize> = [[u8; N]; 48];'
pub type Fiz<const N: usize> = [[u8; N]; 3 << 4];
}
}
-// @has foo/struct.Foz.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Foz.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Foz<const N: usize>(_);'
define_me!(Foz<N>);
const ASSOC: usize = N;
}
-// @has foo/fn.q_user.html '//pre[@class="rust fn"]' \
+// @has foo/fn.q_user.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn q_user() -> [u8; 13]'
pub fn q_user() -> [u8; <[u8; 13] as Q>::ASSOC] {
[0; <[u8; 13] as Q>::ASSOC]
}
-// @has foo/union.Union.html '//pre[@class="rust union"]' \
+// @has foo/union.Union.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub union Union<const N: usize>'
pub union Union<const N: usize> {
// @has - //pre "pub arr: [u8; N]"
pub another_arr: [(); N],
}
-// @has foo/enum.Enum.html '//pre[@class="rust enum"]' \
+// @has foo/enum.Enum.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub enum Enum<const N: usize>'
pub enum Enum<const N: usize> {
// @has - //pre "Variant([u8; N])"
Unsorted,
}
-// @has foo/struct.VSet.html '//pre[@class="rust struct"]' 'pub struct VSet<T, const ORDER: Order>'
+// @has foo/struct.VSet.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct VSet<T, const ORDER: Order>'
// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>'
// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>'
pub struct VSet<T, const ORDER: Order> {
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
// make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647
-// @has foo/struct.Ice.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Ice.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Ice<const N: usize>;'
pub struct Ice<const N: usize> where [(); N + 1]:;
#![crate_name = "foo"]
-// @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex<const D: usize> = [i64; D];'
+// @has foo/type.CellIndex.html '//div[@class="item-decl"]/pre[@class="rust"]' 'type CellIndex<const D: usize> = [i64; D];'
pub type CellIndex<const D: usize> = [i64; D];
extern "rust-intrinsic" {
// @has 'foo/fn.transmute.html'
- // @has - '//pre[@class="rust fn"]' 'pub const unsafe extern "rust-intrinsic" fn transmute<T, U>(_: T) -> U'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub const unsafe extern "rust-intrinsic" fn transmute<T, U>(_: T) -> U'
#[stable(since="1.0.0", feature="rust1")]
#[rustc_const_stable(feature = "const_transmute", since = "1.56.0")]
pub fn transmute<T, U>(_: T) -> U;
// @has 'foo/fn.unreachable.html'
- // @has - '//pre[@class="rust fn"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
#[stable(since="1.0.0", feature="rust1")]
pub fn unreachable() -> !;
}
extern "C" {
// @has 'foo/fn.needs_drop.html'
- // @has - '//pre[@class="rust fn"]' 'pub unsafe extern "C" fn needs_drop() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "C" fn needs_drop() -> !'
#[stable(since="1.0.0", feature="rust1")]
pub fn needs_drop() -> !;
}
-// @has deprecated/index.html '//*[@class="item-left module-item"]/span[@class="stab deprecated"]' \
+// @has deprecated/index.html '//*[@class="item-left"]/span[@class="stab deprecated"]' \
// 'Deprecated'
// @has - '//*[@class="item-right docblock-short"]' 'Deprecated docs'
fn foo(foo: Self::Fuu);
}
-// @has doc_assoc_item/struct.Foo.html '//*[@class="impl has-srclink"]' 'impl<T: Bar<Fuu = u32>> Foo<T>'
+// @has doc_assoc_item/struct.Foo.html '//*[@class="impl"]' 'impl<T: Bar<Fuu = u32>> Foo<T>'
impl<T: Bar<Fuu = u32>> Foo<T> {
pub fn new(t: T) -> Foo<T> {
Foo {
// @has doc_cfg/unix_only/index.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on Unix only.'
-// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AARM\Z'
+// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\AARM\Z'
// @count - '//*[@class="stab portability"]' 2
#[doc(cfg(unix))]
pub mod unix_only {
// @has doc_cfg/wasi_only/index.html \
// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'Available on WASI only.'
-// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AWebAssembly\Z'
+// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\AWebAssembly\Z'
// @count - '//*[@class="stab portability"]' 2
#[doc(cfg(target_os = "wasi"))]
pub mod wasi_only {
// the portability header is different on the module view versus the full view
// @has doc_cfg/index.html
-// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\Aavx\Z'
+// @matches - '//*[@class="item-left"]//*[@class="stab portability"]' '\Aavx\Z'
// @has doc_cfg/fn.uses_target_feature.html
// @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
-<script type="text/json" id="notable-traits-data">{"&'static [SomeStruct]":"<h3>Notable traits for <code>&amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait_slice::SomeTrait\">SomeTrait</a> for &amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"&'static [SomeStruct]":"<h3>Notable traits for <code>&amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait_slice::SomeTrait\">SomeTrait</a> for &amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>","Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>","Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
#![feature(doc_cfg)]
// @has 'foo/index.html'
-// @matches '-' '//*[@class="item-left module-item"]//*[@class="stab portability"]' '^sync$'
-// @has '-' '//*[@class="item-left module-item"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
+// @matches '-' '//*[@class="item-left"]//*[@class="stab portability"]' '^sync$'
+// @has '-' '//*[@class="item-left"]//*[@class="stab portability"]/@title' 'Available on crate feature `sync` only'
// @has 'foo/struct.Foo.html'
// @has '-' '//*[@class="stab portability"]' 'sync'
// @has issue_33054/impls/struct.Foo.html
// @has - '//h3[@class="code-header"]' 'impl Foo'
// @has - '//h3[@class="code-header"]' 'impl Bar for Foo'
-// @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
-// @count - '//*[@id="main-content"]/div[@id="implementations-list"]/details/summary/*[@class="impl has-srclink"]' 1
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @count - '//*[@id="main-content"]/div[@id="implementations-list"]/details/summary/*[@class="impl"]' 1
// @has issue_33054/impls/bar/trait.Bar.html
// @has - '//h3[@class="code-header"]' 'impl Bar for Foo'
// @count - '//*[@class="struct"]' 1
// blanket implementations.
// @has 'foo/struct.Whatever.html'
-// @count - '//*[@id="blanket-implementations-list"]/section[@class="impl has-srclink"]' 1
+// @count - '//*[@id="blanket-implementations-list"]/section[@class="impl"]' 1
pub trait Something<T> { }
pub struct Whatever;
// There are 3 impl blocks with public item and one that should not be displayed
// by default because it only contains private items (but not in this case because
// we used `--document-private-items`).
-// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 4
+// @count - '//*[@class="impl"]' 'impl Foo' 4
// Impl block only containing private items should not be displayed unless the
// `--document-private-items` flag is used.
// There are 3 impl blocks with public item and one that should not be displayed
// because it only contains private items.
-// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 3
+// @count - '//*[@class="impl"]' 'impl Foo' 3
// Impl block only containing private items should not be displayed.
/// Private
/// Hello empty impl block!
impl Foo {}
// We ensure that this empty impl block without doc isn't rendered.
-// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 1
+// @count - '//*[@class="impl"]' 'impl Foo' 1
impl Foo {}
// Just to ensure that empty trait impl blocks are rendered.
#![crate_name = "foo"]
// @has foo/fn.f.html
-// @has - '//*[@class="rust fn"]' 'pub fn f(callback: fn(len: usize, foo: u32))'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f(callback: fn(len: usize, foo: u32))'
pub fn f(callback: fn(len: usize, foo: u32)) {}
// @has 'glob_shadowing/index.html'
-// @count - '//div[@class="item-left module-item"]' 6
+// @count - '//div[@class="item-left"]' 6
// @!has - '//div[@class="item-right docblock-short"]' 'sub1::describe'
// @has - '//div[@class="item-right docblock-short"]' 'sub2::describe'
// this test as long as one can ensure that private fields are not leaked!
//
// @has hide_complex_unevaluated_const_arguments/trait.Sub.html \
-// '//*[@class="rust trait"]' \
+// '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub trait Sub: Sup<{ _ }, { _ }> { }'
pub trait Sub: Sup<{ 90 * 20 * 4 }, { Struct { private: () } }> {}
+++ /dev/null
-// Regression test for #83026.
-// The goal of this test is to ensure that impl blocks inside
-// const expressions are documented as well.
-
-#![crate_name = "foo"]
-
-// @has 'foo/struct.A.html'
-// @has - '//*[@id="method.new"]/*[@class="code-header"]' 'pub fn new() -> A'
-// @has - '//*[@id="method.bar"]/*[@class="code-header"]' 'pub fn bar(&self)'
-// @has - '//*[@id="method.woo"]/*[@class="code-header"]' 'pub fn woo(&self)'
-// @has - '//*[@id="method.yoo"]/*[@class="code-header"]' 'pub fn yoo()'
-// @has - '//*[@id="method.yuu"]/*[@class="code-header"]' 'pub fn yuu()'
-pub struct A;
-
-const _: () = {
- impl A {
- const FOO: () = {
- impl A {
- pub fn woo(&self) {}
- }
- };
-
- pub fn new() -> A {
- A
- }
- }
-};
-pub const X: () = {
- impl A {
- pub fn bar(&self) {}
- }
-};
-
-fn foo() {
- impl A {
- pub fn yoo() {}
- }
- const _: () = {
- impl A {
- pub fn yuu() {}
- }
- };
-}
pub struct Foo<T> { field: T }
-// @has impl_parts/struct.Foo.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has impl_parts/struct.Foo.html '//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !AnAutoTrait for Foo<T>where T: Sync + Clone,"
// @has impl_parts/trait.AnAutoTrait.html '//*[@id="implementors-list"]//h3[@class="code-header"]' \
// "impl<T> !AnAutoTrait for Foo<T>where T: Sync + Clone,"
extern crate inline_default_methods;
// @has inline_default_methods/trait.Foo.html
-// @has - '//*[@class="rust trait"]' 'fn bar(&self);'
-// @has - '//*[@class="rust trait"]' 'fn foo(&mut self) { ... }'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn bar(&self);'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn foo(&mut self) { ... }'
pub use inline_default_methods::Foo;
pub struct SomeStruct;
pub fn some_fn() {}
+
+pub enum Shadowed {}
// @has cross_glob/struct.SomeStruct.html
// @has cross_glob/fn.some_fn.html
+// @!has cross_glob/enum.Shadowed.html
// @!has cross_glob/index.html '//code' 'pub use inner::*;'
#[doc(inline)]
pub use inner::*;
+
+// This type shadows the glob-imported enum `Shadowed`.
+// @has cross_glob/type.Shadowed.html
+pub type Shadowed = u8;
pub use dyn_trait::Ty3;
// @has user/fn.func0.html
-// @has - '//pre[@class="rust fn"]' "func0(_: &dyn Fn())"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func0(_: &dyn Fn())"
// FIXME(fmease): Show placeholder-lifetime bound, render "func0(_: &(dyn Fn() + '_))"
pub use dyn_trait::func0;
// @has user/fn.func1.html
-// @has - '//pre[@class="rust fn"]' "func1<'func>(_: &(dyn Fn() + 'func))"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func1<'func>(_: &(dyn Fn() + 'func))"
pub use dyn_trait::func1;
extern crate impl_trait_aux;
// @has impl_trait/fn.func.html
-// @has - '//pre[@class="rust fn"]' "pub fn func<'a>(_x: impl Clone + Into<Vec<u8, Global>> + 'a)"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn func<'a>(_x: impl Clone + Into<Vec<u8, Global>> + 'a)"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func;
// @has impl_trait/fn.func2.html
-// @has - '//pre[@class="rust fn"]' "func2<T>("
-// @has - '//pre[@class="rust fn"]' "_x: impl Deref<Target = Option<T>> + Iterator<Item = T>,"
-// @has - '//pre[@class="rust fn"]' "_y: impl Iterator<Item = u8>)"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func2<T>("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_x: impl Deref<Target = Option<T>> + Iterator<Item = T>,"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_y: impl Iterator<Item = u8>)"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func2;
// @has impl_trait/fn.func3.html
-// @has - '//pre[@class="rust fn"]' "func3("
-// @has - '//pre[@class="rust fn"]' "_x: impl Iterator<Item = impl Iterator<Item = u8>> + Clone)"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func3("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_x: impl Iterator<Item = impl Iterator<Item = u8>> + Clone)"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func3;
// @has impl_trait/fn.func4.html
-// @has - '//pre[@class="rust fn"]' "func4<T>("
-// @has - '//pre[@class="rust fn"]' "T: Iterator<Item = impl Clone>,"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func4<T>("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "T: Iterator<Item = impl Clone>,"
pub use impl_trait_aux::func4;
// @has impl_trait/fn.func5.html
-// @has - '//pre[@class="rust fn"]' "func5("
-// @has - '//pre[@class="rust fn"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
-// @has - '//pre[@class="rust fn"]' "_a: impl for<'alpha, 'beta> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func5("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_a: impl for<'alpha, 'beta> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func5;
// @has impl_trait/fn.async_fn.html
-// @has - '//pre[@class="rust fn"]' "pub async fn async_fn()"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn async_fn()"
pub use impl_trait_aux::async_fn;
// @has impl_trait/struct.Foo.html
extern crate rustdoc_nonreachable_impls;
// @has issue_31948_1/struct.Wobble.html
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Bark for'
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Woof for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
pub use rustdoc_nonreachable_impls::hidden::Wobble;
extern crate rustdoc_nonreachable_impls;
// @has issue_31948_2/struct.Wobble.html
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Qux for'
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Bark for'
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Woof for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
pub use rustdoc_nonreachable_impls::hidden::Wobble;
extern crate rustdoc_nonreachable_impls;
// @has issue_31948/struct.Foo.html
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Bark for'
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Woof for'
-// @!has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'Bar for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bark for'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'Woof for'
+// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'Bar for'
// @!has - '//*[@class="impl"]//h3[@class="code-header"]' 'Qux for'
pub use rustdoc_nonreachable_impls::Foo;
extern crate macros;
-// @has foo/index.html '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab deprecated"]' \
+// @has foo/index.html '//*[@class="item-left unstable deprecated"]/span[@class="stab deprecated"]' \
// Deprecated
-// @has - '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab unstable"]' \
+// @has - '//*[@class="item-left unstable deprecated"]/span[@class="stab unstable"]' \
// Experimental
// @has foo/macro.my_macro.html
--- /dev/null
+// This is a regression test for <https://github.com/rust-lang/rust/issues/107350>.
+// It shouldn't loop indefinitely.
+
+#![crate_name = "foo"]
+
+// @has 'foo/oops/enum.OhNo.html'
+
+pub mod oops {
+ pub use crate::oops::OhNo;
+
+ mod inner {
+ pub enum OhNo {
+ Item = 1,
+ }
+ }
+
+ pub use self::inner::*;
+}
}
// @has issue_20646/fn.fun.html \
-// '//*[@class="rust fn"]' 'where T: Trait<Output = i32>'
+// '//div[@class="item-decl"]/pre[@class="rust"]' 'where T: Trait<Output = i32>'
pub fn fun<T>(_: T) where T: Trait<Output=i32> {}
pub mod reexport {
// '//*[@id="associatedtype.Output"]' \
// 'type Output'
// @has issue_20646/reexport/fn.fun.html \
- // '//*[@class="rust fn"]' 'where T: Trait<Output = i32>'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'where T: Trait<Output = i32>'
pub use issue_20646::{Trait, fun};
}
// @has issue_20727_2/trait.Add.html
pub trait Add<RHS = Self> {
- // @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
- // @has - '//*[@class="rust trait"]' 'type Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Add<RHS = Self> {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output;'
type Output;
- // @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn add(self, rhs: RHS) -> Self::Output;'
fn add(self, rhs: RHS) -> Self::Output;
}
// @has issue_20727_2/reexport/trait.Add.html
pub mod reexport {
- // @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
- // @has - '//*[@class="rust trait"]' 'type Output;'
- // @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Add<RHS = Self> {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn add(self, rhs: RHS) -> Self::Output;'
pub use issue_20727::Add;
}
// @has issue_20727_3/trait.Deref2.html
pub trait Deref2 {
- // @has - '//*[@class="rust trait"]' 'trait Deref2 {'
- // @has - '//*[@class="rust trait"]' 'type Target: Bar;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref2 {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: Bar;'
type Target: Bar;
- // @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn deref(&self) -> Self::Target;'
fn deref(&self) -> Self::Target;
}
// @has issue_20727_3/reexport/trait.Deref2.html
pub mod reexport {
- // @has - '//*[@class="rust trait"]' 'trait Deref2 {'
- // @has - '//*[@class="rust trait"]' 'type Target: Bar;'
- // @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref2 {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: Bar;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn deref(&self) -> Self::Target;'
pub use issue_20727::Deref2;
}
// @has issue_20727_4/trait.Index.html
pub trait Index<Idx: ?Sized> {
- // @has - '//*[@class="rust trait"]' 'trait Index<Idx: ?Sized> {'
- // @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Index<Idx: ?Sized> {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output: ?Sized'
type Output: ?Sized;
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
fn index(&self, index: Idx) -> &Self::Output;
}
// @has issue_20727_4/trait.IndexMut.html
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'trait IndexMut<Idx: ?Sized>: Index<Idx> {'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}
pub mod reexport {
// @has issue_20727_4/reexport/trait.Index.html
- // @has - '//*[@class="rust trait"]' 'trait Index<Idx>where Idx: ?Sized,{'
- // @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Index<Idx>where Idx: ?Sized,{'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output: ?Sized'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
pub use issue_20727::Index;
// @has issue_20727_4/reexport/trait.IndexMut.html
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'trait IndexMut<Idx>: Index<Idx>where Idx: ?Sized,{'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
pub use issue_20727::IndexMut;
}
// @has issue_20727/trait.Deref.html
pub trait Deref {
- // @has - '//*[@class="rust trait"]' 'trait Deref {'
- // @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: ?Sized;'
type Target: ?Sized;
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// "fn deref<'a>(&'a self) -> &'a Self::Target;"
fn deref<'a>(&'a self) -> &'a Self::Target;
}
// @has issue_20727/reexport/trait.Deref.html
pub mod reexport {
- // @has - '//*[@class="rust trait"]' 'trait Deref {'
- // @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: ?Sized;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// "fn deref<'a>(&'a self) -> &'a Self::Target;"
pub use issue_20727::Deref;
}
pub trait Blah { }
// @count issue_21474/struct.What.html \
-// '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
+// '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
pub struct What;
extern "C" {
// @has issue_22038/fn.foo1.html \
- // '//*[@class="rust fn"]' 'pub unsafe extern "C" fn foo1()'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "C" fn foo1()'
pub fn foo1();
}
extern "system" {
// @has issue_22038/fn.foo2.html \
- // '//*[@class="rust fn"]' 'pub unsafe extern "system" fn foo2()'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "system" fn foo2()'
pub fn foo2();
}
// @has issue_22038/fn.bar.html \
-// '//*[@class="rust fn"]' 'pub extern "C" fn bar()'
+// '//div[@class="item-decl"]/pre[@class="rust"]' 'pub extern "C" fn bar()'
pub extern "C" fn bar() {}
// @has issue_22038/fn.baz.html \
-// '//*[@class="rust fn"]' 'pub extern "system" fn baz()'
+// '//div[@class="item-decl"]/pre[@class="rust"]' 'pub extern "system" fn baz()'
pub extern "system" fn baz() {}
#![doc(issue_tracker_base_url = "https://issue_url/")]
#![unstable(feature = "test", issue = "32374")]
-// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab deprecated"]' \
+// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated"]/span[@class="stab deprecated"]' \
// 'Deprecated'
-// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated module-item"]/span[@class="stab unstable"]' \
+// @matches issue_32374/index.html '//*[@class="item-left unstable deprecated"]/span[@class="stab unstable"]' \
// 'Experimental'
// @matches issue_32374/index.html '//*[@class="item-right docblock-short"]/text()' 'Docs'
pub struct S;
// @has issue_33302/constant.CST.html \
- // '//pre[@class="rust const"]' 'pub const CST: i32'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub const CST: i32'
pub const CST: i32 = ($n * $n);
// @has issue_33302/static.ST.html \
- // '//pre[@class="rust static"]' 'pub static ST: i32'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub static ST: i32'
pub static ST: i32 = ($n * $n);
pub trait T<X> {
fn ignore(_: &X) {}
const C: X;
// @has issue_33302/trait.T.html \
- // '//*[@class="rust trait"]' 'const D: i32'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'const D: i32'
// @has - '//*[@id="associatedconstant.D"]' 'const D: i32'
const D: i32 = ($n * $n);
}
// @has issue_33302/struct.S.html \
- // '//*[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S'
+ // '//*[@class="impl"]' 'impl T<[i32; 16]> for S'
// @has - '//*[@id="associatedconstant.C"]' 'const C: [i32; 16]'
// @has - '//*[@id="associatedconstant.D"]' 'const D: i32'
impl T<[i32; ($n * $n)]> for S {
}
// @has issue_33302/struct.S.html \
- // '//*[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S'
+ // '//*[@class="impl"]' 'impl T<[i32; 16]> for S'
// @has - '//*[@id="associatedconstant.C-1"]' 'const C: (i32,)'
// @has - '//*[@id="associatedconstant.D-1"]' 'const D: i32'
impl T<(i32,)> for S {
}
// @has issue_33302/struct.S.html \
- // '//*[@class="impl has-srclink"]' 'impl T<(i32, i32)> for S'
+ // '//*[@class="impl"]' 'impl T<(i32, i32)> for S'
// @has - '//*[@id="associatedconstant.C-2"]' 'const C: (i32, i32)'
// @has - '//*[@id="associatedconstant.D-2"]' 'const D: i32'
impl T<(i32, i32)> for S {
// @has 'foo/struct.Foo1.html'
pub struct Foo1;
-// @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
-// @has - '//*[@class="impl has-srclink"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @has - '//*[@class="impl"]' "impl Bar<Foo1, &'static Foo1> for Foo1"
impl Bar<Foo1, &'static Foo1> for Foo1 {}
// @has 'foo/struct.Foo2.html'
pub struct Foo2;
-// @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
-// @has - '//*[@class="impl has-srclink"]' "impl Bar<&'static Foo2, Foo2> for u8"
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @has - '//*[@class="impl"]' "impl Bar<&'static Foo2, Foo2> for u8"
impl Bar<&'static Foo2, Foo2> for u8 {}
// @has - '//h3[@class="code-header"]' 'impl<B> Send for Switch<B>where <B as Signal>::Item: Send'
// @has - '//h3[@class="code-header"]' 'impl<B> Sync for Switch<B>where <B as Signal>::Item: Sync'
// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
pub struct Switch<B: Signal> {
pub inner: <B as Signal2>::Item2,
}
}
// @has issue_51236/struct.Owned.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> Send for Owned<T>where <T as Owned<'static>>::Reader: Send"
pub struct Owned<T> where T: for<'a> ::traits::Owned<'a> {
marker: PhantomData<<T as ::traits::Owned<'static>>::Reader>,
}
// @has issue_53812/trait.MyIterator.html
-// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][1]' 'MyStruct<[T; 0]>'
-// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][2]' 'MyStruct<[T; 1]>'
-// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][3]' 'MyStruct<[T; 2]>'
-// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][4]' 'MyStruct<[T; 3]>'
-// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][5]' 'MyStruct<[T; 10]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl"][1]' 'MyStruct<[T; 0]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl"][2]' 'MyStruct<[T; 1]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl"][3]' 'MyStruct<[T; 2]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl"][4]' 'MyStruct<[T; 3]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl"][5]' 'MyStruct<[T; 10]>'
array_impls! { 10 3 2 1 0 }
pub trait ScopeHandle<'scope> {}
// @has issue_54705/struct.ScopeFutureContents.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'scope, S> Send for ScopeFutureContents<'scope, S>where S: Sync"
//
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'scope, S> Sync for ScopeFutureContents<'scope, S>where S: Sync"
pub struct ScopeFutureContents<'scope, S>
where S: ScopeHandle<'scope>,
#![feature(negative_impls)]
// @has issue_55321/struct.A.html
-// @has - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl !Send for A"
-// @has - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl !Sync for A"
pub struct A();
impl !Sync for A {}
// @has issue_55321/struct.B.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !Send for B<T>"
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !Sync for B<T>"
pub struct B<T: ?Sized>(A, Box<T>);
// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
// Though there should be such links later
-// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
/// See either [foo] or [bar].
pub mod subtwo {
}
// @has issue_56822/struct.Parser.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'a> Send for Parser<'a>"
pub struct Parser<'a> {
field: <Wrapper<Inner<'a, u8>> as MyTrait>::Output
{}
// @has issue_60726/struct.IntoIter.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !Send for IntoIter<T>"
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !Sync for IntoIter<T>"
pub struct IntoIter<T>{
hello:DynTrait<FooInterface<T>>,
pub struct Struct {}
impl Struct {
- // @has 'issue_76501/struct.Struct.html' '//*[@class="method has-srclink"]' \
+ // @has 'issue_76501/struct.Struct.html' '//*[@class="method"]' \
// 'pub const fn blurp() -> i32'
/// A useless function that always returns 1.
pub const fn blurp() -> i32 {
impl<T: Something> AnAmazingTrait for T {}
// @has 'issue_78673/struct.MyStruct.html'
-// @has - '//*[@class="impl has-srclink"]' 'AnAmazingTrait for MyStruct'
-// @!has - '//*[@class="impl has-srclink"]' 'AnAmazingTrait for T'
+// @has - '//*[@class="impl"]' 'AnAmazingTrait for MyStruct'
+// @!has - '//*[@class="impl"]' 'AnAmazingTrait for T'
pub struct MyStruct;
impl AnAmazingTrait for MyStruct {}
// generic structs may have _both_ specific and blanket impls that apply
// @has 'issue_78673/struct.AnotherStruct.html'
-// @has - '//*[@class="impl has-srclink"]' 'AnAmazingTrait for AnotherStruct<()>'
-// @has - '//*[@class="impl has-srclink"]' 'AnAmazingTrait for T'
+// @has - '//*[@class="impl"]' 'AnAmazingTrait for AnotherStruct<()>'
+// @has - '//*[@class="impl"]' 'AnAmazingTrait for T'
pub struct AnotherStruct<T>(T);
impl<T: Something> Something for AnotherStruct<T> {}
#[doc(inline)]
pub use sub::*;
-// @count foo/index.html '//a[@class="mod"][@title="foo::prelude mod"]' 1
+// @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
// @count foo/prelude/index.html '//div[@class="item-row"]' 0
pub mod prelude {}
}
}
-// @count foo/index.html '//a[@class="mod"][@title="foo::prelude mod"]' 1
+// @count foo/index.html '//a[@class="mod"][@title="mod foo::prelude"]' 1
// @count foo/prelude/index.html '//div[@class="item-row"]' 0
pub mod prelude {}
extern crate issue_85454;
// @has foo/trait.FromResidual.html
-// @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
pub trait FromResidual<R = <Self as Try>::Residual> {
fn from_residual(residual: R) -> Self;
}
pub mod reexport {
// @has foo/reexport/trait.FromResidual.html
- // @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
pub use issue_85454::*;
}
-// @has issue_95873/index.html "//*[@class='item-left import-item']" "pub use ::std as x;"
+// @has issue_95873/index.html "//*[@class='item-left']" "pub use ::std as x;"
pub use ::std as x;
extern crate issue_98697_reexport_with_anonymous_lifetime;
-// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>()where F: Fn(&str)'
-// @!has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'for<'
+// @has issue_98697/fn.repro.html '//div[@class="item-decl"]/pre[@class="rust"]/code' 'fn repro<F>()where F: Fn(&str)'
+// @!has issue_98697/fn.repro.html '//div[@class="item-decl"]/pre[@class="rust"]/code' 'for<'
pub use issue_98697_reexport_with_anonymous_lifetime::repro;
// @has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra'
pub use issue_99221_aux::*;
-// @count foo/index.html '//a[@class="struct"][@title="foo::Print struct"]' 1
+// @count foo/index.html '//a[@class="struct"][@title="struct foo::Print"]' 1
pub struct Print;
pub use issue_99734_aux::*;
-// @count foo/index.html '//a[@class="fn"][@title="foo::main fn"]' 1
+// @count foo/index.html '//a[@class="fn"][@title="fn foo::main"]' 1
extern "C" {
pub fn main() -> std::ffi::c_int;
pub use issue_99734_aux::*;
-// @count foo/index.html '//a[@class="mod"][@title="foo::task mod"]' 1
+// @count foo/index.html '//a[@class="mod"][@title="mod foo::task"]' 1
pub mod task {}
#![feature(rustc_attrs)]
// @has 'foo/fn.foo.html'
-// @has - '//*[@class="rust fn"]' 'fn foo(x: usize, const Y: usize, z: usize) -> [usize; 3]'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn foo(x: usize, const Y: usize, z: usize) -> [usize; 3]'
#[rustc_legacy_const_generics(1)]
pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
[x, Y, z]
}
// @has 'foo/fn.bar.html'
-// @has - '//*[@class="rust fn"]' 'fn bar(x: usize, const Y: usize, const Z: usize) -> [usize; 3]'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn bar(x: usize, const Y: usize, const Z: usize) -> [usize; 3]'
#[rustc_legacy_const_generics(1, 2)]
pub fn bar<const Y: usize, const Z: usize>(x: usize) -> [usize; 3] {
[x, Y, z]
#![crate_name = "foo"]
// @has 'foo/type.Resolutions.html'
-// @has - '//*[@class="rust typedef"]' "pub type Resolutions<'tcx> = &'tcx u8;"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub type Resolutions<'tcx> = &'tcx u8;"
pub type Resolutions<'tcx> = &'tcx u8;
pub struct Foo;
-// @count foo/struct.Foo.html '//*[@class="impl-items"]//*[@class="method has-srclink"]' 2
+// @count foo/struct.Foo.html '//*[@class="impl-items"]//*[@class="method"]' 2
// @!has - '//*[@class="impl-items"]//*[@class="method"]' 'mut'
impl Foo {
pub fn foo(mut self) {}
pub fn bar(mut bar: ()) {}
}
-// @count foo/fn.baz.html '//*[@class="rust fn"]' 1
-// @!has - '//*[@class="rust fn"]' 'mut'
+// @count foo/fn.baz.html '//div[@class="item-decl"]/pre[@class="rust"]' 1
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'mut'
pub fn baz(mut foo: Foo) {}
// @matches negative_impl/struct.Bravo.html '//pre' "pub struct Bravo<B>"
pub struct Bravo<B>(B);
-// @matches negative_impl/struct.Alpha.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @matches negative_impl/struct.Alpha.html '//*[@class="impl"]//h3[@class="code-header"]' \
// "impl !Send for Alpha"
impl !Send for Alpha {}
-// @matches negative_impl/struct.Bravo.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' "\
+// @matches negative_impl/struct.Bravo.html '//*[@class="impl"]//h3[@class="code-header"]' "\
// impl<B> !Send for Bravo<B>"
impl<B> !Send for Bravo<B> {}
type X = <() as Trait>::X;
}
-// @has 'normalize_assoc_item/fn.f.html' '//pre[@class="rust fn"]' 'pub fn f() -> isize'
+// @has 'normalize_assoc_item/fn.f.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f() -> isize'
pub fn f() -> <usize as Trait>::X {
0
}
-// @has 'normalize_assoc_item/fn.f2.html' '//pre[@class="rust fn"]' 'pub fn f2() -> fn() -> i32'
+// @has 'normalize_assoc_item/fn.f2.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f2() -> fn() -> i32'
pub fn f2() -> <isize as Trait>::X {
todo!()
}
// These can't be normalized because they depend on a generic parameter.
// However the user can choose whether the text should be displayed as `Inner::X` or `<Inner as Trait>::X`.
-// @has 'normalize_assoc_item/struct.Unknown.html' '//pre[@class="rust struct"]' 'pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);'
+// @has 'normalize_assoc_item/struct.Unknown.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);'
pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);
-// @has 'normalize_assoc_item/struct.Unknown2.html' '//pre[@class="rust struct"]' 'pub struct Unknown2<Inner: Trait>(pub Inner::X);'
+// @has 'normalize_assoc_item/struct.Unknown2.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Unknown2<Inner: Trait>(pub Inner::X);'
pub struct Unknown2<Inner: Trait>(pub Inner::X);
trait Lifetimes<'a> {
type Y = &'a isize;
}
-// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust fn"]' "pub fn g() -> &isize"
+// @has 'normalize_assoc_item/fn.g.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn g() -> &isize"
pub fn g() -> <usize as Lifetimes<'static>>::Y {
&0
}
-// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust const"]' "pub const A: &isize"
+// @has 'normalize_assoc_item/constant.A.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub const A: &isize"
pub const A: <usize as Lifetimes<'static>>::Y = &0;
// test cross-crate re-exports
extern crate inner;
-// @has 'normalize_assoc_item/fn.foo.html' '//pre[@class="rust fn"]' "pub fn foo() -> i32"
+// @has 'normalize_assoc_item/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn foo() -> i32"
pub use inner::foo;
-// @has 'normalize_assoc_item/fn.h.html' '//pre[@class="rust fn"]' "pub fn h<T>() -> IntoIter<T, Global>"
+// @has 'normalize_assoc_item/fn.h.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn h<T>() -> IntoIter<T, Global>"
pub fn h<T>() -> <Vec<T> as IntoIterator>::IntoIter {
vec![].into_iter()
}
pub fn dummy() {}
// ensure that `extern crate foo;` was inserted into code snips automatically:
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern%20crate%20r%23foo%3B%0Afn%20main()%20%7B%0Ause%20foo%3A%3Adummy%3B%0Adummy()%3B%0A%7D&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://example.com/?code=%23!%5Ballow(unused)%5D%0Aextern+crate+r%23foo;%0Afn+main()+%7B%0Ause+foo::dummy;%0Adummy();%0A%7D&edition=2015"]' "Run"
//! }
//! ```
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&edition=2015"]' "Run"
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn%20main()%20%7B%0Aprintln!(%22Hello%2C%20world!%22)%3B%0A%7D&edition=2015"]' "Run"
-// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn%20main()%20%7B%0A%20%20%20%20println!(%22Hello%2C%20world!%22)%3B%0A%7D&version=nightly&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0Aprintln!(%22Hello,+world!%22);%0A%7D&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&edition=2015"]' "Run"
+// @matches foo/index.html '//a[@class="test-arrow"][@href="https://www.example.com/?code=%23!%5Ballow(unused)%5D%0A%23!%5Bfeature(something)%5D%0A%0Afn+main()+%7B%0A++++println!(%22Hello,+world!%22);%0A%7D&version=nightly&edition=2015"]' "Run"
// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// There should be only one implementation listed.
-// @count - '//*[@class="impl has-srclink"]' 1
+// @count - '//*[@class="impl"]' 1
// @has - '//*[@id="impl-Foo%3C%26A%3E-for-%26B"]/*[@class="code-header"]' \
// 'impl<A, B> Foo<&A> for &B'
#[doc(primitive = "reference")]
#![crate_name = "foo"]
// @has foo/fn.bar.html
-// @has - '//*[@class="rust fn"]' 'pub fn bar() -> '
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn bar() -> '
/// foo
pub fn bar() -> usize {
2
}
// @has foo/struct.Foo.html
-// @has - '//*[@class="method has-srclink"]' 'pub fn new()'
-// @has - '//*[@class="method has-srclink"]' 'fn not_pub()'
+// @has - '//*[@class="method"]' 'pub fn new()'
+// @has - '//*[@class="method"]' 'fn not_pub()'
pub struct Foo(usize);
impl Foo {
#![crate_name = "foo"]
// @has foo/fn.f.html
-// @has - '//*[@class="rust fn"]' 'pub fn f(_: u8)'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f(_: u8)'
pub fn f(0u8..=255: u8) {}
extern crate reexport_check;
// @!has 'foo/index.html' '//code' 'pub use self::i32;'
-// @has 'foo/index.html' '//div[@class="item-left deprecated module-item"]' 'i32'
+// @has 'foo/index.html' '//div[@class="item-left deprecated"]' 'i32'
// @has 'foo/i32/index.html'
#[allow(deprecated, deprecated_in_future)]
pub use std::i32;
// @!has 'foo/index.html' '//code' 'pub use self::string::String;'
-// @has 'foo/index.html' '//div[@class="item-left module-item"]' 'String'
+// @has 'foo/index.html' '//div[@class="item-left"]' 'String'
pub use std::string::String;
// @has 'foo/index.html' '//div[@class="item-right docblock-short"]' 'Docs in original'
// @!has 'foo/enum.BarLocal.html'
use reexports::BarLocal;
-// @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+// @has 'foo/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
// @!has 'foo/fn.foo_crate.html'
pub(crate) use reexports::foo_crate;
// @!has 'foo/fn.foo_local.html'
use reexports::foo_local;
-// @has 'foo/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+// @has 'foo/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
// @!has 'foo/type.TypeCrate.html'
pub(crate) use reexports::TypeCrate;
// @!has 'foo/outer/inner/enum.BarLocal.html'
use reexports::BarLocal;
- // @has 'foo/outer/inner/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+ // @has 'foo/outer/inner/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
- // @has 'foo/outer/inner/fn.foo_crate.html' '//*[@class="rust fn"]' 'pub(crate) fn foo_crate()'
+ // @has 'foo/outer/inner/fn.foo_crate.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(crate) fn foo_crate()'
pub(crate) use reexports::foo_crate;
- // @has 'foo/outer/inner/fn.foo_super.html' '//*[@class="rust fn"]' 'pub(in outer) fn foo_super()'
+ // @has 'foo/outer/inner/fn.foo_super.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(in outer) fn foo_super()'
pub(super) use::reexports::foo_super;
// @!has 'foo/outer/inner/fn.foo_self.html'
pub(self) use reexports::foo_self;
// @!has 'foo/outer/inner/fn.foo_local.html'
use reexports::foo_local;
- // @has 'foo/outer/inner/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+ // @has 'foo/outer/inner/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
- // @has 'foo/outer/inner/type.TypeCrate.html' '//*[@class="rust typedef"]' 'pub(crate) type TypeCrate ='
+ // @has 'foo/outer/inner/type.TypeCrate.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(crate) type TypeCrate ='
pub(crate) use reexports::TypeCrate;
- // @has 'foo/outer/inner/type.TypeSuper.html' '//*[@class="rust typedef"]' 'pub(in outer) type TypeSuper ='
+ // @has 'foo/outer/inner/type.TypeSuper.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(in outer) type TypeSuper ='
pub(super) use reexports::TypeSuper;
// @!has 'foo/outer/inner/type.TypeSelf.html'
pub(self) use reexports::TypeSelf;
// @!has 'foo/enum.BarLocal.html'
use reexports::BarLocal;
-// @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+// @has 'foo/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
// @!has 'foo/fn.foo_crate.html'
pub(crate) use reexports::foo_crate;
// @!has 'foo/fn.foo_local.html'
use reexports::foo_local;
-// @has 'foo/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+// @has 'foo/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
// @!has 'foo/type.TypeCrate.html'
pub(crate) use reexports::TypeCrate;
// @!has 'foo/outer/inner/enum.BarLocal.html'
use reexports::BarLocal;
- // @has 'foo/outer/inner/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+ // @has 'foo/outer/inner/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
// @!has 'foo/outer/inner/fn.foo_crate.html'
pub(crate) use reexports::foo_crate;
// @!has 'foo/outer/inner/fn.foo_local.html'
use reexports::foo_local;
- // @has 'foo/outer/inner/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+ // @has 'foo/outer/inner/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
// @!has 'foo/outer/inner/type.TypeCrate.html'
pub(crate) use reexports::TypeCrate;
pub struct S<T>(T);
-// @!has foo/trait.Tr.html '//pre[@class="rust trait"]/code/a[@class="trait"]' '~const'
-// @has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Clone'
-// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' '~const'
-// @has - '//pre[@class="rust trait"]/code/span[@class="where"]' ': Clone'
+// @!has foo/trait.Tr.html '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' 'Clone'
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where"]' ': Clone'
#[const_trait]
pub trait Tr<T> {
// @!has - '//section[@id="method.a"]/h4[@class="code-header"]' '~const'
}
}
-// @!has foo/fn.foo.html '//pre[@class="rust fn"]/code/a[@class="trait"]' '~const'
-// @has - '//pre[@class="rust fn"]/code/a[@class="trait"]' 'Clone'
-// @!has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' '~const'
-// @has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' ': Clone'
+// @!has foo/fn.foo.html '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' 'Clone'
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where fmt-newline"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where fmt-newline"]' ': Clone'
pub const fn foo<F: ~const Clone + ~const Destruct>()
where
Option<F>: ~const Clone + ~const Destruct,
extern "rust-intrinsic" {
// @has 'foo/fn.abort.html'
- // @has - '//pre[@class="rust fn"]' 'pub extern "rust-intrinsic" fn abort() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub extern "rust-intrinsic" fn abort() -> !'
#[rustc_safe_intrinsic]
pub fn abort() -> !;
// @has 'foo/fn.unreachable.html'
- // @has - '//pre[@class="rust fn"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
pub fn unreachable() -> !;
}
extern "C" {
// @has 'foo/fn.needs_drop.html'
- // @has - '//pre[@class="rust fn"]' 'pub unsafe extern "C" fn needs_drop() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "C" fn needs_drop() -> !'
pub fn needs_drop() -> !;
}
pub struct MyBox<T: ?Sized>(*const T);
// @has 'foo/fn.alpha.html'
-// @snapshot link_slice_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn alpha() -> &'static [u32] {
loop {}
}
// @has 'foo/fn.beta.html'
-// @snapshot link_slice_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn beta<T>() -> &'static [T] {
loop {}
}
// @has 'foo/fn.gamma.html'
-// @snapshot link_box_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn gamma() -> MyBox<[u32]> {
loop {}
}
// @has 'foo/fn.delta.html'
-// @snapshot link_box_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn delta<T>() -> MyBox<[T]> {
loop {}
}
-<script type="text/json" id="notable-traits-data">{"Odd":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html\" title=\"trait core::iter::traits::iterator::Iterator\">Iterator</a> for <a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></span><span class=\"where fmt-newline\"> type <a href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html#associatedtype.Item\" class=\"associatedtype\">Item</a> = <a class=\"primitive\" href=\"{{channel}}/std/primitive.usize.html\">usize</a>;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"Odd":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html\" title=\"trait core::iter::traits::iterator::Iterator\">Iterator</a> for <a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></span><span class=\"where fmt-newline\"> type <a href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html#associatedtype.Item\" class=\"associatedtype\">Item</a> = <a class=\"primitive\" href=\"{{channel}}/std/primitive.usize.html\">usize</a>;</span>"}</script>
\ No newline at end of file
hir_id: usize,
}
-// @has 'foo/fn.body_owner.html' '//*[@class="rust fn"]' 'pub fn body_owner(_: BodyId)'
+// @has 'foo/fn.body_owner.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn body_owner(_: BodyId)'
pub fn body_owner(BodyId { hir_id }: BodyId) {
// ...
}
// @has basic/struct.Foo.html
// @has - '//h3[@class="code-header"]' 'impl<T> Send for Foo<T>where T: Send'
// @has - '//h3[@class="code-header"]' 'impl<T> Sync for Foo<T>where T: Sync'
-// @count - '//*[@id="implementations-list"]//*[@class="impl has-srclink"]' 0
-// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 5
+// @count - '//*[@id="implementations-list"]//*[@class="impl"]' 0
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 5
pub struct Foo<T> {
field: T,
}
}
// @has complex/struct.NotOuter.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'a, T, K: ?Sized> Send for Outer<'a, T, K>where K: for<'b> Fn((&'b bool, &'a u8)) \
// -> &'b i8, T: MyTrait<'a>, <T as MyTrait<'a>>::MyItem: Copy, 'a: 'static"
{}
// @has lifetimes/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'c, K> Send for Foo<'c, K>where K: for<'b> Fn(&'b bool) -> &'c u8, 'c: 'static"
//
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'c, K> Sync for Foo<'c, K>where K: Sync"
pub struct Foo<'c, K: 'c> {
inner_field: Inner<'c, K>,
// @has manual/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// 'impl<T> Sync for Foo<T>where T: Sync'
//
-// @has - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="trait-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// 'impl<T> Send for Foo<T>'
//
-// @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
-// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]' 4
+// @count - '//*[@id="trait-implementations-list"]//*[@class="impl"]' 1
+// @count - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]' 4
pub struct Foo<T> {
field: T,
}
}
// @has negative/struct.Outer.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !Send for Outer<T>"
//
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> !Sync for Outer<T>"
pub struct Outer<T: Copy> {
inner_field: Inner<T>,
}
// @has nested/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// 'impl<T> Send for Foo<T>where T: Copy'
//
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// 'impl<T> Sync for Foo<T>where T: Sync'
pub struct Foo<T> {
inner_field: Inner<T>,
}
// @has no_redundancy/struct.Outer.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> Send for Outer<T>where T: Send + Copy"
pub struct Outer<T> {
inner_field: Inner<T>,
}
// @has project/struct.Foo.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'c, K> Send for Foo<'c, K>where K: MyTrait<MyItem = bool>, 'c: 'static"
//
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<'c, K> Sync for Foo<'c, K>where K: MyTrait, <K as MyTrait>::MyItem: OtherTrait, \
// 'c: 'static,"
pub struct Foo<'c, K: 'c> {
// @has self_referential/struct.WriteAndThen.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<P1> Send for WriteAndThen<P1>where <P1 as Pattern>::Value: Send"
pub struct WriteAndThen<P1>(pub P1::Value,pub <Constrain<P1, Wrapper<P1::Value>> as Pattern>::Value)
where P1: Pattern;
}
// @has static_region/struct.Owned.html
-// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has - '//*[@id="synthetic-implementations-list"]//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<T> Send for Owned<T>where <T as OwnedTrait<'static>>::Reader: Send"
pub struct Owned<T> where T: OwnedTrait<'static> {
marker: <T as OwnedTrait<'static>>::Reader,
#![crate_name = "foo"]
// @has foo/fn.foo.html
-// @has - '//*[@class="rust fn"]' "_: &(dyn ToString + 'static)"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_: &(dyn ToString + 'static)"
pub fn foo(_: &(ToString + 'static)) {}
}
// @has 'toggle_item_contents/enum.LargeEnum.html'
-// @count - '//*[@class="rust enum"]//details[@class="toggle type-contents-toggle"]' 1
-// @has - '//*[@class="rust enum"]//details[@class="toggle type-contents-toggle"]' 'Show 13 variants'
+// @count - '//div[@class="item-decl"]/pre//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//div[@class="item-decl"]/pre//details[@class="toggle type-contents-toggle"]' 'Show 13 variants'
pub enum LargeEnum {
A, B, C, D, E, F(u8), G, H, I, J, K, L, M
}
);
// @has foo/enum.Bar.html
-// @has - '//pre[@class="rust enum"]' 'BarVariant(String),'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'BarVariant(String),'
// @matches - '//*[@id="variant.BarVariant.fields"]/h4' '^Tuple Fields$'
// @has - '//*[@id="variant.BarVariant.field.0"]' '0: String'
// @has - '//*[@id="variant.BarVariant.fields"]//*[@class="docblock"]' 'Hello docs'
#![crate_name = "foo"]
// @has foo/fn.tuple0.html //pre 'pub fn tuple0(x: ())'
-// @snapshot link_unit - '//pre[@class="rust fn"]/code'
+// @snapshot link_unit - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple0(x: ()) -> () { x }
// @has foo/fn.tuple1.html //pre 'pub fn tuple1(x: (i32,)) -> (i32,)'
-// @snapshot link1_i32 - '//pre[@class="rust fn"]/code'
+// @snapshot link1_i32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple1(x: (i32,)) -> (i32,) { x }
// @has foo/fn.tuple2.html //pre 'pub fn tuple2(x: (i32, i32)) -> (i32, i32)'
-// @snapshot link2_i32 - '//pre[@class="rust fn"]/code'
+// @snapshot link2_i32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple2(x: (i32, i32)) -> (i32, i32) { x }
// @has foo/fn.tuple1_t.html //pre 'pub fn tuple1_t<T>(x: (T,)) -> (T,)'
-// @snapshot link1_t - '//pre[@class="rust fn"]/code'
+// @snapshot link1_t - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple1_t<T>(x: (T,)) -> (T,) { x }
// @has foo/fn.tuple2_t.html //pre 'pub fn tuple2_t<T>(x: (T, T)) -> (T, T)'
-// @snapshot link2_t - '//pre[@class="rust fn"]/code'
+// @snapshot link2_t - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple2_t<T>(x: (T, T)) -> (T, T) { x }
// @has foo/fn.tuple2_tu.html //pre 'pub fn tuple2_tu<T, U>(x: (T, U)) -> (T, U)'
-// @snapshot link2_tu - '//pre[@class="rust fn"]/code'
+// @snapshot link2_tu - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple2_tu<T, U>(x: (T, U)) -> (T, U) { x }
}
// @has typedef/type.MyAlias.html
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'impl MyAlias'
-// @has - '//*[@class="impl has-srclink"]//h3[@class="code-header"]' 'impl MyTrait for MyAlias'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl MyAlias'
+// @has - '//*[@class="impl"]//h3[@class="code-header"]' 'impl MyTrait for MyAlias'
// @hasraw - 'Alias docstring'
// @has - '//*[@class="sidebar"]//*[@class="location"]' 'MyAlias'
// @has - '//*[@class="sidebar"]//a[@href="#implementations"]' 'Methods'
extern crate unit_return;
-// @has 'foo/fn.f0.html' '//*[@class="rust fn"]' 'F: FnMut(u8) + Clone'
+// @has 'foo/fn.f0.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u8) + Clone'
pub fn f0<F: FnMut(u8) + Clone>(f: F) {}
-// @has 'foo/fn.f1.html' '//*[@class="rust fn"]' 'F: FnMut(u16) + Clone'
+// @has 'foo/fn.f1.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u16) + Clone'
pub fn f1<F: FnMut(u16) -> () + Clone>(f: F) {}
-// @has 'foo/fn.f2.html' '//*[@class="rust fn"]' 'F: FnMut(u32) + Clone'
+// @has 'foo/fn.f2.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u32) + Clone'
pub use unit_return::f2;
-// @has 'foo/fn.f3.html' '//*[@class="rust fn"]' 'F: FnMut(u64) + Clone'
+// @has 'foo/fn.f3.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u64) + Clone'
pub use unit_return::f3;
#![crate_name = "foo"]
// @has foo/fn.foo.html
-// @has - '//*[@class="rust fn"]' 'pub fn foo<X, Y: ?Sized>(_: &X)'
-// @has - '//*[@class="rust fn"]' 'where X: ?Sized,'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo<X, Y: ?Sized>(_: &X)'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where X: ?Sized,'
pub fn foo<X, Y: ?Sized>(_: &X) where X: ?Sized {}
-<div class="item-decl"><pre class="rust struct"><code>pub struct Simd<T>(_)<br /><span class="where">where<br />    T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre></div>
\ No newline at end of file
+<div class="item-decl"><pre class="rust"><code>pub struct Simd<T>(_)<br /><span class="where">where<br />    T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust trait"><code>pub trait TraitWhere {
+<div class="item-decl"><pre class="rust"><code>pub trait TraitWhere {
type <a href="#associatedtype.Item" class="associatedtype">Item</a><'a><br />    <span class="where">where<br />        Self: 'a</span>;
fn <a href="#method.func" class="fn">func</a>(self)<br />    <span class="where">where<br />        Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
pub struct Delta<D>(D);
-// @has foo/struct.Delta.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has foo/struct.Delta.html '//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<D> Delta<D>where D: MyTrait"
impl<D> Delta<D> where D: MyTrait {
pub fn delta() {}
{ todo!() }
}
-// @has foo/struct.Echo.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has foo/struct.Echo.html '//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<E> MyTrait for Echo<E>where E: MyTrait"
// @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header"]' \
// "impl<E> MyTrait for Echo<E>where E: MyTrait"
pub enum Foxtrot<F> { Foxtrot1(F) }
-// @has foo/enum.Foxtrot.html '//*[@class="impl has-srclink"]//h3[@class="code-header"]' \
+// @has foo/enum.Foxtrot.html '//*[@class="impl"]//h3[@class="code-header"]' \
// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
// @has foo/trait.MyTrait.html '//*[@id="implementors-list"]//h3[@class="code-header"]' \
// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
impl<F> MyTrait for Foxtrot<F>where F: MyTrait {}
-// @has foo/type.Golf.html '//pre[@class="rust typedef"]' \
+// @has foo/type.Golf.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// "type Golf<T>where T: Clone, = (T, T)"
pub type Golf<T> where T: Clone = (T, T);
-<div class="item-decl"><pre class="rust enum"><code>pub enum Cow<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
- Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B),
+<div class="item-decl"><pre class="rust"><code>pub enum Cow<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
+ Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>),
Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust enum"><code>pub enum Cow2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
- Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B),
+<div class="item-decl"><pre class="rust"><code>pub enum Cow2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+ Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>),
Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust struct"><code>pub struct Struct<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
- pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B,
+<div class="item-decl"><pre class="rust"><code>pub struct Struct<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
+ pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>,
pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust struct"><code>pub struct Struct2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
- pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B,
+<div class="item-decl"><pre class="rust"><code>pub struct Struct2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+ pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>,
pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust trait"><code>pub trait ToOwned<T><span class="where fmt-newline">where<br />    T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</span>{
+<div class="item-decl"><pre class="rust"><code>pub trait ToOwned<T><span class="where fmt-newline">where<br />    T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</span>{
type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&self) -> Self::<a class="associatedtype" href="trait.ToOwned.html#associatedtype.Owned" title="type foo::ToOwned::Owned">Owned</a>;
-<div class="item-decl"><pre class="rust trait"><code>pub trait ToOwned2<T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> {
+<div class="item-decl"><pre class="rust"><code>pub trait ToOwned2<T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> {
type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&self) -> Self::<a class="associatedtype" href="trait.ToOwned2.html#associatedtype.Owned" title="type foo::ToOwned2::Owned">Owned</a>;
-<div class="item-decl"><pre class="rust union"><code>pub union Union<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
+<div class="item-decl"><pre class="rust"><code>pub union Union<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
/* private fields */
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust union"><code>pub union Union2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+<div class="item-decl"><pre class="rust"><code>pub union Union2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
/* private fields */
}</code></pre></div>
\ No newline at end of file
use std::fmt::Debug;
-// @has 'wrapping/fn.foo.html' '//pre[@class="rust fn"]' 'pub fn foo() -> impl Debug'
-// @count - '//pre[@class="rust fn"]/br' 0
+// @has 'wrapping/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo() -> impl Debug'
+// @count - '//div[@class="item-decl"]/pre[@class="rust"]/br' 0
pub fn foo() -> impl Debug {}
use rustc_hir::intravisit;
use rustc_hir::Node;
use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map;
#[no_mangle]
_: &'tcx hir::FnDecl,
_: &'tcx hir::Body,
span: source_map::Span,
- id: hir::HirId,
+ def_id: LocalDefId,
) {
+ let id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
let item = match cx.tcx.hir().get(id) {
Node::Item(item) => item,
_ => cx.tcx.hir().expect_item(cx.tcx.hir().get_parent_item(id).def_id),
error[E0597]: `arena` does not live long enough
--> $DIR/dropck-tarena-cycle-checked.rs:116:7
|
+LL | let arena = TypedArena::default();
+ | ----- binding `arena` declared here
LL | f(&arena);
| ^^^^^^ borrowed value does not live long enough
LL | }
error[E0597]: `arena` does not live long enough
--> $DIR/dropck-tarena-unsound-drop.rs:41:7
|
+LL | let arena: TypedArena<C> = TypedArena::default();
+ | ----- binding `arena` declared here
LL | f(&arena);
| ^^^^^^ borrowed value does not live long enough
LL | }
--- /dev/null
+missing_message_ref = {message}
use self::fluent_generated::{DEFAULT_LOCALE_RESOURCES, test_crate_foo, with_hyphens};
}
+
+mod missing_message_ref {
+ use super::fluent_messages;
+
+ fluent_messages! {
+ missing => "./missing-message-ref.ftl"
+//~^ ERROR referenced message `message` does not exist
+ }
+}
|
= help: replace any '-'s with '_'s
-error: aborting due to 10 previous errors
+error: referenced message `message` does not exist (in message `missing_message_ref`)
+ --> $DIR/test.rs:104:20
+ |
+LL | missing => "./missing-message-ref.ftl"
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: you may have meant to use a variable reference (`{$message}`)
+
+error: aborting due to 11 previous errors
For more information about this error, try `rustc --explain E0428`.
TyKind::Closure(..) => (), //~ ERROR usage of `ty::TyKind::<kind>`
TyKind::Generator(..) => (), //~ ERROR usage of `ty::TyKind::<kind>`
TyKind::GeneratorWitness(..) => (), //~ ERROR usage of `ty::TyKind::<kind>`
+ TyKind::GeneratorWitnessMIR(..) => (), //~ ERROR usage of `ty::TyKind::<kind>`
TyKind::Never => (), //~ ERROR usage of `ty::TyKind::<kind>`
TyKind::Tuple(..) => (), //~ ERROR usage of `ty::TyKind::<kind>`
TyKind::Alias(..) => (), //~ ERROR usage of `ty::TyKind::<kind>`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:34:9
|
-LL | TyKind::Never => (),
+LL | TyKind::GeneratorWitnessMIR(..) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:35:9
|
-LL | TyKind::Tuple(..) => (),
+LL | TyKind::Never => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:36:9
|
-LL | TyKind::Alias(..) => (),
+LL | TyKind::Tuple(..) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:37:9
|
-LL | TyKind::Param(..) => (),
+LL | TyKind::Alias(..) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:38:9
|
-LL | TyKind::Bound(..) => (),
+LL | TyKind::Param(..) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:39:9
|
-LL | TyKind::Placeholder(..) => (),
+LL | TyKind::Bound(..) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:40:9
|
-LL | TyKind::Infer(..) => (),
+LL | TyKind::Placeholder(..) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
--> $DIR/ty_tykind_usage.rs:41:9
|
+LL | TyKind::Infer(..) => (),
+ | ^^^^^^ help: try using `ty::<kind>` directly: `ty`
+
+error: usage of `ty::TyKind::<kind>`
+ --> $DIR/ty_tykind_usage.rs:42:9
+ |
LL | TyKind::Error(_) => (),
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind::<kind>`
- --> $DIR/ty_tykind_usage.rs:46:12
+ --> $DIR/ty_tykind_usage.rs:47:12
|
LL | if let TyKind::Int(int_ty) = kind {}
| ^^^^^^ help: try using `ty::<kind>` directly: `ty`
error: usage of `ty::TyKind`
- --> $DIR/ty_tykind_usage.rs:48:24
+ --> $DIR/ty_tykind_usage.rs:49:24
|
LL | fn ty_kind(ty_bad: TyKind<'_>, ty_good: Ty<'_>) {}
| ^^^^^^^^^^
= help: try using `Ty` instead
error: usage of `ty::TyKind`
- --> $DIR/ty_tykind_usage.rs:50:37
+ --> $DIR/ty_tykind_usage.rs:51:37
|
LL | fn ir_ty_kind<I: Interner>(bad: IrTyKind<I>) -> IrTyKind<I> {
| ^^^^^^^^^^^
= help: try using `Ty` instead
error: usage of `ty::TyKind`
- --> $DIR/ty_tykind_usage.rs:50:53
+ --> $DIR/ty_tykind_usage.rs:51:53
|
LL | fn ir_ty_kind<I: Interner>(bad: IrTyKind<I>) -> IrTyKind<I> {
| ^^^^^^^^^^^
= help: try using `Ty` instead
error: usage of `ty::TyKind::<kind>`
- --> $DIR/ty_tykind_usage.rs:53:9
+ --> $DIR/ty_tykind_usage.rs:54:9
|
LL | IrTyKind::Bool
| --------^^^^^^
| |
| help: try using `ty::<kind>` directly: `ty`
-error: aborting due to 32 previous errors
+error: aborting due to 33 previous errors
// Testing that a librustc_ast can parse modules with canonicalized base path
// ignore-cross-compile
// ignore-remote
+// no-remap-src-base: Reading `file!()` (expectedly) fails when enabled.
#![feature(rustc_private)]
help: consider borrowing the argument
|
LL | f1(|_: &(), _: &()| {});
- | ~~~ ~~~
+ | + +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:3:5
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `f2`
help: consider borrowing the argument
|
-LL | f2(|_: &'a (), _: &()| {});
- | ~~~~~~ ~~~
+LL | f2(|_: &(), _: &()| {});
+ | + +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:4:5
help: consider borrowing the argument
|
LL | f3(|_: &(), _: &()| {});
- | ~~~ ~~~
+ | + +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:5:5
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `f4`
help: consider borrowing the argument
|
-LL | f4(|_: &(), _: &'r ()| {});
- | ~~~ ~~~~~~
+LL | f4(|_: &(), _: &()| {});
+ | + +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:6:5
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `f5`
help: consider borrowing the argument
|
-LL | f5(|_: &'r (), _: &'r ()| {});
- | ~~~~~~ ~~~~~~
+LL | f5(|_: &(), _: &()| {});
+ | + +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:7:5
|
LL | g1(|_: (), _: ()| {});
- | ^^ --------------
- | | | |
- | | | help: consider borrowing the argument: `&()`
- | | found signature defined here
+ | ^^ -------------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `for<'a> fn(&'a (), Box<(dyn for<'a> Fn(&'a ()) + 'static)>) -> _`
|
LL | fn g1<F>(_: F) where F: Fn(&(), Box<dyn Fn(&())>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `g1`
+help: consider borrowing the argument
+ |
+LL | g1(|_: &(), _: ()| {});
+ | +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:8:5
|
LL | g2(|_: (), _: ()| {});
- | ^^ --------------
- | | | |
- | | | help: consider borrowing the argument: `&()`
- | | found signature defined here
+ | ^^ -------------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `for<'a> fn(&'a (), for<'a> fn(&'a ())) -> _`
|
LL | fn g2<F>(_: F) where F: Fn(&(), fn(&())) {}
| ^^^^^^^^^^^^^^^^ required by this bound in `g2`
+help: consider borrowing the argument
+ |
+LL | g2(|_: &(), _: ()| {});
+ | +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:9:5
|
LL | g3(|_: (), _: ()| {});
- | ^^ --------------
- | | | |
- | | | help: consider borrowing the argument: `&'s ()`
- | | found signature defined here
+ | ^^ -------------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `for<'s> fn(&'s (), Box<(dyn for<'a> Fn(&'a ()) + 'static)>) -> _`
|
LL | fn g3<F>(_: F) where F: for<'s> Fn(&'s (), Box<dyn Fn(&())>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `g3`
+help: consider borrowing the argument
+ |
+LL | g3(|_: &(), _: ()| {});
+ | +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:10:5
|
LL | g4(|_: (), _: ()| {});
- | ^^ --------------
- | | | |
- | | | help: consider borrowing the argument: `&()`
- | | found signature defined here
+ | ^^ -------------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `for<'a> fn(&'a (), for<'r> fn(&'r ())) -> _`
|
LL | fn g4<F>(_: F) where F: Fn(&(), for<'r> fn(&'r ())) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `g4`
+help: consider borrowing the argument
+ |
+LL | g4(|_: &(), _: ()| {});
+ | +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:11:5
help: consider borrowing the argument
|
LL | h1(|_: &(), _: (), _: &(), _: ()| {});
- | ~~~ ~~~
+ | + +
error[E0631]: type mismatch in closure arguments
--> $DIR/anonymous-higher-ranked-lifetime.rs:12:5
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `h2`
help: consider borrowing the argument
|
-LL | h2(|_: &(), _: (), _: &'t0 (), _: ()| {});
- | ~~~ ~~~~~~~
+LL | h2(|_: &(), _: (), _: &(), _: ()| {});
+ | + +
error: aborting due to 11 previous errors
--> $DIR/type-check-4.rs:14:9
|
LL | let p = &a;
- | -- borrow of `a` occurs here
+ | -- `a` is borrowed here
LL | asm!("{}", out(reg) a);
- | ^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `a` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^ `a` is assigned to here but it was already borrowed
LL |
LL | println!("{}", p);
| - borrow later used here
--> $DIR/type-check-4.rs:22:28
|
LL | let p = &mut a;
- | ------ borrow of `a` occurs here
+ | ------ `a` is borrowed here
LL | asm!("{}", in(reg) a);
| ^ use of borrowed `a`
LL |
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/associated-types-outlives.rs:22:14
|
+LL | F: for<'a> FnOnce(<T as Foo<'a>>::Bar)>(x: T, f: F) {
+ | - binding `x` declared here
+...
LL | 's: loop { y = denormalise(&x); break }
| -- borrow of `x` occurs here
LL | drop(x);
const C: Self::Ty = 0u8;
}
+pub trait Trait {
+ type Res = isize; //~ NOTE associated type defaults can't be assumed inside the trait defining them
+
+ fn infer_me_correctly() -> Self::Res {
+ //~^ NOTE expected `<Self as Trait>::Res` because of return type
+
+ // {integer} == isize
+ 2
+ //~^ ERROR mismatched types
+ //~| NOTE expected associated type, found integer
+ //~| NOTE expected associated type `<Self as Trait>::Res`
+ }
+}
+
fn main() {}
= note: expected associated type `<Self as AssocConst>::Ty`
found type `u8`
-error: aborting due to 2 previous errors
+error[E0308]: mismatched types
+ --> $DIR/defaults-in-other-trait-items.rs:54:9
+ |
+LL | type Res = isize;
+ | ----------------- associated type defaults can't be assumed inside the trait defining them
+LL |
+LL | fn infer_me_correctly() -> Self::Res {
+ | --------- expected `<Self as Trait>::Res` because of return type
+...
+LL | 2
+ | ^ expected associated type, found integer
+ |
+ = note: expected associated type `<Self as Trait>::Res`
+ found type `{integer}`
+
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0308`.
error[E0308]: mismatched types
--> $DIR/issue-26681.rs:17:39
|
+LL | type Fv: Foo = u8;
+ | ------------------ associated type defaults can't be assumed inside the trait defining them
LL | const C: <Self::Fv as Foo>::Bar = 6665;
| ^^^^ expected associated type, found integer
|
= note: expected associated type `<<Self as Baz>::Fv as Foo>::Bar`
found type `{integer}`
- = help: consider constraining the associated type `<<Self as Baz>::Fv as Foo>::Bar` to `{integer}`
- = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error: aborting due to previous error
LL | type Assoc = T;
| ^ the trait `Copy` is not implemented for `T`
|
+note: required for `<T as Complete>::Assoc` to implement `Partial<T>`
+ --> $DIR/issue-43784-associated-type.rs:1:11
+ |
+LL | pub trait Partial<X: ?Sized>: Copy {
+ | ^^^^^^^
note: required by a bound in `Complete::Assoc`
--> $DIR/issue-43784-associated-type.rs:5:17
|
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:48:13
+ |
+LL | is_send(foo(Some(true)));
+ | ^^^^^^^^^^^^^^^ future returned by `foo` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:11:14
+ |
+LL | let r = Rc::new(());
+ | - has type `Rc<()>` which is not `Send`
+LL | bar().await
+ | ^^^^^^ await occurs here, with `r` maybe used later
+LL | };
+ | - `r` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error[E0277]: `Rc<()>` cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:50:13
+ |
+LL | async fn foo2(x: Option<bool>) {
+ | - within this `impl Future<Output = ()>`
+...
+LL | is_send(foo2(Some(true)));
+ | ------- ^^^^^^^^^^^^^^^^ `Rc<()>` cannot be sent between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: required because it's used within this `async fn` body
+ --> $DIR/async-await-let-else.rs:27:29
+ |
+LL | async fn bar2<T>(_: T) -> ! {
+ | _____________________________^
+LL | | panic!()
+LL | | }
+ | |_^
+ = note: required because it captures the following types: `ResumeTy`, `Option<bool>`, `impl Future<Output = !>`, `()`
+note: required because it's used within this `async fn` body
+ --> $DIR/async-await-let-else.rs:21:32
+ |
+LL | async fn foo2(x: Option<bool>) {
+ | ________________________________^
+LL | | let Some(_) = x else {
+LL | | bar2(Rc::new(())).await
+LL | | };
+LL | | }
+ | |_^
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:52:13
+ |
+LL | is_send(foo3(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo3` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:33:28
+ |
+LL | (Rc::new(()), bar().await);
+ | ----------- ^^^^^^ - `Rc::new(())` is later dropped here
+ | | |
+ | | await occurs here, with `Rc::new(())` maybe used later
+ | has type `Rc<()>` which is not `Send`
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:54:13
+ |
+LL | is_send(foo4(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo4` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:41:14
+ |
+LL | let r = Rc::new(());
+ | - has type `Rc<()>` which is not `Send`
+LL | bar().await;
+ | ^^^^^^ await occurs here, with `r` maybe used later
+...
+LL | };
+ | - `r` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:48:13
+ |
+LL | is_send(foo(Some(true)));
+ | ^^^^^^^^^^^^^^^ future returned by `foo` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:11:14
+ |
+LL | let r = Rc::new(());
+ | - has type `Rc<()>` which is not `Send`
+LL | bar().await
+ | ^^^^^^ await occurs here, with `r` maybe used later
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error[E0277]: `Rc<()>` cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:50:13
+ |
+LL | async fn foo2(x: Option<bool>) {
+ | - within this `impl Future<Output = ()>`
+...
+LL | is_send(foo2(Some(true)));
+ | ------- ^^^^^^^^^^^^^^^^ `Rc<()>` cannot be sent between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: required because it's used within this `async fn` body
+ --> $DIR/async-await-let-else.rs:27:29
+ |
+LL | async fn bar2<T>(_: T) -> ! {
+ | _____________________________^
+LL | | panic!()
+LL | | }
+ | |_^
+ = note: required because it captures the following types: `impl Future<Output = !>`
+note: required because it's used within this `async fn` body
+ --> $DIR/async-await-let-else.rs:21:32
+ |
+LL | async fn foo2(x: Option<bool>) {
+ | ________________________________^
+LL | | let Some(_) = x else {
+LL | | bar2(Rc::new(())).await
+LL | | };
+LL | | }
+ | |_^
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:52:13
+ |
+LL | is_send(foo3(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo3` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:33:28
+ |
+LL | (Rc::new(()), bar().await);
+ | ----------- ^^^^^^ await occurs here, with `Rc::new(())` maybe used later
+ | |
+ | has type `Rc<()>` which is not `Send`
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:54:13
+ |
+LL | is_send(foo4(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo4` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:41:14
+ |
+LL | let r = Rc::new(());
+ | - has type `Rc<()>` which is not `Send`
+LL | bar().await;
+ | ^^^^^^ await occurs here, with `r` maybe used later
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:48:13
+ |
+LL | is_send(foo(Some(true)));
+ | ^^^^^^^^^^^^^^^ future returned by `foo` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:11:14
+ |
+LL | let r = Rc::new(());
+ | - has type `Rc<()>` which is not `Send`
+LL | bar().await
+ | ^^^^^^ await occurs here, with `r` maybe used later
+LL | };
+ | - `r` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:50:13
+ |
+LL | is_send(foo2(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo2` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:23:26
+ |
+LL | bar2(Rc::new(())).await
+ | ----------- ^^^^^^ await occurs here, with `Rc::new(())` maybe used later
+ | |
+ | has type `Rc<()>` which is not `Send`
+LL | };
+ | - `Rc::new(())` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:52:13
+ |
+LL | is_send(foo3(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo3` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:33:28
+ |
+LL | (Rc::new(()), bar().await);
+ | ----------- ^^^^^^ - `Rc::new(())` is later dropped here
+ | | |
+ | | await occurs here, with `Rc::new(())` maybe used later
+ | has type `Rc<()>` which is not `Send`
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-await-let-else.rs:54:13
+ |
+LL | is_send(foo4(Some(true)));
+ | ^^^^^^^^^^^^^^^^ future returned by `foo4` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-await-let-else.rs:41:14
+ |
+LL | let r = Rc::new(());
+ | - has type `Rc<()>` which is not `Send`
+LL | bar().await;
+ | ^^^^^^ await occurs here, with `r` maybe used later
+...
+LL | };
+ | - `r` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/async-await-let-else.rs:19:15
+ |
+LL | fn is_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `is_send`
+
+error: aborting due to 4 previous errors
+
// edition:2021
-// revisions: drop-tracking no-drop-tracking
-// [drop-tracking] compile-flags: -Zdrop-tracking=yes
-// [no-drop-tracking] compile-flags: -Zdrop-tracking=no
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
use std::rc::Rc;
--- /dev/null
+error[E0277]: `()` is not a future
+ --> $DIR/async-error-span.rs:10:20
+ |
+LL | fn get_future() -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
+ |
+ = help: the trait `Future` is not implemented for `()`
+ = note: () must be a future or must implement `IntoFuture` to be awaited
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/async-error-span.rs:16:9
+ |
+LL | let a;
+ | ^ cannot infer type
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/async-error-span.rs:19:17
+ |
+LL | get_future().await;
+ | ^^^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0698.
+For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+error[E0277]: `()` is not a future
+ --> $DIR/async-error-span.rs:10:20
+ |
+LL | fn get_future() -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
+ |
+ = help: the trait `Future` is not implemented for `()`
+ = note: () must be a future or must implement `IntoFuture` to be awaited
+
+error[E0282]: type annotations needed
+ --> $DIR/async-error-span.rs:16:9
+ |
+LL | let a;
+ | ^
+ |
+help: consider giving `a` an explicit type
+ |
+LL | let a: /* Type */;
+ | ++++++++++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0282.
+For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+error[E0277]: `()` is not a future
+ --> $DIR/async-error-span.rs:10:20
+ |
+LL | fn get_future() -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
+ |
+ = help: the trait `Future` is not implemented for `()`
+ = note: () must be a future or must implement `IntoFuture` to be awaited
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/async-error-span.rs:16:9
+ |
+LL | let a;
+ | ^ cannot infer type
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/async-error-span.rs:19:17
+ |
+LL | get_future().await;
+ | ^^^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0698.
+For more information about an error, try `rustc --explain E0277`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
// Regression test for issue #62382.
}
async fn foo() {
- let a; //~ ERROR type inside `async fn` body must be known in this context
+ let a;
+ //[no_drop_tracking,drop_tracking]~^ ERROR type inside `async fn` body must be known in this context
+ //[drop_tracking_mir]~^^ ERROR type annotations needed
get_future().await;
}
+++ /dev/null
-error[E0277]: `()` is not a future
- --> $DIR/async-error-span.rs:7:20
- |
-LL | fn get_future() -> impl Future<Output = ()> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
- |
- = help: the trait `Future` is not implemented for `()`
- = note: () must be a future or must implement `IntoFuture` to be awaited
-
-error[E0698]: type inside `async fn` body must be known in this context
- --> $DIR/async-error-span.rs:13:9
- |
-LL | let a;
- | ^ cannot infer type
- |
-note: the type is part of the `async fn` body because of this `await`
- --> $DIR/async-error-span.rs:14:17
- |
-LL | get_future().await;
- | ^^^^^^
-
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0277, E0698.
-For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:72:17
+ |
+LL | assert_send(non_send_temporary_in_match());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:36:25
+ |
+LL | match Some(non_send()) {
+ | ---------------- has type `Option<impl Debug>` which is not `Send`
+LL | Some(_) => fut().await,
+ | ^^^^^^ await occurs here, with `Some(non_send())` maybe used later
+...
+LL | }
+ | - `Some(non_send())` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:74:17
+ |
+LL | assert_send(non_sync_with_method_call());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:49:14
+ |
+LL | let f: &mut std::fmt::Formatter = &mut get_formatter();
+ | --------------- has type `Formatter<'_>` which is not `Send`
+...
+LL | fut().await;
+ | ^^^^^^ await occurs here, with `get_formatter()` maybe used later
+LL | }
+LL | }
+ | - `get_formatter()` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:72:17
+ |
+LL | assert_send(non_send_temporary_in_match());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:36:25
+ |
+LL | match Some(non_send()) {
+ | ---------------- has type `Option<impl Debug>` which is not `Send`
+LL | Some(_) => fut().await,
+ | ^^^^^^ await occurs here, with `Some(non_send())` maybe used later
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:74:17
+ |
+LL | assert_send(non_sync_with_method_call());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:49:14
+ |
+LL | let f: &mut std::fmt::Formatter = &mut get_formatter();
+ | --------------- has type `Formatter<'_>` which is not `Send`
+...
+LL | fut().await;
+ | ^^^^^^ await occurs here, with `get_formatter()` maybe used later
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:70:17
+ |
+LL | assert_send(local_dropped_before_await());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `local_dropped_before_await` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:27:10
+ |
+LL | let x = non_send();
+ | - has type `impl Debug` which is not `Send`
+LL | drop(x);
+LL | fut().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:72:17
+ |
+LL | assert_send(non_send_temporary_in_match());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:36:25
+ |
+LL | match Some(non_send()) {
+ | ---------- has type `impl Debug` which is not `Send`
+LL | Some(_) => fut().await,
+ | ^^^^^^ await occurs here, with `non_send()` maybe used later
+...
+LL | }
+ | - `non_send()` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:74:17
+ |
+LL | assert_send(non_sync_with_method_call());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:49:14
+ |
+LL | let f: &mut std::fmt::Formatter = &mut get_formatter();
+ | --------------- has type `Formatter<'_>` which is not `Send`
+...
+LL | fut().await;
+ | ^^^^^^ await occurs here, with `get_formatter()` maybe used later
+LL | }
+LL | }
+ | - `get_formatter()` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:76:17
+ |
+LL | assert_send(non_sync_with_method_call_panic());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call_panic` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:56:14
+ |
+LL | let f: &mut std::fmt::Formatter = panic!();
+ | - has type `&mut Formatter<'_>` which is not `Send`
+LL | if non_sync().fmt(f).unwrap() == () {
+LL | fut().await;
+ | ^^^^^^ await occurs here, with `f` maybe used later
+LL | }
+LL | }
+ | - `f` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/async-fn-nonsend.rs:78:17
+ |
+LL | assert_send(non_sync_with_method_call_infinite_loop());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call_infinite_loop` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/async-fn-nonsend.rs:63:14
+ |
+LL | let f: &mut std::fmt::Formatter = loop {};
+ | - has type `&mut Formatter<'_>` which is not `Send`
+LL | if non_sync().fmt(f).unwrap() == () {
+LL | fut().await;
+ | ^^^^^^ await occurs here, with `f` maybe used later
+LL | }
+LL | }
+ | - `f` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/async-fn-nonsend.rs:67:24
+ |
+LL | fn assert_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 5 previous errors
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
-// compile-flags: --crate-type lib -Zdrop-tracking
+// compile-flags: --crate-type lib
use std::{cell::RefCell, fmt::Debug, rc::Rc};
pub fn pass_assert() {
assert_send(local_dropped_before_await());
+ //[no_drop_tracking]~^ ERROR future cannot be sent between threads safely
assert_send(non_send_temporary_in_match());
//~^ ERROR future cannot be sent between threads safely
assert_send(non_sync_with_method_call());
//~^ ERROR future cannot be sent between threads safely
assert_send(non_sync_with_method_call_panic());
+ //[no_drop_tracking]~^ ERROR future cannot be sent between threads safely
assert_send(non_sync_with_method_call_infinite_loop());
+ //[no_drop_tracking]~^ ERROR future cannot be sent between threads safely
}
error: future cannot be sent between threads safely
- --> $DIR/async-fn-nonsend.rs:68:17
+ --> $DIR/async-fn-nonsend.rs:72:17
|
LL | assert_send(non_send_temporary_in_match());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
note: future is not `Send` as this value is used across an await
- --> $DIR/async-fn-nonsend.rs:33:25
+ --> $DIR/async-fn-nonsend.rs:36:25
|
LL | match Some(non_send()) {
| ---------------- has type `Option<impl Debug>` which is not `Send`
LL | }
| - `Some(non_send())` is later dropped here
note: required by a bound in `assert_send`
- --> $DIR/async-fn-nonsend.rs:64:24
+ --> $DIR/async-fn-nonsend.rs:67:24
|
LL | fn assert_send(_: impl Send) {}
| ^^^^ required by this bound in `assert_send`
error: future cannot be sent between threads safely
- --> $DIR/async-fn-nonsend.rs:70:17
+ --> $DIR/async-fn-nonsend.rs:74:17
|
LL | assert_send(non_sync_with_method_call());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `dyn std::fmt::Write`
note: future is not `Send` as this value is used across an await
- --> $DIR/async-fn-nonsend.rs:46:14
+ --> $DIR/async-fn-nonsend.rs:49:14
|
LL | let f: &mut std::fmt::Formatter = &mut get_formatter();
| --------------- has type `Formatter<'_>` which is not `Send`
LL | }
| - `get_formatter()` is later dropped here
note: required by a bound in `assert_send`
- --> $DIR/async-fn-nonsend.rs:64:24
+ --> $DIR/async-fn-nonsend.rs:67:24
|
LL | fn assert_send(_: impl Send) {}
| ^^^^ required by this bound in `assert_send`
--- /dev/null
+// edition:2021
+
+pub trait T {}
+impl T for () {}
+
+pub struct S {}
+
+impl S {
+ pub async fn f<'a>(&self) -> impl T + 'a {
+ ()
+ }
+}
--- /dev/null
+// edition:2021
+// compile-flags: -Z drop-tracking
+// build-pass
+
+use std::collections::HashMap;
+
+fn main() {
+ let _ = real_main();
+}
+
+async fn nop() {}
+
+async fn real_main() {
+ nop().await;
+ nop().await;
+ nop().await;
+ nop().await;
+
+ let mut map: HashMap<(), ()> = HashMap::new();
+ map.insert((), nop().await);
+}
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// build-pass
// edition:2018
-// compile-flags: -Zdrop-tracking=y
fn main() {
let _ = foo();
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2021
-// compile-flags: -Zdrop-tracking
// build-pass
struct A;
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/drop-track-field-assign-nonsend.rs:45:17
+ |
+LL | assert_send(agent.handle());
+ | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/drop-track-field-assign-nonsend.rs:23:38
+ |
+LL | let mut info = self.info_result.clone();
+ | -------- has type `InfoResult` which is not `Send`
+...
+LL | let _ = send_element(element).await;
+ | ^^^^^^ await occurs here, with `mut info` maybe used later
+LL | }
+ | - `mut info` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/drop-track-field-assign-nonsend.rs:40:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/drop-track-field-assign-nonsend.rs:45:17
+ |
+LL | assert_send(agent.handle());
+ | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/drop-track-field-assign-nonsend.rs:23:38
+ |
+LL | let mut info = self.info_result.clone();
+ | -------- has type `InfoResult` which is not `Send`
+...
+LL | let _ = send_element(element).await;
+ | ^^^^^^ await occurs here, with `mut info` maybe used later
+note: required by a bound in `assert_send`
+ --> $DIR/drop-track-field-assign-nonsend.rs:40:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/drop-track-field-assign-nonsend.rs:45:17
+ |
+LL | assert_send(agent.handle());
+ | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/drop-track-field-assign-nonsend.rs:23:38
+ |
+LL | let mut info = self.info_result.clone();
+ | -------- has type `InfoResult` which is not `Send`
+...
+LL | let _ = send_element(element).await;
+ | ^^^^^^ await occurs here, with `mut info` maybe used later
+LL | }
+ | - `mut info` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/drop-track-field-assign-nonsend.rs:40:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// Derived from an ICE found in tokio-xmpp during a crater run.
// edition:2021
-// compile-flags: -Zdrop-tracking
#![allow(dead_code)]
+++ /dev/null
-error: future cannot be sent between threads safely
- --> $DIR/drop-track-field-assign-nonsend.rs:43:17
- |
-LL | assert_send(agent.handle());
- | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
- |
- = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
-note: future is not `Send` as this value is used across an await
- --> $DIR/drop-track-field-assign-nonsend.rs:21:38
- |
-LL | let mut info = self.info_result.clone();
- | -------- has type `InfoResult` which is not `Send`
-...
-LL | let _ = send_element(element).await;
- | ^^^^^^ await occurs here, with `mut info` maybe used later
-LL | }
- | - `mut info` is later dropped here
-note: required by a bound in `assert_send`
- --> $DIR/drop-track-field-assign-nonsend.rs:38:19
- |
-LL | fn assert_send<T: Send>(_: T) {}
- | ^^^^ required by this bound in `assert_send`
-
-error: aborting due to previous error
-
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// Derived from an ICE found in tokio-xmpp during a crater run.
// edition:2021
-// compile-flags: -Zdrop-tracking
// build-pass
#![allow(dead_code)]
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/field-assign-nonsend.rs:45:17
+ |
+LL | assert_send(agent.handle());
+ | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/field-assign-nonsend.rs:23:38
+ |
+LL | let mut info = self.info_result.clone();
+ | -------- has type `InfoResult` which is not `Send`
+...
+LL | let _ = send_element(element).await;
+ | ^^^^^^ await occurs here, with `mut info` maybe used later
+LL | }
+ | - `mut info` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/field-assign-nonsend.rs:40:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/field-assign-nonsend.rs:45:17
+ |
+LL | assert_send(agent.handle());
+ | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/field-assign-nonsend.rs:23:38
+ |
+LL | let mut info = self.info_result.clone();
+ | -------- has type `InfoResult` which is not `Send`
+...
+LL | let _ = send_element(element).await;
+ | ^^^^^^ await occurs here, with `mut info` maybe used later
+note: required by a bound in `assert_send`
+ --> $DIR/field-assign-nonsend.rs:40:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/field-assign-nonsend.rs:45:17
+ |
+LL | assert_send(agent.handle());
+ | ^^^^^^^^^^^^^^ future returned by `handle` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<String>`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/field-assign-nonsend.rs:23:38
+ |
+LL | let mut info = self.info_result.clone();
+ | -------- has type `InfoResult` which is not `Send`
+...
+LL | let _ = send_element(element).await;
+ | ^^^^^^ await occurs here, with `mut info` maybe used later
+LL | }
+ | - `mut info` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/field-assign-nonsend.rs:40:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
--- /dev/null
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// Derived from an ICE found in tokio-xmpp during a crater run.
+// edition:2021
+
+#![allow(dead_code)]
+
+#[derive(Clone)]
+struct InfoResult {
+ node: Option<std::rc::Rc<String>>
+}
+
+struct Agent {
+ info_result: InfoResult
+}
+
+impl Agent {
+ async fn handle(&mut self) {
+ let mut info = self.info_result.clone();
+ info.node = None;
+ let element = parse_info(info);
+ let _ = send_element(element).await;
+ }
+}
+
+struct Element {
+}
+
+async fn send_element(_: Element) {}
+
+fn parse(_: &[u8]) -> Result<(), ()> {
+ Ok(())
+}
+
+fn parse_info(_: InfoResult) -> Element {
+ Element { }
+}
+
+fn assert_send<T: Send>(_: T) {}
+
+fn main() {
+ let agent = Agent { info_result: InfoResult { node: None } };
+ // FIXME: It would be nice for this to work. See #94067.
+ assert_send(agent.handle());
+ //~^ cannot be sent between threads safely
+}
--- /dev/null
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// Derived from an ICE found in tokio-xmpp during a crater run.
+// edition:2021
+// build-pass
+
+#![allow(dead_code)]
+
+#[derive(Clone)]
+struct InfoResult {
+ node: Option<String>
+}
+
+struct Agent {
+ info_result: InfoResult
+}
+
+impl Agent {
+ async fn handle(&mut self) {
+ let mut info = self.info_result.clone();
+ info.node = Some("bar".into());
+ let element = parse_info(info);
+ let _ = send_element(element).await;
+ }
+}
+
+struct Element {
+}
+
+async fn send_element(_: Element) {}
+
+fn parse(_: &[u8]) -> Result<(), ()> {
+ Ok(())
+}
+
+fn parse_info(_: InfoResult) -> Element {
+ Element { }
+}
+
+fn main() {
+ let mut agent = Agent {
+ info_result: InfoResult { node: None }
+ };
+ let _ = agent.handle();
+}
--- /dev/null
+// edition:2021
+
+#![feature(async_fn_in_trait)]
+//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+
+trait Foo {
+ async fn bar();
+}
+
+async fn test<T: Foo>() {
+ T::bar().await;
+}
+
+fn test2<T: Foo>() {
+ assert_is_send(test::<T>());
+ //~^ ERROR future cannot be sent between threads safely
+}
+
+fn assert_is_send(_: impl Send) {}
+
+fn main() {}
--- /dev/null
+warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/missing-send-bound.rs:3:12
+ |
+LL | #![feature(async_fn_in_trait)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: future cannot be sent between threads safely
+ --> $DIR/missing-send-bound.rs:15:20
+ |
+LL | assert_is_send(test::<T>());
+ | ^^^^^^^^^^^ future returned by `test` is not `Send`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `impl Future<Output = ()>`
+note: future is not `Send` as it awaits another future which is not `Send`
+ --> $DIR/missing-send-bound.rs:11:5
+ |
+LL | T::bar().await;
+ | ^^^^^^^^ await occurs here on type `impl Future<Output = ()>`, which is not `Send`
+note: required by a bound in `assert_is_send`
+ --> $DIR/missing-send-bound.rs:19:27
+ |
+LL | fn assert_is_send(_: impl Send) {}
+ | ^^^^ required by this bound in `assert_is_send`
+
+error: aborting due to previous error; 1 warning emitted
+
--- /dev/null
+// aux-build:issue-107036.rs
+// edition:2021
+// check-pass
+
+extern crate issue_107036;
+use issue_107036::S;
+
+async fn f() {
+ S{}.f().await;
+}
+
+fn main() {
+ let _ = f();
+}
--- /dev/null
+error: future cannot be shared between threads safely
+ --> $DIR/issue-64130-1-sync.rs:25:13
+ |
+LL | is_sync(bar());
+ | ^^^^^ future returned by `bar` is not `Sync`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Sync` is not implemented for `Foo`
+note: future is not `Sync` as this value is used across an await
+ --> $DIR/issue-64130-1-sync.rs:18:10
+ |
+LL | let x = Foo;
+ | - has type `Foo` which is not `Sync`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | drop(x);
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `is_sync`
+ --> $DIR/issue-64130-1-sync.rs:14:15
+ |
+LL | fn is_sync<T: Sync>(t: T) { }
+ | ^^^^ required by this bound in `is_sync`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be shared between threads safely
+ --> $DIR/issue-64130-1-sync.rs:25:13
+ |
+LL | is_sync(bar());
+ | ^^^^^ future returned by `bar` is not `Sync`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Sync` is not implemented for `Foo`
+note: future is not `Sync` as this value is used across an await
+ --> $DIR/issue-64130-1-sync.rs:18:10
+ |
+LL | let x = Foo;
+ | - has type `Foo` which is not `Sync`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+note: required by a bound in `is_sync`
+ --> $DIR/issue-64130-1-sync.rs:14:15
+ |
+LL | fn is_sync<T: Sync>(t: T) { }
+ | ^^^^ required by this bound in `is_sync`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be shared between threads safely
+ --> $DIR/issue-64130-1-sync.rs:25:13
+ |
+LL | is_sync(bar());
+ | ^^^^^ future returned by `bar` is not `Sync`
+ |
+ = help: within `impl Future<Output = ()>`, the trait `Sync` is not implemented for `Foo`
+note: future is not `Sync` as this value is used across an await
+ --> $DIR/issue-64130-1-sync.rs:18:10
+ |
+LL | let x = Foo;
+ | - has type `Foo` which is not `Sync`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | drop(x);
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `is_sync`
+ --> $DIR/issue-64130-1-sync.rs:14:15
+ |
+LL | fn is_sync<T: Sync>(t: T) { }
+ | ^^^^ required by this bound in `is_sync`
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(negative_impls)]
// edition:2018
async fn bar() {
let x = Foo;
baz().await;
+ drop(x);
}
async fn baz() { }
error: future cannot be shared between threads safely
- --> $DIR/issue-64130-1-sync.rs:21:13
+ --> $DIR/issue-64130-1-sync.rs:24:13
|
LL | is_sync(bar());
| ^^^^^ future returned by `bar` is not `Sync`
|
= help: within `impl Future<Output = ()>`, the trait `Sync` is not implemented for `Foo`
note: future is not `Sync` as this value is used across an await
- --> $DIR/issue-64130-1-sync.rs:15:10
+ --> $DIR/issue-64130-1-sync.rs:18:10
|
LL | let x = Foo;
| - has type `Foo` which is not `Sync`
LL | }
| - `x` is later dropped here
note: required by a bound in `is_sync`
- --> $DIR/issue-64130-1-sync.rs:11:15
+ --> $DIR/issue-64130-1-sync.rs:14:15
|
LL | fn is_sync<T: Sync>(t: T) { }
| ^^^^ required by this bound in `is_sync`
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-64130-2-send.rs:24:13
+ |
+LL | is_send(bar());
+ | ^^^^^ future returned by `bar` is not `Send`
+ |
+ = note: the trait bound `Unique<Foo>: Send` is not satisfied
+note: future is not `Send` as this value is used across an await
+ --> $DIR/issue-64130-2-send.rs:18:10
+ |
+LL | let x = Box::new(Foo);
+ | - has type `Box<Foo>` which is not `Send`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/issue-64130-2-send.rs:14:15
+ |
+LL | fn is_send<T: Send>(t: T) { }
+ | ^^^^ required by this bound in `is_send`
+help: consider borrowing here
+ |
+LL | is_send(&bar());
+ | +
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-64130-2-send.rs:24:13
+ |
+LL | is_send(bar());
+ | ^^^^^ future returned by `bar` is not `Send`
+ |
+ = note: the trait bound `Unique<Foo>: Send` is not satisfied
+note: future is not `Send` as this value is used across an await
+ --> $DIR/issue-64130-2-send.rs:18:10
+ |
+LL | let x = Box::new(Foo);
+ | - has type `Box<Foo>` which is not `Send`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+note: required by a bound in `is_send`
+ --> $DIR/issue-64130-2-send.rs:14:15
+ |
+LL | fn is_send<T: Send>(t: T) { }
+ | ^^^^ required by this bound in `is_send`
+help: consider borrowing here
+ |
+LL | is_send(&bar());
+ | +
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-64130-2-send.rs:24:13
+ |
+LL | is_send(bar());
+ | ^^^^^ future returned by `bar` is not `Send`
+ |
+ = note: the trait bound `Unique<Foo>: Send` is not satisfied
+note: future is not `Send` as this value is used across an await
+ --> $DIR/issue-64130-2-send.rs:18:10
+ |
+LL | let x = Box::new(Foo);
+ | - has type `Box<Foo>` which is not `Send`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `is_send`
+ --> $DIR/issue-64130-2-send.rs:14:15
+ |
+LL | fn is_send<T: Send>(t: T) { }
+ | ^^^^ required by this bound in `is_send`
+help: consider borrowing here
+ |
+LL | is_send(&bar());
+ | +
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(negative_impls)]
// edition:2018
fn is_send<T: Send>(t: T) { }
async fn bar() {
- let x = Foo;
+ let x = Box::new(Foo);
baz().await;
}
error: future cannot be sent between threads safely
- --> $DIR/issue-64130-2-send.rs:21:13
+ --> $DIR/issue-64130-2-send.rs:24:13
|
LL | is_send(bar());
| ^^^^^ future returned by `bar` is not `Send`
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Foo`
note: future is not `Send` as this value is used across an await
- --> $DIR/issue-64130-2-send.rs:15:10
+ --> $DIR/issue-64130-2-send.rs:18:10
|
LL | let x = Foo;
| - has type `Foo` which is not `Send`
LL | }
| - `x` is later dropped here
note: required by a bound in `is_send`
- --> $DIR/issue-64130-2-send.rs:11:15
+ --> $DIR/issue-64130-2-send.rs:14:15
|
LL | fn is_send<T: Send>(t: T) { }
| ^^^^ required by this bound in `is_send`
--- /dev/null
+error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl Future<Output = ()>`
+ --> $DIR/issue-64130-3-other.rs:27:12
+ |
+LL | async fn bar() {
+ | - within this `impl Future<Output = ()>`
+...
+LL | is_qux(bar());
+ | ^^^^^ within `impl Future<Output = ()>`, the trait `Qux` is not implemented for `Foo`
+ |
+note: future does not implement `Qux` as this value is used across an await
+ --> $DIR/issue-64130-3-other.rs:21:10
+ |
+LL | let x = Box::new(Foo);
+ | - has type `Box<Foo>` which does not implement `Qux`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `is_qux`
+ --> $DIR/issue-64130-3-other.rs:17:14
+ |
+LL | fn is_qux<T: Qux>(t: T) {}
+ | ^^^ required by this bound in `is_qux`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl Future<Output = ()>`
+ --> $DIR/issue-64130-3-other.rs:27:12
+ |
+LL | async fn bar() {
+ | - within this `impl Future<Output = ()>`
+...
+LL | is_qux(bar());
+ | ^^^^^ within `impl Future<Output = ()>`, the trait `Qux` is not implemented for `Foo`
+ |
+note: future does not implement `Qux` as this value is used across an await
+ --> $DIR/issue-64130-3-other.rs:21:10
+ |
+LL | let x = Box::new(Foo);
+ | - has type `Box<Foo>` which does not implement `Qux`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+note: required by a bound in `is_qux`
+ --> $DIR/issue-64130-3-other.rs:17:14
+ |
+LL | fn is_qux<T: Qux>(t: T) {}
+ | ^^^ required by this bound in `is_qux`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl Future<Output = ()>`
+ --> $DIR/issue-64130-3-other.rs:27:12
+ |
+LL | async fn bar() {
+ | - within this `impl Future<Output = ()>`
+...
+LL | is_qux(bar());
+ | ^^^^^ within `impl Future<Output = ()>`, the trait `Qux` is not implemented for `Foo`
+ |
+note: future does not implement `Qux` as this value is used across an await
+ --> $DIR/issue-64130-3-other.rs:21:10
+ |
+LL | let x = Box::new(Foo);
+ | - has type `Box<Foo>` which does not implement `Qux`
+LL | baz().await;
+ | ^^^^^^ await occurs here, with `x` maybe used later
+LL | }
+ | - `x` is later dropped here
+note: required by a bound in `is_qux`
+ --> $DIR/issue-64130-3-other.rs:17:14
+ |
+LL | fn is_qux<T: Qux>(t: T) {}
+ | ^^^ required by this bound in `is_qux`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(auto_traits)]
#![feature(negative_impls)]
// edition:2018
fn is_qux<T: Qux>(t: T) {}
async fn bar() {
- let x = Foo;
+ let x = Box::new(Foo);
baz().await;
}
error[E0277]: the trait bound `Foo: Qux` is not satisfied in `impl Future<Output = ()>`
- --> $DIR/issue-64130-3-other.rs:24:12
+ --> $DIR/issue-64130-3-other.rs:27:12
|
LL | async fn bar() {
| - within this `impl Future<Output = ()>`
| ^^^^^ within `impl Future<Output = ()>`, the trait `Qux` is not implemented for `Foo`
|
note: future does not implement `Qux` as this value is used across an await
- --> $DIR/issue-64130-3-other.rs:18:10
+ --> $DIR/issue-64130-3-other.rs:21:10
|
LL | let x = Foo;
| - has type `Foo` which does not implement `Qux`
LL | }
| - `x` is later dropped here
note: required by a bound in `is_qux`
- --> $DIR/issue-64130-3-other.rs:14:14
+ --> $DIR/issue-64130-3-other.rs:17:14
|
LL | fn is_qux<T: Qux>(t: T) {}
| ^^^ required by this bound in `is_qux`
error: future cannot be sent between threads safely
- --> $DIR/issue-64130-4-async-move.rs:19:17
+ --> $DIR/issue-64130-4-async-move.rs:20:17
|
LL | pub fn foo() -> impl Future + Send {
| ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `(dyn Any + Send + 'static)`
note: future is not `Send` as this value is used across an await
- --> $DIR/issue-64130-4-async-move.rs:25:31
+ --> $DIR/issue-64130-4-async-move.rs:27:31
|
LL | match client.status() {
| ------ has type `&Client` which is not `Send`
LL | }
| - `client` is later dropped here
help: consider moving this into a `let` binding to create a shorter lived borrow
- --> $DIR/issue-64130-4-async-move.rs:23:15
+ --> $DIR/issue-64130-4-async-move.rs:25:15
|
LL | match client.status() {
| ^^^^^^^^^^^^^^^
error: future cannot be sent between threads safely
- --> $DIR/issue-64130-4-async-move.rs:19:17
+ --> $DIR/issue-64130-4-async-move.rs:21:17
|
LL | pub fn foo() -> impl Future + Send {
| ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `(dyn Any + Send + 'static)`
note: future is not `Send` as this value is used across an await
- --> $DIR/issue-64130-4-async-move.rs:25:31
+ --> $DIR/issue-64130-4-async-move.rs:27:31
|
LL | match client.status() {
| ------ has type `&Client` which is not `Send`
LL | }
| - `client` is later dropped here
help: consider moving this into a `let` binding to create a shorter lived borrow
- --> $DIR/issue-64130-4-async-move.rs:23:15
+ --> $DIR/issue-64130-4-async-move.rs:25:15
|
LL | match client.status() {
| ^^^^^^^^^^^^^^^
// edition:2018
-// revisions: no_drop_tracking drop_tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// [drop_tracking_mir] check-pass
// [drop_tracking] check-pass
-// [drop_tracking] compile-flags: -Zdrop-tracking=yes
-// [no_drop_tracking] compile-flags: -Zdrop-tracking=no
+
use std::any::Any;
use std::future::Future;
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-67252-unnamed-future.rs:21:11
+ |
+LL | spawn(async {
+ | ___________^
+LL | | let a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
+LL | | AFuture.await;
+LL | | drop(a);
+LL | | });
+ | |_____^ future created by async block is not `Send`
+ |
+ = help: within `[async block@$DIR/issue-67252-unnamed-future.rs:21:11: 25:6]`, the trait `Send` is not implemented for `*mut ()`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/issue-67252-unnamed-future.rs:23:16
+ |
+LL | let a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
+ | - has type `*mut ()` which is not `Send`
+LL | AFuture.await;
+ | ^^^^^^ await occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `spawn`
+ --> $DIR/issue-67252-unnamed-future.rs:9:13
+ |
+LL | fn spawn<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `spawn`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-67252-unnamed-future.rs:21:5
+ |
+LL | spawn(async {
+ | ^^^^^ future created by async block is not `Send`
+ |
+ = help: within `[async block@$DIR/issue-67252-unnamed-future.rs:21:11: 25:6]`, the trait `Send` is not implemented for `*mut ()`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/issue-67252-unnamed-future.rs:23:16
+ |
+LL | let a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
+ | - has type `*mut ()` which is not `Send`
+LL | AFuture.await;
+ | ^^^^^^ await occurs here, with `a` maybe used later
+note: required by a bound in `spawn`
+ --> $DIR/issue-67252-unnamed-future.rs:9:13
+ |
+LL | fn spawn<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `spawn`
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-67252-unnamed-future.rs:21:11
+ |
+LL | spawn(async {
+ | ___________^
+LL | | let a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
+LL | | AFuture.await;
+LL | | drop(a);
+LL | | });
+ | |_____^ future created by async block is not `Send`
+ |
+ = help: within `[async block@$DIR/issue-67252-unnamed-future.rs:21:11: 25:6]`, the trait `Send` is not implemented for `*mut ()`
+note: future is not `Send` as this value is used across an await
+ --> $DIR/issue-67252-unnamed-future.rs:23:16
+ |
+LL | let a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
+ | - has type `*mut ()` which is not `Send`
+LL | AFuture.await;
+ | ^^^^^^ await occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `spawn`
+ --> $DIR/issue-67252-unnamed-future.rs:9:13
+ |
+LL | fn spawn<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `spawn`
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
use std::future::Future;
use std::pin::Pin;
async fn foo() {
spawn(async { //~ ERROR future cannot be sent between threads safely
- let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
+ let a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
AFuture.await;
+ drop(a);
});
}
+++ /dev/null
-error: future cannot be sent between threads safely
- --> $DIR/issue-67252-unnamed-future.rs:18:11
- |
-LL | spawn(async {
- | ___________^
-LL | | let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
-LL | | AFuture.await;
-LL | | });
- | |_____^ future created by async block is not `Send`
- |
- = help: within `[async block@$DIR/issue-67252-unnamed-future.rs:18:11: 21:6]`, the trait `Send` is not implemented for `*mut ()`
-note: future is not `Send` as this value is used across an await
- --> $DIR/issue-67252-unnamed-future.rs:20:16
- |
-LL | let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
- | -- has type `*mut ()` which is not `Send`
-LL | AFuture.await;
- | ^^^^^^ await occurs here, with `_a` maybe used later
-LL | });
- | - `_a` is later dropped here
-note: required by a bound in `spawn`
- --> $DIR/issue-67252-unnamed-future.rs:6:13
- |
-LL | fn spawn<T: Send>(_: T) {}
- | ^^^^ required by this bound in `spawn`
-
-error: aborting due to previous error
-
| ^^^^^^^^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: future is not `Send` as it awaits another future which is not `Send`
--> $DIR/issue-68112.rs:34:17
|
| ^^^^^^^^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: future is not `Send` as it awaits another future which is not `Send`
--> $DIR/issue-68112.rs:43:17
|
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
= note: required for `Arc<RefCell<i32>>` to implement `Send`
note: required because it's used within this `async fn` body
--> $DIR/issue-68112.rs:50:31
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-68112.rs:37:5
+ |
+LL | require_send(send_fut);
+ | ^^^^^^^^^^^^ future created by async block is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: future is not `Send` as it awaits another future which is not `Send`
+ --> $DIR/issue-68112.rs:34:17
+ |
+LL | let _ = non_send_fut.await;
+ | ^^^^^^^^^^^^ await occurs here on type `impl Future<Output = Arc<RefCell<i32>>>`, which is not `Send`
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:14:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: future cannot be sent between threads safely
+ --> $DIR/issue-68112.rs:46:5
+ |
+LL | require_send(send_fut);
+ | ^^^^^^^^^^^^ future created by async block is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: future is not `Send` as it awaits another future which is not `Send`
+ --> $DIR/issue-68112.rs:43:17
+ |
+LL | let _ = make_non_send_future1().await;
+ | ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl Future<Output = Arc<RefCell<i32>>>`, which is not `Send`
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:14:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/issue-68112.rs:65:5
+ |
+LL | require_send(send_fut);
+ | ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this `async fn` body
+ --> $DIR/issue-68112.rs:50:31
+ |
+LL | async fn ready2<T>(t: T) -> T {
+ | _______________________________^
+LL | | t
+LL | | }
+ | |_^
+note: required because it appears within the type `impl Future<Output = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:53:31
+ |
+LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `impl Future<Output = Arc<RefCell<i32>>>`, `Ready<i32>`
+note: required because it's used within this `async` block
+ --> $DIR/issue-68112.rs:60:20
+ |
+LL | let send_fut = async {
+ | ____________________^
+LL | | let non_send_fut = make_non_send_future2();
+LL | | let _ = non_send_fut.await;
+LL | | ready(0).await;
+LL | | };
+ | |_____^
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:14:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
| ^^^^^^^^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: future is not `Send` as it awaits another future which is not `Send`
--> $DIR/issue-68112.rs:34:17
|
| ^^^^^^^^ future created by async block is not `Send`
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: future is not `Send` as it awaits another future which is not `Send`
--> $DIR/issue-68112.rs:43:17
|
| required by a bound introduced by this call
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
= note: required for `Arc<RefCell<i32>>` to implement `Send`
note: required because it's used within this `async fn` body
--> $DIR/issue-68112.rs:50:31
// edition:2018
-// revisions: no_drop_tracking drop_tracking
-// [drop_tracking] compile-flags: -Zdrop-tracking=yes
-// [no_drop_tracking] compile-flags: -Zdrop-tracking=no
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
use std::{
cell::RefCell,
fn require_send(_: impl Send) {}
struct Ready<T>(Option<T>);
-impl<T> Future for Ready<T> {
+impl<T: Unpin> Future for Ready<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
Poll::Ready(self.0.take().unwrap())
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-70818.rs:7:38
+ |
+LL | fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
+ |
+note: captured value is not `Send`
+ --> $DIR/issue-70818.rs:9:18
+ |
+LL | async { (ty, ty1) }
+ | ^^^ has type `U` which is not `Send`
+help: consider restricting type parameter `U`
+ |
+LL | fn foo<T: Send, U: std::marker::Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
+ | +++++++++++++++++++
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-70818.rs:7:38
+ |
+LL | fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
+ |
+note: captured value is not `Send`
+ --> $DIR/issue-70818.rs:9:18
+ |
+LL | async { (ty, ty1) }
+ | ^^^ has type `U` which is not `Send`
+help: consider restricting type parameter `U`
+ |
+LL | fn foo<T: Send, U: std::marker::Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
+ | +++++++++++++++++++
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-70818.rs:7:38
+ |
+LL | fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
+ |
+note: captured value is not `Send`
+ --> $DIR/issue-70818.rs:9:18
+ |
+LL | async { (ty, ty1) }
+ | ^^^ has type `U` which is not `Send`
+help: consider restricting type parameter `U`
+ |
+LL | fn foo<T: Send, U: std::marker::Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
+ | +++++++++++++++++++
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
use std::future::Future;
error: future cannot be sent between threads safely
- --> $DIR/issue-70818.rs:4:38
+ --> $DIR/issue-70818.rs:7:38
|
LL | fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
|
note: captured value is not `Send`
- --> $DIR/issue-70818.rs:6:18
+ --> $DIR/issue-70818.rs:9:18
|
LL | async { (ty, ty1) }
| ^^^ has type `U` which is not `Send`
--- /dev/null
+error[E0277]: `Sender<i32>` cannot be shared between threads safely
+ --> $DIR/issue-70935-complex-spans.rs:13:45
+ |
+LL | fn foo(tx: std::sync::mpsc::Sender<i32>) -> impl Future + Send {
+ | ^^^^^^^^^^^^^^^^^^ `Sender<i32>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Sender<i32>`
+ = note: required for `&Sender<i32>` to implement `Send`
+note: required because it's used within this closure
+ --> $DIR/issue-70935-complex-spans.rs:17:13
+ |
+LL | baz(|| async{
+ | ^^
+note: required because it's used within this `async fn` body
+ --> $DIR/issue-70935-complex-spans.rs:10:67
+ |
+LL | async fn baz<T>(_c: impl FnMut() -> T) where T: Future<Output=()> {
+ | ___________________________________________________________________^
+LL | | }
+ | |_^
+ = note: required because it captures the following types: `impl Future<Output = ()>`
+note: required because it's used within this `async` block
+ --> $DIR/issue-70935-complex-spans.rs:16:5
+ |
+LL | / async move {
+LL | | baz(|| async{
+LL | | foo(tx.clone());
+LL | | }).await;
+LL | | }
+ | |_____^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
// edition:2018
-// revisions: no_drop_tracking drop_tracking
-// [no_drop_tracking]compile-flags:-Zdrop-tracking=no
-// [drop_tracking]compile-flags:-Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// #70935: Check if we do not emit snippet
// with newlines which lead complex diagnostics.
fn foo(tx: std::sync::mpsc::Sender<i32>) -> impl Future + Send {
//[no_drop_tracking]~^ ERROR future cannot be sent between threads safely
- //[drop_tracking]~^^ ERROR `Sender<i32>` cannot be shared between threads
+ //[drop_tracking,drop_tracking_mir]~^^ ERROR `Sender<i32>` cannot be shared between threads
async move {
baz(|| async{
foo(tx.clone());
--- /dev/null
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/issue-73741-type-err-drop-tracking.rs:11:7
+ |
+LL | 1 = 2;
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0070`.
--- /dev/null
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/issue-73741-type-err-drop-tracking.rs:11:7
+ |
+LL | 1 = 2;
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0070`.
--- /dev/null
+error[E0070]: invalid left-hand side of assignment
+ --> $DIR/issue-73741-type-err-drop-tracking.rs:11:7
+ |
+LL | 1 = 2;
+ | - ^
+ | |
+ | cannot assign to this expression
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0070`.
// edition:2018
-// compile-flags: -Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+//
// Regression test for issue #73741
// Ensures that we don't emit spurious errors when
// a type error ocurrs in an `async fn`
error[E0070]: invalid left-hand side of assignment
- --> $DIR/issue-73741-type-err-drop-tracking.rs:8:7
+ --> $DIR/issue-73741-type-err-drop-tracking.rs:11:7
|
LL | 1 = 2;
| - ^
LL | pub async fn async_fn(x: &mut i32) -> &i32 {
| - let's call the lifetime of this reference `'1`
LL | let y = &*x;
- | --- borrow of `*x` occurs here
+ | --- `*x` is borrowed here
LL | *x += 1;
- | ^^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^^ `*x` is assigned to here but it was already borrowed
LL | y
| - returning this value requires that `*x` is borrowed for `'1`
--> $DIR/issue-74072-lifetime-name-annotations.rs:16:9
|
LL | let y = &*x;
- | --- borrow of `*x` occurs here
+ | --- `*x` is borrowed here
LL | *x += 1;
- | ^^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^^ `*x` is assigned to here but it was already borrowed
LL | y
| - returning this value requires that `*x` is borrowed for `'1`
LL | })()
LL | (async move || -> &i32 {
| - let's call the lifetime of this reference `'1`
LL | let y = &*x;
- | --- borrow of `*x` occurs here
+ | --- `*x` is borrowed here
LL | *x += 1;
- | ^^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^^ `*x` is assigned to here but it was already borrowed
LL | y
| - returning this value requires that `*x` is borrowed for `'1`
--> $DIR/issue-74072-lifetime-name-annotations.rs:32:9
|
LL | let y = &*x;
- | --- borrow of `*x` occurs here
+ | --- `*x` is borrowed here
LL | *x += 1;
- | ^^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^^ `*x` is assigned to here but it was already borrowed
LL | y
| - returning this value requires that `*x` is borrowed for `'1`
LL | }
LL | pub async fn async_fn(x: &mut i32) -> (&i32, &i32) {
| - let's call the lifetime of this reference `'1`
LL | let y = &*x;
- | --- borrow of `*x` occurs here
+ | --- `*x` is borrowed here
LL | *x += 1;
- | ^^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^^ `*x` is assigned to here but it was already borrowed
LL | (&32, y)
| -------- returning this value requires that `*x` is borrowed for `'1`
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-86507.rs:20:13
+ |
+LL | / Box::pin(
+LL | | async move {
+LL | | let x = x;
+LL | | }
+LL | | )
+ | |_____________^ future created by async block is not `Send`
+ |
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+ --> $DIR/issue-86507.rs:22:29
+ |
+LL | let x = x;
+ | ^ has type `&T` which is not `Send`, because `T` is not `Sync`
+ = note: required for the cast from `[async block@$DIR/issue-86507.rs:21:17: 23:18]` to the object type `dyn Future<Output = ()> + Send`
+help: consider further restricting this bound
+ |
+LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
+ | +++++++++++++++++++
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-86507.rs:20:13
+ |
+LL | / Box::pin(
+LL | | async move {
+LL | | let x = x;
+LL | | }
+LL | | )
+ | |_____________^ future created by async block is not `Send`
+ |
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+ --> $DIR/issue-86507.rs:22:29
+ |
+LL | let x = x;
+ | ^ has type `&T` which is not `Send`, because `T` is not `Sync`
+ = note: required for the cast from `[async block@$DIR/issue-86507.rs:21:17: 23:18]` to the object type `dyn Future<Output = ()> + Send`
+help: consider further restricting this bound
+ |
+LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
+ | +++++++++++++++++++
+
+error: aborting due to previous error
+
--- /dev/null
+error: future cannot be sent between threads safely
+ --> $DIR/issue-86507.rs:20:13
+ |
+LL | / Box::pin(
+LL | | async move {
+LL | | let x = x;
+LL | | }
+LL | | )
+ | |_____________^ future created by async block is not `Send`
+ |
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+ --> $DIR/issue-86507.rs:22:29
+ |
+LL | let x = x;
+ | ^ has type `&T` which is not `Send`, because `T` is not `Sync`
+ = note: required for the cast from `[async block@$DIR/issue-86507.rs:21:17: 23:18]` to the object type `dyn Future<Output = ()> + Send`
+help: consider further restricting this bound
+ |
+LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
+ | +++++++++++++++++++
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
use ::core::pin::Pin;
+++ /dev/null
-error: future cannot be sent between threads safely
- --> $DIR/issue-86507.rs:17:13
- |
-LL | / Box::pin(
-LL | | async move {
-LL | | let x = x;
-LL | | }
-LL | | )
- | |_____________^ future created by async block is not `Send`
- |
-note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
- --> $DIR/issue-86507.rs:19:29
- |
-LL | let x = x;
- | ^ has type `&T` which is not `Send`, because `T` is not `Sync`
- = note: required for the cast from `[async block@$DIR/issue-86507.rs:18:17: 20:18]` to the object type `dyn Future<Output = ()> + Send`
-help: consider further restricting this bound
- |
-LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
- | +++++++++++++++++++
-
-error: aborting due to previous error
-
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2021
// build-pass
-// compile-flags: -Zdrop-tracking
fn main() {
let _ = async {
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
use std::sync::{Arc, Mutex};
error: future cannot be sent between threads safely
- --> $DIR/issue-65436-raw-ptr-not-send.rs:16:17
+ --> $DIR/issue-65436-raw-ptr-not-send.rs:17:17
|
LL | assert_send(async {
| _________________^
LL | | })
| |_____^ future created by async block is not `Send`
|
- = help: within `[async block@$DIR/issue-65436-raw-ptr-not-send.rs:16:17: 19:6]`, the trait `Send` is not implemented for `*const u8`
+ = help: within `[async block@$DIR/issue-65436-raw-ptr-not-send.rs:17:17: 20:6]`, the trait `Send` is not implemented for `*const u8`
note: future is not `Send` as this value is used across an await
- --> $DIR/issue-65436-raw-ptr-not-send.rs:18:35
+ --> $DIR/issue-65436-raw-ptr-not-send.rs:19:35
|
LL | bar(Foo(std::ptr::null())).await;
| ---------------- ^^^^^^- `std::ptr::null()` is later dropped here
| | await occurs here, with `std::ptr::null()` maybe used later
| has type `*const u8` which is not `Send`
help: consider moving this into a `let` binding to create a shorter lived borrow
- --> $DIR/issue-65436-raw-ptr-not-send.rs:18:13
+ --> $DIR/issue-65436-raw-ptr-not-send.rs:19:13
|
LL | bar(Foo(std::ptr::null())).await;
| ^^^^^^^^^^^^^^^^^^^^^
note: required by a bound in `assert_send`
- --> $DIR/issue-65436-raw-ptr-not-send.rs:13:19
+ --> $DIR/issue-65436-raw-ptr-not-send.rs:14:19
|
LL | fn assert_send<T: Send>(_: T) {}
| ^^^^ required by this bound in `assert_send`
// edition:2018
-// revisions: no_drop_tracking drop_tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// [drop_tracking] check-pass
-// [drop_tracking] compile-flags: -Zdrop-tracking=yes
-// [no_drop_tracking] compile-flags: -Zdrop-tracking=no
+// [drop_tracking_mir] check-pass
struct Foo(*const u8);
// build-pass
// edition:2018
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
static mut A: [i32; 5] = [1, 2, 3, 4, 5];
fn is_send_sync<T: Send + Sync>(_: T) {}
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `MutexGuard<'_, ()>`
note: future is not `Send` as this value is used across an await
- --> $DIR/auxiliary/issue_67893.rs:9:26
+ --> $DIR/auxiliary/issue_67893.rs:12:26
|
LL | f(*x.lock().unwrap()).await;
| ----------------- ^^^^^^- `x.lock().unwrap()` is later dropped here
--> $DIR/ret-ref.rs:16:5
|
LL | let future = multiple_named_lifetimes(&a, &b);
- | -- borrow of `a` occurs here
+ | -- `a` is borrowed here
LL | a += 1;
- | ^^^^^^ assignment to borrowed `a` occurs here
+ | ^^^^^^ `a` is assigned to here but it was already borrowed
LL | b += 1;
LL | let p = future.await;
| ------ borrow later used here
--> $DIR/ret-ref.rs:17:5
|
LL | let future = multiple_named_lifetimes(&a, &b);
- | -- borrow of `b` occurs here
+ | -- `b` is borrowed here
LL | a += 1;
LL | b += 1;
- | ^^^^^^ assignment to borrowed `b` occurs here
+ | ^^^^^^ `b` is assigned to here but it was already borrowed
LL | let p = future.await;
| ------ borrow later used here
--> $DIR/ret-ref.rs:28:5
|
LL | let future = multiple_named_lifetimes(&a, &b);
- | -- borrow of `a` occurs here
+ | -- `a` is borrowed here
LL | let p = future.await;
LL | a += 1;
- | ^^^^^^ assignment to borrowed `a` occurs here
+ | ^^^^^^ `a` is assigned to here but it was already borrowed
LL | b += 1;
LL | drop(p);
| - borrow later used here
--- /dev/null
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:18
+ |
+LL | async fn rec_1() {
+ | ^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:13:18
+ |
+LL | async fn rec_2() {
+ | ^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0733`.
--- /dev/null
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:18
+ |
+LL | async fn rec_1() {
+ | ^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:13:18
+ |
+LL | async fn rec_2() {
+ | ^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0733`.
--- /dev/null
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:18
+ |
+LL | async fn rec_1() {
+ | ^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:13:18
+ |
+LL | async fn rec_2() {
+ | ^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0733`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
// edition:2018
// Test that impl trait does not allow creating recursive types that are
// otherwise forbidden when using `async` and `await`.
error[E0733]: recursion in an `async fn` requires boxing
- --> $DIR/mutually-recursive-async-impl-trait-type.rs:5:18
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:18
|
LL | async fn rec_1() {
| ^ recursive `async fn`
= note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
error[E0733]: recursion in an `async fn` requires boxing
- --> $DIR/mutually-recursive-async-impl-trait-type.rs:9:18
+ --> $DIR/mutually-recursive-async-impl-trait-type.rs:13:18
|
LL | async fn rec_2() {
| ^ recursive `async fn`
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// build-pass
// edition:2018
-// compile-flags: -Zdrop-tracking=y
#![feature(generators)]
--- /dev/null
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/recursive-async-impl-trait-type.rs:8:40
+ |
+LL | async fn recursive_async_function() -> () {
+ | ^^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0733`.
--- /dev/null
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/recursive-async-impl-trait-type.rs:8:40
+ |
+LL | async fn recursive_async_function() -> () {
+ | ^^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0733`.
--- /dev/null
+error[E0733]: recursion in an `async fn` requires boxing
+ --> $DIR/recursive-async-impl-trait-type.rs:8:40
+ |
+LL | async fn recursive_async_function() -> () {
+ | ^^ recursive `async fn`
+ |
+ = note: a recursive `async fn` must be rewritten to return a boxed `dyn Future`
+ = note: consider using the `async_recursion` crate: https://crates.io/crates/async_recursion
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0733`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
// Test that impl trait does not allow creating recursive types that are
// otherwise forbidden when using `async` and `await`.
error[E0733]: recursion in an `async fn` requires boxing
- --> $DIR/recursive-async-impl-trait-type.rs:5:40
+ --> $DIR/recursive-async-impl-trait-type.rs:8:40
|
LL | async fn recursive_async_function() -> () {
| ^^ recursive `async fn`
--- /dev/null
+// edition: 2021
+// check-pass
+
+// This test verifies that we do not create a query cycle when typechecking has several inference
+// variables that point to the same generator interior type.
+
+use std::future::Future;
+use std::pin::Pin;
+use std::task::{Context, Poll};
+
+type ChannelTask = Pin<Box<dyn Future<Output = ()> + Send>>;
+
+pub fn register_message_type() -> ChannelTask {
+ Box::pin(async move {
+ let f = |__cx: &mut Context<'_>| Poll::<()>::Pending;
+ PollFn { f }.await
+ })
+}
+
+struct PollFn<F> {
+ f: F,
+}
+
+impl<F> Unpin for PollFn<F> {}
+
+impl<T, F> Future for PollFn<F>
+where
+ F: FnMut(&mut Context<'_>) -> Poll<T>,
+{
+ type Output = T;
+
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
+ (&mut self.f)(cx)
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0698`.
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type of the type parameter `T` declared on the function `bar`
+ |
+help: consider specifying the generic argument
+ |
+LL | bar::<T>().await;
+ | +++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
--- /dev/null
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error[E0698]: type inside `async fn` body must be known in this context
+ --> $DIR/unresolved_type_param.rs:12:5
+ |
+LL | bar().await;
+ | ^^^ cannot infer type for type parameter `T` declared on the function `bar`
+ |
+note: the type is part of the `async fn` body because of this `await`
+ --> $DIR/unresolved_type_param.rs:12:10
+ |
+LL | bar().await;
+ | ^^^^^^
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0698`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// Provoke an unresolved type error (T).
// Error message should pinpoint the type parameter T as needing to be bound
// (rather than give a general error message)
// edition:2018
-// compile-flags: -Zdrop-tracking
async fn bar<T>() -> () {}
async fn foo() {
bar().await;
- //~^ ERROR type inside `async fn` body must be known in this context
- //~| ERROR type inside `async fn` body must be known in this context
- //~| ERROR type inside `async fn` body must be known in this context
- //~| NOTE cannot infer type for type parameter `T`
- //~| NOTE cannot infer type for type parameter `T`
- //~| NOTE cannot infer type for type parameter `T`
- //~| NOTE the type is part of the `async fn` body because of this `await`
- //~| NOTE the type is part of the `async fn` body because of this `await`
- //~| NOTE the type is part of the `async fn` body because of this `await`
- //~| NOTE in this expansion of desugaring of `await`
- //~| NOTE in this expansion of desugaring of `await`
- //~| NOTE in this expansion of desugaring of `await`
+ //[drop_tracking_mir]~^ ERROR type annotations needed
+ //[drop_tracking_mir]~| NOTE cannot infer type of the type parameter `T`
+ //[no_drop_tracking,drop_tracking]~^^^ ERROR type inside `async fn` body must be known in this context
+ //[no_drop_tracking,drop_tracking]~| ERROR type inside `async fn` body must be known in this context
+ //[no_drop_tracking,drop_tracking]~| ERROR type inside `async fn` body must be known in this context
+ //[no_drop_tracking,drop_tracking]~| NOTE cannot infer type for type parameter `T`
+ //[no_drop_tracking,drop_tracking]~| NOTE cannot infer type for type parameter `T`
+ //[no_drop_tracking,drop_tracking]~| NOTE cannot infer type for type parameter `T`
+ //[no_drop_tracking,drop_tracking]~| NOTE the type is part of the `async fn` body because of this `await`
+ //[no_drop_tracking,drop_tracking]~| NOTE the type is part of the `async fn` body because of this `await`
+ //[no_drop_tracking,drop_tracking]~| NOTE the type is part of the `async fn` body because of this `await`
+ //[no_drop_tracking,drop_tracking]~| NOTE in this expansion of desugaring of `await`
+ //[no_drop_tracking,drop_tracking]~| NOTE in this expansion of desugaring of `await`
+ //[no_drop_tracking,drop_tracking]~| NOTE in this expansion of desugaring of `await`
+ //[no_drop_tracking]~^^^^^^^^^^^^^^^ ERROR type inside `async fn` body must be known in this context
+ //[no_drop_tracking]~| ERROR type inside `async fn` body must be known in this context
+ //[no_drop_tracking]~| NOTE cannot infer type for type parameter `T`
+ //[no_drop_tracking]~| NOTE cannot infer type for type parameter `T`
+ //[no_drop_tracking]~| NOTE the type is part of the `async fn` body because of this `await`
+ //[no_drop_tracking]~| NOTE the type is part of the `async fn` body because of this `await`
+ //[no_drop_tracking]~| NOTE in this expansion of desugaring of `await`
+ //[no_drop_tracking]~| NOTE in this expansion of desugaring of `await`
}
fn main() {}
error[E0698]: type inside `async fn` body must be known in this context
- --> $DIR/unresolved_type_param.rs:10:5
+ --> $DIR/unresolved_type_param.rs:13:5
|
LL | bar().await;
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
|
note: the type is part of the `async fn` body because of this `await`
- --> $DIR/unresolved_type_param.rs:10:10
+ --> $DIR/unresolved_type_param.rs:13:10
|
LL | bar().await;
| ^^^^^^
error[E0698]: type inside `async fn` body must be known in this context
- --> $DIR/unresolved_type_param.rs:10:5
+ --> $DIR/unresolved_type_param.rs:13:5
|
LL | bar().await;
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
|
note: the type is part of the `async fn` body because of this `await`
- --> $DIR/unresolved_type_param.rs:10:10
+ --> $DIR/unresolved_type_param.rs:13:10
|
LL | bar().await;
| ^^^^^^
error[E0698]: type inside `async fn` body must be known in this context
- --> $DIR/unresolved_type_param.rs:10:5
+ --> $DIR/unresolved_type_param.rs:13:5
|
LL | bar().await;
| ^^^ cannot infer type for type parameter `T` declared on the function `bar`
|
note: the type is part of the `async fn` body because of this `await`
- --> $DIR/unresolved_type_param.rs:10:10
+ --> $DIR/unresolved_type_param.rs:13:10
|
LL | bar().await;
| ^^^^^^
|
= note: this error originates in the macro `bug` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: unexpected expression: `{
- let res =
- ::alloc::fmt::format(::core::fmt::Arguments::new_v1(&[""],
- &[::core::fmt::ArgumentV1::new_display(&"u8")]));
- res
- }.as_str()`
+error: unexpected expression: `{ let res = ::alloc::fmt::format(format_args!("{0}", "u8")); res }.as_str()`
--> $DIR/key-value-expansion.rs:48:23
|
LL | doc_comment! {format!("{coor}", coor = stringify!($t1)).as_str()}
}
fn main() {
- let mut x = Int(1);
+ let mut x = Int(1); //~ NOTE binding `x` declared here
x
//~^ NOTE borrow of `x` occurs here
+=
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/augmented-assignments.rs:16:5
|
+LL | let mut x = Int(1);
+ | ----- binding `x` declared here
LL | x
| - borrow of `x` occurs here
...
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/binop-move-semantics.rs:21:5
|
+LL | fn move_borrowed<T: Add<Output=()>>(x: T, mut y: T) {
+ | - binding `x` declared here
LL | let m = &x;
| -- borrow of `x` occurs here
...
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/binop-move-semantics.rs:23:5
|
+LL | fn move_borrowed<T: Add<Output=()>>(x: T, mut y: T) {
+ | ----- binding `y` declared here
+LL | let m = &x;
LL | let n = &mut y;
| ------ borrow of `y` occurs here
...
LL | ref foo @ [.., ref mut bar] => (),
| -------^^^^^^^^-----------^
| | |
- | | mutable borrow, by `bar`, occurs here
- | immutable borrow, by `foo`, occurs here
+ | | value is mutably borrowed by `bar` here
+ | value is borrowed by `foo` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:120:9
LL | ref foo @ Some(box ref mut s) => (),
| -------^^^^^^^^^^^^---------^
| | |
- | | mutable borrow, by `s`, occurs here
- | immutable borrow, by `foo`, occurs here
+ | | value is mutably borrowed by `s` here
+ | value is borrowed by `foo` here
error[E0382]: borrow of moved value: `x`
--> $DIR/bindings-after-at-or-patterns-slice-patterns-box-patterns.rs:18:5
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrow-tuple-fields.rs:12:13
|
+LL | let x: (Box<_>, _) = (Box::new(1), 2);
+ | - binding `x` declared here
LL | let r = &x.0;
| ---- borrow of `x.0` occurs here
LL | let y = x;
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrow-tuple-fields.rs:28:13
|
+LL | let x = Foo(Box::new(1), 2);
+ | - binding `x` declared here
LL | let r = &x.0;
| ---- borrow of `x.0` occurs here
LL | let y = x;
--> $DIR/borrowck-anon-fields-variant.rs:16:19
|
LL | Foo::Y(ref mut a, _) => a,
- | --------- borrow of `y.0` occurs here
+ | --------- `y.0` is borrowed here
...
LL | let b = match y {
| ^ use of borrowed `y.0`
--> $DIR/borrowck-anon-fields-variant.rs:34:19
|
LL | Foo::Y(ref mut a, _) => a,
- | --------- borrow of `y.0` occurs here
+ | --------- `y.0` is borrowed here
...
LL | let b = match y {
| ^ use of borrowed `y.0`
--> $DIR/borrowck-assign-comp.rs:10:5
|
LL | let q = &p;
- | -- borrow of `p.x` occurs here
+ | -- `p.x` is borrowed here
...
LL | p.x = 5;
- | ^^^^^^^ assignment to borrowed `p.x` occurs here
+ | ^^^^^^^ `p.x` is assigned to here but it was already borrowed
LL | q.x;
| --- borrow later used here
--> $DIR/borrowck-assign-comp.rs:20:5
|
LL | let q = &p.y;
- | ---- borrow of `p` occurs here
+ | ---- `p` is borrowed here
LL | p = Point {x: 5, y: 7};
- | ^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `p` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^ `p` is assigned to here but it was already borrowed
LL | p.x; // silence warning
LL | *q; // stretch loan
| -- borrow later used here
--> $DIR/borrowck-assign-comp.rs:31:5
|
LL | let q = &p.y;
- | ---- borrow of `p.y` occurs here
+ | ---- `p.y` is borrowed here
LL | p.y = 5;
- | ^^^^^^^ assignment to borrowed `p.y` occurs here
+ | ^^^^^^^ `p.y` is assigned to here but it was already borrowed
LL | *q;
| -- borrow later used here
--> $DIR/borrowck-assign-to-andmut-in-borrowed-loc.rs:18:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `y` occurs here
+ | ------ `y` is borrowed here
LL | *y.pointer += 1;
| ^^^^^^^^^^^^^^^ use of borrowed `y`
...
--> $DIR/borrowck-assign-to-andmut-in-borrowed-loc.rs:18:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `*y.pointer` occurs here
+ | ------ `*y.pointer` is borrowed here
LL | *y.pointer += 1;
- | ^^^^^^^^^^^^^^^ assignment to borrowed `*y.pointer` occurs here
+ | ^^^^^^^^^^^^^^^ `*y.pointer` is assigned to here but it was already borrowed
...
LL | *z.pointer += 1;
| --------------- borrow later used here
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/borrowck-bad-nested-calls-move.rs:25:9
|
+LL | let mut a: Box<_> = Box::new(1);
+ | ----- binding `a` declared here
+...
LL | add(
| --- borrow later used by call
LL | &*a,
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/borrowck-bad-nested-calls-move.rs:32:9
|
+LL | let mut a: Box<_> = Box::new(1);
+ | ----- binding `a` declared here
LL | add(
| --- borrow later used by call
LL | &*a,
LL | let c2 = || x * 5;
| -- - borrow occurs due to use in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | x = 5;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL |
LL | drop(c2);
| -- borrow later used here
LL | let c1 = || get(&x);
| -- - borrow occurs due to use in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | x = 5;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL |
LL | drop(c1);
| -- borrow later used here
LL | let c1 = || get(&*x);
| -- -- borrow occurs due to use in closure
| |
- | borrow of `*x` occurs here
+ | `*x` is borrowed here
LL | *x = 5;
- | ^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^ `*x` is assigned to here but it was already borrowed
LL |
LL | drop(c1);
| -- borrow later used here
LL | let c1 = || get(&*x.f);
| -- ---- borrow occurs due to use in closure
| |
- | borrow of `*x.f` occurs here
+ | `*x.f` is borrowed here
LL | *x.f = 5;
- | ^^^^^^^^ assignment to borrowed `*x.f` occurs here
+ | ^^^^^^^^ `*x.f` is assigned to here but it was already borrowed
LL |
LL | drop(c1);
| -- borrow later used here
--> $DIR/borrowck-describe-lvalue.rs:37:9
|
LL | let x = f.x();
- | ----- borrow of `f` occurs here
+ | ----- `f` is borrowed here
LL | f.x;
| ^^^ use of borrowed `f`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:44:9
|
LL | let x = g.x();
- | ----- borrow of `g` occurs here
+ | ----- `g` is borrowed here
LL | g.0;
| ^^^ use of borrowed `g`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:51:9
|
LL | let x = &mut h.0;
- | -------- borrow of `h.0` occurs here
+ | -------- `h.0` is borrowed here
LL | h.0;
| ^^^ use of borrowed `h.0`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:59:20
|
LL | let x = e.x();
- | ----- borrow of `e` occurs here
+ | ----- `e` is borrowed here
LL | match e {
LL | Baz::X(value) => value
| ^^^^^ use of borrowed `e`
--> $DIR/borrowck-describe-lvalue.rs:67:9
|
LL | let x = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | u.a;
| ^^^ use of borrowed `u.a`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:74:9
|
LL | let x = f.x();
- | ----- borrow of `*f` occurs here
+ | ----- `*f` is borrowed here
LL | f.x;
| ^^^ use of borrowed `*f`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:81:9
|
LL | let x = g.x();
- | ----- borrow of `*g` occurs here
+ | ----- `*g` is borrowed here
LL | g.0;
| ^^^ use of borrowed `*g`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:88:9
|
LL | let x = &mut h.0;
- | -------- borrow of `h.0` occurs here
+ | -------- `h.0` is borrowed here
LL | h.0;
| ^^^ use of borrowed `h.0`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:96:20
|
LL | let x = e.x();
- | ----- borrow of `*e` occurs here
+ | ----- `*e` is borrowed here
LL | match *e {
LL | Baz::X(value) => value
| ^^^^^ use of borrowed `*e`
--> $DIR/borrowck-describe-lvalue.rs:105:9
|
LL | let x = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | u.a;
| ^^^ use of borrowed `u.a`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:113:15
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | match v {
LL | &[x, _, .., _, _] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:118:18
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, x, .., _, _] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:123:25
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, _, .., x, _] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:128:28
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, _, .., _, x] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:139:15
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | match v {
LL | &[x @ ..] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:144:18
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, x @ ..] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:149:15
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[x @ .., _] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:154:18
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, x @ .., _] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:166:15
|
LL | let x = &mut e;
- | ------ borrow of `e` occurs here
+ | ------ `e` is borrowed here
LL | match e {
| ^ use of borrowed `e`
...
--> $DIR/borrowck-describe-lvalue.rs:232:9
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | v[0].y;
| ^^^^ use of borrowed `v`
...
--> $DIR/borrowck-describe-lvalue.rs:232:9
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | v[0].y;
| ^^^^^^ use of borrowed `v`
...
error[E0505]: cannot move out of `x.b` because it is borrowed
--> $DIR/borrowck-field-sensitivity.rs:34:10
|
+LL | let x = A { a: 1, b: Box::new(2) };
+ | - binding `x` declared here
LL | let p = &x.b;
| ---- borrow of `x.b` occurs here
LL | drop(x.b);
error[E0505]: cannot move out of `x.b` because it is borrowed
--> $DIR/borrowck-field-sensitivity.rs:41:14
|
+LL | let x = A { a: 1, b: Box::new(2) };
+ | - binding `x` declared here
LL | let p = &x.b;
| ---- borrow of `x.b` occurs here
LL | let _y = A { a: 3, .. x };
--> $DIR/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs:6:9
|
LL | let b = &mut _a;
- | ------- borrow of `_a` occurs here
+ | ------- `_a` is borrowed here
...
LL | _a = 4;
- | ^^^^^^ assignment to borrowed `_a` occurs here
+ | ^^^^^^ `_a` is assigned to here but it was already borrowed
...
LL | drop(b);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:25:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:35:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:45:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:55:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:65:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:75:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:85:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:95:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-lend-flow-match.rs:12:13
|
LL | Some(ref r) => {
- | ----- borrow of `x` occurs here
+ | ----- `x` is borrowed here
LL | x = Some(1);
- | ^^^^^^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^^^^^^ `x` is assigned to here but it was already borrowed
LL | drop(r);
| - borrow later used here
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move-cc.rs:14:19
|
+LL | let v: Box<_> = Box::new(3);
+ | - binding `v` declared here
LL | let w = &v;
| -- borrow of `v` occurs here
LL | thread::spawn(move|| {
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move-cc.rs:24:19
|
+LL | let v: Box<_> = Box::new(3);
+ | - binding `v` declared here
LL | let w = &v;
| -- borrow of `v` occurs here
LL | thread::spawn(move|| {
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move.rs:11:10
|
+LL | let v = Box::new(3);
+ | - binding `v` declared here
LL | let w = &v;
| -- borrow of `v` occurs here
LL | take(v);
--> $DIR/borrowck-loan-of-static-data-issue-27616.rs:16:5
|
LL | let alias: &'static mut String = s;
- | ------------------- - borrow of `*s` occurs here
+ | ------------------- - `*s` is borrowed here
| |
| type annotation requires that `*s` is borrowed for `'static`
...
LL | *s = String::new();
- | ^^ assignment to borrowed `*s` occurs here
+ | ^^ `*s` is assigned to here but it was already borrowed
error: aborting due to previous error
--> $DIR/borrowck-loan-rcvr-overloaded-op.rs:38:5
|
LL | let q = &mut p;
- | ------ borrow of `p` occurs here
+ | ------ `p` is borrowed here
LL |
LL | p + 3;
| ^ use of borrowed `p`
error[E0597]: `z.1` does not live long enough
--> $DIR/borrowck-local-borrow-with-panic-outlives-fn.rs:3:15
|
+LL | let mut z = (0, 0);
+ | ----- binding `z` declared here
LL | *x = Some(&mut z.1);
| ----------^^^^^^^^-
| | |
--> $DIR/borrowck-match-already-borrowed.rs:9:19
|
LL | let p = &mut foo;
- | -------- borrow of `foo` occurs here
+ | -------- `foo` is borrowed here
LL | let _ = match foo {
| ^^^ use of borrowed `foo`
...
--> $DIR/borrowck-match-already-borrowed.rs:12:16
|
LL | let p = &mut foo;
- | -------- borrow of `foo` occurs here
+ | -------- `foo` is borrowed here
...
LL | Foo::A(x) => x
| ^ use of borrowed `foo`
--> $DIR/borrowck-match-already-borrowed.rs:22:9
|
LL | let r = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | let _ = match x {
LL | x => x + 1,
| ^ use of borrowed `x`
--> $DIR/borrowck-match-already-borrowed.rs:23:9
|
LL | let r = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | y => y + 2,
| ^ use of borrowed `x`
| | |
| | variable moved due to use in closure
| | move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
- | move out of `bar` occurs here
+ | `bar` is moved here
error: aborting due to previous error
error[E0505]: cannot move out of `*a` because it is borrowed
--> $DIR/borrowck-move-from-subpath-of-borrowed-path.rs:12:13
|
+LL | let a: Box<Box<_>> = Box::new(Box::new(2));
+ | - binding `a` declared here
LL | let b = &a;
| -- borrow of `a` occurs here
LL |
error[E0505]: cannot move out of `t0` because it is borrowed
--> $DIR/borrowck-move-mut-base-ptr.rs:10:14
|
+LL | fn foo(t0: &mut isize) {
+ | -- binding `t0` declared here
LL | let p: &isize = &*t0; // Freezes `*t0`
| ---- borrow of `*t0` occurs here
LL | let t1 = t0;
error[E0505]: cannot move out of `a.x` because it is borrowed
--> $DIR/borrowck-move-subcomponent.rs:15:14
|
+LL | let a : S = S { x : Box::new(1) };
+ | - binding `a` declared here
LL | let pb = &a;
| -- borrow of `a` occurs here
LL | let S { x: ax } = a;
error[E0505]: cannot move out of `x1` because it is borrowed
--> $DIR/borrowck-multiple-captures.rs:12:19
|
+LL | let x1: Box<_> = Box::new(1);
+ | -- binding `x1` declared here
LL | let p1 = &x1;
| --- borrow of `x1` occurs here
...
error[E0505]: cannot move out of `x2` because it is borrowed
--> $DIR/borrowck-multiple-captures.rs:12:19
|
+LL | let x2: Box<_> = Box::new(2);
+ | -- binding `x2` declared here
LL | let p2 = &x2;
| --- borrow of `x2` occurs here
LL | thread::spawn(move|| {
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrowck-multiple-captures.rs:38:19
|
+LL | let x: Box<_> = Box::new(1);
+ | - binding `x` declared here
LL | let p = &x;
| -- borrow of `x` occurs here
LL | thread::spawn(move|| {
--> $DIR/borrowck-overloaded-index-and-overloaded-deref.rs:31:5
|
LL | let i = &v[0].f;
- | - borrow of `v` occurs here
+ | - `v` is borrowed here
LL | v = MyVec { x: MyPtr { x: Foo { f: 23 } } };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `v` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `v` is assigned to here but it was already borrowed
LL |
LL | read(*i);
| -- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:71:5
|
LL | let p = &f.foo[&s];
- | ----- borrow of `f.foo` occurs here
+ | ----- `f.foo` is borrowed here
LL | f.foo = g;
- | ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
+ | ^^^^^^^^^ `f.foo` is assigned to here but it was already borrowed
LL | p.use_ref();
| ----------- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:77:5
|
LL | let p = &f.foo[&s];
- | ----- borrow of `*f` occurs here
+ | ----- `*f` is borrowed here
LL | *f = g;
- | ^^^^^^ assignment to borrowed `*f` occurs here
+ | ^^^^^^ `*f` is assigned to here but it was already borrowed
LL | p.use_ref();
| ----------- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:83:5
|
LL | let p = &mut f.foo[&s];
- | ----- borrow of `f.foo` occurs here
+ | ----- `f.foo` is borrowed here
LL | f.foo = g;
- | ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
+ | ^^^^^^^^^ `f.foo` is assigned to here but it was already borrowed
LL | p.use_mut();
| ----------- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:89:5
|
LL | let p = &mut f.foo[&s];
- | ----- borrow of `*f` occurs here
+ | ----- `*f` is borrowed here
LL | *f = g;
- | ^^^^^^ assignment to borrowed `*f` occurs here
+ | ^^^^^^ `*f` is assigned to here but it was already borrowed
LL | p.use_mut();
| ----------- borrow later used here
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/borrowck-overloaded-index-move-index.rs:50:22
|
+LL | let mut s = "hello".to_string();
+ | ----- binding `s` declared here
LL | let rs = &mut s;
| ------ borrow of `s` occurs here
LL |
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/borrowck-overloaded-index-move-index.rs:53:7
|
+LL | let mut s = "hello".to_string();
+ | ----- binding `s` declared here
LL | let rs = &mut s;
| ------ borrow of `s` occurs here
...
--> $DIR/borrowck-pat-reassign-binding.rs:10:11
|
LL | Some(ref i) => {
- | ----- borrow of `x` occurs here
+ | ----- `x` is borrowed here
LL | // But on this branch, `i` is an outstanding borrow
LL | x = Some(*i+1);
- | ^^^^^^^^^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^^^^^^^^^ `x` is assigned to here but it was already borrowed
LL | drop(i);
| - borrow later used here
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrowck-unary-move.rs:3:10
|
+LL | fn foo(x: Box<isize>) -> isize {
+ | - binding `x` declared here
LL | let y = &*x;
| --- borrow of `*x` occurs here
LL | free(x);
--> $DIR/borrowck-union-borrow-nested.rs:24:21
|
LL | let ra = &mut u.s.a;
- | ---------- borrow of `u.s.a` occurs here
+ | ---------- `u.s.a` is borrowed here
LL | let b = u.c;
| ^^^ use of borrowed `u.s.a`
LL | ra.use_mut();
--> $DIR/borrowck-union-borrow.rs:28:13
|
LL | let ra = &u.a;
- | ---- borrow of `u.a` occurs here
+ | ---- `u.a` is borrowed here
LL | u.a = 1;
- | ^^^^^^^ assignment to borrowed `u.a` occurs here
+ | ^^^^^^^ `u.a` is assigned to here but it was already borrowed
LL | drop(ra);
| -- borrow later used here
--> $DIR/borrowck-union-borrow.rs:49:13
|
LL | let ra = &u.a;
- | ---- borrow of `u.b` occurs here
+ | ---- `u.b` is borrowed here
LL | u.b = 1;
- | ^^^^^^^ assignment to borrowed `u.b` occurs here
+ | ^^^^^^^ `u.b` is assigned to here but it was already borrowed
LL | drop(ra);
| -- borrow later used here
--> $DIR/borrowck-union-borrow.rs:60:21
|
LL | let ra = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | let a = u.a;
| ^^^ use of borrowed `u.a`
LL | drop(ra);
--> $DIR/borrowck-union-borrow.rs:70:13
|
LL | let rma = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | u.a = 1;
- | ^^^^^^^ assignment to borrowed `u.a` occurs here
+ | ^^^^^^^ `u.a` is assigned to here but it was already borrowed
LL | drop(rma);
| --- borrow later used here
--> $DIR/borrowck-union-borrow.rs:81:21
|
LL | let ra = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | let b = u.b;
| ^^^ use of borrowed `u.a`
LL |
--> $DIR/borrowck-union-borrow.rs:92:13
|
LL | let rma = &mut u.a;
- | -------- borrow of `u.b` occurs here
+ | -------- `u.b` is borrowed here
LL | u.b = 1;
- | ^^^^^^^ assignment to borrowed `u.b` occurs here
+ | ^^^^^^^ `u.b` is assigned to here but it was already borrowed
LL | drop(rma);
| --- borrow later used here
--> $DIR/borrowck-use-mut-borrow.rs:11:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(x);
| ^ use of borrowed `x`
LL | *p = 2;
--> $DIR/borrowck-use-mut-borrow.rs:18:10
|
LL | let p = &mut x.a;
- | -------- borrow of `x.a` occurs here
+ | -------- `x.a` is borrowed here
LL | drop(x);
| ^ use of borrowed `x.a`
LL | *p = 3;
--> $DIR/borrowck-use-mut-borrow.rs:25:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(x.a);
| ^^^ use of borrowed `x`
LL | p.a = 3;
--> $DIR/borrowck-use-mut-borrow.rs:32:10
|
LL | let p = &mut x.a;
- | -------- borrow of `x.a` occurs here
+ | -------- `x.a` is borrowed here
LL | drop(x.a);
| ^^^ use of borrowed `x.a`
LL | *p = 3;
--> $DIR/borrowck-use-mut-borrow.rs:39:13
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | let y = A { b: 3, .. x };
| ^^^^^^^^^^^^^^^^ use of borrowed `x`
LL | drop(y);
--> $DIR/borrowck-use-mut-borrow.rs:47:13
|
LL | let p = &mut x.a;
- | -------- borrow of `x.a` occurs here
+ | -------- `x.a` is borrowed here
LL | let y = A { b: 3, .. x };
| ^^^^^^^^^^^^^^^^ use of borrowed `x.a`
LL | drop(y);
--> $DIR/borrowck-use-mut-borrow.rs:55:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(*x);
| ^^ use of borrowed `x`
LL | **p = 2;
--> $DIR/borrowck-use-mut-borrow.rs:62:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(*x.b);
| ^^^^ use of borrowed `x`
LL | p.a = 3;
--> $DIR/borrowck-use-mut-borrow.rs:69:10
|
LL | let p = &mut x.b;
- | -------- borrow of `x.b` occurs here
+ | -------- `x.b` is borrowed here
LL | drop(*x.b);
| ^^^^ use of borrowed `x.b`
LL | **p = 3;
--> $DIR/borrowck-vec-pattern-move-tail.rs:8:5
|
LL | [1, 2, ref tail @ ..] => tail,
- | -------- borrow of `a[_]` occurs here
+ | -------- `a[_]` is borrowed here
...
LL | a[2] = 0;
- | ^^^^^^^^ assignment to borrowed `a[_]` occurs here
+ | ^^^^^^^^ `a[_]` is assigned to here but it was already borrowed
LL | println!("t[0]: {}", t[0]);
| ---- borrow later used here
let mut vec = [Box::new(1), Box::new(2), Box::new(3)];
match vec {
[box ref _a, _, _] => {
- //~^ NOTE borrow of `vec[_]` occurs here
+ //~^ NOTE `vec[_]` is borrowed here
vec[0] = Box::new(4); //~ ERROR cannot assign
- //~^ NOTE assignment to borrowed `vec[_]` occurs here
+ //~^ NOTE `vec[_]` is assigned to here
_a.use_ref();
//~^ NOTE borrow later used here
}
let vec: &mut [Box<isize>] = &mut vec;
match vec {
&mut [ref _b @ ..] => {
- //~^ borrow of `vec[_]` occurs here
+ //~^ `vec[_]` is borrowed here
vec[0] = Box::new(4); //~ ERROR cannot assign
- //~^ NOTE assignment to borrowed `vec[_]` occurs here
+ //~^ NOTE `vec[_]` is assigned to here
_b.use_ref();
//~^ NOTE borrow later used here
}
--> $DIR/borrowck-vec-pattern-nesting.rs:9:13
|
LL | [box ref _a, _, _] => {
- | ------ borrow of `vec[_]` occurs here
+ | ------ `vec[_]` is borrowed here
LL |
LL | vec[0] = Box::new(4);
- | ^^^^^^ assignment to borrowed `vec[_]` occurs here
+ | ^^^^^^ `vec[_]` is assigned to here but it was already borrowed
LL |
LL | _a.use_ref();
| ------------ borrow later used here
--> $DIR/borrowck-vec-pattern-nesting.rs:23:13
|
LL | &mut [ref _b @ ..] => {
- | ------ borrow of `vec[_]` occurs here
+ | ------ `vec[_]` is borrowed here
LL |
LL | vec[0] = Box::new(4);
- | ^^^^^^ assignment to borrowed `vec[_]` occurs here
+ | ^^^^^^ `vec[_]` is assigned to here but it was already borrowed
LL |
LL | _b.use_ref();
| ------------ borrow later used here
| ^^^^^^^^^^^ use of borrowed `*self`
...
LL | let r = &mut *self;
- | ---------- borrow of `*self` occurs here
+ | ---------- `*self` is borrowed here
LL | r.get_size(width!(self))
| -------- ------------ in this macro invocation
| |
--> $DIR/issue-52713-bug.rs:12:5
|
LL | let y = &x;
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
...
LL | x += 1;
- | ^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^ `x` is assigned to here but it was already borrowed
LL | println!("{}", y);
| - borrow later used here
LL | let res = (|| (|| &greeting)())();
| -- -------- borrow occurs due to use in closure
| |
- | borrow of `greeting` occurs here
+ | `greeting` is borrowed here
LL |
LL | greeting = "DEALLOCATED".to_string();
- | ^^^^^^^^ assignment to borrowed `greeting` occurs here
+ | ^^^^^^^^ `greeting` is assigned to here but it was already borrowed
...
LL | println!("thread result: {:?}", res);
| --- borrow later used here
--> $DIR/issue-81365-1.rs:21:9
|
LL | let first = &self.target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-10.rs:21:9
|
LL | let first = &self.deref().target_field;
- | ------------ borrow of `self.container_field` occurs here
+ | ------------ `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
--> $DIR/issue-81365-11.rs:27:9
|
LL | let first = &mut self.target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
--> $DIR/issue-81365-2.rs:25:9
|
LL | let first = &self.container.target_field;
- | -------------- borrow of `self.container.container_field` occurs here
+ | -------------- `self.container.container_field` is borrowed here
LL | self.container.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-3.rs:32:9
|
LL | let first = &self.target_field;
- | ---- borrow of `self.container.container_field` occurs here
+ | ---- `self.container.container_field` is borrowed here
LL | self.container.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-4.rs:33:9
|
LL | let first = &self.target_field;
- | ---- borrow of `self.outer_field` occurs here
+ | ---- `self.outer_field` is borrowed here
LL | self.outer_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.outer_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^ `self.outer_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-5.rs:28:9
|
LL | let first = self.get();
- | ---------- borrow of `self.container_field` occurs here
+ | ---------- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-6.rs:18:9
|
LL | let first = &self[0];
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-7.rs:20:5
|
LL | let first = &c.target_field;
- | - borrow of `c.container_field` occurs here
+ | - `c.container_field` is borrowed here
LL | c.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `c.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ `c.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-8.rs:21:9
|
LL | let first = &(*self).target_field;
- | ------- borrow of `self.container_field` occurs here
+ | ------- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-9.rs:21:9
|
LL | let first = &Deref::deref(self).target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
--- /dev/null
+#![feature(no_core)]
+#![feature(lang_items)]
+
+#![no_core]
+
+#[cfg(target_os = "linux")]
+#[link(name = "c")]
+extern {}
+
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ //~^ ERROR: incorrect number of parameters for the `start` lang item
+ 40+2
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ drop_in_place(to_drop)
+}
+
+#[lang = "add"]
+trait Add<RHS> {
+ type Output;
+ fn add(self, other: RHS) -> Self::Output;
+}
+
+impl Add<isize> for isize {
+ type Output = isize;
+ fn add(self, other: isize) -> isize {
+ self + other
+ }
+}
+
+fn main() {}
--- /dev/null
+error: incorrect number of parameters for the `start` lang item
+ --> $DIR/issue-92157.rs:11:1
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `start` lang item should have four parameters, but found 3
+ = note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
+
+error: aborting due to previous error
+
--> $DIR/two-phase-allow-access-during-reservation.rs:26:19
|
LL | /*1*/ let p = &mut i; // (reservation of `i` starts here)
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
LL |
LL | /*2*/ let j = i; // OK: `i` is only reserved here
| ^ use of borrowed `i`
--> $DIR/two-phase-allow-access-during-reservation.rs:31:19
|
LL | /*1*/ let p = &mut i; // (reservation of `i` starts here)
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
...
LL | /*4*/ let k = i;
| ^ use of borrowed `i`
--> $DIR/two-phase-surprise-no-conflict.rs:21:23
|
LL | let _mut_borrow = &mut *self;
- | ---------- borrow of `*self` occurs here
+ | ---------- `*self` is borrowed here
LL | let _access = self.cx;
| ^^^^^^^ use of borrowed `*self`
LL |
error[E0505]: cannot move out of `alloc` because it is borrowed
--> $DIR/leak-alloc.rs:26:10
|
+LL | let alloc = Alloc {};
+ | ----- binding `alloc` declared here
LL | let boxed = Box::new_in(10, alloc.by_ref());
| -------------- borrow of `alloc` occurs here
LL | let theref = Box::leak(boxed);
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/btreemap_dropck.rs:15:10
|
+LL | let s = String::from("Hello World!");
+ | - binding `s` declared here
LL | let _map = BTreeMap::from_iter([((), PrintOnDrop(&s))]);
| -- borrow of `s` occurs here
LL | drop(s);
--> $DIR/variadic-ffi-4.rs:28:11
|
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
- | - let's call the lifetime of this reference `'3`
+ | - ------- binding `ap1` declared here
+ | |
+ | let's call the lifetime of this reference `'3`
LL | ap0 = &mut ap1;
| ------^^^^^^^^
| | |
let u = 5 as bool; //~ ERROR cannot cast as `bool`
//~| HELP compare with zero instead
//~| SUGGESTION 5 != 0
+
let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool`
//~| HELP compare with zero instead
//~| SUGGESTION (1 + 2) != 0
- let v = "hello" as bool; //~ ERROR casting `&'static str` as `bool` is invalid
+
+ let v = "hello" as bool;
+ //~^ ERROR casting `&'static str` as `bool` is invalid
+ //~| HELP consider using the `is_empty` method on `&'static str` to determine if it contains anything
}
| ^^^^^^^^^ help: compare with zero instead: `5 != 0`
error[E0054]: cannot cast as `bool`
- --> $DIR/cast-as-bool.rs:5:13
+ --> $DIR/cast-as-bool.rs:6:13
|
LL | let t = (1 + 2) as bool;
| ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
error[E0606]: casting `&'static str` as `bool` is invalid
- --> $DIR/cast-as-bool.rs:8:13
+ --> $DIR/cast-as-bool.rs:10:13
|
LL | let v = "hello" as bool;
| ^^^^^^^^^^^^^^^
+ |
+help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
+ |
+LL | let v = !"hello".is_empty();
+ | + ~~~~~~~~~~~
error: aborting due to 3 previous errors
--- /dev/null
+use std::ops::Deref;
+
+struct Foo;
+
+impl Deref for Foo {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &[]
+ }
+}
+
+fn main() {
+ let _ = "foo" as bool;
+ //~^ ERROR casting `&'static str` as `bool` is invalid [E0606]
+
+ let _ = String::from("foo") as bool;
+ //~^ ERROR non-primitive cast: `String` as `bool` [E0605]
+
+ let _ = Foo as bool;
+ //~^ ERROR non-primitive cast: `Foo` as `bool` [E0605]
+}
+
+fn _slice(bar: &[i32]) -> bool {
+ bar as bool
+ //~^ ERROR casting `&[i32]` as `bool` is invalid [E0606]
+}
--- /dev/null
+error[E0606]: casting `&'static str` as `bool` is invalid
+ --> $DIR/issue-106883-is-empty.rs:14:13
+ |
+LL | let _ = "foo" as bool;
+ | ^^^^^^^^^^^^^
+ |
+help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
+ |
+LL | let _ = !"foo".is_empty();
+ | + ~~~~~~~~~~~
+
+error[E0605]: non-primitive cast: `String` as `bool`
+ --> $DIR/issue-106883-is-empty.rs:17:13
+ |
+LL | let _ = String::from("foo") as bool;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+ |
+note: this expression `Deref`s to `str` which implements `is_empty`
+ --> $DIR/issue-106883-is-empty.rs:17:13
+ |
+LL | let _ = String::from("foo") as bool;
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider using the `is_empty` method on `String` to determine if it contains anything
+ |
+LL | let _ = !String::from("foo").is_empty();
+ | + ~~~~~~~~~~~
+
+error[E0605]: non-primitive cast: `Foo` as `bool`
+ --> $DIR/issue-106883-is-empty.rs:20:13
+ |
+LL | let _ = Foo as bool;
+ | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+ |
+note: this expression `Deref`s to `[u8]` which implements `is_empty`
+ --> $DIR/issue-106883-is-empty.rs:20:13
+ |
+LL | let _ = Foo as bool;
+ | ^^^
+help: consider using the `is_empty` method on `Foo` to determine if it contains anything
+ |
+LL | let _ = !Foo.is_empty();
+ | + ~~~~~~~~~~~
+
+error[E0606]: casting `&[i32]` as `bool` is invalid
+ --> $DIR/issue-106883-is-empty.rs:25:5
+ |
+LL | bar as bool
+ | ^^^^^^^^^^^
+ |
+help: consider using the `is_empty` method on `&[i32]` to determine if it contains anything
+ |
+LL | !bar.is_empty()
+ | + ~~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0605, E0606.
+For more information about an error, try `rustc --explain E0605`.
-// check-fail
-// known-bug
+// edition:2021
+// known-bug: unknown
// unset-rustc-env:RUST_BACKTRACE
-// compile-flags:-Z trait-solver=chalk --edition=2021
+// compile-flags:-Z trait-solver=chalk
// error-pattern:internal compiler error
// failure-status:101
// normalize-stderr-test "DefId([^)]*)" -> "..."
= help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:23:29: 25:2]`
= note: [async fn body@$DIR/async.rs:23:29: 25:2] must be a future or must implement `IntoFuture` to be awaited
-error: internal compiler error: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:23:29: 25:2]], def_id: ...), _use_mk_alias_ty_instead: () }, Term::Ty(u32)), []), depth=0)`
+error: internal compiler error: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:23:29: 25:2]], def_id: ...) }, Term::Ty(u32)), []), depth=0)`
--> $DIR/async.rs:23:25
|
LL | async fn foo(x: u32) -> u32 {
--> $DIR/arrays.rs:14:5
|
LL | let mut c = || {
- | -- borrow of `arr` occurs here
+ | -- `arr` is borrowed here
LL | arr[0] += 10;
| --- borrow occurs due to use of `arr` in closure
...
--> $DIR/arrays.rs:14:5
|
LL | let mut c = || {
- | -- borrow of `arr` occurs here
+ | -- `arr` is borrowed here
LL | arr[0] += 10;
| --- borrow occurs due to use of `arr` in closure
...
--> $DIR/arrays.rs:29:5
|
LL | let c = || {
- | -- borrow of `arr[_]` occurs here
+ | -- `arr[_]` is borrowed here
LL | println!("{:#?}", &arr[3..4]);
| --- borrow occurs due to use in closure
...
LL | arr[1] += 10;
- | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+ | ^^^^^^^^^^^^ `arr[_]` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
--> $DIR/arrays.rs:43:5
|
LL | let c = || {
- | -- borrow of `arr[_]` occurs here
+ | -- `arr[_]` is borrowed here
LL | println!("{}", arr[3]);
| --- borrow occurs due to use in closure
...
LL | arr[1] += 10;
- | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+ | ^^^^^^^^^^^^ `arr[_]` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
--> $DIR/arrays.rs:57:20
|
LL | let mut c = || {
- | -- borrow of `arr` occurs here
+ | -- `arr` is borrowed here
LL | arr[1] += 10;
| --- borrow occurs due to use of `arr` in closure
...
--> $DIR/box.rs:21:5
|
LL | let mut c = || {
- | -- borrow of `e.0.0.m.x` occurs here
+ | -- `e.0.0.m.x` is borrowed here
LL | e.0.0.m.x = format!("not-x");
| --------- borrow occurs due to use in closure
...
LL | e.0.0.m.x = format!("not-x");
- | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+ | ^^^^^^^^^ `e.0.0.m.x` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
--> $DIR/box.rs:55:5
|
LL | let c = || {
- | -- borrow of `e.0.0.m.x` occurs here
+ | -- `e.0.0.m.x` is borrowed here
LL | println!("{}", e.0.0.m.x);
| --------- borrow occurs due to use in closure
...
LL | e.0.0.m.x = format!("not-x");
- | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+ | ^^^^^^^^^ `e.0.0.m.x` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
fn main() {
let mut a = A { y: 1 };
let mut c = || {
- //~^ borrow of `a.y` occurs here
+ //~^ `a.y` is borrowed here
let _ = unsafe { &a.y };
let _ = &mut a;
//~^ borrow occurs due to use in closure
};
a.y = 1;
//~^ cannot assign to `a.y` because it is borrowed [E0506]
- //~| assignment to borrowed `a.y` occurs here
+ //~| `a.y` is assigned to here
c();
//~^ borrow later used here
}
--> $DIR/union.rs:20:5
|
LL | let mut c = || {
- | -- borrow of `a.y` occurs here
+ | -- `a.y` is borrowed here
...
LL | let _ = &mut a;
| - borrow occurs due to use in closure
...
LL | a.y = 1;
- | ^^^^^^^ assignment to borrowed `a.y` occurs here
+ | ^^^^^^^ `a.y` is assigned to here but it was already borrowed
...
LL | c();
| - borrow later used here
| |
| arguments to this struct are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-84128.rs:13:9
+ |
+LL | Foo(())
+ | ^^^^--^
+ | |
+ | this argument influences the type of `Foo`
note: tuple struct defined here
--> $DIR/issue-84128.rs:5:8
|
| |
| arguments to this enum variant are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-87461.rs:10:5
+ |
+LL | Ok(())
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
| |
| arguments to this enum variant are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-87461.rs:17:5
+ |
+LL | Ok(())
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
| |
| arguments to this enum variant are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-87461.rs:26:9
+ |
+LL | Ok(())
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
--> $DIR/multiple-fn-bounds.rs:10:5
|
LL | foo(move |x| v);
- | ^^^ --------
- | | | |
- | | | help: do not borrow the argument: `char`
- | | found signature defined here
+ | ^^^ -------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `fn(char) -> _`
|
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
| ^^^^^^^^^^^^^^^^ required by this bound in `foo`
+help: do not borrow the argument
+ |
+LL | foo(move |char| v);
+ | ~~~~
error: aborting due to previous error
--> $DIR/coerce-overloaded-autoderef-fail.rs:17:5
|
LL | let y = borrow(x);
- | - borrow of `**x` occurs here
+ | - `**x` is borrowed here
LL | let z = borrow(x);
LL | **x += 1;
- | ^^^^^^^^ assignment to borrowed `**x` occurs here
+ | ^^^^^^^^ `**x` is assigned to here but it was already borrowed
LL |
LL | drop((y, z));
| - borrow later used here
// Test that encountering closures during coherence does not cause issues.
#![feature(type_alias_impl_trait, generators)]
+#![cfg_attr(specialized, feature(specialization))]
+#![allow(incomplete_features)]
+
+// revisions: stock specialized
+// [specialized]check-pass
+
type OpaqueGenerator = impl Sized;
fn defining_use() -> OpaqueGenerator {
|| {
trait Trait {}
impl Trait for Wrapper<OpaqueGenerator> {}
impl<T: Sync> Trait for Wrapper<T> {}
-//~^ ERROR conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
+//[stock]~^ ERROR conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
fn main() {}
+++ /dev/null
-error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
- --> $DIR/coherence-with-generator.rs:15:1
- |
-LL | impl Trait for Wrapper<OpaqueGenerator> {}
- | --------------------------------------- first implementation here
-LL | impl<T: Sync> Trait for Wrapper<T> {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueGenerator>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0119`.
--- /dev/null
+error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
+ --> $DIR/coherence-with-generator.rs:21:1
+ |
+LL | impl Trait for Wrapper<OpaqueGenerator> {}
+ | --------------------------------------- first implementation here
+LL | impl<T: Sync> Trait for Wrapper<T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueGenerator>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
let _: Wow<A.0>;
//~^ ERROR expected one of
//~| HELP expressions must be enclosed in braces to be used as const generic arguments
-
- // FIXME(compiler-errors): This one is still unsatisfying,
- // and probably a case I could see someone typing by accident..
+ let _: Wow<[]>;
+ //~^ ERROR expected type
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
let _: Wow<[12]>;
- //~^ ERROR expected type, found
- //~| ERROR type provided when a constant was expected
+ //~^ ERROR expected type
+ //~| ERROR invalid const generic expression
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ let _: Wow<[0, 1, 3]>;
+ //~^ ERROR expected type
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ let _: Wow<[0xff; 8]>;
+ //~^ ERROR expected type
+ //~| ERROR invalid const generic expression
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ let _: Wow<[1, 2]>; // Regression test for issue #81698.
+ //~^ ERROR expected type
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ let _: Wow<&0>;
+ //~^ ERROR expected type
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ let _: Wow<("", 0)>;
+ //~^ ERROR expected type
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ let _: Wow<(1 + 2) * 3>;
+ //~^ ERROR expected type
+ //~| HELP expressions must be enclosed in braces to be used as const generic arguments
+ // FIXME(fmease): This one is pretty bad.
+ let _: Wow<!0>;
+ //~^ ERROR expected one of
+ //~| HELP you might have meant to end the type parameters here
}
LL | let _: Wow<{ A.0 }>;
| + +
+error: expected type, found `]`
+ --> $DIR/bad-const-generic-exprs.rs:16:17
+ |
+LL | let _: Wow<[]>;
+ | ^ expected type
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ [] }>;
+ | + +
+
error: expected type, found `12`
--> $DIR/bad-const-generic-exprs.rs:19:17
|
LL | let _: Wow<[12]>;
| ^^ expected type
-error[E0747]: type provided when a constant was expected
+error: invalid const generic expression
--> $DIR/bad-const-generic-exprs.rs:19:16
|
LL | let _: Wow<[12]>;
| ^^^^
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ [12] }>;
+ | + +
+
+error: expected type, found `0`
+ --> $DIR/bad-const-generic-exprs.rs:23:17
+ |
+LL | let _: Wow<[0, 1, 3]>;
+ | ^ expected type
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ [0, 1, 3] }>;
+ | + +
+
+error: expected type, found `0xff`
+ --> $DIR/bad-const-generic-exprs.rs:26:17
+ |
+LL | let _: Wow<[0xff; 8]>;
+ | ^^^^ expected type
+
+error: invalid const generic expression
+ --> $DIR/bad-const-generic-exprs.rs:26:16
+ |
+LL | let _: Wow<[0xff; 8]>;
+ | ^^^^^^^^^
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ [0xff; 8] }>;
+ | + +
+
+error: expected type, found `1`
+ --> $DIR/bad-const-generic-exprs.rs:30:17
+ |
+LL | let _: Wow<[1, 2]>; // Regression test for issue #81698.
+ | ^ expected type
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ [1, 2] }>; // Regression test for issue #81698.
+ | + +
+
+error: expected type, found `0`
+ --> $DIR/bad-const-generic-exprs.rs:33:17
+ |
+LL | let _: Wow<&0>;
+ | ^ expected type
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ &0 }>;
+ | + +
+
+error: expected type, found `""`
+ --> $DIR/bad-const-generic-exprs.rs:36:17
+ |
+LL | let _: Wow<("", 0)>;
+ | ^^ expected type
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ ("", 0) }>;
+ | + +
+
+error: expected type, found `1`
+ --> $DIR/bad-const-generic-exprs.rs:39:17
+ |
+LL | let _: Wow<(1 + 2) * 3>;
+ | ^ expected type
+ |
+help: expressions must be enclosed in braces to be used as const generic arguments
+ |
+LL | let _: Wow<{ (1 + 2) * 3 }>;
+ | + +
+
+error: expected one of `,` or `>`, found `0`
+ --> $DIR/bad-const-generic-exprs.rs:43:17
+ |
+LL | let _: Wow<!0>;
+ | - ^ expected one of `,` or `>`
+ | |
+ | while parsing the type for `_`
+ |
+help: you might have meant to end the type parameters here
+ |
+LL | let _: Wow<!>0>;
+ | +
-error: aborting due to 6 previous errors
+error: aborting due to 15 previous errors
-For more information about this error, try `rustc --explain E0747`.
LL | pub struct SelfDependent<const N: [u8; N]>;
| ^ the type must not depend on the parameter `N`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; N]` is forbidden as the type of a const generic parameter
--> $DIR/const-param-type-depends-on-const-param.rs:11:47
|
LL | pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]);
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; N]` is forbidden as the type of a const generic parameter
--> $DIR/const-param-type-depends-on-const-param.rs:15:35
|
LL | pub struct SelfDependent<const N: [u8; N]>;
pub struct Dependent<const N: usize, const X: [u8; N]>([(); N]);
//~^ ERROR: the type of const parameters must not depend on other generic parameters
-//[min]~^^ ERROR `[u8; _]` is forbidden
+//[min]~^^ ERROR `[u8; N]` is forbidden
pub struct SelfDependent<const N: [u8; N]>;
//~^ ERROR: the type of const parameters must not depend on other generic parameters
-//[min]~^^ ERROR `[u8; _]` is forbidden
+//[min]~^^ ERROR `[u8; N]` is forbidden
fn main() {}
-error[E0277]: the trait bound `[Adt; _]: Foo` is not satisfied
+error[E0277]: the trait bound `[Adt; std::mem::size_of::<Self::Assoc>()]: Foo` is not satisfied
--> $DIR/dont-evaluate-array-len-on-err-1.rs:15:9
|
LL | <[Adt; std::mem::size_of::<Self::Assoc>()] as Foo>::bar()
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `[Adt; _]`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `[Adt; std::mem::size_of::<Self::Assoc>()]`
error: aborting due to previous error
--> $DIR/array-size-in-generic-struct-param.rs:19:15
|
LL | arr: [u8; CFG.arr_size],
- | ^^^^^^^^^^^^ field access is not supported in generic constant
+ | ^^^^^^^^^^^^ field access is not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+pub struct Foo<const N: usize>;
+
+pub fn foo<const N: usize>() -> Foo<{ N + 1 }> {
+ Foo
+}
--- /dev/null
+#![feature(inline_const, generic_const_exprs)]
+//~^ WARN the feature `generic_const_exprs` is incomplete
+
+fn foo<T>() {
+ let _ = [0u8; const { std::mem::size_of::<T>() }];
+ //~^ ERROR: overly complex generic constant
+}
+
+fn main() {
+ foo::<i32>();
+}
--- /dev/null
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/const-block-is-poly.rs:1:26
+ |
+LL | #![feature(inline_const, generic_const_exprs)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: overly complex generic constant
+ --> $DIR/const-block-is-poly.rs:5:19
+ |
+LL | let _ = [0u8; const { std::mem::size_of::<T>() }];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const blocks are not supported in generic constants
+ |
+ = help: consider moving this anonymous constant into a `const` function
+ = note: this operation may be supported in the future
+
+error: aborting due to previous error; 1 warning emitted
+
| arguments to this struct are incorrect
|
= note: expected array `[u32; X]`
- found array `[u32; _]`
+ found array `[u32; Self::SIZE]`
note: tuple struct defined here
--> $DIR/issue-62504.rs:14:8
|
--> $DIR/issue-79518-default_trait_method_normalization.rs:16:32
|
LL | Self::AssocInstance == [(); std::mem::size_of::<Self::Assoc>()];
- | ------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found array `[(); _]`
+ | ------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found array `[(); std::mem::size_of::<Self::Assoc>()]`
| |
| expected because this is `<Self as Foo>::Assoc`
|
= note: expected associated type `<Self as Foo>::Assoc`
- found array `[(); _]`
- = help: consider constraining the associated type `<Self as Foo>::Assoc` to `[(); _]` or calling a method that returns `<Self as Foo>::Assoc`
+ found array `[(); std::mem::size_of::<Self::Assoc>()]`
+ = help: consider constraining the associated type `<Self as Foo>::Assoc` to `[(); std::mem::size_of::<Self::Assoc>()]` or calling a method that returns `<Self as Foo>::Assoc`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error: aborting due to previous error
--> $DIR/let-bindings.rs:6:68
|
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
- | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--> $DIR/let-bindings.rs:6:35
|
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
- | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+// aux-build:anon_const_non_local.rs
+
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+extern crate anon_const_non_local;
+
+fn bar<const M: usize>()
+where
+ [(); M + 1]:,
+{
+ let _: anon_const_non_local::Foo<2> = anon_const_non_local::foo::<M>();
+ //~^ ERROR: mismatched types
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/non_local_anon_const_diagnostics.rs:12:43
+ |
+LL | let _: anon_const_non_local::Foo<2> = anon_const_non_local::foo::<M>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `anon_const_non_local::::foo::{constant#0}`
+ |
+ = note: expected constant `2`
+ found constant `anon_const_non_local::::foo::{constant#0}`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--> $DIR/unused_expr.rs:4:34
|
LL | fn add<const N: usize>() -> [u8; { N + 1; 5 }] {
- | ^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--> $DIR/unused_expr.rs:9:34
|
LL | fn div<const N: usize>() -> [u8; { N / 1; 5 }] {
- | ^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--> $DIR/unused_expr.rs:16:38
|
LL | fn fn_call<const N: usize>() -> [u8; { foo(N); 5 }] {
- | ^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+// check-pass
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+#[derive(Clone)]
+struct Bar<const A: usize, const B: usize>
+where
+ [(); A as usize]:,
+ [(); B as usize]:,
+{}
+
+fn main() {}
LL | fn foo<const N: usize, const A: [u8; N]>() {}
| ^ the type must not depend on the parameter `N`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; N]` is forbidden as the type of a const generic parameter
--> $DIR/issue-62878.rs:5:33
|
LL | fn foo<const N: usize, const A: [u8; N]>() {}
fn foo<const N: usize, const A: [u8; N]>() {}
//~^ ERROR the type of const parameters must not
-//[min]~| ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~| ERROR `[u8; N]` is forbidden as the type of a const generic parameter
fn main() {
foo::<_, { [1] }>();
LL | |
LL | | 0
LL | | }],
- | |_____^ blocks are not supported in generic constant
+ | |_____^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
LL | |
LL | | 0
LL | | }],
- | |_____^ blocks are not supported in generic constant
+ | |_____^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
LL | |
LL | | 0
LL | | }],
- | |_____^ blocks are not supported in generic constant
+ | |_____^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
LL | fn foo<const LEN: usize, const DATA: [u8; LEN]>() {}
| ^^^ the type must not depend on the parameter `LEN`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; LEN]` is forbidden as the type of a const generic parameter
--> $DIR/issue-71169.rs:5:38
|
LL | fn foo<const LEN: usize, const DATA: [u8; LEN]>() {}
fn foo<const LEN: usize, const DATA: [u8; LEN]>() {}
//~^ ERROR the type of const parameters must not
-//[min]~^^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^^ ERROR `[u8; LEN]` is forbidden as the type of a const generic parameter
fn main() {
const DATA: [u8; 4] = *b"ABCD";
foo::<4, DATA>();
-error: `[u32; _]` is forbidden as the type of a const generic parameter
+error: `[u32; LEN]` is forbidden as the type of a const generic parameter
--> $DIR/issue-73491.rs:8:19
|
LL | fn hoge<const IN: [u32; LEN]>() {}
const LEN: usize = 1024;
fn hoge<const IN: [u32; LEN]>() {}
-//[min]~^ ERROR `[u32; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u32; LEN]` is forbidden as the type of a const generic parameter
fn main() {}
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
--> $DIR/issue-74101.rs:6:18
|
LL | fn test<const N: [u8; 1 + 2]>() {}
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
--> $DIR/issue-74101.rs:9:21
|
LL | struct Foo<const N: [u8; 1 + 2]>;
#![cfg_attr(full, allow(incomplete_features))]
fn test<const N: [u8; 1 + 2]>() {}
-//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
struct Foo<const N: [u8; 1 + 2]>;
-//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
fn main() {}
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; Bar::<u32>::value()]` is forbidden as the type of a const generic parameter
--> $DIR/issue-75047.rs:14:21
|
LL | struct Foo<const N: [u8; Bar::<u32>::value()]>;
}
struct Foo<const N: [u8; Bar::<u32>::value()]>;
-//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u8; Bar::<u32>::value()]` is forbidden as the type of a const generic parameter
fn main() {}
--> $DIR/issue-77357.rs:6:46
|
LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
// check-pass
-// known-bug
+// known-bug: unknown
// This should not compile, as the compiler should not know
// `A - 0` is satisfied `?x - 0` if `?x` is inferred to `A`.
impl<'a> Ref<'a> {
pub fn foo<const A: usize>() -> [(); A - 0] {
- //~^ WARN function cannot
Self::foo()
}
}
|
LL | pub fn foo<const A: usize>() -> [(); A - 0] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
-LL |
LL | Self::foo()
| ----------- recursive call site
|
fn consume<T: 'static>(_val: T)
where
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- //~^ ERROR: can't compare
+ //~^ overly complex generic constant
{
}
fn test<T: 'static>()
where
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- //~^ ERROR: can't compare
+ //~^ overly complex generic constant
{
}
-error[E0277]: can't compare `TypeId` with `_` in const contexts
- --> $DIR/issue-90318.rs:14:28
+error: overly complex generic constant
+ --> $DIR/issue-90318.rs:14:8
|
LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^ no implementation for `TypeId == _`
+ | ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | borrowing is not supported in generic constants
|
- = help: the trait `~const PartialEq<_>` is not implemented for `TypeId`
-note: the trait `PartialEq<_>` is implemented for `TypeId`, but that implementation is not `const`
- --> $DIR/issue-90318.rs:14:28
- |
-LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^
+ = help: consider moving this anonymous constant into a `const` function
+ = note: this operation may be supported in the future
-error[E0277]: can't compare `TypeId` with `_` in const contexts
- --> $DIR/issue-90318.rs:21:28
+error: overly complex generic constant
+ --> $DIR/issue-90318.rs:21:8
|
LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^ no implementation for `TypeId == _`
+ | ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | borrowing is not supported in generic constants
|
- = help: the trait `~const PartialEq<_>` is not implemented for `TypeId`
-note: the trait `PartialEq<_>` is implemented for `TypeId`, but that implementation is not `const`
- --> $DIR/issue-90318.rs:21:28
- |
-LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^
+ = help: consider moving this anonymous constant into a `const` function
+ = note: this operation may be supported in the future
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0277`.
| in this macro invocation
...
LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type
+ | ^ expected type
|
= note: this error originates in the macro `gimme_a_const` (in Nightly builds, run with -Z macro-backtrace for more info)
| in this macro invocation
...
LL | ($rusty: ident) => {{ let $rusty = 3; *&$rusty }}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type
+ | ^ expected type
|
= note: this error originates in the macro `gimme_a_const` (in Nightly builds, run with -Z macro-backtrace for more info)
error: expected type, found `{`
--> $DIR/macro-fail.rs:4:10
|
-LL | () => {{
- | __________^
-LL | |
-LL | | const X: usize = 1337;
-LL | | X
-LL | | }}
- | |___^ expected type
+LL | () => {{
+ | ^ expected type
...
-LL | let _fail = Example::<external_macro!()>;
- | -----------------
- | |
- | this macro call doesn't expand to a type
- | in this macro invocation
+LL | let _fail = Example::<external_macro!()>;
+ | -----------------
+ | |
+ | this macro call doesn't expand to a type
+ | in this macro invocation
|
= note: this error originates in the macro `external_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; {
+ struct Foo<const N: usize>;
+
+ impl<const N: usize> Foo<N> {
+ fn value() -> usize {
+ N
+ }
+ }
+
+ Foo::<17>::value()
+ }]` is forbidden as the type of a const generic parameter
--> $DIR/nested-type.rs:6:21
|
LL | struct Foo<const N: [u8; {
// run-rustfix
#![warn(unused_braces)]
+macro_rules! make_1 {
+ () => {
+ 1
+ }
+}
+
struct A<const N: usize>;
fn main() {
let _: A<7>; // ok
let _: A<7>; //~ WARN unnecessary braces
let _: A<{ 3 + 5 }>; // ok
+ let _: A<{make_1!()}>; // ok
}
// run-rustfix
#![warn(unused_braces)]
+macro_rules! make_1 {
+ () => {
+ 1
+ }
+}
+
struct A<const N: usize>;
fn main() {
let _: A<7>; // ok
let _: A<{ 7 }>; //~ WARN unnecessary braces
let _: A<{ 3 + 5 }>; // ok
+ let _: A<{make_1!()}>; // ok
}
warning: unnecessary braces around const expression
- --> $DIR/unused_braces.rs:9:14
+ --> $DIR/unused_braces.rs:15:14
|
LL | let _: A<{ 7 }>;
| ^^ ^^
--- /dev/null
+#![crate_type = "lib"]
+#![feature(const_closures, const_trait_impl)]
+#![allow(incomplete_features)]
+
+pub const fn test() {
+ let cl = const || {};
+ cl();
+}
--- /dev/null
+// aux-build:closure-in-foreign-crate.rs
+// build-pass
+
+extern crate closure_in_foreign_crate;
+
+const _: () = closure_in_foreign_crate::test();
+
+fn main() {}
error[E0080]: evaluation of constant value failed
- --> $DIR/infinite_loop.rs:6:15
+ --> $DIR/infinite_loop.rs:6:9
|
-LL | while n != 0 {
- | ^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+LL | / while n != 0 {
+LL | |
+LL | | n = if n % 2 == 0 { n/2 } else { 3*n + 1 };
+LL | | }
+ | |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`)
error: aborting due to previous error
let _ = [(); {
let mut x = &0;
let mut n = 0;
- while n < 5 {
- n = (n + 1) % 5; //~ ERROR evaluation of constant value failed
+ while n < 5 { //~ ERROR evaluation of constant value failed [E0080]
+ n = (n + 1) % 5;
x = &0; // Materialize a new AllocId
}
0
error[E0080]: evaluation of constant value failed
- --> $DIR/issue-52475.rs:6:17
+ --> $DIR/issue-52475.rs:5:9
|
-LL | n = (n + 1) % 5;
- | ^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+LL | / while n < 5 {
+LL | | n = (n + 1) % 5;
+LL | | x = &0; // Materialize a new AllocId
+LL | | }
+ | |_________^ exceeded interpreter step limit (see `#[const_eval_limit]`)
error: aborting due to previous error
--- /dev/null
+// check-fail
+// compile-flags: -Z tiny-const-eval-limit
+
+const fn foo() {}
+
+const fn call_foo() -> u32 {
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+
+ foo();
+ foo();
+ foo();
+ foo();
+ foo();
+
+ foo();
+ foo();
+ foo();
+ foo(); //~ ERROR evaluation of constant value failed [E0080]
+ 0
+}
+
+const X: u32 = call_foo();
+
+fn main() {
+ println!("{X}");
+}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $DIR/ctfe-fn-call.rs:28:5
+ |
+LL | foo();
+ | ^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+ |
+note: inside `call_foo`
+ --> $DIR/ctfe-fn-call.rs:28:5
+ |
+LL | foo();
+ | ^^^^^
+note: inside `X`
+ --> $DIR/ctfe-fn-call.rs:32:16
+ |
+LL | const X: u32 = call_foo();
+ | ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+// check-fail
+// compile-flags: -Z tiny-const-eval-limit
+
+const fn labelled_loop(n: u32) -> u32 {
+ let mut i = 0;
+ 'mylabel: loop { //~ ERROR evaluation of constant value failed [E0080]
+ if i > n {
+ break 'mylabel
+ }
+ i += 1;
+ }
+ 0
+}
+
+const X: u32 = labelled_loop(19);
+
+fn main() {
+ println!("{X}");
+}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $DIR/ctfe-labelled-loop.rs:6:5
+ |
+LL | / 'mylabel: loop {
+LL | | if i > n {
+LL | | break 'mylabel
+LL | | }
+LL | | i += 1;
+LL | | }
+ | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+ |
+note: inside `labelled_loop`
+ --> $DIR/ctfe-labelled-loop.rs:6:5
+ |
+LL | / 'mylabel: loop {
+LL | | if i > n {
+LL | | break 'mylabel
+LL | | }
+LL | | i += 1;
+LL | | }
+ | |_____^
+note: inside `X`
+ --> $DIR/ctfe-labelled-loop.rs:15:16
+ |
+LL | const X: u32 = labelled_loop(19);
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+// check-fail
+// compile-flags: -Z tiny-const-eval-limit
+
+const fn recurse(n: u32) -> u32 {
+ if n == 0 {
+ n
+ } else {
+ recurse(n - 1) //~ ERROR evaluation of constant value failed [E0080]
+ }
+}
+
+const X: u32 = recurse(19);
+
+fn main() {
+ println!("{X}");
+}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $DIR/ctfe-recursion.rs:8:9
+ |
+LL | recurse(n - 1)
+ | ^^^^^^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+ |
+note: inside `recurse`
+ --> $DIR/ctfe-recursion.rs:8:9
+ |
+LL | recurse(n - 1)
+ | ^^^^^^^^^^^^^^
+note: [... 18 additional calls inside `recurse` ...]
+ --> $DIR/ctfe-recursion.rs:8:9
+ |
+LL | recurse(n - 1)
+ | ^^^^^^^^^^^^^^
+note: inside `X`
+ --> $DIR/ctfe-recursion.rs:12:16
+ |
+LL | const X: u32 = recurse(19);
+ | ^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+// check-fail
+// compile-flags: -Z tiny-const-eval-limit
+const fn simple_loop(n: u32) -> u32 {
+ let mut index = 0;
+ while index < n { //~ ERROR evaluation of constant value failed [E0080]
+ index = index + 1;
+ }
+ 0
+}
+
+const X: u32 = simple_loop(19);
+
+fn main() {
+ println!("{X}");
+}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $DIR/ctfe-simple-loop.rs:5:5
+ |
+LL | / while index < n {
+LL | | index = index + 1;
+LL | | }
+ | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+ |
+note: inside `simple_loop`
+ --> $DIR/ctfe-simple-loop.rs:5:5
+ |
+LL | / while index < n {
+LL | | index = index + 1;
+LL | | }
+ | |_____^
+note: inside `X`
+ --> $DIR/ctfe-simple-loop.rs:11:16
+ |
+LL | const X: u32 = simple_loop(19);
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
--- /dev/null
+// check-pass
+//
+// Exercising an edge case which was found during Stage 2 compilation.
+// Compilation would fail for this code when running the `CtfeLimit`
+// MirPass (specifically when looking up the dominators).
+#![crate_type="lib"]
+
+const DUMMY: Expr = Expr::Path(ExprPath {
+ attrs: Vec::new(),
+ path: Vec::new(),
+});
+
+pub enum Expr {
+ Path(ExprPath),
+}
+pub struct ExprPath {
+ pub attrs: Vec<()>,
+ pub path: Vec<()>,
+}
|
LL | A = { if let 0 = 0 { todo!() } 0 },
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | A = { let _0 = 0; 0 },
+ | +
error: aborting due to previous error
|
LL | let x: [i32; { if let 0 = 0 { todo!() } 0 }] = [];
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | let x: [i32; { let _0 = 0; 0 }] = [];
+ | +
error: aborting due to previous error
|
LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | const X: i32 = { let _0 = 0; 0 };
+ | +
error[E0005]: refutable pattern in local binding
--> $DIR/const-match-check.rs:8:23
|
LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | static Y: i32 = { let _0 = 0; 0 };
+ | +
error[E0005]: refutable pattern in local binding
--> $DIR/const-match-check.rs:13:26
|
LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | const X: i32 = { let _0 = 0; 0 };
+ | +
error[E0005]: refutable pattern in local binding
--> $DIR/const-match-check.rs:19:26
|
LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | const X: i32 = { let _0 = 0; 0 };
+ | +
error: aborting due to 4 previous errors
LL | bytes: [u8; std::mem::size_of::<Foo>()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
- = note: ...which requires computing layout of `[u8; _]`...
- = note: ...which requires normalizing `[u8; _]`...
+ = note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
+ = note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
= note: ...which again requires evaluating type-level constant, completing the cycle
note: cycle used when checking that `Foo` is well-formed
--> $DIR/const-size_of-cycle.rs:3:1
--- /dev/null
+// run-pass
+#![feature(const_type_id)]
+#![feature(const_trait_impl)]
+
+use std::any::TypeId;
+
+const fn main() {
+ assert!(TypeId::of::<u8>() == TypeId::of::<u8>());
+ assert!(TypeId::of::<()>() != TypeId::of::<u8>());
+ const _A: bool = TypeId::of::<u8>() < TypeId::of::<u16>();
+ // can't assert `_A` because it is not deterministic
+}
error[E0080]: evaluation of constant value failed
- --> $DIR/const_eval_limit_reached.rs:6:11
+ --> $DIR/const_eval_limit_reached.rs:6:5
|
-LL | while x != 1000 {
- | ^^^^^^^^^ exceeded interpreter step limit (see `#[const_eval_limit]`)
+LL | / while x != 1000 {
+LL | |
+LL | | x += 1;
+LL | | }
+ | |_____^ exceeded interpreter step limit (see `#[const_eval_limit]`)
error: aborting due to previous error
LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
- = note: ...which requires computing layout of `[u8; _]`...
- = note: ...which requires normalizing `[u8; _]`...
+ = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
+ = note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
= note: ...which again requires evaluating type-level constant, completing the cycle
note: cycle used when checking that `Foo` is well-formed
--> $DIR/issue-44415.rs:5:1
#![feature(const_type_id)]
#![feature(const_type_name)]
+#![feature(const_trait_impl)]
use std::any::{self, TypeId};
}
const fn check_type_id<T: 'static>() -> bool {
- matches!(GetTypeId::<T>::VALUE, GetTypeId::<usize>::VALUE)
+ GetTypeId::<T>::VALUE == GetTypeId::<usize>::VALUE
}
pub struct GetTypeNameLen<T>(T);
LL | let x: &'static u32 = {
| ------------ type annotation requires that `y` is borrowed for `'static`
LL | let y = 42;
+ | - binding `y` declared here
LL | &y
| ^^ borrowed value does not live long enough
LL | };
[5; Self::HOST_SIZE] == [6; 0]
//~^ ERROR constant expression depends on a generic parameter
//~| ERROR constant expression depends on a generic parameter
- //~| ERROR can't compare `[{integer}; _]` with `[{integer}; 0]`
+ //~| ERROR can't compare `[{integer}; Self::HOST_SIZE]` with `[{integer}; 0]`
}
}
|
= note: this may fail depending on what value the parameter takes
-error[E0277]: can't compare `[{integer}; _]` with `[{integer}; 0]`
+error[E0277]: can't compare `[{integer}; Self::HOST_SIZE]` with `[{integer}; 0]`
--> $DIR/too_generic_eval_ice.rs:7:30
|
LL | [5; Self::HOST_SIZE] == [6; 0]
- | ^^ no implementation for `[{integer}; _] == [{integer}; 0]`
+ | ^^ no implementation for `[{integer}; Self::HOST_SIZE] == [{integer}; 0]`
|
- = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; _]`
+ = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]`
= help: the following other types implement trait `PartialEq<Rhs>`:
<&[B] as PartialEq<[A; N]>>
<&[T] as PartialEq<Vec<U, A>>>
-error: values of the type `[u8; SIZE]` are too big for the current architecture
+error: values of the type `[u8; usize::MAX]` are too big for the current architecture
error: aborting due to previous error
-error: values of the type `[u8; SIZE]` are too big for the current architecture
+error: values of the type `[u8; usize::MAX]` are too big for the current architecture
error: aborting due to previous error
--- /dev/null
+// Test dump-dep-graph requires query-dep-graph enabled
+
+// incremental
+// compile-flags: -Z dump-dep-graph
+
+fn main() {}
--- /dev/null
+error: can't dump dependency graph without `-Z query-dep-graph`
+
| |
| doesn't satisfy `Value: Eq`
| doesn't satisfy `Value: Hash`
+ | doesn't satisfy `Value: PartialEq`
...
LL | hs.insert(Value(0));
| ^^^^^^
|
= note: the following trait bounds were not satisfied:
`Value: Eq`
+ `Value: PartialEq`
+ which is required by `Value: Eq`
`Value: Hash`
help: consider annotating `Value` with `#[derive(Eq, Hash, PartialEq)]`
|
--> $DIR/issue-91550.rs:26:9
|
LL | pub struct NoDerives;
- | -------------------- doesn't satisfy `NoDerives: Eq`
+ | --------------------
+ | |
+ | doesn't satisfy `NoDerives: Eq`
+ | doesn't satisfy `NoDerives: PartialEq`
LL |
LL | struct Object<T>(T);
| ---------------- method `use_eq` not found for this struct
| ^^ ---------
| |
| unsatisfied trait bound introduced here
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: Eq`
help: consider annotating `NoDerives` with `#[derive(Eq, PartialEq)]`
|
LL | #[derive(Eq, PartialEq)]
--> $DIR/issue-91550.rs:27:9
|
LL | pub struct NoDerives;
- | -------------------- doesn't satisfy `NoDerives: Ord`
+ | --------------------
+ | |
+ | doesn't satisfy `NoDerives: Eq`
+ | doesn't satisfy `NoDerives: Ord`
+ | doesn't satisfy `NoDerives: PartialEq`
+ | doesn't satisfy `NoDerives: PartialOrd`
LL |
LL | struct Object<T>(T);
| ---------------- method `use_ord` not found for this struct
| ^^^ ---------
| |
| unsatisfied trait bound introduced here
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: PartialOrd`
+ which is required by `NoDerives: Ord`
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: Ord`
+ `NoDerives: Eq`
+ which is required by `NoDerives: Ord`
help: consider annotating `NoDerives` with `#[derive(Eq, Ord, PartialEq, PartialOrd)]`
|
LL | #[derive(Eq, Ord, PartialEq, PartialOrd)]
LL | pub struct NoDerives;
| --------------------
| |
+ | doesn't satisfy `NoDerives: Eq`
| doesn't satisfy `NoDerives: Ord`
+ | doesn't satisfy `NoDerives: PartialEq`
| doesn't satisfy `NoDerives: PartialOrd`
LL |
LL | struct Object<T>(T);
| | |
| | unsatisfied trait bound introduced here
| unsatisfied trait bound introduced here
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: Ord`
+ `NoDerives: Eq`
+ which is required by `NoDerives: Ord`
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: PartialOrd`
help: consider annotating `NoDerives` with `#[derive(Eq, Ord, PartialEq, PartialOrd)]`
|
LL | #[derive(Eq, Ord, PartialEq, PartialOrd)]
#[automatically_derived]
impl ::core::fmt::Debug for Fieldless {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- match self {
- Fieldless::A => ::core::fmt::Formatter::write_str(f, "A"),
- Fieldless::B => ::core::fmt::Formatter::write_str(f, "B"),
- Fieldless::C => ::core::fmt::Formatter::write_str(f, "C"),
- }
+ ::core::fmt::Formatter::write_str(f,
+ match self {
+ Fieldless::A => "A",
+ Fieldless::B => "B",
+ Fieldless::C => "C",
+ })
}
}
#[automatically_derived]
-> ::core::option::Option<::core::cmp::Ordering> {
let __self_tag = ::core::intrinsics::discriminant_value(self);
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
- match ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag) {
- ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
- match (self, other) {
- (Mixed::R(__self_0), Mixed::R(__arg1_0)) =>
- ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
- (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S {
- d1: __arg1_0, d2: __arg1_1 }) =>
- match ::core::cmp::PartialOrd::partial_cmp(__self_0,
- __arg1_0) {
- ::core::option::Option::Some(::core::cmp::Ordering::Equal)
- => ::core::cmp::PartialOrd::partial_cmp(__self_1, __arg1_1),
- cmp => cmp,
- },
- _ =>
- ::core::option::Option::Some(::core::cmp::Ordering::Equal),
+ match (self, other) {
+ (Mixed::R(__self_0), Mixed::R(__arg1_0)) =>
+ ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+ (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S {
+ d1: __arg1_0, d2: __arg1_1 }) =>
+ match ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0)
+ {
+ ::core::option::Option::Some(::core::cmp::Ordering::Equal)
+ => ::core::cmp::PartialOrd::partial_cmp(__self_1, __arg1_1),
+ cmp => cmp,
},
- cmp => cmp,
+ _ =>
+ ::core::cmp::PartialOrd::partial_cmp(&__self_tag,
+ &__arg1_tag),
}
}
}
-> ::core::option::Option<::core::cmp::Ordering> {
let __self_tag = ::core::intrinsics::discriminant_value(self);
let __arg1_tag = ::core::intrinsics::discriminant_value(other);
- match ::core::cmp::PartialOrd::partial_cmp(&__self_tag, &__arg1_tag) {
- ::core::option::Option::Some(::core::cmp::Ordering::Equal) =>
- match (self, other) {
- (Fielded::X(__self_0), Fielded::X(__arg1_0)) =>
- ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
- (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) =>
- ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
- (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) =>
- ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
- _ => unsafe { ::core::intrinsics::unreachable() }
- },
- cmp => cmp,
+ match (self, other) {
+ (Fielded::X(__self_0), Fielded::X(__arg1_0)) =>
+ ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+ (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) =>
+ ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+ (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) =>
+ ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0),
+ _ =>
+ ::core::cmp::PartialOrd::partial_cmp(&__self_tag,
+ &__arg1_tag),
}
}
}
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/drop-with-active-borrows-1.rs:4:10
|
+LL | let a = "".to_string();
+ | - binding `a` declared here
LL | let b: Vec<&str> = a.lines().collect();
| --------- borrow of `a` occurs here
LL | drop(a);
error[E0597]: `c_shortest` does not live long enough
--> $DIR/dropck-eyepatch-extern-crate.rs:46:23
|
+LL | let (mut dt, mut dr, c_shortest): (Dt<_>, Dr<_>, Cell<_>);
+ | ---------- binding `c_shortest` declared here
+...
LL | dt = Dt("dt", &c_shortest);
| ^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `c_shortest` does not live long enough
--> $DIR/dropck-eyepatch-extern-crate.rs:68:32
|
+LL | let (mut pt, mut pr, c_shortest): (Pt<_, _>, Pr<_>, Cell<_>);
+ | ---------- binding `c_shortest` declared here
+...
LL | pt = Pt("pt", &c_long, &c_shortest);
| ^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `c_shortest` does not live long enough
--> $DIR/dropck-eyepatch-reorder.rs:64:23
|
+LL | let (mut dt, mut dr, c_shortest): (Dt<_>, Dr<_>, Cell<_>);
+ | ---------- binding `c_shortest` declared here
+...
LL | dt = Dt("dt", &c_shortest);
| ^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `c_shortest` does not live long enough
--> $DIR/dropck-eyepatch-reorder.rs:86:32
|
+LL | let (mut pt, mut pr, c_shortest): (Pt<_, _>, Pr<_>, Cell<_>);
+ | ---------- binding `c_shortest` declared here
+...
LL | pt = Pt("pt", &c_long, &c_shortest);
| ^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `c_shortest` does not live long enough
--> $DIR/dropck-eyepatch.rs:88:23
|
+LL | let (mut dt, mut dr, c_shortest): (Dt<_>, Dr<_>, Cell<_>);
+ | ---------- binding `c_shortest` declared here
+...
LL | dt = Dt("dt", &c_shortest);
| ^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `c_shortest` does not live long enough
--> $DIR/dropck-eyepatch.rs:110:32
|
+LL | let (mut pt, mut pr, c_shortest): (Pt<_, _>, Pr<_>, Cell<_>);
+ | ---------- binding `c_shortest` declared here
+...
LL | pt = Pt("pt", &c_long, &c_shortest);
| ^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `v` does not live long enough
--> $DIR/dropck-union.rs:37:18
|
+LL | let v : Wrap<C> = Wrap::new(C(Cell::new(None)));
+ | - binding `v` declared here
LL | v.0.set(Some(&v));
| ^^ borrowed value does not live long enough
LL | }
--> $DIR/dropck_trait_cycle_checked.rs:111:13
|
LL | let (o1, o2, o3): (Box<dyn Obj>, Box<dyn Obj>, Box<dyn Obj>) = (O::new(), O::new(), O::new());
- | -------- cast requires that `o2` is borrowed for `'static`
+ | -- binding `o2` declared here -------- cast requires that `o2` is borrowed for `'static`
LL | o1.set0(&o2);
| ^^^ borrowed value does not live long enough
...
--> $DIR/dropck_trait_cycle_checked.rs:112:13
|
LL | let (o1, o2, o3): (Box<dyn Obj>, Box<dyn Obj>, Box<dyn Obj>) = (O::new(), O::new(), O::new());
- | -------- cast requires that `o3` is borrowed for `'static`
+ | -- binding `o3` declared here -------- cast requires that `o3` is borrowed for `'static`
LL | o1.set0(&o2);
LL | o1.set1(&o3);
| ^^^ borrowed value does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:113:13
|
LL | let (o1, o2, o3): (Box<dyn Obj>, Box<dyn Obj>, Box<dyn Obj>) = (O::new(), O::new(), O::new());
- | -------- cast requires that `o2` is borrowed for `'static`
+ | -- binding `o2` declared here -------- cast requires that `o2` is borrowed for `'static`
...
LL | o2.set0(&o2);
| ^^^ borrowed value does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:114:13
|
LL | let (o1, o2, o3): (Box<dyn Obj>, Box<dyn Obj>, Box<dyn Obj>) = (O::new(), O::new(), O::new());
- | -------- cast requires that `o3` is borrowed for `'static`
+ | -- binding `o3` declared here -------- cast requires that `o3` is borrowed for `'static`
...
LL | o2.set1(&o3);
| ^^^ borrowed value does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:115:13
|
LL | let (o1, o2, o3): (Box<dyn Obj>, Box<dyn Obj>, Box<dyn Obj>) = (O::new(), O::new(), O::new());
- | -------- cast requires that `o1` is borrowed for `'static`
+ | -- binding `o1` declared here -------- cast requires that `o1` is borrowed for `'static`
...
LL | o3.set0(&o1);
| ^^^ borrowed value does not live long enough
--> $DIR/dropck_trait_cycle_checked.rs:116:13
|
LL | let (o1, o2, o3): (Box<dyn Obj>, Box<dyn Obj>, Box<dyn Obj>) = (O::new(), O::new(), O::new());
- | -------- cast requires that `o2` is borrowed for `'static`
+ | -- binding `o2` declared here -------- cast requires that `o2` is borrowed for `'static`
...
LL | o3.set1(&o2);
| ^^^ borrowed value does not live long enough
|
LL | fn baz<'a>() {
| -- lifetime `'a` defined here
-...
+LL | // With a vec of ints.
+LL | let f1 = Fat { ptr: [1, 2, 3] };
+ | -- binding `f1` declared here
LL | let f2: &Fat<[isize; 3]> = &f1;
| ^^^ borrowed value does not live long enough
LL | let f3: &'a Fat<[isize]> = f2;
LL | fn baz<'a>() {
| -- lifetime `'a` defined here
...
+LL | let f1 = Fat { ptr: Foo };
+ | -- binding `f1` declared here
LL | let f2: &Fat<Foo> = &f1;
| ^^^ borrowed value does not live long enough
LL | let f3: &'a Fat<dyn Bar> = f2;
LL | fn baz<'a>() {
| -- lifetime `'a` defined here
...
+LL | let f1 = ([1, 2, 3],);
+ | -- binding `f1` declared here
LL | let f2: &([isize; 3],) = &f1;
| ^^^ borrowed value does not live long enough
LL | let f3: &'a ([isize],) = f2;
LL | fn baz<'a>() {
| -- lifetime `'a` defined here
...
+LL | let f1 = (Foo,);
+ | -- binding `f1` declared here
LL | let f2: &(Foo,) = &f1;
| ^^^ borrowed value does not live long enough
LL | let f3: &'a (dyn Bar,) = f2;
LL | macro_two!();
| ^^^^^^^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
two_macros::macro_two
error: aborting due to previous error
--- /dev/null
+#![feature(rustc_attrs)]
+
+#[rustc_variance]
+struct Foo<'a, T> { //~ ERROR [+, o]
+ t: &'a mut T,
+}
+
+fn main() {}
--- /dev/null
+error: [+, o]
+ --> $DIR/E0208.rs:4:1
+ |
+LL | struct Foo<'a, T> {
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--> $DIR/E0503.rs:4:16
|
LL | let _borrow = &mut value;
- | ---------- borrow of `value` occurs here
+ | ---------- `value` is borrowed here
LL | let _sum = value + 1;
| ^^^^^ use of borrowed `value`
LL | _borrow.use_mut();
error[E0505]: cannot move out of `fancy_num` because it is borrowed
--> $DIR/E0504.rs:9:13
|
+LL | let fancy_num = FancyNum { num: 5 };
+ | --------- binding `fancy_num` declared here
LL | let fancy_ref = &fancy_num;
| ---------- borrow of `fancy_num` occurs here
LL |
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/E0505.rs:9:13
|
+LL | let x = Value{};
+ | - binding `x` declared here
+LL | {
LL | let _ref_to_val: &Value = &x;
| -- borrow of `x` occurs here
LL | eat(x);
--> $DIR/E0506.rs:8:5
|
LL | let fancy_ref = &fancy_num;
- | ---------- borrow of `fancy_num` occurs here
+ | ---------- `fancy_num` is borrowed here
LL | fancy_num = FancyNum { num: 6 };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `fancy_num` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fancy_num` is assigned to here but it was already borrowed
LL |
LL | println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num);
| ------------- borrow later used here
error[E0597]: `y` does not live long enough
--> $DIR/E0597.rs:8:16
|
+LL | let y = 0;
+ | - binding `y` declared here
LL | x.x = Some(&y);
| ^^ borrowed value does not live long enough
LL |
fn main() {
- &0u8 as u8; //~ ERROR E0606
+ let x = &(&0u8 as u8); //~ ERROR E0606
+ x as u8; //~ casting `&u8` as `u8` is invalid [E0606]
}
error[E0606]: casting `&u8` as `u8` is invalid
- --> $DIR/E0606.rs:2:5
+ --> $DIR/E0606.rs:2:14
|
-LL | &0u8 as u8;
- | ----^^^^^^
- | |
- | cannot cast `&u8` as `u8`
- | help: dereference the expression: `*&0u8`
+LL | let x = &(&0u8 as u8);
+ | ^^^^^^^^^^^^
+ |
+help: remove the unneeded borrow
+ |
+LL - let x = &(&0u8 as u8);
+LL + let x = &(0u8 as u8);
+ |
+
+error[E0606]: casting `&u8` as `u8` is invalid
+ --> $DIR/E0606.rs:3:5
+ |
+LL | x as u8;
+ | ^^^^^^^
+ |
+help: dereference the expression
+ |
+LL | *x as u8;
+ | +
-error: aborting due to previous error
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0606`.
--- /dev/null
+// compile-flags: --crate-type lib
+
+#![feature(rustc_attrs)]
+#![feature(staged_api)]
+#![unstable(feature = "foo_module", reason = "...", issue = "123")]
+
+#[rustc_allowed_through_unstable_modules]
+// #[stable(feature = "foo", since = "1.0")]
+struct Foo;
+//~^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+//~^^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+// FIXME: we shouldn't have two errors here, only occurs when using `-Zdeduplicate-diagnostics=no`
--- /dev/null
+error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+ --> $DIR/E0789.rs:9:1
+ |
+LL | struct Foo;
+ | ^^^^^^^^^^^
+
+error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+ --> $DIR/E0789.rs:9:1
+ |
+LL | struct Foo;
+ | ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0789`.
--> $DIR/error-festival.rs:37:18
|
LL | let y: u32 = x as u32;
- | -^^^^^^^
- | |
- | cannot cast `&u8` as `u32`
- | help: dereference the expression: `*x`
+ | ^^^^^^^^
+ |
+help: dereference the expression
+ |
+LL | let y: u32 = *x as u32;
+ | +
error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]`
--> $DIR/error-festival.rs:41:5
--- /dev/null
+// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
+// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file.
+
+pub struct SomeStruct {} // This line should be show as part of the error.
--- /dev/null
+error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
+ --> $DIR/remap-path-prefix-reverse.rs:16:13
+ |
+LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
+ |
+ ::: remapped-aux/remapped_dep.rs:4:1
+ |
+LL | pub struct SomeStruct {} // This line should be show as part of the error.
+ | --------------------- `remapped_dep::SomeStruct` defined here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0423`.
--- /dev/null
+error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
+ --> $DIR/remap-path-prefix-reverse.rs:16:13
+ |
+LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
+ |
+ ::: remapped-aux/remapped_dep.rs:4:1
+ |
+LL | pub struct SomeStruct {} // This line should be show as part of the error.
+ | --------------------- `remapped_dep::SomeStruct` defined here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0423`.
--- /dev/null
+// aux-build:remapped_dep.rs
+// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
+
+// revisions: local-self remapped-self
+// [local-self] no-remap-src-base: The hack should work regardless of remapping.
+// [remapped-self] remap-src-base
+
+// Verify that the expected source code is shown.
+// error-pattern: pub struct SomeStruct {} // This line should be show
+
+extern crate remapped_dep;
+
+fn main() {
+ // The actual error is irrelevant. The important part it that is should show
+ // a snippet of the dependency's source.
+ let _ = remapped_dep::SomeStruct; // ~ERROR E0423
+}
--- /dev/null
+// compile-flags: --remap-path-prefix={{src-base}}=remapped
+// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file.
+
+// The remapped paths are not normalized by compiletest.
+// normalize-stderr-test: "\\(errors)" -> "/$1"
+
+// The remapped paths aren't recognized by compiletest, so we
+// cannot use line-specific patterns.
+// error-pattern: E0425
+
+fn main() {
+ // We cannot actually put an ERROR marker here because
+ // the file name in the error message is not what the
+ // test framework expects (since the filename gets remapped).
+ // We still test the expected error in the stderr file.
+ ferris
+}
--- /dev/null
+error[E0425]: cannot find value `ferris` in this scope
+ --> remapped/errors/remap-path-prefix.rs:16:5
+ |
+LL | ferris
+ | ^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
help: consider importing this module instead
|
LL | use std::env;
- | ~~~~~~~~~
+ | ~~~~~~~~
error: cannot determine resolution for the macro `env`
--> $DIR/issue-55897.rs:6:22
--- /dev/null
+// Check that even though Cell: DispatchFromDyn it remains an invalid self parameter type
+
+use std::cell::Cell;
+
+trait Trait{
+ fn cell(self: Cell<&Self>); //~ ERROR invalid `self` parameter type: Cell<&Self>
+}
+
+fn main() {}
--- /dev/null
+error[E0307]: invalid `self` parameter type: Cell<&Self>
+ --> $DIR/feature-gate-dispatch-from-dyn-cell.rs:6:19
+ |
+LL | fn cell(self: Cell<&Self>);
+ | ^^^^^^^^^^^
+ |
+ = note: type of `self` must be `Self` or a type that dereferences to it
+ = help: consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, `self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one of the previous types except `Self`)
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0307`.
--- /dev/null
+// Check that a self parameter type requires a DispatchFromDyn impl to be object safe
+
+#![feature(arbitrary_self_types, unsize, coerce_unsized)]
+
+use std::{
+ marker::Unsize,
+ ops::{CoerceUnsized, Deref},
+};
+
+struct Ptr<T: ?Sized>(Box<T>);
+
+impl<T: ?Sized> Deref for Ptr<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &*self.0
+ }
+}
+
+impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
+// Because this impl is missing the coercion below fails.
+// impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
+
+trait Trait {
+ fn ptr(self: Ptr<Self>);
+}
+impl Trait for i32 {
+ fn ptr(self: Ptr<Self>) {}
+}
+
+fn main() {
+ Ptr(Box::new(4)) as Ptr<dyn Trait>;
+ //~^ ERROR the trait `Trait` cannot be made into an object
+ //~^^ ERROR the trait `Trait` cannot be made into an object
+}
--- /dev/null
+error[E0038]: the trait `Trait` cannot be made into an object
+ --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:25
+ |
+LL | fn ptr(self: Ptr<Self>);
+ | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self`
+...
+LL | Ptr(Box::new(4)) as Ptr<dyn Trait>;
+ | ^^^^^^^^^^^^^^ `Trait` cannot be made into an object
+ |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18
+ |
+LL | trait Trait {
+ | ----- this trait cannot be made into an object...
+LL | fn ptr(self: Ptr<Self>);
+ | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on
+
+error[E0038]: the trait `Trait` cannot be made into an object
+ --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:32:5
+ |
+LL | fn ptr(self: Ptr<Self>);
+ | --------- help: consider changing method `ptr`'s `self` parameter to be `&self`: `&Self`
+...
+LL | Ptr(Box::new(4)) as Ptr<dyn Trait>;
+ | ^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object
+ |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:25:18
+ |
+LL | trait Trait {
+ | ----- this trait cannot be made into an object...
+LL | fn ptr(self: Ptr<Self>);
+ | ^^^^^^^^^ ...because method `ptr`'s `self` parameter cannot be dispatched on
+note: required for `Ptr<{integer}>` to implement `CoerceUnsized<Ptr<dyn Trait>>`
+ --> $DIR/feature-gate-dispatch-from-dyn-missing-impl.rs:20:40
+ |
+LL | impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
+ | --------- ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+ = note: required by cast to type `Ptr<dyn Trait>`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0038`.
--- /dev/null
+// check-pass
+
+#![deny(multiple_supertrait_upcastable)]
+//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
+//~| WARNING unknown lint: `multiple_supertrait_upcastable`
+//~| WARNING unknown lint: `multiple_supertrait_upcastable`
+#![warn(multiple_supertrait_upcastable)]
+//~^ WARNING unknown lint: `multiple_supertrait_upcastable`
+//~| WARNING unknown lint: `multiple_supertrait_upcastable`
+//~| WARNING unknown lint: `multiple_supertrait_upcastable`
+
+fn main() {}
--- /dev/null
+warning: unknown lint: `multiple_supertrait_upcastable`
+ --> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
+ |
+LL | #![deny(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `multiple_supertrait_upcastable` lint is unstable
+ = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
+ = note: `#[warn(unknown_lints)]` on by default
+
+warning: unknown lint: `multiple_supertrait_upcastable`
+ --> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
+ |
+LL | #![warn(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `multiple_supertrait_upcastable` lint is unstable
+ = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
+
+warning: unknown lint: `multiple_supertrait_upcastable`
+ --> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
+ |
+LL | #![deny(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `multiple_supertrait_upcastable` lint is unstable
+ = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
+
+warning: unknown lint: `multiple_supertrait_upcastable`
+ --> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
+ |
+LL | #![warn(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `multiple_supertrait_upcastable` lint is unstable
+ = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
+
+warning: unknown lint: `multiple_supertrait_upcastable`
+ --> $DIR/feature-gate-multiple_supertrait_upcastable.rs:3:1
+ |
+LL | #![deny(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `multiple_supertrait_upcastable` lint is unstable
+ = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
+
+warning: unknown lint: `multiple_supertrait_upcastable`
+ --> $DIR/feature-gate-multiple_supertrait_upcastable.rs:7:1
+ |
+LL | #![warn(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `multiple_supertrait_upcastable` lint is unstable
+ = help: add `#![feature(multiple_supertrait_upcastable)]` to the crate attributes to enable
+
+warning: 6 warnings emitted
+
TokenStream::from(TokenTree::Literal(lit))
}
+
#[proc_macro]
pub fn respan_to_invalid_format_literal(input: TokenStream) -> TokenStream {
let mut s = Literal::string("{");
TokenTree::from(Group::new(Delimiter::Parenthesis, TokenTree::from(s).into())),
])
}
+
+#[proc_macro]
+pub fn capture_a_with_prepended_space_preserve_span(input: TokenStream) -> TokenStream {
+ let mut s = Literal::string(" {a}");
+ s.set_span(input.into_iter().next().unwrap().span());
+ TokenStream::from_iter([
+ TokenTree::from(Ident::new("format", Span::call_site())),
+ TokenTree::from(Punct::new('!', Spacing::Alone)),
+ TokenTree::from(Group::new(Delimiter::Parenthesis, TokenTree::from(s).into())),
+ ])
+}
--- /dev/null
+// aux-build:format-string-proc-macro.rs
+// check-pass
+
+extern crate format_string_proc_macro;
+
+fn main() {
+ let a = 0;
+ format_string_proc_macro::capture_a_with_prepended_space_preserve_span!("{a}");
+}
// aux-build:format-string-proc-macro.rs
+// check-fail
+// known-bug: #106191
+// unset-rustc-env:RUST_BACKTRACE
+// had to be reverted
+// error-pattern:internal compiler error
+// failure-status:101
+// dont-check-compiler-stderr
extern crate format_string_proc_macro;
fn main() {
format_string_proc_macro::respan_to_invalid_format_literal!("¡");
- //~^ ERROR invalid format string: expected `'}'` but string was terminated
format_args!(r#concat!("¡ {"));
- //~^ ERROR invalid format string: expected `'}'` but string was terminated
}
-error: invalid format string: expected `'}'` but string was terminated
- --> $DIR/respanned-literal-issue-106191.rs:6:65
- |
-LL | format_string_proc_macro::respan_to_invalid_format_literal!("¡");
- | ^^^ expected `'}'` in format string
- |
- = note: if you intended to print `{`, you can escape it using `{{`
-
-error: invalid format string: expected `'}'` but string was terminated
- --> $DIR/respanned-literal-issue-106191.rs:8:18
- |
-LL | format_args!(r#concat!("¡ {"));
- | ^^^^^^^^^^^^^^^^^^^^^^^ expected `'}'` in format string
- |
- = note: if you intended to print `{`, you can escape it using `{{`
- = note: this error originates in the macro `concat` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 2 previous errors
-
+ query stack during panic:
+end of query stack
|
= note: expected fn item `fn() {f}`
found fn item `fn() {g}`
+ = note: different fn items have unique types, even if their signatures are the same
+ = help: consider casting both fn items to fn pointers using `as fn()`
error: aborting due to 2 previous errors
// Test that the types of distinct fn items are not compatible by
// default. See also `run-pass/fn-item-type-*.rs`.
-fn foo<T>(x: isize) -> isize { x * 2 }
-fn bar<T>(x: isize) -> isize { x * 4 }
+fn foo<T>(x: isize) -> isize {
+ x * 2
+}
+fn bar<T>(x: isize) -> isize {
+ x * 4
+}
-fn eq<T>(x: T, y: T) { }
+fn eq<T>(x: T, y: T) {}
-trait Foo { fn foo() { /* this is a default fn */ } }
-impl<T> Foo for T { /* `foo` is still default here */ }
+trait Foo {
+ fn foo() { /* this is a default fn */
+ }
+}
+impl<T> Foo for T {
+ /* `foo` is still default here */
+}
fn main() {
eq(foo::<u8>, bar::<u8>);
//~| expected fn item `fn(_) -> _ {foo::<u8>}`
//~| found fn item `fn(_) -> _ {bar::<u8>}`
//~| expected fn item, found a different fn item
- //~| different `fn` items always have unique types, even if their signatures are the same
- //~| change the expected type to be function pointer
- //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+ //~| different fn items have unique types, even if their signatures are the same
eq(foo::<u8>, foo::<i8>);
//~^ ERROR mismatched types
//~| expected `u8`, found `i8`
- //~| different `fn` items always have unique types, even if their signatures are the same
- //~| change the expected type to be function pointer
- //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+ //~| different fn items have unique types, even if their signatures are the same
eq(bar::<String>, bar::<Vec<u8>>);
//~^ ERROR mismatched types
//~| found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
//~| expected struct `String`, found struct `Vec`
- //~| different `fn` items always have unique types, even if their signatures are the same
- //~| change the expected type to be function pointer
- //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+ //~| different fn items have unique types, even if their signatures are the same
// Make sure we distinguish between trait methods correctly.
eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
//~^ ERROR mismatched types
//~| expected `u8`, found `u16`
- //~| different `fn` items always have unique types, even if their signatures are the same
- //~| change the expected type to be function pointer
- //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
+ //~| different fn items have unique types, even if their signatures are the same
eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
//~^ ERROR mismatched types
//~| found fn pointer `fn(_) -> _`
//~| expected fn item, found fn pointer
- //~| change the expected type to be function pointer
- //~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
eq(foo::<u8> as fn(isize) -> isize, bar::<u8>); // ok!
}
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:13:19
+ --> $DIR/fn-item-type.rs:22:19
|
LL | eq(foo::<u8>, bar::<u8>);
| -- ^^^^^^^^^ expected fn item, found a different fn item
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn item `fn(_) -> _ {bar::<u8>}`
- = note: different `fn` items always have unique types, even if their signatures are the same
- = help: change the expected type to be function pointer `fn(isize) -> isize`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+ = note: different fn items have unique types, even if their signatures are the same
note: function defined here
- --> $DIR/fn-item-type.rs:7:4
+ --> $DIR/fn-item-type.rs:11:4
|
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
| ^^ ----
+ = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:22:19
+ --> $DIR/fn-item-type.rs:29:19
|
LL | eq(foo::<u8>, foo::<i8>);
| -- ^^^^^^^^^ expected `u8`, found `i8`
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn item `fn(_) -> _ {foo::<i8>}`
- = note: different `fn` items always have unique types, even if their signatures are the same
- = help: change the expected type to be function pointer `fn(isize) -> isize`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+ = note: different fn items have unique types, even if their signatures are the same
note: function defined here
- --> $DIR/fn-item-type.rs:7:4
+ --> $DIR/fn-item-type.rs:11:4
|
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
| ^^ ----
+ = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:29:23
+ --> $DIR/fn-item-type.rs:34:23
|
LL | eq(bar::<String>, bar::<Vec<u8>>);
| -- ^^^^^^^^^^^^^^ expected struct `String`, found struct `Vec`
|
= note: expected fn item `fn(_) -> _ {bar::<String>}`
found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
- = note: different `fn` items always have unique types, even if their signatures are the same
- = help: change the expected type to be function pointer `fn(isize) -> isize`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar::<String> as fn(isize) -> isize`
+ = note: different fn items have unique types, even if their signatures are the same
note: function defined here
- --> $DIR/fn-item-type.rs:7:4
+ --> $DIR/fn-item-type.rs:11:4
|
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
| ^^ ----
+ = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:38:26
+ --> $DIR/fn-item-type.rs:41:26
|
LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
| -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
|
= note: expected fn item `fn() {<u8 as Foo>::foo}`
found fn item `fn() {<u16 as Foo>::foo}`
- = note: different `fn` items always have unique types, even if their signatures are the same
- = help: change the expected type to be function pointer `fn()`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()`
+ = note: different fn items have unique types, even if their signatures are the same
note: function defined here
- --> $DIR/fn-item-type.rs:7:4
+ --> $DIR/fn-item-type.rs:11:4
|
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
| ^^ ----
+ = help: consider casting both fn items to fn pointers using `as fn()`
error[E0308]: mismatched types
- --> $DIR/fn-item-type.rs:45:19
+ --> $DIR/fn-item-type.rs:46:19
|
LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn pointer `fn(_) -> _`
- = help: change the expected type to be function pointer `fn(isize) -> isize`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
+ = help: consider casting the fn item to a fn pointer: `foo::<u8> as fn(isize) -> isize`
note: function defined here
- --> $DIR/fn-item-type.rs:7:4
+ --> $DIR/fn-item-type.rs:11:4
|
-LL | fn eq<T>(x: T, y: T) { }
+LL | fn eq<T>(x: T, y: T) {}
| ^^ ----
error: aborting due to 5 previous errors
--- /dev/null
+fn foo(x: u32) -> u32 {
+ x * 2
+}
+
+fn bar(x: u32) -> u32 {
+ x * 3
+}
+
+// original example from Issue #102608
+fn foobar(n: u32) -> u32 {
+ let g = if n % 2 == 0 { &foo } else { &bar };
+ //~^ ERROR `if` and `else` have incompatible types
+ //~| different fn items have unique types, even if their signatures are the same
+ g(n)
+}
+
+fn main() {
+ assert_eq!(foobar(7), 21);
+ assert_eq!(foobar(8), 16);
+
+ // general mismatch of fn item types
+ let mut a = foo;
+ a = bar;
+ //~^ ERROR mismatched types
+ //~| expected fn item `fn(_) -> _ {foo}`
+ //~| found fn item `fn(_) -> _ {bar}`
+ //~| different fn items have unique types, even if their signatures are the same
+
+ // display note even when boxed
+ let mut b = Box::new(foo);
+ b = Box::new(bar);
+ //~^ ERROR mismatched types
+ //~| different fn items have unique types, even if their signatures are the same
+
+ // suggest removing reference
+ let c: fn(u32) -> u32 = &foo;
+ //~^ ERROR mismatched types
+ //~| expected fn pointer `fn(u32) -> u32`
+ //~| found reference `&fn(u32) -> u32 {foo}`
+
+ // suggest using reference
+ let d: &fn(u32) -> u32 = foo;
+ //~^ ERROR mismatched types
+ //~| expected reference `&fn(u32) -> u32`
+ //~| found fn item `fn(u32) -> u32 {foo}`
+
+ // suggest casting with reference
+ let e: &fn(u32) -> u32 = &foo;
+ //~^ ERROR mismatched types
+ //~| expected reference `&fn(u32) -> u32`
+ //~| found reference `&fn(u32) -> u32 {foo}`
+
+ // OK
+ let mut z: fn(u32) -> u32 = foo as fn(u32) -> u32;
+ z = bar;
+}
--- /dev/null
+error[E0308]: `if` and `else` have incompatible types
+ --> $DIR/fn-pointer-mismatch.rs:11:43
+ |
+LL | let g = if n % 2 == 0 { &foo } else { &bar };
+ | ---- ^^^^ expected fn item, found a different fn item
+ | |
+ | expected because of this
+ |
+ = note: expected reference `&fn(u32) -> u32 {foo}`
+ found reference `&fn(u32) -> u32 {bar}`
+ = note: different fn items have unique types, even if their signatures are the same
+ = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32`
+
+error[E0308]: mismatched types
+ --> $DIR/fn-pointer-mismatch.rs:23:9
+ |
+LL | let mut a = foo;
+ | --- expected due to this value
+LL | a = bar;
+ | ^^^ expected fn item, found a different fn item
+ |
+ = note: expected fn item `fn(_) -> _ {foo}`
+ found fn item `fn(_) -> _ {bar}`
+ = note: different fn items have unique types, even if their signatures are the same
+ = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32`
+
+error[E0308]: mismatched types
+ --> $DIR/fn-pointer-mismatch.rs:31:18
+ |
+LL | b = Box::new(bar);
+ | -------- ^^^ expected fn item, found a different fn item
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected fn item `fn(_) -> _ {foo}`
+ found fn item `fn(_) -> _ {bar}`
+ = note: different fn items have unique types, even if their signatures are the same
+note: associated function defined here
+ --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+ = help: consider casting both fn items to fn pointers using `as fn(u32) -> u32`
+
+error[E0308]: mismatched types
+ --> $DIR/fn-pointer-mismatch.rs:36:29
+ |
+LL | let c: fn(u32) -> u32 = &foo;
+ | -------------- ^^^^
+ | | |
+ | | expected fn pointer, found reference
+ | | help: consider removing the reference: `foo`
+ | expected due to this
+ |
+ = note: expected fn pointer `fn(u32) -> u32`
+ found reference `&fn(u32) -> u32 {foo}`
+
+error[E0308]: mismatched types
+ --> $DIR/fn-pointer-mismatch.rs:42:30
+ |
+LL | let d: &fn(u32) -> u32 = foo;
+ | --------------- ^^^
+ | | |
+ | | expected `&fn(u32) -> u32`, found fn item
+ | | help: consider using a reference: `&foo`
+ | expected due to this
+ |
+ = note: expected reference `&fn(u32) -> u32`
+ found fn item `fn(u32) -> u32 {foo}`
+
+error[E0308]: mismatched types
+ --> $DIR/fn-pointer-mismatch.rs:48:30
+ |
+LL | let e: &fn(u32) -> u32 = &foo;
+ | --------------- ^^^^
+ | | |
+ | | expected fn pointer, found fn item
+ | | help: consider casting to a fn pointer: `&(foo as fn(u32) -> u32)`
+ | expected due to this
+ |
+ = note: expected reference `&fn(u32) -> u32`
+ found reference `&fn(u32) -> u32 {foo}`
+ = note: fn items are distinct from fn pointers
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/implied-bounds-unnorm-associated-type-4.rs:21:10
|
+LL | let x = String::from("Hello World!");
+ | - binding `x` declared here
LL | let y = f(&x, ());
| -- borrow of `x` occurs here
LL | drop(x);
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/implied-bounds-unnorm-associated-type.rs:20:10
|
+LL | let x = String::from("Hello World!");
+ | - binding `x` declared here
LL | let y = f(&x, ());
| -- borrow of `x` occurs here
LL | drop(x);
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// run-pass
// Regression test for broken MIR error (#61442)
// Due to the two possible evaluation orders for
--- /dev/null
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/auto-trait-regions.rs:48:24
+ |
+LL | let a = A(&mut true, &mut true, No);
+ | ^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary value which is freed while still in use
+...
+LL | assert_foo(a);
+ | - borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/auto-trait-regions.rs:48:35
+ |
+LL | let a = A(&mut true, &mut true, No);
+ | ^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary value which is freed while still in use
+...
+LL | assert_foo(a);
+ | - borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:34:5
+ |
+LL | assert_foo(gen);
+ | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`...
+ = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:54:5
+ |
+LL | assert_foo(gen);
+ | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
--- /dev/null
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/auto-trait-regions.rs:48:24
+ |
+LL | let a = A(&mut true, &mut true, No);
+ | ^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary value which is freed while still in use
+...
+LL | assert_foo(a);
+ | - borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/auto-trait-regions.rs:48:35
+ |
+LL | let a = A(&mut true, &mut true, No);
+ | ^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary value which is freed while still in use
+...
+LL | assert_foo(a);
+ | - borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:34:5
+ |
+LL | assert_foo(gen);
+ | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`...
+ = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:54:5
+ |
+LL | assert_foo(gen);
+ | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
--- /dev/null
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/auto-trait-regions.rs:48:24
+ |
+LL | let a = A(&mut true, &mut true, No);
+ | ^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary value which is freed while still in use
+...
+LL | assert_foo(a);
+ | - borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/auto-trait-regions.rs:48:35
+ |
+LL | let a = A(&mut true, &mut true, No);
+ | ^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary value which is freed while still in use
+...
+LL | assert_foo(a);
+ | - borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:34:5
+ |
+LL | assert_foo(gen);
+ | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `&'0 OnlyFooIfStaticRef` must implement `Foo`, for any lifetime `'0`...
+ = note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef`
+
+error: implementation of `Foo` is not general enough
+ --> $DIR/auto-trait-regions.rs:54:5
+ |
+LL | assert_foo(gen);
+ | ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
+ |
+ = note: `Foo` would have to be implemented for the type `A<'0, '1>`, for any two lifetimes `'0` and `'1`...
+ = note: ...but `Foo` is actually implemented for the type `A<'_, '2>`, for some specific lifetime `'2`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(generators)]
#![feature(auto_traits)]
#![feature(negative_impls)]
error[E0716]: temporary value dropped while borrowed
- --> $DIR/auto-trait-regions.rs:45:24
+ --> $DIR/auto-trait-regions.rs:48:24
|
LL | let a = A(&mut true, &mut true, No);
| ^^^^ - temporary value is freed at the end of this statement
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
- --> $DIR/auto-trait-regions.rs:45:35
+ --> $DIR/auto-trait-regions.rs:48:35
|
LL | let a = A(&mut true, &mut true, No);
| ^^^^ - temporary value is freed at the end of this statement
= note: consider using a `let` binding to create a longer lived value
error: implementation of `Foo` is not general enough
- --> $DIR/auto-trait-regions.rs:31:5
+ --> $DIR/auto-trait-regions.rs:34:5
|
LL | assert_foo(gen);
| ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
= note: ...but `Foo` is actually implemented for the type `&'static OnlyFooIfStaticRef`
error: implementation of `Foo` is not general enough
- --> $DIR/auto-trait-regions.rs:51:5
+ --> $DIR/auto-trait-regions.rs:54:5
|
LL | assert_foo(gen);
| ^^^^^^^^^^^^^^^ implementation of `Foo` is not general enough
--- /dev/null
+error[E0597]: `a` does not live long enough
+ --> $DIR/borrowing.rs:13:33
+ |
+LL | let _b = {
+ | -- borrow later stored here
+LL | let a = 3;
+LL | Pin::new(&mut || yield &a).resume(())
+ | -- ^ borrowed value does not live long enough
+ | |
+ | value captured here by generator
+LL |
+LL | };
+ | - `a` dropped here while still borrowed
+
+error[E0597]: `a` does not live long enough
+ --> $DIR/borrowing.rs:20:20
+ |
+LL | let _b = {
+ | -- borrow later stored here
+LL | let a = 3;
+LL | || {
+ | -- value captured here by generator
+LL | yield &a
+ | ^ borrowed value does not live long enough
+...
+LL | };
+ | - `a` dropped here while still borrowed
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
--- /dev/null
+error[E0597]: `a` does not live long enough
+ --> $DIR/borrowing.rs:13:33
+ |
+LL | Pin::new(&mut || yield &a).resume(())
+ | ----------^
+ | | |
+ | | borrowed value does not live long enough
+ | value captured here by generator
+ | a temporary with access to the borrow is created here ...
+LL |
+LL | };
+ | -- ... and the borrow might be used here, when that temporary is dropped and runs the destructor for generator
+ | |
+ | `a` dropped here while still borrowed
+ |
+ = note: the temporary is part of an expression at the end of a block;
+ consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
+help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
+ |
+LL | let x = Pin::new(&mut || yield &a).resume(()); x
+ | +++++++ +++
+
+error[E0597]: `a` does not live long enough
+ --> $DIR/borrowing.rs:20:20
+ |
+LL | let _b = {
+ | -- borrow later stored here
+LL | let a = 3;
+LL | || {
+ | -- value captured here by generator
+LL | yield &a
+ | ^ borrowed value does not live long enough
+...
+LL | };
+ | - `a` dropped here while still borrowed
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
--- /dev/null
+error[E0597]: `a` does not live long enough
+ --> $DIR/borrowing.rs:13:33
+ |
+LL | let _b = {
+ | -- borrow later stored here
+LL | let a = 3;
+LL | Pin::new(&mut || yield &a).resume(())
+ | -- ^ borrowed value does not live long enough
+ | |
+ | value captured here by generator
+LL |
+LL | };
+ | - `a` dropped here while still borrowed
+
+error[E0597]: `a` does not live long enough
+ --> $DIR/borrowing.rs:20:20
+ |
+LL | let _b = {
+ | -- borrow later stored here
+LL | let a = 3;
+LL | || {
+ | -- value captured here by generator
+LL | yield &a
+ | ^ borrowed value does not live long enough
+...
+LL | };
+ | - `a` dropped here while still borrowed
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0597`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
#![feature(generators, generator_trait)]
use std::ops::Generator;
error[E0597]: `a` does not live long enough
- --> $DIR/borrowing.rs:9:33
+ --> $DIR/borrowing.rs:13:33
|
LL | let _b = {
| -- borrow later stored here
| - `a` dropped here while still borrowed
error[E0597]: `a` does not live long enough
- --> $DIR/borrowing.rs:16:20
+ --> $DIR/borrowing.rs:20:20
|
LL | let _b = {
| -- borrow later stored here
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:13
+ |
+LL | assert_send(g);
+ | ^^^^^^^^^^^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:13
+ |
+LL | assert_send(g);
+ | ^^^^^^^^^^^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:13
+ |
+LL | assert_send(g);
+ | ^^^^^^^^^^^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `copy::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `copy::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `copy::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `copy::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/drop-tracking-parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/drop-tracking-parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/drop-tracking-parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 8 previous errors
+
-// compile-flags: -Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
#![feature(generators, negative_impls, rustc_attrs)]
macro_rules! type_combinations {
let g = move || match drop($name::Client { ..$name::Client::default() }) {
//~^ `significant_drop::Client` which is not `Send`
//~| `insignificant_dtor::Client` which is not `Send`
- //~| `derived_drop::Client` which is not `Send`
+ //[no_drop_tracking,drop_tracking]~| `derived_drop::Client` which is not `Send`
_ => yield,
};
assert_send(g);
//~^ ERROR cannot be sent between threads
//~| ERROR cannot be sent between threads
//~| ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
}
// Simple owned value. This works because the Client is considered moved into `drop`,
_ => yield,
};
assert_send(g);
+ //[no_drop_tracking]~^ ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
}
)* }
}
+++ /dev/null
-error: generator cannot be sent between threads safely
- --> $DIR/drop-tracking-parent-expression.rs:24:25
- |
-LL | assert_send(g);
- | ^ generator is not `Send`
-...
-LL | / type_combinations!(
-LL | | // OK
-LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
-LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
-... |
-LL | | };
-LL | | );
- | |_____- in this macro invocation
- |
- = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:18:21: 18:28]`, the trait `Send` is not implemented for `derived_drop::Client`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/drop-tracking-parent-expression.rs:22:22
- |
-LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
- | ------------------------ has type `derived_drop::Client` which is not `Send`
-...
-LL | _ => yield,
- | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
-LL | };
- | - `$name::Client::default()` is later dropped here
-...
-LL | / type_combinations!(
-LL | | // OK
-LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
-LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
-... |
-LL | | };
-LL | | );
- | |_____- in this macro invocation
-note: required by a bound in `assert_send`
- --> $DIR/drop-tracking-parent-expression.rs:41:19
- |
-LL | fn assert_send<T: Send>(_thing: T) {}
- | ^^^^ required by this bound in `assert_send`
- = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: generator cannot be sent between threads safely
- --> $DIR/drop-tracking-parent-expression.rs:24:25
- |
-LL | assert_send(g);
- | ^ generator is not `Send`
-...
-LL | / type_combinations!(
-LL | | // OK
-LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
-LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
-... |
-LL | | };
-LL | | );
- | |_____- in this macro invocation
- |
- = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:18:21: 18:28]`, the trait `Send` is not implemented for `significant_drop::Client`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/drop-tracking-parent-expression.rs:22:22
- |
-LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
- | ------------------------ has type `significant_drop::Client` which is not `Send`
-...
-LL | _ => yield,
- | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
-LL | };
- | - `$name::Client::default()` is later dropped here
-...
-LL | / type_combinations!(
-LL | | // OK
-LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
-LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
-... |
-LL | | };
-LL | | );
- | |_____- in this macro invocation
-note: required by a bound in `assert_send`
- --> $DIR/drop-tracking-parent-expression.rs:41:19
- |
-LL | fn assert_send<T: Send>(_thing: T) {}
- | ^^^^ required by this bound in `assert_send`
- = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: generator cannot be sent between threads safely
- --> $DIR/drop-tracking-parent-expression.rs:24:25
- |
-LL | assert_send(g);
- | ^ generator is not `Send`
-...
-LL | / type_combinations!(
-LL | | // OK
-LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
-LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
-... |
-LL | | };
-LL | | );
- | |_____- in this macro invocation
- |
- = help: within `[generator@$DIR/drop-tracking-parent-expression.rs:18:21: 18:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/drop-tracking-parent-expression.rs:22:22
- |
-LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
- | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
-...
-LL | _ => yield,
- | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
-LL | };
- | - `$name::Client::default()` is later dropped here
-...
-LL | / type_combinations!(
-LL | | // OK
-LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
-LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
-... |
-LL | | };
-LL | | );
- | |_____- in this macro invocation
-note: required by a bound in `assert_send`
- --> $DIR/drop-tracking-parent-expression.rs:41:19
- |
-LL | fn assert_send<T: Send>(_thing: T) {}
- | ^^^^ required by this bound in `assert_send`
- = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 3 previous errors
-
// build-pass
// edition:2018
-// compile-flags: -Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(generators)]
error[E0597]: `*cell` does not live long enough
--> $DIR/dropck.rs:10:40
|
+LL | let (mut gen, cell);
+ | ---- binding `cell` declared here
+LL | cell = Box::new(RefCell::new(0));
LL | let ref_ = Box::leak(Box::new(Some(cell.borrow_mut())));
| ^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
--- /dev/null
+error[E0382]: borrow of moved value: `g`
+ --> $DIR/issue-105084.rs:44:14
+ |
+LL | let mut g = || {
+ | ----- move occurs because `g` has type `[generator@$DIR/issue-105084.rs:22:17: 22:19]`, which does not implement the `Copy` trait
+...
+LL | let mut h = copy(g);
+ | - value moved here
+...
+LL | Pin::new(&mut g).resume(());
+ | ^^^^^^ value borrowed here after move
+ |
+note: consider changing this parameter type in function `copy` to borrow instead if owning the value isn't necessary
+ --> $DIR/issue-105084.rs:17:21
+ |
+LL | fn copy<T: Copy>(x: T) -> T {
+ | ---- ^ this parameter takes ownership of the value
+ | |
+ | in this function
+help: consider cloning the value if the performance cost is acceptable
+ |
+LL | let mut h = copy(g.clone());
+ | ++++++++
+
+error[E0277]: the trait bound `Box<(i32, ())>: Copy` is not satisfied in `[generator@$DIR/issue-105084.rs:22:17: 22:19]`
+ --> $DIR/issue-105084.rs:38:17
+ |
+LL | let mut g = || {
+ | -- within this `[generator@$DIR/issue-105084.rs:22:17: 22:19]`
+...
+LL | let mut h = copy(g);
+ | ^^^^ within `[generator@$DIR/issue-105084.rs:22:17: 22:19]`, the trait `Copy` is not implemented for `Box<(i32, ())>`
+ |
+note: generator does not implement `Copy` as this value is used across a yield
+ --> $DIR/issue-105084.rs:28:25
+ |
+LL | let t = box (5, yield);
+ | --------^^^^^-
+ | | |
+ | | yield occurs here, with `box (5, yield)` maybe used later
+ | has type `Box<(i32, ())>` which does not implement `Copy`
+note: required by a bound in `copy`
+ --> $DIR/issue-105084.rs:17:12
+ |
+LL | fn copy<T: Copy>(x: T) -> T {
+ | ^^^^ required by this bound in `copy`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0382.
+For more information about an error, try `rustc --explain E0277`.
--- /dev/null
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// [no_drop_tracking] known-bug: #105084
+// [no_drop_tracking] check-pass
+// [drop_tracking] known-bug: #105084
+// [drop_tracking] check-pass
+
+#![feature(generators)]
+#![feature(generator_clone)]
+#![feature(generator_trait)]
+#![feature(box_syntax)]
+
+use std::ops::Generator;
+use std::pin::Pin;
+
+fn copy<T: Copy>(x: T) -> T {
+ x
+}
+
+fn main() {
+ let mut g = || {
+ // This is desuraged as 4 stages:
+ // - allocate a `*mut u8` with `exchange_malloc`;
+ // - create a Box that is ignored for trait computations;
+ // - compute fields (and yields);
+ // - assign to `t`.
+ let t = box (5, yield);
+ drop(t);
+ };
+
+ // Allocate the temporary box.
+ Pin::new(&mut g).resume(());
+
+ // The temporary box is in generator locals.
+ // As it is not taken into account for trait computation,
+ // the generator is `Copy`.
+ let mut h = copy(g);
+ //[drop_tracking_mir]~^ ERROR the trait bound `Box<(i32, ())>: Copy` is not satisfied in
+
+ // We now have 2 boxes with the same backing allocation:
+ // one inside `g` and one inside `h`.
+ // Proceed and drop `t` in `g`.
+ Pin::new(&mut g).resume(());
+ //[drop_tracking_mir]~^ ERROR borrow of moved value: `g`
+
+ // Proceed and drop `t` in `h` -> double free!
+ Pin::new(&mut h).resume(());
+}
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57017.rs:31:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: the trait `Sync` is not implemented for `copy::unsync::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57017.rs:29:28
+ |
+LL | let g = move || match drop(&$name::unsync::Client::default()) {
+ | --------------------------------- has type `©::unsync::Client` which is not `Send`
+LL | _status => yield,
+ | ^^^^^ yield occurs here, with `&$name::unsync::Client::default()` maybe used later
+LL | };
+ | - `&$name::unsync::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57017.rs:51:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57017.rs:43:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/issue-57017.rs:40:21: 40:28]`, the trait `Send` is not implemented for `copy::unsend::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57017.rs:41:28
+ |
+LL | let g = move || match drop($name::unsend::Client::default()) {
+ | -------------------------------- has type `copy::unsend::Client` which is not `Send`
+LL | _status => yield,
+ | ^^^^^ yield occurs here, with `$name::unsend::Client::default()` maybe used later
+LL | };
+ | - `$name::unsend::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57017.rs:51:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57017.rs:31:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: the trait `Sync` is not implemented for `derived_drop::unsync::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57017.rs:29:28
+ |
+LL | let g = move || match drop(&$name::unsync::Client::default()) {
+ | --------------------------------- has type `&derived_drop::unsync::Client` which is not `Send`
+LL | _status => yield,
+ | ^^^^^ yield occurs here, with `&$name::unsync::Client::default()` maybe used later
+LL | };
+ | - `&$name::unsync::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57017.rs:51:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57017.rs:43:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/issue-57017.rs:40:21: 40:28]`, the trait `Send` is not implemented for `derived_drop::unsend::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57017.rs:41:28
+ |
+LL | let g = move || match drop($name::unsend::Client::default()) {
+ | -------------------------------- has type `derived_drop::unsend::Client` which is not `Send`
+LL | _status => yield,
+ | ^^^^^ yield occurs here, with `$name::unsend::Client::default()` maybe used later
+LL | };
+ | - `$name::unsend::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57017.rs:51:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57017.rs:31:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: the trait `Sync` is not implemented for `significant_drop::unsync::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57017.rs:29:28
+ |
+LL | let g = move || match drop(&$name::unsync::Client::default()) {
+ | --------------------------------- has type `&significant_drop::unsync::Client` which is not `Send`
+LL | _status => yield,
+ | ^^^^^ yield occurs here, with `&$name::unsync::Client::default()` maybe used later
+LL | };
+ | - `&$name::unsync::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57017.rs:51:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57017.rs:43:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/issue-57017.rs:40:21: 40:28]`, the trait `Send` is not implemented for `significant_drop::unsend::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57017.rs:41:28
+ |
+LL | let g = move || match drop($name::unsend::Client::default()) {
+ | -------------------------------- has type `significant_drop::unsend::Client` which is not `Send`
+LL | _status => yield,
+ | ^^^^^ yield occurs here, with `$name::unsend::Client::default()` maybe used later
+LL | };
+ | - `$name::unsend::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+LL | | significant_drop => {
+... |
+LL | | }
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57017.rs:51:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 6 previous errors
+
-// build-pass
-// compile-flags: -Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// [drop_tracking] build-pass
+// [drop_tracking_mir] build-pass
+
#![feature(generators, negative_impls)]
macro_rules! type_combinations {
_status => yield,
};
assert_send(g);
+ //[no_drop_tracking]~^ ERROR generator cannot be sent between threads safely
+ //[no_drop_tracking]~| ERROR generator cannot be sent between threads safely
+ //[no_drop_tracking]~| ERROR generator cannot be sent between threads safely
}
// This tests that `Client` is properly considered to be dropped after moving it into the
_status => yield,
};
assert_send(g);
+ //[no_drop_tracking]~^ ERROR generator cannot be sent between threads safely
+ //[no_drop_tracking]~| ERROR generator cannot be sent between threads safely
+ //[no_drop_tracking]~| ERROR generator cannot be sent between threads safely
}
)* }
}
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-57478.rs:13:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let guard = Foo;
+LL | | drop(guard);
+LL | | yield;
+LL | | })
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/issue-57478.rs:13:17: 13:19]`, the trait `Send` is not implemented for `Foo`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-57478.rs:17:9
+ |
+LL | let guard = Foo;
+ | ----- has type `Foo` which is not `Send`
+LL | drop(guard);
+LL | yield;
+ | ^^^^^ yield occurs here, with `guard` maybe used later
+LL | })
+ | - `guard` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/issue-57478.rs:21:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
-// check-pass
-// compile-flags: -Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// [drop_tracking] check-pass
+// [drop_tracking_mir] check-pass
#![feature(negative_impls, generators)]
fn main() {
assert_send(|| {
+ //[no_drop_tracking]~^ ERROR generator cannot be sent between threads safely
let guard = Foo;
drop(guard);
yield;
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-68112.rs:43:18
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-68112.rs:39:9
+ |
+LL | let _non_send_gen = make_non_send_generator();
+ | ------------- has type `impl Generator<Return = Arc<RefCell<i32>>>` which is not `Send`
+LL |
+LL | yield;
+ | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+...
+LL | };
+ | - `_non_send_gen` is later dropped here
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:25:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/issue-68112.rs:67:18
+ |
+LL | require_send(send_gen);
+ | ------------ ^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this generator
+ --> $DIR/issue-68112.rs:52:5
+ |
+LL | || {
+ | ^^
+note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:49:30
+ |
+LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:57:34
+ |
+LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `impl Generator<Return = Arc<RefCell<i32>>>`, `()`
+note: required because it's used within this generator
+ --> $DIR/issue-68112.rs:63:20
+ |
+LL | let send_gen = || {
+ | ^^
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:25:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-68112.rs:43:5
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^^^^^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-68112.rs:39:9
+ |
+LL | let _non_send_gen = make_non_send_generator();
+ | ------------- has type `impl Generator<Return = Arc<RefCell<i32>>>` which is not `Send`
+LL |
+LL | yield;
+ | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:25:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/issue-68112.rs:67:5
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this generator
+ --> $DIR/issue-68112.rs:52:5
+ |
+LL | || {
+ | ^^
+note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:49:30
+ |
+LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:57:34
+ |
+LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `impl Generator<Return = Arc<RefCell<i32>>>`
+note: required because it's used within this generator
+ --> $DIR/issue-68112.rs:63:20
+ |
+LL | let send_gen = || {
+ | ^^
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:25:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/issue-68112.rs:43:18
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/issue-68112.rs:39:9
+ |
+LL | let _non_send_gen = make_non_send_generator();
+ | ------------- has type `impl Generator<Return = Arc<RefCell<i32>>>` which is not `Send`
+LL |
+LL | yield;
+ | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+...
+LL | };
+ | - `_non_send_gen` is later dropped here
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:25:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/issue-68112.rs:67:18
+ |
+LL | require_send(send_gen);
+ | ------------ ^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this generator
+ --> $DIR/issue-68112.rs:52:5
+ |
+LL | || {
+ | ^^
+note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:49:30
+ |
+LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
+ --> $DIR/issue-68112.rs:57:34
+ |
+LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `impl Generator<Return = Arc<RefCell<i32>>>`, `()`
+note: required because it's used within this generator
+ --> $DIR/issue-68112.rs:63:20
+ |
+LL | let send_gen = || {
+ | ^^
+note: required by a bound in `require_send`
+ --> $DIR/issue-68112.rs:25:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(generators, generator_trait)]
use std::{
};
pub struct Ready<T>(Option<T>);
-impl<T> Generator<()> for Ready<T> {
+impl<T: Unpin> Generator<()> for Ready<T> {
type Return = T;
type Yield = ();
fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> {
yield;
//~^ NOTE yield occurs here
//~| NOTE value is used across a yield
- }; //~ NOTE later dropped here
+ }; //[no_drop_tracking,drop_tracking]~ NOTE later dropped here
require_send(send_gen);
//~^ ERROR generator cannot be sent between threads
//~| NOTE not `Send`
+ //~| NOTE use `std::sync::RwLock` instead
}
pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
//~^ ERROR `RefCell<i32>` cannot be shared between threads safely
//~| NOTE `RefCell<i32>` cannot be shared between threads safely
//~| NOTE required for
- //~| NOTE required by a bound introduced by this call
+ //[no_drop_tracking,drop_tracking]~| NOTE required by a bound introduced by this call
//~| NOTE captures the following types
+ //~| NOTE use `std::sync::RwLock` instead
}
fn main() {}
+++ /dev/null
-error: generator cannot be sent between threads safely
- --> $DIR/issue-68112.rs:40:18
- |
-LL | require_send(send_gen);
- | ^^^^^^^^ generator is not `Send`
- |
- = help: the trait `Sync` is not implemented for `RefCell<i32>`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/issue-68112.rs:36:9
- |
-LL | let _non_send_gen = make_non_send_generator();
- | ------------- has type `impl Generator<Return = Arc<RefCell<i32>>>` which is not `Send`
-LL |
-LL | yield;
- | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
-...
-LL | };
- | - `_non_send_gen` is later dropped here
-note: required by a bound in `require_send`
- --> $DIR/issue-68112.rs:22:25
- |
-LL | fn require_send(_: impl Send) {}
- | ^^^^ required by this bound in `require_send`
-
-error[E0277]: `RefCell<i32>` cannot be shared between threads safely
- --> $DIR/issue-68112.rs:63:18
- |
-LL | require_send(send_gen);
- | ------------ ^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
- | |
- | required by a bound introduced by this call
- |
- = help: the trait `Sync` is not implemented for `RefCell<i32>`
- = note: required for `Arc<RefCell<i32>>` to implement `Send`
-note: required because it's used within this generator
- --> $DIR/issue-68112.rs:48:5
- |
-LL | || {
- | ^^
-note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
- --> $DIR/issue-68112.rs:45:30
- |
-LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
- --> $DIR/issue-68112.rs:53:34
- |
-LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `impl Generator<Return = Arc<RefCell<i32>>>`, `()`
-note: required because it's used within this generator
- --> $DIR/issue-68112.rs:59:20
- |
-LL | let send_gen = || {
- | ^^
-note: required by a bound in `require_send`
- --> $DIR/issue-68112.rs:22:25
- |
-LL | fn require_send(_: impl Send) {}
- | ^^^^ required by this bound in `require_send`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2021
// run-pass
-// compile-flags: -Zdrop-tracking
#![feature(never_type)]
--- /dev/null
+error: generator cannot be shared between threads safely
+ --> $DIR/not-send-sync.rs:17:17
+ |
+LL | assert_sync(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSync;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Sync`
+ |
+ = help: within `[generator@$DIR/not-send-sync.rs:17:17: 17:19]`, the trait `Sync` is not implemented for `NotSync`
+note: generator is not `Sync` as this value is used across a yield
+ --> $DIR/not-send-sync.rs:20:9
+ |
+LL | let a = NotSync;
+ | - has type `NotSync` which is not `Sync`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_sync`
+ --> $DIR/not-send-sync.rs:14:23
+ |
+LL | fn assert_sync<T: Sync>(_: T) {}
+ | ^^^^ required by this bound in `assert_sync`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/not-send-sync.rs:24:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSend;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/not-send-sync.rs:24:17: 24:19]`, the trait `Send` is not implemented for `NotSend`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/not-send-sync.rs:27:9
+ |
+LL | let a = NotSend;
+ | - has type `NotSend` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/not-send-sync.rs:15:23
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: generator cannot be shared between threads safely
+ --> $DIR/not-send-sync.rs:17:5
+ |
+LL | assert_sync(|| {
+ | ^^^^^^^^^^^ generator is not `Sync`
+ |
+ = help: within `[generator@$DIR/not-send-sync.rs:17:17: 17:19]`, the trait `Sync` is not implemented for `NotSync`
+note: generator is not `Sync` as this value is used across a yield
+ --> $DIR/not-send-sync.rs:20:9
+ |
+LL | let a = NotSync;
+ | - has type `NotSync` which is not `Sync`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+note: required by a bound in `assert_sync`
+ --> $DIR/not-send-sync.rs:14:23
+ |
+LL | fn assert_sync<T: Sync>(_: T) {}
+ | ^^^^ required by this bound in `assert_sync`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/not-send-sync.rs:24:5
+ |
+LL | assert_send(|| {
+ | ^^^^^^^^^^^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/not-send-sync.rs:24:17: 24:19]`, the trait `Send` is not implemented for `NotSend`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/not-send-sync.rs:27:9
+ |
+LL | let a = NotSend;
+ | - has type `NotSend` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+note: required by a bound in `assert_send`
+ --> $DIR/not-send-sync.rs:15:23
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: generator cannot be shared between threads safely
+ --> $DIR/not-send-sync.rs:17:17
+ |
+LL | assert_sync(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSync;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Sync`
+ |
+ = help: within `[generator@$DIR/not-send-sync.rs:17:17: 17:19]`, the trait `Sync` is not implemented for `NotSync`
+note: generator is not `Sync` as this value is used across a yield
+ --> $DIR/not-send-sync.rs:20:9
+ |
+LL | let a = NotSync;
+ | - has type `NotSync` which is not `Sync`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_sync`
+ --> $DIR/not-send-sync.rs:14:23
+ |
+LL | fn assert_sync<T: Sync>(_: T) {}
+ | ^^^^ required by this bound in `assert_sync`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/not-send-sync.rs:24:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSend;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/not-send-sync.rs:24:17: 24:19]`, the trait `Send` is not implemented for `NotSend`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/not-send-sync.rs:27:9
+ |
+LL | let a = NotSend;
+ | - has type `NotSend` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/not-send-sync.rs:15:23
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(generators)]
+#![feature(negative_impls)]
-use std::cell::Cell;
+struct NotSend;
+struct NotSync;
+
+impl !Send for NotSend {}
+impl !Sync for NotSync {}
fn main() {
fn assert_sync<T: Sync>(_: T) {}
assert_sync(|| {
//~^ ERROR: generator cannot be shared between threads safely
- let a = Cell::new(2);
+ let a = NotSync;
yield;
+ drop(a);
});
- let a = Cell::new(2);
assert_send(|| {
- //~^ ERROR: E0277
- drop(&a);
+ //~^ ERROR: generator cannot be sent between threads safely
+ let a = NotSend;
yield;
+ drop(a);
});
}
+++ /dev/null
-error[E0277]: `Cell<i32>` cannot be shared between threads safely
- --> $DIR/not-send-sync.rs:16:17
- |
-LL | assert_send(|| {
- | _____-----------_^
- | | |
- | | required by a bound introduced by this call
-LL | |
-LL | | drop(&a);
-LL | | yield;
-LL | | });
- | |_____^ `Cell<i32>` cannot be shared between threads safely
- |
- = help: the trait `Sync` is not implemented for `Cell<i32>`
- = note: required for `&Cell<i32>` to implement `Send`
-note: required because it's used within this generator
- --> $DIR/not-send-sync.rs:16:17
- |
-LL | assert_send(|| {
- | ^^
-note: required by a bound in `assert_send`
- --> $DIR/not-send-sync.rs:7:23
- |
-LL | fn assert_send<T: Send>(_: T) {}
- | ^^^^ required by this bound in `assert_send`
-
-error: generator cannot be shared between threads safely
- --> $DIR/not-send-sync.rs:9:17
- |
-LL | assert_sync(|| {
- | _________________^
-LL | |
-LL | | let a = Cell::new(2);
-LL | | yield;
-LL | | });
- | |_____^ generator is not `Sync`
- |
- = help: within `[generator@$DIR/not-send-sync.rs:9:17: 9:19]`, the trait `Sync` is not implemented for `Cell<i32>`
-note: generator is not `Sync` as this value is used across a yield
- --> $DIR/not-send-sync.rs:12:9
- |
-LL | let a = Cell::new(2);
- | - has type `Cell<i32>` which is not `Sync`
-LL | yield;
- | ^^^^^ yield occurs here, with `a` maybe used later
-LL | });
- | - `a` is later dropped here
-note: required by a bound in `assert_sync`
- --> $DIR/not-send-sync.rs:6:23
- |
-LL | fn assert_sync<T: Sync>(_: T) {}
- | ^^^^ required by this bound in `assert_sync`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:13
+ |
+LL | assert_send(g);
+ | ^^^^^^^^^^^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:13
+ |
+LL | assert_send(g);
+ | ^^^^^^^^^^^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:13
+ |
+LL | assert_send(g);
+ | ^^^^^^^^^^^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `copy::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `copy::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `copy::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `copy::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `derived_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `derived_drop::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `significant_drop::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `significant_drop::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:27:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:21:21: 21:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:25:22
+ |
+LL | let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+...
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: generator cannot be sent between threads safely
+ --> $DIR/parent-expression.rs:40:25
+ |
+LL | assert_send(g);
+ | ^ generator is not `Send`
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+ |
+ = help: within `[generator@$DIR/parent-expression.rs:37:21: 37:28]`, the trait `Send` is not implemented for `insignificant_dtor::Client`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/parent-expression.rs:38:22
+ |
+LL | let g = move || match drop($name::Client::default()) {
+ | ------------------------ has type `insignificant_dtor::Client` which is not `Send`
+LL | _ => yield,
+ | ^^^^^ yield occurs here, with `$name::Client::default()` maybe used later
+LL | };
+ | - `$name::Client::default()` is later dropped here
+...
+LL | / type_combinations!(
+LL | | // OK
+LL | | copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+LL | | // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+... |
+LL | | };
+LL | | );
+ | |_____- in this macro invocation
+note: required by a bound in `assert_send`
+ --> $DIR/parent-expression.rs:49:19
+ |
+LL | fn assert_send<T: Send>(_thing: T) {}
+ | ^^^^ required by this bound in `assert_send`
+ = note: this error originates in the macro `type_combinations` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 8 previous errors
+
--- /dev/null
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
+#![feature(generators, negative_impls, rustc_attrs)]
+
+macro_rules! type_combinations {
+ (
+ $( $name:ident => { $( $tt:tt )* } );* $(;)?
+ ) => { $(
+ mod $name {
+ $( $tt )*
+
+ impl !Sync for Client {}
+ impl !Send for Client {}
+ }
+
+ // Struct update syntax. This fails because the Client used in the update is considered
+ // dropped *after* the yield.
+ {
+ let g = move || match drop($name::Client { ..$name::Client::default() }) {
+ //~^ `significant_drop::Client` which is not `Send`
+ //~| `insignificant_dtor::Client` which is not `Send`
+ //~| `derived_drop::Client` which is not `Send`
+ _ => yield,
+ };
+ assert_send(g);
+ //~^ ERROR cannot be sent between threads
+ //~| ERROR cannot be sent between threads
+ //~| ERROR cannot be sent between threads
+ //[no_drop_tracking]~^^^^ ERROR cannot be sent between threads
+ }
+
+ // Simple owned value. This works because the Client is considered moved into `drop`,
+ // even though the temporary expression doesn't end until after the yield.
+ {
+ let g = move || match drop($name::Client::default()) {
+ _ => yield,
+ };
+ assert_send(g);
+ //[no_drop_tracking]~^ ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
+ //[no_drop_tracking]~| ERROR cannot be sent between threads
+ }
+ )* }
+}
+
+fn assert_send<T: Send>(_thing: T) {}
+
+fn main() {
+ type_combinations!(
+ // OK
+ copy => { #[derive(Copy, Clone, Default)] pub struct Client; };
+ // NOT OK: MIR borrowck thinks that this is used after the yield, even though
+ // this has no `Drop` impl and only the drops of the fields are observable.
+ // FIXME: this should compile.
+ derived_drop => { #[derive(Default)] pub struct Client { pub nickname: String } };
+ // NOT OK
+ significant_drop => {
+ #[derive(Default)]
+ pub struct Client;
+ impl Drop for Client {
+ fn drop(&mut self) {}
+ }
+ };
+ // NOT OK (we need to agree with MIR borrowck)
+ insignificant_dtor => {
+ #[derive(Default)]
+ #[rustc_insignificant_dtor]
+ pub struct Client;
+ impl Drop for Client {
+ fn drop(&mut self) {}
+ }
+ };
+ );
+}
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/partial-drop.rs:17:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let guard = Bar { foo: Foo, x: 42 };
+LL | | drop(guard.foo);
+LL | | yield;
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/partial-drop.rs:17:17: 17:19]`, the trait `Send` is not implemented for `Foo`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/partial-drop.rs:21:9
+ |
+LL | let guard = Bar { foo: Foo, x: 42 };
+ | ----- has type `Bar` which is not `Send`
+LL | drop(guard.foo);
+LL | yield;
+ | ^^^^^ yield occurs here, with `guard` maybe used later
+LL | });
+ | - `guard` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/partial-drop.rs:33:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/partial-drop.rs:24:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let guard = Bar { foo: Foo, x: 42 };
+LL | | let Bar { foo, x } = guard;
+LL | | drop(foo);
+LL | | yield;
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/partial-drop.rs:24:17: 24:19]`, the trait `Send` is not implemented for `Foo`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/partial-drop.rs:29:9
+ |
+LL | let guard = Bar { foo: Foo, x: 42 };
+ | ----- has type `Bar` which is not `Send`
+...
+LL | yield;
+ | ^^^^^ yield occurs here, with `guard` maybe used later
+LL | });
+ | - `guard` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/partial-drop.rs:33:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/partial-drop.rs:17:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let guard = Bar { foo: Foo, x: 42 };
+LL | | drop(guard.foo);
+LL | | yield;
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/partial-drop.rs:17:17: 17:19]`, the trait `Send` is not implemented for `Foo`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/partial-drop.rs:21:9
+ |
+LL | let guard = Bar { foo: Foo, x: 42 };
+ | ----- has type `Bar` which is not `Send`
+LL | drop(guard.foo);
+LL | yield;
+ | ^^^^^ yield occurs here, with `guard` maybe used later
+LL | });
+ | - `guard` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/partial-drop.rs:33:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/partial-drop.rs:24:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let guard = Bar { foo: Foo, x: 42 };
+LL | | let Bar { foo, x } = guard;
+LL | | drop(foo);
+LL | | yield;
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/partial-drop.rs:24:17: 24:19]`, the trait `Send` is not implemented for `Foo`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/partial-drop.rs:29:9
+ |
+LL | let Bar { foo, x } = guard;
+ | --- has type `Foo` which is not `Send`
+LL | drop(foo);
+LL | yield;
+ | ^^^^^ yield occurs here, with `foo` maybe used later
+LL | });
+ | - `foo` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/partial-drop.rs:33:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
-// compile-flags: -Zdrop-tracking
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+// [drop_tracking_mir] check-pass
#![feature(negative_impls, generators)]
fn main() {
assert_send(|| {
- //~^ ERROR generator cannot be sent between threads safely
- // FIXME: it would be nice to make this work.
+ //[no_drop_tracking,drop_tracking]~^ ERROR generator cannot be sent between threads safely
let guard = Bar { foo: Foo, x: 42 };
drop(guard.foo);
yield;
});
assert_send(|| {
- //~^ ERROR generator cannot be sent between threads safely
- // FIXME: it would be nice to make this work.
- let guard = Bar { foo: Foo, x: 42 };
- drop(guard);
- guard.foo = Foo;
- guard.x = 23;
- yield;
- });
-
- assert_send(|| {
- //~^ ERROR generator cannot be sent between threads safely
- // FIXME: it would be nice to make this work.
+ //[no_drop_tracking,drop_tracking]~^ ERROR generator cannot be sent between threads safely
let guard = Bar { foo: Foo, x: 42 };
let Bar { foo, x } = guard;
drop(foo);
+++ /dev/null
-error: generator cannot be sent between threads safely
- --> $DIR/partial-drop.rs:14:17
- |
-LL | assert_send(|| {
- | _________________^
-LL | |
-LL | | // FIXME: it would be nice to make this work.
-LL | | let guard = Bar { foo: Foo, x: 42 };
-LL | | drop(guard.foo);
-LL | | yield;
-LL | | });
- | |_____^ generator is not `Send`
- |
- = help: within `[generator@$DIR/partial-drop.rs:14:17: 14:19]`, the trait `Send` is not implemented for `Foo`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/partial-drop.rs:19:9
- |
-LL | let guard = Bar { foo: Foo, x: 42 };
- | ----- has type `Bar` which is not `Send`
-LL | drop(guard.foo);
-LL | yield;
- | ^^^^^ yield occurs here, with `guard` maybe used later
-LL | });
- | - `guard` is later dropped here
-note: required by a bound in `assert_send`
- --> $DIR/partial-drop.rs:42:19
- |
-LL | fn assert_send<T: Send>(_: T) {}
- | ^^^^ required by this bound in `assert_send`
-
-error: generator cannot be sent between threads safely
- --> $DIR/partial-drop.rs:22:17
- |
-LL | assert_send(|| {
- | _________________^
-LL | |
-LL | | // FIXME: it would be nice to make this work.
-LL | | let guard = Bar { foo: Foo, x: 42 };
-... |
-LL | | yield;
-LL | | });
- | |_____^ generator is not `Send`
- |
- = help: within `[generator@$DIR/partial-drop.rs:22:17: 22:19]`, the trait `Send` is not implemented for `Foo`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/partial-drop.rs:29:9
- |
-LL | let guard = Bar { foo: Foo, x: 42 };
- | ----- has type `Bar` which is not `Send`
-...
-LL | yield;
- | ^^^^^ yield occurs here, with `guard` maybe used later
-LL | });
- | - `guard` is later dropped here
-note: required by a bound in `assert_send`
- --> $DIR/partial-drop.rs:42:19
- |
-LL | fn assert_send<T: Send>(_: T) {}
- | ^^^^ required by this bound in `assert_send`
-
-error: generator cannot be sent between threads safely
- --> $DIR/partial-drop.rs:32:17
- |
-LL | assert_send(|| {
- | _________________^
-LL | |
-LL | | // FIXME: it would be nice to make this work.
-LL | | let guard = Bar { foo: Foo, x: 42 };
-... |
-LL | | yield;
-LL | | });
- | |_____^ generator is not `Send`
- |
- = help: within `[generator@$DIR/partial-drop.rs:32:17: 32:19]`, the trait `Send` is not implemented for `Foo`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/partial-drop.rs:38:9
- |
-LL | let guard = Bar { foo: Foo, x: 42 };
- | ----- has type `Bar` which is not `Send`
-...
-LL | yield;
- | ^^^^^ yield occurs here, with `guard` maybe used later
-LL | });
- | - `guard` is later dropped here
-note: required by a bound in `assert_send`
- --> $DIR/partial-drop.rs:42:19
- |
-LL | fn assert_send<T: Send>(_: T) {}
- | ^^^^ required by this bound in `assert_send`
-
-error: aborting due to 3 previous errors
-
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/generator-print-verbose-1.rs:40:18
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/generator-print-verbose-1.rs:38:9
+ |
+LL | let _non_send_gen = make_non_send_generator();
+ | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+LL | };
+ | - `_non_send_gen` is later dropped here
+note: required by a bound in `require_send`
+ --> $DIR/generator-print-verbose-1.rs:29:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/generator-print-verbose-1.rs:59:18
+ |
+LL | require_send(send_gen);
+ | ------------ ^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this generator
+ --> $DIR/generator-print-verbose-1.rs:45:5
+ |
+LL | || {
+ | ^^
+note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [Arc<RefCell<i32>>])`
+ --> $DIR/generator-print-verbose-1.rs:44:30
+ |
+LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required because it appears within the type `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
+ --> $DIR/generator-print-verbose-1.rs:50:34
+ |
+LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
+note: required because it's used within this generator
+ --> $DIR/generator-print-verbose-1.rs:55:20
+ |
+LL | let send_gen = || {
+ | ^^
+note: required by a bound in `require_send`
+ --> $DIR/generator-print-verbose-1.rs:29:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/generator-print-verbose-1.rs:40:5
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^^^^^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/generator-print-verbose-1.rs:38:9
+ |
+LL | let _non_send_gen = make_non_send_generator();
+ | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+note: required by a bound in `require_send`
+ --> $DIR/generator-print-verbose-1.rs:29:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/generator-print-verbose-1.rs:59:5
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this generator
+ --> $DIR/generator-print-verbose-1.rs:45:5
+ |
+LL | || {
+ | ^^
+note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [Arc<RefCell<i32>>])`
+ --> $DIR/generator-print-verbose-1.rs:44:30
+ |
+LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required because it appears within the type `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
+ --> $DIR/generator-print-verbose-1.rs:50:34
+ |
+LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
+note: required because it's used within this generator
+ --> $DIR/generator-print-verbose-1.rs:55:20
+ |
+LL | let send_gen = || {
+ | ^^
+note: required by a bound in `require_send`
+ --> $DIR/generator-print-verbose-1.rs:29:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/generator-print-verbose-1.rs:40:18
+ |
+LL | require_send(send_gen);
+ | ^^^^^^^^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/generator-print-verbose-1.rs:38:9
+ |
+LL | let _non_send_gen = make_non_send_generator();
+ | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+LL | };
+ | - `_non_send_gen` is later dropped here
+note: required by a bound in `require_send`
+ --> $DIR/generator-print-verbose-1.rs:29:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error[E0277]: `RefCell<i32>` cannot be shared between threads safely
+ --> $DIR/generator-print-verbose-1.rs:59:18
+ |
+LL | require_send(send_gen);
+ | ------------ ^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+ = note: required for `Arc<RefCell<i32>>` to implement `Send`
+note: required because it's used within this generator
+ --> $DIR/generator-print-verbose-1.rs:45:5
+ |
+LL | || {
+ | ^^
+note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [Arc<RefCell<i32>>])`
+ --> $DIR/generator-print-verbose-1.rs:44:30
+ |
+LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required because it appears within the type `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
+ --> $DIR/generator-print-verbose-1.rs:50:34
+ |
+LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: required because it captures the following types: `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
+note: required because it's used within this generator
+ --> $DIR/generator-print-verbose-1.rs:55:20
+ |
+LL | let send_gen = || {
+ | ^^
+note: required by a bound in `require_send`
+ --> $DIR/generator-print-verbose-1.rs:29:25
+ |
+LL | fn require_send(_: impl Send) {}
+ | ^^^^ required by this bound in `require_send`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// compile-flags: -Zverbose
// Same as: tests/ui/generator/issue-68112.stderr
};
pub struct Ready<T>(Option<T>);
-impl<T> Generator<()> for Ready<T> {
+impl<T: Unpin> Generator<()> for Ready<T> {
type Return = T;
type Yield = ();
fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> {
+++ /dev/null
-error: generator cannot be sent between threads safely
- --> $DIR/generator-print-verbose-1.rs:37:18
- |
-LL | require_send(send_gen);
- | ^^^^^^^^ generator is not `Send`
- |
- = help: the trait `Sync` is not implemented for `RefCell<i32>`
-note: generator is not `Send` as this value is used across a yield
- --> $DIR/generator-print-verbose-1.rs:35:9
- |
-LL | let _non_send_gen = make_non_send_generator();
- | ------------- has type `Opaque(DefId(0:34 ~ generator_print_verbose_1[749a]::make_non_send_generator::{opaque#0}), [])` which is not `Send`
-LL | yield;
- | ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
-LL | };
- | - `_non_send_gen` is later dropped here
-note: required by a bound in `require_send`
- --> $DIR/generator-print-verbose-1.rs:26:25
- |
-LL | fn require_send(_: impl Send) {}
- | ^^^^ required by this bound in `require_send`
-
-error[E0277]: `RefCell<i32>` cannot be shared between threads safely
- --> $DIR/generator-print-verbose-1.rs:56:18
- |
-LL | require_send(send_gen);
- | ------------ ^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
- | |
- | required by a bound introduced by this call
- |
- = help: the trait `Sync` is not implemented for `RefCell<i32>`
- = note: required for `Arc<RefCell<i32>>` to implement `Send`
-note: required because it's used within this generator
- --> $DIR/generator-print-verbose-1.rs:42:5
- |
-LL | || {
- | ^^
-note: required because it appears within the type `Opaque(DefId(0:35 ~ generator_print_verbose_1[749a]::make_gen2::{opaque#0}), [Arc<RefCell<i32>>])`
- --> $DIR/generator-print-verbose-1.rs:41:30
- |
-LL | pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: required because it appears within the type `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`
- --> $DIR/generator-print-verbose-1.rs:47:34
- |
-LL | fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `Opaque(DefId(0:36 ~ generator_print_verbose_1[749a]::make_non_send_generator2::{opaque#0}), [])`, `()`
-note: required because it's used within this generator
- --> $DIR/generator-print-verbose-1.rs:52:20
- |
-LL | let send_gen = || {
- | ^^
-note: required by a bound in `require_send`
- --> $DIR/generator-print-verbose-1.rs:26:25
- |
-LL | fn require_send(_: impl Send) {}
- | ^^^^ required by this bound in `require_send`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: generator cannot be shared between threads safely
+ --> $DIR/generator-print-verbose-2.rs:20:17
+ |
+LL | assert_sync(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSync;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Sync`
+ |
+ = help: within `[main::{closure#0} upvar_tys=() {NotSync, ()}]`, the trait `Sync` is not implemented for `NotSync`
+note: generator is not `Sync` as this value is used across a yield
+ --> $DIR/generator-print-verbose-2.rs:23:9
+ |
+LL | let a = NotSync;
+ | - has type `NotSync` which is not `Sync`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_sync`
+ --> $DIR/generator-print-verbose-2.rs:17:23
+ |
+LL | fn assert_sync<T: Sync>(_: T) {}
+ | ^^^^ required by this bound in `assert_sync`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/generator-print-verbose-2.rs:27:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSend;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[main::{closure#1} upvar_tys=() {NotSend, ()}]`, the trait `Send` is not implemented for `NotSend`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/generator-print-verbose-2.rs:30:9
+ |
+LL | let a = NotSend;
+ | - has type `NotSend` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/generator-print-verbose-2.rs:18:23
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: generator cannot be shared between threads safely
+ --> $DIR/generator-print-verbose-2.rs:20:5
+ |
+LL | assert_sync(|| {
+ | ^^^^^^^^^^^ generator is not `Sync`
+ |
+ = help: within `[main::{closure#0} upvar_tys=() [main::{closure#0}]]`, the trait `Sync` is not implemented for `NotSync`
+note: generator is not `Sync` as this value is used across a yield
+ --> $DIR/generator-print-verbose-2.rs:23:9
+ |
+LL | let a = NotSync;
+ | - has type `NotSync` which is not `Sync`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+note: required by a bound in `assert_sync`
+ --> $DIR/generator-print-verbose-2.rs:17:23
+ |
+LL | fn assert_sync<T: Sync>(_: T) {}
+ | ^^^^ required by this bound in `assert_sync`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/generator-print-verbose-2.rs:27:5
+ |
+LL | assert_send(|| {
+ | ^^^^^^^^^^^ generator is not `Send`
+ |
+ = help: within `[main::{closure#1} upvar_tys=() [main::{closure#1}]]`, the trait `Send` is not implemented for `NotSend`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/generator-print-verbose-2.rs:30:9
+ |
+LL | let a = NotSend;
+ | - has type `NotSend` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+note: required by a bound in `assert_send`
+ --> $DIR/generator-print-verbose-2.rs:18:23
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: generator cannot be shared between threads safely
+ --> $DIR/generator-print-verbose-2.rs:20:17
+ |
+LL | assert_sync(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSync;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Sync`
+ |
+ = help: within `[main::{closure#0} upvar_tys=() {NotSync, ()}]`, the trait `Sync` is not implemented for `NotSync`
+note: generator is not `Sync` as this value is used across a yield
+ --> $DIR/generator-print-verbose-2.rs:23:9
+ |
+LL | let a = NotSync;
+ | - has type `NotSync` which is not `Sync`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_sync`
+ --> $DIR/generator-print-verbose-2.rs:17:23
+ |
+LL | fn assert_sync<T: Sync>(_: T) {}
+ | ^^^^ required by this bound in `assert_sync`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/generator-print-verbose-2.rs:27:17
+ |
+LL | assert_send(|| {
+ | _________________^
+LL | |
+LL | | let a = NotSend;
+LL | | yield;
+LL | | drop(a);
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[main::{closure#1} upvar_tys=() {NotSend, ()}]`, the trait `Send` is not implemented for `NotSend`
+note: generator is not `Send` as this value is used across a yield
+ --> $DIR/generator-print-verbose-2.rs:30:9
+ |
+LL | let a = NotSend;
+ | - has type `NotSend` which is not `Send`
+LL | yield;
+ | ^^^^^ yield occurs here, with `a` maybe used later
+LL | drop(a);
+LL | });
+ | - `a` is later dropped here
+note: required by a bound in `assert_send`
+ --> $DIR/generator-print-verbose-2.rs:18:23
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// compile-flags: -Zverbose
// Same as test/ui/generator/not-send-sync.rs
#![feature(generators)]
+#![feature(negative_impls)]
-use std::cell::Cell;
+struct NotSend;
+struct NotSync;
+
+impl !Send for NotSend {}
+impl !Sync for NotSync {}
fn main() {
fn assert_sync<T: Sync>(_: T) {}
assert_sync(|| {
//~^ ERROR: generator cannot be shared between threads safely
- let a = Cell::new(2);
+ let a = NotSync;
yield;
+ drop(a);
});
- let a = Cell::new(2);
assert_send(|| {
- //~^ ERROR: E0277
- drop(&a);
+ //~^ ERROR: generator cannot be sent between threads safely
+ let a = NotSend;
yield;
+ drop(a);
});
}
+++ /dev/null
-error[E0277]: `Cell<i32>` cannot be shared between threads safely
- --> $DIR/generator-print-verbose-2.rs:19:17
- |
-LL | assert_send(|| {
- | _____-----------_^
- | | |
- | | required by a bound introduced by this call
-LL | |
-LL | | drop(&a);
-LL | | yield;
-LL | | });
- | |_____^ `Cell<i32>` cannot be shared between threads safely
- |
- = help: the trait `Sync` is not implemented for `Cell<i32>`
- = note: required for `&'_#4r Cell<i32>` to implement `Send`
-note: required because it's used within this generator
- --> $DIR/generator-print-verbose-2.rs:19:17
- |
-LL | assert_send(|| {
- | ^^
-note: required by a bound in `assert_send`
- --> $DIR/generator-print-verbose-2.rs:10:23
- |
-LL | fn assert_send<T: Send>(_: T) {}
- | ^^^^ required by this bound in `assert_send`
-
-error: generator cannot be shared between threads safely
- --> $DIR/generator-print-verbose-2.rs:12:17
- |
-LL | assert_sync(|| {
- | _________________^
-LL | |
-LL | | let a = Cell::new(2);
-LL | | yield;
-LL | | });
- | |_____^ generator is not `Sync`
- |
- = help: within `[main::{closure#0} upvar_tys=() {Cell<i32>, ()}]`, the trait `Sync` is not implemented for `Cell<i32>`
-note: generator is not `Sync` as this value is used across a yield
- --> $DIR/generator-print-verbose-2.rs:15:9
- |
-LL | let a = Cell::new(2);
- | - has type `Cell<i32>` which is not `Sync`
-LL | yield;
- | ^^^^^ yield occurs here, with `a` maybe used later
-LL | });
- | - `a` is later dropped here
-note: required by a bound in `assert_sync`
- --> $DIR/generator-print-verbose-2.rs:9:23
- |
-LL | fn assert_sync<T: Sync>(_: T) {}
- | ^^^^ required by this bound in `assert_sync`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error[E0499]: cannot borrow `thing` as mutable more than once at a time
+ --> $DIR/retain-resume-ref.rs:27:25
+ |
+LL | gen.as_mut().resume(&mut thing);
+ | ---------- first mutable borrow occurs here
+LL | gen.as_mut().resume(&mut thing);
+ | ------ ^^^^^^^^^^ second mutable borrow occurs here
+ | |
+ | first borrow later used by call
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
--- /dev/null
+error[E0499]: cannot borrow `thing` as mutable more than once at a time
+ --> $DIR/retain-resume-ref.rs:27:25
+ |
+LL | gen.as_mut().resume(&mut thing);
+ | ---------- first mutable borrow occurs here
+LL | gen.as_mut().resume(&mut thing);
+ | ^^^^^^^^^^ second mutable borrow occurs here
+LL |
+LL | }
+ | - first borrow might be used here, when `gen` is dropped and runs the destructor for generator
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
--- /dev/null
+error[E0499]: cannot borrow `thing` as mutable more than once at a time
+ --> $DIR/retain-resume-ref.rs:27:25
+ |
+LL | gen.as_mut().resume(&mut thing);
+ | ---------- first mutable borrow occurs here
+LL | gen.as_mut().resume(&mut thing);
+ | ------ ^^^^^^^^^^ second mutable borrow occurs here
+ | |
+ | first borrow later used by call
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0499`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
//! This test ensures that a mutable reference cannot be passed as a resume argument twice.
#![feature(generators, generator_trait)]
error[E0499]: cannot borrow `thing` as mutable more than once at a time
- --> $DIR/retain-resume-ref.rs:23:25
+ --> $DIR/retain-resume-ref.rs:27:25
|
LL | gen.as_mut().resume(&mut thing);
| ---------- first mutable borrow occurs here
// build-pass
-// revisions: mir thir
+// revisions: mir thir drop_tracking drop_tracking_mir
// [thir]compile-flags: -Zthir-unsafeck
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
#![feature(generators)]
// check-fail
-// known-bug
+// known-bug: unknown
// This gives us problems because `for<'a> I::Item<'a>: Debug` should mean "for
// all 'a where I::Item<'a> is WF", but really means "for all 'a possible"
fn main() {
let slice = &mut ();
- //~^ temporary value dropped while borrowed
let windows = WindowsMut { slice };
print_items::<WindowsMut<'_>>(windows);
}
|
LL | let slice = &mut ();
| ^^ creates a temporary value which is freed while still in use
-...
+LL | let windows = WindowsMut { slice };
LL | print_items::<WindowsMut<'_>>(windows);
| -------------------------------------- argument requires that borrow lasts for `'static`
LL | }
// check-fail
-// known-bug
+// known-bug: unknown
// This gives us problems because `for<'a> I::Item<'a>: Debug` should mean "for
// all 'a where I::Item<'a> is WF", but really means "for all 'a possible"
{
let mut iter2 = Eat(iter, f);
let _next = iter2.next();
- //~^ borrowed data escapes
true
}
impl<I: LendingIterator> LendingIterator for &mut I {
// check-fail
-// known-bug
+// known-bug: unknown
// edition: 2021
// We really should accept this, but we need implied bounds between the regions
fn call<I: FutureIterator>() -> impl Send {
async { // a generator checked for autotrait impl `Send`
- //~^ lifetime bound not satisfied
let x = None::<I::Future<'_, '_>>; // a type referencing GAT
async {}.await; // a yield point
}
fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
async { // a generator checked for autotrait impl `Send`
- //~^ lifetime bound not satisfied
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
- //~^ lifetime may not live long enough
async {}.await; // a yield point
}
}
fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
async { // a generator checked for autotrait impl `Send`
- //~^ lifetime bound not satisfied
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
async {}.await; // a yield point
}
--> $DIR/issue-100013.rs:15:5
|
LL | / async { // a generator checked for autotrait impl `Send`
-LL | |
LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
LL | | async {}.await; // a yield point
LL | | }
| |_____^
|
note: the lifetime defined here...
- --> $DIR/issue-100013.rs:17:38
+ --> $DIR/issue-100013.rs:16:38
|
LL | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
| ^^
note: ...must outlive the lifetime defined here
- --> $DIR/issue-100013.rs:17:34
+ --> $DIR/issue-100013.rs:16:34
|
LL | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
| ^^
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:23:5
+ --> $DIR/issue-100013.rs:22:5
|
LL | / async { // a generator checked for autotrait impl `Send`
-LL | |
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
-LL | |
LL | | async {}.await; // a yield point
LL | | }
| |_____^
|
note: the lifetime defined here...
- --> $DIR/issue-100013.rs:22:14
+ --> $DIR/issue-100013.rs:21:14
|
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
| ^^
note: ...must outlive the lifetime defined here
- --> $DIR/issue-100013.rs:22:10
+ --> $DIR/issue-100013.rs:21:10
|
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
| ^^
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
error: lifetime may not live long enough
- --> $DIR/issue-100013.rs:25:17
+ --> $DIR/issue-100013.rs:23:17
|
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
-...
+LL | async { // a generator checked for autotrait impl `Send`
LL | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
| ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:32:5
+ --> $DIR/issue-100013.rs:29:5
|
LL | / async { // a generator checked for autotrait impl `Send`
-LL | |
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
LL | | async {}.await; // a yield point
LL | | }
| |_____^
|
note: the lifetime defined here...
- --> $DIR/issue-100013.rs:31:18
+ --> $DIR/issue-100013.rs:28:18
|
LL | fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
| ^^
note: ...must outlive the lifetime defined here
- --> $DIR/issue-100013.rs:31:10
+ --> $DIR/issue-100013.rs:28:10
|
LL | fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
| ^^
// check-fail
-// known-bug
+// known-bug: unknown
// We almost certainly want this to pass, but
// it's particularly difficult currently, because we need a way of specifying
arg = self;
ret = <Self::Base as Functor>::fmap(arg);
- //~^ type annotations needed
}
}
LL | | let _x = x;
LL | | };
| |_____^
+ |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+ --> $DIR/collectivity-regression.rs:11:16
+ |
+LL | for<'a> T: Get<Value<'a> = ()>,
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider restricting the type parameter to the `'static` lifetime
+ |
+LL | for<'a> T: Get<Value<'a> = ()> + 'static,
+ | +++++++++
error: aborting due to previous error
LL | fn bug<'a, T: ?Sized + Fun<F<'a> = [u8]>>(_ : Box<T>) -> &'static T::F<'a> {
| -- lifetime `'a` defined here
LL | let a = [0; 1];
+ | - binding `a` declared here
LL | let _x = T::identity(&a);
| ------------^^-
| | |
| ^^^^^^ the trait `Clone` is not implemented for `T`
|
= note: required for `Box<T>` to implement `Clone`
+ = note: required for `<Self as UnsafeCopy>::Copy<T>` to implement `Copy`
note: required by a bound in `UnsafeCopy::Copy`
--> $DIR/issue-74824.rs:6:19
|
--- /dev/null
+// run-rustfix
+
+trait GatTrait {
+ type Gat<'a> where Self: 'a;
+
+ fn test(&self) -> Self::Gat<'_>;
+}
+
+trait SuperTrait<T>
+where
+ Self: 'static,
+ for<'a> Self: GatTrait<Gat<'a> = &'a T>,
+{
+ fn copy(&self) -> Self::Gat<'_> where T: Copy {
+ self.test()
+ //~^ mismatched types
+ }
+}
+
+fn main() {}
+// run-rustfix
+
trait GatTrait {
type Gat<'a> where Self: 'a;
error[E0308]: mismatched types
- --> $DIR/issue-88360.rs:13:9
+ --> $DIR/issue-88360.rs:15:9
|
LL | trait SuperTrait<T>
| - this type parameter
LL | fn copy(&self) -> Self::Gat<'_> where T: Copy {
| ------------- expected `&T` because of return type
LL | *self.test()
- | ^^^^^^^^^^^^
- | |
- | expected `&T`, found type parameter `T`
- | help: consider borrowing here: `&*self.test()`
+ | ^^^^^^^^^^^^ expected `&T`, found type parameter `T`
|
= note: expected reference `&T`
found type parameter `T`
+help: consider removing deref here
+ |
+LL - *self.test()
+LL + self.test()
+ |
error: aborting due to previous error
|
= note: expected type parameter `B`
found associated type `<B as Add>::Output`
+help: the type constructed contains `<B as Add>::Output` due to the type of the argument passed
+ --> $DIR/missing-bounds.rs:11:9
+ |
+LL | A(self.0 + rhs.0)
+ | ^^--------------^
+ | |
+ | this argument influences the type of `A`
note: tuple struct defined here
--> $DIR/missing-bounds.rs:5:8
|
--- /dev/null
+trait Trait {}
+
+fn foo(_: impl &Trait) {}
+//~^ ERROR expected a trait, found type
+
+fn bar<T: &Trait>(_: T) {}
+//~^ ERROR expected a trait, found type
+
+fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
+//~^ ERROR expected a trait, found type
+
+fn foo_bad(_: impl &BadTrait) {}
+//~^ ERROR expected a trait, found type
+//~^^ ERROR cannot find trait `BadTrait` in this scope
+
+fn bar_bad<T: &BadTrait>(_: T) {}
+//~^ ERROR expected a trait, found type
+//~^^ ERROR cannot find trait `BadTrait` in this scope
+
+fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+//~^ ERROR expected a trait, found type
+//~^^ ERROR cannot find trait `BadTrait` in this scope
+
+fn main() {}
--- /dev/null
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:3:16
+ |
+LL | fn foo(_: impl &Trait) {}
+ | ^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn foo(_: impl &Trait) {}
+LL + fn foo(_: impl Trait) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:6:11
+ |
+LL | fn bar<T: &Trait>(_: T) {}
+ | ^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn bar<T: &Trait>(_: T) {}
+LL + fn bar<T: Trait>(_: T) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:9:35
+ |
+LL | fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
+ | ^^^^^^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
+LL + fn partially_correct_impl(_: impl Trait + Copy) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:12:20
+ |
+LL | fn foo_bad(_: impl &BadTrait) {}
+ | ^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn foo_bad(_: impl &BadTrait) {}
+LL + fn foo_bad(_: impl BadTrait) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:16:15
+ |
+LL | fn bar_bad<T: &BadTrait>(_: T) {}
+ | ^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn bar_bad<T: &BadTrait>(_: T) {}
+LL + fn bar_bad<T: BadTrait>(_: T) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:20:39
+ |
+LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+LL + fn partially_correct_impl_bad(_: impl BadTrait + Copy) {}
+ |
+
+error[E0405]: cannot find trait `BadTrait` in this scope
+ --> $DIR/issue-106694.rs:12:21
+ |
+LL | fn foo_bad(_: impl &BadTrait) {}
+ | ^^^^^^^^ not found in this scope
+
+error[E0405]: cannot find trait `BadTrait` in this scope
+ --> $DIR/issue-106694.rs:16:16
+ |
+LL | fn bar_bad<T: &BadTrait>(_: T) {}
+ | ^^^^^^^^ not found in this scope
+
+error[E0405]: cannot find trait `BadTrait` in this scope
+ --> $DIR/issue-106694.rs:20:48
+ |
+LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+ | ^^^^^^^^ not found in this scope
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0405`.
--- /dev/null
+fn main() {
+ let x = 42;
+ match x {
+ 0..=73 => {},
+ 74..=> {}, //~ ERROR unexpected `=>` after open range
+ //~^ ERROR expected one of `=>`, `if`, or `|`, found `>`
+ }
+}
--- /dev/null
+error: unexpected `=>` after open range
+ --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:11
+ |
+LL | 74..=> {},
+ | ^^^
+ |
+help: add a space between the pattern and `=>`
+ |
+LL | 74.. => {},
+ | +
+
+error: expected one of `=>`, `if`, or `|`, found `>`
+ --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:14
+ |
+LL | 74..=> {},
+ | ^ expected one of `=>`, `if`, or `|`
+
+error: aborting due to 2 previous errors
+
--> $DIR/hrtb-identity-fn-borrows.rs:14:5
|
LL | let y = f.call(&x);
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
LL | x = 5;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
...
LL | drop(y);
| - borrow later used here
--- /dev/null
+// check-pass
+
+fn lifetime<'a>()
+where
+ &'a (): 'a,
+{
+ /* do nothing */
+}
+
+fn doesnt_work()
+where
+ for<'a> &'a (): 'a,
+{
+ /* do nothing */
+}
+
+fn main() {
+ lifetime();
+ doesnt_work();
+}
LL | n!(f);
| ^ not found in this scope
|
- = note: consider importing this function:
+ = help: consider importing this function:
foo::f
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | f
| ^ not found in this scope
|
- = note: consider importing this function:
+ = help: consider importing this function:
foo::f
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | print!();
| ^^^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
std::print
error: aborting due to previous error
LL | let x = {
| - borrow later stored here
LL | let bar = 22;
+ | --- binding `bar` declared here
LL | Foo::new(&bar).into()
| ^^^^ borrowed value does not live long enough
LL |
LL | let x = {
| - borrow later stored here
LL | let y = ();
+ | - binding `y` declared here
LL | foo(&y)
| ^^ borrowed value does not live long enough
LL |
LL | let x = {
| - borrow later stored here
LL | let y = ();
+ | - binding `y` declared here
LL | foo(&y)
| ^^ borrowed value does not live long enough
LL |
--- /dev/null
+error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+ --> $DIR/issue-55872-2.rs:17:9
+ |
+LL | async {}
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+ --> $DIR/issue-55872-2.rs:17:9
+ |
+LL | async {}
+ | ^^^^^^^^
+
+error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+ --> $DIR/issue-55872-2.rs:17:9
+ |
+LL | async {}
+ | ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+ --> $DIR/issue-55872-2.rs:17:9
+ |
+LL | async {}
+ | ^^^^^^^^
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
#![feature(type_alias_impl_trait)]
fn foo<T>() -> Self::E {
async {}
//~^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
+ //[drop_tracking_mir]~^^ ERROR type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
}
}
error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
- --> $DIR/issue-55872-2.rs:14:9
+ --> $DIR/issue-55872-2.rs:17:9
|
LL | async {}
| ^^^^^^^^
| ---------- returning here with type `Foo<impl Quux>`
...
LL | fn bar() -> impl Quux {
- | --------- returning this opaque type `Foo<impl Quux>`
+ | --------- returning this type `Foo<impl Quux>`
error[E0720]: cannot resolve opaque type
--> $DIR/infinite-impl-trait-issue-38064.rs:14:13
|
LL | fn foo() -> impl Quux {
- | --------- returning this opaque type `Bar<impl Quux>`
+ | --------- returning this type `Bar<impl Quux>`
...
LL | fn bar() -> impl Quux {
| ^^^^^^^^^ recursive opaque type
--- /dev/null
+// check-pass
+
+use std::io::Write;
+
+struct A(Vec<u8>);
+
+struct B<'a> {
+ one: &'a mut A,
+ two: &'a mut Vec<u8>,
+ three: Vec<u8>,
+}
+
+impl<'a> B<'a> {
+ fn one(&mut self) -> &mut impl Write {
+ &mut self.one.0
+ }
+ fn two(&mut self) -> &mut impl Write {
+ &mut *self.two
+ }
+ fn three(&mut self) -> &mut impl Write {
+ &mut self.three
+ }
+}
+
+struct C<'a>(B<'a>);
+
+impl<'a> C<'a> {
+ fn one(&mut self) -> &mut impl Write {
+ self.0.one()
+ }
+ fn two(&mut self) -> &mut impl Write {
+ self.0.two()
+ }
+ fn three(&mut self) -> &mut impl Write {
+ self.0.three()
+ }
+}
+
+fn main() {}
--- /dev/null
+#![feature(generators, generator_trait)]
+
+use std::ops::{Generator, GeneratorState};
+
+fn foo() -> impl Generator<Yield = (), Return = ()> {
+ //~^ ERROR cannot resolve opaque type
+ //~| NOTE recursive opaque type
+ //~| NOTE in this expansion of desugaring of
+ || {
+ //~^ NOTE returning here
+ let mut gen = Box::pin(foo());
+ //~^ NOTE generator captures itself here
+ let mut r = gen.as_mut().resume(());
+ while let GeneratorState::Yielded(v) = r {
+ yield v;
+ r = gen.as_mut().resume(());
+ }
+ }
+}
+
+fn main() {
+ foo();
+}
--- /dev/null
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-generator.rs:5:13
+ |
+LL | fn foo() -> impl Generator<Yield = (), Return = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ recursive opaque type
+...
+LL | / || {
+LL | |
+LL | | let mut gen = Box::pin(foo());
+ | | ------- generator captures itself here
+LL | |
+... |
+LL | | }
+LL | | }
+ | |_____- returning here with type `[generator@$DIR/recursive-generator.rs:9:5: 9:7]`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:11:22
+ |
+LL | fn option(i: i32) -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | if i < 0 { None } else { Some((option(i - 1), i)) }
+ | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>`
+ | |
+ | returning here with type `Option<(impl Sized, i32)>`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:16:15
+ |
+LL | fn tuple() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | (tuple(),)
+ | ---------- returning here with type `(impl Sized,)`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:21:15
+ |
+LL | fn array() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | [array()]
+ | --------- returning here with type `[impl Sized; 1]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:26:13
+ |
+LL | fn ptr() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | &ptr() as *const _
+ | ------------------ returning here with type `*const impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:31:16
+ |
+LL | fn fn_ptr() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | fn_ptr as fn() -> _
+ | ------------------- returning here with type `fn() -> impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:36:25
+ |
+LL | fn closure_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | x;
+ | | - closure captures itself here
+LL | | }
+ | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:39:5: 39:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:44:29
+ |
+LL | fn closure_ref_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | &x;
+ | | - closure captures itself here
+LL | | }
+ | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:47:5: 47:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:52:21
+ |
+LL | fn closure_sig() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | || closure_sig()
+ | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:57:23
+ |
+LL | fn generator_sig() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | || generator_sig()
+ | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:59:5: 59:7]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:62:27
+ |
+LL | fn generator_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | yield;
+LL | | x;
+ | | - generator captures itself here
+LL | | }
+ | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:65:5: 65:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:71:35
+ |
+LL | fn substs_change<T: 'static>() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | (substs_change::<&T>(),)
+ | ------------------------ returning here with type `(impl Sized,)`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:76:24
+ |
+LL | fn generator_hold() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | / move || {
+LL | | let x = generator_hold();
+ | | - generator captures itself here
+LL | | yield;
+LL | | x;
+LL | | }
+ | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:78:5: 78:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:90:26
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | ^^^^^^^^^ recursive opaque type
+LL |
+LL | mutual_recursion_b()
+ | -------------------- returning here with type `impl Sized`
+...
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ---------- returning this opaque type `impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:95:28
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | --------- returning this opaque type `impl Sync`
+...
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | mutual_recursion()
+ | ------------------ returning here with type `impl Sync`
+
+error: aborting due to 14 previous errors
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:11:22
+ |
+LL | fn option(i: i32) -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | if i < 0 { None } else { Some((option(i - 1), i)) }
+ | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>`
+ | |
+ | returning here with type `Option<(impl Sized, i32)>`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:16:15
+ |
+LL | fn tuple() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | (tuple(),)
+ | ---------- returning here with type `(impl Sized,)`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:21:15
+ |
+LL | fn array() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | [array()]
+ | --------- returning here with type `[impl Sized; 1]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:26:13
+ |
+LL | fn ptr() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | &ptr() as *const _
+ | ------------------ returning here with type `*const impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:31:16
+ |
+LL | fn fn_ptr() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | fn_ptr as fn() -> _
+ | ------------------- returning here with type `fn() -> impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:36:25
+ |
+LL | fn closure_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | x;
+ | | - closure captures itself here
+LL | | }
+ | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:39:5: 39:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:44:29
+ |
+LL | fn closure_ref_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | &x;
+ | | - closure captures itself here
+LL | | }
+ | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:47:5: 47:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:52:21
+ |
+LL | fn closure_sig() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | || closure_sig()
+ | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:57:23
+ |
+LL | fn generator_sig() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | || generator_sig()
+ | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:59:5: 59:7]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:62:27
+ |
+LL | fn generator_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | yield;
+LL | | x;
+ | | - generator captures itself here
+LL | | }
+ | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:65:5: 65:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:71:35
+ |
+LL | fn substs_change<T: 'static>() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | (substs_change::<&T>(),)
+ | ------------------------ returning here with type `(impl Sized,)`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:76:24
+ |
+LL | fn generator_hold() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:90:26
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | ^^^^^^^^^ recursive opaque type
+LL |
+LL | mutual_recursion_b()
+ | -------------------- returning here with type `impl Sized`
+...
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ---------- returning this opaque type `impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:95:28
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | --------- returning this opaque type `impl Sync`
+...
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | mutual_recursion()
+ | ------------------ returning here with type `impl Sync`
+
+error: aborting due to 14 previous errors
+
+For more information about this error, try `rustc --explain E0720`.
--- /dev/null
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:11:22
+ |
+LL | fn option(i: i32) -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | if i < 0 { None } else { Some((option(i - 1), i)) }
+ | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>`
+ | |
+ | returning here with type `Option<(impl Sized, i32)>`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:16:15
+ |
+LL | fn tuple() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | (tuple(),)
+ | ---------- returning here with type `(impl Sized,)`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:21:15
+ |
+LL | fn array() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | [array()]
+ | --------- returning here with type `[impl Sized; 1]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:26:13
+ |
+LL | fn ptr() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | &ptr() as *const _
+ | ------------------ returning here with type `*const impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:31:16
+ |
+LL | fn fn_ptr() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | fn_ptr as fn() -> _
+ | ------------------- returning here with type `fn() -> impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:36:25
+ |
+LL | fn closure_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | x;
+ | | - closure captures itself here
+LL | | }
+ | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:39:5: 39:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:44:29
+ |
+LL | fn closure_ref_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | &x;
+ | | - closure captures itself here
+LL | | }
+ | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:47:5: 47:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:52:21
+ |
+LL | fn closure_sig() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | || closure_sig()
+ | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:54:5: 54:7]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:57:23
+ |
+LL | fn generator_sig() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | || generator_sig()
+ | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:59:5: 59:7]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:62:27
+ |
+LL | fn generator_capture() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+...
+LL | / move || {
+LL | | yield;
+LL | | x;
+ | | - generator captures itself here
+LL | | }
+ | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:65:5: 65:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:71:35
+ |
+LL | fn substs_change<T: 'static>() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | (substs_change::<&T>(),)
+ | ------------------------ returning here with type `(impl Sized,)`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:76:24
+ |
+LL | fn generator_hold() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | / move || {
+LL | | let x = generator_hold();
+ | | - generator captures itself here
+LL | | yield;
+LL | | x;
+LL | | }
+ | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:78:5: 78:12]`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:90:26
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | ^^^^^^^^^ recursive opaque type
+LL |
+LL | mutual_recursion_b()
+ | -------------------- returning here with type `impl Sized`
+...
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ---------- returning this opaque type `impl Sized`
+
+error[E0720]: cannot resolve opaque type
+ --> $DIR/recursive-impl-trait-type-indirect.rs:95:28
+ |
+LL | fn mutual_recursion() -> impl Sync {
+ | --------- returning this opaque type `impl Sync`
+...
+LL | fn mutual_recursion_b() -> impl Sized {
+ | ^^^^^^^^^^ recursive opaque type
+LL |
+LL | mutual_recursion()
+ | ------------------ returning here with type `impl Sync`
+
+error: aborting due to 14 previous errors
+
+For more information about this error, try `rustc --explain E0720`.
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
// Test that impl trait does not allow creating recursive types that are
// otherwise forbidden.
+++ /dev/null
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:7:22
- |
-LL | fn option(i: i32) -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | if i < 0 { None } else { Some((option(i - 1), i)) }
- | ---- ------------------------ returning here with type `Option<(impl Sized, i32)>`
- | |
- | returning here with type `Option<(impl Sized, i32)>`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:12:15
- |
-LL | fn tuple() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | (tuple(),)
- | ---------- returning here with type `(impl Sized,)`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:17:15
- |
-LL | fn array() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | [array()]
- | --------- returning here with type `[impl Sized; 1]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:22:13
- |
-LL | fn ptr() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | &ptr() as *const _
- | ------------------ returning here with type `*const impl Sized`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:27:16
- |
-LL | fn fn_ptr() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | fn_ptr as fn() -> _
- | ------------------- returning here with type `fn() -> impl Sized`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:32:25
- |
-LL | fn closure_capture() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-...
-LL | / move || {
-LL | | x;
-LL | | }
- | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:40:29
- |
-LL | fn closure_ref_capture() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-...
-LL | / move || {
-LL | | &x;
-LL | | }
- | |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:48:21
- |
-LL | fn closure_sig() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | || closure_sig()
- | ---------------- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:50:5: 50:7]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:53:23
- |
-LL | fn generator_sig() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | || generator_sig()
- | ------------------ returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:55:5: 55:7]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:58:27
- |
-LL | fn generator_capture() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-...
-LL | / move || {
-LL | | yield;
-LL | | x;
-LL | | }
- | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:67:35
- |
-LL | fn substs_change<T: 'static>() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | (substs_change::<&T>(),)
- | ------------------------ returning here with type `(impl Sized,)`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:72:24
- |
-LL | fn generator_hold() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | / move || {
-LL | | let x = generator_hold();
-LL | | yield;
-LL | | x;
-LL | | }
- | |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:74:5: 74:12]`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:86:26
- |
-LL | fn mutual_recursion() -> impl Sync {
- | ^^^^^^^^^ recursive opaque type
-LL |
-LL | mutual_recursion_b()
- | -------------------- returning here with type `impl Sized`
-...
-LL | fn mutual_recursion_b() -> impl Sized {
- | ---------- returning this opaque type `impl Sized`
-
-error[E0720]: cannot resolve opaque type
- --> $DIR/recursive-impl-trait-type-indirect.rs:91:28
- |
-LL | fn mutual_recursion() -> impl Sync {
- | --------- returning this opaque type `impl Sync`
-...
-LL | fn mutual_recursion_b() -> impl Sized {
- | ^^^^^^^^^^ recursive opaque type
-LL |
-LL | mutual_recursion()
- | ------------------ returning here with type `impl Sync`
-
-error: aborting due to 14 previous errors
-
-For more information about this error, try `rustc --explain E0720`.
error[E0597]: `x` does not live long enough
--> $DIR/assoc-ty-wf-used-to-get-assoc-ty.rs:24:31
|
+LL | let x: u8 = 3;
+ | - binding `x` declared here
LL | let _: &'static u8 = test(&x, &&3);
| -----^^------
| | |
LL | use super::{super::C::D::AA, AA as _};
| ^^^^^^^^^^^^^^^ no `AA` in `C::D`
|
- = note: consider importing this type alias instead:
+ = help: consider importing this type alias instead:
crate::A::AA
error[E0432]: unresolved import `crate::C::AA`
LL | use crate::C::{self, AA};
| ^^ no `AA` in `C`
|
- = note: consider importing this type alias instead:
+ = help: consider importing this type alias instead:
crate::A::AA
error[E0432]: unresolved import `crate::C::BB`
LL | use crate::{A, C::BB};
| ^^^^^ no `BB` in `C`
|
- = note: consider importing this type alias instead:
+ = help: consider importing this type alias instead:
crate::A::BB
error: aborting due to 3 previous errors
help: consider importing this type alias instead
|
LL | use A::B as _;
- | ~~~~~~~~~~
+ | ~~~~~~~~~
error[E0432]: unresolved import `crate::D::B2`
--> $DIR/bad-import-with-rename.rs:10:9
help: consider importing this type alias instead
|
LL | use A::B2;
- | ~~~~~~
+ | ~~~~~
error: aborting due to 2 previous errors
--> $DIR/import-prefix-macro-1.rs:11:27
|
LL | ($p: path) => (use $p {S, Z});
- | ^^^^^^ expected one of `::`, `;`, or `as`
+ | ^ expected one of `::`, `;`, or `as`
...
LL | import! { a::b::c }
| ------------------- in this macro invocation
help: consider importing one of these items instead
|
LL | use crate::m3::last_segment::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | use crate::m3::non_last_segment::non_last_segment::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | use issue_56125::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
LL | use issue_56125::last_segment::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
and 1 other candidate
error[E0659]: `issue_56125` is ambiguous
help: consider importing this module instead
|
LL | use glob_ok::something;
- | ~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+
+ mod p {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+ }
+
+ use ::nu;
+pub use self::p::{other_item as _};
+ //~^ ERROR unresolved import `self::p::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+
+ mod p {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+ }
+
+ pub use self::p::{nu, other_item as _};
+ //~^ ERROR unresolved import `self::p::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+error[E0432]: unresolved import `self::p::nu`
+ --> $DIR/issue-99695-b.rs:14:23
+ |
+LL | pub use self::p::{nu, other_item as _};
+ | ^^ no `nu` in `m::p`
+ |
+ = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
+help: a macro with this name exists at the root of the crate
+ |
+LL ~ use ::nu;
+LL ~ pub use self::p::{other_item as _};
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0432`.
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+
+ use ::nu;
+pub use self::{other_item as _};
+ //~^ ERROR unresolved import `self::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+
+ pub use self::{nu, other_item as _};
+ //~^ ERROR unresolved import `self::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+error[E0432]: unresolved import `self::nu`
+ --> $DIR/issue-99695.rs:11:20
+ |
+LL | pub use self::{nu, other_item as _};
+ | ^^ no `nu` in `m`
+ |
+ = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
+help: a macro with this name exists at the root of the crate
+ |
+LL ~ use ::nu;
+LL ~ pub use self::{other_item as _};
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0432`.
error[E0282]: type annotations needed
--> $DIR/cannot-infer-partial-try-return.rs:20:9
|
-LL | infallible()?;
- | ------------- type must be known at this point
LL | Ok(())
| ^^ cannot infer type of the type parameter `E` declared on the enum `Result`
|
--- /dev/null
+use std::marker::PhantomData;
+struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+where
+ Foo<'short, 'out, T>: Convert<'a, 'b>;
+ //~^ ERROR mismatched types
+ //~^^ ERROR mismatched types
+ //~^^^ ERROR use of undeclared lifetime name
+ //~| ERROR use of undeclared lifetime name `'out`
+
+trait Convert<'a, 'b>: Sized {
+ fn cast(&'a self) -> &'b Self;
+}
+impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ //~^ ERROR use of undeclared lifetime name
+ //~^^ ERROR use of undeclared lifetime name `'out`
+ //~| ERROR cannot infer an appropriate lifetime for lifetime parameter
+ fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ //~^ ERROR use of undeclared lifetime name
+ //~| ERROR cannot infer an appropriate lifetime for lifetime parameter
+ self
+ }
+}
+
+fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ //~^ ERROR use of undeclared lifetime name
+ //~^^ ERROR incompatible lifetime on type
+ //~| ERROR `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement
+ sadness.cast()
+}
+
+fn main() {}
--- /dev/null
+error[E0261]: use of undeclared lifetime name `'short`
+ --> $DIR/issue-107090.rs:4:9
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^ undeclared lifetime
+ |
+ = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider making the bound lifetime-generic with a new `'short` lifetime
+ |
+LL | for<'short> Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | +++++++++++
+help: consider introducing lifetime `'short` here
+ |
+LL | struct Foo<'short, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | +++++++
+
+error[E0261]: use of undeclared lifetime name `'out`
+ --> $DIR/issue-107090.rs:4:17
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^ undeclared lifetime
+ |
+help: consider making the bound lifetime-generic with a new `'out` lifetime
+ |
+LL | for<'out> Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | +++++++++
+help: consider introducing lifetime `'out` here
+ |
+LL | struct Foo<'out, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | +++++
+
+error[E0261]: use of undeclared lifetime name `'b`
+ --> $DIR/issue-107090.rs:13:47
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | - ^^ undeclared lifetime
+ | |
+ | help: consider introducing lifetime `'b` here: `'b,`
+
+error[E0261]: use of undeclared lifetime name `'out`
+ --> $DIR/issue-107090.rs:13:67
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | - help: consider introducing lifetime `'out` here: `'out,` ^^^^ undeclared lifetime
+
+error[E0261]: use of undeclared lifetime name `'out`
+ --> $DIR/issue-107090.rs:17:49
+ |
+LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ^^^^ undeclared lifetime
+ |
+help: consider introducing lifetime `'out` here
+ |
+LL | fn cast<'out>(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ++++++
+help: consider introducing lifetime `'out` here
+ |
+LL | impl<'out, 'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | +++++
+
+error[E0261]: use of undeclared lifetime name `'short`
+ --> $DIR/issue-107090.rs:24:68
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | - ^^^^^^ undeclared lifetime
+ | |
+ | help: consider introducing lifetime `'short` here: `'short,`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-107090.rs:4:27
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^^^^^^^^^^ lifetime mismatch
+ |
+ = note: expected trait `Convert<'static, 'static>`
+ found trait `Convert<'a, 'b>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/issue-107090.rs:2:12
+ |
+LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error[E0308]: mismatched types
+ --> $DIR/issue-107090.rs:4:27
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^^^^^^^^^^ lifetime mismatch
+ |
+ = note: expected trait `Convert<'static, 'static>`
+ found trait `Convert<'a, 'b>`
+note: the lifetime `'b` as defined here...
+ --> $DIR/issue-107090.rs:2:16
+ |
+LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements
+ --> $DIR/issue-107090.rs:13:55
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'short` as defined here...
+ --> $DIR/issue-107090.rs:13:21
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^
+ = note: ...but the lifetime must also be valid for the static lifetime...
+note: ...so that the types are compatible
+ --> $DIR/issue-107090.rs:13:55
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ = note: expected `Convert<'short, 'static>`
+ found `Convert<'_, 'static>`
+
+error: incompatible lifetime on type
+ --> $DIR/issue-107090.rs:24:29
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: because this has an unmet lifetime requirement
+ --> $DIR/issue-107090.rs:4:27
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^^^^^^^^^^ introduces a `'static` lifetime requirement
+note: the lifetime `'out` as defined here...
+ --> $DIR/issue-107090.rs:24:17
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | ^^^^
+note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
+ --> $DIR/issue-107090.rs:13:1
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0759]: `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/issue-107090.rs:24:29
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | ^^^^^^^^^^^^^^^^^^
+ | |
+ | this data with lifetime `'in_`...
+ | ...is used and required to live as long as `'static` here
+
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements
+ --> $DIR/issue-107090.rs:17:13
+ |
+LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'short` as defined here...
+ --> $DIR/issue-107090.rs:13:21
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^
+ = note: ...but the lifetime must also be valid for the static lifetime...
+note: ...so that the types are compatible
+ --> $DIR/issue-107090.rs:17:13
+ |
+LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^
+ = note: expected `Convert<'short, 'static>`
+ found `Convert<'_, 'static>`
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0261, E0308, E0495, E0759.
+For more information about an error, try `rustc --explain E0261`.
fn main() {
let _ = foo("foo");
- //~^ ERROR: type annotations needed for `[usize; _]`
+ //~^ ERROR: type annotations needed for `[usize; N]`
}
-error[E0282]: type annotations needed for `[usize; _]`
+error[E0282]: type annotations needed for `[usize; N]`
--> $DIR/issue-83606.rs:8:9
|
LL | let _ = foo("foo");
|
help: consider giving this pattern a type, where the the value of const parameter `N` is specified
|
-LL | let _: [usize; _] = foo("foo");
+LL | let _: [usize; N] = foo("foo");
| ++++++++++++
error: aborting due to previous error
error[E0282]: type annotations needed
- --> $DIR/question-mark-type-infer.rs:10:30
+ --> $DIR/question-mark-type-infer.rs:10:21
|
LL | l.iter().map(f).collect()?
- | ^ cannot infer type
+ | ^^^^^^^ cannot infer type of the type parameter `B` declared on the associated function `collect`
+ |
+help: consider specifying the generic argument
+ |
+LL | l.iter().map(f).collect::<Vec<_>>()?
+ | ++++++++++
error: aborting due to previous error
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let y = ();
+ | - binding `y` declared here
LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW });
| ------------------^^-
| | |
--- /dev/null
+// Issue #66530: We would ICE if someone compiled with `-o /dev/null`,
+// because we would try to generate auxiliary files in `/dev/` (which
+// at least the OS X file system rejects).
+//
+// An attempt to `-o` into a directory we cannot write into should indeed
+// be an error; but not an ICE.
+//
+// However, some folks run tests as root, which can write `/dev/` and end
+// up clobbering `/dev/null`. Instead we'll use a non-existent path, which
+// also used to ICE, but even root can't magically write there.
+
+// compile-flags: -Z temps-dir=/does-not-exist/output
+
+// The error-pattern check occurs *before* normalization, and the error patterns
+// are wildly different between build environments. So this is a cop-out (and we
+// rely on the checking of the normalized stderr output as our actual
+// "verification" of the diagnostic).
+
+// error-pattern: error
+
+// On Mac OS X, we get an error like the below
+// normalize-stderr-test "failed to write bytecode to /does-not-exist/output.non_ice_error_on_worker_io_fail.*" -> "io error modifying /does-not-exist/"
+
+// On Linux, we get an error like the below
+// normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /does-not-exist/"
+
+// ignore-windows - this is a unix-specific test
+// ignore-emscripten - the file-system issues do not replicate here
+// ignore-wasm - the file-system issues do not replicate here
+// ignore-arm - the file-system issues do not replicate here, at least on armhf-gnu
+
+#![crate_type = "lib"]
+#![cfg_attr(not(feature = "std"), no_std)]
+pub mod task {
+ pub mod __internal {
+ use crate::task::Waker;
+ }
+ pub use core::task::Waker;
+}
--- /dev/null
+error: failed to find or create the directory specified by `--temps-dir`
+
+error: aborting due to previous error
+
--- /dev/null
+// Issue #66530: We would ICE if someone compiled with `-o /dev/null`,
+// because we would try to generate auxiliary files in `/dev/` (which
+// at least the OS X file system rejects).
+//
+// An attempt to `-o` into a directory we cannot write into should indeed
+// be an error; but not an ICE.
+//
+// However, some folks run tests as root, which can write `/dev/` and end
+// up clobbering `/dev/null`. Instead we'll use a non-existent path, which
+// also used to ICE, but even root can't magically write there.
+
+// compile-flags: -o /does-not-exist/output
+
+// The error-pattern check occurs *before* normalization, and the error patterns
+// are wildly different between build environments. So this is a cop-out (and we
+// rely on the checking of the normalized stderr output as our actual
+// "verification" of the diagnostic).
+
+// error-pattern: error
+
+// On Mac OS X, we get an error like the below
+// normalize-stderr-test "failed to write bytecode to /does-not-exist/output.non_ice_error_on_worker_io_fail.*" -> "io error modifying /does-not-exist/"
+
+// On Linux, we get an error like the below
+// normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /does-not-exist/"
+
+// ignore-windows - this is a unix-specific test
+// ignore-emscripten - the file-system issues do not replicate here
+// ignore-wasm - the file-system issues do not replicate here
+// ignore-arm - the file-system issues do not replicate here, at least on armhf-gnu
+
+#![crate_type="lib"]
+
+#![cfg_attr(not(feature = "std"), no_std)]
+pub mod task {
+ pub mod __internal {
+ use crate::task::Waker;
+ }
+ pub use core::task::Waker;
+}
--- /dev/null
+warning: ignoring --out-dir flag due to -o flag
+
+error: io error modifying /does-not-exist/
+
+error: aborting due to previous error; 1 warning emitted
+
-// run-pass
// run-rustfix
#![allow(non_snake_case)]
match self {
&
Foo::Bar if true
-//~^ WARN pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+//~^ ERROR pattern binding `Bar` is named the same as one of the variants of the type `Foo`
=> println!("bar"),
&
Foo::Baz if false
-//~^ WARN pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+//~^ ERROR pattern binding `Baz` is named the same as one of the variants of the type `Foo`
=> println!("baz"),
_ => ()
}
-// run-pass
// run-rustfix
#![allow(non_snake_case)]
match self {
&
Bar if true
-//~^ WARN pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+//~^ ERROR pattern binding `Bar` is named the same as one of the variants of the type `Foo`
=> println!("bar"),
&
Baz if false
-//~^ WARN pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+//~^ ERROR pattern binding `Baz` is named the same as one of the variants of the type `Foo`
=> println!("baz"),
_ => ()
}
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-19100.rs:18:1
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-19100.rs:17:1
|
LL | Bar if true
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-19100.rs:22:1
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-19100.rs:21:1
|
LL | Baz if false
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning: 2 warnings emitted
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0170`.
--> $DIR/issue-40288.rs:16:5
|
LL | save_ref(&*refr, &mut out);
- | ------ borrow of `*refr` occurs here
+ | ------ `*refr` is borrowed here
...
LL | *refr = 3;
- | ^^^^^^^^^ assignment to borrowed `*refr` occurs here
+ | ^^^^^^^^^ `*refr` is assigned to here but it was already borrowed
...
LL | println!("{:?}", out[0]);
| ------ borrow later used here
--> $DIR/issue-45697-1.rs:20:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `y` occurs here
+ | ------ `y` is borrowed here
LL | *y.pointer += 1;
| ^^^^^^^^^^^^^^^ use of borrowed `y`
...
--> $DIR/issue-45697-1.rs:20:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `*y.pointer` occurs here
+ | ------ `*y.pointer` is borrowed here
LL | *y.pointer += 1;
- | ^^^^^^^^^^^^^^^ assignment to borrowed `*y.pointer` occurs here
+ | ^^^^^^^^^^^^^^^ `*y.pointer` is assigned to here but it was already borrowed
...
LL | *z.pointer += 1;
| --------------- borrow later used here
--> $DIR/issue-45697.rs:20:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `y` occurs here
+ | ------ `y` is borrowed here
LL | *y.pointer += 1;
| ^^^^^^^^^^^^^^^ use of borrowed `y`
...
--> $DIR/issue-45697.rs:20:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `*y.pointer` occurs here
+ | ------ `*y.pointer` is borrowed here
LL | *y.pointer += 1;
- | ^^^^^^^^^^^^^^^ assignment to borrowed `*y.pointer` occurs here
+ | ^^^^^^^^^^^^^^^ `*y.pointer` is assigned to here but it was already borrowed
...
LL | *z.pointer += 1;
| --------------- borrow later used here
error[E0597]: `z` does not live long enough
--> $DIR/issue-46471-1.rs:4:9
|
+LL | let mut z = 0;
+ | ----- binding `z` declared here
LL | &mut z
- | ^^^^^^
- | |
- | borrowed value does not live long enough
- | borrow later used here
+ | ^^^^^^ borrowed value does not live long enough
LL | };
| - `z` dropped here while still borrowed
error[E0597]: `line` does not live long enough
--> $DIR/issue-52126-assign-op-invariance.rs:34:28
|
+LL | for line in vec!["123456789".to_string(), "12345678".to_string()] {
+ | ---- binding `line` declared here
LL | let v: Vec<&str> = line.split_whitespace().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
...
--- /dev/null
+error[E0034]: multiple applicable items in scope
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:24:13
+ |
+LL | r#fn {}.r#struct();
+ | ^^^^^^^^ multiple `r#struct` found
+ |
+note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:7:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:13:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+help: disambiguate the associated function for candidate #1
+ |
+LL | async::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: disambiguate the associated function for candidate #2
+ |
+LL | await::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0034`.
--- /dev/null
+error[E0034]: multiple applicable items in scope
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:24:13
+ |
+LL | r#fn {}.r#struct();
+ | ^^^^^^^^ multiple `r#struct` found
+ |
+note: candidate #1 is defined in an impl of the trait `r#async` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:7:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl of the trait `r#await` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:13:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+help: disambiguate the associated function for candidate #1
+ |
+LL | r#async::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: disambiguate the associated function for candidate #2
+ |
+LL | r#await::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0034`.
+// revisions: edition2015 edition2018
+//[edition2018]edition:2018
+
#![allow(non_camel_case_types)]
trait r#async {
+++ /dev/null
-error[E0034]: multiple applicable items in scope
- --> $DIR/issue-65634-raw-ident-suggestion.rs:21:13
- |
-LL | r#fn {}.r#struct();
- | ^^^^^^^^ multiple `r#struct` found
- |
-note: candidate #1 is defined in an impl of the trait `async` for the type `fn`
- --> $DIR/issue-65634-raw-ident-suggestion.rs:4:5
- |
-LL | fn r#struct(&self) {
- | ^^^^^^^^^^^^^^^^^^
-note: candidate #2 is defined in an impl of the trait `await` for the type `fn`
- --> $DIR/issue-65634-raw-ident-suggestion.rs:10:5
- |
-LL | fn r#struct(&self) {
- | ^^^^^^^^^^^^^^^^^^
-help: disambiguate the associated function for candidate #1
- |
-LL | async::r#struct(&r#fn {});
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
-help: disambiguate the associated function for candidate #2
- |
-LL | await::r#struct(&r#fn {});
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0034`.
-error[E0282]: type annotations needed
- --> $DIR/issue-69455.rs:29:20
+error[E0284]: type annotations needed
+ --> $DIR/issue-69455.rs:29:41
|
LL | println!("{}", 23u64.test(xs.iter().sum()));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the associated function `new_display`
+ | ---- ^^^ cannot infer type of the type parameter `S` declared on the associated function `sum`
+ | |
+ | type must be known at this point
|
- = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: cannot satisfy `<u64 as Test<_>>::Output == _`
help: consider specifying the generic argument
|
-LL | println!("{}", 23u64.test(xs.iter().sum())::<T>);
- | +++++
+LL | println!("{}", 23u64.test(xs.iter().sum::<S>()));
+ | +++++
error[E0283]: type annotations needed
--> $DIR/issue-69455.rs:29:41
error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0282, E0283.
-For more information about an error, try `rustc --explain E0282`.
+Some errors have detailed explanations: E0283, E0284.
+For more information about an error, try `rustc --explain E0283`.
| ^^^^^^^^^^^^^^^^^^^ `RefCell<isize>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `RefCell<isize>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
= note: required for `Unique<RefCell<isize>>` to implement `Sync`
= note: required because it appears within the type `Box<RefCell<isize>>`
= note: shared static variables must have a type that implements `Sync`
--- /dev/null
+fn main() {
+ let x = Some(123);
+ if let Some(y) = x else { //~ ERROR this `if` expression is missing a block
+ return;
+ };
+}
--- /dev/null
+error: this `if` expression is missing a block after the condition
+ --> $DIR/accidental-if.rs:3:5
+ |
+LL | if let Some(y) = x else {
+ | ^^
+ |
+help: add a block here
+ --> $DIR/accidental-if.rs:3:23
+ |
+LL | if let Some(y) = x else {
+ | ^
+help: remove the `if` if you meant to write a `let...else` statement
+ --> $DIR/accidental-if.rs:3:5
+ |
+LL | if let Some(y) = x else {
+ | ^^
+
+error: aborting due to previous error
+
--- /dev/null
+// run-rustfix
+
+trait Greeter0 {
+ fn greet(&self);
+}
+
+trait Greeter1 {
+ fn greet(&self);
+}
+
+type BoxedGreeter<'a> = (Box<dyn Greeter0 + 'a>, Box<dyn Greeter1 + 'a>);
+//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
+
+struct FixedGreeter<'a>(pub &'a str);
+
+impl Greeter0 for FixedGreeter<'_> {
+ fn greet(&self) {
+ println!("0 {}", self.0)
+ }
+}
+
+impl Greeter1 for FixedGreeter<'_> {
+ fn greet(&self) {
+ println!("1 {}", self.0)
+ }
+}
+
+struct Greetings(pub Vec<String>);
+
+impl Greetings {
+ pub fn get(&self, i: usize) -> BoxedGreeter {
+ (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
+ //~^ ERROR lifetime may not live long enough
+ }
+}
+
+fn main() {
+ let mut g = Greetings {0 : vec!()};
+ g.0.push("a".to_string());
+ g.0.push("b".to_string());
+ g.get(0).0.greet();
+ g.get(0).1.greet();
+ g.get(1).0.greet();
+ g.get(1).1.greet();
+}
--- /dev/null
+// run-rustfix
+
+trait Greeter0 {
+ fn greet(&self);
+}
+
+trait Greeter1 {
+ fn greet(&self);
+}
+
+type BoxedGreeter = (Box<dyn Greeter0>, Box<dyn Greeter1>);
+//~^ HELP to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
+
+struct FixedGreeter<'a>(pub &'a str);
+
+impl Greeter0 for FixedGreeter<'_> {
+ fn greet(&self) {
+ println!("0 {}", self.0)
+ }
+}
+
+impl Greeter1 for FixedGreeter<'_> {
+ fn greet(&self) {
+ println!("1 {}", self.0)
+ }
+}
+
+struct Greetings(pub Vec<String>);
+
+impl Greetings {
+ pub fn get(&self, i: usize) -> BoxedGreeter {
+ (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
+ //~^ ERROR lifetime may not live long enough
+ }
+}
+
+fn main() {
+ let mut g = Greetings {0 : vec!()};
+ g.0.push("a".to_string());
+ g.0.push("b".to_string());
+ g.get(0).0.greet();
+ g.get(0).1.greet();
+ g.get(1).0.greet();
+ g.get(1).1.greet();
+}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-103582-hint-for-missing-lifetime-bound-on-trait-object-using-type-alias.rs:32:9
+ |
+LL | pub fn get(&self, i: usize) -> BoxedGreeter {
+ | - let's call the lifetime of this reference `'1`
+LL | (Box::new(FixedGreeter(&self.0[i])), Box::new(FixedGreeter(&self.0[i])))
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `self`, you can add a lifetime parameter `'a` in the type alias
+ |
+LL | type BoxedGreeter<'a> = (Box<dyn Greeter0 + 'a>, Box<dyn Greeter1 + 'a>);
+ | ++++ ++++ ++++
+
+error: aborting due to previous error
+
--- /dev/null
+// run-rustfix
+//
+#![allow(warnings)]
+struct Wrapper<'a, T: ?Sized>(&'a T);
+
+trait Project {
+ type Projected<'a> where Self: 'a;
+ fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_>;
+}
+trait MyTrait {}
+trait ProjectedMyTrait {}
+
+impl<T> Project for Option<T> {
+ type Projected<'a> = Option<Wrapper<'a, T>> where T: 'a;
+ fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_> {
+ this.0.as_ref().map(Wrapper)
+ }
+}
+
+impl<T: MyTrait> MyTrait for Option<Wrapper<'_, T>> {}
+
+impl<T: ProjectedMyTrait> MyTrait for Wrapper<'_, T> {}
+
+impl<T> ProjectedMyTrait for T
+ where
+ T: Project,
+ for<'a> T::Projected<'a>: MyTrait,
+ //~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
+ //~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
+{}
+
+fn require_trait<T: MyTrait>(_: T) {}
+
+fn foo<T : MyTrait + 'static + 'static, U : MyTrait + 'static + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
+ //~^ HELP consider restricting the type parameter to the `'static` lifetime
+ //~| HELP consider restricting the type parameter to the `'static` lifetime
+ require_trait(wrap);
+ //~^ ERROR `T` does not live long enough
+ require_trait(wrap1);
+ //~^ ERROR `U` does not live long enough
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+//
+#![allow(warnings)]
+struct Wrapper<'a, T: ?Sized>(&'a T);
+
+trait Project {
+ type Projected<'a> where Self: 'a;
+ fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_>;
+}
+trait MyTrait {}
+trait ProjectedMyTrait {}
+
+impl<T> Project for Option<T> {
+ type Projected<'a> = Option<Wrapper<'a, T>> where T: 'a;
+ fn project(this: Wrapper<'_, Self>) -> Self::Projected<'_> {
+ this.0.as_ref().map(Wrapper)
+ }
+}
+
+impl<T: MyTrait> MyTrait for Option<Wrapper<'_, T>> {}
+
+impl<T: ProjectedMyTrait> MyTrait for Wrapper<'_, T> {}
+
+impl<T> ProjectedMyTrait for T
+ where
+ T: Project,
+ for<'a> T::Projected<'a>: MyTrait,
+ //~^ NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
+ //~| NOTE due to current limitations in the borrow checker, this implies a `'static` lifetime
+{}
+
+fn require_trait<T: MyTrait>(_: T) {}
+
+fn foo<T : MyTrait, U : MyTrait>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
+ //~^ HELP consider restricting the type parameter to the `'static` lifetime
+ //~| HELP consider restricting the type parameter to the `'static` lifetime
+ require_trait(wrap);
+ //~^ ERROR `T` does not live long enough
+ require_trait(wrap1);
+ //~^ ERROR `U` does not live long enough
+}
+
+fn main() {}
--- /dev/null
+error: `T` does not live long enough
+ --> $DIR/issue-105507.rs:37:5
+ |
+LL | require_trait(wrap);
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+ --> $DIR/issue-105507.rs:27:35
+ |
+LL | for<'a> T::Projected<'a>: MyTrait,
+ | ^^^^^^^
+help: consider restricting the type parameter to the `'static` lifetime
+ |
+LL | fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
+ | +++++++++ +++++++++
+
+error: `U` does not live long enough
+ --> $DIR/issue-105507.rs:39:5
+ |
+LL | require_trait(wrap1);
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+ --> $DIR/issue-105507.rs:27:35
+ |
+LL | for<'a> T::Projected<'a>: MyTrait,
+ | ^^^^^^^
+help: consider restricting the type parameter to the `'static` lifetime
+ |
+LL | fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
+ | +++++++++ +++++++++
+
+error: aborting due to 2 previous errors
+
error[E0597]: `foo` does not live long enough
--> $DIR/issue-90600-expected-return-static-indirect.rs:7:32
|
+LL | fn inner(mut foo: &[u8]) {
+ | ------- binding `foo` declared here
LL | let refcell = RefCell::new(&mut foo);
| ^^^^^^^^ borrowed value does not live long enough
LL |
-error: values of the type `[usize; 4294967295]` are too big for the current architecture
+error: values of the type `[usize; usize::MAX]` are too big for the current architecture
--> $DIR/issue-15919-32.rs:9:9
|
LL | let x = [0usize; 0xffff_ffff];
-error: values of the type `[usize; 18446744073709551615]` are too big for the current architecture
+error: values of the type `[usize; usize::MAX]` are too big for the current architecture
--> $DIR/issue-15919-64.rs:9:9
|
LL | let x = [0usize; 0xffff_ffff_ffff_ffff];
// build-fail
-// normalize-stderr-test "\[&usize; \d+\]" -> "[&usize; N]"
+// normalize-stderr-test "\[&usize; \d+\]" -> "[&usize; usize::MAX]"
// error-pattern: too big for the current architecture
// FIXME https://github.com/rust-lang/rust/issues/59774
-error: values of the type `[&usize; N]` are too big for the current architecture
+error: values of the type `[&usize; usize::MAX]` are too big for the current architecture
error: aborting due to previous error
-error[E0080]: values of the type `[u8; SIZE]` are too big for the current architecture
+error[E0080]: values of the type `[u8; usize::MAX]` are too big for the current architecture
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
|
-note: inside `std::mem::size_of::<[u8; SIZE]>`
+note: inside `std::mem::size_of::<[u8; usize::MAX]>`
--> $SRC_DIR/core/src/mem/mod.rs:LL:COL
note: inside `main`
--> $DIR/issue-55878.rs:7:26
-error: values of the type `[u8; 18446744073709551615]` are too big for the current architecture
+error: values of the type `[u8; usize::MAX]` are too big for the current architecture
--> $DIR/issue-69485-var-size-diffs-too-large.rs:6:5
|
LL | Bug::V([0; !0]);
-error: values of the type `[u8; 18446744073709551615]` are too big for the current architecture
+error: values of the type `[u8; usize::MAX]` are too big for the current architecture
error: aborting due to previous error
fn is_empty<T>(s: Stack<T>) -> bool {
match s {
Nil => true,
-//~^ WARN pattern binding `Nil` is named the same as one of the variants of the type `Stack`
+//~^ ERROR pattern binding `Nil` is named the same as one of the variants of the type `Stack`
_ => false
//~^ ERROR unreachable pattern
}
-warning[E0170]: pattern binding `Nil` is named the same as one of the variants of the type `Stack`
+error[E0170]: pattern binding `Nil` is named the same as one of the variants of the type `Stack`
--> $DIR/issue-30302.rs:13:9
|
LL | Nil => true,
| ^^^ help: to match on the variant, qualify the path: `Stack::Nil`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
error: unreachable pattern
--> $DIR/issue-30302.rs:15:9
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error; 1 warning emitted
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0170`.
match foo::Foo::Foo {
Foo => {}
//~^ ERROR variable `Foo` should have a snake case name
- //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+ //~^^ ERROR `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`
}
let Foo = foo::Foo::Foo;
//~^ ERROR variable `Foo` should have a snake case name
- //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+ //~^^ ERROR `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`
fn in_param(Foo: foo::Foo) {}
//~^ ERROR variable `Foo` should have a snake case name
- //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+ //~^^ ERROR `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`
test(1);
-warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
|
LL | Foo => {}
| ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:28:9
|
LL | let Foo = foo::Foo::Foo;
| ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
-warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
+error[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:33:17
|
LL | fn in_param(Foo: foo::Foo) {}
LL | fn in_param(Foo: foo::Foo) {}
| ^^^ help: convert the identifier to snake case (notice the capitalization): `foo`
-error: aborting due to 6 previous errors; 6 warnings emitted
+error: aborting due to 9 previous errors; 3 warnings emitted
For more information about this error, try `rustc --explain E0170`.
--- /dev/null
+error: `No` held across a suspend point, but should not be
+ --> $DIR/dedup.rs:19:9
+ |
+LL | let no = No {};
+ | ^^
+LL | wheeee(&no).await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/dedup.rs:19:9
+ |
+LL | let no = No {};
+ | ^^
+note: the lint level is defined here
+ --> $DIR/dedup.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+error: `No` held across a suspend point, but should not be
+ --> $DIR/dedup.rs:19:9
+ |
+LL | let no = No {};
+ | ^^
+LL | wheeee(&no).await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/dedup.rs:19:9
+ |
+LL | let no = No {};
+ | ^^
+note: the lint level is defined here
+ --> $DIR/dedup.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+error: `No` held across a suspend point, but should not be
+ --> $DIR/dedup.rs:19:9
+ |
+LL | let no = No {};
+ | ^^
+LL | wheeee(&no).await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/dedup.rs:19:9
+ |
+LL | let no = No {};
+ | ^^
+note: the lint level is defined here
+ --> $DIR/dedup.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: `No` held across a suspend point, but should not be
+ --> $DIR/dedup.rs:20:13
+ |
+LL | wheeee(&no).await;
+ | ^^ ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/dedup.rs:20:13
+ |
+LL | wheeee(&no).await;
+ | ^^
+
+error: aborting due to 2 previous errors
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
}
async fn yes() {
- wheeee(&No {}).await; //~ ERROR `No` held across
+ let no = No {}; //~ ERROR `No` held across
+ wheeee(&no).await; //[no_drop_tracking]~ ERROR `No` held across
+ drop(no);
}
fn main() {
error: `No` held across a suspend point, but should not be
- --> $DIR/dedup.rs:16:13
+ --> $DIR/dedup.rs:19:13
|
LL | wheeee(&No {}).await;
| ^^^^^ ------ the value is held across this suspend point
|
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/dedup.rs:16:13
+ --> $DIR/dedup.rs:19:13
|
LL | wheeee(&No {}).await;
| ^^^^^
note: the lint level is defined here
- --> $DIR/dedup.rs:3:9
+ --> $DIR/dedup.rs:6:9
|
LL | #![deny(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
error: reference to `Umm` held across a suspend point, but should not be
- --> $DIR/ref.rs:21:13
+ --> $DIR/ref.rs:22:13
|
LL | let guard = &mut self.u;
| ^^^^^
| ------ the value is held across this suspend point
|
note: You gotta use Umm's, ya know?
- --> $DIR/ref.rs:21:13
+ --> $DIR/ref.rs:22:13
|
LL | let guard = &mut self.u;
| ^^^^^
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/ref.rs:21:13
+ --> $DIR/ref.rs:22:13
|
LL | let guard = &mut self.u;
| ^^^^^
note: the lint level is defined here
- --> $DIR/ref.rs:6:9
+ --> $DIR/ref.rs:7:9
|
LL | #![deny(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
--- /dev/null
+error: reference to `Umm` held across a suspend point, but should not be
+ --> $DIR/ref.rs:22:13
+ |
+LL | let guard = &mut self.u;
+ | ^^^^^
+LL |
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/ref.rs:22:13
+ |
+LL | let guard = &mut self.u;
+ | ^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/ref.rs:22:13
+ |
+LL | let guard = &mut self.u;
+ | ^^^^^
+note: the lint level is defined here
+ --> $DIR/ref.rs:7:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
error: `Umm` held across a suspend point, but should not be
- --> $DIR/ref.rs:21:26
+ --> $DIR/ref.rs:22:26
|
LL | let guard = &mut self.u;
| ^^^^^^
| ------ the value is held across this suspend point
|
note: You gotta use Umm's, ya know?
- --> $DIR/ref.rs:21:26
+ --> $DIR/ref.rs:22:26
|
LL | let guard = &mut self.u;
| ^^^^^^
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/ref.rs:21:26
+ --> $DIR/ref.rs:22:26
|
LL | let guard = &mut self.u;
| ^^^^^^
note: the lint level is defined here
- --> $DIR/ref.rs:6:9
+ --> $DIR/ref.rs:7:9
|
LL | #![deny(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
// edition:2018
-// revisions: no_drop_tracking drop_tracking
-// [drop_tracking] compile-flags: -Zdrop-tracking=yes
-// [no_drop_tracking] compile-flags: -Zdrop-tracking=no
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
+
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
other().await;
+ let _g = &*guard;
*guard = Umm { i: 2 }
}
}
--- /dev/null
+error: implementer of `Wow` held across a suspend point, but should not be
+ --> $DIR/trait.rs:24:9
+ |
+LL | let _guard1 = r#impl();
+ | ^^^^^^^
+...
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/trait.rs:24:9
+ |
+LL | let _guard1 = r#impl();
+ | ^^^^^^^
+note: the lint level is defined here
+ --> $DIR/trait.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: boxed `Wow` trait object held across a suspend point, but should not be
+ --> $DIR/trait.rs:25:9
+ |
+LL | let _guard2 = r#dyn();
+ | ^^^^^^^
+LL |
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/trait.rs:25:9
+ |
+LL | let _guard2 = r#dyn();
+ | ^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: implementer of `Wow` held across a suspend point, but should not be
+ --> $DIR/trait.rs:24:9
+ |
+LL | let _guard1 = r#impl();
+ | ^^^^^^^
+...
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/trait.rs:24:9
+ |
+LL | let _guard1 = r#impl();
+ | ^^^^^^^
+note: the lint level is defined here
+ --> $DIR/trait.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: boxed `Wow` trait object held across a suspend point, but should not be
+ --> $DIR/trait.rs:25:9
+ |
+LL | let _guard2 = r#dyn();
+ | ^^^^^^^
+LL |
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/trait.rs:25:9
+ |
+LL | let _guard2 = r#dyn();
+ | ^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+error: implementer of `Wow` held across a suspend point, but should not be
+ --> $DIR/trait.rs:24:9
+ |
+LL | let _guard1 = r#impl();
+ | ^^^^^^^
+...
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/trait.rs:24:9
+ |
+LL | let _guard1 = r#impl();
+ | ^^^^^^^
+note: the lint level is defined here
+ --> $DIR/trait.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: boxed `Wow` trait object held across a suspend point, but should not be
+ --> $DIR/trait.rs:25:9
+ |
+LL | let _guard2 = r#dyn();
+ | ^^^^^^^
+LL |
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/trait.rs:25:9
+ |
+LL | let _guard2 = r#dyn();
+ | ^^^^^^^
+
+error: aborting due to 2 previous errors
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
let _guard2 = r#dyn(); //~ ERROR boxed `Wow` trait object held across
other().await;
+
+ drop(_guard1);
+ drop(_guard2);
}
fn main() {
error: implementer of `Wow` held across a suspend point, but should not be
- --> $DIR/trait.rs:21:9
+ --> $DIR/trait.rs:24:9
|
LL | let _guard1 = r#impl();
| ^^^^^^^
| ------ the value is held across this suspend point
|
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/trait.rs:21:9
+ --> $DIR/trait.rs:24:9
|
LL | let _guard1 = r#impl();
| ^^^^^^^
note: the lint level is defined here
- --> $DIR/trait.rs:3:9
+ --> $DIR/trait.rs:6:9
|
LL | #![deny(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
error: boxed `Wow` trait object held across a suspend point, but should not be
- --> $DIR/trait.rs:22:9
+ --> $DIR/trait.rs:25:9
|
LL | let _guard2 = r#dyn();
| ^^^^^^^
| ------ the value is held across this suspend point
|
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/trait.rs:22:9
+ --> $DIR/trait.rs:25:9
|
LL | let _guard2 = r#dyn();
| ^^^^^^^
--- /dev/null
+error: `Umm` held across a suspend point, but should not be
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+note: the lint level is defined here
+ --> $DIR/unit.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+error: `Umm` held across a suspend point, but should not be
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+note: the lint level is defined here
+ --> $DIR/unit.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+error: `Umm` held across a suspend point, but should not be
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/unit.rs:22:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+note: the lint level is defined here
+ --> $DIR/unit.rs:6:9
+ |
+LL | #![deny(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
#![feature(must_not_suspend)]
#![deny(must_not_suspend)]
i: i64
}
-
fn bar() -> Umm {
Umm {
i: 1
pub async fn uhoh() {
let _guard = bar(); //~ ERROR `Umm` held across
other().await;
+ drop(_guard);
}
fn main() {
error: `Umm` held across a suspend point, but should not be
- --> $DIR/unit.rs:20:9
+ --> $DIR/unit.rs:23:9
|
LL | let _guard = bar();
| ^^^^^^
| ------ the value is held across this suspend point
|
note: You gotta use Umm's, ya know?
- --> $DIR/unit.rs:20:9
+ --> $DIR/unit.rs:23:9
|
LL | let _guard = bar();
| ^^^^^^
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/unit.rs:20:9
+ --> $DIR/unit.rs:23:9
|
LL | let _guard = bar();
| ^^^^^^
note: the lint level is defined here
- --> $DIR/unit.rs:3:9
+ --> $DIR/unit.rs:6:9
|
LL | #![deny(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
--- /dev/null
+warning: `Umm` held across a suspend point, but should not be
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+note: the lint level is defined here
+ --> $DIR/warn.rs:7:9
+ |
+LL | #![warn(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
--- /dev/null
+warning: `Umm` held across a suspend point, but should not be
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+note: the lint level is defined here
+ --> $DIR/warn.rs:7:9
+ |
+LL | #![warn(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
--- /dev/null
+warning: `Umm` held across a suspend point, but should not be
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+LL | other().await;
+ | ------ the value is held across this suspend point
+ |
+note: You gotta use Umm's, ya know?
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
+ --> $DIR/warn.rs:24:9
+ |
+LL | let _guard = bar();
+ | ^^^^^^
+note: the lint level is defined here
+ --> $DIR/warn.rs:7:9
+ |
+LL | #![warn(must_not_suspend)]
+ | ^^^^^^^^^^^^^^^^
+
+warning: 1 warning emitted
+
+// revisions: no_drop_tracking drop_tracking drop_tracking_mir
+// [drop_tracking] compile-flags: -Zdrop-tracking
+// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir
// edition:2018
// run-pass
#![feature(must_not_suspend)]
pub async fn uhoh() {
let _guard = bar(); //~ WARNING `Umm` held across
other().await;
+ drop(_guard);
}
fn main() {
warning: `Umm` held across a suspend point, but should not be
- --> $DIR/warn.rs:21:9
+ --> $DIR/warn.rs:24:9
|
LL | let _guard = bar();
| ^^^^^^
| ------ the value is held across this suspend point
|
note: You gotta use Umm's, ya know?
- --> $DIR/warn.rs:21:9
+ --> $DIR/warn.rs:24:9
|
LL | let _guard = bar();
| ^^^^^^
help: consider using a block (`{ ... }`) to shrink the value's scope, ending before the suspend point
- --> $DIR/warn.rs:21:9
+ --> $DIR/warn.rs:24:9
|
LL | let _guard = bar();
| ^^^^^^
note: the lint level is defined here
- --> $DIR/warn.rs:4:9
+ --> $DIR/warn.rs:7:9
|
LL | #![warn(must_not_suspend)]
| ^^^^^^^^^^^^^^^^
warning: 3 warnings emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | foo!(first)
+ | ----------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo`
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:24:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(second);
+ | ------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:29:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(third);
+ | ----------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:32:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(fourth);
+ | ------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:37:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | foo!(warn_in_block)
+ | ------------------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo`
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:4:9
+ |
+LL | #![warn(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(warn_in_expr);
+ | ------------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:4:9
+ |
+LL | #![warn(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = #[allow(semicolon_in_expressions_from_macros)] foo!(allow_does_not_work);
+ | ------------------------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:4:9
+ |
+LL | #![warn(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
warning: 1 warning emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/warn-semicolon-in-expressions-from-macros.rs:6:13
+ |
+LL | true;
+ | ^
+...
+LL | _ => foo!()
+ | ------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
--- /dev/null
+#![warn(unused)]
+#![deny(warnings)]
+
+fn main() {
+ let _x: ([u32; 3]); //~ ERROR unnecessary parentheses around type
+ let _y: [u8; (3)]; //~ ERROR unnecessary parentheses around const expression
+ let _z: ([u8; (3)]);
+ //~^ ERROR unnecessary parentheses around const expression
+ //~| ERROR unnecessary parentheses around type
+
+}
--- /dev/null
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-array-lint.rs:5:13
+ |
+LL | let _x: ([u32; 3]);
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-105061-array-lint.rs:2:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(warnings)]`
+help: remove these parentheses
+ |
+LL - let _x: ([u32; 3]);
+LL + let _x: [u32; 3];
+ |
+
+error: unnecessary parentheses around const expression
+ --> $DIR/issue-105061-array-lint.rs:6:18
+ |
+LL | let _y: [u8; (3)];
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - let _y: [u8; (3)];
+LL + let _y: [u8; 3];
+ |
+
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-array-lint.rs:7:13
+ |
+LL | let _z: ([u8; (3)]);
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - let _z: ([u8; (3)]);
+LL + let _z: [u8; (3)];
+ |
+
+error: unnecessary parentheses around const expression
+ --> $DIR/issue-105061-array-lint.rs:7:19
+ |
+LL | let _z: ([u8; (3)]);
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - let _z: ([u8; (3)]);
+LL + let _z: ([u8; 3]);
+ |
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+#![warn(unused)]
+#![deny(warnings)]
+
+struct Inv<'a>(&'a mut &'a ());
+
+trait Trait<'a> {}
+impl<'b> Trait<'b> for for<'a> fn(Inv<'a>) {}
+
+fn with_bound()
+where
+ for<'b> (for<'a> fn(Inv<'a>)): Trait<'b>, //~ ERROR unnecessary parentheses around type
+{}
+
+trait Hello<T> {}
+fn with_dyn_bound<T>()
+where
+ (dyn Hello<(for<'b> fn(&'b ()))>): Hello<T> //~ ERROR unnecessary parentheses around type
+{}
+
+fn main() {
+ with_bound();
+ with_dyn_bound();
+}
--- /dev/null
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-should-lint.rs:11:13
+ |
+LL | for<'b> (for<'a> fn(Inv<'a>)): Trait<'b>,
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-105061-should-lint.rs:2:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(warnings)]`
+help: remove these parentheses
+ |
+LL - for<'b> (for<'a> fn(Inv<'a>)): Trait<'b>,
+LL + for<'b> for<'a> fn(Inv<'a>): Trait<'b>,
+ |
+
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-should-lint.rs:17:16
+ |
+LL | (dyn Hello<(for<'b> fn(&'b ()))>): Hello<T>
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - (dyn Hello<(for<'b> fn(&'b ()))>): Hello<T>
+LL + (dyn Hello<for<'b> fn(&'b ())>): Hello<T>
+ |
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![warn(unused)]
+#![deny(warnings)]
+
+struct Inv<'a>(&'a mut &'a ());
+
+trait Trait {}
+impl Trait for (for<'a> fn(Inv<'a>),) {}
+
+
+fn with_bound()
+where
+ ((for<'a> fn(Inv<'a>)),): Trait, //~ ERROR unnecessary parentheses around type
+{}
+
+fn main() {
+ with_bound();
+}
--- /dev/null
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061.rs:12:6
+ |
+LL | ((for<'a> fn(Inv<'a>)),): Trait,
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-105061.rs:2:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(warnings)]`
+help: remove these parentheses
+ |
+LL - ((for<'a> fn(Inv<'a>)),): Trait,
+LL + (for<'a> fn(Inv<'a>),): Trait,
+ |
+
+error: aborting due to previous error
+
if { return } {
}
+
+ // regression test for https://github.com/rust-lang/rust/issues/106899
+ return println!("!");
+ //~^ WARN unnecessary braces
}
if { return } {
}
+
+ // regression test for https://github.com/rust-lang/rust/issues/106899
+ return { println!("!") };
+ //~^ WARN unnecessary braces
}
LL + consume(7);
|
-warning: 5 warnings emitted
+warning: unnecessary braces around `return` value
+ --> $DIR/unused_braces.rs:55:12
+ |
+LL | return { println!("!") };
+ | ^^ ^^
+ |
+help: remove these braces
+ |
+LL - return { println!("!") };
+LL + return println!("!");
+ |
+
+warning: 6 warnings emitted
error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:41:27
|
+LL | let mutex = Mutex;
+ | ----- binding `mutex` declared here
LL | write!(Out, "{}", mutex.lock()) /* no semicolon */
| ^^^^^^^^^^^^
| |
error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:47:29
|
+LL | let mutex = Mutex;
+ | ----- binding `mutex` declared here
LL | writeln!(Out, "{}", mutex.lock()) /* no semicolon */
| ^^^^^^^^^^^^
| |
error: aborting due to previous error
+Future incompatibility report: Future breakage diagnostic:
+error: trailing semicolon in macro used in expression position
+ --> $DIR/issue-84195-lint-anon-const.rs:8:14
+ |
+LL | () => { 0; };
+ | ^
+...
+LL | let val: [u8; len!()] = [];
+ | ------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/issue-84195-lint-anon-const.rs:5:9
+ |
+LL | #![deny(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `len` (in Nightly builds, run with -Z macro-backtrace for more info)
+
#[derive(Bla)]
//~^ ERROR cannot find derive macro `Bla`
-//~| NOTE consider importing this derive macro
+//~| HELP consider importing this derive macro
struct A;
#[derive(println)]
fn main() {
bla!();
//~^ ERROR cannot find macro `bla`
- //~| NOTE consider importing this macro
+ //~| HELP consider importing this macro
}
LL | bla!();
| ^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
crate::hey::bla
error: cannot find derive macro `println` in this scope
LL | #[derive(Bla)]
| ^^^
|
- = note: consider importing this derive macro:
+ = help: consider importing this derive macro:
crate::hey::Bla
error: aborting due to 3 previous errors
warning: 1 warning emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/lint-trailing-macro-call.rs:9:25
+ |
+LL | #[cfg(FALSE)] 25;
+ | ^
+...
+LL | expand_it!()
+ | ------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `expand_it`
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `expand_it` (in Nightly builds, run with -Z macro-backtrace for more info)
+
Some errors have detailed explanations: E0412, E0425.
For more information about an error, try `rustc --explain E0412`.
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/macro-context.rs:3:15
+ |
+LL | () => ( i ; typeof );
+ | ^
+...
+LL | let i = m!();
+ | ---- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
error: aborting due to previous error; 1 warning emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/macro-in-expression-context.rs:5:29
+ |
+LL | assert_eq!("A", "A");
+ | ^
+...
+LL | foo!()
+ | ------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo`
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
LL | macro_rules! macro_one { () => ("one") }
| ---------------------- similarly named macro `macro_one` defined here
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
two_macros::macro_two
error: aborting due to previous error
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem as usize\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem as usize\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!&*__local_bind0) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: &elem\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: &elem\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!(*__local_bind0 == 1)) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem == 1\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem == 1\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!(*__local_bind0 >= 1)) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem >= 1\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem >= 1\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!(*__local_bind0 > 0)) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem > 0\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem > 0\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!(*__local_bind0 < 3)) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem < 3\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem < 3\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!(*__local_bind0 <= 3)) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem <= 3\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem <= 3\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!(*__local_bind0 != 3)) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: elem != 3\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: elem != 3\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
if ::core::intrinsics::unlikely(!**__local_bind0) {
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
{
- ::std::rt::panic_fmt(::core::fmt::Arguments::new_v1(&["Assertion failed: *elem\nWith captures:\n elem = ",
- "\n"], &[::core::fmt::ArgumentV1::new_debug(&__capture0)]))
+ ::std::rt::panic_fmt(format_args!("Assertion failed: *elem\nWith captures:\n elem = {0:?}\n",
+ __capture0))
}
}
};
LL | let _arg = match args.next() {
| ---- borrow later stored here
LL | Some(arg) => {
+ | --- binding `arg` declared here
LL | match arg.to_str() {
| ^^^^^^^^^^^^ borrowed value does not live long enough
...
--- /dev/null
+struct Foo;
+
+impl Foo {
+ #[doc(alias = "quux")]
+ fn bar(&self) {}
+}
+
+fn main() {
+ Foo.quux();
+ //~^ ERROR no method named `quux` found for struct `Foo` in the current scope
+}
--- /dev/null
+error[E0599]: no method named `quux` found for struct `Foo` in the current scope
+ --> $DIR/method-not-found-but-doc-alias.rs:9:9
+ |
+LL | struct Foo;
+ | ---------- method `quux` not found for this struct
+...
+LL | Foo.quux();
+ | ^^^^ help: there is a method with a similar name: `bar`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// build-pass
+// compile-flags: --crate-type=lib
+#![feature(custom_mir, core_intrinsics)]
+use std::intrinsics::mir::*;
+
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+pub fn f(a: u32) -> u32 {
+ mir!(
+ let x: u32;
+ {
+ // Previously code generation failed with ICE "use of .. before def ..." because the
+ // definition of x was incorrectly identified as dominating the use of x located in the
+ // same statement:
+ x = x + a;
+ RET = x;
+ Return()
+ }
+ )
+}
--> $DIR/cast-rfc0401.rs:71:30
|
LL | vec![0.0].iter().map(|s| s as f32).collect::<Vec<f32>>();
- | -^^^^^^^
- | |
- | cannot cast `&{float}` as `f32`
- | help: dereference the expression: `*s`
+ | ^^^^^^^^
+ |
+help: dereference the expression
+ |
+LL | vec![0.0].iter().map(|s| *s as f32).collect::<Vec<f32>>();
+ | +
error: aborting due to 34 previous errors
--- /dev/null
+// run-rustfix
+fn main() {
+ let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments
+ let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments
+}
--- /dev/null
+// run-rustfix
+fn main() {
+ let _ = (-10..=10).find(|x: i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments
+ let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //~ ERROR type mismatch in closure arguments
+}
--- /dev/null
+error[E0631]: type mismatch in closure arguments
+ --> $DIR/closure-arg-type-mismatch-issue-45727.rs:3:24
+ |
+LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0);
+ | ^^^^ -------- found signature defined here
+ | |
+ | expected due to this
+ |
+ = note: expected closure signature `for<'a> fn(&'a {integer}) -> _`
+ found closure signature `fn(i32) -> _`
+note: required by a bound in `find`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider borrowing the argument
+ |
+LL | let _ = (-10..=10).find(|x: &i32| x.signum() == 0);
+ | +
+
+error[E0631]: type mismatch in closure arguments
+ --> $DIR/closure-arg-type-mismatch-issue-45727.rs:4:24
+ |
+LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
+ | ^^^^ ----------- found signature defined here
+ | |
+ | expected due to this
+ |
+ = note: expected closure signature `for<'a> fn(&'a {integer}) -> _`
+ found closure signature `for<'a, 'b, 'c> fn(&'a &'b &'c i32) -> _`
+note: required by a bound in `find`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: do not borrow the argument
+ |
+LL - let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
+LL + let _ = (-10..=10).find(|x: &i32| x.signum() == 0);
+ |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0631`.
--> $DIR/closure-arg-type-mismatch.rs:3:14
|
LL | a.iter().map(|_: (u32, u32)| 45);
- | ^^^ ---------------
- | | | |
- | | | help: consider borrowing the argument: `&(u32, u32)`
- | | found signature defined here
+ | ^^^ --------------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `fn(&(u32, u32)) -> _`
found closure signature `fn((u32, u32)) -> _`
note: required by a bound in `map`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider borrowing the argument
+ |
+LL | a.iter().map(|_: &(u32, u32)| 45);
+ | +
error[E0631]: type mismatch in closure arguments
--> $DIR/closure-arg-type-mismatch.rs:4:14
|
= note: expected type parameter `bool` (type parameter `bool`)
found type `bool` (`bool`)
+help: the type constructed contains `bool` due to the type of the argument passed
+ --> $DIR/issue-35030.rs:9:9
+ |
+LL | Some(true)
+ | ^^^^^----^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
--> $DIR/issue-36053-2.rs:7:32
|
LL | once::<&str>("str").fuse().filter(|a: &str| true).count();
- | ^^^^^^ ---------
- | | | |
- | | | help: consider borrowing the argument: `&&str`
- | | found signature defined here
+ | ^^^^^^ --------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `for<'a> fn(&'a &str) -> _`
found closure signature `for<'a> fn(&'a str) -> _`
note: required by a bound in `filter`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider borrowing the argument
+ |
+LL | once::<&str>("str").fuse().filter(|a: &&str| true).count();
+ | +
error[E0599]: the method `count` exists for struct `Filter<Fuse<Once<&str>>, [closure@issue-36053-2.rs:7:39]>`, but its trait bounds were not satisfied
--> $DIR/issue-36053-2.rs:7:55
|
= note: the following trait bounds were not satisfied:
`T: Eq`
+ `T: PartialEq`
+ which is required by `T: Eq`
`T: Hash`
help: consider restricting the type parameters to satisfy the trait bounds
|
LL | macro_two!();
| ^^^^^^^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
two_macros::macro_two
error: aborting due to previous error
error[E0505]: cannot move out of `mut_foo` because it is borrowed
--> $DIR/move-fn-self-receiver.rs:50:5
|
+LL | let mut mut_foo = Foo;
+ | ----------- binding `mut_foo` declared here
LL | let ret = mut_foo.use_mut_self();
| ---------------------- borrow of `mut_foo` occurs here
LL | mut_foo;
--- /dev/null
+// run-rustfix
+use std::pin::Pin;
+
+fn foo(_: &mut ()) {}
+
+fn main() {
+ let mut uwu = ();
+ let mut r = Pin::new(&mut uwu);
+ foo(r.as_mut().get_mut());
+ foo(r.get_mut()); //~ ERROR use of moved value
+}
--- /dev/null
+// run-rustfix
+use std::pin::Pin;
+
+fn foo(_: &mut ()) {}
+
+fn main() {
+ let mut uwu = ();
+ let mut r = Pin::new(&mut uwu);
+ foo(r.get_mut());
+ foo(r.get_mut()); //~ ERROR use of moved value
+}
--- /dev/null
+error[E0382]: use of moved value: `r`
+ --> $DIR/pin-mut-reborrow-infer-var-issue-107419.rs:10:9
+ |
+LL | let mut r = Pin::new(&mut uwu);
+ | ----- move occurs because `r` has type `Pin<&mut ()>`, which does not implement the `Copy` trait
+LL | foo(r.get_mut());
+ | --------- `r` moved due to this method call
+LL | foo(r.get_mut());
+ | ^ value used here after move
+ |
+note: `Pin::<&'a mut T>::get_mut` takes ownership of the receiver `self`, which moves `r`
+ --> $SRC_DIR/core/src/pin.rs:LL:COL
+help: consider reborrowing the `Pin` instead of moving it
+ |
+LL | foo(r.as_mut().get_mut());
+ | +++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
--> $DIR/mut-pattern-internal-mutability.rs:13:5
|
LL | let &mut ref x = foo;
- | ----- borrow of `*foo` occurs here
+ | ----- `*foo` is borrowed here
LL | *foo += 1;
- | ^^^^^^^^^ assignment to borrowed `*foo` occurs here
+ | ^^^^^^^^^ `*foo` is assigned to here but it was already borrowed
LL | drop(x);
| - borrow later used here
+++ /dev/null
-// MutexGuard<Cell<i32>> must not be Sync, that would be unsound.
-use std::sync::Mutex;
-use std::cell::Cell;
-
-fn test_sync<T: Sync>(_t: T) {}
-
-fn main()
-{
- let m = Mutex::new(Cell::new(0i32));
- let guard = m.lock().unwrap();
- test_sync(guard);
- //~^ ERROR `Cell<i32>` cannot be shared between threads safely [E0277]
-}
+++ /dev/null
-error[E0277]: `Cell<i32>` cannot be shared between threads safely
- --> $DIR/mutexguard-sync.rs:11:15
- |
-LL | test_sync(guard);
- | --------- ^^^^^ `Cell<i32>` cannot be shared between threads safely
- | |
- | required by a bound introduced by this call
- |
- = help: the trait `Sync` is not implemented for `Cell<i32>`
- = note: required for `MutexGuard<'_, Cell<i32>>` to implement `Sync`
-note: required by a bound in `test_sync`
- --> $DIR/mutexguard-sync.rs:5:17
- |
-LL | fn test_sync<T: Sync>(_t: T) {}
- | ^^^^ required by this bound in `test_sync`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
LL | let x = gimme({
| ----- borrow later used by call
LL | let v = (22,);
+ | - binding `v` declared here
LL | &v
| ^^ borrowed value does not live long enough
LL |
--> $DIR/borrowed-match-issue-45045.rs:12:11
|
LL | let f = &mut e;
- | ------ borrow of `e` occurs here
+ | ------ `e` is borrowed here
LL | let g = f;
LL | match e {
| ^ use of borrowed `e`
error[E0597]: `y` does not live long enough
--> $DIR/capture-ref-in-struct.rs:18:16
|
+LL | let y = 22;
+ | - binding `y` declared here
+...
LL | y: &y,
| ^^ borrowed value does not live long enough
...
--> $DIR/closure-access-spans.rs:23:13
|
LL | let r = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | move || x;
| ^ use of borrowed `x`
LL | r.use_ref();
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/closure-access-spans.rs:29:5
|
+LL | fn closure_move_capture_conflict(mut x: String) {
+ | ----- binding `x` declared here
LL | let r = &x;
| -- borrow of `x` occurs here
LL | || x;
LL | let f = || x;
| -- - borrow occurs due to use in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | f.use_ref();
| ----------- borrow later used here
LL | let f = || x = 0;
| -- - borrow occurs due to use of `x` in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | let y = x;
| ^ use of borrowed `x`
LL | f.use_ref();
LL | let f = || x = 0;
| -- - borrow occurs due to use in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | f.use_ref();
| ----------- borrow later used here
LL | let f = || *x = 0;
| -- -- borrow occurs due to use in closure
| |
- | borrow of `*x` occurs here
+ | `*x` is borrowed here
LL | *x = 1;
- | ^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^ `*x` is assigned to here but it was already borrowed
LL | f.use_ref();
| ----------- borrow later used here
error[E0597]: `y` does not live long enough
--> $DIR/escape-argument.rs:27:25
|
+LL | let y = 22;
+ | - binding `y` declared here
+LL | let mut closure = expect_sig(|p, y| *p = y);
LL | closure(&mut p, &y);
| ^^ borrowed value does not live long enough
LL |
error[E0597]: `a` does not live long enough
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:30:26
|
+LL | let a = 0;
+ | - binding `a` declared here
LL | let cell = Cell::new(&a);
| ^^ borrowed value does not live long enough
...
--> $DIR/closure-use-spans.rs:5:5
|
LL | let y = &x;
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
LL | x = 0;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | || *y;
| -- borrow later captured here by closure
--> $DIR/closure-use-spans.rs:11:5
|
LL | let y = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | x = 0;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | || *y = 1;
| -- borrow later captured here by closure
--> $DIR/closure-use-spans.rs:17:5
|
LL | let y = &x;
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
LL | x = 0;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | move || *y;
| -- borrow later captured here by closure
error[E0597]: `s` does not live long enough
--> $DIR/do-not-ignore-lifetime-bounds-in-copy-proj.rs:9:18
|
+LL | let s = 2;
+ | - binding `s` declared here
LL | let a = (Foo(&s),);
| ^^ borrowed value does not live long enough
LL | drop(a.0);
error[E0597]: `s` does not live long enough
--> $DIR/do-not-ignore-lifetime-bounds-in-copy.rs:8:17
|
+LL | let s = 2;
+ | - binding `s` declared here
LL | let a = Foo(&s);
| ^^ borrowed value does not live long enough
LL | drop(a);
LL | for ref mut d in v {
| - a temporary with access to the borrow is created here ...
LL | let y = ();
+ | - binding `y` declared here
LL | *d = D(&y);
| ^^ borrowed value does not live long enough
LL | }
--> $DIR/drop-no-may-dangle.rs:18:9
|
LL | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] };
- | ----- borrow of `v[_]` occurs here
+ | ----- `v[_]` is borrowed here
...
LL | v[0] += 1;
- | ^^^^^^^^^ assignment to borrowed `v[_]` occurs here
+ | ^^^^^^^^^ `v[_]` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `p` is dropped and runs the `Drop` code for type `WrapMayNotDangle`
--> $DIR/drop-no-may-dangle.rs:21:5
|
LL | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] };
- | ----- borrow of `v[_]` occurs here
+ | ----- `v[_]` is borrowed here
...
LL | v[0] += 1;
- | ^^^^^^^^^ assignment to borrowed `v[_]` occurs here
+ | ^^^^^^^^^ `v[_]` is assigned to here but it was already borrowed
LL | }
| - borrow might be used here, when `p` is dropped and runs the `Drop` code for type `WrapMayNotDangle`
--> $DIR/guarantor-issue-46974.rs:7:5
|
LL | let t = &mut *s; // this borrow should last for the entire function
- | ------- borrow of `*s` occurs here
+ | ------- `*s` is borrowed here
LL | let x = &t.0;
LL | *s = (2,);
- | ^^^^^^^^^ assignment to borrowed `*s` occurs here
+ | ^^^^^^^^^ `*s` is assigned to here but it was already borrowed
LL | *x
| -- borrow later used here
LL | if { (|| { let bar = foo; bar.take() })(); false } => {},
| ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
| |
- | move out of `foo` occurs here
+ | `foo` is moved here
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
LL | if let Some(()) = { (|| { let bar = foo; bar.take() })(); None } => {},
| ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
| |
- | move out of `foo` occurs here
+ | `foo` is moved here
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
LL | (|| { let bar = foo; bar.take() })();
| ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
| |
- | move out of `foo` occurs here
+ | `foo` is moved here
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
LL | (|| { let bar = foo; bar.take() })();
| ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
| |
- | move out of `foo` occurs here
+ | `foo` is moved here
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
LL | vecvec[0] += {
| ------
| |
- | _____borrow of `vecvec` occurs here
+ | _____`vecvec` is borrowed here
| |
LL | | vecvec = vec![];
- | | ^^^^^^ assignment to borrowed `vecvec` occurs here
+ | | ^^^^^^ `vecvec` is assigned to here but it was already borrowed
LL | |
LL | | 0
LL | | };
error[E0597]: `a` does not live long enough
--> $DIR/issue-46036.rs:8:24
|
+LL | let a = 3;
+ | - binding `a` declared here
LL | let foo = Foo { x: &a };
| ^^
| |
--> $DIR/issue-48803.rs:10:5
|
LL | let y = &x;
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
...
LL | x = "modified";
- | ^^^^^^^^^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^^^^^^^^^ `x` is assigned to here but it was already borrowed
LL |
LL | println!("{}", w); // prints "modified"
| - borrow later used here
error[E0597]: `x` does not live long enough
--> $DIR/issue-52534-2.rs:6:13
|
+LL | let x = 32;
+ | - binding `x` declared here
LL | y = &x
| ^^ borrowed value does not live long enough
LL |
error[E0597]: `tmp0` does not live long enough
--> $DIR/issue-52663-trait-object.rs:12:20
|
+LL | let tmp0 = 3;
+ | ---- binding `tmp0` declared here
LL | let tmp1 = &tmp0;
| ^^^^^ borrowed value does not live long enough
LL | Box::new(tmp1) as Box<dyn Foo + '_>
error[E0597]: `_thing1` does not live long enough
--> $DIR/issue-54382-use-span-of-tail-of-block.rs:7:29
|
+LL | let mut _thing1 = D(Box::new("thing1"));
+ | ----------- binding `_thing1` declared here
+...
LL | D("other").next(&_thing1)
| ----------------^^^^^^^^-
| | |
error[E0597]: `counter` does not live long enough
--> $DIR/issue-54556-niconii.rs:22:20
|
+LL | let counter = Mutex;
+ | ------- binding `counter` declared here
+LL |
LL | if let Ok(_) = counter.lock() { }
| ^^^^^^^^^^^^^^
| |
error[E0597]: `stmt` does not live long enough
--> $DIR/issue-54556-stephaneyfx.rs:27:21
|
+LL | let stmt = Statement;
+ | ---- binding `stmt` declared here
LL | let rows = Rows(&stmt);
| ^^^^^ borrowed value does not live long enough
LL | rows.map(|row| row).next()
error[E0597]: `_thing1` does not live long enough
--> $DIR/issue-54556-temps-in-tail-diagnostic.rs:5:11
|
+LL | let mut _thing1 = D(Box::new("thing1"));
+ | ----------- binding `_thing1` declared here
+LL | // D("other").next(&_thing1).end()
LL | D(&_thing1).end()
| --^^^^^^^^-
| | |
--> $DIR/issue-54556-used-vs-unused-tails.rs:10:55
|
LL | { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // suggest `;`
- | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
--> $DIR/issue-54556-used-vs-unused-tails.rs:13:55
|
LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } } ; // suggest `;`
- | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
--> $DIR/issue-54556-used-vs-unused-tails.rs:16:55
|
LL | { { let mut _t1 = D(Box::new("t1")); D(&_t1).end() }; } // suggest `;`
- | --^^^^- -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
--> $DIR/issue-54556-used-vs-unused-tails.rs:19:55
|
LL | let _ = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // suggest `;`
- | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
--> $DIR/issue-54556-used-vs-unused-tails.rs:22:55
|
LL | let _u = { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } ; // suggest `;`
- | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
--> $DIR/issue-54556-used-vs-unused-tails.rs:25:55
|
LL | let _x = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x`
- | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
--> $DIR/issue-54556-used-vs-unused-tails.rs:30:55
|
LL | _y = { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } ; // `let x = ...; x`
- | --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
- | | | |
- | | | `_t1` dropped here while still borrowed
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- - - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `D`
+ | | | | |
+ | | | | `_t1` dropped here while still borrowed
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
--> $DIR/issue-54556-used-vs-unused-tails.rs:37:55
|
LL | fn f_local_ref() { let mut _t1 = D(Box::new("t1")); D(&_t1).unit() } // suggest `;`
- | --^^^^- -
- | | | |
- | | | `_t1` 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`
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- -
+ | | | | |
+ | | | | `_t1` 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`
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
--> $DIR/issue-54556-used-vs-unused-tails.rs:40:55
|
LL | fn f() -> String { let mut _t1 = D(Box::new("t1")); D(&_t1).end() } // `let x = ...; x`
- | --^^^^- -
- | | | |
- | | | `_t1` 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`
- | | borrowed value does not live long enough
- | a temporary with access to the borrow is created here ...
+ | ------- --^^^^- -
+ | | | | |
+ | | | | `_t1` 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`
+ | | | borrowed value does not live long enough
+ | | a temporary with access to the borrow is created here ...
+ | binding `_t1` declared here
|
= note: the temporary is part of an expression at the end of a block;
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
--> $DIR/issue-54556-wrap-it-up.rs:27:5
|
LL | let wrap = Wrap { p: &mut x };
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | }
| - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>`
error[E0597]: `a` does not live long enough
--> $DIR/issue-55511.rs:13:28
|
+LL | let a = 22;
+ | - binding `a` declared here
LL | let b = Some(Cell::new(&a));
| ^^ borrowed value does not live long enough
...
--> $DIR/issue-57989.rs:5:5
|
LL | let g = &x;
- | -- borrow of `*x` occurs here
+ | -- `*x` is borrowed here
LL | *x = 0;
- | ^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^ `*x` is assigned to here but it was already borrowed
LL |
LL | g;
| - borrow later used here
--> $DIR/issue-68550.rs:12:20
|
LL | fn run<'a, A>(x: A)
- | -- lifetime `'a` defined here
+ | -- - binding `x` declared here
+ | |
+ | lifetime `'a` defined here
...
LL | let _: &'a A = &x;
| ----- ^^ borrowed value does not live long enough
error[E0597]: `n` does not live long enough
--> $DIR/issue-69114-static-mut-ty.rs:19:15
|
+LL | let n = 42;
+ | - binding `n` declared here
+LL | unsafe {
LL | BAR = &n;
| ------^^
| | |
error[E0597]: `n` does not live long enough
--> $DIR/issue-69114-static-mut-ty.rs:27:22
|
+LL | let n = 42;
+ | - binding `n` declared here
+LL | unsafe {
LL | BAR_ELIDED = &n;
| -------------^^
| | |
error[E0597]: `n` does not live long enough
--> $DIR/issue-69114-static-ty.rs:7:9
|
+LL | let n = 42;
+ | - binding `n` declared here
LL | FOO(&n);
| ----^^-
| | |
--> $DIR/loan_ends_mid_block_pair.rs:12:5
|
LL | let c = &mut data.0;
- | ----------- borrow of `data.0` occurs here
+ | ----------- `data.0` is borrowed here
LL | capitalize(c);
LL | data.0 = 'e';
- | ^^^^^^^^^^^^ assignment to borrowed `data.0` occurs here
+ | ^^^^^^^^^^^^ `data.0` is assigned to here but it was already borrowed
...
LL | capitalize(c);
| - borrow later used here
error[E0597]: `local` does not live long enough
--> $DIR/local-outlives-static-via-hrtb.rs:24:28
|
+LL | let local = 0;
+ | ----- binding `local` declared here
LL | assert_static_via_hrtb(&local);
| -----------------------^^^^^^-
| | |
error[E0597]: `local` does not live long enough
--> $DIR/local-outlives-static-via-hrtb.rs:25:45
|
+LL | let local = 0;
+ | ----- binding `local` declared here
+LL | assert_static_via_hrtb(&local);
LL | assert_static_via_hrtb_with_assoc_type(&&local);
| ----------------------------------------^^^^^^-
| | |
--> $DIR/match-cfg-fake-edges2.rs:8:5
|
LL | let r = &mut y.1;
- | -------- borrow of `y.1` occurs here
+ | -------- `y.1` is borrowed here
...
LL | match y {
| ^^^^^^^ use of borrowed `y.1`
LL | (|| { let bar = foo; bar.take() })();
| ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
| |
- | move out of `foo` occurs here
+ | `foo` is moved here
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
LL | (|| { let bar = foo; bar.take() })();
| ^^ --- move occurs because `foo` has type `&mut Option<&i32>`, which does not implement the `Copy` trait
| |
- | move out of `foo` occurs here
+ | `foo` is moved here
|
= note: variables bound in patterns cannot be moved from until after the end of the pattern guard
--> $DIR/match-guards-partially-borrow.rs:225:13
|
LL | s if {
- | - borrow of `t` occurs here
+ | - `t` is borrowed here
LL | t = !t;
- | ^^^^^^ assignment to borrowed `t` occurs here
+ | ^^^^^^ `t` is assigned to here but it was already borrowed
LL | false
LL | } => (), // What value should `s` have in the arm?
| - borrow later used here
--> $DIR/match-guards-partially-borrow.rs:235:13
|
LL | s if let Some(()) = {
- | - borrow of `t` occurs here
+ | - `t` is borrowed here
LL | t = !t;
- | ^^^^^^ assignment to borrowed `t` occurs here
+ | ^^^^^^ `t` is assigned to here but it was already borrowed
LL | None
LL | } => (), // What value should `s` have in the arm?
| - borrow later used here
--> $DIR/match-on-borrowed.rs:47:11
|
LL | E::V(ref mut x, _) => x,
- | --------- borrow of `e.0` occurs here
+ | --------- `e.0` is borrowed here
...
LL | match e { // Don't know that E uses a tag for its discriminant
| ^ use of borrowed `e.0`
--> $DIR/match-on-borrowed.rs:61:11
|
LL | E::V(ref mut x, _) => x,
- | --------- borrow of `f.0` occurs here
+ | --------- `f.0` is borrowed here
...
LL | match f { // Don't know that E uses a tag for its discriminant
| ^ use of borrowed `f.0`
--> $DIR/match-on-borrowed.rs:81:5
|
LL | let x = &mut t;
- | ------ borrow of `t` occurs here
+ | ------ `t` is borrowed here
LL | match t {
| ^^^^^^^ use of borrowed `t`
...
--> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:17:5
|
LL | let wrap = Wrap { p: &mut x };
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones
LL | }
| - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>`
--> $DIR/maybe-initialized-drop-with-fragment.rs:19:5
|
LL | let wrap = Wrap { p: &mut x };
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | }
| - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>`
--> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:20:5
|
LL | let wrap = Wrap { p: &mut x };
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | // FIXME ^ This currently errors and it should not.
LL | }
| - borrow might be used here, when `foo` is dropped and runs the destructor for type `Foo<'_>`
--> $DIR/maybe-initialized-drop.rs:14:5
|
LL | let wrap = Wrap { p: &mut x };
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | x = 1;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL | }
| - borrow might be used here, when `wrap` is dropped and runs the `Drop` code for type `Wrap`
--> $DIR/polonius-smoke-test.rs:12:13
|
LL | let y = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | let z = x;
| ^ use of borrowed `x`
LL | let w = y;
--> $DIR/polonius-smoke-test.rs:18:13
|
LL | pub fn use_while_mut_fr(x: &mut i32) -> &mut i32 {
- | - let's call the lifetime of this reference `'1`
+ | - - let's call the lifetime of this reference `'1`
+ | |
+ | binding `x` declared here
LL | let y = &mut *x;
| ------- borrow of `*x` occurs here
LL | let z = x;
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/polonius-smoke-test.rs:42:5
|
+LL | let s = &mut 1;
+ | - binding `s` declared here
LL | let r = &mut *s;
| ------- borrow of `*s` occurs here
LL | let tmp = foo(&r);
LL | let ptr = {
| --- borrow later stored here
LL | let l = 3;
+ | - binding `l` declared here
LL | let b = &l;
| ^^ borrowed value does not live long enough
...
--> $DIR/reference-carried-through-struct-field.rs:6:5
|
LL | let wrapper = Wrap { w: &mut x };
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | x += 1;
| ^^^^^^ use of borrowed `x`
LL | *wrapper.w += 1;
error[E0597]: `b` does not live long enough
--> $DIR/var-appears-twice.rs:20:38
|
+LL | let b = 44;
+ | - binding `b` declared here
+...
LL | let x: DoubleCell<_> = make_cell(&b);
| ------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `c` does not live long enough
--> $DIR/adt-brace-enums.rs:25:48
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | SomeEnum::SomeVariant::<&'static u32> { t: &c };
| ^^
| |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
LL | let c = 66;
+ | - binding `c` declared here
LL | SomeEnum::SomeVariant::<&'a u32> { t: &c };
| ^^
| |
error[E0597]: `c` does not live long enough
--> $DIR/adt-brace-structs.rs:23:37
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | SomeStruct::<&'static u32> { t: &c };
| ^^
| |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
LL | let c = 66;
+ | - binding `c` declared here
LL | SomeStruct::<&'a u32> { t: &c };
| ^^
| |
error[E0597]: `c` does not live long enough
--> $DIR/adt-nullary-enums.rs:33:41
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | / combine(
LL | | SomeEnum::SomeVariant(Cell::new(&c)),
| | ^^ borrowed value does not live long enough
|
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
-...
+LL | let c = 66;
+ | - binding `c` declared here
+LL | combine(
LL | SomeEnum::SomeVariant(Cell::new(&c)),
| ----------^^-
| | |
error[E0597]: `c` does not live long enough
--> $DIR/adt-tuple-enums.rs:28:43
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | SomeEnum::SomeVariant::<&'static u32>(&c);
| ^^
| |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
LL | let c = 66;
+ | - binding `c` declared here
LL | SomeEnum::SomeVariant::<&'a u32>(&c);
| ^^
| |
error[E0597]: `c` does not live long enough
--> $DIR/adt-tuple-struct-calls.rs:27:7
|
+LL | let c = 66;
+ | - binding `c` declared here
+LL | let f = SomeStruct::<&'static u32>;
LL | f(&c);
| --^^-
| | |
|
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
-...
+LL | let c = 66;
+ | - binding `c` declared here
+LL | let f = SomeStruct::<&'a u32>;
LL | f(&c);
| --^^-
| | |
error[E0597]: `c` does not live long enough
--> $DIR/adt-tuple-struct.rs:23:32
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | SomeStruct::<&'static u32>(&c);
| ^^
| |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
LL | let c = 66;
+ | - binding `c` declared here
LL | SomeStruct::<&'a u32>(&c);
| ^^
| |
error[E0597]: `x` does not live long enough
--> $DIR/cast_static_lifetime.rs:5:19
|
+LL | let x = 22_u32;
+ | - binding `x` declared here
LL | let y: &u32 = (&x) as &'static u32;
| ^^^^----------------
| |
error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:23:9
|
+LL | let x = ();
+ | - binding `x` declared here
LL | FUN(&x);
| ----^^-
| | |
error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:24:23
|
+LL | let x = ();
+ | - binding `x` declared here
+LL | FUN(&x);
LL | A::ASSOCIATED_FUN(&x);
| ------------------^^-
| | |
error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:25:28
|
+LL | let x = ();
+ | - binding `x` declared here
+...
LL | B::ALSO_ASSOCIATED_FUN(&x);
| -----------------------^^-
| | |
error[E0597]: `x` does not live long enough
--> $DIR/constant-in-expr-inherent-2.rs:26:31
|
+LL | let x = ();
+ | - binding `x` declared here
+...
LL | <_>::TRAIT_ASSOCIATED_FUN(&x);
| --------------------------^^-
| | |
error[E0597]: `c` does not live long enough
--> $DIR/fns.rs:23:29
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | some_fn::<&'static u32>(&c);
| ------------------------^^-
| | |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
LL | let c = 66;
+ | - binding `c` declared here
LL | some_fn::<&'a u32>(&c);
| -------------------^^-
| | |
error[E0597]: `c` does not live long enough
--> $DIR/method-call.rs:36:34
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | a.method::<&'static u32>(b, &c);
| -----------------------------^^-
| | |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
...
+LL | let c = 66;
+ | - binding `c` declared here
LL | a.method::<&'a u32>(b, &c);
| ------------------------^^-
| | |
error[E0597]: `a` does not live long enough
--> $DIR/method-ufcs-1.rs:30:7
|
+LL | let a = 22;
+ | - binding `a` declared here
+...
LL | x(&a, b, c);
| --^^-------
| | |
|
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
+LL | let a = 22;
+ | - binding `a` declared here
...
LL | <&'a u32 as Bazoom<_>>::method(&a, b, c);
| -------------------------------^^-------
error[E0597]: `a` does not live long enough
--> $DIR/method-ufcs-2.rs:30:7
|
+LL | let a = 22;
+ | - binding `a` declared here
+...
LL | x(&a, b, c);
| --^^-------
| | |
|
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
-...
+LL | let a = 22;
+LL | let b = 44;
+ | - binding `b` declared here
+LL | let c = 66;
LL | <_ as Bazoom<&'a u32>>::method(a, &b, c);
| ----------------------------------^^----
| | |
error[E0597]: `c` does not live long enough
--> $DIR/method-ufcs-3.rs:36:53
|
+LL | let c = 66;
+ | - binding `c` declared here
LL | <_ as Bazoom<_>>::method::<&'static u32>(&a, b, &c);
| ------------------------------------------------^^-
| | |
LL | fn annot_reference_named_lifetime<'a>(_d: &'a u32) {
| -- lifetime `'a` defined here
...
+LL | let c = 66;
+ | - binding `c` declared here
LL | <_ as Bazoom<_>>::method::<&'a u32>(&a, b, &c);
| -------------------------------------------^^-
| | |
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let v = 22;
+ | - binding `v` declared here
LL | let x = A::<'a>::new(&v, 22);
| -------------^^-----
| | |
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let v = 22;
+ | - binding `v` declared here
LL | let x = A::<'a>::new::<&'a u32>(&v, &v);
| ------------------------^^-----
| | |
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let v = 22;
+ | - binding `v` declared here
LL | let x = A::<'a>::new::<&'a u32>(&v, &v);
| ----------------------------^^-
| | |
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let v = 22;
+ | - binding `v` declared here
LL | let x = <A<'a>>::new(&v, 22);
| -------------^^-----
| | |
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let v = 22;
+ | - binding `v` declared here
LL | let x = <A<'a>>::new::<&'a u32>(&v, &v);
| ------------------------^^-----
| | |
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let v = 22;
+ | - binding `v` declared here
LL | let x = <A<'a>>::new::<&'a u32>(&v, &v);
| ----------------------------^^-
| | |
error[E0597]: `a` does not live long enough
--> $DIR/normalization.rs:10:31
|
+LL | let a = 22;
+ | - binding `a` declared here
LL | let _: <() as Foo>::Out = &a;
| ---------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `a` does not live long enough
--> $DIR/normalization.rs:13:40
|
+LL | let a = 22;
+ | - binding `a` declared here
LL | let _: <&'static () as Foo>::Out = &a;
| ------------------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_enum_variant.rs:7:33
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo::Bar { field: &y };
| ^^ borrowed value does not live long enough
LL |
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_enum_variant.rs:14:33
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo::Bar { field: &y };
| ^^ borrowed value does not live long enough
...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_struct.rs:5:28
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo { field: &y };
| ^^ borrowed value does not live long enough
LL |
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_brace_struct.rs:12:28
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo { field: &y };
| ^^ borrowed value does not live long enough
...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_enum_variant.rs:7:24
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo::Bar(&y);
| ^^ borrowed value does not live long enough
LL |
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_enum_variant.rs:14:24
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo::Bar(&y);
| ^^ borrowed value does not live long enough
...
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_struct.rs:5:19
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo(&y);
| ^^ borrowed value does not live long enough
LL |
error[E0597]: `y` does not live long enough
--> $DIR/pattern_substs_on_tuple_struct.rs:12:19
|
+LL | let y = 22;
+ | - binding `y` declared here
LL | let foo = Foo(&y);
| ^^ borrowed value does not live long enough
...
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:6:9
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let y: &'static u32;
| ------------ type annotation requires that `x` is borrowed for `'static`
LL | y = &x;
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:14:9
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let (y, z): (&'static u32, &'static u32);
| ---------------------------- type annotation requires that `x` is borrowed for `'static`
LL | y = &x;
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:20:13
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let y = &x;
| ^^ borrowed value does not live long enough
LL | let ref z: &'static u32 = y;
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:39:9
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let Single { value: y }: Single<&'static u32>;
| -------------------- type annotation requires that `x` is borrowed for `'static`
LL | y = &x;
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:51:10
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let Single2 { value: mut _y }: Single2<StaticU32>;
| ------------------ type annotation requires that `x` is borrowed for `'static`
LL | _y = &x;
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:56:27
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let y: &'static u32 = &x;
| ------------ ^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:61:27
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let _: &'static u32 = &x;
| ------------ ^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:75:40
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let (_, _): (&'static u32, u32) = (&x, 44);
| ------------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:80:40
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let (y, _): (&'static u32, u32) = (&x, 44);
| ------------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:85:69
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let Single { value: y }: Single<&'static u32> = Single { value: &x };
| -------------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:90:69
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let Single { value: _ }: Single<&'static u32> = Single { value: &x };
| -------------------- ^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/patterns.rs:98:17
|
+LL | let x = 22;
+ | - binding `x` declared here
LL | let Double { value1: _, value2: _ }: Double<&'static u32> = Double {
| -------------------- type annotation requires that `x` is borrowed for `'static`
LL | value1: &x,
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let x = 0;
+ | - binding `x` declared here
LL | let f = &drop::<&'a i32>;
| ---------------- assignment requires that `x` is borrowed for `'a`
LL | f(&x);
error[E0597]: `x` does not live long enough
--> $DIR/type_ascription_static_lifetime.rs:6:33
|
+LL | let x = 22_u32;
+ | - binding `x` declared here
LL | let y: &u32 = type_ascribe!(&x, &'static u32);
| --------------^^---------------
| | |
+++ /dev/null
-// Issue #66530: We would ICE if someone compiled with `-o /dev/null`,
-// because we would try to generate auxiliary files in `/dev/` (which
-// at least the OS X file system rejects).
-//
-// An attempt to `-o` into a directory we cannot write into should indeed
-// be an error; but not an ICE.
-//
-// However, some folks run tests as root, which can write `/dev/` and end
-// up clobbering `/dev/null`. Instead we'll use a non-existent path, which
-// also used to ICE, but even root can't magically write there.
-
-// compile-flags: -o /does-not-exist/output
-
-// The error-pattern check occurs *before* normalization, and the error patterns
-// are wildly different between build environments. So this is a cop-out (and we
-// rely on the checking of the normalized stderr output as our actual
-// "verification" of the diagnostic).
-
-// error-pattern: error
-
-// On Mac OS X, we get an error like the below
-// normalize-stderr-test "failed to write bytecode to /does-not-exist/output.non_ice_error_on_worker_io_fail.*" -> "io error modifying /does-not-exist/"
-
-// On Linux, we get an error like the below
-// normalize-stderr-test "couldn't create a temp dir.*" -> "io error modifying /does-not-exist/"
-
-// ignore-windows - this is a unix-specific test
-// ignore-emscripten - the file-system issues do not replicate here
-// ignore-wasm - the file-system issues do not replicate here
-// ignore-arm - the file-system issues do not replicate here, at least on armhf-gnu
-
-#![crate_type="lib"]
-
-#![cfg_attr(not(feature = "std"), no_std)]
-pub mod task {
- pub mod __internal {
- use crate::task::Waker;
- }
- pub use core::task::Waker;
-}
+++ /dev/null
-warning: ignoring --out-dir flag due to -o flag
-
-error: io error modifying /does-not-exist/
-
-error: aborting due to previous error; 1 warning emitted
-
--- /dev/null
+fn foo(x: bool | i32) -> i32 | f64 {
+//~^ ERROR anonymous enums are not supported
+//~| ERROR anonymous enums are not supported
+ match x {
+ x: i32 => x, //~ ERROR expected
+ true => 42.,
+ false => 0.333,
+ }
+}
+
+fn main() {
+ match foo(true) {
+ 42: i32 => (), //~ ERROR expected
+ _: f64 => (), //~ ERROR expected
+ x: i32 => (), //~ ERROR expected
+ }
+}
--- /dev/null
+error: anonymous enums are not supported
+ --> $DIR/anon-enums.rs:1:16
+ |
+LL | fn foo(x: bool | i32) -> i32 | f64 {
+ | ---- ^ ---
+ |
+ = help: create a named `enum` and use it here instead:
+ enum Name {
+ Variant1(bool),
+ Variant2(i32),
+ }
+
+error: anonymous enums are not supported
+ --> $DIR/anon-enums.rs:1:30
+ |
+LL | fn foo(x: bool | i32) -> i32 | f64 {
+ | --- ^ ---
+ |
+ = help: create a named `enum` and use it here instead:
+ enum Name {
+ Variant1(i32),
+ Variant2(f64),
+ }
+
+error: expected one of `@` or `|`, found `:`
+ --> $DIR/anon-enums.rs:5:10
+ |
+LL | x: i32 => x,
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected one of `@` or `|`
+ |
+help: maybe write a path separator here
+ |
+LL | x::i32 => x,
+ | ~~
+
+error: expected one of `...`, `..=`, `..`, or `|`, found `:`
+ --> $DIR/anon-enums.rs:13:11
+ |
+LL | 42: i32 => (),
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected one of `...`, `..=`, `..`, or `|`
+
+error: expected `|`, found `:`
+ --> $DIR/anon-enums.rs:14:10
+ |
+LL | _: f64 => (),
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected `|`
+
+error: expected one of `@` or `|`, found `:`
+ --> $DIR/anon-enums.rs:15:10
+ |
+LL | x: i32 => (),
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected one of `@` or `|`
+ |
+help: maybe write a path separator here
+ |
+LL | x::i32 => (),
+ | ~~
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+#![feature(let_chains)]
+trait Demo {}
+
+impl dyn Demo {
+ pub fn report(&self) -> u32 {
+ let sum = |a: u32,
+ b: u32,
+ c: u32| {
+ a + b + c
+ };
+ sum(1, 2, 3)
+ }
+
+ fn check(&self, val: Option<u32>, num: Option<u32>) {
+ if let Some(b) = val
+ && let Some(c) = num {
+ && b == c {
+ //~^ ERROR expected struct
+ //~| ERROR mismatched types
+ }
+ }
+}
+
+fn main() { } //~ ERROR this file contains an unclosed delimiter
--- /dev/null
+error: this file contains an unclosed delimiter
+ --> $DIR/deli-ident-issue-1.rs:24:66
+ |
+LL | impl dyn Demo {
+ | - unclosed delimiter
+...
+LL | && let Some(c) = num {
+ | - this delimiter might not be properly closed...
+...
+LL | }
+ | - ...as it matches this but it has different indentation
+...
+LL | fn main() { }
+ | ^
+
+error[E0574]: expected struct, variant or union type, found local variable `c`
+ --> $DIR/deli-ident-issue-1.rs:17:17
+ |
+LL | && b == c {
+ | ^ not a struct, variant or union type
+
+error[E0308]: mismatched types
+ --> $DIR/deli-ident-issue-1.rs:17:9
+ |
+LL | fn check(&self, val: Option<u32>, num: Option<u32>) {
+ | - expected `()` because of default return type
+...
+LL | / && b == c {
+LL | |
+LL | |
+LL | | }
+ | |_________^ expected `()`, found `bool`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0574.
+For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+fn main() {
+ if 1 < 2 {
+ let _a = vec!]; //~ ERROR mismatched closing delimiter
+ }
+} //~ ERROR unexpected closing delimiter
+
+fn main() {}
--- /dev/null
+error: unexpected closing delimiter: `}`
+ --> $DIR/deli-ident-issue-2.rs:5:1
+ |
+LL | let _a = vec!];
+ | - missing open `[` for this delimiter
+LL | }
+LL | }
+ | ^ unexpected closing delimiter
+
+error: mismatched closing delimiter: `]`
+ --> $DIR/deli-ident-issue-2.rs:2:14
+ |
+LL | if 1 < 2 {
+ | ^ unclosed delimiter
+LL | let _a = vec!];
+ | ^ mismatched closing delimiter
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// build-pass
+macro_rules! check_ty {
+ ($Z:ty) => { compile_error!("triggered"); };
+ ($X:ty | $Y:ty) => { $X };
+}
+
+macro_rules! check {
+ ($Z:ty) => { compile_error!("triggered"); };
+ ($X:ty | $Y:ty) => { };
+}
+
+check! { i32 | u8 }
+
+fn foo(x: check_ty! { i32 | u8 }) -> check_ty! { i32 | u8 } {
+ x
+}
+fn main() {
+ let x: check_ty! { i32 | u8 } = 42;
+ let _: check_ty! { i32 | u8 } = foo(x);
+}
--- /dev/null
+// This file has unexpected closing delimiter,
+
+fn func(o: Option<u32>) {
+ match o {
+ Some(_x) => {} // Extra '}'
+ let _ = if true {};
+ }
+ None => {}
+ }
+} //~ ERROR unexpected closing delimiter
+
+fn main() {}
--- /dev/null
+error: unexpected closing delimiter: `}`
+ --> $DIR/issue-68987-unmatch-issue-1.rs:10:1
+ |
+LL | match o {
+ | - this delimiter might not be properly closed...
+LL | Some(_x) => {} // Extra '}'
+ | -- block is empty, you might have not meant to close it
+LL | let _ = if true {};
+LL | }
+ | - ...as it matches this but it has different indentation
+...
+LL | }
+ | ^ unexpected closing delimiter
+
+error: aborting due to previous error
+
--- /dev/null
+// FIXME: this case need more work to fix
+// currently the TokenTree matching ')' with '{', which is not user friendly for diagnostics
+async fn obstest() -> Result<> {
+ let obs_connect = || -> Result<(), MyError) { //~ ERROR mismatched closing delimiter
+ async {
+ }
+ }
+
+ if let Ok(version, scene_list) = obs_connect() {
+
+ } else {
+
+ }
+} //~ ERROR unexpected closing delimiter
--- /dev/null
+error: unexpected closing delimiter: `}`
+ --> $DIR/issue-68987-unmatch-issue-2.rs:14:1
+ |
+LL | let obs_connect = || -> Result<(), MyError) {
+ | - missing open `(` for this delimiter
+...
+LL | }
+ | ^ unexpected closing delimiter
+
+error: mismatched closing delimiter: `)`
+ --> $DIR/issue-68987-unmatch-issue-2.rs:3:32
+ |
+LL | async fn obstest() -> Result<> {
+ | ^ unclosed delimiter
+LL | let obs_connect = || -> Result<(), MyError) {
+ | ^ mismatched closing delimiter
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// the `{` is closed with `)`, there is a missing `(`
+fn f(i: u32, j: u32) {
+ let res = String::new();
+ let mut cnt = i;
+ while cnt < j {
+ write!&mut res, " "); //~ ERROR mismatched closing delimiter
+ }
+} //~ ERROR unexpected closing delimiter
--- /dev/null
+error: unexpected closing delimiter: `}`
+ --> $DIR/issue-68987-unmatch-issue-3.rs:8:1
+ |
+LL | write!&mut res, " ");
+ | - missing open `(` for this delimiter
+LL | }
+LL | }
+ | ^ unexpected closing delimiter
+
+error: mismatched closing delimiter: `)`
+ --> $DIR/issue-68987-unmatch-issue-3.rs:5:19
+ |
+LL | while cnt < j {
+ | ^ unclosed delimiter
+LL | write!&mut res, " ");
+ | ^ mismatched closing delimiter
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// This file has unexpected closing delimiter,
+
+fn func(o: Option<u32>) {
+ match o {
+ Some(_x) => // Missing '{'
+ let _ = if true {};
+ }
+ None => {}
+ }
+} //~ ERROR unexpected closing delimiter
+
+fn main() {}
--- /dev/null
+error: unexpected closing delimiter: `}`
+ --> $DIR/issue-68987-unmatch-issue.rs:10:1
+ |
+LL | match o {
+ | - this delimiter might not be properly closed...
+LL | Some(_x) => // Missing '{'
+LL | let _ = if true {};
+ | -- block is empty, you might have not meant to close it
+LL | }
+ | - ...as it matches this but it has different indentation
+...
+LL | }
+ | ^ unexpected closing delimiter
+
+error: aborting due to previous error
+
--> $DIR/issue-81827.rs:11:27
|
LL | fn r()->i{0|{#[cfg(r(0{]0
- | - - ^
- | | |
+ | - - - ^
+ | | | |
+ | | | missing open `[` for this delimiter
| | unclosed delimiter
| unclosed delimiter
--> $DIR/issue-81827.rs:11:27
|
LL | fn r()->i{0|{#[cfg(r(0{]0
- | - - ^
- | | |
+ | - - - ^
+ | | | |
+ | | | missing open `[` for this delimiter
| | unclosed delimiter
| unclosed delimiter
= note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
help: if `bar` is a struct, use braces as delimiters
|
-LL | bar { }
- | ~
+LL | bar { baz: $rest }
+ | ~ ~
help: if `bar` is a function, use the arguments directly
|
LL - bar(baz: $rest)
--> $DIR/issue-62973.rs:8:2
|
LL | fn p() { match s { v, E { [) {) }
- | - - unclosed delimiter
- | |
+ | - - - - missing open `(` for this delimiter
+ | | | |
+ | | | missing open `(` for this delimiter
+ | | unclosed delimiter
| unclosed delimiter
LL |
LL |
--> $DIR/issue-62973.rs:8:2
|
LL | fn p() { match s { v, E { [) {) }
- | - - unclosed delimiter
- | |
+ | - - - - missing open `(` for this delimiter
+ | | | |
+ | | | missing open `(` for this delimiter
+ | | unclosed delimiter
| unclosed delimiter
LL |
LL |
--> $DIR/issue-63116.rs:3:18
|
LL | impl W <s(f;Y(;]
- | - ^
- | |
+ | - - ^
+ | | |
+ | | missing open `[` for this delimiter
| unclosed delimiter
error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `;`
--- /dev/null
+fn main() {}
+
+fn f) {} //~ ERROR unexpected closing delimiter
--- /dev/null
+error: unexpected closing delimiter: `)`
+ --> $DIR/issue-69259.rs:3:5
+ |
+LL | fn f) {}
+ | ^ unexpected closing delimiter
+
+error: aborting due to previous error
+
--> $DIR/issue-70583-block-is-empty-1.rs:20:1
|
LL | fn struct_generic(x: Vec<i32>) {
- | - this opening brace...
+ | - this delimiter might not be properly closed...
...
LL | }
- | - ...matches this closing brace
+ | - ...as it matches this but it has different indentation
LL | }
| ^ unexpected closing delimiter
error: unexpected closing delimiter: `}`
--> $DIR/issue-70583-block-is-empty-2.rs:14:1
|
+LL | match self {
+ | - this delimiter might not be properly closed...
LL | ErrorHandled::Reported => {}}
- | -- block is empty, you might have not meant to close it
+ | --- ...as it matches this but it has different indentation
+ | |
+ | block is empty, you might have not meant to close it
...
LL | }
| ^ unexpected closing delimiter
Foo:Bar::Baz => {}
//~^ ERROR: expected one of
//~| HELP: maybe write a path separator here
- //~| ERROR: failed to resolve: `Bar` is a variant, not a module
}
match myfoo {
Foo::Bar => {}
--> $DIR/issue-87086-colon-path-sep.rs:17:12
|
LL | Foo:Bar => {}
- | ^
+ | ^--- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | Foo::Bar => {}
+ | ~~
error: expected one of `!`, `(`, `...`, `..=`, `..`, `::`, `{`, or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:23:17
|
LL | qux::Foo:Bar => {}
- | ^
+ | ^--- specifying the type of a pattern isn't supported
| |
| expected one of 8 possible tokens
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | qux::Foo::Bar => {}
+ | ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:29:12
|
LL | qux:Foo::Baz => {}
- | ^
+ | ^-------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | qux::Foo::Baz => {}
+ | ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:35:12
|
LL | qux: Foo::Baz if true => {}
- | ^
+ | ^ -------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | qux::Foo::Baz if true => {}
+ | ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:40:15
|
LL | if let Foo:Bar = f() {
- | ^
+ | ^--- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | if let Foo::Bar = f() {
+ | ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:48:16
|
LL | ref qux: Foo::Baz => {}
- | ^
+ | ^ -------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | ref qux::Foo::Baz => {}
+ | ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:57:16
|
LL | mut qux: Foo::Baz => {}
- | ^
+ | ^ -------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | mut qux::Foo::Baz => {}
+ | ~~
error: expected one of `@` or `|`, found `:`
--> $DIR/issue-87086-colon-path-sep.rs:68:12
|
LL | Foo:Bar::Baz => {}
- | ^
+ | ^-------- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
+ |
+help: maybe write a path separator here
+ |
+LL | Foo::Bar::Baz => {}
+ | ~~
error: expected one of `@` or `|`, found `:`
- --> $DIR/issue-87086-colon-path-sep.rs:75:12
+ --> $DIR/issue-87086-colon-path-sep.rs:74:12
|
LL | Foo:Bar => {}
- | ^
+ | ^--- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
- | help: maybe write a path separator here: `::`
-
-error[E0433]: failed to resolve: `Bar` is a variant, not a module
- --> $DIR/issue-87086-colon-path-sep.rs:68:13
|
-LL | Foo:Bar::Baz => {}
- | ^^^ `Bar` is a variant, not a module
+help: maybe write a path separator here
+ |
+LL | Foo::Bar => {}
+ | ~~
-error: aborting due to 10 previous errors
+error: aborting due to 9 previous errors
-For more information about this error, try `rustc --explain E0433`.
--> $DIR/macro-mismatched-delim-paren-brace.rs:5:1
|
LL | fn main() {
- | - this opening brace...
+ | - this delimiter might not be properly closed...
...
LL | }
- | - ...matches this closing brace
+ | - ...as it matches this but it has different indentation
LL | }
| ^ unexpected closing delimiter
--- /dev/null
+// run-rustfix
+
+fn main() {
+ 'label: loop { break 'label }; //~ error: cannot find value `label` in this scope
+ 'label: loop { break 'label 0 }; //~ error: expected a label, found an identifier
+ 'label: loop { continue 'label }; //~ error: expected a label, found an identifier
+}
--- /dev/null
+// run-rustfix
+
+fn main() {
+ 'label: loop { break label }; //~ error: cannot find value `label` in this scope
+ 'label: loop { break label 0 }; //~ error: expected a label, found an identifier
+ 'label: loop { continue label }; //~ error: expected a label, found an identifier
+}
--- /dev/null
+error: expected a label, found an identifier
+ --> $DIR/recover-unticked-labels.rs:5:26
+ |
+LL | 'label: loop { break label 0 };
+ | ^^^^^ help: labels start with a tick: `'label`
+
+error: expected a label, found an identifier
+ --> $DIR/recover-unticked-labels.rs:6:29
+ |
+LL | 'label: loop { continue label };
+ | ^^^^^ help: labels start with a tick: `'label`
+
+error[E0425]: cannot find value `label` in this scope
+ --> $DIR/recover-unticked-labels.rs:4:26
+ |
+LL | 'label: loop { break label };
+ | ------ ^^^^^
+ | | |
+ | | not found in this scope
+ | | help: use the similarly named label: `'label`
+ | a label with a similar name exists
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds
+fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {} //~ ERROR incorrect braces around trait bounds
+
fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{`
//~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `for`, `~`, lifetime, or path, found `{`
//~| ERROR at least one trait is required for an object type
help: remove the parentheses
|
LL - fn foo2(_: &dyn (Drop + AsRef<str>)) {}
-LL + fn foo2(_: &dyn Drop + AsRef<str>) {}
+LL + fn foo2(_: &dyn Drop + AsRef<str>) {}
+ |
+
+error: incorrect braces around trait bounds
+ --> $DIR/trait-object-delimiters.rs:8:25
+ |
+LL | fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {}
+ | ^ ^
+ |
+help: remove the parentheses
+ |
+LL - fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {}
+LL + fn foo2_no_space(_: &dyn Drop + AsRef<str>) {}
|
error: expected parameter name, found `{`
- --> $DIR/trait-object-delimiters.rs:8:17
+ --> $DIR/trait-object-delimiters.rs:10:17
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| ^ expected parameter name
error: expected one of `!`, `(`, `)`, `*`, `,`, `?`, `for`, `~`, lifetime, or path, found `{`
- --> $DIR/trait-object-delimiters.rs:8:17
+ --> $DIR/trait-object-delimiters.rs:10:17
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| -^ expected one of 10 possible tokens
| help: missing `,`
error: expected identifier, found `<`
- --> $DIR/trait-object-delimiters.rs:12:17
+ --> $DIR/trait-object-delimiters.rs:14:17
|
LL | fn foo4(_: &dyn <Drop + AsRef<str>>) {}
| ^ expected identifier
error: invalid `dyn` keyword
- --> $DIR/trait-object-delimiters.rs:14:25
+ --> $DIR/trait-object-delimiters.rs:16:25
|
LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
| ^^^ help: remove this keyword
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
error[E0224]: at least one trait is required for an object type
- --> $DIR/trait-object-delimiters.rs:8:13
+ --> $DIR/trait-object-delimiters.rs:10:13
|
LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
| ^^^
error[E0225]: only auto traits can be used as additional traits in a trait object
- --> $DIR/trait-object-delimiters.rs:14:29
+ --> $DIR/trait-object-delimiters.rs:16:29
|
LL | fn foo5(_: &(dyn Drop + dyn AsRef<str>)) {}
| ---- ^^^^^^^^^^ additional non-auto trait
= help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Drop + AsRef<str> {}`
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
Some errors have detailed explanations: E0224, E0225.
For more information about an error, try `rustc --explain E0224`.
let y = 0;
//~^ ERROR unknown start of token: \u{37e}
//~^^ HELP Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not
+ let x = 0;
+ //~^ ERROR unknown start of token: \u{a0}
+ //~^^ NOTE character appears 3 more times
+ //~^^^ HELP Unicode character ' ' (No-Break Space) looks like ' ' (Space), but it is not
+ let _ = 1 ⩵ 2;
+ //~^ ERROR unknown start of token
+ //~^^ HELP Unicode character '⩵' (Two Consecutive Equals Signs) looks like '==' (Double Equals Sign), but it is not
}
LL | let y = 0;
| ~
-error: aborting due to previous error
+error: unknown start of token: \u{a0}
+ --> $DIR/unicode-chars.rs:5:5
+ |
+LL | let x = 0;
+ | ^^^^
+ |
+ = note: character appears 3 more times
+help: Unicode character ' ' (No-Break Space) looks like ' ' (Space), but it is not
+ |
+LL | let x = 0;
+ | ++++
+
+error: unknown start of token: \u{2a75}
+ --> $DIR/unicode-chars.rs:9:15
+ |
+LL | let _ = 1 ⩵ 2;
+ | ^
+ |
+help: Unicode character '⩵' (Two Consecutive Equals Signs) looks like '==' (Double Equals Sign), but it is not
+ |
+LL | let _ = 1 == 2;
+ | ~~
+
+error: aborting due to 3 previous errors
LL | Some(ref _y @ _z) => {}
| ------^^^--
| | |
- | | value moved into `_z` here
- | value borrowed, by `_y`, here
+ | | value is moved into `_z` here
+ | value is borrowed by `_y` here
error: borrow of moved value
--> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:19:14
LL | Some(ref mut _y @ _z) => {}
| ----------^^^--
| | |
- | | value moved into `_z` here
- | value borrowed, by `_y`, here
+ | | value is moved into `_z` here
+ | value is mutably borrowed by `_y` here
error: borrow of moved value
--> $DIR/bind-by-move-neither-can-live-while-the-other-survives-1.rs:33:14
LL | let ref a @ box b = Box::new(NC);
| -----^^^^^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-at-and-box.rs:34:9
LL | let ref a @ box ref mut b = Box::new(nc());
| -----^^^^^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-at-and-box.rs:36:9
LL | let ref a @ box ref mut b = Box::new(NC);
| -----^^^^^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-at-and-box.rs:38:9
LL | let ref a @ box ref mut b = Box::new(NC);
| -----^^^^^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-at-and-box.rs:42:9
LL | let ref a @ box ref mut b = Box::new(NC);
| -----^^^^^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-at-and-box.rs:48:9
LL | let ref mut a @ box ref b = Box::new(NC);
| ---------^^^^^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-at-and-box.rs:62:9
LL | ref mut a @ box ref b => {
| ---------^^^^^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-at-and-box.rs:54:11
LL | fn f5(ref mut a @ box ref b: Box<NC>) {
| ---------^^^^^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error[E0382]: borrow of moved value
--> $DIR/borrowck-pat-at-and-box.rs:31:9
LL | let ref a @ b = U;
| -----^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:26:9
LL | let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
| -----^^^^^^^^^^^^-----^^^^^^^^^^-^
| | | |
- | | | value moved into `e` here
- | | value moved into `c` here
- | value borrowed, by `a`, here
+ | | | value is moved into `e` here
+ | | value is moved into `c` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:26:18
LL | let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
| -----^^^-----
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is borrowed by `b` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:26:33
LL | let ref a @ (ref b @ mut c, ref d @ e) = (U, U);
| -----^^^-
| | |
- | | value moved into `e` here
- | value borrowed, by `d`, here
+ | | value is moved into `e` here
+ | value is borrowed by `d` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:30:9
LL | let ref mut a @ [b, mut c] = [U, U];
| ---------^^^^-^^-----^
| | | |
- | | | value moved into `c` here
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | | value is moved into `c` here
+ | | value is moved into `b` here
+ | value is mutably borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:33:9
LL | let ref a @ b = u();
| -----^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:36:9
LL | let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
| -----^^^^^^^^^^^^-----^^^^^^^^^^-^
| | | |
- | | | value moved into `e` here
- | | value moved into `c` here
- | value borrowed, by `a`, here
+ | | | value is moved into `e` here
+ | | value is moved into `c` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:36:18
LL | let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
| -----^^^-----
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is borrowed by `b` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:36:33
LL | let ref a @ (ref b @ mut c, ref d @ e) = (u(), u());
| -----^^^-
| | |
- | | value moved into `e` here
- | value borrowed, by `d`, here
+ | | value is moved into `e` here
+ | value is borrowed by `d` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:42:9
LL | let ref mut a @ [b, mut c] = [u(), u()];
| ---------^^^^-^^-----^
| | | |
- | | | value moved into `c` here
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | | value is moved into `c` here
+ | | value is moved into `b` here
+ | value is mutably borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:47:9
LL | ref a @ Some(b) => {}
| -----^^^^^^^^-^
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:52:9
LL | ref a @ Some((ref b @ mut c, ref d @ e)) => {}
| -----^^^^^^^^^^^^^^^^^-----^^^^^^^^^^-^^
| | | |
- | | | value moved into `e` here
- | | value moved into `c` here
- | value borrowed, by `a`, here
+ | | | value is moved into `e` here
+ | | value is moved into `c` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:52:23
LL | ref a @ Some((ref b @ mut c, ref d @ e)) => {}
| -----^^^-----
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is borrowed by `b` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:52:38
LL | ref a @ Some((ref b @ mut c, ref d @ e)) => {}
| -----^^^-
| | |
- | | value moved into `e` here
- | value borrowed, by `d`, here
+ | | value is moved into `e` here
+ | value is borrowed by `d` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:59:9
LL | ref mut a @ Some([b, mut c]) => {}
| ---------^^^^^^^^^-^^-----^^
| | | |
- | | | value moved into `c` here
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | | value is moved into `c` here
+ | | value is moved into `b` here
+ | value is mutably borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:64:9
LL | ref a @ Some(b) => {}
| -----^^^^^^^^-^
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:69:9
LL | ref a @ Some((ref b @ mut c, ref d @ e)) => {}
| -----^^^^^^^^^^^^^^^^^-----^^^^^^^^^^-^^
| | | |
- | | | value moved into `e` here
- | | value moved into `c` here
- | value borrowed, by `a`, here
+ | | | value is moved into `e` here
+ | | value is moved into `c` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:69:23
LL | ref a @ Some((ref b @ mut c, ref d @ e)) => {}
| -----^^^-----
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is borrowed by `b` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:69:38
LL | ref a @ Some((ref b @ mut c, ref d @ e)) => {}
| -----^^^-
| | |
- | | value moved into `e` here
- | value borrowed, by `d`, here
+ | | value is moved into `e` here
+ | value is borrowed by `d` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:78:9
LL | ref mut a @ Some([b, mut c]) => {}
| ---------^^^^^^^^^-^^-----^^
| | | |
- | | | value moved into `c` here
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | | value is moved into `c` here
+ | | value is moved into `b` here
+ | value is mutably borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:11:11
LL | fn f1(ref a @ b: U) {}
| -----^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:14:11
LL | fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
| -----^^^^^^^^^^^^-----^^^^^^^^^^-^
| | | |
- | | | value moved into `e` here
- | | value moved into `c` here
- | value borrowed, by `a`, here
+ | | | value is moved into `e` here
+ | | value is moved into `c` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:14:20
LL | fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
| -----^^^-----
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is borrowed by `b` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:14:35
LL | fn f2(ref a @ (ref b @ mut c, ref d @ e): (U, U)) {}
| -----^^^-
| | |
- | | value moved into `e` here
- | value borrowed, by `d`, here
+ | | value is moved into `e` here
+ | value is borrowed by `d` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-by-move-and-ref.rs:20:11
LL | fn f3(ref mut a @ [b, mut c]: [U; 2]) {}
| ---------^^^^-^^-----^
| | | |
- | | | value moved into `c` here
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | | value is moved into `c` here
+ | | value is moved into `b` here
+ | value is mutably borrowed by `a` here
error[E0382]: borrow of partially moved value
--> $DIR/borrowck-pat-by-move-and-ref.rs:30:9
LL | ref mut z @ &mut Some(ref a) => {
| ---------^^^^^^^^^^^^^-----^
| | |
- | | immutable borrow, by `a`, occurs here
- | mutable borrow, by `z`, occurs here
+ | | value is borrowed by `a` here
+ | value is mutably borrowed by `z` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:33:9
LL | let ref mut a @ (ref b @ ref mut c) = u(); // sub-in-sub
| ---------^^^^-----------------^
| | | |
- | | | another mutable borrow, by `c`, occurs here
- | | also borrowed as immutable, by `b`, here
- | first mutable borrow, by `a`, occurs here
+ | | | value is mutably borrowed by `c` here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:33:22
LL | let ref mut a @ (ref b @ ref mut c) = u(); // sub-in-sub
| -----^^^---------
| | |
- | | mutable borrow, by `c`, occurs here
- | immutable borrow, by `b`, occurs here
+ | | value is mutably borrowed by `c` here
+ | value is borrowed by `b` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:37:9
LL | let ref a @ ref mut b = U;
| -----^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:39:9
LL | let ref mut a @ ref b = U;
| ---------^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:41:9
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| | | |
- | | | mutable borrow, by `c`, occurs here
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | | value is mutably borrowed by `c` here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:43:9
LL | let ref mut a @ (ref b, ref c) = (U, U);
| ---------^^^^-----^^-----^
| | | |
- | | | immutable borrow, by `c`, occurs here
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | | value is borrowed by `c` here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:46:9
LL | let ref mut a @ ref b = u();
| ---------^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:51:9
LL | let ref a @ ref mut b = u();
| -----^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:57:9
LL | let ref mut a @ ref b = U;
| ---------^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:61:9
LL | let ref a @ ref mut b = U;
| -----^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:67:9
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) => {
| ---------^^^^^^-----^
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:67:33
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) => {
| ---------^^^^^^^-----^
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:76:9
LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
| -----^^^^^^---------^
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:76:33
LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) => {
| -----^^^^^^^---------^
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:87:9
LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { *b = U; false } => {}
| -----^^^^^^---------^
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:87:33
LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { *b = U; false } => {}
| -----^^^^^^^---------^
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:94:9
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { *a = Err(U); false } => {}
| ---------^^^^^^-----^
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:94:33
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { *a = Err(U); false } => {}
| ---------^^^^^^^-----^
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:101:9
LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
| -----^^^^^^---------^
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:101:33
LL | ref a @ Ok(ref mut b) | ref a @ Err(ref mut b) if { drop(b); false } => {}
| -----^^^^^^^---------^
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:109:9
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
| ---------^^^^^^-----^
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:109:33
LL | ref mut a @ Ok(ref b) | ref mut a @ Err(ref b) if { drop(a); false } => {}
| ---------^^^^^^^-----^
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:117:9
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| | | |
- | | | mutable borrow, by `c`, occurs here
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | | value is mutably borrowed by `c` here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:123:9
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| | | |
- | | | mutable borrow, by `c`, occurs here
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | | value is mutably borrowed by `c` here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:129:9
LL | let ref a @ (ref mut b, ref mut c) = (U, U);
| -----^^^^---------^^---------^
| | | |
- | | | mutable borrow, by `c`, occurs here
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | | value is mutably borrowed by `c` here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:134:9
LL | let ref mut a @ (ref b, ref c) = (U, U);
| ---------^^^^-----^^-----^
| | | |
- | | | immutable borrow, by `c`, occurs here
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | | value is borrowed by `c` here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:22:11
LL | fn f1(ref a @ ref mut b: U) {}
| -----^^^---------
| | |
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:24:11
LL | fn f2(ref mut a @ ref b: U) {}
| ---------^^^-----
| | |
- | | immutable borrow, by `b`, occurs here
- | mutable borrow, by `a`, occurs here
+ | | value is borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:26:11
LL | fn f3(ref a @ [ref b, ref mut mid @ .., ref c]: [U; 4]) {}
| -----^^^^^^^^^^^----------------^^^^^^^^
| | |
- | | mutable borrow, by `mid`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `mid` here
+ | value is borrowed by `a` here
error: cannot borrow value as mutable because it is also borrowed as immutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:28:22
LL | fn f4_also_moved(ref a @ ref mut b @ c: U) {}
| -----^^^-------------
| | | |
- | | | also moved into `c` here
- | | mutable borrow, by `b`, occurs here
- | immutable borrow, by `a`, occurs here
+ | | | value is moved into `c` here
+ | | value is mutably borrowed by `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:28:30
LL | fn f4_also_moved(ref a @ ref mut b @ c: U) {}
| ---------^^^-
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is mutably borrowed by `b` here
error[E0502]: cannot borrow value as immutable because it is also borrowed as mutable
--> $DIR/borrowck-pat-ref-mut-and-ref.rs:8:31
LL | let ref mut a @ ref mut b = U;
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:29:9
LL | let ref mut a @ ref mut b = U;
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:33:9
LL | let ref mut a @ ref mut b = U;
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:36:9
LL | let ref mut a @ ref mut b = U;
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:39:9
LL | let ref mut a @ ref mut b = U;
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:44:9
LL | let ref mut a @ (
| ^--------
| |
- | _________first mutable borrow, by `a`, occurs here
+ | _________value is mutably borrowed by `a` here
| |
LL | |
LL | | ref mut b,
- | | --------- another mutable borrow, by `b`, occurs here
+ | | --------- value is mutably borrowed by `b` here
LL | | [
LL | | ref mut c,
- | | --------- another mutable borrow, by `c`, occurs here
+ | | --------- value is mutably borrowed by `c` here
LL | | ref mut d,
- | | --------- another mutable borrow, by `d`, occurs here
+ | | --------- value is mutably borrowed by `d` here
LL | | ref e,
- | | ----- also borrowed as immutable, by `e`, here
+ | | ----- value is borrowed by `e` here
LL | | ]
LL | | ) = (U, [U, U, U]);
| |_____^
LL | let ref mut a @ (
| ^--------
| |
- | _________first mutable borrow, by `a`, occurs here
+ | _________value is mutably borrowed by `a` here
| |
LL | |
LL | | ref mut b,
- | | --------- another mutable borrow, by `b`, occurs here
+ | | --------- value is mutably borrowed by `b` here
LL | | [
LL | | ref mut c,
- | | --------- another mutable borrow, by `c`, occurs here
+ | | --------- value is mutably borrowed by `c` here
LL | | ref mut d,
- | | --------- another mutable borrow, by `d`, occurs here
+ | | --------- value is mutably borrowed by `d` here
LL | | ref e,
- | | ----- also borrowed as immutable, by `e`, here
+ | | ----- value is borrowed by `e` here
LL | | ]
LL | | ) = (u(), [u(), u(), u()]);
| |_________^
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:76:37
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:82:9
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:82:37
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:89:9
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:89:37
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:101:9
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:101:37
LL | ref mut a @ Ok(ref mut b) | ref mut a @ Err(ref mut b) => {
| ---------^^^^^^^---------^
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:8:11
LL | fn f1(ref mut a @ ref mut b: U) {}
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:10:11
LL | fn f2(ref mut a @ ref mut b: U) {}
| ---------^^^---------
| | |
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:13:9
LL | ref mut a @ [
| ^--------
| |
- | _________first mutable borrow, by `a`, occurs here
+ | _________value is mutably borrowed by `a` here
| |
LL | |
LL | | [ref b @ .., _],
- | | ---------- also borrowed as immutable, by `b`, here
+ | | ---------- value is borrowed by `b` here
LL | | [_, ref mut mid @ ..],
- | | ---------------- another mutable borrow, by `mid`, occurs here
+ | | ---------------- value is mutably borrowed by `mid` here
LL | | ..,
LL | | [..],
LL | | ] : [[U; 4]; 5]
LL | fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
| ---------^^^-------------
| | | |
- | | | also moved into `c` here
- | | another mutable borrow, by `b`, occurs here
- | first mutable borrow, by `a`, occurs here
+ | | | value is moved into `c` here
+ | | value is mutably borrowed by `b` here
+ | value is mutably borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/borrowck-pat-ref-mut-twice.rs:21:34
LL | fn f4_also_moved(ref mut a @ ref mut b @ c: U) {}
| ---------^^^-
| | |
- | | value moved into `c` here
- | value borrowed, by `b`, here
+ | | value is moved into `c` here
+ | value is mutably borrowed by `b` here
error[E0499]: cannot borrow value as mutable more than once at a time
--> $DIR/borrowck-pat-ref-mut-twice.rs:29:9
LL | let ref a @ b = NotCopy;
| -----^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/default-binding-modes-both-sides-independent.rs:29:9
LL | let ref mut a @ b = NotCopy;
| ---------^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is mutably borrowed by `a` here
error: cannot move out of value because it is borrowed
--> $DIR/default-binding-modes-both-sides-independent.rs:34:12
LL | Ok(ref a @ b) | Err(b @ ref a) => {
| -----^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error: borrow of moved value
--> $DIR/default-binding-modes-both-sides-independent.rs:34:29
LL | ref a @ b => {
| -----^^^-
| | |
- | | value moved into `b` here
- | value borrowed, by `a`, here
+ | | value is moved into `b` here
+ | value is borrowed by `a` here
error[E0382]: borrow of moved value
--> $DIR/default-binding-modes-both-sides-independent.rs:29:9
--- /dev/null
+fn main() {
+ let 5 = 6;
+ //~^ error refutable pattern in local binding [E0005]
+
+ let x @ 5 = 6;
+ //~^ error refutable pattern in local binding [E0005]
+}
--- /dev/null
+error[E0005]: refutable pattern in local binding
+ --> $DIR/issue-106552.rs:2:9
+ |
+LL | let 5 = 6;
+ | ^ patterns `i32::MIN..=4_i32` and `6_i32..=i32::MAX` not covered
+ |
+ = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+ = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+ = note: the matched value is of type `i32`
+help: you might want to use `if let` to ignore the variants that aren't matched
+ |
+LL | if let 5 = 6 { todo!() }
+ | ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | let _5 = 6;
+ | +
+
+error[E0005]: refutable pattern in local binding
+ --> $DIR/issue-106552.rs:5:9
+ |
+LL | let x @ 5 = 6;
+ | ^^^^^ patterns `i32::MIN..=4_i32` and `6_i32..=i32::MAX` not covered
+ |
+ = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+ = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+ = note: the matched value is of type `i32`
+help: you might want to use `let else` to handle the variants that aren't matched
+ |
+LL | let x @ 5 = 6 else { todo!() };
+ | ++++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0005`.
pub fn key(e: ::E) -> &'static str {
match e {
A => "A",
-//~^ WARN pattern binding `A` is named the same as one of the variants of the type `E`
+//~^ ERROR pattern binding `A` is named the same as one of the variants of the type `E`
B => "B", //~ ERROR: unreachable pattern
-//~^ WARN pattern binding `B` is named the same as one of the variants of the type `E`
+//~^ ERROR pattern binding `B` is named the same as one of the variants of the type `E`
}
}
}
-warning[E0170]: pattern binding `A` is named the same as one of the variants of the type `E`
+error[E0170]: pattern binding `A` is named the same as one of the variants of the type `E`
--> $DIR/issue-14221.rs:13:13
|
LL | A => "A",
| ^ help: to match on the variant, qualify the path: `E::A`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `B` is named the same as one of the variants of the type `E`
+error[E0170]: pattern binding `B` is named the same as one of the variants of the type `E`
--> $DIR/issue-14221.rs:15:13
|
LL | B => "B",
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error; 2 warnings emitted
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0170`.
// Test for issue #67776: binding named the same as enum variant
-// should report a warning even when matching against a reference type
-
-// check-pass
+// should report an error even when matching against a reference type
#![allow(unused_variables)]
#![allow(non_snake_case)]
fn fn1(e: Foo) {
match e {
Bar => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
Baz => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
}
}
fn fn2(e: &Foo) {
match e {
Bar => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
Baz => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
}
}
fn fn3(e: &mut &&mut Foo) {
match e {
Bar => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
Baz => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
}
}
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:17:9
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:15:9
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:19:9
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:17:9
|
LL | Baz => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:26:9
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:24:9
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:28:9
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:26:9
|
LL | Baz => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:35:9
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:33:9
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:37:9
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:35:9
|
LL | Baz => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning: 6 warnings emitted
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0170`.
error[E0505]: cannot move out of `arr[..]` because it is borrowed
--> $DIR/borrowck-move-ref-pattern.rs:8:24
|
+LL | let mut arr = [U, U, U, U, U];
+ | ------- binding `arr` declared here
LL | let hold_all = &arr;
| ---- borrow of `arr` occurs here
LL | let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
--- /dev/null
+// check-pass
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![allow(private_in_public)]
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn foo<T>(t: T) -> TokenStream {
+ TokenStream::new()
+}
+
+trait Project {
+ type Assoc;
+}
+
+impl Project for () {
+ type Assoc = TokenStream;
+}
+
+#[proc_macro]
+pub fn uwu(_input: <() as Project>::Assoc) -> <() as Project>::Assoc {
+ TokenStream::new()
+}
LL | #[derive(GenHelperUse)]
| ^^^^^^^^^^^^
|
- = note: consider importing this attribute macro:
+ = help: consider importing this attribute macro:
empty_helper
= note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | gen_helper_use!();
| ----------------- in this macro invocation
|
- = note: consider importing this attribute macro:
+ = help: consider importing this attribute macro:
crate::empty_helper
= note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info)
// aux-build:expand-expr.rs
+// no-remap-src-base: check_expand_expr_file!() fails when enabled.
+
#![feature(concat_bytes)]
extern crate expand_expr;
// Check builtin macros can be expanded.
-expand_expr_is!(11u32, line!());
+expand_expr_is!(13u32, line!());
expand_expr_is!(24u32, column!());
expand_expr_is!("Hello, World!", concat!("Hello, ", "World", "!"));
error: expected one of `.`, `?`, or an operator, found `;`
- --> $DIR/expand-expr.rs:106:27
+ --> $DIR/expand-expr.rs:108:27
|
LL | expand_expr_fail!("string"; hello);
| ^ expected one of `.`, `?`, or an operator
error: expected expression, found `$`
- --> $DIR/expand-expr.rs:109:19
+ --> $DIR/expand-expr.rs:111:19
|
LL | expand_expr_fail!($);
| ^ expected expression
error: expected expression, found `$`
- --> $DIR/expand-expr.rs:38:23
+ --> $DIR/expand-expr.rs:40:23
|
LL | ($($t:tt)*) => { $($t)* };
| ^^^^ expected expression
error: expected expression, found `$`
- --> $DIR/expand-expr.rs:111:28
+ --> $DIR/expand-expr.rs:113:28
|
LL | expand_expr_fail!(echo_pm!($));
| ^ expected expression
error: macro expansion ignores token `hello` and any following
- --> $DIR/expand-expr.rs:115:47
+ --> $DIR/expand-expr.rs:117:47
|
LL | expand_expr_is!("string", echo_tts!("string"; hello));
| --------------------^^^^^- caused by the macro expansion here
| +
error: macro expansion ignores token `;` and any following
- --> $DIR/expand-expr.rs:116:44
+ --> $DIR/expand-expr.rs:118:44
|
LL | expand_expr_is!("string", echo_pm!("string"; hello));
| -----------------^------- caused by the macro expansion here
| +
error: recursion limit reached while expanding `recursive_expand!`
- --> $DIR/expand-expr.rs:124:16
+ --> $DIR/expand-expr.rs:126:16
|
LL | const _: u32 = recursive_expand!();
| ^^^^^^^^^^^^^^^^^^^
#[derive(generate_mod::CheckDerive)] //~ ERROR cannot find type `FromOutside` in this scope
//~| ERROR cannot find type `OuterDerive` in this scope
+ //~| WARN this was previously accepted
+ //~| WARN this was previously accepted
struct Z;
fn inner_block() {
#[derive(generate_mod::CheckDerive)] //~ ERROR cannot find type `FromOutside` in this scope
//~| ERROR cannot find type `OuterDerive` in this scope
+ //~| WARN this was previously accepted
+ //~| WARN this was previously accepted
struct InnerZ;
}
-#[derive(generate_mod::CheckDeriveLint)] //~ ERROR cannot find type `OuterDeriveLint` in this scope
- //~| ERROR cannot find type `FromOutside` in this scope
+#[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
struct W;
fn main() {}
LL | generate_mod::check!();
| ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
FromOutside
= note: this error originates in the macro `generate_mod::check` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | generate_mod::check!();
| ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
Outer
= note: this error originates in the macro `generate_mod::check` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | #[generate_mod::check_attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
FromOutside
= note: this error originates in the attribute macro `generate_mod::check_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | #[generate_mod::check_attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
OuterAttr
= note: this error originates in the attribute macro `generate_mod::check_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `FromOutside` in this scope
+error: cannot find type `FromOutside` in this scope
--> $DIR/generate-mod.rs:16:10
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- FromOutside
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `OuterDerive` in this scope
+error: cannot find type `OuterDerive` in this scope
--> $DIR/generate-mod.rs:16:10
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- OuterDerive
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `FromOutside` in this scope
- --> $DIR/generate-mod.rs:21:14
+error: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:23:14
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- FromOutside
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `OuterDerive` in this scope
- --> $DIR/generate-mod.rs:21:14
+error: cannot find type `OuterDerive` in this scope
+ --> $DIR/generate-mod.rs:23:14
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- OuterDerive
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `FromOutside` in this scope
- --> $DIR/generate-mod.rs:26:10
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0412`.
+Future incompatibility report: Future breakage diagnostic:
+error: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:16:10
|
-LL | #[derive(generate_mod::CheckDeriveLint)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- FromOutside
- = note: this error originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+error: cannot find type `OuterDerive` in this scope
+ --> $DIR/generate-mod.rs:16:10
+ |
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `OuterDeriveLint` in this scope
- --> $DIR/generate-mod.rs:26:10
+Future breakage diagnostic:
+error: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:23:14
|
-LL | #[derive(generate_mod::CheckDeriveLint)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- OuterDeriveLint
- = note: this error originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 10 previous errors
+Future breakage diagnostic:
+error: cannot find type `OuterDerive` in this scope
+ --> $DIR/generate-mod.rs:23:14
+ |
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+note: the lint level is defined here
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: cannot find type `OuterDeriveLint` in this scope
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+note: the lint level is defined here
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
-For more information about this error, try `rustc --explain E0412`.
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `#[deny(proc_macro_back_compat)]` on by default
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: older versions of the `rental` crate will stop compiling in future versions of Rust; please update to `rental` v0.5.6, or switch to one of the `rental` alternatives
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future incompatibility report: Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Future breakage diagnostic:
error: using an old version of `rental`
- --> remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
|
LL | enum ProceduralMasqueradeDummyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
- span: remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0),
+ span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0),
},
Ident {
ident: "ProceduralMasqueradeDummyType",
- span: remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6: 4:35 (#0),
+ span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6: 4:35 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [
Ident {
ident: "Input",
- span: remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:13:5: 13:10 (#0),
+ span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:13:5: 13:10 (#0),
},
],
- span: remapped/proc-macro/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:36: 14:2 (#0),
+ span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:36: 14:2 (#0),
},
]
PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
- span: remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0),
+ span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0),
},
Ident {
ident: "ProceduralMasqueradeDummyType",
- span: remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6: 4:35 (#0),
+ span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6: 4:35 (#0),
},
Group {
delimiter: Brace,
stream: TokenStream [
Ident {
ident: "Input",
- span: remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:13:5: 13:10 (#0),
+ span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:13:5: 13:10 (#0),
},
],
- span: remapped/proc-macro/pretty-print-hack/rental-0.5.5/src/lib.rs:4:36: 14:2 (#0),
+ span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:36: 14:2 (#0),
},
]
// aux-build:test-macros.rs
// compile-flags: -Z span-debug
// revisions: local remapped
-// [remapped]compile-flags: --remap-path-prefix={{src-base}}=remapped
-
-// The remapped paths are not normalized by compiletest.
-// normalize-stdout-test: "\\(proc-macro|pretty-print-hack)" -> "/$1"
-// normalize-stderr-test: "\\(proc-macro|pretty-print-hack)" -> "/$1"
+// [local] no-remap-src-base: The hack should work regardless of remapping.
+// [remapped] remap-src-base
#![no_std] // Don't load unnecessary hygiene information from std
extern crate std;
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+#![allow(warnings)]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub extern "C" fn abi(a: TokenStream) -> TokenStream {
+ //~^ ERROR proc macro functions may not be `extern "C"`
+ a
+}
+
+#[proc_macro]
+pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
+ //~^ ERROR proc macro functions may not be `extern "system"`
+ a
+}
+
+#[proc_macro]
+pub extern fn abi3(a: TokenStream) -> TokenStream {
+ //~^ ERROR proc macro functions may not be `extern "C"`
+ a
+}
+
+#[proc_macro]
+pub extern "Rust" fn abi4(a: TokenStream) -> TokenStream {
+ a
+}
--- /dev/null
+error: proc macro functions may not be `extern "C"`
+ --> $DIR/proc-macro-abi.rs:11:1
+ |
+LL | pub extern "C" fn abi(a: TokenStream) -> TokenStream {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: proc macro functions may not be `extern "system"`
+ --> $DIR/proc-macro-abi.rs:17:1
+ |
+LL | pub extern "system" fn abi2(a: TokenStream) -> TokenStream {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: proc macro functions may not be `extern "C"`
+ --> $DIR/proc-macro-abi.rs:23:1
+ |
+LL | pub extern fn abi3(a: TokenStream) -> TokenStream {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro_attribute]
+pub fn bad_input(input: String) -> TokenStream {
+ //~^ ERROR mismatched attribute proc macro signature
+ ::proc_macro::TokenStream::new()
+}
+
+#[proc_macro_attribute]
+pub fn bad_output(input: TokenStream) -> String {
+ //~^ ERROR mismatched attribute proc macro signature
+ //~| ERROR mismatched attribute proc macro signature
+ String::from("blah")
+}
+
+#[proc_macro_attribute]
+pub fn bad_everything(input: String) -> String {
+ //~^ ERROR mismatched attribute proc macro signature
+ //~| ERROR mismatched attribute proc macro signature
+ input
+}
+
+#[proc_macro_attribute]
+pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
+ //~^ ERROR mismatched attribute proc macro signature
+}
--- /dev/null
+error: mismatched attribute proc macro signature
+ --> $DIR/signature-proc-macro-attribute.rs:10:1
+ |
+LL | pub fn bad_input(input: String) -> TokenStream {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream`
+
+error: mismatched attribute proc macro signature
+ --> $DIR/signature-proc-macro-attribute.rs:16:42
+ |
+LL | pub fn bad_output(input: TokenStream) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream`
+
+error: mismatched attribute proc macro signature
+ --> $DIR/signature-proc-macro-attribute.rs:16:1
+ |
+LL | pub fn bad_output(input: TokenStream) -> String {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream`
+
+error: mismatched attribute proc macro signature
+ --> $DIR/signature-proc-macro-attribute.rs:23:41
+ |
+LL | pub fn bad_everything(input: String) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: attribute proc macros must have a signature of `fn(TokenStream, TokenStream) -> TokenStream`
+
+error: mismatched attribute proc macro signature
+ --> $DIR/signature-proc-macro-attribute.rs:23:1
+ |
+LL | pub fn bad_everything(input: String) -> String {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attribute proc macro must have two arguments of type `proc_macro::TokenStream`
+
+error: mismatched attribute proc macro signature
+ --> $DIR/signature-proc-macro-attribute.rs:30:49
+ |
+LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
+ | ^^^^^^^^^ found unexpected argument
+
+error: aborting due to 6 previous errors
+
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro_derive(Blah)]
+pub fn bad_input(input: String) -> TokenStream {
+ //~^ ERROR mismatched derive proc macro signature
+ TokenStream::new()
+}
+
+#[proc_macro_derive(Bleh)]
+pub fn bad_output(input: TokenStream) -> String {
+ //~^ ERROR mismatched derive proc macro signature
+ String::from("blah")
+}
+
+#[proc_macro_derive(Bluh)]
+pub fn bad_everything(input: String) -> String {
+ //~^ ERROR mismatched derive proc macro signature
+ //~| ERROR mismatched derive proc macro signature
+ input
+}
+
+#[proc_macro_derive(Blih)]
+pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
+ //~^ ERROR mismatched derive proc macro signature
+}
--- /dev/null
+error: mismatched derive proc macro signature
+ --> $DIR/signature-proc-macro-derive.rs:10:25
+ |
+LL | pub fn bad_input(input: String) -> TokenStream {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature-proc-macro-derive.rs:16:42
+ |
+LL | pub fn bad_output(input: TokenStream) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature-proc-macro-derive.rs:22:41
+ |
+LL | pub fn bad_everything(input: String) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature-proc-macro-derive.rs:22:30
+ |
+LL | pub fn bad_everything(input: String) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature-proc-macro-derive.rs:29:33
+ |
+LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro]
+pub fn bad_input(input: String) -> TokenStream {
+ //~^ ERROR mismatched function-like proc macro signature
+ ::proc_macro::TokenStream::new()
+}
+
+#[proc_macro]
+pub fn bad_output(input: TokenStream) -> String {
+ //~^ ERROR mismatched function-like proc macro signature
+ String::from("blah")
+}
+
+#[proc_macro]
+pub fn bad_everything(input: String) -> String {
+ //~^ ERROR mismatched function-like proc macro signature
+ //~| ERROR mismatched function-like proc macro signature
+ input
+}
+
+#[proc_macro]
+pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
+ //~^ ERROR mismatched function-like proc macro signature
+}
--- /dev/null
+error: mismatched function-like proc macro signature
+ --> $DIR/signature-proc-macro.rs:10:25
+ |
+LL | pub fn bad_input(input: String) -> TokenStream {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched function-like proc macro signature
+ --> $DIR/signature-proc-macro.rs:16:42
+ |
+LL | pub fn bad_output(input: TokenStream) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched function-like proc macro signature
+ --> $DIR/signature-proc-macro.rs:22:41
+ |
+LL | pub fn bad_everything(input: String) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched function-like proc macro signature
+ --> $DIR/signature-proc-macro.rs:22:30
+ |
+LL | pub fn bad_everything(input: String) -> String {
+ | ^^^^^^ found std::string::String, expected type `proc_macro::TokenStream`
+ |
+ = note: function-like proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched function-like proc macro signature
+ --> $DIR/signature-proc-macro.rs:29:33
+ |
+LL | pub fn too_many(a: TokenStream, b: TokenStream, c: String) -> TokenStream {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ found unexpected arguments
+
+error: aborting due to 5 previous errors
+
#[proc_macro_derive(A)]
pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
- //~^ ERROR: expected a `Fn<(proc_macro::TokenStream,)>` closure, found `unsafe extern "C" fn
+ //~^ ERROR: mismatched derive proc macro signature
+ //~| mismatched derive proc macro signature
+ //~| mismatched derive proc macro signature
+ //~| proc macro functions may not be `extern
+ //~| proc macro functions may not be `unsafe
loop {}
}
-error[E0277]: expected a `Fn<(proc_macro::TokenStream,)>` closure, found `unsafe extern "C" fn(i32, u32) -> u32 {foo}`
+error: proc macro functions may not be `extern "C"`
--> $DIR/signature.rs:10:1
|
-LL | / pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
-LL | |
-LL | | loop {}
-LL | | }
- | | ^
- | | |
- | |_call the function in a closure: `|| unsafe { /* code */ }`
- | required by a bound introduced by this call
+LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: proc macro functions may not be `unsafe`
+ --> $DIR/signature.rs:10:1
+ |
+LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature.rs:10:49
+ |
+LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
+ | ^^^ found u32, expected type `proc_macro::TokenStream`
+ |
+ = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature.rs:10:33
+ |
+LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
+ | ^^^ found i32, expected type `proc_macro::TokenStream`
+ |
+ = note: derive proc macros must have a signature of `fn(TokenStream) -> TokenStream`
+
+error: mismatched derive proc macro signature
+ --> $DIR/signature.rs:10:38
|
- = help: the trait `Fn<(proc_macro::TokenStream,)>` is not implemented for fn item `unsafe extern "C" fn(i32, u32) -> u32 {foo}`
- = note: unsafe function cannot be called generically without an unsafe block
-note: required by a bound in `ProcMacro::custom_derive`
- --> $SRC_DIR/proc_macro/src/bridge/client.rs:LL:COL
+LL | pub unsafe extern "C" fn foo(a: i32, b: u32) -> u32 {
+ | ^^^^^^ found unexpected argument
-error: aborting due to previous error
+error: aborting due to 5 previous errors
-For more information about this error, try `rustc --explain E0277`.
error[E0597]: `x` does not live long enough
--> $DIR/do-not-suggest-adding-bound-to-opaque-type.rs:9:7
|
+LL | let x = ();
+ | - binding `x` declared here
LL | S(&x)
| --^^-
| | |
--- /dev/null
+// FIXME: This test should pass as the first two fields add implied bounds that
+// `'a` is equal to `'b` while the last one should simply use that fact. With
+// the current implementation this errors. We have to be careful as implied bounds
+// are only sound if they're also correctly checked.
+
+struct Inv<T>(*mut T); // `T` is invariant.
+type A = for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>);
+type B = for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>);
+
+fn main() {
+ let x: A = |_, _, _| ();
+ let y: B = x; //~ ERROR mismatched types
+ let _: A = y; //~ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-implied.rs:12:16
+ |
+LL | let y: B = x;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>)`
+ found fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>)`
+
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-implied.rs:13:16
+ |
+LL | let _: A = y;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>)`
+ found fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>)`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error[E0597]: `a` does not live long enough
--> $DIR/regions-addr-of-arg.rs:5:30
|
+LL | fn foo(a: isize) {
+ | - binding `a` declared here
LL | let _p: &'static isize = &a;
| -------------- ^^ borrowed value does not live long enough
| |
LL | fn call1<'a>(x: &'a usize) {
| -- lifetime `'a` defined here
...
+LL | let y: usize = 3;
+ | - binding `y` declared here
LL | let z: &'a & usize = &(&y);
| ----------- ^^^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/regions-infer-proc-static-upvar.rs:10:13
|
+LL | let x = 3;
+ | - binding `x` declared here
LL | let y = &x;
| ^^ borrowed value does not live long enough
LL | / foo(move|| {
error[E0597]: `y` does not live long enough
--> $DIR/regions-nested-fns.rs:5:18
|
+LL | let y = 3;
+ | - binding `y` declared here
LL | let mut ay = &y;
| ^^ borrowed value does not live long enough
...
error[E0597]: `line` does not live long enough
--> $DIR/regions-pattern-typing-issue-19552.rs:5:14
|
+LL | let line = String::new();
+ | ---- binding `line` declared here
LL | match [&*line] {
| ^^^^ borrowed value does not live long enough
LL | [ word ] => { assert_static(word); }
--> $DIR/regions-pattern-typing-issue-19997.rs:7:13
|
LL | match (&a1,) {
- | --- borrow of `a1` occurs here
+ | --- `a1` is borrowed here
LL | (&ref b0,) => {
LL | a1 = &f;
- | ^^^^^^^ assignment to borrowed `a1` occurs here
+ | ^^^^^^^ `a1` is assigned to here but it was already borrowed
LL | drop(b0);
| -- borrow later used here
|
= note: expected fn item `extern "rust-intrinsic" fn(_) -> _ {likely}`
found fn item `extern "rust-intrinsic" fn(_) -> _ {unlikely}`
- = note: different `fn` items always have unique types, even if their signatures are the same
- = help: change the expected type to be function pointer `extern "rust-intrinsic" fn(bool) -> bool`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `likely as extern "rust-intrinsic" fn(bool) -> bool`
+ = note: different fn items have unique types, even if their signatures are the same
error: aborting due to 3 previous errors
+++ /dev/null
-// compile-flags: --remap-path-prefix={{src-base}}=remapped
-
-fn main() {
- // We cannot actually put an ERROR marker here because
- // the file name in the error message is not what the
- // test framework expects (since the filename gets remapped).
- // We still test the expected error in the stderr file.
- ferris
-}
+++ /dev/null
-error[E0425]: cannot find value `ferris` in this scope
- --> remapped/remap-path-prefix.rs:8:5
- |
-LL | ferris
- | ^^^^^^ not found in this scope
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0425`.
--> $DIR/borrowck-non-exhaustive.rs:12:11
|
LL | let y = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | match x {
| ^ use of borrowed `x`
...
help: consider importing one of these items instead
|
LL | use core::alloc;
- | ~~~~~~~~~~~~
-LL | use std::alloc;
| ~~~~~~~~~~~
+LL | use std::alloc;
+ | ~~~~~~~~~~
error: aborting due to previous error
|
LL | if let Some(n) = opt else {
| ^
+help: remove the `if` if you meant to write a `let...else` statement
+ --> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:24:5
+ |
+LL | if let Some(n) = opt else {
+ | ^^
error: this `if` expression is missing a block after the condition
--> $DIR/ensure-that-let-else-does-not-interact-with-let-chains.rs:28:5
| ---------------------------------- `#[target_feature]` added here
...
LL | let foo: fn() = foo;
- | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
- | |
+ | ---- ^^^
+ | | |
+ | | cannot coerce functions with `#[target_feature]` to safe function pointers
+ | | help: consider casting to a fn pointer: `foo as fn()`
| expected due to this
|
= note: expected fn pointer `fn()`
found fn item `fn() {foo}`
+ = note: fn items are distinct from fn pointers
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
error: aborting due to previous error
| ---------------------------------- `#[target_feature]` added here
...
LL | let foo: fn() = foo;
- | ---- ^^^ cannot coerce functions with `#[target_feature]` to safe function pointers
- | |
+ | ---- ^^^
+ | | |
+ | | cannot coerce functions with `#[target_feature]` to safe function pointers
+ | | help: consider casting to a fn pointer: `foo as fn()`
| expected due to this
|
= note: expected fn pointer `fn()`
found fn item `fn() {foo}`
+ = note: fn items are distinct from fn pointers
= note: functions with `#[target_feature]` can only be coerced to `unsafe` function pointers
error: aborting due to previous error
error[E0597]: `s` does not live long enough
--> $DIR/lifetime-update.rs:20:17
|
+LL | let s = String::from("hello");
+ | - binding `s` declared here
+...
LL | lt_str: &s,
| ^^ borrowed value does not live long enough
...
}
}
+// https://github.com/rust-lang/rust/issues/106870
+mod multiple_predicates_with_same_span {
+ macro_rules! m {
+ ($($name:ident)+) => {
+ struct Inline<'a, $($name: 'a,)+>(&'a ($($name,)+));
+ //~^ ERROR: outlives requirements can be inferred
+ struct FullWhere<'a, $($name,)+>(&'a ($($name,)+)) where $($name: 'a,)+;
+ //~^ ERROR: outlives requirements can be inferred
+ struct PartialWhere<'a, $($name,)+>(&'a ($($name,)+)) where (): Sized, $($name: 'a,)+;
+ //~^ ERROR: outlives requirements can be inferred
+ struct Interleaved<'a, $($name,)+>(&'a ($($name,)+))
+ where
+ (): Sized,
+ $($name: 'a, $name: 'a, )+ //~ ERROR: outlives requirements can be inferred
+ $($name: 'a, $name: 'a, )+;
+ }
+ }
+ m!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
+}
+
fn main() {}
LL + union BeeWhereAyTeeYooWhereOutlivesAyIsDebugBee<'a, 'b, T, U> where U: Debug, {
|
-error: aborting due to 68 previous errors
+error: outlives requirements can be inferred
+ --> $DIR/edition-lint-infer-outlives-multispan.rs:372:38
+ |
+LL | struct Inline<'a, $($name: 'a,)+>(&'a ($($name,)+));
+ | ^^^^ help: remove these bounds
+...
+LL | m!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
+ | --------------------------------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: outlives requirements can be inferred
+ --> $DIR/edition-lint-infer-outlives-multispan.rs:374:64
+ |
+LL | struct FullWhere<'a, $($name,)+>(&'a ($($name,)+)) where $($name: 'a,)+;
+ | ^^^^^^^^^^^^^^^^^^ help: remove these bounds
+...
+LL | m!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
+ | --------------------------------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: outlives requirements can be inferred
+ --> $DIR/edition-lint-infer-outlives-multispan.rs:376:86
+ |
+LL | struct PartialWhere<'a, $($name,)+>(&'a ($($name,)+)) where (): Sized, $($name: 'a,)+;
+ | ^^^^^^^^^ help: remove these bounds
+...
+LL | m!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
+ | --------------------------------------------------------- in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: outlives requirements can be inferred
+ --> $DIR/edition-lint-infer-outlives-multispan.rs:381:19
+ |
+LL | $($name: 'a, $name: 'a, )+
+ | ^^^^^^^^^ ^^^^^^^^^
+LL | $($name: 'a, $name: 'a, )+;
+ | ^^^^^^^^^ ^^^^^^^^^
+...
+LL | m!(T0 T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15);
+ | ---------------------------------------------------------
+ | |
+ | in this macro invocation
+ | in this macro invocation
+ | in this macro invocation
+ | in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: remove these bounds
+ |
+LL ~ $(, , )+
+LL ~ $(, , )+;
+ |
+
+error: aborting due to 72 previous errors
field: &'static T
}
+struct TrailingCommaInWhereClause<'a, T, U>
+where
+ T: 'a,
+
+ //~^ ERROR outlives requirements can be inferred
+{
+ tee: T,
+ yoo: &'a U
+}
fn main() {}
field: &'static T
}
+struct TrailingCommaInWhereClause<'a, T, U>
+where
+ T: 'a,
+ U: 'a,
+ //~^ ERROR outlives requirements can be inferred
+{
+ tee: T,
+ yoo: &'a U
+}
fn main() {}
error: outlives requirements can be inferred
- --> $DIR/edition-lint-infer-outlives.rs:26:31
+ --> $DIR/edition-lint-infer-outlives.rs:797:5
|
-LL | struct TeeOutlivesAy<'a, T: 'a> {
- | ^^^^ help: remove this bound
+LL | U: 'a,
+ | ^^^^^^ help: remove this bound
|
note: the lint level is defined here
--> $DIR/edition-lint-infer-outlives.rs:4:9
LL | #![deny(explicit_outlives_requirements)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+error: outlives requirements can be inferred
+ --> $DIR/edition-lint-infer-outlives.rs:26:31
+ |
+LL | struct TeeOutlivesAy<'a, T: 'a> {
+ | ^^^^ help: remove this bound
+
error: outlives requirements can be inferred
--> $DIR/edition-lint-infer-outlives.rs:31:40
|
LL | union BeeWhereOutlivesAyTeeWhereDebug<'a, 'b, T> where 'b: 'a, T: Debug {
| ^^^^^^^^ help: remove this bound
-error: aborting due to 152 previous errors
+error: aborting due to 153 previous errors
#![feature(rustc_attrs)]
use std::{
+ cell::Cell,
ops::{Deref, CoerceUnsized, DispatchFromDyn},
marker::Unsize,
};
impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
+
+struct CellPtr<'a, T: ?Sized>(Cell<&'a T>);
+
+impl<'a, T: ?Sized> Deref for CellPtr<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ self.0.get()
+ }
+}
+
+impl<'a, T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<CellPtr<'a, U>> for CellPtr<'a, T> {}
+impl<'a, T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<CellPtr<'a, U>> for CellPtr<'a, T> {}
+
struct Wrapper<T: ?Sized>(T);
impl<T: ?Sized> Deref for Wrapper<T> {
fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
+ fn cell(self: CellPtr<Self>) -> i32;
}
impl Trait for i32 {
fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32 {
***self
}
+ fn cell(self: CellPtr<Self>) -> i32 {
+ *self
+ }
}
fn main() {
let wpw = Wrapper(Ptr(Box::new(Wrapper(7)))) as Wrapper<Ptr<Wrapper<dyn Trait>>>;
assert_eq!(wpw.wrapper_ptr_wrapper(), 7);
+
+ let c = CellPtr(Cell::new(&8)) as CellPtr<dyn Trait>;
+ assert_eq!(c.cell(), 8);
}
error[E0597]: `x` does not live long enough
--> $DIR/issue-61882-2.rs:6:14
|
+LL | let x = 0;
+ | - binding `x` declared here
LL | Self(&x);
| ^^
| |
help: consider importing this module instead
|
LL | use std::intrinsics;
- | ~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
--- /dev/null
+#![feature(decl_macro, rustc_attrs)]
+#![deny(single_use_lifetimes)]
+
+mod type_params {
+ macro m($T:ident) {
+ fn f<$T: Clone, T: PartialEq>(t1: $T, t2: T) -> ($T, bool) {
+ (t1.clone(), t2 == t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "semitransparent"]
+ macro n($T:ident) {
+ fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ fn h<T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ }
+
+ #[rustc_macro_transparency = "transparent"]
+ macro p($T:ident) {
+ fn j<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ fn k<T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ }
+
+ m!(T);
+ n!(T);
+ p!(T);
+}
+
+mod lifetime_params {
+ macro m($a:lifetime) {
+ fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "semitransparent"]
+ macro n($a:lifetime) {
+ fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ fn h<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "transparent"]
+ macro p($a:lifetime) {
+ fn j<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ }
+
+ m!('a); //~ ERROR lifetime parameter `'a` only used once
+ n!('a);
+ p!('a);
+}
+
+mod const_params {
+ macro m($C:ident) {
+ fn f<const $C: usize, const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); $C], [(); C]) {
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "semitransparent"]
+ macro n($C:ident) {
+ fn g<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ fn h<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "transparent"]
+ macro p($C:ident) {
+ fn j<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ fn k<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ }
+
+ m!(C);
+ n!(C);
+ p!(C);
+}
+
+fn main() {}
--- /dev/null
+error: lifetime parameter `'a` only used once
+ --> $DIR/issue-104440.rs:63:8
+ |
+LL | m!('a);
+ | ^^
+ | |
+ | this lifetime...
+ | ...is used only here
+ |
+note: the lint level is defined here
+ --> $DIR/issue-104440.rs:2:9
+ |
+LL | #![deny(single_use_lifetimes)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: lifetime parameter `'a` only used once
+ --> $DIR/issue-104440.rs:38:30
+ |
+LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) {
+ | ^^ this lifetime... -- ...is used only here
+...
+LL | m!('a);
+ | ------ in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
error[E0505]: cannot move out of `f` because it is borrowed
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16
|
+LL | let mut f = move |g: Box<dyn FnMut(isize)>, b: isize| {
+ | ----- binding `f` declared here
+...
LL | f(Box::new(|a| {
| - ^^^ move out of `f` occurs here
| |
{
let young = ['y']; // statement 3
+ //~^ NOTE binding `young` declared here
v2.push(&young[0]); // statement 4
//~^ ERROR `young[_]` does not live long enough
error[E0597]: `young[_]` does not live long enough
- --> $DIR/borrowck-let-suggestion-suffixes.rs:12:17
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:13:17
|
+LL | let young = ['y']; // statement 3
+ | ----- binding `young` declared here
+...
LL | v2.push(&young[0]); // statement 4
| ^^^^^^^^^ borrowed value does not live long enough
...
| -- borrow later used here
error[E0716]: temporary value dropped while borrowed
- --> $DIR/borrowck-let-suggestion-suffixes.rs:19:14
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:20:14
|
LL | v3.push(&id('x')); // statement 6
| ^^^^^^^ - temporary value is freed at the end of this statement
|
error[E0716]: temporary value dropped while borrowed
- --> $DIR/borrowck-let-suggestion-suffixes.rs:29:18
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:30:18
|
LL | v4.push(&id('y'));
| ^^^^^^^ - temporary value is freed at the end of this statement
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
- --> $DIR/borrowck-let-suggestion-suffixes.rs:40:14
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:41:14
|
LL | v5.push(&id('z'));
| ^^^^^^^ - temporary value is freed at the end of this statement
error[E0597]: `*a` does not live long enough
--> $DIR/destructor-restrictions.rs:8:10
|
+LL | let a = Box::new(RefCell::new(4));
+ | - binding `a` declared here
LL | *a.borrow() + 1
| ^^^^^^^^^^
| |
error[E0597]: `*m` does not live long enough
--> $DIR/dropck-object-cycle.rs:27:31
|
+LL | let m : Box<dyn Trait+'static> = make_val();
+ | - binding `m` declared here
LL | assert_eq!(object_invoke1(&*m), (4,5));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `b2` does not live long enough
--> $DIR/dropck_arr_cycle_checked.rs:93:24
|
+LL | let (b1, b2, b3);
+ | -- binding `b2` declared here
+...
LL | b1.a[0].v.set(Some(&b2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `b3` does not live long enough
--> $DIR/dropck_arr_cycle_checked.rs:95:24
|
+LL | let (b1, b2, b3);
+ | -- binding `b3` declared here
+...
LL | b1.a[1].v.set(Some(&b3));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `b1` does not live long enough
--> $DIR/dropck_arr_cycle_checked.rs:99:24
|
+LL | let (b1, b2, b3);
+ | -- binding `b1` declared here
+...
LL | b3.a[0].v.set(Some(&b1));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `d2` does not live long enough
--> $DIR/dropck_direct_cycle_with_drop.rs:36:19
|
+LL | let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2")));
+ | -- binding `d2` declared here
LL | d1.p.set(Some(&d2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `d1` does not live long enough
--> $DIR/dropck_direct_cycle_with_drop.rs:38:19
|
+LL | let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2")));
+ | -- binding `d1` declared here
+...
LL | d2.p.set(Some(&d1));
| ^^^ borrowed value does not live long enough
LL |
error[E0597]: `bomb` does not live long enough
--> $DIR/dropck_misc_variants.rs:23:36
|
+LL | let (_w, bomb);
+ | ---- binding `bomb` declared here
+LL | bomb = vec![""];
LL | _w = Wrap::<&[&str]>(NoisyDrop(&bomb));
| ^^^^^ borrowed value does not live long enough
LL | }
error[E0597]: `v` does not live long enough
--> $DIR/dropck_misc_variants.rs:31:27
|
+LL | let (_w,v);
+ | - binding `v` declared here
+...
LL | let u = NoisyDrop(&v);
| ^^ borrowed value does not live long enough
...
error[E0597]: `c2` does not live long enough
--> $DIR/dropck_vec_cycle_checked.rs:98:24
|
+LL | let (mut c1, mut c2, mut c3);
+ | ------ binding `c2` declared here
+...
LL | c1.v[0].v.set(Some(&c2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `c3` does not live long enough
--> $DIR/dropck_vec_cycle_checked.rs:100:24
|
+LL | let (mut c1, mut c2, mut c3);
+ | ------ binding `c3` declared here
+...
LL | c1.v[1].v.set(Some(&c3));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `c1` does not live long enough
--> $DIR/dropck_vec_cycle_checked.rs:104:24
|
+LL | let (mut c1, mut c2, mut c3);
+ | ------ binding `c1` declared here
+...
LL | c3.v[0].v.set(Some(&c1));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `y` does not live long enough
--> $DIR/issue-23338-locals-die-before-temps-of-body.rs:10:5
|
+LL | let y = x;
+ | - binding `y` declared here
LL | y.borrow().clone()
| ^^^^^^^^^^
| |
error[E0597]: `y` does not live long enough
--> $DIR/issue-23338-locals-die-before-temps-of-body.rs:17:9
|
+LL | let y = x;
+ | - binding `y` declared here
LL | y.borrow().clone()
| ^^^^^^^^^^
| |
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-child-has-items-via-parent.rs:28:18
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+...
LL | _d = D_Child(&d1);
| ^^^ borrowed value does not live long enough
...
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-trait-has-items.rs:37:26
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+LL | d1 = D_HasSelfMethod(1);
LL | _d = D_HasSelfMethod(&d1);
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-trait-has-items.rs:43:33
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+LL | d1 = D_HasMethodWithSelfArg(1);
LL | _d = D_HasMethodWithSelfArg(&d1);
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-trait-has-items.rs:49:20
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+LL | d1 = D_HasType(1);
LL | _d = D_HasType(&d1);
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24895-copy-clone-dropck.rs:27:14
|
+LL | let (d2, d1);
+ | -- binding `d1` declared here
+LL | d1 = D(34, "d1");
LL | d2 = D(S(&d1, "inner"), "d2");
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `container` does not live long enough
--> $DIR/issue-25199.rs:70:27
|
+LL | let container = Container::new();
+ | --------- binding `container` declared here
LL | let test = Test{test: &container};
| ^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `ticking` does not live long enough
--> $DIR/issue-26656.rs:40:35
|
+LL | let (mut zook, ticking);
+ | ------- binding `ticking` declared here
+...
LL | zook.button = B::BigRedButton(&ticking);
| ^^^^^^^^ borrowed value does not live long enough
LL | }
error[E0597]: `x` does not live long enough
--> $DIR/issue-29106.rs:16:26
|
+LL | let (y, x);
+ | - binding `x` declared here
+LL | x = "alive".to_string();
LL | y = Arc::new(Foo(&x));
| ^^ borrowed value does not live long enough
LL | }
error[E0597]: `x` does not live long enough
--> $DIR/issue-29106.rs:23:25
|
+LL | let (y, x);
+ | - binding `x` declared here
+LL | x = "alive".to_string();
LL | y = Rc::new(Foo(&x));
| ^^ borrowed value does not live long enough
LL | }
error[E0597]: `a` does not live long enough
--> $DIR/issue-36537.rs:5:13
|
+LL | let a = 42;
+ | - binding `a` declared here
LL | p = &a;
| ^^ borrowed value does not live long enough
...
--> $DIR/issue-40157.rs:2:53
|
LL | {println!("{:?}", match { let foo = vec![1, 2]; foo.get(1) } { x => x });}
- | ------------------------^^^^^^^^^^--
- | | | |
- | | | `foo` dropped here while still borrowed
- | | borrowed value does not live long enough
- | borrow later used here
+ | --- ^^^^^^^^^^ - `foo` dropped here while still borrowed
+ | | |
+ | | borrowed value does not live long enough
+ | binding `foo` declared here
error: aborting due to previous error
error[E0597]: `first_dropped` does not live long enough
--> $DIR/issue28498-reject-lifetime-param.rs:32:19
|
+LL | let (foo1, first_dropped);
+ | ------------- binding `first_dropped` declared here
+...
LL | foo1 = Foo(1, &first_dropped);
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `first_dropped` does not live long enough
--> $DIR/issue28498-reject-passed-to-fn.rs:34:19
|
+LL | let (foo1, first_dropped);
+ | ------------- binding `first_dropped` declared here
+...
LL | foo1 = Foo(1, &first_dropped, Box::new(callback));
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `first_dropped` does not live long enough
--> $DIR/issue28498-reject-trait-bound.rs:34:19
|
+LL | let (foo1, first_dropped);
+ | ------------- binding `first_dropped` declared here
+...
LL | foo1 = Foo(1, &first_dropped);
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `b` does not live long enough
--> $DIR/mut-ptr-cant-outlive-ref.rs:8:15
|
+LL | let b = m.borrow();
+ | - binding `b` declared here
LL | p = &*b;
| ^ borrowed value does not live long enough
LL | }
|
LL | let r = {
| - borrow later stored here
-...
+LL | let a = 42;
+ | - binding `a` declared here
+LL | let b = 42;
LL | &a..&b
| ^^ borrowed value does not live long enough
LL | };
|
LL | let r = {
| - borrow later stored here
-...
+LL | let a = 42;
+LL | let b = 42;
+ | - binding `b` declared here
LL | &a..&b
| ^^ borrowed value does not live long enough
LL | };
error[E0597]: `c` does not live long enough
--> $DIR/regionck-unboxed-closure-lifetimes.rs:8:21
|
+LL | let c = 1;
+ | - binding `c` declared here
LL | let c_ref = &c;
| ^^ borrowed value does not live long enough
...
error[E0597]: `tmp0` does not live long enough
--> $DIR/regions-close-over-type-parameter-2.rs:23:20
|
+LL | let tmp0 = 3;
+ | ---- binding `tmp0` declared here
LL | let tmp1 = &tmp0;
| ^^^^^ borrowed value does not live long enough
LL | repeater3(tmp1)
--> $DIR/regions-escape-loop-via-variable.rs:11:13
|
LL | let x = 1 + *p;
- | -- borrow later used here
+ | - -- borrow later used here
+ | |
+ | binding `x` declared here
LL | p = &x;
| ^^ borrowed value does not live long enough
LL | }
--> $DIR/regions-escape-loop-via-vec.rs:5:11
|
LL | let mut _y = vec![&mut x];
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | while x < 10 {
| ^ use of borrowed `x`
LL | let mut z = x;
--> $DIR/regions-escape-loop-via-vec.rs:6:21
|
LL | let mut _y = vec![&mut x];
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | while x < 10 {
LL | let mut z = x;
| ^ use of borrowed `x`
error[E0597]: `z` does not live long enough
--> $DIR/regions-escape-loop-via-vec.rs:7:17
|
+LL | let mut z = x;
+ | ----- binding `z` declared here
LL | _y.push(&mut z);
- | --------^^^^^^-
- | | |
- | | borrowed value does not live long enough
- | borrow later used here
+ | ^^^^^^ borrowed value does not live long enough
...
LL | }
| - `z` dropped here while still borrowed
--> $DIR/regions-escape-loop-via-vec.rs:9:9
|
LL | let mut _y = vec![&mut x];
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | _y.push(&mut z);
| --------------- borrow later used here
error[E0597]: `*x` does not live long enough
--> $DIR/regions-infer-borrow-scope-within-loop.rs:13:20
|
+LL | let x = make_box();
+ | - binding `x` declared here
+...
LL | y = borrow(&*x);
| ^^^ borrowed value does not live long enough
...
LL | let bad = {
| --- borrow later stored here
LL | let x = 1;
+ | - binding `x` declared here
LL | let y = &x;
| ^^ borrowed value does not live long enough
...
LL | let lock = {
| ---- borrow later stored here
LL | let x = 1;
+ | - binding `x` declared here
LL | Mutex::new(&x)
| ^^ borrowed value does not live long enough
LL | };
LL | let lock = {
| ---- borrow later stored here
LL | let x = 1;
+ | - binding `x` declared here
LL | RwLock::new(&x)
| ^^ borrowed value does not live long enough
LL | };
|
LL | let (_tx, rx) = {
| --- borrow later used here
-...
+LL | let x = 1;
+ | - binding `x` declared here
+LL | let (tx, rx) = mpsc::channel();
LL | let _ = tx.send(&x);
| ^^ borrowed value does not live long enough
LL | (tx, rx)
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/send-is-not-static-std-sync.rs:13:10
|
+LL | let y = Box::new(1);
+ | - binding `y` declared here
+LL | let lock = Mutex::new(&x);
LL | *lock.lock().unwrap() = &*y;
| --- borrow of `*y` occurs here
LL | drop(y);
error[E0597]: `z` does not live long enough
--> $DIR/send-is-not-static-std-sync.rs:16:33
|
+LL | let z = 2;
+ | - binding `z` declared here
LL | *lock.lock().unwrap() = &z;
| ^^ borrowed value does not live long enough
LL | }
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/send-is-not-static-std-sync.rs:27:10
|
+LL | let y = Box::new(1);
+ | - binding `y` declared here
+LL | let lock = RwLock::new(&x);
LL | *lock.write().unwrap() = &*y;
| --- borrow of `*y` occurs here
LL | drop(y);
error[E0597]: `z` does not live long enough
--> $DIR/send-is-not-static-std-sync.rs:30:34
|
+LL | let z = 2;
+ | - binding `z` declared here
LL | *lock.write().unwrap() = &z;
| ^^ borrowed value does not live long enough
LL | }
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/send-is-not-static-std-sync.rs:43:10
|
+LL | let y = Box::new(1);
+ | - binding `y` declared here
+...
LL | tx.send(&*y);
| --- borrow of `*y` occurs here
LL | drop(y);
error[E0597]: `z` does not live long enough
--> $DIR/send-is-not-static-std-sync.rs:46:17
|
+LL | let z = 2;
+ | - binding `z` declared here
LL | tx.send(&z).unwrap();
| ^^ borrowed value does not live long enough
LL | }
error[E0597]: `c2` does not live long enough
--> $DIR/vec-must-not-hide-type-from-dropck.rs:117:24
|
+LL | let (mut c1, mut c2);
+ | ------ binding `c2` declared here
+...
LL | c1.v[0].v.set(Some(&c2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `c1` does not live long enough
--> $DIR/vec-must-not-hide-type-from-dropck.rs:119:24
|
+LL | let (mut c1, mut c2);
+ | ------ binding `c1` declared here
+...
LL | c2.v[0].v.set(Some(&c1));
| ^^^ borrowed value does not live long enough
LL |
error[E0597]: `x` does not live long enough
--> $DIR/vec_refs_data_with_early_death.rs:17:12
|
+LL | let x: i8 = 3;
+ | - binding `x` declared here
+...
LL | v.push(&x);
| ^^ borrowed value does not live long enough
...
error[E0597]: `y` does not live long enough
--> $DIR/vec_refs_data_with_early_death.rs:19:12
|
+LL | let y: i8 = 4;
+ | - binding `y` declared here
+...
LL | v.push(&y);
| ^^ borrowed value does not live long enough
...
LL | let dangling = {
| -------- borrow later stored here
LL | let pointer = Box::new(42);
+ | ------- binding `pointer` declared here
LL | f2.xmute(&pointer)
| ^^^^^^^^ borrowed value does not live long enough
LL | };
--- /dev/null
+// #![feature(staged_api)] // note: `staged_api` not enabled
+
+#![stable(feature = "foo", since = "1.0.0")]
+//~^ ERROR stability attributes may not be used outside of the standard library
+
+#[unstable(feature = "foo", issue = "none")]
+//~^ ERROR stability attributes may not be used outside of the standard library
+fn foo_unstable() {}
+
+fn main() {}
--- /dev/null
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-106589.rs:6:1
+ |
+LL | #[unstable(feature = "foo", issue = "none")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-106589.rs:3:1
+ |
+LL | #![stable(feature = "foo", since = "1.0.0")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0734`.
error[E0597]: `x` does not live long enough
--> $DIR/static-lifetime-bound.rs:5:7
|
+LL | let x = 0;
+ | - binding `x` declared here
LL | f(&x);
| --^^-
| | |
--> $DIR/static-reference-to-fn-1.rs:17:15
|
LL | func: &foo,
- | ^^^^ expected fn pointer, found fn item
+ | ^^^^
+ | |
+ | expected fn pointer, found fn item
+ | help: consider casting to a fn pointer: `&(foo as fn() -> Option<isize>)`
|
= note: expected reference `&fn() -> Option<isize>`
found reference `&fn() -> Option<isize> {foo}`
+ = note: fn items are distinct from fn pointers
error: aborting due to previous error
| ^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `Cell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
note: required by a bound in `test`
--> $DIR/not-sync.rs:5:12
|
| ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: required by a bound in `test`
--> $DIR/not-sync.rs:5:12
|
| ^
= note: expected tuple `(i32, bool)`
found type `{integer}`
+help: the type constructed contains `{integer}` due to the type of the argument passed
+ --> $DIR/args-instead-of-tuple-errors.rs:6:34
+ |
+LL | let _: Option<(i32, bool)> = Some(1, 2);
+ | ^^^^^-^^^^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
help: remove the extra argument
|
= note: expected tuple `(i32,)`
found type `usize`
+help: the type constructed contains `usize` due to the type of the argument passed
+ --> $DIR/args-instead-of-tuple-errors.rs:14:29
+ |
+LL | let _: Option<(i32,)> = Some(5_usize);
+ | ^^^^^-------^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
|
= note: expected tuple `(i32,)`
found type `usize`
+help: the type constructed contains `usize` due to the type of the argument passed
+ --> $DIR/args-instead-of-tuple-errors.rs:17:29
+ |
+LL | let _: Option<(i32,)> = Some((5_usize));
+ | ^^^^^---------^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
--- /dev/null
+struct Foo;
+
+impl Foo {
+ const A_CONST: usize = 1;
+
+ fn foo() -> usize {
+ A_CONST //~ ERROR cannot find value `A_CONST` in this scope
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0425]: cannot find value `A_CONST` in this scope
+ --> $DIR/assoc-const-without-self.rs:7:9
+ |
+LL | A_CONST
+ | ^^^^^^^ not found in this scope
+ |
+help: consider using the associated constant
+ |
+LL | Self::A_CONST
+ | ++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/borrow-for-loop-head.rs:4:18
|
+LL | let a = vec![1, 2, 3];
+ | - binding `a` declared here
LL | for i in &a {
| -- borrow of `a` occurs here
LL | for j in a {
--- /dev/null
+trait Foo {}
+
+impl Foo for i32 {}
+
+fn needs_foo(_: impl Foo) {}
+
+fn test(x: &Box<dyn Fn() -> i32>) {
+ needs_foo(x);
+ //~^ ERROR the trait bound
+ //~| HELP use parentheses to call this trait object
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `&Box<dyn Fn() -> i32>: Foo` is not satisfied
+ --> $DIR/call-on-unimplemented-with-autoderef.rs:8:15
+ |
+LL | needs_foo(x);
+ | --------- ^ the trait `Foo` is not implemented for `&Box<dyn Fn() -> i32>`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `needs_foo`
+ --> $DIR/call-on-unimplemented-with-autoderef.rs:5:22
+ |
+LL | fn needs_foo(_: impl Foo) {}
+ | ^^^ required by this bound in `needs_foo`
+help: use parentheses to call this trait object
+ |
+LL | needs_foo(x());
+ | ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--> $DIR/constrain-suggest-ice.rs:6:9
|
LL | F
- | ^
- |
-help: a local variable with a similar name exists
- |
-LL | x
- | ~
-help: you might be missing a type parameter
- |
-LL | struct Bug<S, F>{
- | +++
+ | ^ help: a local variable with a similar name exists: `x`
error: generic `Self` types are currently not permitted in anonymous constants
--> $DIR/constrain-suggest-ice.rs:3:21
#![allow(unused, nonstandard_style)]
-#![deny(bindings_with_variant_name)]
// If an enum has two different variants,
// then it cannot be matched upon in a function argument.
-// It still gets a warning, but no suggestions.
+// It still gets an error, but no suggestions.
enum Foo {
C,
D,
error[E0170]: pattern binding `C` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-88730.rs:12:8
+ --> $DIR/issue-88730.rs:11:8
|
LL | fn foo(C: Foo) {}
| ^
|
-note: the lint level is defined here
- --> $DIR/issue-88730.rs:2:9
- |
-LL | #![deny(bindings_with_variant_name)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: `#[deny(bindings_with_variant_name)]` on by default
error[E0170]: pattern binding `C` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-88730.rs:15:9
+ --> $DIR/issue-88730.rs:14:9
|
LL | let C = Foo::D;
| ^
--- /dev/null
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::rc::Rc;
+
+pub struct Trader<'a> {
+ closure: Box<dyn Fn(&mut Trader) + 'a>,
+}
+
+impl<'a> Trader<'a> {
+ pub fn new() -> Self {
+ Trader {
+ closure: Box::new(|_| {}),
+ }
+ }
+ pub fn set_closure(&mut self, function: impl Fn(&mut Trader) + 'a) {
+ //foo
+ }
+}
+
+fn main() {
+ let closure = |trader : Trader| {
+ println!("Woooosh!");
+ };
+
+ let mut trader = Trader::new();
+ trader.set_closure(closure);
+ //~^ ERROR type mismatch in closure arguments
+}
--- /dev/null
+error[E0631]: type mismatch in closure arguments
+ --> $DIR/late-bound-in-borrow-closure-sugg.rs:26:24
+ |
+LL | let closure = |trader : Trader| {
+ | ----------------- found signature defined here
+...
+LL | trader.set_closure(closure);
+ | ----------- ^^^^^^^ expected due to this
+ | |
+ | required by a bound introduced by this call
+ |
+ = note: expected closure signature `for<'a, 'b> fn(&'a mut Trader<'b>) -> _`
+ found closure signature `for<'a> fn(Trader<'a>) -> _`
+note: required by a bound in `Trader::<'a>::set_closure`
+ --> $DIR/late-bound-in-borrow-closure-sugg.rs:15:50
+ |
+LL | pub fn set_closure(&mut self, function: impl Fn(&mut Trader) + 'a) {
+ | ^^^^^^^^^^^^^^^ required by this bound in `Trader::<'a>::set_closure`
+help: consider borrowing the argument
+ |
+LL | let closure = |trader : &mut Trader| {
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0631`.
| -- captured by this `FnMut` closure
LL | // Shouldn't suggest `move ||.as_ref()` here
LL | move || {
- | ^^^^^^^ move out of `var` occurs here
+ | ^^^^^^^ `var` is moved here
LL |
LL | var = Some(NotCopyable);
| ---
LL | let ref _moved @ _from = String::from("foo");
| ----------^^^-----
| | |
- | | value moved into `_from` here
- | value borrowed, by `_moved`, here
+ | | value is moved into `_from` here
+ | value is borrowed by `_moved` here
error: cannot move out of value because it is borrowed
--> $DIR/ref-pattern-binding.rs:15:9
LL | let ref _moved @ S { f } = S { f: String::from("foo") };
| ----------^^^^^^^-^^
| | |
- | | value moved into `f` here
- | value borrowed, by `_moved`, here
+ | | value is moved into `f` here
+ | value is borrowed by `_moved` here
error: borrow of moved value
--> $DIR/ref-pattern-binding.rs:18:9
|
= note: expected reference `&str`
found closure `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]`
+help: the return type of this call is `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]` due to the type of the argument passed
+ --> $DIR/sugg-else-for-closure.rs:6:14
+ |
+LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
+ | ^^^^^^^^^^^^-------------------------------^
+ | |
+ | this argument influences the return type of `unwrap_or`
note: associated function defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
help: try calling `unwrap_or_else` instead
--- /dev/null
+// run-rustfix
+
+//issue #106496
+
+struct S;
+
+trait X {}
+impl X for S {}
+
+fn foo<T: X>(_: &T) {}
+fn test_foo() {
+ let hello = &S;
+ foo(hello);
+ //~^ ERROR mismatched types
+}
+
+fn bar(_: &String) {}
+fn test_bar() {
+ let v = String::from("hello");
+ let s = &v;
+ bar(s);
+ //~^ ERROR mismatched types
+}
+
+fn main() {
+ test_foo();
+ test_bar();
+}
--- /dev/null
+// run-rustfix
+
+//issue #106496
+
+struct S;
+
+trait X {}
+impl X for S {}
+
+fn foo<T: X>(_: &T) {}
+fn test_foo() {
+ let hello = &S;
+ foo(*hello);
+ //~^ ERROR mismatched types
+}
+
+fn bar(_: &String) {}
+fn test_bar() {
+ let v = String::from("hello");
+ let s = &v;
+ bar(*s);
+ //~^ ERROR mismatched types
+}
+
+fn main() {
+ test_foo();
+ test_bar();
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/suggest-remove-deref.rs:13:9
+ |
+LL | foo(*hello);
+ | --- ^^^^^^ expected reference, found struct `S`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected reference `&_`
+ found struct `S`
+note: function defined here
+ --> $DIR/suggest-remove-deref.rs:10:4
+ |
+LL | fn foo<T: X>(_: &T) {}
+ | ^^^ -----
+help: consider removing deref here
+ |
+LL - foo(*hello);
+LL + foo(hello);
+ |
+
+error[E0308]: mismatched types
+ --> $DIR/suggest-remove-deref.rs:21:9
+ |
+LL | bar(*s);
+ | --- ^^ expected `&String`, found struct `String`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/suggest-remove-deref.rs:17:4
+ |
+LL | fn bar(_: &String) {}
+ | ^^^ ----------
+help: consider removing deref here
+ |
+LL - bar(*s);
+LL + bar(s);
+ |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// Tests that a suggestion is issued for type mismatch errors when a
+// u8 is expected and a char literal which is ASCII is supplied.
+
+fn foo(_t: u8) {}
+
+fn main() {
+ let _x: u8 = 'X';
+ //~^ ERROR: mismatched types [E0308]
+ //~| HELP: if you meant to write a byte literal, prefix with `b`
+
+ foo('#');
+ //~^ ERROR: mismatched types [E0308]
+ //~| HELP: if you meant to write a byte literal, prefix with `b`
+
+ // Do not issue the suggestion if the char literal isn't ASCII
+ let _t: u8 = '€';
+ //~^ ERROR: mismatched types [E0308]
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch-byte-literal.rs:7:18
+ |
+LL | let _x: u8 = 'X';
+ | -- ^^^ expected `u8`, found `char`
+ | |
+ | expected due to this
+ |
+help: if you meant to write a byte literal, prefix with `b`
+ |
+LL | let _x: u8 = b'X';
+ | ~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch-byte-literal.rs:11:9
+ |
+LL | foo('#');
+ | --- ^^^ expected `u8`, found `char`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/type-mismatch-byte-literal.rs:4:4
+ |
+LL | fn foo(_t: u8) {}
+ | ^^^ ------
+help: if you meant to write a byte literal, prefix with `b`
+ |
+LL | foo(b'#');
+ | ~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch-byte-literal.rs:16:18
+ |
+LL | let _t: u8 = '€';
+ | -- ^^^ expected `u8`, found `char`
+ | |
+ | expected due to this
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
-error: symbol-name(_ZN5basic4main17hcbad207c0eeb0b3bE)
+error: symbol-name(_ZN5basic4main17he9f658e438f1cac0E)
--> $DIR/basic.rs:8:1
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
-error: demangling(basic::main::hcbad207c0eeb0b3b)
+error: demangling(basic::main::he9f658e438f1cac0)
--> $DIR/basic.rs:8:1
|
LL | #[rustc_symbol_name]
}
impl Foo for [u8; 1 + 2] {
- #[rustc_def_path] //~ ERROR def-path(<[u8; _] as Foo>::baz)
- fn baz() { }
+ #[rustc_def_path] //~ ERROR def-path(<[u8; 1 + 2] as Foo>::baz)
+ fn baz() {}
}
-fn main() {
-}
+fn main() {}
-error: def-path(<[u8; _] as Foo>::baz)
+error: def-path(<[u8; 1 + 2] as Foo>::baz)
--> $DIR/impl2.rs:11:5
|
LL | #[rustc_def_path]
-error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h2f2efcf580c9b1eeE)
+error: symbol-name(_ZN11issue_609253foo37Foo$LT$issue_60925..llv$u6d$..Foo$GT$3foo17h13209029be24b923E)
--> $DIR/issue-60925.rs:21:9
|
LL | #[rustc_symbol_name]
| ^^^^^^^^^^^^^^^^^^^^
-error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h2f2efcf580c9b1ee)
+error: demangling(issue_60925::foo::Foo<issue_60925::llvm::Foo>::foo::h13209029be24b923)
--> $DIR/issue-60925.rs:21:9
|
LL | #[rustc_symbol_name]
--- /dev/null
+// MutexGuard<Cell<i32>> must not be Sync, that would be unsound.
+use std::sync::Mutex;
+use std::cell::Cell;
+
+fn test_sync<T: Sync>(_t: T) {}
+
+fn main()
+{
+ let m = Mutex::new(Cell::new(0i32));
+ let guard = m.lock().unwrap();
+ test_sync(guard);
+ //~^ ERROR `Cell<i32>` cannot be shared between threads safely [E0277]
+}
--- /dev/null
+error[E0277]: `Cell<i32>` cannot be shared between threads safely
+ --> $DIR/mutexguard-sync.rs:11:15
+ |
+LL | test_sync(guard);
+ | --------- ^^^^^ `Cell<i32>` cannot be shared between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Sync` is not implemented for `Cell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
+ = note: required for `MutexGuard<'_, Cell<i32>>` to implement `Sync`
+note: required by a bound in `test_sync`
+ --> $DIR/mutexguard-sync.rs:5:17
+ |
+LL | fn test_sync<T: Sync>(_t: T) {}
+ | ^^^^ required by this bound in `test_sync`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+fn require_sync<T: Sync>() {}
+//~^ NOTE required by this bound in `require_sync`
+//~| NOTE required by this bound in `require_sync`
+//~| NOTE required by this bound in `require_sync`
+//~| NOTE required by this bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+
+fn main() {
+ require_sync::<std::cell::Cell<()>>();
+ //~^ ERROR `Cell<()>` cannot be shared between threads safely
+ //~| NOTE `Cell<()>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
+
+ require_sync::<std::cell::Cell<u8>>();
+ //~^ ERROR `Cell<u8>` cannot be shared between threads safely
+ //~| NOTE `Cell<u8>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead
+
+ require_sync::<std::cell::Cell<i32>>();
+ //~^ ERROR `Cell<i32>` cannot be shared between threads safely
+ //~| NOTE `Cell<i32>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
+
+ require_sync::<std::cell::Cell<bool>>();
+ //~^ ERROR `Cell<bool>` cannot be shared between threads safely
+ //~| NOTE `Cell<bool>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead
+}
--- /dev/null
+error[E0277]: `Cell<()>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:12:20
+ |
+LL | require_sync::<std::cell::Cell<()>>();
+ | ^^^^^^^^^^^^^^^^^^^ `Cell<()>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<()>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error[E0277]: `Cell<u8>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:17:20
+ |
+LL | require_sync::<std::cell::Cell<u8>>();
+ | ^^^^^^^^^^^^^^^^^^^ `Cell<u8>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<u8>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error[E0277]: `Cell<i32>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:22:20
+ |
+LL | require_sync::<std::cell::Cell<i32>>();
+ | ^^^^^^^^^^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error[E0277]: `Cell<bool>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:27:20
+ |
+LL | require_sync::<std::cell::Cell<bool>>();
+ | ^^^^^^^^^^^^^^^^^^^^^ `Cell<bool>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<bool>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(once_cell)]
+
+fn require_sync<T: Sync>() {}
+//~^ NOTE required by this bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+
+fn main() {
+ require_sync::<std::cell::OnceCell<()>>();
+ //~^ ERROR `OnceCell<()>` cannot be shared between threads safely
+ //~| NOTE `OnceCell<()>` cannot be shared between threads safely
+ //~| NOTE use `std::sync::OnceLock` instead
+}
--- /dev/null
+error[E0277]: `OnceCell<()>` cannot be shared between threads safely
+ --> $DIR/suggest-once-cell.rs:8:20
+ |
+LL | require_sync::<std::cell::OnceCell<()>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ `OnceCell<()>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `OnceCell<()>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-once-cell.rs:3:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(once_cell)]
+
+fn require_sync<T: Sync>() {}
+//~^ NOTE required by this bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+
+fn main() {
+ require_sync::<std::cell::RefCell<()>>();
+ //~^ ERROR `RefCell<()>` cannot be shared between threads safely
+ //~| NOTE `RefCell<()>` cannot be shared between threads safely
+ //~| NOTE use `std::sync::RwLock` instead
+}
--- /dev/null
+error[E0277]: `RefCell<()>` cannot be shared between threads safely
+ --> $DIR/suggest-ref-cell.rs:8:20
+ |
+LL | require_sync::<std::cell::RefCell<()>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^ `RefCell<()>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<()>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-ref-cell.rs:3:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
help: consider importing this module instead
|
LL | use test::test as y;
- | ~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
--- /dev/null
+// compile-flags: -Z unpretty=thir-flat
+// check-pass
+
+pub fn main() {}
--- /dev/null
+DefId(0:3 ~ thir_flat[45a6]::main):
+Thir {
+ arms: [],
+ blocks: [
+ Block {
+ targeted_by_break: false,
+ region_scope: Node(1),
+ opt_destruction_scope: None,
+ span: $DIR/thir-flat.rs:4:15: 4:17 (#0),
+ stmts: [],
+ expr: None,
+ safety_mode: Safe,
+ },
+ ],
+ exprs: [
+ Expr {
+ ty: (),
+ temp_lifetime: Some(
+ Node(2),
+ ),
+ span: $DIR/thir-flat.rs:4:15: 4:17 (#0),
+ kind: Block {
+ block: b0,
+ },
+ },
+ Expr {
+ ty: (),
+ temp_lifetime: Some(
+ Node(2),
+ ),
+ span: $DIR/thir-flat.rs:4:15: 4:17 (#0),
+ kind: Scope {
+ region_scope: Node(2),
+ lint_level: Explicit(
+ HirId(DefId(0:3 ~ thir_flat[45a6]::main).2),
+ ),
+ value: e0,
+ },
+ },
+ Expr {
+ ty: (),
+ temp_lifetime: Some(
+ Node(2),
+ ),
+ span: $DIR/thir-flat.rs:4:15: 4:17 (#0),
+ kind: Scope {
+ region_scope: Destruction(2),
+ lint_level: Inherited,
+ value: e1,
+ },
+ },
+ ],
+ stmts: [],
+ params: [],
+}
+
--- /dev/null
+// check-pass
+// compile-flags: -Zunpretty=thir-tree
+
+enum Bar {
+ First,
+ Second,
+ Third,
+}
+
+enum Foo {
+ FooOne(Bar),
+ FooTwo,
+}
+
+fn has_match(foo: Foo) -> bool {
+ match foo {
+ Foo::FooOne(Bar::First) => true,
+ Foo::FooOne(_) => false,
+ Foo::FooTwo => true,
+ }
+}
+
+fn main() {}
--- /dev/null
+DefId(0:16 ~ thir_tree_match[3c9a]::has_match):
+params: [
+ Param {
+ ty: Foo
+ ty_span: Some($DIR/thir-tree-match.rs:15:19: 15:22 (#0))
+ self_kind: None
+ hir_id: Some(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).1))
+ param: Some(
+ Pat: {
+ ty: Foo
+ span: $DIR/thir-tree-match.rs:15:14: 15:17 (#0)
+ kind: PatKind {
+ Binding {
+ mutability: Not
+ name: "foo"
+ mode: ByValue
+ var: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).2))
+ ty: Foo
+ is_primary: true
+ subpattern: None
+ }
+ }
+ }
+ )
+ }
+]
+body:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0)
+ kind:
+ Scope {
+ region_scope: Destruction(26)
+ lint_level: Inherited
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0)
+ kind:
+ Scope {
+ region_scope: Node(26)
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).26))
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0)
+ kind:
+ Block {
+ targeted_by_break: false
+ opt_destruction_scope: None
+ span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0)
+ region_scope: Node(25)
+ safety_mode: Safe
+ stmts: []
+ expr:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0)
+ kind:
+ Scope {
+ region_scope: Node(3)
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).3))
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0)
+ kind:
+ Match {
+ scrutinee:
+ Expr {
+ ty: Foo
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0)
+ kind:
+ Scope {
+ region_scope: Node(4)
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).4))
+ value:
+ Expr {
+ ty: Foo
+ temp_lifetime: Some(Node(26))
+ span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0)
+ kind:
+ VarRef {
+ id: LocalVarId(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).2))
+ }
+ }
+ }
+ }
+ arms: [
+ Arm {
+ pattern:
+ Pat: {
+ ty: Foo
+ span: $DIR/thir-tree-match.rs:17:9: 17:32 (#0)
+ kind: PatKind {
+ Variant {
+ adt_def:
+ AdtDef {
+ did: DefId(0:10 ~ thir_tree_match[3c9a]::Foo)
+ variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[3c9a]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[3c9a]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[3c9a]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[3c9a])) }], flags: NO_VARIANT_FLAGS }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[3c9a]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[3c9a]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], flags: NO_VARIANT_FLAGS }]
+ flags: IS_ENUM
+ repr: ReprOptions { int: None, align: None, pack: None, flags: (empty), field_shuffle_seed: 11573694388057581 }
+ substs: []
+ variant_index: 0
+ subpatterns: [
+ Pat: {
+ ty: Bar
+ span: $DIR/thir-tree-match.rs:17:21: 17:31 (#0)
+ kind: PatKind {
+ Variant {
+ adt_def:
+ AdtDef {
+ did: DefId(0:3 ~ thir_tree_match[3c9a]::Bar)
+ variants: [VariantDef { def_id: DefId(0:4 ~ thir_tree_match[3c9a]::Bar::First), ctor: Some((Const, DefId(0:5 ~ thir_tree_match[3c9a]::Bar::First::{constructor#0}))), name: "First", discr: Relative(0), fields: [], flags: NO_VARIANT_FLAGS }, VariantDef { def_id: DefId(0:6 ~ thir_tree_match[3c9a]::Bar::Second), ctor: Some((Const, DefId(0:7 ~ thir_tree_match[3c9a]::Bar::Second::{constructor#0}))), name: "Second", discr: Relative(1), fields: [], flags: NO_VARIANT_FLAGS }, VariantDef { def_id: DefId(0:8 ~ thir_tree_match[3c9a]::Bar::Third), ctor: Some((Const, DefId(0:9 ~ thir_tree_match[3c9a]::Bar::Third::{constructor#0}))), name: "Third", discr: Relative(2), fields: [], flags: NO_VARIANT_FLAGS }]
+ flags: IS_ENUM
+ repr: ReprOptions { int: None, align: None, pack: None, flags: (empty), field_shuffle_seed: 3125160937860410723 }
+ substs: []
+ variant_index: 0
+ subpatterns: []
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ guard: None
+ body:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(13))
+ span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0)
+ kind:
+ Scope {
+ region_scope: Destruction(13)
+ lint_level: Inherited
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(13))
+ span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0)
+ kind:
+ Scope {
+ region_scope: Node(13)
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).13))
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(13))
+ span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0)
+ kind:
+ Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false)
+
+ }
+ }
+ }
+ }
+ }
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).12))
+ scope: Node(12)
+ span: $DIR/thir-tree-match.rs:17:9: 17:40 (#0)
+ }
+ Arm {
+ pattern:
+ Pat: {
+ ty: Foo
+ span: $DIR/thir-tree-match.rs:18:9: 18:23 (#0)
+ kind: PatKind {
+ Variant {
+ adt_def:
+ AdtDef {
+ did: DefId(0:10 ~ thir_tree_match[3c9a]::Foo)
+ variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[3c9a]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[3c9a]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[3c9a]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[3c9a])) }], flags: NO_VARIANT_FLAGS }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[3c9a]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[3c9a]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], flags: NO_VARIANT_FLAGS }]
+ flags: IS_ENUM
+ repr: ReprOptions { int: None, align: None, pack: None, flags: (empty), field_shuffle_seed: 11573694388057581 }
+ substs: []
+ variant_index: 0
+ subpatterns: [
+ Pat: {
+ ty: Bar
+ span: $DIR/thir-tree-match.rs:18:21: 18:22 (#0)
+ kind: PatKind {
+ Wild
+ }
+ }
+ ]
+ }
+ }
+ }
+ guard: None
+ body:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(19))
+ span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0)
+ kind:
+ Scope {
+ region_scope: Destruction(19)
+ lint_level: Inherited
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(19))
+ span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0)
+ kind:
+ Scope {
+ region_scope: Node(19)
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).19))
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(19))
+ span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0)
+ kind:
+ Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false)
+
+ }
+ }
+ }
+ }
+ }
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).18))
+ scope: Node(18)
+ span: $DIR/thir-tree-match.rs:18:9: 18:32 (#0)
+ }
+ Arm {
+ pattern:
+ Pat: {
+ ty: Foo
+ span: $DIR/thir-tree-match.rs:19:9: 19:20 (#0)
+ kind: PatKind {
+ Variant {
+ adt_def:
+ AdtDef {
+ did: DefId(0:10 ~ thir_tree_match[3c9a]::Foo)
+ variants: [VariantDef { def_id: DefId(0:11 ~ thir_tree_match[3c9a]::Foo::FooOne), ctor: Some((Fn, DefId(0:12 ~ thir_tree_match[3c9a]::Foo::FooOne::{constructor#0}))), name: "FooOne", discr: Relative(0), fields: [FieldDef { did: DefId(0:13 ~ thir_tree_match[3c9a]::Foo::FooOne::0), name: "0", vis: Restricted(DefId(0:0 ~ thir_tree_match[3c9a])) }], flags: NO_VARIANT_FLAGS }, VariantDef { def_id: DefId(0:14 ~ thir_tree_match[3c9a]::Foo::FooTwo), ctor: Some((Const, DefId(0:15 ~ thir_tree_match[3c9a]::Foo::FooTwo::{constructor#0}))), name: "FooTwo", discr: Relative(1), fields: [], flags: NO_VARIANT_FLAGS }]
+ flags: IS_ENUM
+ repr: ReprOptions { int: None, align: None, pack: None, flags: (empty), field_shuffle_seed: 11573694388057581 }
+ substs: []
+ variant_index: 1
+ subpatterns: []
+ }
+ }
+ }
+ guard: None
+ body:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(24))
+ span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0)
+ kind:
+ Scope {
+ region_scope: Destruction(24)
+ lint_level: Inherited
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(24))
+ span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0)
+ kind:
+ Scope {
+ region_scope: Node(24)
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).24))
+ value:
+ Expr {
+ ty: bool
+ temp_lifetime: Some(Node(24))
+ span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0)
+ kind:
+ Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false)
+
+ }
+ }
+ }
+ }
+ }
+ lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[3c9a]::has_match).23))
+ scope: Node(23)
+ span: $DIR/thir-tree-match.rs:19:9: 19:28 (#0)
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+DefId(0:17 ~ thir_tree_match[3c9a]::main):
+params: [
+]
+body:
+ Expr {
+ ty: ()
+ temp_lifetime: Some(Node(2))
+ span: $DIR/thir-tree-match.rs:23:11: 23:13 (#0)
+ kind:
+ Scope {
+ region_scope: Destruction(2)
+ lint_level: Inherited
+ value:
+ Expr {
+ ty: ()
+ temp_lifetime: Some(Node(2))
+ span: $DIR/thir-tree-match.rs:23:11: 23:13 (#0)
+ kind:
+ Scope {
+ region_scope: Node(2)
+ lint_level: Explicit(HirId(DefId(0:17 ~ thir_tree_match[3c9a]::main).2))
+ value:
+ Expr {
+ ty: ()
+ temp_lifetime: Some(Node(2))
+ span: $DIR/thir-tree-match.rs:23:11: 23:13 (#0)
+ kind:
+ Block {
+ targeted_by_break: false
+ opt_destruction_scope: None
+ span: $DIR/thir-tree-match.rs:23:11: 23:13 (#0)
+ region_scope: Node(1)
+ safety_mode: Safe
+ stmts: []
+ expr: []
+ }
+ }
+ }
+ }
+ }
+ }
+
+
--- /dev/null
+// compile-flags: -Z unpretty=thir-tree
+// check-pass
+
+pub fn main() {}
--- /dev/null
+DefId(0:3 ~ thir_tree[8f1d]::main):
+params: [
+]
+body:
+ Expr {
+ ty: ()
+ temp_lifetime: Some(Node(2))
+ span: $DIR/thir-tree.rs:4:15: 4:17 (#0)
+ kind:
+ Scope {
+ region_scope: Destruction(2)
+ lint_level: Inherited
+ value:
+ Expr {
+ ty: ()
+ temp_lifetime: Some(Node(2))
+ span: $DIR/thir-tree.rs:4:15: 4:17 (#0)
+ kind:
+ Scope {
+ region_scope: Node(2)
+ lint_level: Explicit(HirId(DefId(0:3 ~ thir_tree[8f1d]::main).2))
+ value:
+ Expr {
+ ty: ()
+ temp_lifetime: Some(Node(2))
+ span: $DIR/thir-tree.rs:4:15: 4:17 (#0)
+ kind:
+ Block {
+ targeted_by_break: false
+ opt_destruction_scope: None
+ span: $DIR/thir-tree.rs:4:15: 4:17 (#0)
+ region_scope: Node(1)
+ safety_mode: Safe
+ stmts: []
+ expr: []
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+++ /dev/null
-// compile-flags: -Z unpretty=thir-tree
-// check-pass
-
-pub fn main() {}
+++ /dev/null
-DefId(0:3 ~ thir_tree[8f1d]::main):
-Thir {
- arms: [],
- blocks: [
- Block {
- targeted_by_break: false,
- region_scope: Node(1),
- opt_destruction_scope: None,
- span: $DIR/thir-tree.rs:4:15: 4:17 (#0),
- stmts: [],
- expr: None,
- safety_mode: Safe,
- },
- ],
- exprs: [
- Expr {
- ty: (),
- temp_lifetime: Some(
- Node(2),
- ),
- span: $DIR/thir-tree.rs:4:15: 4:17 (#0),
- kind: Block {
- block: b0,
- },
- },
- Expr {
- ty: (),
- temp_lifetime: Some(
- Node(2),
- ),
- span: $DIR/thir-tree.rs:4:15: 4:17 (#0),
- kind: Scope {
- region_scope: Node(2),
- lint_level: Explicit(
- HirId(DefId(0:3 ~ thir_tree[8f1d]::main).2),
- ),
- value: e0,
- },
- },
- Expr {
- ty: (),
- temp_lifetime: Some(
- Node(2),
- ),
- span: $DIR/thir-tree.rs:4:15: 4:17 (#0),
- kind: Scope {
- region_scope: Destruction(2),
- lint_level: Inherited,
- value: e1,
- },
- },
- ],
- stmts: [],
- params: [],
-}
-
--- /dev/null
+// check-pass
+
+#![feature(trait_alias)]
+
+struct MyStruct {}
+trait MyFn = Fn(&MyStruct);
+
+fn foo(_: impl MyFn) {}
+
+fn main() {
+ foo(|_| {});
+}
error[E0597]: `s` does not live long enough
--> $DIR/check-trait-object-bounds-3.rs:15:34
|
+LL | let s = String::from("abcdef");
+ | - binding `s` declared here
LL | z = f::<dyn X<Y = &str>>(&s);
| ---------------------^^-
| | |
error[E0597]: `person` does not live long enough
--> $DIR/coercion-generic-regions.rs:17:24
|
+LL | let person = "Fred".to_string();
+ | ------ binding `person` declared here
LL | let person: &str = &person;
| ^^^^^^^
| |
LL | impl<T> Copy for Foo<T> {}
| ^^^^^^ the trait `TraitFoo` is not implemented for `T`
|
+note: required for `Foo<T>` to implement `Clone`
+ --> $DIR/copy-impl-cannot-normalize.rs:12:9
+ |
+LL | impl<T> Clone for Foo<T>
+ | ^^^^^ ^^^^^^
+LL | where
+LL | T: TraitFoo,
+ | -------- unsatisfied trait bound introduced here
+note: required by a bound in `Copy`
+ --> $SRC_DIR/core/src/marker.rs:LL:COL
help: consider restricting type parameter `T`
|
LL | impl<T: TraitFoo> Copy for Foo<T> {}
--- /dev/null
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/copy-is-not-modulo-regions.rs:13:21
+ |
+LL | struct Bar<'lt>(Foo<'lt>);
+ | -------- this field does not implement `Copy`
+...
+LL | impl<'any> Copy for Bar<'any> {}
+ | ^^^^^^^^^
+ |
+note: the `Copy` impl for `Foo<'any>` requires that `'any: 'static`
+ --> $DIR/copy-is-not-modulo-regions.rs:10:17
+ |
+LL | struct Bar<'lt>(Foo<'lt>);
+ | ^^^^^^^^
+help: consider restricting type parameter `'any`
+ |
+LL | impl<'any: 'static> Copy for Bar<'any> {}
+ | +++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0204`.
--- /dev/null
+// revisions: not_static yes_static
+//[yes_static] check-pass
+
+#[derive(Clone)]
+struct Foo<'lt>(&'lt ());
+
+impl Copy for Foo<'static> {}
+
+#[derive(Clone)]
+struct Bar<'lt>(Foo<'lt>);
+
+#[cfg(not_static)]
+impl<'any> Copy for Bar<'any> {}
+//[not_static]~^ the trait `Copy` may not be implemented for this type
+
+#[cfg(yes_static)]
+impl<'any> Copy for Bar<'static> {}
+
+fn main() {}
--- /dev/null
+// check-pass
+
+#[derive(Clone)]
+struct A<'a, T>(&'a T);
+
+impl<'a, T: Copy + 'a> Copy for A<'a, T> {}
+
+#[derive(Clone)]
+struct B<'a, T>(A<'a, T>);
+
+// `T: '_` should be implied by `WF(B<'_, T>)`.
+impl<T: Copy> Copy for B<'_, T> {}
+
+fn main() {}
--- /dev/null
+// There are two different instances to check that even if
+// the trait is implemented for the output of a function,
+// it will still be displayed if the function itself implements a trait.
+trait Foo {}
+
+impl Foo for fn() -> bool {}
+impl Foo for bool {}
+
+fn example() -> bool {
+ true
+}
+
+trait NoOtherFoo {}
+
+impl NoOtherFoo for fn() -> bool {}
+
+fn do_on_foo(v: impl Foo) {}
+fn do_on_single_foo(v: impl NoOtherFoo) {}
+
+fn main() {
+ do_on_foo(example);
+ //~^ ERROR the trait bound
+
+ do_on_single_foo(example);
+ //~^ ERROR the trait bound
+}
--- /dev/null
+error[E0277]: the trait bound `fn() -> bool {example}: Foo` is not satisfied
+ --> $DIR/fn-trait-cast-diagnostic.rs:21:15
+ |
+LL | do_on_foo(example);
+ | --------- ^^^^^^^ the trait `Foo` is not implemented for fn item `fn() -> bool {example}`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `do_on_foo`
+ --> $DIR/fn-trait-cast-diagnostic.rs:17:22
+ |
+LL | fn do_on_foo(v: impl Foo) {}
+ | ^^^ required by this bound in `do_on_foo`
+help: use parentheses to call this function
+ |
+LL | do_on_foo(example());
+ | ++
+help: the trait `Foo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
+ |
+LL | do_on_foo(example as fn() -> bool);
+ | +++++++++++++++
+
+error[E0277]: the trait bound `fn() -> bool {example}: NoOtherFoo` is not satisfied
+ --> $DIR/fn-trait-cast-diagnostic.rs:24:22
+ |
+LL | do_on_single_foo(example);
+ | ---------------- ^^^^^^^ the trait `NoOtherFoo` is not implemented for fn item `fn() -> bool {example}`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `do_on_single_foo`
+ --> $DIR/fn-trait-cast-diagnostic.rs:18:29
+ |
+LL | fn do_on_single_foo(v: impl NoOtherFoo) {}
+ | ^^^^^^^^^^ required by this bound in `do_on_single_foo`
+help: the trait `NoOtherFoo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
+ |
+LL | do_on_single_foo(example as fn() -> bool);
+ | +++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#[derive(Clone)] //~ trait objects must include the `dyn` keyword
+ //~| trait objects must include the `dyn` keyword
+struct Foo;
+trait Foo {} //~ the name `Foo` is defined multiple times
+fn main() {}
--- /dev/null
+error[E0428]: the name `Foo` is defined multiple times
+ --> $DIR/issue-106072.rs:4:1
+ |
+LL | struct Foo;
+ | ----------- previous definition of the type `Foo` here
+LL | trait Foo {}
+ | ^^^^^^^^^ `Foo` redefined here
+ |
+ = note: `Foo` must be defined only once in the type namespace of this module
+
+error[E0782]: trait objects must include the `dyn` keyword
+ --> $DIR/issue-106072.rs:1:10
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ |
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0782]: trait objects must include the `dyn` keyword
+ --> $DIR/issue-106072.rs:1:10
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ |
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0428, E0782.
+For more information about an error, try `rustc --explain E0428`.
LL | impl<T> Complete for T {}
| ^ the trait `Copy` is not implemented for `T`
|
+note: required for `T` to implement `Partial`
+ --> $DIR/issue-43784-supertrait.rs:1:11
+ |
+LL | pub trait Partial: Copy {
+ | ^^^^^^^
note: required by a bound in `Complete`
--> $DIR/issue-43784-supertrait.rs:4:21
|
//~| ERROR cannot find type `NotDefined` in this scope
//~| ERROR cannot find type `N` in this scope
//~| ERROR cannot find type `N` in this scope
-//~| ERROR `i32` is not an iterator
#[derive(Clone, Copy)]
//~^ ERROR the trait `Copy` may not be implemented for this type
struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
//~^ ERROR cannot find type `NotDefined` in this scope
//~| ERROR cannot find type `N` in this scope
-//~| ERROR `i32` is not an iterator
fn main() {}
| ++++++++++++
error[E0412]: cannot find type `N` in this scope
- --> $DIR/issue-50480.rs:12:18
+ --> $DIR/issue-50480.rs:11:18
|
LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| - ^
| +++
error[E0412]: cannot find type `NotDefined` in this scope
- --> $DIR/issue-50480.rs:12:21
+ --> $DIR/issue-50480.rs:11:21
|
LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| ^^^^^^^^^^ not found in this scope
-error[E0277]: `i32` is not an iterator
- --> $DIR/issue-50480.rs:3:27
- |
-LL | struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator
- |
- = help: the trait `Iterator` is not implemented for `i32`
- = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end`
-
error[E0204]: the trait `Copy` may not be implemented for this type
--> $DIR/issue-50480.rs:1:17
|
|
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0277]: `i32` is not an iterator
- --> $DIR/issue-50480.rs:12:33
- |
-LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator
- |
- = help: the trait `Iterator` is not implemented for `i32`
- = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end`
-
error[E0204]: the trait `Copy` may not be implemented for this type
- --> $DIR/issue-50480.rs:10:17
+ --> $DIR/issue-50480.rs:9:17
|
LL | #[derive(Clone, Copy)]
| ^^^^
|
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 10 previous errors
+error: aborting due to 8 previous errors
-Some errors have detailed explanations: E0204, E0277, E0412.
+Some errors have detailed explanations: E0204, E0412.
For more information about an error, try `rustc --explain E0204`.
|
= note: expected type parameter `F`
found struct `Class<P>`
+help: the return type of this call is `Class<P>` due to the type of the argument passed
+ --> $DIR/issue-52893.rs:53:9
+ |
+LL | builder.push(output);
+ | ^^^^^^^^^^^^^------^
+ | |
+ | this argument influences the return type of `push`
note: associated function defined here
--> $DIR/issue-52893.rs:11:8
|
| |
| required by a bound introduced by this call
|
- = help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`
note: required by a bound in `takes`
--> $DIR/issue-99875.rs:9:18
|
LL | fn takes(_: impl Trait) {}
| ^^^^^ required by this bound in `takes`
+help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, try casting using `as`
+ |
+LL | takes(function as fn(Argument) -> Return);
+ | +++++++++++++++++++++++++
error[E0277]: the trait bound `[closure@$DIR/issue-99875.rs:14:11: 14:34]: Trait` is not satisfied
--> $DIR/issue-99875.rs:14:11
--- /dev/null
+error[E0271]: expected `[async block@$DIR/async.rs:12:17: 12:25]` to be a future that resolves to `i32`, but it resolves to `()`
+ --> $DIR/async.rs:12:17
+ |
+LL | needs_async(async {});
+ | ----------- ^^^^^^^^ expected `i32`, found `()`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `needs_async`
+ --> $DIR/async.rs:8:31
+ |
+LL | fn needs_async(_: impl Future<Output = i32>) {}
+ | ^^^^^^^^^^^^ required by this bound in `needs_async`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0271`.
--- /dev/null
+// compile-flags: -Ztrait-solver=next
+// edition: 2021
+// revisions: pass fail
+//[pass] check-pass
+
+use std::future::Future;
+
+fn needs_async(_: impl Future<Output = i32>) {}
+
+#[cfg(fail)]
+fn main() {
+ needs_async(async {});
+ //[fail]~^ ERROR to be a future that resolves to `i32`, but it resolves to `()`
+}
+
+#[cfg(pass)]
+fn main() {
+ needs_async(async { 1i32 });
+}
--- /dev/null
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+fn require_fn(_: impl Fn() -> i32) {}
+
+fn main() {
+ require_fn(|| -> i32 { 1i32 });
+}
--- /dev/null
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+fn require_fn(_: impl Fn() -> i32) {}
+
+fn f() -> i32 {
+ 1i32
+}
+
+fn main() {
+ require_fn(f);
+ require_fn(f as fn() -> i32);
+}
--- /dev/null
+error[E0277]: the trait bound `[generator@$DIR/generator.rs:18:21: 18:23]: Generator<A>` is not satisfied
+ --> $DIR/generator.rs:18:21
+ |
+LL | needs_generator(|| {
+ | _____---------------_^
+ | | |
+ | | required by a bound introduced by this call
+LL | |
+LL | |
+LL | |
+LL | | yield ();
+LL | | });
+ | |_____^ the trait `Generator<A>` is not implemented for `[generator@$DIR/generator.rs:18:21: 18:23]`
+ |
+note: required by a bound in `needs_generator`
+ --> $DIR/generator.rs:14:28
+ |
+LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `needs_generator`
+
+error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Yield == B`
+ --> $DIR/generator.rs:18:21
+ |
+LL | needs_generator(|| {
+ | _____---------------_^
+ | | |
+ | | required by a bound introduced by this call
+LL | |
+LL | |
+LL | |
+LL | | yield ();
+LL | | });
+ | |_____^ types differ
+ |
+note: required by a bound in `needs_generator`
+ --> $DIR/generator.rs:14:41
+ |
+LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+ | ^^^^^^^^^ required by this bound in `needs_generator`
+
+error[E0271]: type mismatch resolving `<[generator@$DIR/generator.rs:18:21: 18:23] as Generator<A>>::Return == C`
+ --> $DIR/generator.rs:18:21
+ |
+LL | needs_generator(|| {
+ | _____---------------_^
+ | | |
+ | | required by a bound introduced by this call
+LL | |
+LL | |
+LL | |
+LL | | yield ();
+LL | | });
+ | |_____^ types differ
+ |
+note: required by a bound in `needs_generator`
+ --> $DIR/generator.rs:14:52
+ |
+LL | fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+ | ^^^^^^^^^^ required by this bound in `needs_generator`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0271, E0277.
+For more information about an error, try `rustc --explain E0271`.
--- /dev/null
+// compile-flags: -Ztrait-solver=next
+// edition: 2021
+// revisions: pass fail
+//[pass] check-pass
+
+#![feature(generator_trait, generators)]
+
+use std::ops::Generator;
+
+struct A;
+struct B;
+struct C;
+
+fn needs_generator(_: impl Generator<A, Yield = B, Return = C>) {}
+
+#[cfg(fail)]
+fn main() {
+ needs_generator(|| {
+ //[fail]~^ ERROR Generator<A>` is not satisfied
+ //[fail]~| ERROR as Generator<A>>::Yield == B`
+ //[fail]~| ERROR as Generator<A>>::Return == C`
+ yield ();
+ });
+}
+
+#[cfg(pass)]
+fn main() {
+ needs_generator(|_: A| {
+ let _: A = yield B;
+ C
+ })
+}
--- /dev/null
+// compile-flags: -Ztrait-solver=next
+// check-pass
+#![feature(ptr_metadata)]
+
+use std::ptr::{DynMetadata, Pointee};
+
+trait Trait<U> {}
+struct MyDst<T: ?Sized>(T);
+
+fn works<T>() {
+ let _: <T as Pointee>::Metadata = ();
+ let _: <[T] as Pointee>::Metadata = 1_usize;
+ let _: <str as Pointee>::Metadata = 1_usize;
+ let _: <dyn Trait<T> as Pointee>::Metadata = give::<DynMetadata<dyn Trait<T>>>();
+ let _: <MyDst<T> as Pointee>::Metadata = ();
+ let _: <((((([u8],),),),),) as Pointee>::Metadata = 1_usize;
+}
+
+fn give<U>() -> U {
+ loop {}
+}
+
+fn main() {}
--- /dev/null
+#![feature(pointer_sized_trait)]
+
+use std::marker::PointerSized;
+
+fn require_pointer_sized(_: impl PointerSized) {}
+
+fn main() {
+ require_pointer_sized(1usize);
+ require_pointer_sized(1u16);
+ //~^ ERROR `u16` needs to be a pointer-sized type
+ require_pointer_sized(&1i16);
+}
--- /dev/null
+error[E0277]: `u16` needs to be a pointer-sized type
+ --> $DIR/pointer-sized.rs:9:27
+ |
+LL | require_pointer_sized(1u16);
+ | --------------------- ^^^^ the trait `PointerSized` is not implemented for `u16`
+ | |
+ | required by a bound introduced by this call
+ |
+ = note: the trait bound `u16: PointerSized` is not satisfied
+note: required by a bound in `require_pointer_sized`
+ --> $DIR/pointer-sized.rs:5:34
+ |
+LL | fn require_pointer_sized(_: impl PointerSized) {}
+ | ^^^^^^^^^^^^ required by this bound in `require_pointer_sized`
+help: consider borrowing here
+ |
+LL | require_pointer_sized(&1u16);
+ | +
+LL | require_pointer_sized(&mut 1u16);
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// These are simplifications of the tower traits by the same name:
+
+pub trait Service<Request> {
+ type Response;
+}
+
+pub trait Layer<C> {
+ type Service;
+}
+
+// Any type will do here:
+
+pub struct Req;
+pub struct Res;
+
+// This is encoding a trait alias.
+
+pub trait ParticularService:
+ Service<Req, Response = Res> {
+}
+
+impl<T> ParticularService for T
+where
+ T: Service<Req, Response = Res>,
+{
+}
+
+// This is also a trait alias.
+// The weird = <Self as ...> bound is there so that users of the trait do not
+// need to repeat the bounds. See https://github.com/rust-lang/rust/issues/20671
+// for context, and in particular the workaround in:
+// https://github.com/rust-lang/rust/issues/20671#issuecomment-529752828
+
+pub trait ParticularServiceLayer<C>:
+ Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
+{
+ type Service: ParticularService;
+}
+
+impl<T, C> ParticularServiceLayer<C> for T
+where
+ T: Layer<C>,
+ T::Service: ParticularService,
+{
+ type Service = T::Service;
+}
+
+// These are types that implement the traits that the trait aliases refer to.
+// They should also implement the alias traits due to the blanket impls.
+
+struct ALayer<C>(C);
+impl<C> Layer<C> for ALayer<C> {
+ type Service = AService;
+}
+
+struct AService;
+impl Service<Req> for AService {
+ // However, AService does _not_ meet the blanket implementation,
+ // since its Response type is bool, not Res as it should be.
+ type Response = bool;
+}
+
+// This is a wrapper type around ALayer that uses the trait alias
+// as a way to communicate the requirements of the provided types.
+struct Client<C>(C);
+
+// The method and the free-standing function below both have the same bounds.
+
+impl<C> Client<C>
+where
+ ALayer<C>: ParticularServiceLayer<C>,
+{
+ fn check(&self) {}
+}
+
+fn check<C>(_: C) where ALayer<C>: ParticularServiceLayer<C> {}
+
+// But, they give very different error messages.
+
+fn main() {
+ // This gives a very poor error message that does nothing to point the user
+ // at the underlying cause of why the types involved do not meet the bounds.
+ Client(()).check(); //~ ERROR E0599
+
+ // This gives a good(ish) error message that points the user at _why_ the
+ // bound isn't met, and thus how they might fix it.
+ check(()); //~ ERROR E0271
+}
--- /dev/null
+error[E0599]: the method `check` exists for struct `Client<()>`, but its trait bounds were not satisfied
+ --> $DIR/track-obligations.rs:83:16
+ |
+LL | struct ALayer<C>(C);
+ | ----------------
+ | |
+ | doesn't satisfy `<_ as Layer<()>>::Service = <ALayer<()> as ParticularServiceLayer<()>>::Service`
+ | doesn't satisfy `ALayer<()>: ParticularServiceLayer<()>`
+...
+LL | struct Client<C>(C);
+ | ---------------- method `check` not found for this struct
+...
+LL | Client(()).check();
+ | ^^^^^ method cannot be called on `Client<()>` due to unsatisfied trait bounds
+ |
+note: trait bound `<ALayer<()> as Layer<()>>::Service = <ALayer<()> as ParticularServiceLayer<()>>::Service` was not satisfied
+ --> $DIR/track-obligations.rs:35:14
+ |
+LL | pub trait ParticularServiceLayer<C>:
+ | ----------------------
+LL | Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
+note: trait bound `ALayer<()>: ParticularServiceLayer<()>` was not satisfied
+ --> $DIR/track-obligations.rs:71:16
+ |
+LL | impl<C> Client<C>
+ | ---------
+LL | where
+LL | ALayer<C>: ParticularServiceLayer<C>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
+note: the trait `ParticularServiceLayer` must be implemented
+ --> $DIR/track-obligations.rs:34:1
+ |
+LL | / pub trait ParticularServiceLayer<C>:
+LL | | Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
+ | |____________________________________________________________________^
+
+error[E0271]: type mismatch resolving `<AService as Service<Req>>::Response == Res`
+ --> $DIR/track-obligations.rs:87:11
+ |
+LL | check(());
+ | ----- ^^ type mismatch resolving `<AService as Service<Req>>::Response == Res`
+ | |
+ | required by a bound introduced by this call
+ |
+note: expected this to be `Res`
+ --> $DIR/track-obligations.rs:60:21
+ |
+LL | type Response = bool;
+ | ^^^^
+note: required for `AService` to implement `ParticularService`
+ --> $DIR/track-obligations.rs:22:9
+ |
+LL | impl<T> ParticularService for T
+ | ^^^^^^^^^^^^^^^^^ ^
+LL | where
+LL | T: Service<Req, Response = Res>,
+ | -------------- unsatisfied trait bound introduced here
+note: required for `ALayer<_>` to implement `ParticularServiceLayer<_>`
+ --> $DIR/track-obligations.rs:40:12
+ |
+LL | impl<T, C> ParticularServiceLayer<C> for T
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ ^
+...
+LL | T::Service: ParticularService,
+ | ----------------- unsatisfied trait bound introduced here
+note: required by a bound in `check`
+ --> $DIR/track-obligations.rs:76:36
+ |
+LL | fn check<C>(_: C) where ALayer<C>: ParticularServiceLayer<C> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0271, E0599.
+For more information about an error, try `rustc --explain E0271`.
--- /dev/null
+#![feature(multiple_supertrait_upcastable)]
+#![deny(multiple_supertrait_upcastable)]
+
+trait A {}
+trait B {}
+
+trait C: A + B {}
+//~^ ERROR `C` is object-safe and has multiple supertraits
+
+fn main() {}
--- /dev/null
+error: `C` is object-safe and has multiple supertraits
+ --> $DIR/multiple_supertrait_upcastable.rs:7:1
+ |
+LL | trait C: A + B {}
+ | ^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/multiple_supertrait_upcastable.rs:2:9
+ |
+LL | #![deny(multiple_supertrait_upcastable)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/issue-97381.rs:26:14
|
+LL | let v = [1, 2, 3]
+ | - binding `v` declared here
+...
LL | let el = &v[0];
| - borrow of `v` occurs here
LL |
LL | let result: Result<(), &str> = try {
| ------ borrow later stored here
LL | let my_string = String::from("");
+ | --------- binding `my_string` declared here
LL | let my_str: & str = & my_string;
| ^^^^^^^^^^^ borrowed value does not live long enough
...
--> $DIR/try-block-bad-lifetime.rs:29:13
|
LL | let k = &mut i;
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
...
LL | i = 10;
- | ^^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^^ `i` is assigned to here but it was already borrowed
LL | };
LL | ::std::mem::drop(k);
| - borrow later used here
--> $DIR/try-block-bad-lifetime.rs:32:9
|
LL | let k = &mut i;
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
...
LL | i = 40;
- | ^^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^^ `i` is assigned to here but it was already borrowed
LL |
LL | let i_ptr = if let Err(i_ptr) = j { i_ptr } else { panic ! ("") };
| - borrow later used here
--> $DIR/try-block-maybe-bad-lifetime.rs:17:9
|
LL | &i
- | -- borrow of `i` occurs here
+ | -- `i` is borrowed here
LL | };
LL | i = 0;
- | ^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^ `i` is assigned to here but it was already borrowed
LL | let _ = i;
LL | do_something_with(x);
| - borrow later used here
--> $DIR/try-block-maybe-bad-lifetime.rs:40:9
|
LL | j = &i;
- | -- borrow of `i` occurs here
+ | -- `i` is borrowed here
LL | };
LL | i = 0;
- | ^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^ `i` is assigned to here but it was already borrowed
LL | let _ = i;
LL | do_something_with(j);
| - borrow later used here
fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> {
()
- //~^ ERROR non-defining opaque type use
+ //~^ ERROR expected generic type parameter, found `<T as TraitWithAssoc>::Assoc`
}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `<T as TraitWithAssoc>::Assoc`
--> $DIR/bound_reduction2.rs:16:5
|
+LL | type Foo<V> = impl Trait<V>;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | ()
| ^^
- |
-note: used non-generic type `<T as TraitWithAssoc>::Assoc` for generic parameter
- --> $DIR/bound_reduction2.rs:9:10
- |
-LL | type Foo<V> = impl Trait<V>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
fn f<'a: 'static>(t: &'a str) -> X<'a> {
//~^ WARNING unnecessary lifetime parameter
t
- //~^ ERROR non-defining opaque type use
+ //~^ ERROR expected generic lifetime parameter, found `'static`
}
fn extend_lt<'a>(o: &'a str) -> &'static str {
|
= help: you can use the `'static` lifetime directly, in place of `'a`
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic lifetime parameter, found `'static`
--> $DIR/bounds-are-checked.rs:10:5
|
LL | type X<'a> = impl Into<&'static str> + From<&'a str>;
error: aborting due to previous error; 1 warning emitted
+For more information about this error, try `rustc --explain E0792`.
type OneConst<const X: usize> = impl Debug;
-
// Not defining uses, because they doesn't define *all* possible generics.
fn concrete_ty() -> OneTy<u32> {
5u32
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic type parameter, found `u32`
}
fn concrete_lifetime() -> OneLifetime<'static> {
6u32
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic lifetime parameter, found `'static`
}
fn concrete_const() -> OneConst<{ 123 }> {
7u32
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic constant parameter, found `123`
}
-error: non-defining opaque type use in defining scope
- --> $DIR/generic_nondefining_use.rs:17:5
+error[E0792]: expected generic type parameter, found `u32`
+ --> $DIR/generic_nondefining_use.rs:16:5
|
+LL | type OneTy<T> = impl Debug;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | 5u32
| ^^^^
- |
-note: used non-generic type `u32` for generic parameter
- --> $DIR/generic_nondefining_use.rs:7:12
- |
-LL | type OneTy<T> = impl Debug;
- | ^
-error: non-defining opaque type use in defining scope
- --> $DIR/generic_nondefining_use.rs:22:5
+error[E0792]: expected generic lifetime parameter, found `'static`
+ --> $DIR/generic_nondefining_use.rs:21:5
|
LL | type OneLifetime<'a> = impl Debug;
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
LL | 6u32
| ^^^^
-error: non-defining opaque type use in defining scope
- --> $DIR/generic_nondefining_use.rs:27:5
+error[E0792]: expected generic constant parameter, found `123`
+ --> $DIR/generic_nondefining_use.rs:26:5
|
+LL | type OneConst<const X: usize> = impl Debug;
+ | -------------- this generic parameter must be used with a generic constant parameter
+...
LL | 7u32
| ^^^^
- |
-note: used non-generic constant `123` for generic parameter
- --> $DIR/generic_nondefining_use.rs:11:15
- |
-LL | type OneConst<const X: usize> = impl Debug;
- | ^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0792`.
let y = 42;
let x = wrong_generic(&y);
let z: i32 = x;
- //~^ ERROR non-defining opaque type use
+ //~^ ERROR expected generic type parameter, found `&'static i32
}
type WrongGeneric<T> = impl 'static;
LL | type WrongGeneric<T> = impl 'static;
| ^^^^^^^^^^^^
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `&'static i32`
--> $DIR/generic_type_does_not_live_long_enough.rs:6:18
|
LL | let z: i32 = x;
| ^
- |
-note: used non-generic type `&'static i32` for generic parameter
- --> $DIR/generic_type_does_not_live_long_enough.rs:10:19
- |
+...
LL | type WrongGeneric<T> = impl 'static;
- | ^
+ | - this generic parameter must be used with a generic type parameter
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/generic_type_does_not_live_long_enough.rs:14:5
error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0310`.
+Some errors have detailed explanations: E0310, E0792.
+For more information about an error, try `rustc --explain E0310`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+#![cfg_attr(specialized, feature(specialization))]
+#![allow(incomplete_features)]
+
+// revisions: stock specialized
+// [specialized]check-pass
+
+trait OpaqueTrait {}
+impl<T> OpaqueTrait for T {}
+type OpaqueType = impl OpaqueTrait;
+fn mk_opaque() -> OpaqueType {
+ || 0
+}
+trait AnotherTrait {}
+impl<T: Send> AnotherTrait for T {}
+impl AnotherTrait for OpaqueType {}
+//[stock]~^ conflicting implementations of trait `AnotherTrait` for type `OpaqueType`
+
+fn main() {}
--- /dev/null
+error[E0119]: conflicting implementations of trait `AnotherTrait` for type `OpaqueType`
+ --> $DIR/issue-104817.rs:16:1
+ |
+LL | impl<T: Send> AnotherTrait for T {}
+ | -------------------------------- first implementation here
+LL | impl AnotherTrait for OpaqueType {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `OpaqueType`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
type Bug<T, U> = impl Fn(T) -> U + Copy; //~ ERROR cycle detected
const CONST_BUG: Bug<u8, ()> = unsafe { std::mem::transmute(|_: u8| ()) };
-//~^ ERROR: cannot transmute
fn make_bug<T, U: From<T>>() -> Bug<T, U> {
|x| x.into() //~ ERROR the trait bound `U: From<T>` is not satisfied
LL | | }
| |_^
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
- --> $DIR/issue-53092-2.rs:6:41
- |
-LL | const CONST_BUG: Bug<u8, ()> = unsafe { std::mem::transmute(|_: u8| ()) };
- | ^^^^^^^^^^^^^^^^^^^
- |
- = note: source type: `[closure@$DIR/issue-53092-2.rs:6:61: 6:68]` (0 bits)
- = note: target type: `Bug<u8, ()>` (size can vary because of [type error])
-
error[E0277]: the trait bound `U: From<T>` is not satisfied
- --> $DIR/issue-53092-2.rs:10:5
+ --> $DIR/issue-53092-2.rs:9:5
|
LL | |x| x.into()
| ^^^^^^^^^^^^ the trait `From<T>` is not implemented for `U`
|
note: required by a bound in `make_bug`
- --> $DIR/issue-53092-2.rs:9:19
+ --> $DIR/issue-53092-2.rs:8:19
|
LL | fn make_bug<T, U: From<T>>() -> Bug<T, U> {
| ^^^^^^^ required by this bound in `make_bug`
LL | type Bug<T, U: std::convert::From<T>> = impl Fn(T) -> U + Copy;
| +++++++++++++++++++++++
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0277, E0391, E0512.
+Some errors have detailed explanations: E0277, E0391.
For more information about an error, try `rustc --explain E0277`.
type BitsIter = IterBitsIter<T, E, u8>;
fn iter_bits(self, n: u8) -> Self::BitsIter {
(0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic type parameter, found `u8`
}
}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `u8`
--> $DIR/issue-60564.rs:20:9
|
+LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: used non-generic type `u8` for generic parameter
- --> $DIR/issue-60564.rs:8:25
- |
-LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
type Alias<'a, U> = impl Trait<U>;
fn f<'a>() -> Alias<'a, ()> {}
-//~^ ERROR non-defining opaque type use in defining scope
+//~^ ERROR expected generic type parameter, found `()`
fn main() {}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `()`
--> $DIR/issue-68368-non-defining-use-2.rs:9:29
|
+LL | type Alias<'a, U> = impl Trait<U>;
+ | - this generic parameter must be used with a generic type parameter
+LL |
LL | fn f<'a>() -> Alias<'a, ()> {}
| ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-68368-non-defining-use-2.rs:7:16
- |
-LL | type Alias<'a, U> = impl Trait<U>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
type Alias<'a, U> = impl Trait<U>;
fn f<'a>() -> Alias<'a, ()> {}
-//~^ ERROR non-defining opaque type use in defining scope
+//~^ ERROR expected generic type parameter, found `()`
fn main() {}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `()`
--> $DIR/issue-68368-non-defining-use.rs:9:29
|
+LL | type Alias<'a, U> = impl Trait<U>;
+ | - this generic parameter must be used with a generic type parameter
+LL |
LL | fn f<'a>() -> Alias<'a, ()> {}
| ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-68368-non-defining-use.rs:7:16
- |
-LL | type Alias<'a, U> = impl Trait<U>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
//~^ ERROR use of undeclared lifetime name `'a`
fn my_fun() -> Return<()> {}
-//~^ ERROR non-defining opaque type use in defining scope
+//~^ ERROR expected generic type parameter, found `()`
fn main() {}
LL | type Return<'a, A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
| +++
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `()`
--> $DIR/issue-69136-inner-lifetime-resolve-error.rs:20:27
|
+LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | fn my_fun() -> Return<()> {}
| ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:17:13
- |
-LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
- | ^
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0261`.
+Some errors have detailed explanations: E0261, E0792.
+For more information about an error, try `rustc --explain E0261`.
fn main() {
let _: foo::Foo = std::mem::transmute(0u8);
- //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
}
|
= note: `Foo` must be used in combination with a concrete type within the same module
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
- --> $DIR/no_inferrable_concrete_type.rs:17:23
- |
-LL | let _: foo::Foo = std::mem::transmute(0u8);
- | ^^^^^^^^^^^^^^^^^^^
- |
- = note: source type: `u8` (8 bits)
- = note: target type: `Foo` (size can vary because of [type error])
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-For more information about this error, try `rustc --explain E0512`.
--- /dev/null
+// Here we process outlive obligations involving
+// opaque types with bound vars in substs.
+// This was an ICE.
+//
+// check-pass
+#![feature(type_alias_impl_trait)]
+
+type Ty<'a> = impl Sized + 'a;
+fn define<'a>() -> Ty<'a> {}
+
+// Ty<'^0>: 'static
+fn test1(_: &'static fn(Ty<'_>)) {}
+
+fn test2() {
+ None::<&fn(Ty<'_>)>;
+}
+
+fn main() { }
--> $DIR/cannot_infer_local_or_vec_in_tuples.rs:2:9
|
LL | let (x, ) = (vec![], );
- | ^^^^^
+ | ^^^^^ ---------- type must be known at this point
|
help: consider giving this pattern a type, where the type for type parameter `T` is specified
|
--- /dev/null
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo4(x: Result<(), A>) -> Result<(), B> {
+ match true {
+ true => x, //~ ERROR mismatched types
+ false => x,
+ }
+}
+fn foo5(x: Result<(), A>) -> Result<(), B> {
+ match true {
+ true => return x, //~ ERROR mismatched types
+ false => return x,
+ }
+}
+fn main() {
+ let _ = foo4(Ok(()));
+ let _ = foo5(Ok(()));
+ let _: Result<(), B> = { //~ ERROR mismatched types
+ Err(A);
+ };
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value-2.rs:8:17
+ |
+LL | fn foo4(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | match true {
+LL | true => x,
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | true => Ok(x?),
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value-2.rs:14:24
+ |
+LL | fn foo5(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | match true {
+LL | true => return x,
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | true => return Ok(x?),
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value-2.rs:21:28
+ |
+LL | let _: Result<(), B> = {
+ | ____________________________^
+LL | | Err(A);
+LL | | };
+ | |_____^ expected enum `Result`, found `()`
+ |
+ = note: expected enum `Result<(), B>`
+ found unit type `()`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// run-rustfix
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+ Ok(x?) //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+ return Ok(x?); //~ ERROR mismatched types
+}
+fn foo3(x: Result<(), A>) -> Result<(), B> {
+ if true {
+ Ok(x?) //~ ERROR mismatched types
+ } else {
+ Ok(x?) //~ ERROR mismatched types
+ }
+}
+fn main() {
+ let _ = foo1(Ok(()));
+ let _ = foo2(Ok(()));
+ let _ = foo3(Ok(()));
+}
--- /dev/null
+// run-rustfix
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+ x //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+ return x; //~ ERROR mismatched types
+}
+fn foo3(x: Result<(), A>) -> Result<(), B> {
+ if true {
+ x //~ ERROR mismatched types
+ } else {
+ x //~ ERROR mismatched types
+ }
+}
+fn main() {
+ let _ = foo1(Ok(()));
+ let _ = foo2(Ok(()));
+ let _ = foo3(Ok(()));
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:8:5
+ |
+LL | fn foo1(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:11:12
+ |
+LL | fn foo2(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | return x;
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | return Ok(x?);
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:15:9
+ |
+LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | if true {
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:17:9
+ |
+LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+...
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+fn function<T>(x: T, y: bool) -> T {
+ x
+}
+
+struct S {}
+impl S {
+ fn method<T>(&self, x: T) -> T {
+ x
+ }
+}
+
+fn wrong_arg_type(x: u32) -> u32 {
+ x
+}
+
+fn main() {
+ // Should not trigger.
+ let x = wrong_arg_type(0u16); //~ ERROR mismatched types
+ let x: u16 = function(0, 0u8); //~ ERROR mismatched types
+
+ // Should trigger exactly once for the first argument.
+ let x: u16 = function(0u32, 0u8); //~ ERROR arguments to this function are incorrect
+
+ // Should trigger.
+ let x: u16 = function(0u32, true); //~ ERROR mismatched types
+ let x: u16 = (S {}).method(0u32); //~ ERROR mismatched types
+ function(0u32, 8u8) //~ ERROR arguments to this function are incorrect
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:18:28
+ |
+LL | let x = wrong_arg_type(0u16);
+ | -------------- ^^^^ expected `u32`, found `u16`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:12:4
+ |
+LL | fn wrong_arg_type(x: u32) -> u32 {
+ | ^^^^^^^^^^^^^^ ------
+help: change the type of the numeric literal from `u16` to `u32`
+ |
+LL | let x = wrong_arg_type(0u32);
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:19:30
+ |
+LL | let x: u16 = function(0, 0u8);
+ | -------- ^^^ expected `bool`, found `u8`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ -------
+
+error[E0308]: arguments to this function are incorrect
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:22:18
+ |
+LL | let x: u16 = function(0u32, 0u8);
+ | ^^^^^^^^ ---- --- expected `bool`, found `u8`
+ | |
+ | expected `u16`, found `u32`
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:22:18
+ |
+LL | let x: u16 = function(0u32, 0u8);
+ | ^^^^^^^^^----^^^^^^
+ | |
+ | this argument influences the return type of `function`
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ ---- -------
+help: change the type of the numeric literal from `u32` to `u16`
+ |
+LL | let x: u16 = function(0u16, 0u8);
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:27
+ |
+LL | let x: u16 = function(0u32, true);
+ | -------- ^^^^ expected `u16`, found `u32`
+ | |
+ | arguments to this function are incorrect
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:18
+ |
+LL | let x: u16 = function(0u32, true);
+ | ^^^^^^^^^----^^^^^^^
+ | |
+ | this argument influences the return type of `function`
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ ----
+help: change the type of the numeric literal from `u32` to `u16`
+ |
+LL | let x: u16 = function(0u16, true);
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:32
+ |
+LL | let x: u16 = (S {}).method(0u32);
+ | ------ ^^^^ expected `u16`, found `u32`
+ | |
+ | arguments to this method are incorrect
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:18
+ |
+LL | let x: u16 = (S {}).method(0u32);
+ | ^^^^^^^^^^^^^^----^
+ | |
+ | this argument influences the return type of `method`
+note: associated function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:7:8
+ |
+LL | fn method<T>(&self, x: T) -> T {
+ | ^^^^^^ ----
+help: change the type of the numeric literal from `u32` to `u16`
+ |
+LL | let x: u16 = (S {}).method(0u16);
+ | ~~~
+
+error[E0308]: arguments to this function are incorrect
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5
+ |
+LL | function(0u32, 8u8)
+ | ^^^^^^^^ ---- --- expected `bool`, found `u8`
+ | |
+ | expected `()`, found `u32`
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5
+ |
+LL | function(0u32, 8u8)
+ | ^^^^^^^^^----^^^^^^
+ | |
+ | this argument influences the return type of `function`
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ ---- -------
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// The error message here still is pretty confusing.
+
+fn main() {
+ let mut result = vec![1];
+ // The type of `result` is constrained to be `Vec<{integer}>` here.
+ // But the logic we use to find what expression constrains a type
+ // is not sophisticated enough to know this.
+
+ let mut vector = Vec::new();
+ vector.sort();
+ result.push(vector);
+ //~^ ERROR mismatched types
+ // So it thinks that the type of `result` is constrained here.
+}
+
+fn example2() {
+ let mut x = vec![1];
+ x.push("");
+ //~^ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/bad-type-in-vec-push.rs:11:17
+ |
+LL | vector.sort();
+ | ------ here the type of `vector` is inferred to be `Vec<_>`
+LL | result.push(vector);
+ | ---- ^^^^^^ expected integer, found struct `Vec`
+ | |
+ | arguments to this method are incorrect
+ |
+ = note: expected type `{integer}`
+ found struct `Vec<_>`
+note: associated function defined here
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+
+error[E0308]: mismatched types
+ --> $DIR/bad-type-in-vec-push.rs:18:12
+ |
+LL | x.push("");
+ | ---- ^^ expected integer, found `&str`
+ | |
+ | arguments to this method are incorrect
+ |
+note: associated function defined here
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
|
LL | let _: S<impl Oops> = S;
| ^^^^ not found in this scope
- |
-help: you might be missing a type parameter
- |
-LL | fn f<Oops>() {
- | ++++++
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in variable binding
--> $DIR/issue-104513-ice.rs:3:14
--- /dev/null
+struct A<T>(T);
+
+trait Foo {
+ type B;
+}
+
+impl Foo for A<u32> {
+ type B = i32;
+}
+
+impl Foo for A<i32> {
+ type B = i32;
+}
+
+fn main() {
+ A::B::<>::C
+ //~^ ERROR ambiguous associated type
+}
--- /dev/null
+error[E0223]: ambiguous associated type
+ --> $DIR/issue-107087.rs:16:5
+ |
+LL | A::B::<>::C
+ | ^^^^^^^^ help: use the fully-qualified path: `<A<_> as Foo>::B`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0223`.
|
= note: expected enum `Option<()>`
found unit type `()`
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-46112.rs:9:18
+ |
+LL | fn main() { test(Ok(())); }
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
help: try wrapping the expression in `Some`
|
= note: expected tuple `(&mut u8,)`
found type `{integer}`
+help: the return type of this call is `{integer}` due to the type of the argument passed
+ --> $DIR/issue-84768.rs:7:5
+ |
+LL | <F as FnOnce(&mut u8)>::call_once(f, 1)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^
+ | |
+ | this argument influences the return type of `FnOnce`
note: associated function defined here
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
--> $DIR/issue-91334.rs:10:23
|
LL | fn f(){||yield(((){),
- | - - ^
- | | |
+ | - - - ^
+ | | | |
+ | | | missing open `(` for this delimiter
| | unclosed delimiter
| unclosed delimiter
--> $DIR/issue-91334.rs:10:23
|
LL | fn f(){||yield(((){),
- | - - ^
- | | |
+ | - - - ^
+ | | | |
+ | | | missing open `(` for this delimiter
| | unclosed delimiter
| unclosed delimiter
| |
| arguments to this function are incorrect
|
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/ufcs-qpath-self-mismatch.rs:7:5
+ |
+LL | <i32 as Add<i32>>::add(1u32, 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^----^^^^
+ | |
+ | this argument influences the return type of `Add`
note: associated function defined here
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
help: change the type of the numeric literal from `u32` to `i32`
| |
| arguments to this function are incorrect
|
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/ufcs-qpath-self-mismatch.rs:9:5
+ |
+LL | <i32 as Add<i32>>::add(1, 2u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^----^
+ | |
+ | this argument influences the return type of `Add`
note: associated function defined here
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
help: change the type of the numeric literal from `u32` to `i32`
LL | let f = || x += 1;
| -- - borrow occurs due to use of `x` in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | let _y = x;
| ^ use of borrowed `x`
LL | f;
--> $DIR/unboxed-closures-failed-recursive-fn-1.rs:20:5
|
LL | let f = |x: u32| -> u32 {
- | --------------- borrow of `factorial` occurs here
+ | --------------- `factorial` is borrowed here
LL | let g = factorial.as_ref().unwrap();
| --------- borrow occurs due to use in closure
...
LL | factorial = Some(Box::new(f));
| ^^^^^^^^^
| |
- | assignment to borrowed `factorial` occurs here
+ | `factorial` is assigned to here but it was already borrowed
| borrow later used here
error[E0597]: `factorial` does not live long enough
| ----------------------------------------- type annotation requires that `factorial` is borrowed for `'static`
LL |
LL | let f = |x: u32| -> u32 {
- | --------------- borrow of `factorial` occurs here
+ | --------------- `factorial` is borrowed here
LL | let g = factorial.as_ref().unwrap();
| --------- borrow occurs due to use in closure
...
LL | factorial = Some(Box::new(f));
- | ^^^^^^^^^ assignment to borrowed `factorial` occurs here
+ | ^^^^^^^^^ `factorial` is assigned to here but it was already borrowed
error: aborting due to 4 previous errors
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/unop-move-semantics.rs:15:6
|
+LL | fn move_borrowed<T: Not<Output=T>>(x: T, mut y: T) {
+ | - binding `x` declared here
LL | let m = &x;
| -- borrow of `x` occurs here
...
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/unop-move-semantics.rs:17:6
|
+LL | fn move_borrowed<T: Not<Output=T>>(x: T, mut y: T) {
+ | ----- binding `y` declared here
+LL | let m = &x;
LL | let n = &mut y;
| ------ borrow of `y` occurs here
...
help: consider importing this trait instead
|
LL | use a::Trait;
- | ~~~~~~~~~
+ | ~~~~~~~~
error[E0405]: cannot find trait `Trait` in this scope
--> $DIR/unresolved-candidates.rs:10:10
-error[E0208]: [o]
+error: [o]
--> $DIR/variance-associated-consts.rs:13:1
|
LL | struct Foo<T: Trait> {
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0208`.
}
#[rustc_variance]
-struct Foo<'a, T : Trait<'a>> { //~ ERROR [-, +]
+struct Foo<'a, T : Trait<'a>> { //~ ERROR [+, +]
field: (T, &'a ())
}
-error[E0208]: [-, +]
+error: [+, +]
--> $DIR/variance-associated-types.rs:13:1
|
LL | struct Foo<'a, T : Trait<'a>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o, o]
+error: [o, o]
--> $DIR/variance-associated-types.rs:18:1
|
LL | struct Bar<'a, T : Trait<'a>> {
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0208`.
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/variance-issue-20533.rs:28:14
|
+LL | let a = AffineU32(1);
+ | - binding `a` declared here
LL | let x = foo(&a);
| -- borrow of `a` occurs here
LL | drop(a);
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/variance-issue-20533.rs:34:14
|
+LL | let a = AffineU32(1);
+ | - binding `a` declared here
LL | let x = bar(&a);
| -- borrow of `a` occurs here
LL | drop(a);
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/variance-issue-20533.rs:40:14
|
+LL | let a = AffineU32(1);
+ | - binding `a` declared here
LL | let x = baz(&a);
| -- borrow of `a` occurs here
LL | drop(a);
-error[E0208]: [o]
+error: [o]
--> $DIR/variance-object-types.rs:7:1
|
LL | struct Foo<'a> {
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0208`.
// Regions that just appear in normal spots are contravariant:
#[rustc_variance]
-struct Test2<'a, 'b, 'c> { //~ ERROR [-, -, -]
+struct Test2<'a, 'b, 'c> { //~ ERROR [+, +, +]
x: &'a isize,
y: &'b [isize],
c: &'c str
// Those same annotations in function arguments become covariant:
#[rustc_variance]
-struct Test3<'a, 'b, 'c> { //~ ERROR [+, +, +]
+struct Test3<'a, 'b, 'c> { //~ ERROR [-, -, -]
x: extern "Rust" fn(&'a isize),
y: extern "Rust" fn(&'b [isize]),
c: extern "Rust" fn(&'c str),
// Mutability induces invariance:
#[rustc_variance]
-struct Test4<'a, 'b:'a> { //~ ERROR [-, o]
+struct Test4<'a, 'b:'a> { //~ ERROR [+, o]
x: &'a mut &'b isize,
}
// contravariant context:
#[rustc_variance]
-struct Test5<'a, 'b:'a> { //~ ERROR [+, o]
+struct Test5<'a, 'b:'a> { //~ ERROR [-, o]
x: extern "Rust" fn(&'a mut &'b isize),
}
// argument list occurs in an invariant context.
#[rustc_variance]
-struct Test6<'a, 'b:'a> { //~ ERROR [-, o]
+struct Test6<'a, 'b:'a> { //~ ERROR [+, o]
x: &'a mut extern "Rust" fn(&'b isize),
}
// Try enums too.
#[rustc_variance]
-enum Test8<'a, 'b, 'c:'b> { //~ ERROR [+, -, o]
+enum Test8<'a, 'b, 'c:'b> { //~ ERROR [-, +, o]
Test8A(extern "Rust" fn(&'a isize)),
Test8B(&'b [isize]),
Test8C(&'b mut &'c str),
-error[E0208]: [-, -, -]
+error: [+, +, +]
--> $DIR/variance-regions-direct.rs:9:1
|
LL | struct Test2<'a, 'b, 'c> {
| ^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+, +, +]
+error: [-, -, -]
--> $DIR/variance-regions-direct.rs:18:1
|
LL | struct Test3<'a, 'b, 'c> {
| ^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [-, o]
+error: [+, o]
--> $DIR/variance-regions-direct.rs:27:1
|
LL | struct Test4<'a, 'b:'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+, o]
+error: [-, o]
--> $DIR/variance-regions-direct.rs:35:1
|
LL | struct Test5<'a, 'b:'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [-, o]
+error: [+, o]
--> $DIR/variance-regions-direct.rs:45:1
|
LL | struct Test6<'a, 'b:'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [*]
+error: [*]
--> $DIR/variance-regions-direct.rs:52:1
|
LL | struct Test7<'a> {
| ^^^^^^^^^^^^^^^^
-error[E0208]: [+, -, o]
+error: [-, +, o]
--> $DIR/variance-regions-direct.rs:59:1
|
LL | enum Test8<'a, 'b, 'c:'b> {
error: aborting due to 7 previous errors
-For more information about this error, try `rustc --explain E0208`.
#![feature(rustc_attrs)]
#[rustc_variance]
-enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR [+, -, o, *]
+enum Base<'a, 'b, 'c:'b, 'd> { //~ ERROR [-, +, o, *]
Test8A(extern "Rust" fn(&'a isize)),
Test8B(&'b [isize]),
Test8C(&'b mut &'c str),
}
#[rustc_variance]
-struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR [*, o, -, +]
+struct Derived1<'w, 'x:'y, 'y, 'z> { //~ ERROR [*, o, +, -]
f: Base<'z, 'y, 'x, 'w>
}
}
#[rustc_variance] // Combine + and o to yield o (just pay attention to 'a here)
-struct Derived3<'a:'b, 'b, 'c> { //~ ERROR [o, -, *]
+struct Derived3<'a:'b, 'b, 'c> { //~ ERROR [o, +, *]
f: Base<'a, 'b, 'a, 'c>
}
#[rustc_variance] // Combine + and * to yield + (just pay attention to 'a here)
-struct Derived4<'a, 'b, 'c:'b> { //~ ERROR [+, -, o]
+struct Derived4<'a, 'b, 'c:'b> { //~ ERROR [-, +, o]
f: Base<'a, 'b, 'c, 'a>
}
-error[E0208]: [+, -, o, *]
+error: [-, +, o, *]
--> $DIR/variance-regions-indirect.rs:8:1
|
LL | enum Base<'a, 'b, 'c:'b, 'd> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [*, o, -, +]
+error: [*, o, +, -]
--> $DIR/variance-regions-indirect.rs:15:1
|
LL | struct Derived1<'w, 'x:'y, 'y, 'z> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o, o, *]
+error: [o, o, *]
--> $DIR/variance-regions-indirect.rs:20:1
|
LL | struct Derived2<'a, 'b:'a, 'c> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o, -, *]
+error: [o, +, *]
--> $DIR/variance-regions-indirect.rs:25:1
|
LL | struct Derived3<'a:'b, 'b, 'c> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+, -, o]
+error: [-, +, o]
--> $DIR/variance-regions-indirect.rs:30:1
|
LL | struct Derived4<'a, 'b, 'c:'b> {
error: aborting due to 5 previous errors
-For more information about this error, try `rustc --explain E0208`.
-error[E0208]: [+, +]
+error: [+, +]
--> $DIR/variance-trait-bounds.rs:16:1
|
LL | struct TestStruct<U,T:Setter<U>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [*, +]
+error: [*, +]
--> $DIR/variance-trait-bounds.rs:21:1
|
LL | enum TestEnum<U,T:Setter<U>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [*, +]
+error: [*, +]
--> $DIR/variance-trait-bounds.rs:26:1
|
LL | struct TestContraStruct<U,T:Setter<U>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [*, +]
+error: [*, +]
--> $DIR/variance-trait-bounds.rs:31:1
|
LL | struct TestBox<U,T:Getter<U>+Setter<U>> {
error: aborting due to 4 previous errors
-For more information about this error, try `rustc --explain E0208`.
trait T { fn foo(&self); }
#[rustc_variance]
-struct TOption<'a> { //~ ERROR [-]
+struct TOption<'a> { //~ ERROR [+]
v: Option<Box<dyn T + 'a>>,
}
-error[E0208]: [-]
+error: [+]
--> $DIR/variance-trait-object-bound.rs:14:1
|
LL | struct TOption<'a> {
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0208`.
-error[E0208]: [+, +]
+error: [+, +]
--> $DIR/variance-types-bounds.rs:7:1
|
LL | struct TestImm<A, B> {
| ^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+, o]
+error: [+, o]
--> $DIR/variance-types-bounds.rs:13:1
|
LL | struct TestMut<A, B:'static> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+, o]
+error: [+, o]
--> $DIR/variance-types-bounds.rs:19:1
|
LL | struct TestIndirect<A:'static, B:'static> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o, o]
+error: [o, o]
--> $DIR/variance-types-bounds.rs:24:1
|
LL | struct TestIndirect2<A:'static, B:'static> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o, o]
+error: [o, o]
--> $DIR/variance-types-bounds.rs:38:1
|
LL | struct TestObject<A, R> {
error: aborting due to 5 previous errors
-For more information about this error, try `rustc --explain E0208`.
// not considered bivariant.
#[rustc_variance]
-struct InvariantMut<'a,A:'a,B:'a> { //~ ERROR [-, o, o]
+struct InvariantMut<'a,A:'a,B:'a> { //~ ERROR [+, o, o]
t: &'a mut (A,B)
}
-error[E0208]: [-, o, o]
+error: [+, o, o]
--> $DIR/variance-types.rs:10:1
|
LL | struct InvariantMut<'a,A:'a,B:'a> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o]
+error: [o]
--> $DIR/variance-types.rs:15:1
|
LL | struct InvariantCell<A> {
| ^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [o]
+error: [o]
--> $DIR/variance-types.rs:20:1
|
LL | struct InvariantIndirect<A> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+]
+error: [+]
--> $DIR/variance-types.rs:25:1
|
LL | struct Covariant<A> {
| ^^^^^^^^^^^^^^^^^^^
-error[E0208]: [-]
+error: [-]
--> $DIR/variance-types.rs:30:1
|
LL | struct Contravariant<A> {
| ^^^^^^^^^^^^^^^^^^^^^^^
-error[E0208]: [+, -, o]
+error: [+, -, o]
--> $DIR/variance-types.rs:35:1
|
LL | enum Enum<A,B,C> {
error: aborting due to 6 previous errors
-For more information about this error, try `rustc --explain E0208`.
"T-*",
]
-[autolabel."A-bootstrap"]
+[autolabel."T-bootstrap"]
trigger_files = [
"x.py",
"x",
"src/tools/x",
"configure",
"Cargo.toml",
- "Cargo.lock",
"config.toml.example",
"src/stage0.json"
]
[autolabel."WG-trait-system-refactor"]
trigger_files = [
- "compiler/rustc_trait_selection/solve"
+ "compiler/rustc_trait_selection/src/solve"
]
[notify-zulip."I-prioritize"]
(See the [Target Tier Policy](https://doc.rust-lang.org/nightly/rustc/target-tier-policy.html).)
"""
+[mentions."src/doc/style-guide"]
+cc = ["@rust-lang/style"]
+
[assign]
warn_non_default_branch = true
contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html"
]
bootstrap = [
"@Mark-Simulacrum",
+ "@albertlarsan68",
+ "@ozkanonur",
]
infra-ci = [
"@Mark-Simulacrum",
fallback = [
"@Mark-Simulacrum"
]
+style-team = [
+ "@calebcartwright",
+ "@compiler-errors",
+ "@joshtriplett",
+ "@yaahc",
+]
[assign.owners]
"/.github/workflows" = ["infra-ci"]
"/src/doc/rust-by-example" = ["@ehuss"]
"/src/doc/rustc-dev-guide" = ["@ehuss"]
"/src/doc/rustdoc" = ["rustdoc"]
+"/src/doc/style-guide" = ["style-team"]
"/src/etc" = ["@Mark-Simulacrum"]
"/src/librustdoc" = ["rustdoc"]
"/src/llvm-project" = ["@cuviper"]