- 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
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.1.1",
+ "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.1.1"
+version = "4.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2"
+checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
dependencies = [
"bitflags",
"clap_derive 4.1.0",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
dependencies = [
- "clap 4.1.1",
+ "clap 4.1.4",
]
[[package]]
[[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.1.1",
+ "clap 4.1.4",
"fs-err",
"rustdoc-json-types",
"serde",
"ammonia",
"anyhow",
"chrono",
- "clap 4.1.1",
+ "clap 4.1.4",
"clap_complete",
"elasticlunr-rs",
"env_logger 0.10.0",
name = "rustbook"
version = "0.1.0"
dependencies = [
- "clap 4.1.1",
+ "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"
"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
/// 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 {
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,
}
--- /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::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
+}
mod block;
mod errors;
mod expr;
+mod format;
mod index;
mod item;
mod lifetime_collector;
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);
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>) {
}
}
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
}
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 {
&& 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,
);
}
}
#![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>>,
(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;
}
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 {
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());
.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,
})
}
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(_))
}
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubAlias(r_a, kind, alias_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Alias(kind, alias_b), r_a));
+ .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
}
}
/// `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)),
}
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(..)
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))),
}];
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)
})),
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)
})),
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,
},
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
-}
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>(
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);
--- /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)
}
}
}
/// 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());
}
bx.range_metadata(load, scalar.valid_range(bx));
}
}
- abi::Pointer => {
+ abi::Pointer(_) => {
if !scalar.valid_range(bx).contains(0) {
bx.nonnull_metadata(load);
}
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),
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
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.
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)
}
}
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)]
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),
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 =
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
| ty::Placeholder(..)
| ty::Alias(..)
| ty::Bound(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::GeneratorWitness(..) => {
bug!(
"debuginfo: Trying to create type name for \
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);
+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());
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(()))
}
}
}
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 }
// 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)?;
}
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(
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 })
}
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)?;
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(..) => {}
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)?;
}
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),
}
)
}
// 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) {
| ty::Bound(..)
| ty::Param(..)
| ty::Alias(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty),
}
}
};
// 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);
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);
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};
// 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);
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`"),
}
}
#[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>;
};
}
}
impl<Node: Idx> Dominators<Node> {
- pub fn dummy() -> Self {
- Self { post_order_rank: IndexVec::new(), immediate_dominators: IndexVec::new() }
- }
-
pub fn is_reachable(&self, node: Node) -> bool {
self.immediate_dominators[node].is_some()
}
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(()),
}
}
}
#![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();
}
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>,
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 => {
_ => unreachable!(),
};
- write_or_print(&out, ofile, tcx.sess);
+ write_or_print(&out, tcx.sess);
Ok(())
}
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"),
// 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
type to a given size, you should provide a size to packed:
```
-#[repr(packed)] // ok!
+#[repr(packed(8))] // ok!
struct Umbrella(i32);
```
--- /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.
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
borrowck_opaque_type_non_generic_param =
expected generic {$kind} parameter, found `{$ty}`
- .label = this generic parameter must be used with a generic {$kind} parameter
+ .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
+ }
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
}{$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
.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
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`
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);
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);
}
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),
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));
});
}
}
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()
/// 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))
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 {
/// 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;
.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)
}
};
) -> 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[..] {
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)
{
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)),
);
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(
}
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(..) => {
///
/// 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:
//
// 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,
},
// 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,
let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
for (predicate, span) in impl_m_own_bounds {
- let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
+ 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`
}
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 {
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(
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)),
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(()) }
// 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
// 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
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());
for (predicate, span) in impl_ty_own_bounds {
- let cause = ObligationCause::misc(span, impl_ty_hir_id);
+ 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);
&& 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,
+ );
}
}
}
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,
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
};
}
// 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);
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);
}
}
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.skip_binder().def_id));
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.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.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: LocalDefId,
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, 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, 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>),
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 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,
}
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 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)
traits::wf::predicate_obligations(
infcx,
wfcx.param_env.without_const(),
- wfcx.body_id,
+ 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,
);
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 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()) {
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>| {
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 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();
is_foreign_item,
generator_kind,
collect_mod_item_types,
+ is_type_alias_impl_trait,
..*providers
};
}
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),
);
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 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),
- )
- })
- .map(ty::EarlyBinder),
- _ => bug!(),
- }
+ 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),
+ )
+ })
+ .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,
_ => {}
}
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);
}
}
) {
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);
}
}
}
}
- 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(|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,
},
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| {
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 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);
// Include the well-formed predicates of the type parameters of the impl.
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(
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()),
);
}
_ => {
.or_insert(span);
}
- Component::Alias(kind, alias) => {
+ Component::Alias(alias_ty) => {
// This would either arise from something like:
//
// ```
//
// Here we want to add an explicit `where <T as Iterator>::Item: 'a`
// or `Opaque<T>: 'a` depending on the alias kind.
- let ty: Ty<'tcx> = tcx.mk_ty(ty::Alias(kind, alias));
+ 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
// requirement is not checked in type definition
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(_) => {}
// 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;
}
}
}
- 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();
}
}
}
prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
) {
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
use super::FnCtxt;
use crate::type_error_struct;
+use hir::ExprKind;
use rustc_errors::{
struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
};
| 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 => {
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_id, body.id(), interior, gen_kind));
let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
Some(GeneratorTypes {
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)
}
// 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 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,
|| 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);
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)
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
// 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;
) -> 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_hir_id, body_id, interior, _) in generators.iter() {
+ let expr_def_id = self.tcx.hir().local_def_id(expr_hir_id);
+ 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")]
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
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(
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()
}
&self,
ty: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
- self.err_ctxt().extract_callable_info(self.body_id, self.param_env, ty)
+ 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(
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(),
)
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<(hir::HirId, 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}"))
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();
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);
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);
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
out_of_scope_traits: Vec::new(),
- lev_candidate: None,
+ similar_candidate: None,
mode,
}));
}
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
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,
}))
}
/// 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))
// 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,
);
}
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() {
// 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(),
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);
&'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 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;
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()
let labeled_user_string = match bound_kind {
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
- GenericKind::Alias(ty::Projection, ref p) => format!("the associated type `{}`", p),
- GenericKind::Alias(ty::Opaque, ref p) => {
- format!(
- "the opaque type `{}`",
- self.tcx.def_path_str_with_substs(p.def_id, p.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()
}
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);
--- /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),
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);
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> {
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(())
}
}
// 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).
- Alias(ty::AliasKind, 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>>),
+ 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, data) => {
- out.push(Component::Alias(ty::Opaque, data));
- },
-
// 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, 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::Alias(ty::Projection, data));
+ out.push(Component::Alias(alias_ty));
} else {
// fallback case: hard code
// OutlivesProjectionComponents. Continue walking
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()));
}
}
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubAlias(r_a, kind, projection_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Alias(kind, projection_b), 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::Alias(kind, data) => {
- self.alias_must_outlive(*kind, *data, origin, region)
- }
- 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);
+ self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
}
#[instrument(level = "debug", skip(self))]
- fn alias_must_outlive(
+ fn alias_ty_must_outlive(
&mut self,
- kind: ty::AliasKind,
- data: ty::AliasTy<'tcx>,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
- ) {
- self.generic_must_outlive(
- origin,
- region,
- GenericKind::Alias(kind, data),
- data.def_id,
- data.substs,
- kind == ty::Opaque,
- |ty| match *ty.kind() {
- ty::Alias(filter_kind, ty::AliasTy { def_id, substs, .. })
- if kind == filter_kind =>
- {
- (def_id, 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;
}
// 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::Alias(kind, data) => self.projection_opaque_bounds(
- GenericKind::Alias(kind, data),
- data.def_id,
- data.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.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)
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
pub enum GenericKind<'tcx> {
Param(ty::ParamTy),
- Alias(ty::AliasKind, ty::AliasTy<'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::Alias(ty::Projection, ref p) => write!(f, "{:?}", p),
- GenericKind::Alias(ty::Opaque, ref p) => ty::tls::with(|tcx| {
- write!(f, "{}", tcx.def_path_str_with_substs(p.def_id, tcx.lift(p.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::Alias(ty::Projection, ref p) => write!(f, "{}", p),
- GenericKind::Alias(ty::Opaque, ref p) => ty::tls::with(|tcx| {
- write!(f, "{}", tcx.def_path_str_with_substs(p.def_id, tcx.lift(p.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::Alias(kind, data) => tcx.mk_ty(ty::Alias(kind, data)),
+ 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)
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> {
Component::UnresolvedInferenceVariable(_) => None,
- Component::Alias(kind, data) => {
- let ty = tcx.mk_ty(ty::Alias(kind, data));
+ 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`.
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
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)
}
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(),
)
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};
// 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()
{
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");
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(
| 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);
+ 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) {
///
/// ### 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,
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 {
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)
__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 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;
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());
+
+ let params_in_repr = self.tcx.params_in_repr(def_id);
+ record!(self.tables.params_in_repr[def_id] <- params_in_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));
+ 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);
+ }
}
}
}
}
- 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) {
+ 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);
}
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"),
}
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::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
}
}
}
impl<'tcx> CanonicalVarValues<'tcx> {
+ /// Creates dummy var values which should not be used in a
+ /// canonical response.
+ pub fn dummy() -> CanonicalVarValues<'tcx> {
+ CanonicalVarValues { var_values: Default::default() }
+ }
+
#[inline]
pub fn len(&self) -> usize {
self.var_values.len()
#![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> {
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> {
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.
+++ /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,
-}
-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(())
}
}
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" }
}
}
+ 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> {
}
/// 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)`.
///
/// 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(
}
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
pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
- RegionSubAlias(ty::Region<'tcx>, ty::AliasKind, ty::AliasTy<'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)
}
// late-bound regions, and we don't want method signatures to show up
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
- tcx.fn_sig(self.def_id).skip_binder().to_string()
+ 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>>,
#![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};
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) {
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 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;
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 =
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;
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::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
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;
// 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:
// 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
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)
}
ident
}
+ // FIXME(vincenzoapalzzo): move the HirId to a LocalDefId
pub fn adjust_ident_and_get_scope(
self,
mut ident: Ident,
}
#[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()) {
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,
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)
if not_previously_inserted {
ty.super_visit_with(self)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
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))
+ }
}
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
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;
bool,
usize,
::rustc_target::abi::VariantIdx,
+ u16,
u32,
u64,
String,
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(())
}
}
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)
+ }
+}
None
}
+
+ pub fn get_id(&self) -> Option<DefId> {
+ match *self {
+ BoundRegionKind::BrNamed(id, _) => return Some(id),
+ _ => None,
+ }
+ }
}
pub trait Article {
) -> 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)
+ })
})
}
if self.0.has_escaping_bound_vars() { None } else { Some(self.skip_binder()) }
}
- pub fn no_bound_vars_ignoring_escaping(self, tcx: TyCtxt<'tcx>) -> Option<T>
- where
- T: TypeFoldable<'tcx>,
- {
- if !self.0.has_escaping_bound_vars() {
- Some(self.skip_binder())
- } else {
- self.0.try_fold_with(&mut SkipBindersAt { index: ty::INNERMOST, tcx }).ok()
- }
- }
-
/// Splits the contents into two things that share the same binder
/// level as the original, returning two distinct binders.
///
/// 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,
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.
/// This hashset records all instances where we behave
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(),
}
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_explicit_item_bounds(
self,
def_id: DefId,
}
}
-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());
}
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),
)
}
//
// 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 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() {
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,
},
}
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(min_specialization)]
// 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 {
// 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 => {}
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 });
}
}
use rustc_middle::thir::{FieldPat, Pat, PatKind};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint;
+use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::Span;
use rustc_trait_selection::traits::predicate_for_trait_def;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
// using `PartialEq::eq` in this scenario in the past.)
let partial_eq_trait_id =
self.tcx().require_lang_item(hir::LangItem::PartialEq, Some(self.span));
+ let def_id = self.tcx().hir().opt_local_def_id(self.id).unwrap_or(CRATE_DEF_ID);
let obligation: PredicateObligation<'_> = predicate_for_trait_def(
self.tcx(),
self.param_env,
- ObligationCause::misc(self.span, self.id),
+ ObligationCause::misc(self.span, 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 {
}
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()?;
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!(
/// 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)
}
}
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> {
+ 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 (upvars, interior, movable) = match *gen_ty.kind() {
+ ty::Generator(_, substs, movability) => {
+ let substs = substs.as_generator();
+ (
+ substs.upvar_tys().collect::<Vec<_>>(),
+ substs.witness(),
+ 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);
+
+ if tcx.sess.opts.unstable_opts.drop_tracking_mir {
+ check_suspend_tys(tcx, &generator_layout, &body);
+ } else {
+ sanitize_witness(tcx, body, interior, upvars, &generator_layout);
+ }
+
+ 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, movable) = match *gen_ty.kind() {
ty::Generator(_, substs, movability) => {
let substs = substs.as_generator();
- (
- substs.upvar_tys().collect(),
- substs.witness(),
- substs.discr_ty(tcx),
- movability == hir::Movability::Movable,
- )
+ (substs.discr_ty(tcx), movability == hir::Movability::Movable)
}
_ => {
tcx.sess
}
};
- 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);
let can_return = can_return(tcx, body, tcx.param_env(body.source.def_id()));
}
}
}
+
+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 });
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");
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
}
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),
return tcx.mir_drops_elaborated_and_const_checked(def);
}
+ if tcx.generator_kind(def.did).is_some() {
+ 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();
&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")),
} 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 {
+++ /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,
- }
- }
-}
};
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
#![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 {
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>(
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() * 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_char.to_string().repeat(count),
+ ascii_str.to_string().repeat(count),
Applicability::MaybeIncorrect,
);
}
/// 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,
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) {
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()?;
// 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 {
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) }
}
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)
+ }
_ => {}
}
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)
// 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,
+}
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)
#![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)]
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(())
}
}
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(())
}
}
}
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(),
})
}
}
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"),
+ ));
}
}
-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(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,
});
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)]
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())));
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 {
(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],
}
}
-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
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(),
/// 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 {
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"));
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,
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)
allow_asm: true,
endian,
linker_flavor: LinkerFlavor::Bpf,
- atomic_cas: true,
+ atomic_cas: false,
dynamic_linking: true,
no_builtins: true,
panic_strategy: PanicStrategy::Abort,
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(),
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::{
- instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty, EvalCtxt,
- Goal,
-};
+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(usize),
+}
+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() };
+ fn consider_auto_trait_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
- acx.assemble_candidates_after_normalizing_self_ty(goal);
+ fn consider_trait_alias_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
- acx.assemble_impl_candidates(goal);
+ fn consider_builtin_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
- acx.candidates
- }
+ fn consider_builtin_copy_clone_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ fn consider_builtin_pointer_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ 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(
+ fn consider_builtin_tuple_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ fn consider_builtin_pointee_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ fn consider_builtin_future_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
+
+ 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 =
- 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),
- )
- })
+ // FIXME: This is broken if we care about the `usize` of `AliasBound` because the self type
+ // could be normalized to yet another projection with different item bounds.
+ 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 (i, (assumption, _)) in self
+ .tcx()
+ .bound_explicit_item_bounds(alias_ty.def_id)
+ .subst_iter_copied(self.tcx(), alias_ty.substs)
+ .enumerate()
+ {
+ match G::consider_assumption(self, goal, assumption) {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::AliasBound(i), 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, Certainty, MaybeCause, Response};
-use super::{EvalCtxt, QueryResult};
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, CanonicalVarValues};
-use rustc_middle::ty::{self, 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(goal));
- }
-
- v.insert(ProvisionalEntry {
- response: response_no_constraints(self.tcx, goal, Certainty::Yes),
- 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(response_no_constraints(
- self.tcx,
- goal,
- Certainty::Maybe(MaybeCause::Ambiguity),
- ))
- }
- }
- }
- }
-
- /// 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);
- }
- }
-}
-
-pub(super) fn response_no_constraints<'tcx>(
- tcx: TyCtxt<'tcx>,
- goal: Canonical<'tcx, impl Sized>,
- certainty: Certainty,
-) -> QueryResult<'tcx> {
- let var_values = goal
- .variables
- .iter()
- .enumerate()
- .map(|(i, info)| 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(),
- })
- .collect();
-
- Ok(Canonical {
- max_universe: goal.max_universe,
- variables: goal.variables,
- value: Response {
- var_values: CanonicalVarValues { var_values },
- external_constraints: Default::default(),
- certainty,
- },
- })
-}
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 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,
+ )
+ }
}
use std::mem;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::canonical::{Canonical, CanonicalVarKind, 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::Certainty as OldCertainty;
-use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
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 crate::traits::ObligationCause;
-use self::cache::response_no_constraints;
-use self::infcx_ext::InferCtxtExt;
-
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 {
/// 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),
- }
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.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((
- !canonical_response.value.var_values.is_identity(),
- instantiate_canonical_query_response(infcx, &orig_values, canonical_response),
- ))
- }
-
- 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;
+ 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.clone(),
+ external_constraints,
+ certainty,
+ }))
+ }
- if let Some(kind) = predicate.kind().no_bound_vars_ignoring_escaping(self.tcx) {
+ /// 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 => {
- // FIXME
- response_no_constraints(self.tcx, canonical_goal, Certainty::Yes)
+ 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> {
- // FIXME
- response_no_constraints(self.tcx, goal, Certainty::Yes)
+ 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 {
+ self.infcx.probe(|_| {
+ 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> {
+ 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().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> {
- // FIXME
- response_no_constraints(self.tcx, goal, Certainty::Yes)
+ self.infcx.probe(|_| {
+ 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> {
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)),
};
}
})
}
+
+ 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 instantiate_canonical_query_response<'tcx>(
assert!(obligations.is_empty());
value
}
+
+pub(super) fn response_no_constraints<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ goal: Canonical<'tcx, impl Sized>,
+ certainty: Certainty,
+) -> QueryResult<'tcx> {
+ let var_values = goal
+ .variables
+ .iter()
+ .enumerate()
+ .map(|(i, info)| 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(),
+ })
+ .collect();
+
+ Ok(Canonical {
+ max_universe: goal.max_universe,
+ variables: goal.variables,
+ value: Response {
+ var_values: CanonicalVarValues { var_values },
+ external_constraints: Default::default(),
+ certainty,
+ },
+ })
+}
+++ /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::cache::response_no_constraints;
-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,
- goal: Canonical<'tcx, impl Sized>,
- ) -> QueryResult<'tcx> {
- self.overflow_data.deal_with_overflow();
- response_no_constraints(self.tcx, goal, Certainty::Maybe(MaybeCause::Overflow))
- }
-
- /// 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))
- }
-}
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.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() {
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))
+ })
+ }
- 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 };
+ 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() {
+ 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)?;
- let certainty = trait_ref_certainty.unify_and(rhs_certainty);
- acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ // 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 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));
+
+ 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> {
+ 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.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() {
+ // 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,
+ )
+ }
- let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
- acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ fn consider_trait_alias_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+
+ 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)
}
(CandidateSource::Impl(_), _)
| (CandidateSource::ParamEnv(_), _)
| (CandidateSource::AliasBound(_), _)
- | (CandidateSource::Builtin, _)
- | (CandidateSource::AutoImpl, _) => unimplemented!(),
+ | (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;
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().subst_identity();
- debug!("orphan_check: trait_ref={:?}", trait_ref);
+ 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
);
let predicates =
- tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx);
+ 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()
}
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,
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 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,
}
}
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(())
}
}
}
.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(
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
fn note_conflicting_closure_bounds(
mut err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
associated_ty: Option<(&'static str, Ty<'tcx>)>,
- body_id: hir::HirId,
+ 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) {
+ let mut body_id = body_id;
+ while let Some(node) = self.tcx.hir().find_by_def_id(body_id) {
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(body_id);
match node {
hir::Node::Item(hir::Item {
ident,
_ => {}
}
-
- hir_id = self.tcx.hir().get_parent_item(hir_id).into();
+ body_id = self.tcx.local_parent(body_id);
}
}
trait_pred.self_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(
- obligation.cause.body_id,
+ body_hir_id,
obligation.param_env,
self_ty,
) else { return false; };
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 };
/// 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,
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),
..
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
);
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);
}
_ => 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>,
// 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;
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 infcx.can_eq(param_env, found_ty, expected_ty).is_ok() {
if found_refs < expected_refs {
- to_borrow.push((arg_span, expected_arg.to_string()));
+ to_borrow.push((arg.span.shrink_to_lo(), "&".repeat(expected_refs - found_refs)));
} else if found_refs > expected_refs {
- remove_borrow.push((arg_span, expected_arg.to_string()));
+ let mut span = arg.span.shrink_to_lo();
+ let mut left = found_refs - expected_refs;
+ 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())
}
}
//! 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;
// 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 => {
&& 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)
}
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`.
.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
}
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()
| 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(_)
| ty::FnPtr(_)
| ty::Char
| ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
| ty::RawPtr(_)
| ty::Ref(..)
| ty::Str
.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 =
| 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(_)
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;
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};
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::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()),
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)
+}
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) {
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
/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
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);
}
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,
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))
// 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::Alias(kind, p) => {
- Some(OutlivesBound::RegionSubAlias(sub_region, kind, p))
- }
- 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;
// 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 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,
}
// 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);
}
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
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();
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 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()));
/// 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);
# 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
#[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
#![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)]
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);
}
///
/// 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]> {
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 {
#[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;
}
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 {
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)]
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> {}
/// 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);
#[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"
)]
#[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(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()))
}
/// 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")]
//!
//! 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};
) 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));
}
#[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")]
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)]
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!(
// 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"
);
}
/// 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]>;
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]>,
{
/// 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]
) -> 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
- }
}
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);
}
}
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 });
}
}
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(
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);
// 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);
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
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 {
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();
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");
// 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
-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
--- /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
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 => {
));
}
-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>(
}
}
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();
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(),
}
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,
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)
};
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)?;
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, Symbol};
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),
+ );
+ }
}
}
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.
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;
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();
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();
// 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<_>>()
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;
}
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
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();
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;
}
-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> {
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(..)
}
// 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 a5d47a72595dd6fbe7d4e4f6ec20dc5fe724edd1
+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,
+ );
+ },
);
}
}
&& 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);
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))
},
} 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(cx.tcx.hir().local_def_id(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(_)
/// unimplemented!();
/// }
/// ```
- #[clippy::version = "1.66.0"]
+ #[clippy::version = "1.67.0"]
pub UNNECESSARY_SAFETY_DOC,
restriction,
"`pub fn` or `pub trait` with `# Safety` docs"
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 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,
/// 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"
},
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()
{
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 def_id = cx.tcx.hir().local_def_id(hir_id);
+ let cause = traits::ObligationCause::misc(span, 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(
// 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) => {
.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"
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`
}
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,
/// '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"
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());
}
}
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
/// 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()
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);
&& 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 fn_ctxt = FnCtxt::new(inherited, cx.param_env, local_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()
}
}
- 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)
+ });
+}
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);
+ 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_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! {
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,
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).map(|t| t.subst_identity())
+ && 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
{
(
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)))
{
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 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;
kind: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
body: &'tcx Body<'tcx>,
- _: Span,
+ sp: Span,
_: HirId,
) {
match kind {
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()
/// ### 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"
// - 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!(
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()
.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,
}
.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();
+ 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);
+ 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;
// 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)?;
// 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,
)?;
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
| ast::ExprKind::Yeet(..)
+ | ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Struct(..)
| ast::ExprKind::Try(..)
| ast::ExprKind::TryBlock(..)
.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, substs, .. }) => {
- sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id).subst(cx.tcx, substs), 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;
+ }
+ }
}
}
) -> 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,
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();
}
--- /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() {}
lazy_static = "1"
walkdir = "2"
ignore = "0.4.18"
+semver = "1.0"
termcolor = "1.1.3"
[[bin]]
("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"),
"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",
];
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 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!(alphabetical, &compiler_path);
check!(alphabetical, &library_path);
+ check!(x_version, &root_path, &cargo);
+
let collected = {
drain_handles(&mut handles);
--- /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
// 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))
+}
// 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"]
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 noundef ptr @__rust_alloc_zeroed(i64 noundef, i64 allocalign noundef) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]]
+++ /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);
+}
#[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
}
}
// 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): [],
--- /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>();
+}
+++ /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
|
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
// 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`
--- /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
+
// 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"),
)
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,
)
"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
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`.
-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)
#![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 {
-// @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'
// @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"]' \
#![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 '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'
+++ /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 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;
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::*;
+}
#![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'
// @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 {
#[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 {}
-// @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;
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 {}
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"
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'
<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 </a>B),
+ 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"><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),
+ 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"><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,
+ 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"><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,
+ 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
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 | }
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
--- /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
--> $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;
| ------^^^^^^^^
| | |
-// 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([^)]*)" -> "..."
--> $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
--> $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`.
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
--- /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
+}
| 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
--- /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`.
--- /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 | 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() {}
// 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
|
-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; {
--- /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() {}
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
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
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
#[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]
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;
--- /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
// 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.
error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
- --> $DIR/remap-path-prefix-reverse.rs:22:13
+ --> $DIR/remap-path-prefix-reverse.rs:16:13
|
-LL | let _ = remapped_dep::SomeStruct;
+LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
|
- ::: remapped-aux/remapped_dep.rs:3:1
+ ::: 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[E0423]: expected value, found struct `remapped_dep::SomeStruct`
- --> remapped/errors/remap-path-prefix-reverse.rs:22:13
+ --> $DIR/remap-path-prefix-reverse.rs:16:13
|
-LL | let _ = remapped_dep::SomeStruct;
+LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
|
- ::: remapped-aux/remapped_dep.rs:3:1
+ ::: 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
// aux-build:remapped_dep.rs
// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
-// The remapped paths are not normalized by compiletest.
-// normalize-stderr-test: "\\(errors)" -> "/$1"
-
// revisions: local-self remapped-self
-// [remapped-self]compile-flags: --remap-path-prefix={{src-base}}=remapped
-
-// The paths from `remapped-self` aren't recognized by compiletest, so we
-// cannot use line-specific patterns for the actual error.
-// error-pattern: E0423
+// [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
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;
+ let _ = remapped_dep::SomeStruct; // ~ERROR E0423
}
// 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"
error[E0425]: cannot find value `ferris` in this scope
- --> remapped/errors/remap-path-prefix.rs:15:5
+ --> remapped/errors/remap-path-prefix.rs:16:5
|
LL | ferris
| ^^^^^^ not found in this scope
--- /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`.
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);
| ------------^^-
| | |
--- /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
--> $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
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);
| -----^^------
| | |
--- /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`.
--- /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
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`.
| ^^^^^^^^^^^^^^^^^^^ `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)
+
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)
+
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)
+
{
- ::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`.
--> $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
--> $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
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;
--> $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
+// 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);
+}
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`.
--- /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`.
//~^ 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 x = 0;
| ++++
-error: aborting due to 2 previous errors
+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
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()
+}
// 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() {}
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
|
- = help: 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
|
- = help: 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
|
- = help: 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
|
- = help: 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
|
- = help: 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)
-error[E0412]: cannot find type `OuterDeriveLint` in this scope
- --> $DIR/generate-mod.rs:26:10
+Future breakage diagnostic:
+error: cannot find type `OuterDerive` 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
|
- = help: 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 `FromOutside` 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:
+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
--> $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`
...
|
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
...
#![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);
| ^^
| |
--- /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 | };
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
|
--- /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 {
#![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;
| ^
| -- 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
--- /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`.
-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`.
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() {}
//~| 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`.
--- /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`.
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 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`.
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 }> {
LL | 5u32
| ^^^^
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic lifetime parameter, found `'static`
--> $DIR/generic_nondefining_use.rs:21:5
|
LL | type OneLifetime<'a> = impl Debug;
--- /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`.
--- /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() { }
--- /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
+// 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 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
...
-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`.
-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`.
-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`.
-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`.
-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`.
-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",
(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"]