971c549ca334b7b7406e61e958efcca9c4152822
# refactor infcx building
283abbf0e7d20176f76006825b5c52e9a4234e4c
+# format libstd/sys
+c34fbfaad38cf5829ef5cfe780dc9d58480adeaa
+# move tests
+cf2dff2b1e3fa55fa5415d524200070d0d7aacfe
+++ /dev/null
----
-name: Diagnostic issue
-about: Create a bug report or feature request for a change to `rustc`'s error output
-labels: A-diagnostics, T-compiler
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
-
-If you cannot produce a minimal reproduction case (something that would work in
-isolation), please provide the steps or even link to a repository that causes
-the problematic output to occur.
--->
-
-Given the following code: <!-- Please provide a link to play.rust-lang.org -->
-
-```rust
-<code>
-```
-
-The current output is:
-
-```
-<rustc output>
-```
-
-<!-- The following is not always necessary. -->
-Ideally the output should look like:
-
-```
-<proposed output>
-```
-
-<!--
-If the problem is not self-explanatory, please provide a rationale for the
-change.
--->
-
-<!--
-If dramatically different output is caused by small changes, consider also
-adding them here.
-
-If you're using the stable version of the compiler, you should also check if the
-bug also exists in the beta or nightly versions. The output might also be
-different depending on the Edition.
--->
--- /dev/null
+name: Diagnostic issue
+description: Create a bug report or feature request for a change to `rustc`'s error output
+labels: ["A-diagnostics", "T-compiler"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for filing a diagnostics bug report! 🐛
+
+ Please provide a short summary of the bug, along with any information you feel relevant to replicating the bug.
+
+ If you cannot produce a minimal reproduction case (something that would work in isolation), please provide the steps or even link to a repository that causes the problematic output to occur.
+ - type: textarea
+ id: code
+ attributes:
+ label: Code
+ description: Please provide code that can reproduce the problem
+ placeholder: code
+ render: Rust
+ validations:
+ required: true
+ - type: textarea
+ id: output
+ attributes:
+ label: Current output
+ description: Please provide the `rustc` output you see
+ placeholder: rustc output
+ render: Shell
+ validations:
+ required: true
+ - type: textarea
+ id: desired-output
+ attributes:
+ label: Desired output
+ description: Please provide what the output *should* be
+ placeholder: proposed output
+ render: Shell
+ validations:
+ required: false
+ - type: textarea
+ id: rationale
+ attributes:
+ label: Rationale and extra context
+ description: If the problem is not self-explanatory, please provide a rationale for the change.
+ validations:
+ required: false
+ - type: textarea
+ id: other-output
+ attributes:
+ label: Other cases
+ description: If dramatically different output is caused by small changes, consider also adding them here.
+ render: Rust
+ validations:
+ required: false
+ - type: markdown
+ attributes:
+ value: |
+ If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions. The output might also be different depending on the Edition.
+ - type: textarea
+ id: extra
+ attributes:
+ label: Anything else?
+ description: If you have more details you want to give us to reproduce this issue, please add it here
+ validations:
+ required: false
\ No newline at end of file
+++ /dev/null
----
-name: Documentation problem
-about: Create a report for a documentation problem.
-labels: A-docs
----
-<!--
-
-Thank you for finding a documentation problem! 📚
-
-Documentation problems might be grammatical issues, typos, or unclear wording, please provide details regarding the documentation including where it is present.
-
-Note: If your issue is for one of these, please use their dedicated issue tracker instead:
-
-- The Rust Book: https://github.com/rust-lang/book/issues
-- Rust by Example: https://github.com/rust-lang/rust-by-example/issues
-- The Edition Guide: https://github.com/rust-lang/edition-guide/issues
-- The Cargo Book: https://github.com/rust-lang/cargo/issues
-- The Clippy Book: https://github.com/rust-lang/rust-clippy/issues
-- The Reference: https://github.com/rust-lang/reference/issues
-- The Rustonomicon: https://github.com/rust-lang/nomicon/issues
-- The Embedded Book: https://github.com/rust-embedded/book/issues
-
-All other documentation issues should be filed here.
-
-Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
-
--->
-
-### Location
-
-### Summary
--- /dev/null
+name: Documentation problem
+description: Create a report for a documentation problem.
+labels: ["A-docs"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for finding a documentation problem! 📚
+
+ Documentation problems might be grammatical issues, typos, or unclear wording, please provide details regarding the documentation including where it is present.
+
+ Note: If your issue is for one of these, please use their dedicated issue tracker instead:
+ - [The Rust Book](https://github.com/rust-lang/book/issues)
+ - [Rust by Example](https://github.com/rust-lang/rust-by-example/issues)
+ - [The Edition Guide](https://github.com/rust-lang/edition-guide/issues)
+ - [The Cargo Book](https://github.com/rust-lang/cargo/issues)
+ - [The Clippy Book](https://github.com/rust-lang/rust-clippy/issues)
+ - [The Reference](https://github.com/rust-lang/reference/issues)
+ - [The Rustonomicon](https://github.com/rust-lang/nomicon/issues)
+ - [The Embedded Book](https://github.com/rust-embedded/book/issues)
+
+ All other documentation issues should be filed here.
+
+ Or, if you find an issue related to rustdoc (e.g. doctest, rustdoc UI), please use the bug report or blank issue template instead.
+
+ - type: textarea
+ id: location
+ attributes:
+ label: Location
+ validations:
+ required: true
+
+ - type: textarea
+ id: summary
+ attributes:
+ label: Summary
+ validations:
+ required: true
\ No newline at end of file
+++ /dev/null
----
-name: Internal Compiler Error
-about: Create a report for an internal compiler error in rustc.
-labels: C-bug, I-ICE, T-compiler
----
-<!--
-Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide
-a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
-how to create smaller examples.
-
-http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
--->
-
-### Code
-
-```Rust
-<code>
-```
-
-
-### Meta
-<!--
-If you're using the stable version of the compiler, you should also check if the
-bug also exists in the beta or nightly versions.
--->
-
-`rustc --version --verbose`:
-```
-<version>
-```
-
-### Error output
-
-```
-<output>
-```
-
-<!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo build`.
--->
-<details><summary><strong>Backtrace</strong></summary>
-<p>
-
-```
-<backtrace>
-```
-
-</p>
-</details>
-
--- /dev/null
+name: Internal Compiler Error
+description: Create a report for an internal compiler error in `rustc`
+labels: ["C-bug", "I-ICE", "T-compiler"]
+title: "[ICE]: "
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for finding an Internal Compiler Error! 🧊
+
+ If possible, try to provide a minimal verifiable example.
+
+ You can read "[Rust Bug Minimization Patterns](http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/)" for how to create smaller examples.
+
+ - type: textarea
+ id: code
+ attributes:
+ label: Code
+ description: Please provide code or a link to a repository that can reproduce the problem
+ placeholder: code
+ render: Rust
+ validations:
+ required: false
+
+ - type: checkboxes
+ attributes:
+ label: Affected release channels
+ description: If you're using the stable version of the compiler, you should also check if the bug also exists in the beta or nightly versions
+ options:
+ - label: Previous Stable
+ required: false
+ - label: Current Stable
+ required: false
+ - label: Current Beta
+ required: false
+ - label: Current Nightly
+ required: false
+
+ - type: textarea
+ id: version
+ attributes:
+ label: Rust Version
+ description: Please provide the `rustc` version, `rustc --version --verbose`
+ placeholder: |
+ $ rustc --version --verbose
+ rustc 1.XX.Y (SHORTHASH DATE)
+ binary: rustc
+ commit-hash: LONGHASHVALUE
+ commit-date: DATE
+ host: PLATFORMTRIPLE
+ release: 1.XX.Y
+ LLVM version: XX.YY.ZZ
+ render: Shell
+ validations:
+ required: true
+
+ - type: textarea
+ id: output
+ attributes:
+ label: Current error output
+ description: Please provide the `rustc` output you see
+ placeholder: output
+ render: Shell
+ validations:
+ required: false
+
+ - type: textarea
+ id: backtrace
+ attributes:
+ label: Backtrace
+ description: Include a backtrace in the code block by setting `RUST_BACKTRACE=full` in your environment, e.g. `RUST_BACKTRACE=full cargo build`
+ render: Shell
+ validations:
+ required: true
+
+ - type: textarea
+ id: extra
+ attributes:
+ label: Anything else?
+ description: If you have more details you want to give us to reproduce this issue, please add it here
+ validations:
+ required: false
\ No newline at end of file
- name: x86_64-gnu-distcheck
os: ubuntu-20.04-xl
env: {}
+ - name: x86_64-gnu-llvm-15
+ env:
+ RUST_BACKTRACE: 1
+ os: ubuntu-20.04-xl
+ - name: x86_64-gnu-llvm-14
+ env:
+ RUST_BACKTRACE: 1
+ os: ubuntu-20.04-xl
- name: x86_64-gnu-llvm-13
env:
RUST_BACKTRACE: 1
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>
"version_check",
]
+[[package]]
+name = "ahash"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+]
+
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "276881980556fdadeb88aa1ffc667e4d2e8fe72531dfabcb7a82bb3c9ea9ba31"
dependencies = [
- "object",
+ "object 0.29.0",
]
[[package]]
"cfg-if",
"libc",
"miniz_oxide",
- "object",
+ "object 0.29.0",
"rustc-demangle",
]
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
- "clap 4.0.15",
+ "clap 4.1.3",
"crates-io",
"curl",
"curl-sys",
[[package]]
name = "cc"
-version = "1.0.76"
+version = "1.0.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76a284da2e6fe2092f2353e51713435363112dfd60030e22add80be333fb928f"
+checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
dependencies = [
"jobserver",
]
[[package]]
name = "clap"
-version = "4.0.15"
+version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bf8832993da70a4c6d13c581f4463c2bdda27b9bf1c5498dc4365543abe6d6f"
+checksum = "d8d93d855ce6a0aa87b8473ef9169482f40abaa2e9e0993024c35c902cbd5920"
dependencies = [
- "atty",
"bitflags",
- "clap_derive 4.0.13",
+ "clap_derive 4.1.0",
"clap_lex 0.3.0",
+ "is-terminal",
"once_cell",
"strsim",
"termcolor",
+ "terminal_size",
]
[[package]]
name = "clap_complete"
-version = "3.1.1"
+version = "4.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
+checksum = "10861370d2ba66b0f5989f83ebf35db6421713fd92351790e7fdd6c36774c56b"
dependencies = [
- "clap 3.2.20",
+ "clap 4.1.3",
]
[[package]]
[[package]]
name = "clap_derive"
-version = "4.0.13"
+version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad"
+checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8"
dependencies = [
"heck",
"proc-macro-error",
[[package]]
name = "gimli"
-version = "0.26.1"
+version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
+checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
dependencies = [
"compiler_builtins",
"fallible-iterator",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
- "ahash",
+ "ahash 0.7.4",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
+[[package]]
+name = "hashbrown"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
+dependencies = [
+ "ahash 0.8.2",
+]
+
[[package]]
name = "heck"
version = "0.4.0"
[[package]]
name = "indexmap"
-version = "1.9.1"
+version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
- "hashbrown",
+ "hashbrown 0.12.3",
"rustc-rayon",
"serde",
]
version = "0.1.0"
dependencies = [
"anyhow",
- "clap 4.0.15",
+ "clap 4.1.3",
"fs-err",
"rustdoc-json-types",
"serde",
[[package]]
name = "mdbook"
-version = "0.4.21"
+version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23f3e133c6d515528745ffd3b9f0c7d975ae039f0b6abb099f2168daa2afb4f9"
+checksum = "d1ed28d5903dde77bd5182645078a37ee57014cac6ccb2d54e1d6496386648e4"
dependencies = [
"ammonia",
"anyhow",
"chrono",
- "clap 3.2.20",
+ "clap 4.1.3",
"clap_complete",
"elasticlunr-rs",
- "env_logger 0.9.0",
+ "env_logger 0.10.0",
"handlebars 4.3.3",
- "lazy_static",
"log",
"memchr",
+ "once_cell",
"opener",
"pulldown-cmark 0.9.2",
"regex",
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
dependencies = [
"compiler_builtins",
+ "memchr",
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+]
+
+[[package]]
+name = "object"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d864c91689fdc196779b98dba0aceac6118594c2df6ee5d943eb6a8df4d107a"
+dependencies = [
"crc32fast",
"flate2",
- "hashbrown",
+ "hashbrown 0.13.1",
"indexmap",
"memchr",
- "rustc-std-workspace-alloc",
- "rustc-std-workspace-core",
]
[[package]]
[[package]]
name = "pest"
-version = "2.3.0"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4"
+checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4"
dependencies = [
"thiserror",
"ucd-trie",
[[package]]
name = "pest_derive"
-version = "2.3.0"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "905708f7f674518498c1f8d644481440f476d39ca6ecae83319bba7c6c12da91"
+checksum = "96504449aa860c8dcde14f9fba5c58dc6658688ca1fe363589d6327b8662c603"
dependencies = [
"pest",
"pest_generator",
[[package]]
name = "pest_generator"
-version = "2.3.0"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5803d8284a629cc999094ecd630f55e91b561a1d1ba75e233b00ae13b91a69ad"
+checksum = "798e0220d1111ae63d66cb66a5dcb3fc2d986d520b98e49e1852bfdb11d7c5e7"
dependencies = [
"pest",
"pest_meta",
[[package]]
name = "pest_meta"
-version = "2.3.0"
+version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04"
+checksum = "984298b75898e30a843e278a9f2452c31e349a073a0ce6fd950a12a74464e065"
dependencies = [
"once_cell",
"pest",
- "sha-1",
+ "sha1",
]
[[package]]
name = "rustbook"
version = "0.1.0"
dependencies = [
- "clap 3.2.20",
+ "clap 4.1.3",
"env_logger 0.7.1",
"mdbook",
]
"cstr",
"libc",
"measureme",
- "object",
+ "object 0.30.1",
"rustc-demangle",
"rustc_ast",
"rustc_attr",
"itertools",
"jobserver",
"libc",
- "object",
+ "object 0.30.1",
"pathdiff",
"regex",
"rustc_arena",
dependencies = [
"rustc_span",
"tracing",
+ "tracing-core",
"tracing-subscriber",
"tracing-tree",
]
name = "rustc_metadata"
version = "0.0.0"
dependencies = [
+ "bitflags",
"libloading",
"odht",
"rustc_ast",
"rustc_macros",
"rustc_serialize",
"scoped-tls",
- "sha-1",
+ "sha1",
"sha2",
"tracing",
"unicode-width",
"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",
"serde",
]
-[[package]]
-name = "sha-1"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
-dependencies = [
- "cfg-if",
- "cpufeatures",
- "digest",
-]
-
[[package]]
name = "sha1"
version = "0.10.5"
"core",
"dlmalloc",
"fortanix-sgx-abi",
- "hashbrown",
+ "hashbrown 0.12.3",
"hermit-abi 0.2.6",
"libc",
"miniz_oxide",
- "object",
+ "object 0.29.0",
"panic_abort",
"panic_unwind",
"profiler_builtins",
"winapi-util",
]
+[[package]]
+name = "terminal_size"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
+dependencies = [
+ "rustix",
+ "windows-sys",
+]
+
[[package]]
name = "termize"
version = "0.1.1"
[[package]]
name = "thiserror"
-version = "1.0.33"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.33"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
[[package]]
name = "thorin-dwp"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6cb0c7868d7f90407531108ab03263d9452a8811b7cdd87675343a40d4aa254"
+checksum = "da8fbf660a019b6bf11ea95762041464aa9099cc293b6a66d77cea5107619671"
dependencies = [
"gimli",
- "hashbrown",
- "object",
+ "hashbrown 0.12.3",
+ "object 0.30.1",
"tracing",
]
name = "tidy"
version = "0.1.0"
dependencies = [
+ "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cargo_metadata 0.14.0",
"ignore",
"lazy_static",
"miropt-test-tools",
"regex",
+ "semver",
"termcolor",
"walkdir",
]
[[package]]
name = "topological-sort"
-version = "0.1.0"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
+checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]]
name = "tracing"
[[package]]
name = "ucd-trie"
-version = "0.1.3"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
[[package]]
name = "ui_test"
[[package]]
name = "version_check"
-version = "0.9.3"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "vte"
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)
===========================
/// named `inverse_memory_index`.
///
// FIXME(eddyb) build a better abstraction for permutations, if possible.
- // FIXME(camlorn) also consider small vector optimization here.
+ // FIXME(camlorn) also consider small vector optimization here.
memory_index: Vec<u32>,
},
}
/// Enum-likes with more than one inhabited variant: each variant comes with
/// a *discriminant* (usually the same as the variant index but the user can
- /// assign explicit discriminant values). That discriminant is encoded
- /// as a *tag* on the machine. The layout of each variant is
+ /// assign explicit discriminant values). That discriminant is encoded
+ /// as a *tag* on the machine. The layout of each variant is
/// a struct, and they all have space reserved for the tag.
/// For enums, the tag is the sole field of the layout.
Multiple {
pub struct Closure {
pub binder: ClosureBinder,
pub capture_clause: CaptureBy,
+ pub constness: Const,
pub asyncness: Async,
pub movability: Movability,
pub fn_decl: P<FnDecl>,
impl Ty {
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
- while let TyKind::Ref(_, MutTy { ty, .. }) = &final_ty.kind {
+ while let TyKind::Ref(_, MutTy { ty, .. }) | TyKind::Ptr(MutTy { ty, .. }) = &final_ty.kind
+ {
final_ty = ty;
}
final_ty
ExprKind::Closure(box Closure {
binder,
capture_clause: _,
+ constness,
asyncness,
movability: _,
fn_decl,
fn_arg_span: _,
}) => {
vis.visit_closure_binder(binder);
+ visit_constness(constness, vis);
vis.visit_asyncness(asyncness);
vis.visit_fn_decl(fn_decl);
vis.visit_expr(body);
| ExprPrecedence::Yeet => PREC_JUMP,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
- // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
+ // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
// ensures that `pprust` will add parentheses in the right places to get the desired
// parse.
ExprPrecedence::Range => PREC_RANGE,
binder,
capture_clause: _,
asyncness: _,
+ constness: _,
movability: _,
fn_decl,
body,
ExprKind::Closure(box Closure {
binder,
capture_clause,
+ constness,
asyncness,
movability,
fn_decl,
binder,
*capture_clause,
e.id,
+ *constness,
*movability,
fn_decl,
body,
fn_decl_span: self.lower_span(span),
fn_arg_span: None,
movability: Some(hir::Movability::Static),
+ constness: hir::Constness::NotConst,
});
hir::ExprKind::Closure(c)
binder: &ClosureBinder,
capture_clause: CaptureBy,
closure_id: NodeId,
+ constness: Const,
movability: Movability,
decl: &FnDecl,
body: &Expr,
fn_decl_span: self.lower_span(fn_decl_span),
fn_arg_span: Some(self.lower_span(fn_arg_span)),
movability: generator_option,
+ constness: self.lower_constness(constness),
});
hir::ExprKind::Closure(c)
fn_decl_span: self.lower_span(fn_decl_span),
fn_arg_span: Some(self.lower_span(fn_arg_span)),
movability: None,
+ constness: hir::Constness::NotConst,
});
hir::ExprKind::Closure(c)
}
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
let mut nodes = IndexVec::new();
// This node's parent should never be accessed: the owner's parent is computed by the
- // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
+ // hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
// used.
nodes.push(Some(ParentedNode { parent: ItemLocalId::INVALID, node: item.into() }));
let mut collector = NodeCollector {
//
// The first two are produced by recursively invoking
// `lower_use_tree` (and indeed there may be things
- // like `use foo::{a::{b, c}}` and so forth). They
+ // like `use foo::{a::{b, c}}` and so forth). They
// wind up being directly added to
// `self.items`. However, the structure of this
// function also requires us to return one item, and
}
}
- fn lower_constness(&mut self, c: Const) -> hir::Constness {
+ pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
match c {
Const::Yes(_) => hir::Constness::Const,
Const::No => hir::Constness::NotConst,
pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
let sess = tcx.sess;
- let krate = tcx.untracked_crate.steal();
- let mut resolver = tcx.resolver_for_lowering(()).steal();
+ let (mut resolver, krate) = tcx.resolver_for_lowering(()).steal();
let ast_index = index_crate(&resolver.node_id_to_def_id, &krate);
let mut owners = IndexVec::from_fn_n(
self.arena.alloc(hir::OwnerInfo { nodes, parenting, attrs, trait_map })
}
- /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
+ /// Hash the HIR node twice, one deep and one shallow hash. This allows to differentiate
/// queries which depend on the full HIR tree and those which only depend on the item signature.
fn hash_owner(
&mut self,
itctx: &ImplTraitContext,
) -> hir::Ty<'hir> {
// Check whether we should interpret this as a bare trait object.
- // This check mirrors the one in late resolution. We only introduce this special case in
+ // This check mirrors the one in late resolution. We only introduce this special case in
// the rare occurrence we need to lower `Fresh` anonymous lifetimes.
// The other cases when a qpath should be opportunistically made a trait object are handled
// by `ty_path`.
this.with_remapping(new_remapping, |this| {
// We have to be careful to get elision right here. The
// idea is that we create a lifetime parameter for each
- // lifetime in the return type. So, given a return type
+ // lifetime in the return type. So, given a return type
// like `async fn foo(..) -> &[&u32]`, we lower to `impl
// Future<Output = &'1 [ &'2 u32 ]>`.
//
// Create the `Foo<...>` reference itself. Note that the `type
// Foo = impl Trait` is, internally, created as a child of the
- // async fn, so the *type parameters* are inherited. It's
+ // async fn, so the *type parameters* are inherited. It's
// only the lifetime parameters that we must supply.
let opaque_ty_ref = hir::TyKind::OpaqueDef(
hir::ItemId { owner_id: hir::OwnerId { def_id: opaque_ty_def_id } },
replace_span: self.ending_semi_or_hi(item.span),
extern_block_suggestion: match sig.header.ext {
Extern::None => None,
- Extern::Implicit(start_span) => Some(ExternBlockSuggestion {
+ Extern::Implicit(start_span) => Some(ExternBlockSuggestion::Implicit {
start_span,
end_span: item.span.shrink_to_hi(),
- abi: None,
- }),
- Extern::Explicit(abi, start_span) => Some(ExternBlockSuggestion {
- start_span,
- end_span: item.span.shrink_to_hi(),
- abi: Some(abi.symbol_unescaped),
}),
+ Extern::Explicit(abi, start_span) => {
+ Some(ExternBlockSuggestion::Explicit {
+ start_span,
+ end_span: item.span.shrink_to_hi(),
+ abi: abi.symbol_unescaped,
+ })
+ }
},
});
}
//! Errors emitted by ast_passes.
-use rustc_errors::{fluent, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessage};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
pub extern_block_suggestion: Option<ExternBlockSuggestion>,
}
-pub struct ExternBlockSuggestion {
- pub start_span: Span,
- pub end_span: Span,
- pub abi: Option<Symbol>,
-}
-
-impl AddToDiagnostic for ExternBlockSuggestion {
- fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
- where
- F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
- {
- let start_suggestion = if let Some(abi) = self.abi {
- format!("extern \"{}\" {{", abi)
- } else {
- "extern {".to_owned()
- };
- let end_suggestion = " }".to_owned();
-
- diag.multipart_suggestion(
- fluent::extern_block_suggestion,
- vec![(self.start_span, start_suggestion), (self.end_span, end_suggestion)],
- Applicability::MaybeIncorrect,
- );
- }
+#[derive(Subdiagnostic)]
+pub enum ExternBlockSuggestion {
+ #[multipart_suggestion(ast_passes_extern_block_suggestion, applicability = "maybe-incorrect")]
+ Implicit {
+ #[suggestion_part(code = "extern {{")]
+ start_span: Span,
+ #[suggestion_part(code = " }}")]
+ end_span: Span,
+ },
+ #[multipart_suggestion(ast_passes_extern_block_suggestion, applicability = "maybe-incorrect")]
+ Explicit {
+ #[suggestion_part(code = "extern \"{abi}\" {{")]
+ start_span: Span,
+ #[suggestion_part(code = " }}")]
+ end_span: Span,
+ abi: Symbol,
+ },
}
ast::ExprKind::TryBlock(_) => {
gate_feature_post!(&self, try_blocks, e.span, "`try` expression is experimental");
}
+ ast::ExprKind::Closure(box ast::Closure { constness: ast::Const::Yes(_), .. }) => {
+ gate_feature_post!(
+ &self,
+ const_closures,
+ e.span,
+ "const closures are experimental"
+ );
+ }
_ => {}
}
visit::walk_expr(self, e)
ast::ExprKind::Closure(box ast::Closure {
binder,
capture_clause,
+ constness,
asyncness,
movability,
fn_decl,
fn_arg_span: _,
}) => {
self.print_closure_binder(binder);
+ self.print_constness(*constness);
self.print_movability(*movability);
self.print_asyncness(*asyncness);
self.print_capture_clause(*capture_clause);
self.word("]");
}
ast::ExprKind::Range(start, end, limits) => {
- // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
+ // Special case for `Range`. `AssocOp` claims that `Range` has higher precedence
// than `Assign`, but `x .. x = x` gives a parse error instead of `x .. (x = x)`.
// Here we use a fake precedence value so that any child with lower precedence than
- // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
+ // a "normal" binop gets parenthesized. (`LOr` is the lowest-precedence binop.)
let fake_prec = AssocOp::LOr.precedence() as i8;
if let Some(e) = start {
self.print_expr_maybe_paren(e, fake_prec);
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
}
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
+use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, LangItem};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
use rustc_span::def_id::LocalDefId;
use rustc_span::hygiene::DesugaringKind;
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{kw, sym};
use rustc_span::{BytePos, Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
use crate::diagnostics::find_all_local_uses;
+use crate::diagnostics::mutability_errors::mut_borrow_of_mutable_ref;
use crate::{
borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
InitializationRequiringAction, MirBorrowckCtxt, PrefixSet, WriteKind,
if let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(_, _, body_id),
..
- })) = hir.find(hir.local_def_id_to_hir_id(self.mir_def_id()))
+ })) = hir.find(self.mir_hir_id())
&& let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id)
{
let place = &self.move_data.move_paths[mpi].place;
let tcx = self.infcx.tcx;
// Find out if the predicates show that the type is a Fn or FnMut
- let find_fn_kind_from_did = |predicates: ty::EarlyBinder<
- &[(ty::Predicate<'tcx>, Span)],
- >,
- substs| {
- predicates.0.iter().find_map(|(pred, _)| {
- let pred = if let Some(substs) = substs {
- predicates.rebind(*pred).subst(tcx, substs).kind().skip_binder()
- } else {
- pred.kind().skip_binder()
- };
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred && pred.self_ty() == ty {
- if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
- return Some(hir::Mutability::Not);
- } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
- return Some(hir::Mutability::Mut);
- }
+ let find_fn_kind_from_did = |(pred, _): (ty::Predicate<'tcx>, _)| {
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = pred.kind().skip_binder()
+ && pred.self_ty() == ty
+ {
+ if Some(pred.def_id()) == tcx.lang_items().fn_trait() {
+ return Some(hir::Mutability::Not);
+ } else if Some(pred.def_id()) == tcx.lang_items().fn_mut_trait() {
+ return Some(hir::Mutability::Mut);
}
- None
- })
+ }
+ None
};
// If the type is opaque/param/closure, and it is Fn or FnMut, let's suggest (mutably)
// borrowing the type, since `&mut F: FnMut` iff `F: FnMut` and similarly for `Fn`.
// These types seem reasonably opaque enough that they could be substituted with their
// borrowed variants in a function body when we see a move error.
- let borrow_level = match ty.kind() {
- ty::Param(_) => find_fn_kind_from_did(
- tcx.bound_explicit_predicates_of(self.mir_def_id().to_def_id())
- .map_bound(|p| p.predicates),
- None,
- ),
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- find_fn_kind_from_did(tcx.bound_explicit_item_bounds(*def_id), Some(*substs))
- }
+ let borrow_level = match *ty.kind() {
+ ty::Param(_) => tcx
+ .explicit_predicates_of(self.mir_def_id().to_def_id())
+ .predicates
+ .iter()
+ .copied()
+ .find_map(find_fn_kind_from_did),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => tcx
+ .bound_explicit_item_bounds(def_id)
+ .subst_iter_copied(tcx, substs)
+ .find_map(find_fn_kind_from_did),
ty::Closure(_, substs) => match substs.as_closure().kind() {
ty::ClosureKind::Fn => Some(hir::Mutability::Not),
ty::ClosureKind::FnMut => Some(hir::Mutability::Mut),
let copy_did = infcx.tcx.require_lang_item(LangItem::Copy, Some(span));
let cause = ObligationCause::new(
span,
- self.mir_hir_id(),
+ self.mir_def_id(),
rustc_infer::traits::ObligationCauseCode::MiscObligation,
);
let errors = rustc_trait_selection::traits::fully_solve_bound(
}
(BorrowKind::Mut { .. }, BorrowKind::Shared) => {
first_borrow_desc = "immutable ";
- self.cannot_reborrow_already_borrowed(
+ let mut err = self.cannot_reborrow_already_borrowed(
span,
&desc_place,
&msg_place,
"immutable",
&msg_borrow,
None,
- )
+ );
+ self.suggest_binding_for_closure_capture_self(
+ &mut err,
+ issued_borrow.borrowed_place,
+ &issued_spans,
+ );
+ err
}
(BorrowKind::Mut { .. }, BorrowKind::Mut { .. }) => {
}
}
+ fn suggest_binding_for_closure_capture_self(
+ &self,
+ err: &mut Diagnostic,
+ borrowed_place: Place<'tcx>,
+ issued_spans: &UseSpans<'tcx>,
+ ) {
+ let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
+ let hir = self.infcx.tcx.hir();
+
+ // check whether the borrowed place is capturing `self` by mut reference
+ let local = borrowed_place.local;
+ let Some(_) = self
+ .body
+ .local_decls
+ .get(local)
+ .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local])) else { return };
+
+ struct ExpressionFinder<'hir> {
+ capture_span: Span,
+ closure_change_spans: Vec<Span>,
+ closure_arg_span: Option<Span>,
+ in_closure: bool,
+ suggest_arg: String,
+ hir: rustc_middle::hir::map::Map<'hir>,
+ closure_local_id: Option<hir::HirId>,
+ closure_call_changes: Vec<(Span, String)>,
+ }
+ impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
+ fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
+ if e.span.contains(self.capture_span) {
+ if let hir::ExprKind::Closure(&hir::Closure {
+ movability: None,
+ body,
+ fn_arg_span,
+ fn_decl: hir::FnDecl{ inputs, .. },
+ ..
+ }) = e.kind &&
+ let Some(hir::Node::Expr(body )) = self.hir.find(body.hir_id) {
+ self.suggest_arg = "this: &Self".to_string();
+ if inputs.len() > 0 {
+ self.suggest_arg.push_str(", ");
+ }
+ self.in_closure = true;
+ self.closure_arg_span = fn_arg_span;
+ self.visit_expr(body);
+ self.in_closure = false;
+ }
+ }
+ if let hir::Expr { kind: hir::ExprKind::Path(path), .. } = e {
+ if let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
+ seg.ident.name == kw::SelfLower && self.in_closure {
+ self.closure_change_spans.push(e.span);
+ }
+ }
+ hir::intravisit::walk_expr(self, e);
+ }
+
+ fn visit_local(&mut self, local: &'hir hir::Local<'hir>) {
+ if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat &&
+ let Some(init) = local.init
+ {
+ if let hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure {
+ movability: None,
+ ..
+ }), .. } = init &&
+ init.span.contains(self.capture_span) {
+ self.closure_local_id = Some(*hir_id);
+ }
+ }
+ hir::intravisit::walk_local(self, local);
+ }
+
+ fn visit_stmt(&mut self, s: &'hir hir::Stmt<'hir>) {
+ if let hir::StmtKind::Semi(e) = s.kind &&
+ let hir::ExprKind::Call(hir::Expr { kind: hir::ExprKind::Path(path), ..}, args) = e.kind &&
+ let hir::QPath::Resolved(_, hir::Path { segments: [seg], ..}) = path &&
+ let Res::Local(hir_id) = seg.res &&
+ Some(hir_id) == self.closure_local_id {
+ let (span, arg_str) = if args.len() > 0 {
+ (args[0].span.shrink_to_lo(), "self, ".to_string())
+ } else {
+ let span = e.span.trim_start(seg.ident.span).unwrap_or(e.span);
+ (span, "(self)".to_string())
+ };
+ self.closure_call_changes.push((span, arg_str));
+ }
+ hir::intravisit::walk_stmt(self, s);
+ }
+ }
+
+ if let Some(hir::Node::ImplItem(
+ hir::ImplItem { kind: hir::ImplItemKind::Fn(_fn_sig, body_id), .. }
+ )) = hir.find(self.mir_hir_id()) &&
+ let Some(hir::Node::Expr(expr)) = hir.find(body_id.hir_id) {
+ let mut finder = ExpressionFinder {
+ capture_span: *capture_kind_span,
+ closure_change_spans: vec![],
+ closure_arg_span: None,
+ in_closure: false,
+ suggest_arg: String::new(),
+ closure_local_id: None,
+ closure_call_changes: vec![],
+ hir,
+ };
+ finder.visit_expr(expr);
+
+ if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
+ return;
+ }
+
+ let mut sugg = vec![];
+ let sm = self.infcx.tcx.sess.source_map();
+
+ if let Some(span) = finder.closure_arg_span {
+ sugg.push((sm.next_point(span.shrink_to_lo()).shrink_to_hi(), finder.suggest_arg));
+ }
+ for span in finder.closure_change_spans {
+ sugg.push((span, "this".to_string()));
+ }
+
+ for (span, suggest) in finder.closure_call_changes {
+ sugg.push((span, suggest));
+ }
+
+ err.multipart_suggestion_verbose(
+ "try explicitly pass `&Self` into the Closure as an argument",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
/// Returns the description of the root place for a conflicting borrow and the full
/// descriptions of the places that caused the conflict.
///
&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);
//! 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 {
};
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"));
}
}
}
}
}
-fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
+pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
match local_decl.local_info.as_deref() {
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,
#[track_caller]
pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) {
let val = val.into();
- self.1.sess.delay_span_bug(DUMMY_SP, "{val:?}");
+ self.1.sess.delay_span_bug(DUMMY_SP, format!("{val:?}"));
self.0.push(val);
}
pub fn is_empty(&self) -> bool {
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;
}
// `self.foo` -- we want to double
// check that the location `*self`
// is mutable (i.e., this is not a
- // `Fn` closure). But if that
+ // `Fn` closure). But if that
// check succeeds, we want to
// *blame* the mutability on
// `place` (that is,
fn is_upvar_field_projection(&self, place_ref: PlaceRef<'tcx>) -> Option<Field> {
path_utils::is_upvar_field_projection(self.infcx.tcx, &self.upvars, place_ref, self.body())
}
+
+ fn dominators(&self) -> &Dominators<BasicBlock> {
+ self.dominators.get_or_init(|| self.body.basic_blocks.dominators())
+ }
}
mod error {
R1: Copy + Hash + Eq,
{
/// Remap the "member region" key using `map_fn`, producing a new
- /// member constraint set. This is used in the NLL code to map from
+ /// member constraint set. This is used in the NLL code to map from
/// the original `RegionVid` to an scc index. In some cases, we
/// may have multiple `R1` values mapping to the same `R2` key -- that
/// is ok, the two sets will be merged.
}
/// Iterate down the constraint indices associated with a given
- /// peek-region. You can then use `choice_regions` and other
+ /// peek-region. You can then use `choice_regions` and other
/// methods to access data.
pub(crate) fn indices(
&self,
// When the enclosing function is tagged with `#[rustc_regions]`,
// we dump out various bits of state as warnings. This is useful
- // for verifying that the compiler is behaving as expected. These
+ // for verifying that the compiler is behaving as expected. These
// warnings focus on the closure region requirements -- for
// viewing the intraprocedural state, the -Zdump-mir output is
// better.
ty::RawPtr(..) | ty::Ref(_, _, hir::Mutability::Not) => {
// For both derefs of raw pointers and `&T`
// references, the original path is `Copy` and
- // therefore not significant. In particular,
+ // therefore not significant. In particular,
// there is nothing the user can do to the
// original path that would invalidate the
// newly created reference -- and if there
match (elem, &base_ty.kind(), access) {
(_, _, Shallow(Some(ArtificialField::ArrayLength)))
| (_, _, Shallow(Some(ArtificialField::ShallowBorrow))) => {
- // The array length is like additional fields on the
+ // The array length is like additional fields on the
// type; it does not overlap any existing data there.
// Furthermore, if cannot actually be a prefix of any
// borrowed place (at least in MIR as it is currently.)
use rustc_data_structures::graph::scc::Sccs;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::CRATE_DEF_ID;
-use rustc_hir::CRATE_HIR_ID;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::outlives::test_type_match;
use rustc_infer::infer::region_constraints::{GenericKind, VarInfos, VerifyBound, VerifyIfEq};
self.scc_values.region_value_str(scc)
}
+ pub(crate) fn placeholders_contained_in<'a>(
+ &'a self,
+ r: RegionVid,
+ ) -> impl Iterator<Item = ty::PlaceholderRegion> + 'a {
+ let scc = self.constraint_sccs.scc(r.to_region_vid());
+ self.scc_values.placeholders_contained_in(scc)
+ }
+
/// Returns access to the value of `r` for debugging purposes.
pub(crate) fn region_universe(&self, r: RegionVid) -> ty::UniverseIndex {
let scc = self.constraint_sccs.scc(r.to_region_vid());
/// enforce the constraint).
///
/// The current value of `scc` at the time the method is invoked
- /// is considered a *lower bound*. If possible, we will modify
+ /// is considered a *lower bound*. If possible, we will modify
/// the constraint to set it equal to one of the option regions.
/// If we make any changes, returns true, else false.
#[instrument(skip(self, member_constraint_index), level = "debug")]
//
// This is needed because -- particularly in the case
// where `ur` is a local bound -- we are sometimes in a
- // position to prove things that our caller cannot. See
+ // position to prove things that our caller cannot. See
// #53570 for an example.
if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) {
continue;
.map(|constraint| BlameConstraint {
category: constraint.category,
from_closure: constraint.from_closure,
- cause: ObligationCause::new(constraint.span, CRATE_HIR_ID, cause_code.clone()),
+ cause: ObligationCause::new(constraint.span, CRATE_DEF_ID, cause_code.clone()),
variance_info: constraint.variance_info,
outlives_constraint: *constraint,
})
// '5: '6 ('6 is the target)
//
// Some of those regions are unified with `'6` (in the same
- // SCC). We want to screen those out. After that point, the
+ // SCC). We want to screen those out. After that point, the
// "closest" constraint we have to the end is going to be the
// most likely to be the point where the value escapes -- but
// we still want to screen for an "interesting" point to
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::ObligationCtxt;
+use crate::session_diagnostics::NonGenericOpaqueTypeParam;
+
use super::RegionInferenceContext;
impl<'tcx> RegionInferenceContext<'tcx> {
/// # Parameters
///
/// - `def_id`, the `impl Trait` type
- /// - `substs`, the substs used to instantiate this opaque type
+ /// - `substs`, the substs used to instantiate this opaque type
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
/// `opaque_defn.concrete_ty`
#[instrument(level = "debug", skip(self))]
}
let definition_ty = instantiated_ty
- .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false, origin)
+ .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
.ty;
if !check_opaque_type_parameter_valid(
// This logic duplicates most of `check_opaque_meets_bounds`.
// FIXME(oli-obk): Also do region checks here and then consider removing `check_opaque_meets_bounds` entirely.
let param_env = self.tcx.param_env(def_id);
- let body_id = self.tcx.local_def_id_to_hir_id(def_id);
// HACK This bubble is required for this tests to pass:
// type-alias-impl-trait/issue-67844-nested-opaque.rs
let infcx =
// the bounds that the function supplies.
let opaque_ty = self.tcx.mk_opaque(def_id.to_def_id(), id_substs);
if let Err(err) = ocx.eq(
- &ObligationCause::misc(instantiated_ty.span, body_id),
+ &ObligationCause::misc(instantiated_ty.span, def_id),
param_env,
opaque_ty,
definition_ty,
infcx
.err_ctxt()
.report_mismatched_types(
- &ObligationCause::misc(instantiated_ty.span, body_id),
+ &ObligationCause::misc(instantiated_ty.span, def_id),
opaque_ty,
definition_ty,
err,
ocx.register_obligation(Obligation::misc(
infcx.tcx,
instantiated_ty.span,
- body_id,
+ def_id,
param_env,
predicate,
));
} else {
// Prevent `fn foo() -> Foo<u32>` from being defining.
let opaque_param = opaque_generics.param_at(i, tcx);
- tcx.sess
- .struct_span_err(span, "non-defining opaque type use in defining scope")
- .span_note(
- tcx.def_span(opaque_param.def_id),
- &format!(
- "used non-generic {} `{}` for generic parameter",
- opaque_param.kind.descr(),
- arg,
- ),
- )
- .emit();
+ let kind = opaque_param.kind.descr();
+ tcx.sess.emit_err(NonGenericOpaqueTypeParam {
+ ty: arg,
+ kind,
+ span,
+ param_span: tcx.def_span(opaque_param.def_id),
+ });
return false;
}
}
use rustc_errors::{IntoDiagnosticArg, MultiSpan};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{GenericArg, Ty};
use rustc_span::Span;
use crate::diagnostics::RegionName;
#[label]
pub borrow_span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(borrowck_opaque_type_non_generic_param, code = "E0792")]
+pub(crate) struct NonGenericOpaqueTypeParam<'a, 'tcx> {
+ pub ty: GenericArg<'tcx>,
+ pub kind: &'a str,
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub param_span: Span,
+}
instantiated_predicates: ty::InstantiatedPredicates<'tcx>,
locations: Locations,
) {
- for (predicate, span) in instantiated_predicates
- .predicates
- .into_iter()
- .zip(instantiated_predicates.spans.into_iter())
- {
+ for (predicate, span) in instantiated_predicates {
debug!(?predicate);
let category = ConstraintCategory::Predicate(span);
let predicate = self.normalize_with_category(predicate, locations, category);
closure_substs: ty::SubstsRef<'tcx>,
) {
// Extract the values of the free regions in `closure_substs`
- // into a vector. These are the regions that we will be
+ // into a vector. These are the regions that we will be
// relating to one another.
let closure_mapping = &UniversalRegions::closure_mapping(
self.tcx,
let upper_bounds = self.non_local_upper_bounds(fr);
// In case we find more than one, reduce to one for
- // convenience. This is to prevent us from generating more
+ // convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds);
let lower_bounds = self.non_local_bounds(&self.outlives, fr);
// In case we find more than one, reduce to one for
- // convenience. This is to prevent us from generating more
+ // convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
- }
-
- OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
- self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
+ .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
}
}
debug_assert!(self.drop_live_at.contains(term_point));
// Otherwise, scan backwards through the statements in the
- // block. One of them may be either a definition or use
+ // block. One of them may be either a definition or use
// live point.
let term_location = self.cx.elements.to_location(term_point);
debug_assert_eq!(self.cx.body.terminator_loc(term_location.block), term_location,);
fn ensure_place_sized(&mut self, ty: Ty<'tcx>, span: Span) {
let tcx = self.tcx();
- // Erase the regions from `ty` to get a global type. The
+ // Erase the regions from `ty` to get a global type. The
// `Sized` bound in no way depends on precise regions, so this
// shouldn't affect `is_sized`.
let erased_ty = tcx.erase_regions(ty);
/// `ty::Region` to the internal `RegionVid` we are using. This is
/// used because trait matching and type-checking will feed us
/// region constraints that reference those regions and we need to
- /// be able to map them our internal `RegionVid`. This is
+ /// be able to map them to our internal `RegionVid`. This is
/// basically equivalent to an `InternalSubsts`, except that it also
/// contains an entry for `ReStatic` -- it might be nice to just
/// use a substs, and then handle `ReStatic` another way.
let closure_ty = tcx.closure_env_ty(def_id, substs, env_region).unwrap();
// The "inputs" of the closure in the
- // signature appear as a tuple. The MIR side
+ // signature appear as a tuple. The MIR side
// flattens this tuple.
let (&output, tuplized_inputs) =
inputs_and_output.skip_binder().split_last().unwrap();
// some additional `AssertParamIsClone` assertions.
//
// We can use the simple form if either of the following are true.
- // - The type derives Copy and there are no generic parameters. (If we
+ // - The type derives Copy and there are no generic parameters. (If we
// used the simple form with generics, we'd have to bound the generics
// with Clone + Copy, and then there'd be no Clone impl at all if the
// user fills in something that is Clone but not Copy. After
nonself_args: Vec::new(),
ret_ty: Self_,
attributes: attrs,
- unify_fieldless_variants: false,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: substructure,
}],
associated_types: Vec::new(),
all_fields = af;
vdata = &variant.data;
}
- EnumTag(..) => cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,)),
+ EnumTag(..) | AllFieldlessEnum(..) => {
+ cx.span_bug(trait_span, &format!("enum tags in `derive({})`", name,))
+ }
StaticEnum(..) | StaticStruct(..) => {
cx.span_bug(trait_span, &format!("associated function in `derive({})`", name))
}
nonself_args: vec![],
ret_ty: Unit,
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| {
cs_total_eq_assert(a, b, c)
})),
nonself_args: vec![(self_ref(), sym::other)],
ret_ty: Path(path_std!(cmp::Ordering)),
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_cmp(a, b, c))),
}],
associated_types: Vec::new(),
nonself_args: vec![(self_ref(), sym::other)],
ret_ty: Path(path_local!(bool)),
attributes: attrs,
- unify_fieldless_variants: true,
+ fieldless_variants_strategy: FieldlessVariantsStrategy::Unify,
combine_substructure: combine_substructure(Box::new(|a, b, c| cs_eq(a, b, c))),
}];
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)
})),
-// The compiler code necessary to support the env! extension. Eventually this
+// The compiler code necessary to support the env! extension. Eventually this
// should all get sucked into either the compiler syntax extension plugin
// interface.
//
if detect_foreign_fmt {
use super::format_foreign as foreign;
- // The set of foreign substitutions we've explained. This prevents spamming the user
+ // The set of foreign substitutions we've explained. This prevents spamming the user
// with `%d should be written as {}` over and over again.
let mut explained = FxHashSet::default();
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Num {
// The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
- // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
+ // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
// is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
// with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
// on a screen.
// Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141
// Declare an internal global `extern_with_linkage_foo` which
- // is initialized with the address of `foo`. If `foo` is
+ // is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
.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)
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};
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(
fx,
}
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(
cstr = "0.2"
libc = "0.2"
measureme = "10.0.0"
-object = { version = "0.29.0", default-features = false, features = ["std", "read"] }
+object = { version = "0.30.1", default-features = false, features = [
+ "std",
+ "read",
+] }
tracing = "0.1"
rustc_middle = { path = "../rustc_middle" }
rustc-demangle = "0.1.21"
bx.store(val, cast_dst, self.layout.align.abi);
} else {
// The actual return type is a struct, but the ABI
- // adaptation code has cast it into some scalar type. The
+ // adaptation code has cast it into some scalar type. The
// code that follows is the only reliable way I have
// found to do a transform like i64 -> {i32,i32}.
// Basically we dump the data onto the stack then memcpy it.
};
// Store mark in a metadata node so we can map LLVM errors
- // back to source locations. See #17552.
+ // back to source locations. See #17552.
let key = "srcloc";
let kind = llvm::LLVMGetMDKindIDInContext(
bx.llcx,
// The binutils linker used on -windows-gnu targets cannot read the import
// libraries generated by LLVM: in our attempts, the linker produced an .EXE
// that loaded but crashed with an AV upon calling one of the imported
- // functions. Therefore, use binutils to create the import library instead,
+ // functions. Therefore, use binutils to create the import library instead,
// by writing a .DEF file to the temp dir and calling binutils's dlltool.
let def_file_path =
tmpdir.join(format!("{}{}", lib_name, name_suffix)).with_extension("def");
// All import names are Rust identifiers and therefore cannot contain \0 characters.
// FIXME: when support for #[link_name] is implemented, ensure that the import names
- // still don't contain any \0 characters. Also need to check that the names don't
+ // still don't contain any \0 characters. Also need to check that the names don't
// contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
// in definition files.
let cstring_import_name_and_ordinal_vector: Vec<(CString, Option<u16>)> =
}
// The user didn't specify the location of the dlltool binary, and we weren't able
- // to find the appropriate one on the PATH. Just return the name of the tool
+ // to find the appropriate one on the PATH. Just return the name of the tool
// and let the invocation fail with a hopefully useful error message.
tool_name
}
// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
// This is required to satisfy `dllimport` references to static data in .rlibs
-// when using MSVC linker. We do this only for data, as linker can fix up
+// when using MSVC linker. We do this only for data, as linker can fix up
// code references on its own.
// See #26591, #27438
fn create_msvc_imps(
layout: TyAndLayout<'tcx>,
offset: Size,
) {
- if !scalar.is_always_valid(bx) {
+ if !scalar.is_uninit_valid() {
bx.noundef_metadata(load);
}
let llptrty = fn_abi.ptr_to_llvm_type(cx);
// This is subtle and surprising, but sometimes we have to bitcast
- // the resulting fn pointer. The reason has to do with external
- // functions. If you have two crates that both bind the same C
+ // the resulting fn pointer. The reason has to do with external
+ // functions. If you have two crates that both bind the same C
// library, they may not use precisely the same types: for
// example, they will probably each declare their own structs,
// which are distinct types from LLVM's point of view (nominal
fn set_global_alignment<'ll>(cx: &CodegenCx<'ll, '_>, gv: &'ll Value, mut align: Align) {
// The target may require greater alignment for globals than the type does.
// Note: GCC and Clang also allow `__attribute__((aligned))` on variables,
- // which can force it to be smaller. Rust doesn't support this yet.
+ // which can force it to be smaller. Rust doesn't support this yet.
if let Some(min) = cx.sess().target.min_global_align {
match Align::from_bits(min) {
Ok(min) => align = align.max(min),
llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage));
// Declare an internal global `extern_with_linkage_foo` which
- // is initialized with the address of `foo`. If `foo` is
+ // is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
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| {
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
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> {
// Type indicator for the exception being thrown.
//
// The first value in this tuple is a pointer to the exception object
- // being thrown. The second value is a "selector" indicating which of
+ // being thrown. The second value is a "selector" indicating which of
// the landing pad clauses the exception's type had been matched to.
// rust_try ignores the selector.
bx.switch_to_block(catch);
// Type indicator for the exception being thrown.
//
// The first value in this tuple is a pointer to the exception object
- // being thrown. The second value is a "selector" indicating which of
+ // being thrown. The second value is a "selector" indicating which of
// the landing pad clauses the exception's type had been matched to.
bx.switch_to_block(catch);
let tydesc = bx.eh_catch_typeinfo();
let scalar = [a, b][index];
// Make sure to return the same type `immediate_llvm_type` would when
- // dealing with an immediate pair. This means that `(bool, bool)` is
+ // dealing with an immediate pair. This means that `(bool, bool)` is
// effectively represented as `{i8, i8}` in memory and two `i1`s as an
// immediate, just like `bool` is typically `i8` in memory and only `i1`
- // when immediate. We need to load/store `bool` as `i8` to avoid
+ // when immediate. We need to load/store `bool` as `i8` to avoid
// crippling LLVM optimizations or triggering other LLVM bugs with `i1`.
if immediate && scalar.is_bool() {
return cx.type_i1();
libc = "0.2.50"
jobserver = "0.1.22"
tempfile = "3.2"
-thorin-dwp = "0.3"
+thorin-dwp = "0.4"
pathdiff = "0.2.0"
serde_json = "1.0.59"
snap = "1"
rustc_const_eval = { path = "../rustc_const_eval" }
[dependencies.object]
-version = "0.29.0"
+version = "0.30.1"
default-features = false
features = ["read_core", "elf", "macho", "pe", "unaligned", "archive", "write"]
/// Extract all symbols defined in raw-dylib libraries, collated by library name.
///
/// If we have multiple extern blocks that specify symbols defined in the same raw-dylib library,
-/// then the CodegenResults value contains one NativeLib instance for each block. However, the
+/// then the CodegenResults value contains one NativeLib instance for each block. However, the
/// linker appears to expect only a single import library for each library used, so we need to
/// collate the symbols together by library name before generating the import libraries.
fn collate_raw_dylibs<'a, 'b>(
if cfg!(any(target_os = "solaris", target_os = "illumos")) {
// On historical Solaris systems, "cc" may have
// been Sun Studio, which is not flag-compatible
- // with "gcc". This history casts a long shadow,
+ // with "gcc". This history casts a long shadow,
// and many modern illumos distributions today
// ship GCC as "gcc" without also making it
// available as "cc".
sess.emit_fatal(errors::LinkerFileStem);
});
+ // Remove any version postfix.
+ let stem = stem
+ .rsplit_once('-')
+ .and_then(|(lhs, rhs)| rhs.chars().all(char::is_numeric).then_some(lhs))
+ .unwrap_or(stem);
+
+ // GCC can have an optional target prefix.
let flavor = if stem == "emcc" {
LinkerFlavor::EmCc
} else if stem == "gcc"
|| stem.ends_with("-gcc")
+ || stem == "g++"
+ || stem.ends_with("-g++")
|| stem == "clang"
- || stem.ends_with("-clang")
+ || stem == "clang++"
{
LinkerFlavor::from_cli(LinkerFlavorCli::Gcc, &sess.target)
} else if stem == "wasm-ld" || stem.ends_with("-wasm-ld") {
// link times negatively.
//
// -dead_strip can't be part of the pre_link_args because it's also used
- // for partial linking when using multiple codegen units (-r). So we
+ // for partial linking when using multiple codegen units (-r). So we
// insert it here.
if self.sess.target.is_like_osx {
self.linker_arg("-dead_strip");
};
let architecture = match &sess.target.arch[..] {
"arm" => Architecture::Arm,
- "aarch64" => Architecture::Aarch64,
+ "aarch64" => {
+ if sess.target.pointer_width == 32 {
+ Architecture::Aarch64_Ilp32
+ } else {
+ Architecture::Aarch64
+ }
+ }
"x86" => Architecture::I386,
"s390x" => Architecture::S390x,
"mips" => Architecture::Mips,
};
e_flags
}
- Architecture::Riscv64 if sess.target.options.features.contains("+d") => {
- // copied from `riscv64-linux-gnu-gcc foo.c -c`, note though
- // that the `+d` target feature represents whether the double
- // float abi is enabled.
- let e_flags = elf::EF_RISCV_RVC | elf::EF_RISCV_FLOAT_ABI_DOUBLE;
+ Architecture::Riscv32 | Architecture::Riscv64 => {
+ // Source: https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/079772828bd10933d34121117a222b4cc0ee2200/riscv-elf.adoc
+ let mut e_flags: u32 = 0x0;
+ let features = &sess.target.options.features;
+ // Check if compressed is enabled
+ if features.contains("+c") {
+ e_flags |= elf::EF_RISCV_RVC;
+ }
+
+ // Select the appropriate floating-point ABI
+ if features.contains("+d") {
+ e_flags |= elf::EF_RISCV_FLOAT_ABI_DOUBLE;
+ } else if features.contains("+f") {
+ e_flags |= elf::EF_RISCV_FLOAT_ABI_SINGLE;
+ } else {
+ e_flags |= elf::EF_RISCV_FLOAT_ABI_SOFT;
+ }
e_flags
}
_ => 0,
return &[];
}
- let mut symbols: Vec<_> = tcx
- .reachable_non_generics(LOCAL_CRATE)
- .iter()
- .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
- .collect();
+ // FIXME: Sorting this is unnecessary since we are sorting later anyway.
+ // Can we skip the later sorting?
+ let mut symbols: Vec<_> = tcx.with_stable_hashing_context(|hcx| {
+ tcx.reachable_non_generics(LOCAL_CRATE)
+ .to_sorted(&hcx, true)
+ .into_iter()
+ .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
+ .collect()
+ });
if tcx.entry_fn(()).is_some() {
let exported_symbol =
pub emit_thin_lto: bool,
pub bc_cmdline: String,
- // Miscellaneous flags. These are mostly copied from command-line
+ // Miscellaneous flags. These are mostly copied from command-line
// options.
pub verify_llvm_ir: bool,
pub no_prepopulate_passes: bool,
let copy_if_one_unit = |output_type: OutputType, keep_numbered: bool| {
if compiled_modules.modules.len() == 1 {
- // 1) Only one codegen unit. In this case it's no difficulty
+ // 1) Only one codegen unit. In this case it's no difficulty
// to copy `foo.0.x` to `foo.x`.
let module_name = Some(&compiled_modules.modules[0].name[..]);
let path = crate_output.temp_path(output_type, module_name);
.to_owned();
if crate_output.outputs.contains_key(&output_type) {
- // 2) Multiple codegen units, with `--emit foo=some_name`. We have
+ // 2) Multiple codegen units, with `--emit foo=some_name`. We have
// no good solution for this case, so warn the user.
sess.emit_warning(errors::IgnoringEmitPath { extension });
} else if crate_output.single_output_file.is_some() {
- // 3) Multiple codegen units, with `-o some_name`. We have
+ // 3) Multiple codegen units, with `-o some_name`. We have
// no good solution for this case, so warn the user.
sess.emit_warning(errors::IgnoringOutput { extension });
} else {
- // 4) Multiple codegen units, but no explicit name. We
+ // 4) Multiple codegen units, but no explicit name. We
// just leave the `foo.0.x` files in place.
// (We don't have to do any work in this case.)
}
match *output_type {
OutputType::Bitcode => {
user_wants_bitcode = true;
- // Copy to .bc, but always keep the .0.bc. There is a later
+ // Copy to .bc, but always keep the .0.bc. There is a later
// check to figure out if we should delete .0.bc files, or keep
// them for making an rlib.
copy_if_one_unit(OutputType::Bitcode, true);
// `-C save-temps` or `--emit=` flags).
if !sess.opts.cg.save_temps {
- // Remove the temporary .#module-name#.o objects. If the user didn't
+ // Remove the temporary .#module-name#.o objects. If the user didn't
// explicitly request bitcode (with --emit=bc), and the bitcode is not
// needed for building an rlib, then we must remove .#module-name#.bc as
// well.
};
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
};
}
sole_meta_list
{
// According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
- // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
+ // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
// in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
// to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
//
// FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
// both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
- // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
+ // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
// for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
// library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
// if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
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!({
}
impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
- /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
+ /// DILocations inherit source file name from the parent DIScope. Due to macro expansions
/// it may so happen that the current span belongs to a different file than the DIScope
- /// corresponding to span's containing source scope. If so, we need to create a DIScope
+ /// corresponding to span's containing source scope. If so, we need to create a DIScope
/// "extension" into that file.
pub fn adjust_dbg_scope_for_span<Cx: CodegenMethods<'tcx, DIScope = S, DILocation = L>>(
&self,
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 is_const { hir::Constness::Const } else { hir::Constness::NotConst }
}
+ hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness,
_ => {
if let Some(fn_kind) = node.fn_kind() {
if fn_kind.constness() == hir::Constness::Const {
/// `align_offset(ptr, target_align)` needs special handling in const eval, because the pointer
/// may not have an address.
///
- /// If `ptr` does have a known address, then we return `CONTINUE` and the function call should
+ /// If `ptr` does have a known address, then we return `Continue(())` and the function call should
/// proceed as normal.
///
/// If `ptr` doesn't have an address, but its underlying allocation's alignment is at most
ret,
StackPopUnwind::NotAllowed,
)?;
- Ok(ControlFlow::BREAK)
+ Ok(ControlFlow::Break(()))
} else {
// Not alignable in const, return `usize::MAX`.
let usize_max = Scalar::from_machine_usize(self.machine_usize_max(), self);
self.write_scalar(usize_max, dest)?;
self.return_to_block(ret)?;
- Ok(ControlFlow::BREAK)
+ Ok(ControlFlow::Break(()))
}
}
Err(_addr) => {
// The pointer has an address, continue with function call.
- Ok(ControlFlow::CONTINUE)
+ Ok(ControlFlow::Continue(()))
}
}
}
// Only check non-glue functions
if let ty::InstanceDef::Item(def) = instance.def {
// Execution might have wandered off into other crates, so we cannot do a stability-
- // sensitive check here. But we can at least rule out functions that are not const
+ // sensitive check here. But we can at least rule out functions that are not const
// at all.
if !ecx.tcx.is_const_fn_raw(def.did) {
// allow calling functions inside a trait marked with #[const_trait].
let eval_to_int =
|op| ecx.read_immediate(&ecx.eval_operand(op, None)?).map(|x| x.to_const_int());
let err = match msg {
- BoundsCheck { ref len, ref index } => {
+ BoundsCheck { len, index } => {
let len = eval_to_int(len)?;
let index = eval_to_int(index)?;
BoundsCheck { len, index }
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
}
- (_, &ty::Dynamic(ref data, _, ty::Dyn)) => {
+ (_, &ty::Dynamic(data, _, ty::Dyn)) => {
// Initial cast from sized to dyn trait
let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
let ptr = self.read_scalar(src)?;
}
}
- /// Overwrite the local. If the local can be overwritten in place, return a reference
+ /// Overwrite the local. If the local can be overwritten in place, return a reference
/// to do so; otherwise return the `MemPlace` to consult instead.
///
/// Note: This may only be invoked from the `Machine::access_local_mut` hook and not from
);
// Recurse to get the size of the dynamically sized field (must be
- // the last field). Can't have foreign types here, how would we
+ // the last field). Can't have foreign types here, how would we
// adjust alignment and size for them?
let field = layout.field(self, layout.fields.count() - 1);
let Some((unsized_size, mut unsized_align)) = self.size_and_align_of(metadata, &field)? else {
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)]
enum InternMode {
- /// A static and its current mutability. Below shared references inside a `static mut`,
+ /// A static and its current mutability. Below shared references inside a `static mut`,
/// this is *immutable*, and below mutable references inside an `UnsafeCell`, this
/// is *mutable*.
Static(hir::Mutability),
}
}
InternMode::Const => {
- // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity
+ // Ignore `UnsafeCell`, everything is immutable. Validity does some sanity
// checking for mutable references that we encounter -- they must all be
// ZST.
InternMode::Const
/// Intern `ret` and everything it references.
///
-/// This *cannot raise an interpreter error*. Doing so is left to validation, which
+/// This *cannot raise an interpreter error*. Doing so is left to validation, which
/// tracks where in the value we are and thus can show much better error messages.
#[instrument(level = "debug", skip(ecx))]
pub fn intern_const_alloc_recursive<
inside_unsafe_cell: false,
}
.visit_value(&mplace);
- // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining
+ // We deliberately *ignore* interpreter errors here. When there is a problem, the remaining
// references are "leftover"-interned, and later validation will show a proper error
// and point at the right part of the value causing the problem.
match res {
return Err(reported);
} else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
// We have hit an `AllocId` that is neither in local or global memory and isn't
- // marked as dangling by local memory. That should be impossible.
+ // marked as dangling by local memory. That should be impossible.
span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);
}
}
}
sym::variant_count => match tp_ty.kind() {
// Correctly handles non-monomorphic calls, so there is no need for ensure_monomorphic_enough.
- ty::Adt(ref adt, _) => {
- ConstValue::from_machine_usize(adt.variants().len() as u64, &tcx)
- }
+ ty::Adt(adt, _) => ConstValue::from_machine_usize(adt.variants().len() as u64, &tcx),
ty::Alias(..) | ty::Param(_) | ty::Placeholder(_) | ty::Infer(_) => {
throw_inval!(TooGeneric)
}
}
if intrinsic_name == sym::assert_zero_valid {
- let should_panic = !self.tcx.permits_zero_init(layout);
+ let should_panic = !self.tcx.permits_zero_init(self.param_env.and(layout));
if should_panic {
M::abort(
}
if intrinsic_name == sym::assert_mem_uninitialized_valid {
- let should_panic = !self.tcx.permits_uninit_init(layout);
+ let should_panic = !self.tcx.permits_uninit_init(self.param_env.and(layout));
if should_panic {
M::abort(
unwind: StackPopUnwind,
) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>>;
- /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
+ /// Execute `fn_val`. It is the hook's responsibility to advance the instruction
/// pointer as appropriate.
fn call_extra_fn(
ecx: &mut InterpCx<'mir, 'tcx, Self>,
}
/// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines
-/// (CTFE and ConstProp) use the same instance. Here, we share that code.
+/// (CTFE and ConstProp) use the same instance. Here, we share that code.
pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
type Provenance = AllocId;
type ProvenanceExtra = ();
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
- /// the machine pointer to the allocation. Must never be used
+ /// the machine pointer to the allocation. Must never be used
/// for any other pointers, nor for TLS statics.
///
/// Using the resulting pointer represents a *direct* access to that memory
&self,
id: AllocId,
) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> {
- // The error type of the inner closure here is somewhat funny. We have two
+ // The error type of the inner closure here is somewhat funny. We have two
// ways of "erroring": An actual error, or because we got a reference from
// `get_global_alloc` that we can actually use directly without inserting anything anywhere.
// So the error type is `InterpResult<'tcx, &Allocation<M::Provenance>>`.
write!(fmt, "{id:?}")?;
match self.ecx.memory.alloc_map.get(id) {
- Some(&(kind, ref alloc)) => {
+ Some((kind, alloc)) => {
// normal alloc
write!(fmt, " ({}, ", kind)?;
write_allocation_track_relocs(
Ok(OpTy { op, layout: place.layout, align: Some(place.align) })
}
- /// Evaluate a place with the goal of reading from it. This lets us sometimes
+ /// Evaluate a place with the goal of reading from it. This lets us sometimes
/// avoid allocations.
pub fn eval_place_to_op(
&self,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> {
use rustc_middle::mir::Operand::*;
- let op = match *mir_op {
+ let op = match mir_op {
// FIXME: do some more logic on `move` to invalidate the old location
- Copy(place) | Move(place) => self.eval_place_to_op(place, layout)?,
+ &Copy(place) | &Move(place) => self.eval_place_to_op(place, layout)?,
- Constant(ref constant) => {
+ Constant(constant) => {
let c =
self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
_ => bug!("len not supported on unsized type {:?}", self.layout.ty),
}
} else {
- // Go through the layout. There are lots of types that support a length,
+ // Go through the layout. There are lots of types that support a length,
// e.g., SIMD types. (But not all repr(simd) types even have FieldsShape::Array!)
match self.layout.fields {
abi::FieldsShape::Array { count, .. } => Ok(count),
M: Machine<'mir, 'tcx, Provenance = Prov>,
{
/// Take a value, which represents a (thin or wide) reference, and make it a place.
- /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
+ /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`.
///
/// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not
/// want to ever use the place for memory access!
&mut Operand::Immediate(local_val) => {
// We need to make an allocation.
- // We need the layout of the local. We can NOT use the layout we got,
+ // We need the layout of the local. We can NOT use the layout we got,
// that might e.g., be an inner field of a struct with `Scalar` layout,
// that has different alignment than the outer field.
let local_layout =
M::retag_place_contents(self, *kind, &dest)?;
}
- Intrinsic(box ref intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
+ Intrinsic(box intrinsic) => self.emulate_nondiverging_intrinsic(intrinsic)?,
// Statements we do not track.
AscribeUserType(..) => {}
self.copy_op(&op, &dest, /*allow_transmute*/ false)?;
}
- CopyForDeref(ref place) => {
- let op = self.eval_place_to_op(*place, Some(dest.layout))?;
+ CopyForDeref(place) => {
+ let op = self.eval_place_to_op(place, Some(dest.layout))?;
self.copy_op(&op, &dest, /* allow_transmute*/ false)?;
}
// they go to.
// For where they come from: If the ABI is RustCall, we untuple the
- // last incoming argument. These two iterators do not have the same type,
+ // last incoming argument. These two iterators do not have the same type,
// so to keep the code paths uniform we accept an allocation
// (for RustCall ABI only).
let caller_args: Cow<'_, [OpTy<'tcx, M::Provenance>]> =
.filter(|arg_and_abi| !matches!(arg_and_abi.1.mode, PassMode::Ignore));
// Now we have to spread them out across the callee's locals,
- // taking into account the `spread_arg`. If we could write
+ // taking into account the `spread_arg`. If we could write
// this is a single iterator (that handles `spread_arg`), then
// `pass_argument` would be the loop body. It takes care to
// not advance `caller_iter` for ZSTs.
unwind: Option<mir::BasicBlock>,
) -> InterpResult<'tcx> {
trace!("drop_in_place: {:?},\n {:?}, {:?}", *place, place.layout.ty, instance);
- // We take the address of the object. This may well be unaligned, which is fine
- // for us here. However, unaligned accesses will probably make the actual drop
+ // We take the address of the object. This may well be unaligned, which is fine
+ // for us here. However, unaligned accesses will probably make the actual drop
// implementation fail -- a problem shared by rustc.
let place = self.force_allocation(place)?;
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.needs_subst() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match *ty.kind() {
return subst.visit_with(self);
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => ty.super_visit_with(self),
}
TupleElem(idx) => write!(out, ".{}", idx),
ArrayElem(idx) => write!(out, "[{}]", idx),
// `.<deref>` does not match Rust syntax, but it is more readable for long paths -- and
- // some of the other items here also are not Rust syntax. Actually we can't
+ // some of the other items here also are not Rust syntax. Actually we can't
// even use the usual syntax because we are just showing the projections,
// not the root.
Deref => write!(out, ".<deref>"),
)
}
// Recursive checking
- if let Some(ref mut ref_tracking) = self.ref_tracking {
+ if let Some(ref_tracking) = self.ref_tracking.as_deref_mut() {
// Proceed recursively even for ZST, no reason to skip them!
// `!` is a ZST and we want to validate it.
if let Ok((alloc_id, _offset, _prov)) = self.ecx.ptr_try_get_alloc_id(place.ptr) {
}
/// Check if this is a value of primitive type, and if yes check the validity of the value
- /// at that type. Return `true` if the type is indeed primitive.
+ /// at that type. Return `true` if the type is indeed primitive.
fn try_visit_primitive(
&mut self,
value: &OpTy<'tcx, M::Provenance>,
// Can only happen during CTFE.
// We support 2 kinds of ranges here: full range, and excluding zero.
if start == 1 && end == max_value {
- // Only null is the niche. So make sure the ptr is NOT null.
+ // Only null is the niche. So make sure the ptr is NOT null.
if self.ecx.scalar_may_be_null(scalar)? {
throw_validation_failure!(self.path,
{ "a potentially null pointer" }
// Recursively walk the value at its type.
self.walk_value(op)?;
- // *After* all of this, check the ABI. We need to check the ABI to handle
+ // *After* all of this, check the ABI. We need to check the ABI to handle
// types like `NonNull` where the `Scalar` info is more restrictive than what
// the fields say (`rustc_layout_scalar_valid_range_start`).
// But in most cases, this will just propagate what the fields say,
// Optimization: we just check the entire range at once.
// NOTE: Keep this in sync with the handling of integer and float
// types above, in `visit_primitive`.
- // In run-time mode, we accept pointers in here. This is actually more
+ // In run-time mode, we accept pointers in here. This is actually more
// permissive than a per-element check would be, e.g., we accept
// a &[u8] that contains a pointer even though bytewise checking would
- // reject it. However, that's good: We don't inherently want
+ // reject it. However, that's good: We don't inherently want
// to reject those pointers, we just do not have the machinery to
// talk about parts of a pointer.
// We also accept uninit, for consistency with the slow path.
};
// Visit the fields of this value.
- match v.layout().fields {
+ match &v.layout().fields {
FieldsShape::Primitive => {}
- FieldsShape::Union(fields) => {
+ &FieldsShape::Union(fields) => {
self.visit_union(v, fields)?;
}
- FieldsShape::Arbitrary { ref offsets, .. } => {
+ FieldsShape::Arbitrary { offsets, .. } => {
// FIXME: We collect in a vec because otherwise there are lifetime
// errors: Projecting to a field needs access to `ecx`.
let fields: Vec<InterpResult<'tcx, Self::V>> =
#![feature(assert_matches)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(decl_macro)]
#![feature(exact_size_is_empty)]
#![feature(let_chains)]
#![feature(trusted_step)]
#![feature(try_blocks)]
#![feature(yeet_expr)]
+#![feature(if_let_guard)]
#![feature(is_some_and)]
#![recursion_limit = "256"]
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)
+ };
}
// impl trait is gone in MIR, so check the return type of a const fn by its signature
// instead of the type of the return place.
self.span = body.local_decls[RETURN_PLACE].source_info.span;
- let return_ty = tcx.fn_sig(def_id).output();
+ let return_ty = self.ccx.fn_sig().output();
self.check_local_or_return_ty(return_ty.skip_binder(), RETURN_PLACE);
}
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);
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: Some(sym::const_trait_impl),
});
return;
}
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);
);
return;
}
+ Ok(Some(ImplSource::Closure(data))) => {
+ if !tcx.is_const_fn_raw(data.closure_def_id) {
+ self.check_op(ops::FnCallNonConst {
+ caller,
+ callee,
+ substs,
+ span: *fn_span,
+ from_hir_call: *from_hir_call,
+ feature: None,
+ });
+
+ return;
+ }
+ }
Ok(Some(ImplSource::UserDefined(data))) => {
let callee_name = tcx.item_name(callee);
if let Some(&did) = tcx
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: None,
});
return;
}
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: None,
});
return;
}
substs,
span: *fn_span,
from_hir_call: *from_hir_call,
+ feature: None,
});
return;
}
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::mir;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, PolyFnSig, TyCtxt};
use rustc_span::Symbol;
pub use self::qualifs::Qualif;
fn is_async(&self) -> bool {
self.tcx.asyncness(self.def_id()).is_async()
}
+
+ pub fn fn_sig(&self) -> PolyFnSig<'tcx> {
+ let did = self.def_id().to_def_id();
+ if self.tcx.is_closure(did) {
+ let ty = self.tcx.type_of(did);
+ let ty::Closure(_, substs) = ty.kind() else { bug!("type_of closure not ty::Closure") };
+ substs.as_closure().sig()
+ } else {
+ self.tcx.fn_sig(did)
+ }
+ }
}
pub fn rustc_allow_const_fn_unstable(
pub substs: SubstsRef<'tcx>,
pub span: Span,
pub from_hir_call: bool,
+ pub feature: Option<Symbol>,
}
impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
ccx: &ConstCx<'_, 'tcx>,
_: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self;
+ let FnCallNonConst { caller, callee, substs, span, from_hir_call, feature } = *self;
let ConstCx { tcx, param_env, .. } = *ccx;
let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
ccx.const_kind(),
));
+ if let Some(feature) = feature && ccx.tcx.sess.is_nightly_build() {
+ err.help(&format!(
+ "add `#![feature({})]` to the crate attributes to enable",
+ feature,
+ ));
+ }
+
if let ConstContext::Static(_) = ccx.const_kind() {
err.note("consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell");
}
}
_ => { /* mark as unpromotable below */ }
}
- } else if let TempState::Defined { ref mut uses, .. } = *temp {
+ } else if let TempState::Defined { uses, .. } = temp {
// We always allow borrows, even mutable ones, as we need
// to promote mutable borrows of some ZSTs e.g., `&mut []`.
let allowed_use = match context {
if loc.statement_index < num_stmts {
let (mut rvalue, source_info) = {
let statement = &mut self.source[loc.block].statements[loc.statement_index];
- let StatementKind::Assign(box (_, ref mut rhs)) = statement.kind else {
+ let StatementKind::Assign(box (_, rhs)) = &mut statement.kind else {
span_bug!(
statement.source_info.span,
"{:?} is not an assignment",
self.source[loc.block].terminator().clone()
} else {
let terminator = self.source[loc.block].terminator_mut();
- let target = match terminator.kind {
- TerminatorKind::Call { target: Some(target), .. } => target,
- ref kind => {
+ let target = match &terminator.kind {
+ TerminatorKind::Call { target: Some(target), .. } => *target,
+ kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
..terminator
};
}
- ref kind => {
+ kind => {
span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
}
};
let local_decls = &mut self.source.local_decls;
let loc = candidate.location;
let statement = &mut blocks[loc.block].statements[loc.statement_index];
- match statement.kind {
- StatementKind::Assign(box (
- _,
- Rvalue::Ref(ref mut region, borrow_kind, ref mut place),
- )) => {
- // Use the underlying local for this (necessarily interior) borrow.
- let ty = local_decls[place.local].ty;
- let span = statement.source_info.span;
-
- let ref_ty = tcx.mk_ref(
- tcx.lifetimes.re_erased,
- ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
- );
+ let StatementKind::Assign(box (_, Rvalue::Ref(region, borrow_kind, place))) = &mut statement.kind else {
+ bug!()
+ };
- *region = tcx.lifetimes.re_erased;
-
- let mut projection = vec![PlaceElem::Deref];
- projection.extend(place.projection);
- place.projection = tcx.intern_place_elems(&projection);
-
- // Create a temp to hold the promoted reference.
- // This is because `*r` requires `r` to be a local,
- // otherwise we would use the `promoted` directly.
- let mut promoted_ref = LocalDecl::new(ref_ty, span);
- promoted_ref.source_info = statement.source_info;
- let promoted_ref = local_decls.push(promoted_ref);
- assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
-
- let promoted_ref_statement = Statement {
- source_info: statement.source_info,
- kind: StatementKind::Assign(Box::new((
- Place::from(promoted_ref),
- Rvalue::Use(promoted_operand(ref_ty, span)),
- ))),
- };
- self.extra_statements.push((loc, promoted_ref_statement));
-
- Rvalue::Ref(
- tcx.lifetimes.re_erased,
- borrow_kind,
- Place {
- local: mem::replace(&mut place.local, promoted_ref),
- projection: List::empty(),
- },
- )
- }
- _ => bug!(),
- }
+ // Use the underlying local for this (necessarily interior) borrow.
+ let ty = local_decls[place.local].ty;
+ let span = statement.source_info.span;
+
+ let ref_ty = tcx.mk_ref(
+ tcx.lifetimes.re_erased,
+ ty::TypeAndMut { ty, mutbl: borrow_kind.to_mutbl_lossy() },
+ );
+
+ *region = tcx.lifetimes.re_erased;
+
+ let mut projection = vec![PlaceElem::Deref];
+ projection.extend(place.projection);
+ place.projection = tcx.intern_place_elems(&projection);
+
+ // Create a temp to hold the promoted reference.
+ // This is because `*r` requires `r` to be a local,
+ // otherwise we would use the `promoted` directly.
+ let mut promoted_ref = LocalDecl::new(ref_ty, span);
+ promoted_ref.source_info = statement.source_info;
+ let promoted_ref = local_decls.push(promoted_ref);
+ assert_eq!(self.temps.push(TempState::Unpromotable), promoted_ref);
+
+ let promoted_ref_statement = Statement {
+ source_info: statement.source_info,
+ kind: StatementKind::Assign(Box::new((
+ Place::from(promoted_ref),
+ Rvalue::Use(promoted_operand(ref_ty, span)),
+ ))),
+ };
+ self.extra_statements.push((loc, promoted_ref_statement));
+
+ Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ *borrow_kind,
+ Place {
+ local: mem::replace(&mut place.local, promoted_ref),
+ projection: List::empty(),
+ },
+ )
};
assert_eq!(self.new_block(), START_BLOCK);
//! Validates the MIR to ensure that invariants are upheld.
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_index::bit_set::BitSet;
+use rustc_index::vec::IndexVec;
use rustc_infer::traits::Reveal;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_mir_dataflow::{Analysis, ResultsCursor};
use rustc_target::abi::{Size, VariantIdx};
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum EdgeKind {
Unwind,
Normal,
.iterate_to_fixpoint()
.into_results_cursor(body);
- TypeChecker {
+ let mut checker = TypeChecker {
when: &self.when,
body,
tcx,
param_env,
mir_phase,
+ unwind_edge_count: 0,
reachable_blocks: traversal::reachable_as_bitset(body),
storage_liveness,
place_cache: Vec::new(),
value_cache: Vec::new(),
- }
- .visit_body(body);
+ };
+ checker.visit_body(body);
+ checker.check_cleanup_control_flow();
}
}
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
mir_phase: MirPhase,
+ unwind_edge_count: usize,
reachable_blocks: BitSet<BasicBlock>,
storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive<'static>>,
place_cache: Vec<PlaceRef<'tcx>>,
);
}
- fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
+ fn check_edge(&mut self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
if bb == START_BLOCK {
self.fail(location, "start block must not have predecessors")
}
match (src.is_cleanup, bb.is_cleanup, edge_kind) {
// Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
(false, false, EdgeKind::Normal)
- // Non-cleanup blocks can jump to cleanup blocks along unwind edges
- | (false, true, EdgeKind::Unwind)
// Cleanup blocks can jump to cleanup blocks along non-unwind edges
| (true, true, EdgeKind::Normal) => {}
+ // Non-cleanup blocks can jump to cleanup blocks along unwind edges
+ (false, true, EdgeKind::Unwind) => {
+ self.unwind_edge_count += 1;
+ }
// All other jumps are invalid
_ => {
self.fail(
}
}
+ fn check_cleanup_control_flow(&self) {
+ if self.unwind_edge_count <= 1 {
+ return;
+ }
+ let doms = self.body.basic_blocks.dominators();
+ let mut post_contract_node = FxHashMap::default();
+ // Reusing the allocation across invocations of the closure
+ let mut dom_path = vec![];
+ let mut get_post_contract_node = |mut bb| {
+ let root = loop {
+ if let Some(root) = post_contract_node.get(&bb) {
+ break *root;
+ }
+ let parent = doms.immediate_dominator(bb);
+ dom_path.push(bb);
+ if !self.body.basic_blocks[parent].is_cleanup {
+ break bb;
+ }
+ bb = parent;
+ };
+ for bb in dom_path.drain(..) {
+ post_contract_node.insert(bb, root);
+ }
+ root
+ };
+
+ let mut parent = IndexVec::from_elem(None, &self.body.basic_blocks);
+ for (bb, bb_data) in self.body.basic_blocks.iter_enumerated() {
+ if !bb_data.is_cleanup || !self.reachable_blocks.contains(bb) {
+ continue;
+ }
+ let bb = get_post_contract_node(bb);
+ for s in bb_data.terminator().successors() {
+ let s = get_post_contract_node(s);
+ if s == bb {
+ continue;
+ }
+ let parent = &mut parent[bb];
+ match parent {
+ None => {
+ *parent = Some(s);
+ }
+ Some(e) if *e == s => (),
+ Some(e) => self.fail(
+ Location { block: bb, statement_index: 0 },
+ format!(
+ "Cleanup control flow violation: The blocks dominated by {:?} have edges to both {:?} and {:?}",
+ bb,
+ s,
+ *e
+ )
+ ),
+ }
+ }
+ }
+
+ // Check for cycles
+ let mut stack = FxHashSet::default();
+ for i in 0..parent.len() {
+ let mut bb = BasicBlock::from_usize(i);
+ stack.clear();
+ stack.insert(bb);
+ loop {
+ let Some(parent)= parent[bb].take() else {
+ break
+ };
+ let no_cycle = stack.insert(parent);
+ if !no_cycle {
+ self.fail(
+ Location { block: bb, statement_index: 0 },
+ format!(
+ "Cleanup control flow violation: Cycle involving edge {:?} -> {:?}",
+ bb, parent,
+ ),
+ );
+ break;
+ }
+ bb = parent;
+ }
+ }
+ }
+
/// Check if src can be assigned into dest.
/// This is not precise, it will accept some incorrect assignments.
fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
/// 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)
}
}
//! ```
//!
//! `Frozen` impls `Deref`, so we can ergonomically call methods on `Bar`, but it doesn't `impl
-//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that
+//! DerefMut`. Now calling `foo.compute.mutate()` will result in a compile-time error stating that
//! `mutate` requires a mutable reference but we don't have one.
//!
//! # Caveats
#[macro_export]
macro_rules! define_id_collections {
($map_name:ident, $set_name:ident, $entry_name:ident, $key:ty) => {
- pub type $map_name<T> = $crate::fx::FxHashMap<$key, T>;
- pub type $set_name = $crate::fx::FxHashSet<$key>;
+ pub type $map_name<T> = $crate::unord::UnordMap<$key, T>;
+ pub type $set_name = $crate::unord::UnordSet<$key>;
pub type $entry_name<'a, T> = $crate::fx::StdEntry<'a, $key, T>;
};
}
// This loop computes the semi[w] for w.
semi[w] = w;
for v in graph.predecessors(pre_order_to_real[w]) {
- let v = real_to_pre_order[v].unwrap();
+ // Reachable vertices may have unreachable predecessors, so ignore any of them
+ let Some(v) = real_to_pre_order[v] else {
+ continue
+ };
// eval returns a vertex x from which semi[x] is minimum among
// vertices semi[v] +> x *> v.
}
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(()),
}
}
}
// 0 -> 1 -> 2 -> 1
//
// and at this point detect a cycle. The state of 2 will thus be
- // `InCycleWith { 1 }`. We will then visit the 1 -> 3 edge, which
+ // `InCycleWith { 1 }`. We will then visit the 1 -> 3 edge, which
// will attempt to visit 0 as well, thus going to the state
// `InCycleWith { 0 }`. Finally, node 1 will complete; the lowest
// depth of any successor was 3 which had depth 0, and thus it
#![feature(associated_type_bounds)]
#![feature(auto_traits)]
#![feature(cell_leak)]
-#![feature(control_flow_enum)]
#![feature(extend_one)]
#![feature(hash_raw_entry)]
#![feature(hasher_prefixfree_extras)]
use crate::stable_hasher::{HashStable, StableHasher, StableOrd};
use std::borrow::Borrow;
-use std::cmp::Ordering;
use std::fmt::Debug;
use std::mem;
use std::ops::{Bound, Index, IndexMut, RangeBounds};
where
F: Fn(&mut K),
{
- self.data.iter_mut().map(|&mut (ref mut k, _)| k).for_each(f);
+ self.data.iter_mut().map(|(k, _)| k).for_each(f);
}
/// Inserts a presorted range of elements into the map. If the range can be
R: RangeBounds<K>,
{
let start = match range.start_bound() {
- Bound::Included(ref k) => match self.lookup_index_for(k) {
+ Bound::Included(k) => match self.lookup_index_for(k) {
Ok(index) | Err(index) => index,
},
- Bound::Excluded(ref k) => match self.lookup_index_for(k) {
+ Bound::Excluded(k) => match self.lookup_index_for(k) {
Ok(index) => index + 1,
Err(index) => index,
},
};
let end = match range.end_bound() {
- Bound::Included(ref k) => match self.lookup_index_for(k) {
+ Bound::Included(k) => match self.lookup_index_for(k) {
Ok(index) => index + 1,
Err(index) => index,
},
- Bound::Excluded(ref k) => match self.lookup_index_for(k) {
+ Bound::Excluded(k) => match self.lookup_index_for(k) {
Ok(index) | Err(index) => index,
},
Bound::Unbounded => self.data.len(),
let mut data: Vec<(K, V)> = iter.into_iter().collect();
data.sort_unstable_by(|(k1, _), (k2, _)| k1.cmp(k2));
- data.dedup_by(|&mut (ref k1, _), &mut (ref k2, _)| k1.cmp(k2) == Ordering::Equal);
+ data.dedup_by(|(k1, _), (k2, _)| k1 == k2);
SortedMap { data }
}
/// Returns an iterator over the items in the map in insertion order.
#[inline]
pub fn iter(&self) -> impl '_ + DoubleEndedIterator<Item = (&K, &V)> {
- self.items.iter().map(|(ref k, ref v)| (k, v))
+ self.items.iter().map(|(k, v)| (k, v))
}
/// Returns an iterator over the items in the map in insertion order along with their indices.
#[inline]
pub fn iter_enumerated(&self) -> impl '_ + DoubleEndedIterator<Item = (I, (&K, &V))> {
- self.items.iter_enumerated().map(|(i, (ref k, ref v))| (i, (k, v)))
+ self.items.iter_enumerated().map(|(i, (k, v))| (i, (k, v)))
}
/// Returns the item in the map with the given index.
let set: SortedIndexMultiMap<usize, _, _> = entries.iter().copied().collect();
// Insertion order is preserved.
- assert!(entries.iter().map(|(ref k, ref v)| (k, v)).eq(set.iter()));
+ assert!(entries.iter().map(|(k, v)| (k, v)).eq(set.iter()));
// Indexing
for (i, expect) in entries.iter().enumerate() {
#[inline]
pub fn remove(&mut self, data: &T) -> bool {
- self.head = match self.head {
- Some(ref mut head) if head.data == *data => head.next.take().map(|x| *x),
- Some(ref mut head) => return head.remove_next(data),
+ self.head = match &mut self.head {
+ Some(head) if head.data == *data => head.next.take().map(|x| *x),
+ Some(head) => return head.remove_next(data),
None => return false,
};
true
#[inline]
pub fn contains(&self, data: &T) -> bool {
let mut elem = self.head.as_ref();
- while let Some(ref e) = elem {
+ while let Some(e) = elem {
if &e.data == data {
return true;
}
}
impl<T: PartialEq> Element<T> {
- fn remove_next(&mut self, data: &T) -> bool {
- let mut n = self;
+ fn remove_next(mut self: &mut Self, data: &T) -> bool {
loop {
- match n.next {
+ match self.next {
Some(ref mut next) if next.data == *data => {
- n.next = next.next.take();
+ self.next = next.next.take();
return true;
}
- Some(ref mut next) => n = next,
+ Some(ref mut next) => self = next,
None => return false,
}
}
impl<T> TinyList<T> {
fn len(&self) -> usize {
let (mut elem, mut count) = (self.head.as_ref(), 0);
- while let Some(ref e) = elem {
+ while let Some(e) = elem {
count += 1;
elem = e.next.as_deref();
}
// values. So here is what we do:
//
// 1. Find the vector `[X | a < X && b < X]` of all values
- // `X` where `a < X` and `b < X`. In terms of the
+ // `X` where `a < X` and `b < X`. In terms of the
// graph, this means all values reachable from both `a`
// and `b`. Note that this vector is also a set, but we
// use the term vector because the order matters
use smallvec::SmallVec;
use std::{
borrow::Borrow,
+ collections::hash_map::Entry,
hash::Hash,
iter::{Product, Sum},
+ ops::Index,
};
use crate::{
fingerprint::Fingerprint,
- stable_hasher::{HashStable, StableHasher, ToStableHashKey},
+ stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey},
};
/// `UnordItems` is the order-less version of `Iterator`. It only contains methods
}
#[inline]
- pub fn all<U, F: Fn(T) -> bool>(mut self, f: F) -> bool {
+ pub fn all<F: Fn(T) -> bool>(mut self, f: F) -> bool {
self.0.all(f)
}
#[inline]
- pub fn any<U, F: Fn(T) -> bool>(mut self, f: F) -> bool {
+ pub fn any<F: Fn(T) -> bool>(mut self, f: F) -> bool {
self.0.any(f)
}
#[inline]
- pub fn filter<U, F: Fn(&T) -> bool>(self, f: F) -> UnordItems<T, impl Iterator<Item = T>> {
+ pub fn filter<F: Fn(&T) -> bool>(self, f: F) -> UnordItems<T, impl Iterator<Item = T>> {
UnordItems(self.0.filter(f))
}
pub fn count(self) -> usize {
self.0.count()
}
+
+ #[inline]
+ pub fn flat_map<U, F, O>(self, f: F) -> UnordItems<O, impl Iterator<Item = O>>
+ where
+ U: IntoIterator<Item = O>,
+ F: Fn(T) -> U,
+ {
+ UnordItems(self.0.flat_map(f))
+ }
}
impl<'a, T: Clone + 'a, I: Iterator<Item = &'a T>> UnordItems<&'a T, I> {
}
impl<V: Eq + Hash> Default for UnordSet<V> {
+ #[inline]
fn default() -> Self {
Self { inner: FxHashSet::default() }
}
}
#[inline]
- pub fn items(&self) -> UnordItems<&V, impl Iterator<Item = &V>> {
+ pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> bool
+ where
+ V: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.remove(k)
+ }
+
+ #[inline]
+ pub fn items<'a>(&'a self) -> UnordItems<&'a V, impl Iterator<Item = &'a V>> {
UnordItems(self.inner.iter())
}
UnordItems(self.inner.into_iter())
}
+ /// Returns the items of this set in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `V` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn to_sorted<HCX>(&self, hcx: &HCX, cache_sort_key: bool) -> Vec<&V>
+ where
+ V: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&x| x)
+ }
+
+ /// Returns the items of this set in stable sort order (as defined by
+ /// `StableOrd`). This method is much more efficient than
+ /// `into_sorted` because it does not need to transform keys to their
+ /// `ToStableHashKey` equivalent.
+ #[inline]
+ pub fn to_sorted_stable_ord(&self) -> Vec<V>
+ where
+ V: Ord + StableOrd + Copy,
+ {
+ let mut items: Vec<V> = self.inner.iter().copied().collect();
+ items.sort_unstable();
+ items
+ }
+
+ /// Returns the items of this set in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `V` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn into_sorted<HCX>(self, hcx: &HCX, cache_sort_key: bool) -> Vec<V>
+ where
+ V: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |x| x)
+ }
+
// We can safely extend this UnordSet from a set of unordered values because that
// won't expose the internal ordering anywhere.
#[inline]
pub fn extend<I: Iterator<Item = V>>(&mut self, items: UnordItems<V, I>) {
self.inner.extend(items.0)
}
+
+ #[inline]
+ pub fn clear(&mut self) {
+ self.inner.clear();
+ }
}
impl<V: Hash + Eq> Extend<V> for UnordSet<V> {
+ #[inline]
fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
self.inner.extend(iter)
}
}
+impl<V: Hash + Eq> FromIterator<V> for UnordSet<V> {
+ #[inline]
+ fn from_iter<T: IntoIterator<Item = V>>(iter: T) -> Self {
+ UnordSet { inner: FxHashSet::from_iter(iter) }
+ }
+}
+
impl<HCX, V: Hash + Eq + HashStable<HCX>> HashStable<HCX> for UnordSet<V> {
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
}
impl<K: Eq + Hash, V> Default for UnordMap<K, V> {
+ #[inline]
fn default() -> Self {
Self { inner: FxHashMap::default() }
}
}
impl<K: Hash + Eq, V> Extend<(K, V)> for UnordMap<K, V> {
+ #[inline]
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
self.inner.extend(iter)
}
}
+impl<K: Hash + Eq, V> FromIterator<(K, V)> for UnordMap<K, V> {
+ #[inline]
+ fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
+ UnordMap { inner: FxHashMap::from_iter(iter) }
+ }
+}
+
+impl<K: Hash + Eq, V, I: Iterator<Item = (K, V)>> From<UnordItems<(K, V), I>> for UnordMap<K, V> {
+ #[inline]
+ fn from(items: UnordItems<(K, V), I>) -> Self {
+ UnordMap { inner: FxHashMap::from_iter(items.0) }
+ }
+}
+
impl<K: Eq + Hash, V> UnordMap<K, V> {
#[inline]
pub fn len(&self) -> usize {
}
#[inline]
- pub fn items(&self) -> UnordItems<(&K, &V), impl Iterator<Item = (&K, &V)>> {
+ pub fn is_empty(&self) -> bool {
+ self.inner.is_empty()
+ }
+
+ #[inline]
+ pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
+ self.inner.entry(key)
+ }
+
+ #[inline]
+ pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
+ where
+ K: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.get(k)
+ }
+
+ #[inline]
+ pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
+ where
+ K: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.get_mut(k)
+ }
+
+ #[inline]
+ pub fn remove<Q: ?Sized>(&mut self, k: &Q) -> Option<V>
+ where
+ K: Borrow<Q>,
+ Q: Hash + Eq,
+ {
+ self.inner.remove(k)
+ }
+
+ #[inline]
+ pub fn items<'a>(&'a self) -> UnordItems<(&'a K, &'a V), impl Iterator<Item = (&'a K, &'a V)>> {
UnordItems(self.inner.iter())
}
pub fn extend<I: Iterator<Item = (K, V)>>(&mut self, items: UnordItems<(K, V), I>) {
self.inner.extend(items.0)
}
+
+ /// Returns the entries of this map in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `K` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn to_sorted<HCX>(&self, hcx: &HCX, cache_sort_key: bool) -> Vec<(&K, &V)>
+ where
+ K: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k)
+ }
+
+ /// Returns the entries of this map in stable sort order (as defined by `StableOrd`).
+ /// This method can be much more efficient than `into_sorted` because it does not need
+ /// to transform keys to their `ToStableHashKey` equivalent.
+ #[inline]
+ pub fn to_sorted_stable_ord(&self) -> Vec<(K, &V)>
+ where
+ K: Ord + StableOrd + Copy,
+ {
+ let mut items: Vec<(K, &V)> = self.inner.iter().map(|(&k, v)| (k, v)).collect();
+ items.sort_unstable_by_key(|&(k, _)| k);
+ items
+ }
+
+ /// Returns the entries of this map in stable sort order (as defined by `ToStableHashKey`).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `K` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn into_sorted<HCX>(self, hcx: &HCX, cache_sort_key: bool) -> Vec<(K, V)>
+ where
+ K: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |(k, _)| k)
+ }
+
+ /// Returns the values of this map in stable sort order (as defined by K's
+ /// `ToStableHashKey` implementation).
+ ///
+ /// The `cache_sort_key` parameter controls if [slice::sort_by_cached_key] or
+ /// [slice::sort_unstable_by_key] will be used for sorting the vec. Use
+ /// `cache_sort_key` when the [ToStableHashKey::to_stable_hash_key] implementation
+ /// for `K` is expensive (e.g. a `DefId -> DefPathHash` lookup).
+ #[inline]
+ pub fn values_sorted<HCX>(&self, hcx: &HCX, cache_sort_key: bool) -> impl Iterator<Item = &V>
+ where
+ K: ToStableHashKey<HCX>,
+ {
+ to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k)
+ .into_iter()
+ .map(|(_, v)| v)
+ }
+}
+
+impl<K, Q: ?Sized, V> Index<&Q> for UnordMap<K, V>
+where
+ K: Eq + Hash + Borrow<Q>,
+ Q: Eq + Hash,
+{
+ type Output = V;
+
+ #[inline]
+ fn index(&self, key: &Q) -> &V {
+ &self.inner[key]
+ }
}
impl<HCX, K: Hash + Eq + HashStable<HCX>, V: HashStable<HCX>> HashStable<HCX> for UnordMap<K, V> {
}
}
+impl<T, I: Iterator<Item = T>> From<UnordItems<T, I>> for UnordBag<T> {
+ fn from(value: UnordItems<T, I>) -> Self {
+ UnordBag { inner: Vec::from_iter(value.0) }
+ }
+}
+
impl<HCX, V: Hash + Eq + HashStable<HCX>> HashStable<HCX> for UnordBag<V> {
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
}
}
+#[inline]
+fn to_sorted_vec<HCX, T, K, I>(
+ hcx: &HCX,
+ iter: I,
+ cache_sort_key: bool,
+ extract_key: fn(&T) -> &K,
+) -> Vec<T>
+where
+ I: Iterator<Item = T>,
+ K: ToStableHashKey<HCX>,
+{
+ let mut items: Vec<T> = iter.collect();
+ if cache_sort_key {
+ items.sort_by_cached_key(|x| extract_key(x).to_stable_hash_key(hcx));
+ } else {
+ items.sort_unstable_by_key(|x| extract_key(x).to_stable_hash_key(hcx));
+ }
+
+ items
+}
+
fn hash_iter_order_independent<
HCX,
T: HashStable<HCX>,
The `driver` crate is effectively the "main" function for the rust
-compiler. It orchestrates the compilation process and "knits together"
+compiler. It orchestrates the compilation process and "knits together"
the code from the other crates within rustc. This crate itself does
not contain any of the "main logic" of the compiler (though it does
have some code related to pretty printing or other minor compiler
crate_cfg: cfg,
crate_check_cfg: check_cfg,
input: Input::File(PathBuf::new()),
- input_path: None,
output_file: ofile,
output_dir: odir,
file_loader,
registry: diagnostics_registry(),
};
+ if !tracing::dispatcher::has_been_set() {
+ init_rustc_env_logger_with_backtrace_option(&config.opts.unstable_opts.log_backtrace);
+ }
+
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 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, &*expanded_crate, *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()?;
+ queries.global_ctxt()?;
if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
return early_exit();
}
- queries.prepare_outputs()?;
-
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();
}
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),
)
});
}
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(),
/// This allows tools to enable rust logging without having to magically match rustc's
/// tracing crate version.
pub fn init_rustc_env_logger() {
- if let Err(error) = rustc_log::init_rustc_env_logger() {
+ init_rustc_env_logger_with_backtrace_option(&None);
+}
+
+/// This allows tools to enable rust logging without having to magically match rustc's
+/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to
+/// choose a target module you wish to show backtraces along with its logging.
+pub fn init_rustc_env_logger_with_backtrace_option(backtrace_target: &Option<String>) {
+ if let Err(error) = rustc_log::init_rustc_env_logger_with_backtrace_option(backtrace_target) {
early_error(ErrorOutputType::default(), &error.to_string());
}
}
pub fn main() -> ! {
let start_time = Instant::now();
let start_rss = get_resident_set_size();
- init_rustc_env_logger();
signal_handler::install();
let mut callbacks = TimePassesCallbacks::default();
install_ice_hook();
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>, krate: &ast::Crate, 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) => {
_ => 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(())
}
-// Error messages for EXXXX errors. Each message should start and end with a
-// new line, and be wrapped to 80 characters. In vim you can `:set tw=80` and
+// Error messages for EXXXX errors. Each message should start and end with a
+// new line, and be wrapped to 80 characters. In vim you can `:set tw=80` and
// use `gq` to wrap paragraphs. Use `:set tw=0` to disable.
//
// /!\ IMPORTANT /!\
E0786: include_str!("./error_codes/E0786.md"),
E0787: include_str!("./error_codes/E0787.md"),
E0788: include_str!("./error_codes/E0788.md"),
+E0789: include_str!("./error_codes/E0789.md"),
E0790: include_str!("./error_codes/E0790.md"),
E0791: include_str!("./error_codes/E0791.md"),
+E0792: include_str!("./error_codes/E0792.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
// E0487, // unsafe use of destructor: destructor might be called while...
// E0488, // lifetime of variable does not enclose its declaration
// E0489, // type/lifetime parameter not in scope here
- E0490, // a value of type `..` is borrowed for too long
+// E0490, // removed: unreachable
E0523, // two dependencies have same (crate-name, disambiguator) but different SVH
// E0526, // shuffle indices are not constant
// E0540, // multiple rustc_deprecated attributes
// E0721, // `await` keyword
// E0723, // unstable feature in `const` context
// E0738, // Removed; errored on `#[track_caller] fn`s in `extern "Rust" { ... }`.
- E0789, // rustc_allowed_through_unstable_modules without stability attribute
}
#### This error code is internal to the compiler and will not be emitted with normal Rust code.
+#### Note: this error code is no longer emitted by the compiler.
+
+This error code shows the variance of a type's generic parameters.
+
+Erroneous code example:
+
+```compile_fail
+// NOTE: this feature is perma-unstable and should *only* be used for
+// testing purposes.
+#![feature(rustc_attrs)]
+
+#[rustc_variance]
+struct Foo<'a, T> { // error: deliberate error to display type's variance
+ t: &'a mut T,
+}
+```
+
+which produces the following error:
+
+```text
+error: [-, o]
+ --> <anon>:4:1
+ |
+4 | struct Foo<'a, T> {
+ | ^^^^^^^^^^^^^^^^^
+```
+
+*Note that while `#[rustc_variance]` still exists and is used within the*
+*compiler, it no longer is marked as `E0208` and instead has no error code.*
+
+This error is deliberately triggered with the `#[rustc_variance]` attribute
+(`#![feature(rustc_attrs)]` must be enabled) and helps to show you the variance
+of the type's generic parameters. You can read more about variance and
+subtyping in [this section of the Rustnomicon]. For a more in depth look at
+variance (including a more complete list of common variances) see
+[this section of the Reference]. For information on how variance is implemented
+in the compiler, see [this section of `rustc-dev-guide`].
+
+This error can be easily fixed by removing the `#[rustc_variance]` attribute,
+the compiler's suggestion to comment it out can be applied automatically with
+`rustfix`.
+
+[this section of the Rustnomicon]: https://doc.rust-lang.org/nomicon/subtyping.html
+[this section of the Reference]: https://doc.rust-lang.org/reference/subtyping.html#variance
+[this section of `rustc-dev-guide`]: https://rustc-dev-guide.rust-lang.org/variance.html
foo(|| x = 2);
}
-// Attempts to take a mutable reference to closed-over data. Error message
+// Attempts to take a mutable reference to closed-over data. Error message
// reads: `cannot borrow data mutably in a captured outer variable...`
fn mut_addr() {
let mut x = 0u32;
access to the fields of the struct when it runs.
This means that when `s` reaches the end of `demo`, its destructor
-gets exclusive access to its `&mut`-borrowed string data. allowing
+gets exclusive access to its `&mut`-borrowed string data. allowing
another borrow of that string data (`p`), to exist across the drop of
`s` would be a violation of the principle that `&mut`-borrows have
exclusive, unaliased access to their referenced data.
```
The items of marker traits cannot be overridden, so there's no need to have them
-when they cannot be changed per-type anyway. If you wanted them for ergonomic
+when they cannot be changed per-type anyway. If you wanted them for ergonomic
reasons, consider making an extension trait instead.
--- /dev/null
+#### This error code is internal to the compiler and will not be emitted with normal Rust code.
+
+The internal `rustc_allowed_through_unstable_modules` attribute must be used
+on an item with a `stable` attribute.
+
+Erroneous code example:
+
+```compile_fail,E0789
+// NOTE: both of these attributes are perma-unstable and should *never* be
+// used outside of the compiler and standard library.
+#![feature(rustc_attrs)]
+#![feature(staged_api)]
+
+#![unstable(feature = "foo_module", reason = "...", issue = "123")]
+
+#[rustc_allowed_through_unstable_modules]
+// #[stable(feature = "foo", since = "1.0")]
+struct Foo;
+// ^^^ error: `rustc_allowed_through_unstable_modules` attribute must be
+// paired with a `stable` attribute
+```
+
+Typically when an item is marked with a `stable` attribute, the modules that
+enclose the item must also be marked with `stable` attributes, otherwise the
+item becomes *de facto* unstable. `#[rustc_allowed_through_unstable_modules]`
+is a workaround which allows an item to "escape" its unstable parent modules.
+This error occurs when an item is marked with
+`#[rustc_allowed_through_unstable_modules]` but no supplementary `stable`
+attribute exists. See [#99288](https://github.com/rust-lang/rust/pull/99288)
+for an example of `#[rustc_allowed_through_unstable_modules]` in use.
--- /dev/null
+A type alias impl trait can only have its hidden type assigned
+when used fully generically (and within their defining scope).
+This means
+
+```compile_fail,E0792
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl std::fmt::Debug;
+
+fn foo() -> Foo<u32> {
+ 5u32
+}
+```
+
+is not accepted. If it were accepted, one could create unsound situations like
+
+```compile_fail,E0792
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl Default;
+
+fn foo() -> Foo<u32> {
+ 5u32
+}
+
+fn main() {
+ let x = Foo::<&'static mut String>::default();
+}
+```
+
+
+Instead you need to make the function generic:
+
+```
+#![feature(type_alias_impl_trait)]
+
+type Foo<T> = impl std::fmt::Debug;
+
+fn foo<U>() -> Foo<U> {
+ 5u32
+}
+```
+
+This means that no matter the generic parameter to `foo`,
+the hidden type will always be `u32`.
+If you want to link the generic parameter to the hidden type,
+you can do that, too:
+
+
+```
+#![feature(type_alias_impl_trait)]
+
+use std::fmt::Debug;
+
+type Foo<T: Debug> = impl Debug;
+
+fn foo<U: Debug>() -> Foo<U> {
+ Vec::<U>::new()
+}
+```
ast_passes_fn_without_body =
free function without a body
.suggestion = provide a definition for the function
- .extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
+
+ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
[value] value
*[other] {$value_place}
} occurs here
+
+borrowck_opaque_type_non_generic_param =
+ expected generic {$kind} parameter, found `{$ty}`
+ .label = 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
hir_typeck_op_trait_generic_params =
`{$method_name}` must not have any generic parameters
+
+hir_typeck_lang_start_incorrect_number_params = incorrect number of parameters for the `start` lang item
+hir_typeck_lang_start_incorrect_number_params_note_expected_count = the `start` lang item should have four parameters, but found {$found_param_count}
+
+hir_typeck_lang_start_expected_sig_note = the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
+
+hir_typeck_lang_start_incorrect_param = parameter {$param_num} of the `start` lang item is incorrect
+ .suggestion = change the type from `{$found_ty}` to `{$expected_ty}`
+
+hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang item is incorrect
+ .suggestion = change the type from `{$found_ty}` to `{$expected_ty}`
infer_reborrow = ...so that reference does not outlive borrowed content
infer_reborrow_upvar = ...so that closure can access `{$name}`
infer_relate_object_bound = ...so that it can be closed over into an object
-infer_data_borrowed = ...so that the type `{$name}` is not borrowed for too long
infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at
infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues ->
[true] ...
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
interface_failed_writing_file =
failed to write file {$path}: {$error}"
+
+interface_proc_macro_crate_panic_abort =
+ building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic
lint_expectation = this lint expectation is unfulfilled
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
+ .rationale = {$rationale}
+
+lint_for_loops_over_fallibles =
+ for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
+ .suggestion = consider using `if let` to clear intent
+ .remove_next = to iterate over `{$recv_snip}` remove the call to `next`
+ .use_while_let = to check pattern in a loop use `while let`
+ .use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents
+
+lint_non_binding_let_on_sync_lock =
+ non-binding let on a synchronization lock
+
+lint_non_binding_let_on_drop_type =
+ non-binding let on a type that implements `Drop`
+
+lint_non_binding_let_suggestion =
+ consider binding to an unused variable to avoid immediately dropping the value
+
+lint_non_binding_let_multi_suggestion =
+ consider immediately dropping the value
+
+lint_deprecated_lint_name =
+ lint name `{$name}` is deprecated and may not have an effect in the future.
+ .suggestion = change it to
+
+lint_renamed_or_removed_lint = {$msg}
+ .suggestion = use the new name
+
+lint_unknown_lint =
+ unknown lint: `{$name}`
+ .suggestion = did you mean
+
+lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
+
+lint_unknown_gated_lint =
+ unknown lint: `{$name}`
+ .note = the `{$name}` lint is unstable
lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
.label = this {$label} contains {$count ->
lint_untranslatable_diag = diagnostics should be created using translatable messages
+lint_bad_opt_access = {$msg}
+
lint_cstring_ptr = getting the inner pointer of a temporary `CString`
.as_ptr_label = this pointer will be invalid
.unwrap_label = this `CString` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
.suggestion = try naming the parameter or explicitly ignoring it
lint_builtin_deprecated_attr_link = use of deprecated attribute `{$name}`: {$reason}. See {$link}
+ .msg_suggestion = {$msg}
+ .default_suggestion = remove this attribute
lint_builtin_deprecated_attr_used = use of deprecated attribute `{$name}`: no longer used.
lint_builtin_deprecated_attr_default_suggestion = remove this attribute
.note = see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information
.help = consider using `min_{$name}` instead, which is more stable and complete
-lint_builtin_clashing_extern_same_name = `{$this_fi}` redeclared with a different signature
+lint_builtin_unpermitted_type_init_zeroed = the type `{$ty}` does not permit zero-initialization
+lint_builtin_unpermitted_type_init_unint = the type `{$ty}` does not permit being left uninitialized
+
+lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
+lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+
+lint_builtin_clashing_extern_same_name = `{$this}` redeclared with a different signature
.previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration
-lint_builtin_clashing_extern_diff_name = `{$this_fi}` redeclares `{$orig}` with a different signature
+lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
.previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration
lint_builtin_asm_labels = avoid using named labels in inline assembly
+lint_builtin_special_module_name_used_lib = found module declaration for lib.rs
+ .note = lib.rs is the root of this crate's library target
+ .help = to refer to it from other targets, use the library's name as the path
+
+lint_builtin_special_module_name_used_main = found module declaration for main.rs
+ .note = a binary crate cannot be used as library
+
+lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target
+ .label = target type is set here
+
lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid
.label = overruled by previous forbid
[one] variant that isn't
*[other] variants that aren't
} matched
+
+mir_build_suggest_attempted_int_lit = alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
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
-passes_see_issue =
see issue #{$issue} <https://github.com/rust-lang/rust/issues/{$issue}> for more information
+passes_incorrect_do_not_recommend_location =
+ `#[do_not_recommend]` can only be placed on trait implementations
+
passes_outer_crate_level_attr =
crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
ty_utils_array_not_supported = array construction is not supported in generic constants
-ty_utils_block_not_supported = blocks are not supported in generic constant
+ty_utils_block_not_supported = blocks are not supported in generic constants
-ty_utils_never_to_any_not_supported = converting nevers to any is not supported in generic constant
+ty_utils_never_to_any_not_supported = converting nevers to any is not supported in generic constants
ty_utils_tuple_not_supported = tuple construction is not supported in generic constants
-ty_utils_index_not_supported = indexing is not supported in generic constant
+ty_utils_index_not_supported = indexing is not supported in generic constants
-ty_utils_field_not_supported = field access is not supported in generic constant
+ty_utils_field_not_supported = field access is not supported in generic constants
-ty_utils_const_block_not_supported = const blocks are not supported in generic constant
+ty_utils_const_block_not_supported = const blocks are not supported in generic constants
ty_utils_adt_not_supported = struct/enum construction is not supported in generic constants
ty_utils_inline_asm_not_supported = assembly is not supported in generic constants
-ty_utils_operation_not_supported = unsupported operation in generic constant
+ty_utils_operation_not_supported = unsupported operation in generic constants
pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
- /// This is not used for highlighting or rendering any error message. Rather, it can be used
- /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
- /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
+ /// This is not used for highlighting or rendering any error message. Rather, it can be used
+ /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
+ /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
pub sort_span: Span,
/// If diagnostic is from Lint, custom hash function ignores notes
if let Some(span) = span.primary_span() {
// Compare the primary span of the diagnostic with the span of the suggestion
- // being emitted. If they belong to the same file, we don't *need* to show the
+ // being emitted. If they belong to the same file, we don't *need* to show the
// file name, saving in verbosity, but if it *isn't* we do need it, otherwise we're
// telling users to make a change but not clarifying *where*.
let loc = sm.lookup_char_pos(parts[0].span.lo());
//
// On Unix systems, we write into a buffered terminal rather than directly to a terminal. When
// the .flush() is called we take the buffer created from the buffered writes and write it at
- // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
+ // one shot. Because the Unix systems use ANSI for the colors, which is a text-based styling
// scheme, this buffered approach works and maintains the styling.
//
// On Windows, styling happens through calls to a terminal API. This prevents us from using the
- // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
+ // same buffering approach. Instead, we use a global Windows mutex, which we acquire long
// enough to output the full error message, then we release.
let _buffer_lock = lock::acquire_global_lock("rustc_errors");
for (pos, line) in rendered_buffer.iter().enumerate() {
use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId};
use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{BytePos, FileName, RealFileName, Span, DUMMY_SP};
+use rustc_span::{BytePos, FileName, Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::iter;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
use std::rc::Rc;
pub(crate) use rustc_span::hygiene::MacroKind;
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,
}
}
if let [variant] = &*enum_def.variants {
if variant.ident.name == sym::Input {
let filename = sess.source_map().span_to_filename(item.ident.span);
- if let FileName::Real(RealFileName::LocalPath(path)) = filename {
- if let Some(c) = path
+ if let FileName::Real(real) = filename {
+ if let Some(c) = real
+ .local_path()
+ .unwrap_or(Path::new(""))
.components()
.flat_map(|c| c.as_os_str().to_str())
.find(|c| c.starts_with("rental") || c.starts_with("allsorts-rental"))
ast::ExprKind::Closure(Box::new(ast::Closure {
binder: ast::ClosureBinder::NotPresent,
capture_clause: ast::CaptureBy::Ref,
+ constness: ast::Const::No,
asyncness: ast::Async::No,
movability: ast::Movability::Movable,
fn_decl,
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),
} else {
match delim {
Delimiter::Brace => {
- // The delimiter is `{`. This indicates the beginning
+ // The delimiter is `{`. This indicates the beginning
// of a meta-variable expression (e.g. `${count(ident)}`).
// Try to parse the meta-variable expression.
match MetaVarExpr::parse(&tts, delim_span.entire(), sess) {
}
}
// If we didn't find a metavar expression above, then we must have a
- // repetition sequence in the macro (e.g. `$(pat)*`). Parse the
+ // repetition sequence in the macro (e.g. `$(pat)*`). Parse the
// contents of the sequence itself
let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition);
// Get the Kleene operator and optional separator
fn next(&mut self) -> Option<&'a mbe::TokenTree> {
match self {
- Frame::Delimited { tts, ref mut idx, .. }
- | Frame::Sequence { tts, ref mut idx, .. } => {
+ Frame::Delimited { tts, idx, .. } | Frame::Sequence { tts, idx, .. } => {
let res = tts.get(*idx);
*idx += 1;
res
let ident = MacroRulesNormalizedIdent::new(original_ident);
if let Some(cur_matched) = lookup_cur_matched(ident, interp, &repeats) {
match cur_matched {
- MatchedTokenTree(ref tt) => {
+ MatchedTokenTree(tt) => {
// `tt`s are emitted into the output stream directly as "raw tokens",
// without wrapping them into groups.
let token = tt.clone();
result.push(token);
}
- MatchedNonterminal(ref nt) => {
+ MatchedNonterminal(nt) => {
// Other variables are emitted into the output stream as groups with
// `Delimiter::Invisible` to maintain parsing priorities.
// `Interpolated` is currently used for such groups in rustc parser.
interpolations: &'a FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
repeats: &[(usize, usize)],
) -> Option<&'a NamedMatch> {
- interpolations.get(&ident).map(|matched| {
- let mut matched = matched;
+ interpolations.get(&ident).map(|mut matched| {
for &(idx, _) in repeats {
match matched {
MatchedTokenTree(_) | MatchedNonterminal(_) => break,
- MatchedSeq(ref ads) => matched = ads.get(idx).unwrap(),
+ MatchedSeq(ads) => matched = ads.get(idx).unwrap(),
}
}
match self {
LockstepIterSize::Unconstrained => other,
LockstepIterSize::Contradiction(_) => self,
- LockstepIterSize::Constraint(l_len, ref l_id) => match other {
+ LockstepIterSize::Constraint(l_len, l_id) => match other {
LockstepIterSize::Unconstrained => self,
LockstepIterSize::Contradiction(_) => other,
LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
repeats: &[(usize, usize)],
) -> LockstepIterSize {
use mbe::TokenTree;
- match *tree {
- TokenTree::Delimited(_, ref delimited) => {
+ match tree {
+ TokenTree::Delimited(_, delimited) => {
delimited.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
size.with(lockstep_iter_size(tt, interpolations, repeats))
})
}
- TokenTree::Sequence(_, ref seq) => {
+ TokenTree::Sequence(_, seq) => {
seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
size.with(lockstep_iter_size(tt, interpolations, repeats))
})
}
TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
- let name = MacroRulesNormalizedIdent::new(name);
+ let name = MacroRulesNormalizedIdent::new(*name);
match lookup_cur_matched(name, interpolations, repeats) {
Some(matched) => match matched {
MatchedTokenTree(_) | MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
- MatchedSeq(ref ads) => LockstepIterSize::Constraint(ads.len(), name),
+ MatchedSeq(ads) => LockstepIterSize::Constraint(ads.len(), name),
},
_ => LockstepIterSize::Unconstrained,
}
}
- TokenTree::MetaVarExpr(_, ref expr) => {
+ TokenTree::MetaVarExpr(_, expr) => {
let default_rslt = LockstepIterSize::Unconstrained;
let Some(ident) = expr.ident() else { return default_rslt; };
let name = MacroRulesNormalizedIdent::new(ident);
match lookup_cur_matched(name, interpolations, repeats) {
- Some(MatchedSeq(ref ads)) => {
+ Some(MatchedSeq(ads)) => {
default_rslt.with(LockstepIterSize::Constraint(ads.len(), name))
}
_ => default_rslt,
Some(_) => Err(out_of_bounds_err(cx, declared_lhs_depth, sp.entire(), "count")),
}
}
- MatchedSeq(ref named_matches) => {
+ MatchedSeq(named_matches) => {
let new_declared_lhs_depth = declared_lhs_depth + 1;
match depth_opt {
None => named_matches
// before we start counting. `matched` contains the various levels of the
// tree as we descend, and its final value is the subtree we are currently at.
for &(idx, _) in repeats {
- if let MatchedSeq(ref ads) = matched {
+ if let MatchedSeq(ads) = matched {
matched = &ads[idx];
}
}
}
impl<'a> visit::Visitor<'a> for PatIdentVisitor {
fn visit_pat(&mut self, p: &'a ast::Pat) {
- match p.kind {
- PatKind::Ident(_, ref ident, _) => {
- self.spans.push(ident.span.clone());
+ match &p.kind {
+ PatKind::Ident(_, ident, _) => {
+ self.spans.push(ident.span);
}
_ => {
visit::walk_pat(self, p);
)
.unwrap();
- let tts: Vec<_> = match expr.kind {
- ast::ExprKind::MacCall(ref mac) => mac.args.tokens.clone().into_trees().collect(),
- _ => panic!("not a macro"),
- };
+ let ast::ExprKind::MacCall(mac) = &expr.kind else { panic!("not a macro") };
+ let tts: Vec<_> = mac.args.tokens.clone().into_trees().collect();
let span = tts.iter().rev().next().unwrap().span();
.unwrap()
.unwrap();
- if let ast::ItemKind::Mod(_, ref mod_kind) = item.kind {
- assert!(matches!(mod_kind, ast::ModKind::Loaded(items, ..) if items.len() == 2));
- } else {
- panic!();
- }
+ let ast::ItemKind::Mod(_, mod_kind) = &item.kind else { panic!() };
+ assert!(matches!(mod_kind, ast::ModKind::Loaded(items, ..) if items.len() == 2));
});
}
let stream = TokenStream::from_nonterminal_ast(&nt);
// A hack used to pass AST fragments to attribute and derive
// macros as a single nonterminal token instead of a token
- // stream. Such token needs to be "unwrapped" and not
+ // stream. Such token needs to be "unwrapped" and not
// represented as a delimited group.
// FIXME: It needs to be removed, but there are some
// compatibility issues (see #73345).
}
fn path(&mut self, file: &Self::SourceFile) -> String {
- match file.name {
- FileName::Real(ref name) => name
+ match &file.name {
+ FileName::Real(name) => name
.local_path()
.expect("attempting to get a file path in an imported file in `proc_macro::SourceFile::path`")
.to_str()
/// Allows `#[target_feature(...)]` on aarch64 platforms
(accepted, aarch64_target_feature, "1.61.0", Some(44839), None),
+ /// Allows using the `efiapi` ABI.
+ (accepted, abi_efiapi, "CURRENT_RUSTC_VERSION", Some(65815), None),
/// Allows the sysV64 ABI to be specified on all platforms
/// instead of just the platforms on which it is the C ABI.
(accepted, abi_sysv64, "1.24.0", Some(36167), None),
(active, abi_avr_interrupt, "1.45.0", Some(69664), None),
/// Allows `extern "C-cmse-nonsecure-call" fn()`.
(active, abi_c_cmse_nonsecure_call, "1.51.0", Some(81391), None),
- /// Allows using the `efiapi` ABI.
- (active, abi_efiapi, "1.40.0", Some(65815), None),
/// Allows `extern "msp430-interrupt" fn()`.
(active, abi_msp430_interrupt, "1.16.0", Some(38487), None),
/// Allows `extern "ptx-*" fn()`.
(active, collapse_debuginfo, "1.65.0", Some(100758), None),
/// Allows `async {}` expressions in const contexts.
(active, const_async_blocks, "1.53.0", Some(85368), None),
- // Allows limiting the evaluation steps of const expressions
+ /// Allows `const || {}` closures in const contexts.
+ (incomplete, const_closures, "CURRENT_RUSTC_VERSION", Some(106003), None),
+ /// Allows limiting the evaluation steps of const expressions
(active, const_eval_limit, "1.43.0", Some(67217), None),
/// Allows the definition of `const extern fn` and `const unsafe extern fn`.
(active, const_extern_fn, "1.40.0", Some(64926), None),
/// Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
pub fn from_environment(krate: Option<&str>) -> Self {
// `true` if this is a feature-staged build, i.e., on the beta or stable channel.
- let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+ let disable_unstable_features =
+ option_env!("CFG_DISABLE_UNSTABLE_FEATURES").map(|s| s != "0").unwrap_or(false);
// Returns whether `krate` should be counted as unstable
let is_unstable_crate = |var: &str| {
krate.map_or(false, |name| var.split(',').any(|new_krate| new_krate == name))
binder: NodeId,
},
/// This variant is used for anonymous lifetimes that we did not resolve during
- /// late resolution. Those lifetimes will be inferred by typechecking.
+ /// late resolution. Those lifetimes will be inferred by typechecking.
Infer,
/// Explicit `'static` lifetime.
Static,
/// Implicit lifetime in a context like `dyn Foo`. This is
/// distinguished from implicit lifetimes elsewhere because the
/// lifetime that they default to must appear elsewhere within the
- /// enclosing type. This means that, in an `impl Trait` context, we
+ /// enclosing type. This means that, in an `impl Trait` context, we
/// don't have to create a parameter for them. That is, `impl
/// Trait<Item = &u32>` expands to an opaque type like `type
/// Foo<'a> = impl Trait<Item = &'a u32>`, but `impl Trait<item =
pub hash_without_bodies: Fingerprint,
/// Full HIR for the current owner.
// The zeroth node's parent should never be accessed: the owner's parent is computed by the
- // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally
+ // hir_owner_parent query. It is set to `ItemLocalId::INVALID` to force an ICE if accidentally
// used.
pub nodes: IndexVec<ItemLocalId, Option<ParentedNode<'tcx>>>,
/// Content of local bodies.
pub struct Closure<'hir> {
pub def_id: LocalDefId,
pub binder: ClosureBinder,
+ pub constness: Constness,
pub capture_clause: CaptureBy,
pub bound_generic_params: &'hir [GenericParam<'hir>],
pub fn_decl: &'hir FnDecl<'hir>,
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,
}
}
+ 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 {
fn_decl_span: _,
fn_arg_span: _,
movability: _,
+ constness: _,
}) => {
walk_list!(visitor, visit_generic_param, bound_generic_params);
visitor.visit_fn(FnKind::Closure, fn_decl, body, expression.span, expression.hir_id)
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;
}
if potential_assoc_types.len() == assoc_items.len() {
// When the amount of missing associated types equals the number of
- // extra type arguments present. A suggesting to replace the generic args with
+ // extra type arguments present. A suggesting to replace the generic args with
// associated types is already emitted.
already_has_generics_args_suggestion = true;
} else if let (Ok(snippet), false) =
// We should never be able to reach this point with well-formed input.
// There are three situations in which we can encounter this issue.
//
- // 1. The number of arguments is incorrect. In this case, an error
- // will already have been emitted, and we can ignore it.
- // 2. There are late-bound lifetime parameters present, yet the
- // lifetime arguments have also been explicitly specified by the
- // user.
- // 3. We've inferred some lifetimes, which have been provided later (i.e.
- // after a type or const). We want to throw an error in this case.
+ // 1. The number of arguments is incorrect. In this case, an error
+ // will already have been emitted, and we can ignore it.
+ // 2. There are late-bound lifetime parameters present, yet the
+ // lifetime arguments have also been explicitly specified by the
+ // user.
+ // 3. We've inferred some lifetimes, which have been provided later (i.e.
+ // after a type or const). We want to throw an error in this case.
if arg_count.correct.is_ok()
&& arg_count.explicit_late_bound == ExplicitLateBound::No
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::{walk_generics, Visitor as _};
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
+use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::GenericParamDefKind;
return tcx.const_error(ty).into();
}
if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
+ tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
if infer_args {
self.astconv.ct_infer(ty, Some(param), self.span).into()
.bindings
.iter()
.map(|binding| {
- let kind = match binding.kind {
- hir::TypeBindingKind::Equality { ref term } => match term {
- hir::Term::Ty(ref ty) => {
+ let kind = match &binding.kind {
+ hir::TypeBindingKind::Equality { term } => match term {
+ hir::Term::Ty(ty) => {
ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
}
- hir::Term::Const(ref c) => {
+ hir::Term::Const(c) => {
let c = Const::from_anon_const(self.tcx(), c.def_id);
ConvertedBindingKind::Equality(c.into())
}
},
- hir::TypeBindingKind::Constraint { ref bounds } => {
+ hir::TypeBindingKind::Constraint { bounds } => {
ConvertedBindingKind::Constraint(bounds)
}
};
/// ```
///
/// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
- /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
+ /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
/// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
///
/// `span` should be the declaration size of the parameter.
i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
assert_eq!(trait_ref.self_ty(), dummy_self);
- // Verify that `dummy_self` did not leak inside default type parameters. This
+ // Verify that `dummy_self` did not leak inside default type parameters. This
// could not be done at path creation, since we need to see through trait aliases.
let mut missing_type_params = vec![];
let mut references_self = false;
fn report_ambiguous_associated_type(
&self,
span: Span,
- type_str: &str,
- trait_str: &str,
+ types: &[String],
+ traits: &[String],
name: Symbol,
) -> ErrorGuaranteed {
let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
.keys()
.any(|full_span| full_span.contains(span))
{
- err.span_suggestion(
+ err.span_suggestion_verbose(
span.shrink_to_lo(),
"you are looking for the module in `std`, not the primitive type",
"std::",
Applicability::MachineApplicable,
);
} else {
- err.span_suggestion(
- span,
- "use fully-qualified syntax",
- format!("<{} as {}>::{}", type_str, trait_str, name),
- Applicability::HasPlaceholders,
- );
+ match (types, traits) {
+ ([], []) => {
+ err.span_suggestion_verbose(
+ span,
+ &format!(
+ "if there were a type named `Type` that implements a trait named \
+ `Trait` with associated type `{name}`, you could use the \
+ fully-qualified path",
+ ),
+ format!("<Type as Trait>::{name}"),
+ Applicability::HasPlaceholders,
+ );
+ }
+ ([], [trait_str]) => {
+ err.span_suggestion_verbose(
+ span,
+ &format!(
+ "if there were a type named `Example` that implemented `{trait_str}`, \
+ you could use the fully-qualified path",
+ ),
+ format!("<Example as {trait_str}>::{name}"),
+ Applicability::HasPlaceholders,
+ );
+ }
+ ([], traits) => {
+ err.span_suggestions(
+ span,
+ &format!(
+ "if there were a type named `Example` that implemented one of the \
+ traits with associated type `{name}`, you could use the \
+ fully-qualified path",
+ ),
+ traits
+ .iter()
+ .map(|trait_str| format!("<Example as {trait_str}>::{name}"))
+ .collect::<Vec<_>>(),
+ Applicability::HasPlaceholders,
+ );
+ }
+ ([type_str], []) => {
+ err.span_suggestion_verbose(
+ span,
+ &format!(
+ "if there were a trait named `Example` with associated type `{name}` \
+ implemented for `{type_str}`, you could use the fully-qualified path",
+ ),
+ format!("<{type_str} as Example>::{name}"),
+ Applicability::HasPlaceholders,
+ );
+ }
+ (types, []) => {
+ err.span_suggestions(
+ span,
+ &format!(
+ "if there were a trait named `Example` with associated type `{name}` \
+ implemented for one of the types, you could use the fully-qualified \
+ path",
+ ),
+ types
+ .into_iter()
+ .map(|type_str| format!("<{type_str} as Example>::{name}")),
+ Applicability::HasPlaceholders,
+ );
+ }
+ (types, traits) => {
+ let mut suggestions = vec![];
+ for type_str in types {
+ for trait_str in traits {
+ suggestions.push(format!("<{type_str} as {trait_str}>::{name}"));
+ }
+ }
+ err.span_suggestions(
+ span,
+ "use the fully-qualified path",
+ suggestions,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
}
err.emit()
}
) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
let tcx = self.tcx();
let assoc_ident = assoc_segment.ident;
- let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind {
+ let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
path.res
} else {
Res::Err
return;
};
let (qself_sugg_span, is_self) = if let hir::TyKind::Path(
- hir::QPath::Resolved(_, ref path)
- ) = qself.kind {
+ hir::QPath::Resolved(_, path)
+ ) = &qself.kind {
// If the path segment already has type params, we want to overwrite
// them.
match &path.segments[..] {
};
self.one_bound_for_assoc_type(
- || traits::supertraits(tcx, ty::Binder::dummy(trait_ref)),
+ || traits::supertraits(tcx, ty::Binder::dummy(trait_ref.subst_identity())),
|| "Self".to_string(),
assoc_ident,
span,
err.emit()
} else if let Err(reported) = qself_ty.error_reported() {
reported
+ } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
+ // `<impl Trait as OtherTrait>::Assoc` makes no sense.
+ struct_span_err!(
+ tcx.sess,
+ tcx.def_span(alias_ty.def_id),
+ E0667,
+ "`impl Trait` is not allowed in path parameters"
+ )
+ .emit() // Already reported in an earlier stage.
} else {
+ // Find all the `impl`s that `qself_ty` has for any trait that has the
+ // associated type, so that we suggest the right one.
+ let infcx = tcx.infer_ctxt().build();
+ // We create a fresh `ty::ParamEnv` instead of the one for `self.item_def_id()`
+ // to avoid a cycle error in `src/test/ui/resolve/issue-102946.rs`.
+ let param_env = ty::ParamEnv::empty();
+ let traits: Vec<_> = self
+ .tcx()
+ .all_traits()
+ .filter(|trait_def_id| {
+ // Consider only traits with the associated type
+ tcx.associated_items(*trait_def_id)
+ .in_definition_order()
+ .any(|i| {
+ i.kind.namespace() == Namespace::TypeNS
+ && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
+ && matches!(i.kind, ty::AssocKind::Type)
+ })
+ // Consider only accessible traits
+ && tcx.visibility(*trait_def_id)
+ .is_accessible_from(self.item_def_id(), tcx)
+ && tcx.all_impls(*trait_def_id)
+ .any(|impl_def_id| {
+ let trait_ref = tcx.impl_trait_ref(impl_def_id);
+ trait_ref.map_or(false, |trait_ref| {
+ let impl_ = trait_ref.subst(
+ tcx,
+ infcx.fresh_substs_for_item(span, impl_def_id),
+ );
+ infcx
+ .can_eq(
+ param_env,
+ tcx.erase_regions(impl_.self_ty()),
+ tcx.erase_regions(qself_ty),
+ )
+ .is_ok()
+ })
+ && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
+ })
+ })
+ .map(|trait_def_id| tcx.def_path_str(trait_def_id))
+ .collect();
+
// Don't print `TyErr` to the user.
self.report_ambiguous_associated_type(
span,
- &qself_ty.to_string(),
- "Trait",
+ &[qself_ty.to_string()],
+ &traits,
assoc_ident.name,
)
};
let is_part_of_self_trait_constraints = def_id == trait_def_id;
let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id);
- let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
- "Self"
+ let type_names = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
+ vec!["Self".to_string()]
} else {
- "Type"
+ // Find all the types that have an `impl` for the trait.
+ tcx.all_impls(trait_def_id)
+ .filter(|impl_def_id| {
+ // Consider only accessible traits
+ tcx.visibility(*impl_def_id).is_accessible_from(self.item_def_id(), tcx)
+ && tcx.impl_polarity(impl_def_id) != ty::ImplPolarity::Negative
+ })
+ .filter_map(|impl_def_id| tcx.impl_trait_ref(impl_def_id))
+ .map(|impl_| impl_.subst_identity().self_ty())
+ // We don't care about blanket impls.
+ .filter(|self_ty| !self_ty.has_non_region_param())
+ .map(|self_ty| tcx.erase_regions(self_ty).to_string())
+ .collect()
};
-
+ // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
+ // references the trait. Relevant for the first case in
+ // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
let reported = self.report_ambiguous_associated_type(
span,
- type_name,
- &path_str,
+ &type_names,
+ &[path_str],
item_segment.ident.name,
);
return tcx.ty_error_with_guaranteed(reported)
match path.res {
Res::Def(DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder, did) => {
// Check for desugared `impl Trait`.
- assert!(ty::is_impl_trait_defn(tcx, did).is_none());
+ assert!(tcx.is_type_alias_impl_trait(did));
let item_segment = path.segments.split_last().unwrap();
self.prohibit_generics(item_segment.1.iter(), |err| {
err.note("`impl Trait` types can't have type parameters");
"generic `Self` types are currently not permitted in anonymous constants",
);
if let Some(hir::Node::Item(&hir::Item {
- kind: hir::ItemKind::Impl(ref impl_),
+ kind: hir::ItemKind::Impl(impl_),
..
})) = tcx.hir().get_if_local(def_id)
{
}
/// Parses the programmer's textual representation of a type into our
- /// internal notion of a type. This is meant to be used within a path.
+ /// internal notion of a type. This is meant to be used within a path.
pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
self.ast_ty_to_ty_inner(ast_ty, false, true)
}
fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> {
let tcx = self.tcx();
- let result_ty = match ast_ty.kind {
- hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
- hir::TyKind::Ptr(ref mt) => {
+ let result_ty = match &ast_ty.kind {
+ hir::TyKind::Slice(ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
+ hir::TyKind::Ptr(mt) => {
tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
}
- hir::TyKind::Ref(ref region, ref mt) => {
+ hir::TyKind::Ref(region, mt) => {
let r = self.ast_region_to_region(region, None);
debug!(?r);
let t = self.ast_ty_to_ty_inner(mt.ty, true, false);
Some(ast_ty),
))
}
- hir::TyKind::TraitObject(bounds, ref lifetime, repr) => {
+ hir::TyKind::TraitObject(bounds, lifetime, repr) => {
self.maybe_lint_bare_trait(ast_ty, in_path);
let repr = match repr {
TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
};
self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
}
- hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => {
+ hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => {
debug!(?maybe_qself, ?path);
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
self.res_to_ty(opt_self_ty, path, false)
}
- hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
+ &hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
let opaque_ty = tcx.hir().item(item_id);
let def_id = item_id.owner_id.to_def_id();
ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
}
}
- hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => {
+ hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
debug!(?qself, ?segment);
let ty = self.ast_ty_to_ty_inner(qself, false, true);
self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
.map(|(ty, _, _)| ty)
.unwrap_or_else(|_| tcx.ty_error())
}
- hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
+ &hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
let def_id = tcx.require_lang_item(lang_item, Some(span));
let (substs, _) = self.create_substs_for_ast_path(
span,
);
EarlyBinder(tcx.at(span).type_of(def_id)).subst(tcx, substs)
}
- hir::TyKind::Array(ref ty, ref length) => {
+ hir::TyKind::Array(ty, length) => {
let length = match length {
&hir::ArrayLen::Infer(_, span) => self.ct_infer(tcx.types.usize, None, span),
hir::ArrayLen::Body(constant) => {
tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length))
}
- hir::TyKind::Typeof(ref e) => {
+ hir::TyKind::Typeof(e) => {
let ty_erased = tcx.type_of(e.def_id);
let ty = tcx.fold_regions(ty_erased, |r, _| {
if r.is_erased() { tcx.lifetimes.re_static } else { r }
let label = "add `dyn` keyword before this trait";
let mut diag =
rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg);
- diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
+ if self_ty.span.can_be_used_for_suggestions() {
+ diag.multipart_suggestion_verbose(
+ label,
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
// check if the impl trait that we are considering is a impl of a local trait
self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
diag.emit();
use crate::traits::query::evaluate_obligation::InferCtxtExt;
use crate::traits::NormalizeExt;
use crate::traits::{self, TraitEngine, TraitEngineExt};
-use rustc_hir as hir;
use rustc_infer::infer::InferCtxt;
use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::Limit;
+use rustc_span::def_id::LocalDefId;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::Span;
// Meta infos:
infcx: &'a InferCtxt<'tcx>,
span: Span,
- body_id: hir::HirId,
+ body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
// Current state:
pub fn new(
infcx: &'a InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_def_id: LocalDefId,
span: Span,
base_ty: Ty<'tcx>,
) -> Autoderef<'a, 'tcx> {
Autoderef {
infcx,
span,
- body_id,
+ body_id: body_def_id,
param_env,
state: AutoderefSnapshot {
steps: vec![],
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!(?t, "root_visit_ty");
if t == self.opaque_identity_ty {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
t.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
if self.references_parent_regions {
ControlFlow::Break(t)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
span: Span,
origin: &hir::OpaqueTyOrigin,
) {
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let defining_use_anchor = match *origin {
hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
hir::OpaqueTyOrigin::TyAlias => def_id,
_ => re,
});
- let misc_cause = traits::ObligationCause::misc(span, hir_id);
+ let misc_cause = traits::ObligationCause::misc(span, def_id);
match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) {
Ok(()) => {}
DefKind::Fn => {} // entirely within check_item_body
DefKind::Impl => {
let it = tcx.hir().item(id);
- let hir::ItemKind::Impl(ref impl_) = it.kind else {
- return;
- };
+ let hir::ItemKind::Impl(impl_) = it.kind else { return };
debug!("ItemKind::Impl {} with id {:?}", it.ident, it.owner_id);
if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.owner_id) {
check_impl_items_against_trait(
tcx,
it.span,
it.owner_id.def_id,
- impl_trait_ref,
+ impl_trait_ref.subst_identity(),
&impl_.items,
);
check_on_unimplemented(tcx, it);
}
DefKind::Trait => {
let it = tcx.hir().item(id);
- let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else {
+ let hir::ItemKind::Trait(_, _, _, _, items) = it.kind else {
return;
};
check_on_unimplemented(tcx, it);
for item in items.iter() {
let item = tcx.hir().trait_item(item.id);
- match item.kind {
- hir::TraitItemKind::Fn(ref sig, _) => {
+ match &item.kind {
+ hir::TraitItemKind::Fn(sig, _) => {
let abi = sig.header.abi;
fn_maybe_err(tcx, item.ident.span, abi);
}
}
let item = tcx.hir().foreign_item(item.id);
- match item.kind {
- hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
+ match &item.kind {
+ hir::ForeignItemKind::Fn(fn_decl, _, _) => {
require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
}
hir::ForeignItemKind::Static(..) => {
///
/// 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}`"));
}
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);
+ }
+ }
}
}
}
use crate::errors::LifetimesOrBoundsMismatchOnTrait;
use hir::def_id::{DefId, LocalDefId};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
+use rustc_errors::{
+ pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan,
+};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit;
let impl_m_span = tcx.def_span(impl_m.def_id);
- if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) {
- return;
- }
-
- if let Err(_) = compare_number_of_generics(tcx, impl_m, trait_m, trait_item_span, false) {
- return;
- }
-
- if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m, false) {
- return;
- }
-
- if let Err(_) =
- compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)
- {
- return;
- }
-
- if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) {
- return;
- }
-
- if let Err(_) = compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
- return;
- }
-
- if let Err(_) = compare_method_predicate_entailment(
- tcx,
- impl_m,
- impl_m_span,
- trait_m,
- impl_trait_ref,
- CheckImpliedWfMode::Check,
- ) {
- return;
- }
+ let _: Result<_, ErrorGuaranteed> = try {
+ compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)?;
+ compare_number_of_generics(tcx, impl_m, trait_m, trait_item_span, false)?;
+ compare_generic_param_kinds(tcx, impl_m, trait_m, false)?;
+ compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)?;
+ compare_synthetic_generics(tcx, impl_m, trait_m)?;
+ compare_asyncness(tcx, impl_m, impl_m_span, trait_m, trait_item_span)?;
+ compare_method_predicate_entailment(
+ tcx,
+ impl_m,
+ impl_m_span,
+ trait_m,
+ impl_trait_ref,
+ CheckImpliedWfMode::Check,
+ )?;
+ };
}
/// This function is best explained by example. Consider a trait:
/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
///
/// This type is also the same but the name of the bound region (`'a`
-/// vs `'b`). However, the normal subtyping rules on fn types handle
+/// vs `'b`). However, the normal subtyping rules on fn types handle
/// this kind of equivalency just fine.
///
/// We now use these substitutions to ensure that all declared bounds are
//
// FIXME(@lcnr): remove that after removing `cause.body_id` from
// obligations.
- let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ let impl_m_def_id = impl_m.def_id.expect_local();
let cause = ObligationCause::new(
impl_m_span,
- impl_m_hir_id,
+ impl_m_def_id,
ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
+ impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
},
//
// We then register the obligations from the impl_m and check to see
// if all constraints hold.
- hybrid_preds
- .predicates
- .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates);
+ hybrid_preds.predicates.extend(
+ trait_m_predicates
+ .instantiate_own(tcx, trait_to_placeholder_substs)
+ .map(|(predicate, _)| predicate),
+ );
// Construct trait parameter environment and then shift it into the placeholder viewpoint.
// The key step here is to update the caller_bounds's predicates to be
// the new hybrid bounds we computed.
- let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_def_id);
let param_env = ty::ParamEnv::new(
tcx.intern_predicates(&hybrid_preds.predicates),
Reveal::UserFacing,
debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
- for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
- let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
+ for (predicate, span) in impl_m_own_bounds {
+ let normalize_cause = traits::ObligationCause::misc(span, impl_m_def_id);
let predicate = ocx.normalize(&normalize_cause, param_env, predicate);
let cause = ObligationCause::new(
span,
- impl_m_hir_id,
+ impl_m_def_id,
ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
+ impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
},
);
let unnormalized_impl_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);
ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
));
}
- let emit_implied_wf_lint = || {
- infcx.tcx.struct_span_lint_hir(
- rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
- impl_m_hir_id,
- infcx.tcx.def_span(impl_m.def_id),
- "impl method assumes more implied bounds than the corresponding trait method",
- |lint| lint,
- );
- };
// Check that all obligations are satisfied by the implementation's
// version.
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,
)
.map(|()| {
// If the skip-mode was successful, emit a lint.
- emit_implied_wf_lint();
+ emit_implied_wf_lint(infcx.tcx, impl_m, impl_m_hir_id, vec![]);
});
}
CheckImpliedWfMode::Skip => {
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,
)
.map(|()| {
+ let bad_args = extract_bad_args_for_implies_lint(
+ tcx,
+ &errors,
+ (trait_m, trait_sig),
+ // Unnormalized impl sig corresponds to the HIR types written
+ (impl_m, unnormalized_impl_sig),
+ impl_m_hir_id,
+ );
// If the skip-mode was successful, emit a lint.
- emit_implied_wf_lint();
+ emit_implied_wf_lint(tcx, impl_m, impl_m_hir_id, bad_args);
});
}
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
Ok(())
}
+fn extract_bad_args_for_implies_lint<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ errors: &[infer::RegionResolutionError<'tcx>],
+ (trait_m, trait_sig): (&ty::AssocItem, ty::FnSig<'tcx>),
+ (impl_m, impl_sig): (&ty::AssocItem, ty::FnSig<'tcx>),
+ hir_id: hir::HirId,
+) -> Vec<(Span, Option<String>)> {
+ let mut blame_generics = vec![];
+ for error in errors {
+ // Look for the subregion origin that contains an input/output type
+ let origin = match error {
+ infer::RegionResolutionError::ConcreteFailure(o, ..) => o,
+ infer::RegionResolutionError::GenericBoundFailure(o, ..) => o,
+ infer::RegionResolutionError::SubSupConflict(_, _, o, ..) => o,
+ infer::RegionResolutionError::UpperBoundUniverseConflict(.., o, _) => o,
+ };
+ // Extract (possible) input/output types from origin
+ match origin {
+ infer::SubregionOrigin::Subtype(trace) => {
+ if let Some((a, b)) = trace.values.ty() {
+ blame_generics.extend([a, b]);
+ }
+ }
+ infer::SubregionOrigin::RelateParamBound(_, ty, _) => blame_generics.push(*ty),
+ infer::SubregionOrigin::ReferenceOutlivesReferent(ty, _) => blame_generics.push(*ty),
+ _ => {}
+ }
+ }
+
+ let fn_decl = tcx.hir().fn_decl_by_hir_id(hir_id).unwrap();
+ let opt_ret_ty = match fn_decl.output {
+ hir::FnRetTy::DefaultReturn(_) => None,
+ hir::FnRetTy::Return(ty) => Some(ty),
+ };
+
+ // 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(),
+ )
+ .filter_map(|(impl_bv, trait_bv)| {
+ if let ty::BoundVariableKind::Region(impl_bv) = impl_bv
+ && let ty::BoundVariableKind::Region(trait_bv) = trait_bv
+ {
+ Some((impl_bv, trait_bv))
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ // For each arg, see if it was in the "blame" of any of the region errors.
+ // If so, then try to produce a suggestion to replace the argument type with
+ // one from the trait.
+ let mut bad_args = vec![];
+ for (idx, (ty, hir_ty)) in
+ std::iter::zip(impl_sig.inputs_and_output, fn_decl.inputs.iter().chain(opt_ret_ty))
+ .enumerate()
+ {
+ let expected_ty = trait_sig.inputs_and_output[idx]
+ .fold_with(&mut RemapLateBound { tcx, mapping: &mapping });
+ if blame_generics.iter().any(|blame| ty.contains(*blame)) {
+ let expected_ty_sugg = expected_ty.to_string();
+ bad_args.push((
+ hir_ty.span,
+ // Only suggest something if it actually changed.
+ (expected_ty_sugg != ty.to_string()).then_some(expected_ty_sugg),
+ ));
+ }
+ }
+
+ bad_args
+}
+
+struct RemapLateBound<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ mapping: &'a FxHashMap<ty::BoundRegionKind, ty::BoundRegionKind>,
+}
+
+impl<'tcx> TypeFolder<'tcx> for RemapLateBound<'_, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+ if let ty::ReFree(fr) = *r {
+ self.tcx.mk_region(ty::ReFree(ty::FreeRegion {
+ bound_region: self
+ .mapping
+ .get(&fr.bound_region)
+ .copied()
+ .unwrap_or(fr.bound_region),
+ ..fr
+ }))
+ } else {
+ r
+ }
+ }
+}
+
+fn emit_implied_wf_lint<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ hir_id: hir::HirId,
+ bad_args: Vec<(Span, Option<String>)>,
+) {
+ let span: MultiSpan = if bad_args.is_empty() {
+ tcx.def_span(impl_m.def_id).into()
+ } else {
+ bad_args.iter().map(|(span, _)| *span).collect::<Vec<_>>().into()
+ };
+ tcx.struct_span_lint_hir(
+ rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
+ hir_id,
+ span,
+ "impl method assumes more implied bounds than the corresponding trait method",
+ |lint| {
+ let bad_args: Vec<_> =
+ bad_args.into_iter().filter_map(|(span, sugg)| Some((span, sugg?))).collect();
+ if !bad_args.is_empty() {
+ lint.multipart_suggestion(
+ format!(
+ "replace {} type{} to make the impl signature compatible",
+ pluralize!("this", bad_args.len()),
+ pluralize!(bad_args.len())
+ ),
+ bad_args,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ lint
+ },
+ );
+}
+
#[derive(Debug, PartialEq, Eq)]
enum CheckImpliedWfMode {
/// Checks implied well-formedness of the impl method. If it fails, we will
) -> Result<&'tcx FxHashMap<DefId, Ty<'tcx>>, ErrorGuaranteed> {
let impl_m = tcx.opt_associated_item(def_id).unwrap();
let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
- let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap();
+ let impl_trait_ref =
+ tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap().subst_identity();
let param_env = tcx.param_env(def_id);
// First, check a few of the same things as `compare_impl_method`,
let trait_to_impl_substs = impl_trait_ref.substs;
- let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ let impl_m_def_id = impl_m.def_id.expect_local();
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m_def_id);
let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span();
let cause = ObligationCause::new(
return_span,
- impl_m_hir_id,
+ impl_m_def_id,
ObligationCauseCode::CompareImplItemObligation {
- impl_item_def_id: impl_m.def_id.expect_local(),
+ impl_item_def_id: impl_m_def_id,
trait_item_def_id: trait_m.def_id,
kind: impl_m.kind,
},
let ocx = ObligationCtxt::new(infcx);
// Normalize the impl signature with fresh variables for lifetime inference.
- let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
+ let norm_cause = ObligationCause::misc(return_span, impl_m_def_id);
let impl_sig = ocx.normalize(
&norm_cause,
param_env,
// 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,
let outlives_environment = OutlivesEnvironment::with_bounds(
param_env,
Some(infcx),
- infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ infcx.implied_bounds_tys(param_env, impl_m_def_id, wf_tys),
);
- infcx.err_ctxt().check_region_obligations_and_report_errors(
- impl_m.def_id.expect_local(),
- &outlives_environment,
- )?;
+ infcx
+ .err_ctxt()
+ .check_region_obligations_and_report_errors(impl_m_def_id, &outlives_environment)?;
let mut collected_tys = FxHashMap::default();
for (def_id, (ty, substs)) in collector.types {
match infcx.fully_resolve(ty) {
Ok(ty) => {
// `ty` contains free regions that we created earlier while liberating the
- // trait fn signature. However, projection normalization expects `ty` to
+ // trait fn signature. However, projection normalization expects `ty` to
// contains `def_id`'s early-bound regions.
let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
debug!(?id_substs, ?substs);
types: FxHashMap<DefId, (Ty<'tcx>, ty::SubstsRef<'tcx>)>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
}
impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> {
ocx: &'a ObligationCtxt<'a, 'tcx>,
span: Span,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
) -> Self {
ImplTraitInTraitCollector { ocx, types: FxHashMap::default(), span, param_env, body_id }
}
// When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
// span points only at the type `Box<Self`>, but we want to cover the whole
// argument pattern and type.
- let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, body) => tcx
- .hir()
- .body_param_names(body)
- .zip(sig.decl.inputs.iter())
- .map(|(param, ty)| param.span.to(ty.span))
- .next()
- .unwrap_or(impl_err_span),
- _ => bug!("{:?} is not a method", impl_m),
- };
+ let ImplItemKind::Fn(ref sig, body) = tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{impl_m:?} is not a method") };
+ let span = tcx
+ .hir()
+ .body_param_names(body)
+ .zip(sig.decl.inputs.iter())
+ .map(|(param, ty)| param.span.to(ty.span))
+ .next()
+ .unwrap_or(impl_err_span);
diag.span_suggestion(
span,
if trait_sig.inputs().len() == *i {
// Suggestion to change output type. We do not suggest in `async` functions
// to avoid complex logic or incorrect output.
- match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, _) if !sig.header.asyncness.is_async() => {
- let msg = "change the output type to match the trait";
- let ap = Applicability::MachineApplicable;
- match sig.decl.output {
- hir::FnRetTy::DefaultReturn(sp) => {
- let sugg = format!("-> {} ", trait_sig.output());
- diag.span_suggestion_verbose(sp, msg, sugg, ap);
- }
- hir::FnRetTy::Return(hir_ty) => {
- let sugg = trait_sig.output();
- diag.span_suggestion(hir_ty.span, msg, sugg, ap);
- }
- };
- }
- _ => {}
+ if let ImplItemKind::Fn(sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind
+ && !sig.header.asyncness.is_async()
+ {
+ let msg = "change the output type to match the trait";
+ let ap = Applicability::MachineApplicable;
+ match sig.decl.output {
+ hir::FnRetTy::DefaultReturn(sp) => {
+ let sugg = format!("-> {} ", trait_sig.output());
+ diag.span_suggestion_verbose(sp, msg, sugg, ap);
+ }
+ hir::FnRetTy::Return(hir_ty) => {
+ let sugg = trait_sig.output();
+ diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+ }
+ };
};
} else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
diag.span_suggestion(
// Must have same number of early-bound lifetime parameters.
// Unfortunately, if the user screws up the bounds, then this
- // will change classification between early and late. E.g.,
+ // will change classification between early and late. E.g.,
// if in trait we have `<'a,'b:'a>`, and in impl we just have
// `<'a,'b>`, then we have 2 early-bound lifetime parameters
// in trait but 0 in the impl. But if we report "expected 2
trait_m: &ty::AssocItem,
) -> (Span, Option<Span>) {
let tcx = infcx.tcx;
- let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref sig, _) => {
- sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
- }
- _ => bug!("{:?} is not a method", impl_m),
+ let mut impl_args = {
+ let ImplItemKind::Fn(sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{:?} is not a method", impl_m) };
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
};
- let trait_args =
- trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind {
- TraitItemKind::Fn(ref sig, _) => {
- sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
- }
- _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
- });
+
+ let trait_args = trait_m.def_id.as_local().map(|def_id| {
+ let TraitItemKind::Fn(sig, _) = &tcx.hir().expect_trait_item(def_id).kind else { bug!("{:?} is not a TraitItemKind::Fn", trait_m) };
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
+ });
match terr {
- TypeError::ArgumentMutability(i) => {
- (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
- }
- TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
+ TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
(impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
}
_ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
impl_trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), ErrorGuaranteed> {
// Try to give more informative error messages about self typing
- // mismatches. Note that any mismatch will also be detected
+ // mismatches. Note that any mismatch will also be detected
// below, where we construct a canonical function type that
- // includes the self parameter as a normal parameter. It's just
+ // includes the self parameter as a normal parameter. It's just
// that the error messages you get out of this code are a bit more
// inscrutable, particularly for cases where one method has no
// self.
} 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());
}
}
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();
+
if trait_number_args != impl_number_args {
- let trait_span = if let Some(def_id) = trait_m.def_id.as_local() {
- match tcx.hir().expect_trait_item(def_id).kind {
- TraitItemKind::Fn(ref trait_m_sig, _) => {
- let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 };
- if let Some(arg) = trait_m_sig.decl.inputs.get(pos) {
- Some(if pos == 0 {
- arg.span
- } else {
- arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
- })
- } else {
- trait_item_span
- }
- }
- _ => bug!("{:?} is not a method", impl_m),
- }
- } else {
- trait_item_span
- };
- let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
- ImplItemKind::Fn(ref impl_m_sig, _) => {
- let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 };
- if let Some(arg) = impl_m_sig.decl.inputs.get(pos) {
+ let trait_span = trait_m
+ .def_id
+ .as_local()
+ .and_then(|def_id| {
+ let TraitItemKind::Fn(trait_m_sig, _) = &tcx.hir().expect_trait_item(def_id).kind else { bug!("{:?} is not a method", impl_m) };
+ let pos = trait_number_args.saturating_sub(1);
+ trait_m_sig.decl.inputs.get(pos).map(|arg| {
if pos == 0 {
arg.span
} else {
- arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
+ arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
}
+ })
+ })
+ .or(trait_item_span);
+
+ let ImplItemKind::Fn(impl_m_sig, _) = &tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind else { bug!("{:?} is not a method", impl_m) };
+ let pos = impl_number_args.saturating_sub(1);
+ let impl_span = impl_m_sig
+ .decl
+ .inputs
+ .get(pos)
+ .map(|arg| {
+ if pos == 0 {
+ arg.span
} else {
- impl_m_span
+ arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
}
- }
- _ => bug!("{:?} is not a method", impl_m),
- };
+ })
+ .unwrap_or(impl_m_span);
+
let mut err = struct_span_err!(
tcx.sess,
impl_span,
tcx.def_path_str(trait_m.def_id),
trait_number_args
);
+
if let Some(trait_span) = trait_span {
err.span_label(
trait_span,
} else {
err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
}
+
err.span_label(
impl_span,
format!(
impl_number_args
),
);
- let reported = err.emit();
- return Err(reported);
+
+ return Err(err.emit());
}
Ok(())
// explicit generics
(true, false) => {
err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
- (|| {
+ let _: Option<_> = try {
// try taking the name from the trait impl
// FIXME: this is obviously suboptimal since the name can already be used
// as another generic argument
],
Applicability::MaybeIncorrect,
);
- Some(())
- })();
+ };
}
// The case where the trait method uses `impl Trait`, but the impl method uses
// explicit generics.
(false, true) => {
err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
- (|| {
+ let _: Option<_> = try {
let impl_m = impl_m.def_id.as_local()?;
let impl_m = tcx.hir().expect_impl_item(impl_m);
- let input_tys = match impl_m.kind {
- hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
- _ => unreachable!(),
- };
+ let hir::ImplItemKind::Fn(sig, _) = &impl_m.kind else { unreachable!() };
+ let input_tys = sig.decl.inputs;
+
struct Visitor(Option<Span>, hir::def_id::LocalDefId);
impl<'v> intravisit::Visitor<'v> for Visitor {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
intravisit::walk_ty(self, ty);
- if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
- ty.kind
+ if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = ty.kind
&& let Res::Def(DefKind::TyParam, def_id) = path.res
&& def_id == self.1.to_def_id()
{
}
}
}
+
let mut visitor = Visitor(None, impl_def_id);
for ty in input_tys {
intravisit::Visitor::visit_ty(&mut visitor, ty);
],
Applicability::MaybeIncorrect,
);
- Some(())
- })();
+ };
}
_ => unreachable!(),
}
- let reported = err.emit();
- error_found = Some(reported);
+ error_found = Some(err.emit());
}
}
if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
) -> Result<(), ErrorGuaranteed> {
let impl_const_item = tcx.associated_item(impl_const_item_def);
let trait_const_item = tcx.associated_item(trait_const_item_def);
- let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap();
+ let impl_trait_ref =
+ tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().subst_identity();
debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
// Create a parameter environment that represents the implementation's
// method.
- let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def);
-
// Compute placeholder form of impl and trait const tys.
let impl_ty = tcx.type_of(impl_const_item_def.to_def_id());
let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs);
let mut cause = ObligationCause::new(
impl_c_span,
- impl_c_hir_id,
+ impl_const_item_def,
ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id: impl_const_item_def,
trait_item_def_id: trait_const_item_def,
);
// Locate the Span containing just the type of the offending impl
- match tcx.hir().expect_impl_item(impl_const_item_def).kind {
- ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
- _ => bug!("{:?} is not a impl const", impl_const_item),
- }
+ let ImplItemKind::Const(ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).kind else { bug!("{impl_const_item:?} is not a impl const") };
+ cause.span = ty.span;
let mut diag = struct_span_err!(
tcx.sess,
let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
// Add a label to the Span containing just the type of the const
- match tcx.hir().expect_trait_item(trait_c_def_id).kind {
- TraitItemKind::Const(ref ty, _) => ty.span,
- _ => bug!("{:?} is not a trait const", trait_const_item),
- }
+ let TraitItemKind::Const(ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).kind else { bug!("{trait_const_item:?} is not a trait const") };
+ ty.span
});
infcx.err_ctxt().note_type_err(
) {
debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
- let _: Result<(), ErrorGuaranteed> = (|| {
+ let _: Result<(), ErrorGuaranteed> = try {
compare_number_of_generics(tcx, impl_ty, trait_ty, trait_item_span, false)?;
compare_generic_param_kinds(tcx, impl_ty, trait_ty, false)?;
let sp = tcx.def_span(impl_ty.def_id);
compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
- check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
- })();
+ check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)?;
+ };
}
/// The equivalent of [compare_method_predicate_entailment], but for associated types
check_region_bounds_on_impl_item(tcx, impl_ty, trait_ty, false)?;
let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
-
- if impl_ty_own_bounds.is_empty() {
+ if impl_ty_own_bounds.len() == 0 {
// Nothing to check.
return Ok(());
}
// This `HirId` should be used for the `body_id` field on each
// `ObligationCause` (and the `FnCtxt`). This is what
// `regionck_item` expects.
- let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ let impl_ty_def_id = impl_ty.def_id.expect_local();
debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
// The predicates declared by the impl definition, the trait and the
// associated type in the trait are assumed.
let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
- hybrid_preds
- .predicates
- .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates);
+ hybrid_preds.predicates.extend(
+ trait_ty_predicates
+ .instantiate_own(tcx, trait_to_impl_substs)
+ .map(|(predicate, _)| predicate),
+ );
debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
- let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
+ let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
let param_env = ty::ParamEnv::new(
tcx.intern_predicates(&hybrid_preds.predicates),
Reveal::UserFacing,
debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
- assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
- for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
- {
- let cause = ObligationCause::misc(span, impl_ty_hir_id);
+ for (predicate, span) in impl_ty_own_bounds {
+ let cause = ObligationCause::misc(span, impl_ty_def_id);
let predicate = ocx.normalize(&cause, param_env, predicate);
let cause = ObligationCause::new(
span,
- impl_ty_hir_id,
+ impl_ty_def_id,
ObligationCauseCode::CompareImplItemObligation {
impl_item_def_id: impl_ty.def_id.expect_local(),
trait_item_def_id: trait_ty.def_id,
};
debug!(?normalize_param_env);
- let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ let impl_ty_def_id = impl_ty.def_id.expect_local();
let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
let normalize_cause = ObligationCause::new(
impl_ty_span,
- impl_ty_hir_id,
+ impl_ty_def_id,
ObligationCauseCode::CheckAssociatedTypeBounds {
impl_item_def_id: impl_ty.def_id.expect_local(),
trait_item_def_id: trait_ty.def_id,
} else {
traits::BindingObligation(trait_ty.def_id, span)
};
- ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
+ ObligationCause::new(impl_ty_span, impl_ty_def_id, code)
};
let obligations = tcx
// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
- let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types);
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_def_id, assumed_wf_types);
let outlives_environment =
OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds);
)
}
_ => {
- // Destructors only work on nominal types. This was
+ // Destructors only work on nominal types. This was
// already checked by coherence, but compilation may
// not have been terminated.
let span = tcx.def_span(drop_impl_did);
&& gen_count_ok(own_counts.consts, 0, "const")
{
let fty = tcx.mk_fn_ptr(sig);
- let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType);
+ 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)), 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,
- main: the main pass does the lion's share of the work: it
determines the types of all expressions, resolves
- methods, checks for most invalid conditions, and so forth. In
+ methods, checks for most invalid conditions, and so forth. In
some cases, where a type is unknown, it may create a type or region
variable and use that as the type of an expression.
In the process of checking, various constraints will be placed on
these type variables through the subtyping relationships requested
- through the `demand` module. The `infer` module is in charge
+ through the `demand` module. The `infer` module is in charge
of resolving those constraints.
- regionck: after main is complete, the regionck pass goes over all
types looking for regions and making sure that they did not escape
- into places where they are not in scope. This may also influence the
+ into places where they are not in scope. This may also influence the
final assignments of the various region variables if there is some
flexibility.
- writeback: writes the final types within a function body, replacing
- type variables with their final inferred types. These final types
+ type variables with their final inferred types. These final types
are written into the `tcx.node_types` table, which should *never* contain
any reference to a type variable.
While type checking a function, the intermediate types for the
expressions, blocks, and so forth contained within the function are
-stored in `fcx.node_types` and `fcx.node_substs`. These types
-may contain unresolved type variables. After type checking is
+stored in `fcx.node_types` and `fcx.node_substs`. These types
+may contain unresolved type variables. After type checking is
complete, the functions in the writeback module are used to take the
types from this table, resolve them, and then write them into their
permanent home in the type context `tcx`.
The types of top-level items, which never contain unbound type
variables, are stored directly into the `tcx` typeck_results.
-N.B., a type variable is not the same thing as a type parameter. A
+N.B., a type variable is not the same thing as a type parameter. A
type variable is an instance of a type parameter. That is,
given a generic function `fn foo<T>(t: T)`, while checking the
function `foo`, the type `ty_param(0)` refers to the type `T`, which
is treated in abstract. However, when `foo()` is called, `T` will be
-substituted for a fresh type variable `N`. This variable will
+substituted for a fresh type variable `N`. This variable will
eventually be resolved to some concrete type (which might itself be
a type parameter).
ty::AssocKind::Fn => {
// We skip the binder here because the binder would deanonymize all
// late-bound regions, and we don't want method signatures to show up
- // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
+ // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
fn_sig_suggestion(
tcx,
visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
- if let Some(hir::Guard::If(ref expr)) = arm.guard {
+ if let Some(hir::Guard::If(expr)) = arm.guard {
visitor.terminating_scopes.insert(expr.hir_id.local_id);
}
// This ensures fixed size stacks.
hir::ExprKind::Binary(
source_map::Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
- ref l,
- ref r,
+ l,
+ r,
) => {
// expr is a short circuiting operator (|| or &&). As its
// functionality can't be overridden by traits, it always
terminating(r.hir_id.local_id);
}
}
- hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
+ hir::ExprKind::If(_, then, Some(otherwise)) => {
terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id);
}
- hir::ExprKind::If(_, ref then, None) => {
+ hir::ExprKind::If(_, then, None) => {
terminating(then.hir_id.local_id);
}
- hir::ExprKind::Loop(ref body, _, _, _) => {
+ hir::ExprKind::Loop(body, _, _, _) => {
terminating(body.hir_id.local_id);
}
- hir::ExprKind::DropTemps(ref expr) => {
+ hir::ExprKind::DropTemps(expr) => {
// `DropTemps(expr)` does not denote a conditional scope.
// Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`.
terminating(expr.hir_id.local_id);
// The idea is that call.callee_id represents *the time when
// the invoked function is actually running* and call.id
// represents *the time to prepare the arguments and make the
- // call*. See the section "Borrows in Calls" borrowck/README.md
+ // call*. See the section "Borrows in Calls" borrowck/README.md
// for an extended explanation of why this distinction is
// important.
//
let body = visitor.tcx.hir().body(body);
visitor.visit_body(body);
}
- hir::ExprKind::AssignOp(_, ref left_expr, ref right_expr) => {
+ hir::ExprKind::AssignOp(_, left_expr, right_expr) => {
debug!(
"resolve_expr - enabling pessimistic_yield, was previously {}",
prev_pessimistic
}
}
- hir::ExprKind::If(ref cond, ref then, Some(ref otherwise)) => {
+ hir::ExprKind::If(cond, then, Some(otherwise)) => {
let expr_cx = visitor.cx;
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
visitor.cx.var_parent = visitor.cx.parent;
visitor.visit_expr(otherwise);
}
- hir::ExprKind::If(ref cond, ref then, None) => {
+ hir::ExprKind::If(cond, then, None) => {
let expr_cx = visitor.cx;
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
visitor.cx.var_parent = visitor.cx.parent;
match pat.kind {
PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true,
- PatKind::Struct(_, ref field_pats, _) => {
+ PatKind::Struct(_, field_pats, _) => {
field_pats.iter().any(|fp| is_binding_pat(&fp.pat))
}
- PatKind::Slice(ref pats1, ref pats2, ref pats3) => {
+ PatKind::Slice(pats1, pats2, pats3) => {
pats1.iter().any(|p| is_binding_pat(&p))
|| pats2.iter().any(|p| is_binding_pat(&p))
|| pats3.iter().any(|p| is_binding_pat(&p))
}
- PatKind::Or(ref subpats)
- | PatKind::TupleStruct(_, ref subpats, _)
- | PatKind::Tuple(ref subpats, _) => subpats.iter().any(|p| is_binding_pat(&p)),
+ PatKind::Or(subpats)
+ | PatKind::TupleStruct(_, subpats, _)
+ | PatKind::Tuple(subpats, _) => subpats.iter().any(|p| is_binding_pat(&p)),
- PatKind::Box(ref subpat) => is_binding_pat(&subpat),
+ PatKind::Box(subpat) => is_binding_pat(&subpat),
PatKind::Ref(_, _)
| PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id);
}
}
- hir::ExprKind::Cast(ref subexpr, _) => {
+ hir::ExprKind::Cast(subexpr, _) => {
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id)
}
- hir::ExprKind::Block(ref block, _) => {
- if let Some(ref subexpr) = block.expr {
+ hir::ExprKind::Block(block, _) => {
+ if let Some(subexpr) = block.expr {
record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id);
}
}
};
use std::cell::LazyCell;
-use std::iter;
use std::ops::{ControlFlow, Deref};
pub(super) struct WfCheckingCtxt<'a, 'tcx> {
pub(super) ocx: ObligationCtxt<'a, 'tcx>,
span: Span,
- body_id: hir::HirId,
+ body_def_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
}
impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> {
T: TypeFoldable<'tcx>,
{
self.ocx.normalize(
- &ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)),
+ &ObligationCause::new(span, self.body_def_id, ObligationCauseCode::WellFormed(loc)),
self.param_env,
value,
)
loc: Option<WellFormedLoc>,
arg: ty::GenericArg<'tcx>,
) {
- let cause =
- traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc));
+ let cause = traits::ObligationCause::new(
+ span,
+ self.body_def_id,
+ ObligationCauseCode::WellFormed(loc),
+ );
// for a type to be WF, we do not need to check if const trait predicates satisfy.
let param_env = self.param_env.without_const();
self.ocx.register_obligation(traits::Obligation::new(
F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>),
{
let param_env = tcx.param_env(body_def_id);
- let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
- let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+ let mut wfcx = WfCheckingCtxt { ocx, span, body_def_id, param_env };
if !tcx.features().trivial_bounds {
wfcx.check_false_global_bounds()
f(&mut wfcx);
let assumed_wf_types = wfcx.ocx.assumed_wf_types(param_env, span, body_def_id);
- let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
+ let implied_bounds = infcx.implied_bounds_tys(param_env, body_def_id, assumed_wf_types);
let errors = wfcx.select_all_or_error();
if !errors.is_empty() {
//
// won't be allowed unless there's an *explicit* implementation of `Send`
// for `T`
- hir::ItemKind::Impl(ref impl_) => {
+ hir::ItemKind::Impl(impl_) => {
let is_auto = tcx
.impl_trait_ref(def_id)
- .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
+ .map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.skip_binder().def_id));
if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
let mut err =
hir::ItemKind::Const(ty, ..) => {
check_item_type(tcx, def_id, ty.span, false);
}
- hir::ItemKind::Struct(_, ref ast_generics) => {
+ hir::ItemKind::Struct(_, ast_generics) => {
check_type_defn(tcx, item, false);
check_variances_for_type_defn(tcx, item, ast_generics);
}
- hir::ItemKind::Union(_, ref ast_generics) => {
+ hir::ItemKind::Union(_, ast_generics) => {
check_type_defn(tcx, item, true);
check_variances_for_type_defn(tcx, item, ast_generics);
}
- hir::ItemKind::Enum(_, ref ast_generics) => {
+ hir::ItemKind::Enum(_, ast_generics) => {
check_type_defn(tcx, item, true);
check_variances_for_type_defn(tcx, item, ast_generics);
}
continue;
}
- let item_hir_id = item.id.hir_id();
let param_env = tcx.param_env(item_def_id);
let item_required_bounds = match item.kind {
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_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,
)
wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(item_id)), item_ty.into());
if forbid_unsized {
wfcx.register_bound(
- traits::ObligationCause::new(ty_span, wfcx.body_id, traits::WellFormed(None)),
+ traits::ObligationCause::new(ty_span, wfcx.body_def_id, traits::WellFormed(None)),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::Sized, None),
if should_check_for_sync {
wfcx.register_bound(
- traits::ObligationCause::new(ty_span, wfcx.body_id, traits::SharedStatic),
+ traits::ObligationCause::new(ty_span, wfcx.body_def_id, traits::SharedStatic),
wfcx.param_env,
item_ty,
tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
constness: hir::Constness,
) {
enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
- match *ast_trait_ref {
- Some(ref ast_trait_ref) => {
+ match ast_trait_ref {
+ Some(ast_trait_ref) => {
// `#[rustc_reservation_impl]` impls are not real impls and
// therefore don't need to be WF (the trait's `Self: Trait` predicate
// won't hold).
- let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap();
- let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref);
+ let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap().subst_identity();
+ let trait_ref = wfcx.normalize(
+ ast_trait_ref.path.span,
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
+ trait_ref,
+ );
let trait_pred = ty::TraitPredicate {
trait_ref,
constness: match constness {
},
polarity: ty::ImplPolarity::Positive,
};
- let obligations = traits::wf::trait_obligations(
+ 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,
);
+ for obligation in &mut obligations {
+ if let Some(pred) = obligation.predicate.to_opt_poly_trait_pred()
+ && pred.self_ty().skip_binder() == trait_ref.self_ty()
+ {
+ obligation.cause.span = ast_self_ty.span;
+ }
+ }
debug!(?obligations);
wfcx.register_obligations(obligations);
}
let infcx = wfcx.infcx;
let tcx = wfcx.tcx();
- let predicates = tcx.bound_predicates_of(def_id.to_def_id());
+ let predicates = tcx.predicates_of(def_id.to_def_id());
let generics = tcx.generics_of(def_id);
let is_our_default = |def: &ty::GenericParamDef| match def.kind {
// is incorrect when dealing with unused substs, for example
// for `struct Foo<const N: usize, const M: usize = { 1 - 2 }>`
// we should eagerly error.
- let default_ct = tcx.const_param_default(param.def_id);
+ let default_ct = tcx.const_param_default(param.def_id).subst_identity();
if !default_ct.needs_subst() {
wfcx.register_wf_obligation(
tcx.def_span(param.def_id),
GenericParamDefKind::Const { .. } => {
// If the param has a default, ...
if is_our_default(param) {
- let default_ct = tcx.const_param_default(param.def_id);
+ let default_ct = tcx.const_param_default(param.def_id).subst_identity();
// ... and it's not a dependent default, ...
if !default_ct.needs_subst() {
// ... then substitute it with the default.
// Now we build the substituted predicates.
let default_obligations = predicates
- .0
.predicates
.iter()
.flat_map(|&(pred, sp)| {
}
fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::BREAK
+ ControlFlow::Break(())
}
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
}
let mut param_count = CountParams::default();
let has_region = pred.visit_with(&mut param_count).is_break();
- let substituted_pred = predicates.rebind(pred).subst(tcx, substs);
+ let substituted_pred = ty::EarlyBinder(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region
{
None
- } else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) {
+ } else if predicates.predicates.iter().any(|&(p, _)| p == substituted_pred) {
// Avoid duplication of predicates that contain no parameters, for example.
None
} else {
let pred = wfcx.normalize(sp, None, pred);
let cause = traits::ObligationCause::new(
sp,
- wfcx.body_id,
+ wfcx.body_def_id,
traits::ItemObligation(def_id.to_def_id()),
);
traits::Obligation::new(tcx, cause, wfcx.param_env, pred)
});
- let predicates = predicates.0.instantiate_identity(tcx);
+ let predicates = predicates.instantiate_identity(tcx);
let predicates = wfcx.normalize(span, None, predicates);
debug!(?predicates.predicates);
assert_eq!(predicates.predicates.len(), predicates.spans.len());
- let wf_obligations =
- iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| {
- traits::wf::predicate_obligations(
- infcx,
- wfcx.param_env.without_const(),
- wfcx.body_id,
- p,
- sp,
- )
- });
-
+ let wf_obligations = predicates.into_iter().flat_map(|(p, sp)| {
+ traits::wf::predicate_obligations(
+ infcx,
+ wfcx.param_env.without_const(),
+ wfcx.body_def_id,
+ p,
+ sp,
+ )
+ });
let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
wfcx.register_obligations(obligations);
}
// Check that the argument is a tuple
if let Some(ty) = inputs.next() {
wfcx.register_bound(
- ObligationCause::new(span, wfcx.body_id, ObligationCauseCode::RustCall),
+ ObligationCause::new(span, wfcx.body_def_id, ObligationCauseCode::RustCall),
wfcx.param_env,
*ty,
tcx.require_lang_item(hir::LangItem::Tuple, Some(span)),
traits::wf::predicate_obligations(
wfcx.infcx,
wfcx.param_env,
- wfcx.body_id,
+ wfcx.body_def_id,
normalized_bound,
bound_span,
)
let infcx = wfcx.infcx;
let tcx = wfcx.tcx();
let cause =
- ObligationCause::new(span, wfcx.body_id, traits::ObligationCauseCode::MethodReceiver);
+ ObligationCause::new(span, wfcx.body_def_id, traits::ObligationCauseCode::MethodReceiver);
let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty).is_ok();
return true;
}
- let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty);
+ let mut autoderef = Autoderef::new(infcx, wfcx.param_env, wfcx.body_def_id, span, receiver_ty);
// The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`.
if arbitrary_self_types_enabled {
let mut span = self.span;
let empty_env = ty::ParamEnv::empty();
- let def_id = tcx.hir().local_def_id(self.body_id);
- let predicates_with_span = tcx.predicates_of(def_id).predicates.iter().copied();
+ let predicates_with_span = tcx.predicates_of(self.body_def_id).predicates.iter().copied();
// Check elaborated bounds.
let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span);
// Match the existing behavior.
if pred.is_global() && !pred.has_late_bound_vars() {
let pred = self.normalize(span, None, pred);
- let hir_node = tcx.hir().find(self.body_id);
+ let hir_node = tcx.hir().find_by_def_id(self.body_def_id);
// only use the span of the predicate clause (#90869)
let obligation = traits::Obligation::new(
tcx,
- traits::ObligationCause::new(span, self.body_id, traits::TrivialBound),
+ traits::ObligationCause::new(span, self.body_def_id, traits::TrivialBound),
empty_env,
pred,
);
fn unused_crates_lint(tcx: TyCtxt<'_>) {
let lint = lint::builtin::UNUSED_EXTERN_CRATES;
- // Collect first the crates that are completely unused. These we
+ // Collect first the crates that are completely unused. These we
// can always suggest removing (no matter which edition we are
// in).
let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::ItemKind;
-use rustc_infer::infer;
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{self, RegionResolutionError};
use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
-use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
+use rustc_trait_selection::traits::misc::{
+ type_allowed_to_implement_copy, CopyImplementationError, InfringingFieldsReason,
+};
use rustc_trait_selection::traits::predicate_for_trait_def;
use rustc_trait_selection::traits::{self, ObligationCause};
use std::collections::BTreeMap;
_ => {}
}
- let sp = match tcx.hir().expect_item(impl_did).kind {
- ItemKind::Impl(ref impl_) => impl_.self_ty.span,
- _ => bug!("expected Drop impl item"),
- };
+ let ItemKind::Impl(impl_) = tcx.hir().expect_item(impl_did).kind else { bug!("expected Drop impl item") };
- tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
+ tcx.sess.emit_err(DropImplOnWrongItem { span: impl_.self_ty.span });
}
fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
-
let self_type = tcx.type_of(impl_did);
debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
_ => bug!("expected Copy impl item"),
};
- let cause = traits::ObligationCause::misc(span, impl_hir_id);
- match can_type_implement_copy(tcx, param_env, self_type, cause) {
+ let cause = traits::ObligationCause::misc(span, impl_did);
+ match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
Ok(()) => {}
Err(CopyImplementationError::InfrigingFields(fields)) => {
let mut err = struct_span_err!(
let mut errors: BTreeMap<_, Vec<_>> = Default::default();
let mut bounds = vec![];
- for (field, ty) in fields {
+ for (field, ty, reason) in fields {
let field_span = tcx.def_span(field.did);
- let field_ty_span = match tcx.hir().get_if_local(field.did) {
- Some(hir::Node::Field(field_def)) => field_def.ty.span,
- _ => field_span,
- };
err.span_label(field_span, "this field does not implement `Copy`");
- // Spin up a new FulfillmentContext, so we can get the _precise_ reason
- // why this field does not implement Copy. This is useful because sometimes
- // it is not immediately clear why Copy is not implemented for a field, since
- // all we point at is the field itself.
- let infcx = tcx.infer_ctxt().ignoring_regions().build();
- for error in traits::fully_solve_bound(
- &infcx,
- traits::ObligationCause::dummy_with_span(field_ty_span),
- param_env,
- ty,
- tcx.require_lang_item(LangItem::Copy, Some(span)),
- ) {
- let error_predicate = error.obligation.predicate;
- // Only note if it's not the root obligation, otherwise it's trivial and
- // should be self-explanatory (i.e. a field literally doesn't implement Copy).
-
- // FIXME: This error could be more descriptive, especially if the error_predicate
- // contains a foreign type or if it's a deeply nested type...
- if error_predicate != error.root_obligation.predicate {
- errors
- .entry((ty.to_string(), error_predicate.to_string()))
- .or_default()
- .push(error.obligation.cause.span);
+
+ match reason {
+ InfringingFieldsReason::Fulfill(fulfillment_errors) => {
+ for error in fulfillment_errors {
+ let error_predicate = error.obligation.predicate;
+ // Only note if it's not the root obligation, otherwise it's trivial and
+ // should be self-explanatory (i.e. a field literally doesn't implement Copy).
+
+ // FIXME: This error could be more descriptive, especially if the error_predicate
+ // contains a foreign type or if it's a deeply nested type...
+ if error_predicate != error.root_obligation.predicate {
+ errors
+ .entry((ty.to_string(), error_predicate.to_string()))
+ .or_default()
+ .push(error.obligation.cause.span);
+ }
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(
+ ty::TraitPredicate {
+ trait_ref,
+ polarity: ty::ImplPolarity::Positive,
+ ..
+ },
+ )) = error_predicate.kind().skip_binder()
+ {
+ let ty = trait_ref.self_ty();
+ if let ty::Param(_) = ty.kind() {
+ bounds.push((
+ format!("{ty}"),
+ trait_ref.print_only_trait_path().to_string(),
+ Some(trait_ref.def_id),
+ ));
+ }
+ }
+ }
}
- if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
- trait_ref,
- polarity: ty::ImplPolarity::Positive,
- ..
- })) = error_predicate.kind().skip_binder()
- {
- let ty = trait_ref.self_ty();
- if let ty::Param(_) = ty.kind() {
- bounds.push((
- format!("{ty}"),
- trait_ref.print_only_trait_path().to_string(),
- Some(trait_ref.def_id),
- ));
+ InfringingFieldsReason::Regions(region_errors) => {
+ for error in region_errors {
+ let ty = ty.to_string();
+ match error {
+ RegionResolutionError::ConcreteFailure(origin, a, b) => {
+ let predicate = format!("{b}: {a}");
+ errors
+ .entry((ty.clone(), predicate.clone()))
+ .or_default()
+ .push(origin.span());
+ if let ty::RegionKind::ReEarlyBound(ebr) = *b && ebr.has_name() {
+ bounds.push((b.to_string(), a.to_string(), None));
+ }
+ }
+ RegionResolutionError::GenericBoundFailure(origin, a, b) => {
+ let predicate = format!("{a}: {b}");
+ errors
+ .entry((ty.clone(), predicate.clone()))
+ .or_default()
+ .push(origin.span());
+ if let infer::region_constraints::GenericKind::Param(_) = a {
+ bounds.push((a.to_string(), b.to_string(), None));
+ }
+ }
+ _ => continue,
+ }
}
}
}
let source = tcx.type_of(impl_did);
assert!(!source.has_escaping_bound_vars());
let target = {
- let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity();
assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
trait_ref.substs.type_at(1)
let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
let infcx = tcx.infer_ctxt().build();
- let cause = ObligationCause::misc(span, impl_hir_id);
+ let cause = ObligationCause::misc(span, impl_did);
use rustc_type_ir::sty::TyKind::*;
match (source.kind(), target.kind()) {
});
let source = tcx.type_of(impl_did);
- let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().subst_identity();
assert_eq!(trait_ref.def_id, coerce_unsized_trait);
let target = trait_ref.substs.type_at(1);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
let infcx = tcx.infer_ctxt().build();
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
- let cause = ObligationCause::misc(span, impl_hir_id);
+ let cause = ObligationCause::misc(span, impl_did);
let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
mt_b: ty::TypeAndMut<'tcx>,
mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
// when this coercion occurs, we would be changing the
// field `ptr` from a thin pointer of type `*mut [i32;
// 3]` to a fat pointer of type `*mut [i32]` (with
- // extra data `3`). **The purpose of this check is to
+ // extra data `3`). **The purpose of this check is to
// make sure that we know how to do this conversion.**
//
// To check if this impl is legal, we would walk down
return err_info;
} else if diff_fields.len() > 1 {
let item = tcx.hir().expect_item(impl_did);
- let span =
- if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = item.kind {
- t.path.span
- } else {
- tcx.def_span(impl_did)
- };
+ let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(t), .. }) = &item.kind {
+ t.path.span
+ } else {
+ tcx.def_span(impl_did)
+ };
struct_span_err!(
tcx.sess,
};
// Register an obligation for `A: Trait<B>`.
- let cause = traits::ObligationCause::misc(span, impl_hir_id);
+ let cause = traits::ObligationCause::misc(span, impl_did);
let predicate =
predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, [source, target]);
let errors = traits::fully_solve_obligation(&infcx, predicate);
}
let item = self.tcx.hir().item(id);
- let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else {
+ let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, items, .. }) = item.kind else {
return;
};
let impls = tcx.hir().trait_impls(def_id);
for &impl_def_id in impls {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
check_impl(tcx, impl_def_id, trait_ref);
check_object_overlap(tcx, impl_def_id, trait_ref);
for component_def_id in component_def_ids {
if !tcx.is_object_safe(component_def_id) {
// Without the 'object_safe_for_dispatch' feature this is an error
- // which will be reported by wfcheck. Ignore it here.
+ // which will be reported by wfcheck. Ignore it here.
// This is tested by `coherence-impl-trait-for-trait-object-safe.rs`.
// With the feature enabled, the trait is not implemented automatically,
// so this is valid.
tcx: TyCtxt<'_>,
impl_def_id: LocalDefId,
) -> Result<(), ErrorGuaranteed> {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
trait_ref.error_reported()?;
let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
let trait_def_id = trait_ref.def_id;
let item = tcx.hir().expect_item(def_id);
- let hir::ItemKind::Impl(ref impl_) = item.kind else {
+ let hir::ItemKind::Impl(impl_) = item.kind else {
bug!("{:?} is not an impl: {:?}", def_id, item);
};
let sp = tcx.def_span(def_id);
if t != self.self_ty_root {
for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) {
match tcx.impl_polarity(impl_def_id) {
- ImplPolarity::Negative => return ControlFlow::BREAK,
+ ImplPolarity::Negative => return ControlFlow::Break(()),
ImplPolarity::Reservation => {}
// FIXME(@lcnr): That's probably not good enough, idk
//
// We might just want to take the rustdoc code and somehow avoid
// explicit impls for `Self`.
- ImplPolarity::Positive => return ControlFlow::CONTINUE,
+ ImplPolarity::Positive => return ControlFlow::Continue(()),
}
}
}
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => t.super_visit_with(self),
}
pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
let item = tcx.hir().expect_item(def_id);
- let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() };
+ let hir::ItemKind::Impl(impl_) = item.kind else { bug!() };
if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) {
+ let trait_ref = trait_ref.subst_identity();
let trait_def = tcx.trait_def(trait_ref.def_id);
let unsafe_attr =
impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
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,
}
}
+// FIXME(vincenzopalazzo): remove the hir item when the refactoring is stable
fn suggest_impl_trait<'tcx>(
tcx: TyCtxt<'tcx>,
ret_ty: Ty<'tcx>,
span: Span,
- hir_id: hir::HirId,
+ _hir_id: hir::HirId,
def_id: LocalDefId,
) -> Option<String> {
let format_as_assoc: fn(_, _, _, _, _) -> _ =
}
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
let item_ty = ocx.normalize(
- &ObligationCause::misc(span, hir_id),
+ &ObligationCause::misc(span, def_id),
param_env,
tcx.mk_projection(assoc_item_def_id, substs),
);
None
}
-fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
+fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::EarlyBinder<ty::TraitRef<'_>>> {
let icx = ItemCtxt::new(tcx, def_id);
let item = tcx.hir().expect_item(def_id.expect_local());
- match item.kind {
- hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
+ let hir::ItemKind::Impl(impl_) = item.kind else { bug!() };
+ impl_
+ .of_trait
+ .as_ref()
+ .map(|ast_trait_ref| {
let selfty = tcx.type_of(def_id);
icx.astconv().instantiate_mono_trait_ref(
ast_trait_ref,
selfty,
check_impl_constness(tcx, impl_.constness, ast_trait_ref),
)
- }),
- _ => bug!(),
- }
+ })
+ .map(ty::EarlyBinder)
}
fn check_impl_constness(
for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
check(input, *ty)
}
- if let hir::FnRetTy::Return(ref ty) = decl.output {
+ if let hir::FnRetTy::Return(ty) = decl.output {
check(ty, fty.output().skip_binder())
}
}
_ => bug!("generator_kind applied to non-local def-id {:?}", def_id),
}
}
+
+fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+ match tcx.hir().get_if_local(def_id) {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. })) => {
+ matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias)
+ }
+ Some(_) => bug!("tried getting opaque_ty_origin for non-opaque: {:?}", def_id),
+ _ => bug!("tried getting opaque_ty_origin for non-local def-id {:?}", def_id),
+ }
+}
GenericParamKind, HirId, Node,
};
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint;
// 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())
Some(tcx.typeck_root_def_id(def_id))
}
Node::Item(item) => match item.kind {
- ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
+ ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin:
+ hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
+ in_trait,
+ ..
+ }) => {
+ if in_trait {
+ assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn))
+ } else {
+ assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn))
+ }
+ Some(fn_def_id.to_def_id())
+ }
+ ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
let parent_id = tcx.hir().get_parent_item(hir_id);
assert_ne!(parent_id, hir::CRATE_OWNER_ID);
debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);
params.extend(ast_generics.params.iter().filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => None,
- GenericParamKind::Type { ref default, synthetic, .. } => {
+ GenericParamKind::Type { default, synthetic, .. } => {
if default.is_some() {
match allow_defaults {
Defaults::Allowed => {}
}
match node {
- Node::TraitItem(item) => match item.kind {
- hir::TraitItemKind::Fn(ref sig, _) => {
- has_late_bound_regions(tcx, &item.generics, sig.decl)
- }
+ Node::TraitItem(item) => match &item.kind {
+ hir::TraitItemKind::Fn(sig, _) => has_late_bound_regions(tcx, &item.generics, sig.decl),
_ => None,
},
- Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::Fn(ref sig, _) => {
- has_late_bound_regions(tcx, &item.generics, sig.decl)
- }
+ Node::ImplItem(item) => match &item.kind {
+ hir::ImplItemKind::Fn(sig, _) => has_late_bound_regions(tcx, &item.generics, sig.decl),
_ => None,
},
Node::ForeignItem(item) => match item.kind {
- hir::ForeignItemKind::Fn(fn_decl, _, ref generics) => {
+ hir::ForeignItemKind::Fn(fn_decl, _, generics) => {
has_late_bound_regions(tcx, generics, fn_decl)
}
_ => None,
},
- Node::Item(item) => match item.kind {
- hir::ItemKind::Fn(ref sig, .., ref generics, _) => {
+ Node::Item(item) => match &item.kind {
+ hir::ItemKind::Fn(sig, .., generics, _) => {
has_late_bound_regions(tcx, generics, sig.decl)
}
_ => None,
}
}
-pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List<ty::Predicate<'_>> {
- tcx.mk_predicates(
+pub(super) fn item_bounds(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+) -> ty::EarlyBinder<&'_ ty::List<ty::Predicate<'_>>> {
+ let bounds = tcx.mk_predicates(
util::elaborate_predicates(
tcx,
tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound),
)
.map(|obligation| obligation.predicate),
- )
+ );
+ ty::EarlyBinder(bounds)
}
//! Resolution of early vs late bound lifetimes.
//!
-//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this
+//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this
//! information, typechecking needs to transform the lifetime parameters into bound lifetimes.
-//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit
-//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
+//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit
+//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
use rustc_ast::walk_list;
/// that it corresponds to.
///
/// FIXME. This struct gets converted to a `ResolveLifetimes` for
-/// actual use. It has the same data, but indexed by `LocalDefId`. This
+/// actual use. It has the same data, but indexed by `LocalDefId`. This
/// is silly.
#[derive(Debug, Default)]
struct NamedRegionMap {
_ => {}
}
match item.kind {
- hir::ItemKind::Fn(_, ref generics, _) => {
+ hir::ItemKind::Fn(_, generics, _) => {
self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_item(this, item);
});
this.with(scope, |this| intravisit::walk_item(this, item))
});
}
- hir::ItemKind::TyAlias(_, ref generics)
- | hir::ItemKind::Enum(_, ref generics)
- | hir::ItemKind::Struct(_, ref generics)
- | hir::ItemKind::Union(_, ref generics)
- | hir::ItemKind::Trait(_, _, ref generics, ..)
- | hir::ItemKind::TraitAlias(ref generics, ..)
- | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
+ hir::ItemKind::TyAlias(_, generics)
+ | hir::ItemKind::Enum(_, generics)
+ | hir::ItemKind::Struct(_, generics)
+ | hir::ItemKind::Union(_, generics)
+ | hir::ItemKind::Trait(_, _, generics, ..)
+ | hir::ItemKind::TraitAlias(generics, ..)
+ | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => {
// These kinds of items have only early-bound lifetime parameters.
let lifetimes = generics
.params
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
match item.kind {
- hir::ForeignItemKind::Fn(_, _, ref generics) => {
+ hir::ForeignItemKind::Fn(_, _, generics) => {
self.visit_early_late(item.hir_id(), generics, |this| {
intravisit::walk_foreign_item(this, item);
})
#[instrument(level = "debug", skip(self))]
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
match ty.kind {
- hir::TyKind::BareFn(ref c) => {
+ hir::TyKind::BareFn(c) => {
let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
.generic_params
.iter()
intravisit::walk_ty(this, ty);
});
}
- hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
+ hir::TyKind::TraitObject(bounds, lifetime, _) => {
debug!(?bounds, ?lifetime, "TraitObject");
let scope = Scope::TraitRefBoundary { s: self.scope };
self.with(scope, |this| {
LifetimeName::Error => {}
}
}
- hir::TyKind::Ref(ref lifetime_ref, ref mt) => {
+ hir::TyKind::Ref(lifetime_ref, ref mt) => {
self.visit_lifetime(lifetime_ref);
let scope = Scope::ObjectLifetimeDefault {
lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(),
// ^ ^ this gets resolved in the scope of
// the opaque_ty generics
let opaque_ty = self.tcx.hir().item(item_id);
- match opaque_ty.kind {
+ match &opaque_ty.kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias,
..
origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
..
}) => {}
- ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
+ i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
};
// Resolve the lifetimes that are applied to the opaque type.
intravisit::walk_trait_item(this, trait_item)
});
}
- Type(bounds, ref ty) => {
+ Type(bounds, ty) => {
let generics = &trait_item.generics;
let lifetimes = generics
.params
Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
intravisit::walk_impl_item(this, impl_item)
}),
- Type(ref ty) => {
+ Type(ty) => {
let generics = &impl_item.generics;
let lifetimes: FxIndexMap<LocalDefId, Region> = generics
.params
fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
for (i, segment) in path.segments.iter().enumerate() {
let depth = path.segments.len() - i - 1;
- if let Some(ref args) = segment.args {
+ if let Some(args) = segment.args {
self.visit_segment_args(path.res, depth, args);
}
}
) {
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);
}
}
// We may fail to resolve higher-ranked lifetimes that are mentioned by APIT.
// AST-based resolution does not care for impl-trait desugaring, which are the
- // responibility of lowering. This may create a mismatch between the resolution
+ // responibility of lowering. This may create a mismatch between the resolution
// AST found (`region_def_id`) which points to HRTB, and what HIR allows.
// ```
// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty),
DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)),
// We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter
- // works. Ignore it because it can't have a meaningful lifetime default.
+ // works. Ignore it because it can't have a meaningful lifetime default.
DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None,
dk => bug!("unexpected def_kind {:?}", dk),
}
}
}
- hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
// consider only the lifetimes on the final
// segment; I am not sure it's even currently
// valid to have them elsewhere, but even if it
Node::ImplItem(item) => item.generics,
Node::Item(item) => match item.kind {
- ItemKind::Impl(ref impl_) => {
+ ItemKind::Impl(impl_) => {
if impl_.defaultness.is_default() {
- is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy);
+ is_default_impl_trait =
+ tcx.impl_trait_ref(def_id).map(|t| ty::Binder::dummy(t.subst_identity()));
}
- &impl_.generics
+ impl_.generics
}
- ItemKind::Fn(.., ref generics, _)
- | ItemKind::TyAlias(_, ref generics)
- | ItemKind::Enum(_, ref generics)
- | ItemKind::Struct(_, ref generics)
- | ItemKind::Union(_, ref generics) => *generics,
+ ItemKind::Fn(.., generics, _)
+ | ItemKind::TyAlias(_, generics)
+ | ItemKind::Enum(_, generics)
+ | ItemKind::Struct(_, generics)
+ | ItemKind::Union(_, generics) => generics,
- ItemKind::Trait(_, _, ref generics, ..) | ItemKind::TraitAlias(ref generics, _) => {
+ ItemKind::Trait(_, _, generics, ..) | ItemKind::TraitAlias(generics, _) => {
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
- *generics
+ generics
}
- ItemKind::OpaqueTy(OpaqueTy { ref generics, .. }) => generics,
+ ItemKind::OpaqueTy(OpaqueTy { generics, .. }) => generics,
_ => NO_GENERICS,
},
Node::ForeignItem(item) => match item.kind {
ForeignItemKind::Static(..) => NO_GENERICS,
- ForeignItemKind::Fn(_, _, ref generics) => *generics,
+ ForeignItemKind::Fn(_, _, generics) => generics,
ForeignItemKind::Type => NO_GENERICS,
},
// Subtle: before we store the predicates into the tcx, we
// sort them so that predicates like `T: Foo<Item=U>` come
- // before uses of `U`. This avoids false ambiguity errors
+ // before uses of `U`. This avoids false ambiguity errors
// in trait checking. See `setup_constraining_predicates`
// for details.
if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node {
let self_ty = tcx.type_of(def_id);
- let trait_ref = tcx.impl_trait_ref(def_id);
+ let trait_ref = tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::subst_identity);
cgp::setup_constraining_predicates(
tcx,
&mut predicates,
let 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 *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,
};
// Get the starting `hir::Ty` using our `WellFormedLoc`.
// We will walk 'into' this type to try to find
// a more precise span for our predicate.
- let ty = match loc {
+ let tys = match loc {
WellFormedLoc::Ty(_) => match hir.get(hir_id) {
hir::Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::Type(ty) => Some(ty),
- hir::ImplItemKind::Const(ty, _) => Some(ty),
+ hir::ImplItemKind::Type(ty) => vec![ty],
+ hir::ImplItemKind::Const(ty, _) => vec![ty],
ref item => bug!("Unexpected ImplItem {:?}", item),
},
hir::Node::TraitItem(item) => match item.kind {
- hir::TraitItemKind::Type(_, ty) => ty,
- hir::TraitItemKind::Const(ty, _) => Some(ty),
+ hir::TraitItemKind::Type(_, ty) => ty.into_iter().collect(),
+ hir::TraitItemKind::Const(ty, _) => vec![ty],
ref item => bug!("Unexpected TraitItem {:?}", item),
},
hir::Node::Item(item) => match item.kind {
- hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => Some(ty),
- hir::ItemKind::Impl(ref impl_) => {
- assert!(impl_.of_trait.is_none(), "Unexpected trait impl: {:?}", impl_);
- Some(impl_.self_ty)
- }
+ hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => vec![ty],
+ hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
+ Some(t) => t
+ .path
+ .segments
+ .last()
+ .iter()
+ .flat_map(|seg| seg.args().args)
+ .filter_map(|arg| {
+ if let hir::GenericArg::Type(ty) = arg { Some(*ty) } else { None }
+ })
+ .chain([impl_.self_ty])
+ .collect(),
+ None => {
+ vec![impl_.self_ty]
+ }
+ },
ref item => bug!("Unexpected item {:?}", item),
},
- hir::Node::Field(field) => Some(field.ty),
+ hir::Node::Field(field) => vec![field.ty],
hir::Node::ForeignItem(ForeignItem {
kind: ForeignItemKind::Static(ty, _), ..
- }) => Some(*ty),
+ }) => vec![*ty],
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Type { default: Some(ty), .. },
..
- }) => Some(*ty),
+ }) => vec![*ty],
ref node => bug!("Unexpected node {:?}", node),
},
WellFormedLoc::Param { function: _, param_idx } => {
// Get return type
if param_idx as usize == fn_decl.inputs.len() {
match fn_decl.output {
- hir::FnRetTy::Return(ty) => Some(ty),
+ hir::FnRetTy::Return(ty) => vec![ty],
// The unit type `()` is always well-formed
- hir::FnRetTy::DefaultReturn(_span) => None,
+ hir::FnRetTy::DefaultReturn(_span) => vec![],
}
} else {
- Some(&fn_decl.inputs[param_idx as usize])
+ vec![&fn_decl.inputs[param_idx as usize]]
}
}
};
- if let Some(ty) = ty {
+ for ty in tys {
visitor.visit_ty(ty);
}
visitor.cause
}
let impl_generics = tcx.generics_of(impl_def_id);
let impl_predicates = tcx.predicates_of(impl_def_id);
- let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::subst_identity);
let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref);
cgp::identify_constrained_generic_params(
fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Option<Node> {
let trait_ref = tcx.impl_trait_ref(impl1_def_id)?;
- let trait_def = tcx.trait_def(trait_ref.def_id);
+ let trait_def = tcx.trait_def(trait_ref.skip_binder().def_id);
let impl2_node = trait_def.ancestors(tcx, impl1_def_id.to_def_id()).ok()?.nth(1)?;
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);
- let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id);
let assumed_wf_types =
ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
return None;
}
- let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types);
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_def_id, assumed_wf_types);
let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
let _ =
infcx.err_ctxt().check_region_obligations_and_report_errors(impl1_def_id, &outlives_env);
let impl_generic_predicates = tcx.predicates_of(impl_def_id);
let mut unconstrained_parameters = FxHashSet::default();
let mut constrained_params = FxHashSet::default();
- let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).map(ty::EarlyBinder::subst_identity);
// Unfortunately the functions in `constrained_generic_parameters` don't do
// what we want here. We want only a list of constrained parameters while
});
// Include the well-formed predicates of the type parameters of the impl.
- for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
+ for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().subst_identity().substs {
let infcx = &tcx.infer_ctxt().build();
- let obligations = wf::obligations(
- infcx,
- tcx.param_env(impl1_def_id),
- tcx.hir().local_def_id_to_hir_id(impl1_def_id),
- 0,
- arg,
- span,
- )
- .unwrap();
+ let obligations =
+ wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, arg, span)
+ .unwrap();
assert!(!obligations.needs_infer());
impl2_predicates.extend(
4. Finally, the check phase then checks function bodies and so forth.
Within the check phase, we check each function body one at a time
(bodies of function expressions are checked as part of the
- containing function). Inference is used to supply types wherever
+ containing function). Inference is used to supply types wherever
they are unknown. The actual checking of a function itself has
several phases (check, regionck, writeback), as discussed in the
documentation for the [`check`] module.
local variables, type parameters, etc as necessary.
- infer: finds the types to use for each type variable such that
- all subtyping and assignment constraints are met. In essence, the check
+ all subtyping and assignment constraints are met. In essence, the check
module specifies the constraints, and the infer module solves them.
## Note
use rustc_errors::{struct_span_err, ErrorGuaranteed};
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_hir::Node;
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
use rustc_middle::middle;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
use rustc_session::{config::EntryFnType, parse::feature_err};
+use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
use rustc_span::{symbol::sym, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use std::iter;
+use std::ops::Not;
use astconv::AstConv;
use bounds::Bounds;
let main_fnsig = tcx.fn_sig(main_def_id);
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)),
);
pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
// In case there are any projections, etc., find the "environment"
// def-ID that will be used to determine the traits/predicates in
- // scope. This is derived from the enclosing item-like thing.
+ // scope. This is derived from the enclosing item-like thing.
let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id);
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
item_cx.astconv().ast_ty_to_ty(hir_ty)
) -> Bounds<'tcx> {
// In case there are any projections, etc., find the "environment"
// def-ID that will be used to determine the traits/predicates in
- // scope. This is derived from the enclosing item-like thing.
+ // scope. This is derived from the enclosing item-like thing.
let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
let mut bounds = Bounds::default();
if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 {
// `unsubstituted_predicate` is `U: 'b` in the
- // example above. So apply the substitution to
+ // example above. So apply the substitution to
// get `T: 'a` (or `predicate`):
let predicate = unsubstituted_predicates
.rebind(*unsubstituted_predicate)
// ```
//
// Here `outlived_region = 'a` and `kind = &'b
- // u32`. Decomposing `&'b u32` into
+ // u32`. Decomposing `&'b u32` into
// components would yield `'b`, and we add the
// where clause that `'b: 'a`.
insert_outlives_predicate(
// ```
//
// Here `outlived_region = 'a` and `kind =
- // Vec<U>`. Decomposing `Vec<U>` into
+ // Vec<U>`. Decomposing `Vec<U>` into
// components would yield `U`, and we add the
// where clause that `U: 'a`.
let ty: Ty<'tcx> = param_ty.to_ty(tcx);
.or_insert(span);
}
- Component::Projection(proj_ty) => {
- // This would arise from something like:
+ Component::Alias(alias_ty) => {
+ // This would either arise from something like:
//
// ```
// struct Foo<'a, T: Iterator> {
// }
// ```
//
- // Here we want to add an explicit `where <T as Iterator>::Item: 'a`.
- let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.def_id, proj_ty.substs);
- required_predicates
- .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
- .or_insert(span);
- }
-
- Component::Opaque(def_id, substs) => {
- // This would arise from something like:
+ // or:
//
// ```rust
// type Opaque<T> = impl Sized;
// struct Ss<'a, T>(&'a Opaque<T>);
// ```
//
- // Here we want to have an implied bound `Opaque<T>: 'a`
-
- let ty = tcx.mk_opaque(def_id, substs);
+ // Here we want to add an explicit `where <T as Iterator>::Item: 'a`
+ // or `Opaque<T>: 'a` depending on the alias kind.
+ let ty = alias_ty.to_ty(tcx);
required_predicates
.entry(ty::OutlivesPredicate(ty.into(), outlived_region))
.or_insert(span);
}
- Component::EscapingProjection(_) => {
+ Component::EscapingAlias(_) => {
// As above, but the projection involves
- // late-bound regions. Therefore, the WF
+ // late-bound regions. Therefore, the WF
// requirement is not checked in type definition
// but at fn call site, so ignore it.
//
// }
//
// The type above might generate a `T: 'b` bound, but we can
- // ignore it. We can't put it on the struct header anyway.
+ // ignore it. We can't put it on the struct header anyway.
ty::ReLateBound(..) => false,
// These regions don't appear in types from type declarations:
if let Some(parent_node) = self.tcx.hir().opt_parent_id(self.path_segment.hir_id)
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
&& let hir::Node::Expr(expr) = parent_node {
- match expr.kind {
- hir::ExprKind::Path(ref qpath) => {
+ match &expr.kind {
+ hir::ExprKind::Path(qpath) => {
self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
err,
qpath,
a.visit_with(self)?;
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
substs.visit_with(self)
}
//! optimal solution to the constraints. The final variance for each
//! inferred is then written into the `variance_map` in the tcx.
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::DefIdMap;
use rustc_middle::ty;
use super::constraints::*;
let ConstraintContext { terms_cx, constraints, .. } = constraints_cx;
let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()];
- for &(id, ref variances) in &terms_cx.lang_items {
- let InferredIndex(start) = terms_cx.inferred_starts[&id];
+ for (id, variances) in &terms_cx.lang_items {
+ let InferredIndex(start) = terms_cx.inferred_starts[id];
for (i, &variance) in variances.iter().enumerate() {
solutions[start + i] = variance;
}
impl<'a, 'tcx> SolveContext<'a, 'tcx> {
fn solve(&mut self) {
- // Propagate constraints until a fixed point is reached. Note
+ // Propagate constraints until a fixed point is reached. Note
// that the maximum number of iterations is 2C where C is the
// number of constraints (each variable can change values at most
// twice). Since number of constraints is linear in size of the
}
}
- fn create_map(&self) -> FxHashMap<DefId, &'tcx [ty::Variance]> {
+ fn create_map(&self) -> DefIdMap<&'tcx [ty::Variance]> {
let tcx = self.terms_cx.tcx;
let solutions = &self.solutions;
- self.terms_cx
- .inferred_starts
- .iter()
- .map(|(&def_id, &InferredIndex(start))| {
+ DefIdMap::from(self.terms_cx.inferred_starts.items().map(
+ |(&def_id, &InferredIndex(start))| {
let generics = tcx.generics_of(def_id);
let count = generics.count();
}
(def_id.to_def_id(), &*variances)
- })
- .collect()
+ },
+ ))
}
fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance {
-use rustc_errors::struct_span_err;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
for id in tcx.hir().items() {
if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_variance) {
let variances_of = tcx.variances_of(id.owner_id);
- struct_span_err!(tcx.sess, tcx.def_span(id.owner_id), E0208, "{:?}", variances_of)
- .emit();
+
+ tcx.sess.struct_span_err(tcx.def_span(id.owner_id), format!("{variances_of:?}")).emit();
}
}
}
}
hir::ExprKind::Closure(&hir::Closure {
binder,
+ constness,
capture_clause,
bound_generic_params,
fn_decl,
def_id: _,
}) => {
self.print_closure_binder(binder, bound_generic_params);
+ self.print_constness(constness);
self.print_capture_clause(capture_clause);
self.print_closure_params(fn_decl, body);
}
pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
- match header.constness {
- hir::Constness::NotConst => {}
- hir::Constness::Const => self.word_nbsp("const"),
- }
+ self.print_constness(header.constness);
match header.asyncness {
hir::IsAsync::NotAsync => {}
self.word("fn")
}
+ pub fn print_constness(&mut self, s: hir::Constness) {
+ match s {
+ hir::Constness::NotConst => {}
+ hir::Constness::Const => self.word_nbsp("const"),
+ }
+ }
+
pub fn print_unsafety(&mut self, s: hir::Unsafety) {
match s {
hir::Unsafety::Normal => {}
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 {
if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) {
let predicates = self.tcx.predicates_of(def_id);
let predicates = predicates.instantiate(self.tcx, subst);
- for (predicate, predicate_span) in
- predicates.predicates.iter().zip(&predicates.spans)
- {
+ for (predicate, predicate_span) in predicates {
let obligation = Obligation::new(
self.tcx,
ObligationCause::dummy_with_span(callee_expr.span),
self.param_env,
- *predicate,
+ predicate,
);
let result = self.evaluate_obligation(&obligation);
self.tcx
callee_expr.span,
&format!("evaluate({:?}) = {:?}", predicate, result),
)
- .span_label(*predicate_span, "predicate")
+ .span_label(predicate_span, "predicate")
.emit();
}
}
};
if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) {
- if let Some((maybe_def, output_ty, _)) =
- self.extract_callable_info(callee_expr, callee_ty)
+ if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty)
&& !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span)
{
let descr = match maybe_def {
use super::FnCtxt;
use crate::type_error_struct;
-use rustc_errors::{struct_span_err, Applicability, DelayDm, DiagnosticBuilder, ErrorGuaranteed};
+use hir::ExprKind;
+use rustc_errors::{
+ struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
use rustc_hir as hir;
use rustc_macros::{TypeFoldable, TypeVisitable};
use rustc_middle::mir::Mutability;
#[derive(Copy, Clone)]
pub enum CastError {
- ErrorGuaranteed,
+ ErrorGuaranteed(ErrorGuaranteed),
CastToBool,
CastToChar,
}
impl From<ErrorGuaranteed> for CastError {
- fn from(_: ErrorGuaranteed) -> Self {
- CastError::ErrorGuaranteed
+ fn from(err: ErrorGuaranteed) -> Self {
+ CastError::ErrorGuaranteed(err)
}
}
fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
match e {
- CastError::ErrorGuaranteed => {
+ CastError::ErrorGuaranteed(_) => {
// an error has already been reported
}
CastError::NeedDeref => {
- let error_span = self.span;
let mut err = make_invalid_casting_error(
fcx.tcx.sess,
self.span,
self.cast_ty,
fcx,
);
- let cast_ty = fcx.ty_to_string(self.cast_ty);
- err.span_label(
- error_span,
- format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty),
- );
- if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr_span) {
- err.span_suggestion(
- self.expr_span,
- "dereference the expression",
- format!("*{}", snippet),
- Applicability::MaybeIncorrect,
+
+ if matches!(self.expr.kind, ExprKind::AddrOf(..)) {
+ // get just the borrow part of the expression
+ let span = self.expr_span.with_hi(self.expr.peel_borrows().span.lo());
+ err.span_suggestion_verbose(
+ span,
+ "remove the unneeded borrow",
+ "",
+ Applicability::MachineApplicable,
);
} else {
- err.span_help(self.expr_span, "dereference the expression with `*`");
+ err.span_suggestion_verbose(
+ self.expr_span.shrink_to_lo(),
+ "dereference the expression",
+ "*",
+ Applicability::MachineApplicable,
+ );
}
+
err.emit();
}
CastError::NeedViaThinPtr | CastError::NeedViaPtr => {
}
));
}
+
+ self.try_suggest_collection_to_bool(fcx, &mut err);
+
err.emit();
}
CastError::NeedViaInt => {
} else {
err.span_label(self.span, "invalid cast");
}
+
+ self.try_suggest_collection_to_bool(fcx, &mut err);
+
err.emit();
}
CastError::SizedUnsizedCast => {
},
);
}
+
+ /// Attempt to suggest using `.is_empty` when trying to cast from a
+ /// collection type to a boolean.
+ fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
+ if self.cast_ty.is_bool() {
+ let derefed = fcx
+ .autoderef(self.expr_span, self.expr_ty)
+ .silence_errors()
+ .find(|t| matches!(t.0.kind(), ty::Str | ty::Slice(..)));
+
+ if let Some((deref_ty, _)) = derefed {
+ // Give a note about what the expr derefs to.
+ if deref_ty != self.expr_ty.peel_refs() {
+ err.span_note(
+ self.expr_span,
+ format!(
+ "this expression `Deref`s to `{}` which implements `is_empty`",
+ fcx.ty_to_string(deref_ty)
+ ),
+ );
+ }
+
+ // Create a multipart suggestion: add `!` and `.is_empty()` in
+ // place of the cast.
+ let suggestion = vec![
+ (self.expr_span.shrink_to_lo(), "!".to_string()),
+ (self.span.with_lo(self.expr_span.hi()), ".is_empty()".to_string()),
+ ];
+
+ err.multipart_suggestion_verbose(format!(
+ "consider using the `is_empty` method on `{}` to determine if it contains anything",
+ fcx.ty_to_string(self.expr_ty),
+ ), suggestion, Applicability::MaybeIncorrect);
+ }
+ }
+ }
}
use crate::coercion::CoerceMany;
+use crate::errors::{
+ LangStartIncorrectNumberArgs, LangStartIncorrectParam, LangStartIncorrectRetTy,
+};
use crate::gather_locals::GatherLocalsVisitor;
use crate::FnCtxt;
use crate::GeneratorTypes;
use rustc_hir_analysis::check::fn_maybe_err;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::RegionVariableOrigin;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Binder, Ty, TyCtxt};
use rustc_span::def_id::LocalDefId;
+use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use std::cell::RefCell;
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,
));
check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty);
}
+ if let Some(lang_start_defid) = tcx.lang_items().start_fn() && lang_start_defid == hir.local_def_id(fn_id).to_def_id() {
+ check_lang_start_fn(tcx, fn_sig, decl, fn_def_id);
+ }
+
gen_ty
}
tcx.sess.span_err(span, "should have no const parameters");
}
}
+
+fn check_lang_start_fn<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ fn_sig: ty::FnSig<'tcx>,
+ decl: &'tcx hir::FnDecl<'tcx>,
+ def_id: LocalDefId,
+) {
+ let inputs = fn_sig.inputs();
+
+ let arg_count = inputs.len();
+ if arg_count != 4 {
+ tcx.sess.emit_err(LangStartIncorrectNumberArgs {
+ params_span: tcx.def_span(def_id),
+ found_param_count: arg_count,
+ });
+ }
+
+ // only check args if they should exist by checking the count
+ // note: this does not handle args being shifted or their order swapped very nicely
+ // but it's a lang item, users shouldn't frequently encounter this
+
+ // first arg is `main: fn() -> T`
+ if let Some(&main_arg) = inputs.get(0) {
+ // make a Ty for the generic on the fn for diagnostics
+ // FIXME: make the lang item generic checks check for the right generic *kind*
+ // for example `start`'s generic should be a type parameter
+ let generics = tcx.generics_of(def_id);
+ let fn_generic = generics.param_at(0, tcx);
+ let generic_tykind =
+ ty::Param(ty::ParamTy { index: fn_generic.index, name: fn_generic.name });
+ let generic_ty = tcx.mk_ty(generic_tykind);
+ let expected_fn_sig =
+ tcx.mk_fn_sig([].iter(), &generic_ty, false, hir::Unsafety::Normal, Abi::Rust);
+ let expected_ty = tcx.mk_fn_ptr(Binder::dummy(expected_fn_sig));
+
+ // we emit the same error to suggest changing the arg no matter what's wrong with the arg
+ let emit_main_fn_arg_err = || {
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[0].span,
+ param_num: 1,
+ expected_ty: expected_ty,
+ found_ty: main_arg,
+ });
+ };
+
+ if let ty::FnPtr(main_fn_sig) = main_arg.kind() {
+ let main_fn_inputs = main_fn_sig.inputs();
+ if main_fn_inputs.iter().count() != 0 {
+ emit_main_fn_arg_err();
+ }
+
+ let output = main_fn_sig.output();
+ output.map_bound(|ret_ty| {
+ // if the output ty is a generic, it's probably the right one
+ if !matches!(ret_ty.kind(), ty::Param(_)) {
+ emit_main_fn_arg_err();
+ }
+ });
+ } else {
+ emit_main_fn_arg_err();
+ }
+ }
+
+ // second arg is isize
+ if let Some(&argc_arg) = inputs.get(1) {
+ if argc_arg != tcx.types.isize {
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[1].span,
+ param_num: 2,
+ expected_ty: tcx.types.isize,
+ found_ty: argc_arg,
+ });
+ }
+ }
+
+ // third arg is `*const *const u8`
+ if let Some(&argv_arg) = inputs.get(2) {
+ let mut argv_is_okay = false;
+ if let ty::RawPtr(outer_ptr) = argv_arg.kind() {
+ if outer_ptr.mutbl.is_not() {
+ if let ty::RawPtr(inner_ptr) = outer_ptr.ty.kind() {
+ if inner_ptr.mutbl.is_not() && inner_ptr.ty == tcx.types.u8 {
+ argv_is_okay = true;
+ }
+ }
+ }
+ }
+
+ if !argv_is_okay {
+ let inner_ptr_ty =
+ tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: tcx.types.u8 });
+ let expected_ty =
+ tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: inner_ptr_ty });
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[2].span,
+ param_num: 3,
+ expected_ty,
+ found_ty: argv_arg,
+ });
+ }
+ }
+
+ // fourth arg is `sigpipe: u8`
+ if let Some(&sigpipe_arg) = inputs.get(3) {
+ if sigpipe_arg != tcx.types.u8 {
+ tcx.sess.emit_err(LangStartIncorrectParam {
+ param_span: decl.inputs[3].span,
+ param_num: 4,
+ expected_ty: tcx.types.u8,
+ found_ty: sigpipe_arg,
+ });
+ }
+ }
+
+ // output type is isize
+ if fn_sig.output() != tcx.types.isize {
+ tcx.sess.emit_err(LangStartIncorrectRetTy {
+ ret_span: decl.output.span(),
+ expected_ty: tcx.types.isize,
+ found_ty: fn_sig.output(),
+ });
+ }
+}
use hir::def::DefKind;
use rustc_hir as hir;
-use rustc_hir::def_id::LocalDefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor};
+use rustc_span::def_id::LocalDefId;
use rustc_span::source_map::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
debug!(?bound_sig, ?liberated_sig);
- let mut fcx = FnCtxt::new(self, self.param_env.without_const(), body.value.hir_id);
+ let mut fcx = FnCtxt::new(self, self.param_env.without_const(), closure.def_id);
let generator_types = check_fn(
&mut fcx,
liberated_sig,
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if t == self.expected_ty {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
t.super_visit_with(self)
}
// FIXME(#45727): As discussed in [this comment][c1], naively
// forcing equality here actually results in suboptimal error
- // messages in some cases. For now, if there would have been
+ // messages in some cases. For now, if there would have been
// an obvious error, we fallback to declaring the type of the
// closure to be the one the user gave, which allows other
// error message code to trigger.
// function.
Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => {
debug!("closure is async fn body");
- self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id)
- .unwrap_or_else(|| {
+ let def_id = self.tcx.hir().body_owner_def_id(body.id());
+ self.deduce_future_output_from_obligations(expr_def_id, def_id).unwrap_or_else(
+ || {
// AFAIK, deducing the future output
// always succeeds *except* in error cases
// like #65159. I'd like to return Error
// *have* reported an
// error. --nikomatsakis
astconv.ty_infer(None, decl.output.span())
- })
+ },
+ )
}
_ => astconv.ty_infer(None, decl.output.span()),
fn deduce_future_output_from_obligations(
&self,
expr_def_id: LocalDefId,
- body_id: hir::HirId,
+ body_def_id: LocalDefId,
) -> Option<Ty<'tcx>> {
let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| {
span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn")
let InferOk { value: output_ty, obligations } = self
.replace_opaque_types_with_inference_vars(
output_ty,
- body_id,
+ body_def_id,
self.tcx.def_span(expr_def_id),
self.param_env,
);
// If we have a parameter of type `&M T_a` and the value
// provided is `expr`, we will be adding an implicit borrow,
- // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
+ // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore,
// to type check, we will construct the type that `&M*expr` would
// yield.
continue;
}
- // At this point, we have deref'd `a` to `referent_ty`. So
+ // At this point, we have deref'd `a` to `referent_ty`. So
// imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`.
// In the autoderef loop for `&'a mut Vec<T>`, we would get
// three callbacks:
// - if in sub mode, that means we want to use `'b` (the
// region from the target reference) for both
// pointers [2]. This is because sub mode (somewhat
- // arbitrarily) returns the subtype region. In the case
+ // arbitrarily) returns the subtype region. In the case
// where we are coercing to a target type, we know we
// want to use that target type region (`'b`) because --
// for the program to type-check -- it must be the
// annotate the region of a borrow), and regionck has
// code that adds edges from the region of a borrow
// (`'b`, here) into the regions in the borrowed
- // expression (`*x`, here). (Search for "link".)
+ // expression (`*x`, here). (Search for "link".)
// - if in lub mode, things can get fairly complicated. The
// easiest thing is just to make a fresh
// region variable [4], which effectively means we defer
if ty == a && mt_a.mutbl.is_not() && autoderef.step_count() == 1 {
// As a special case, if we would produce `&'a *x`, that's
// a total no-op. We end up with the type `&'a T` just as
- // we started with. In that case, just skip it
+ // we started with. In that case, just skip it
// altogether. This is just an optimization.
//
// Note that for `&mut`, we DO want to reborrow --
// if let Some(x) = ... { }
//
// we wind up with a second match arm that is like `_ =>
- // ()`. That is the case we are considering here. We take
+ // ()`. That is the case we are considering here. We take
// a different path to get the right "expected, found"
// message and so forth (and because we know that
// `expression_ty` will be unit).
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
}
+
let reported = err.emit_unless(unsized_return);
self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
}
}
}
+
fn note_unreachable_loop_return(
&self,
err: &mut Diagnostic,
|| 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.annotate_expected_due_to_let_ty(err, expr, error);
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
- self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
+ self.check_wrong_return_type_due_to_generic_arg(err, expr, expr_ty);
}
/// Requires that the two types unify, and prints an error message if
);
}
+ 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(
sugg_sp = receiver.span;
}
}
+
+ if let hir::ExprKind::Unary(hir::UnOp::Deref, ref inner) = expr.kind
+ && let Some(1) = self.deref_steps(expected, checked_ty) {
+ // We have `*&T`, check if what was expected was `&T`.
+ // If so, we may want to suggest removing a `*`.
+ sugg_sp = sugg_sp.with_hi(inner.span.lo());
+ return Some((
+ sugg_sp,
+ "consider removing deref here".to_string(),
+ "".to_string(),
+ Applicability::MachineApplicable,
+ true,
+ false,
+ ));
+ }
+
if let Ok(src) = sm.span_to_snippet(sugg_sp) {
let needs_parens = match expr.kind {
// parenthesize if needed (Issue #46756)
err.span_label(block.span, "this block is missing a tail expression");
}
}
+
+ fn check_wrong_return_type_due_to_generic_arg(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ checked_ty: Ty<'tcx>,
+ ) {
+ let Some(hir::Node::Expr(parent_expr)) = self.tcx.hir().find_parent(expr.hir_id) else { return; };
+ enum CallableKind {
+ Function,
+ Method,
+ Constructor,
+ }
+ let mut maybe_emit_help = |def_id: hir::def_id::DefId,
+ callable: rustc_span::symbol::Ident,
+ args: &[hir::Expr<'_>],
+ kind: CallableKind| {
+ let arg_idx = args.iter().position(|a| a.hir_id == expr.hir_id).unwrap();
+ let fn_ty = self.tcx.bound_type_of(def_id).0;
+ if !fn_ty.is_fn() {
+ return;
+ }
+ let fn_sig = fn_ty.fn_sig(self.tcx).skip_binder();
+ let Some(&arg) = fn_sig.inputs().get(arg_idx + if matches!(kind, CallableKind::Method) { 1 } else { 0 }) else { return; };
+ if matches!(arg.kind(), ty::Param(_))
+ && fn_sig.output().contains(arg)
+ && self.node_ty(args[arg_idx].hir_id) == checked_ty
+ {
+ let mut multi_span: MultiSpan = parent_expr.span.into();
+ multi_span.push_span_label(
+ args[arg_idx].span,
+ format!(
+ "this argument influences the {} of `{}`",
+ if matches!(kind, CallableKind::Constructor) {
+ "type"
+ } else {
+ "return type"
+ },
+ callable
+ ),
+ );
+ err.span_help(
+ multi_span,
+ format!(
+ "the {} `{}` due to the type of the argument passed",
+ match kind {
+ CallableKind::Function => "return type of this call is",
+ CallableKind::Method => "return type of this call is",
+ CallableKind::Constructor => "type constructed contains",
+ },
+ checked_ty
+ ),
+ );
+ }
+ };
+ match parent_expr.kind {
+ hir::ExprKind::Call(fun, args) => {
+ let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = fun.kind else { return; };
+ let hir::def::Res::Def(kind, def_id) = path.res else { return; };
+ let callable_kind = if matches!(kind, hir::def::DefKind::Ctor(_, _)) {
+ CallableKind::Constructor
+ } else {
+ CallableKind::Function
+ };
+ maybe_emit_help(def_id, path.segments[0].ident, args, callable_kind);
+ }
+ hir::ExprKind::MethodCall(method, _receiver, args, _span) => {
+ let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(parent_expr.hir_id) else { return; };
+ maybe_emit_help(def_id, method.ident, args, CallableKind::Method)
+ }
+ _ => return,
+ }
+ }
}
);
}
}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_lang_start_incorrect_number_params)]
+#[note(hir_typeck_lang_start_incorrect_number_params_note_expected_count)]
+#[note(hir_typeck_lang_start_expected_sig_note)]
+pub struct LangStartIncorrectNumberArgs {
+ #[primary_span]
+ pub params_span: Span,
+ pub found_param_count: usize,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_lang_start_incorrect_param)]
+pub struct LangStartIncorrectParam<'tcx> {
+ #[primary_span]
+ #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
+ pub param_span: Span,
+
+ pub param_num: usize,
+ pub expected_ty: Ty<'tcx>,
+ pub found_ty: Ty<'tcx>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_lang_start_incorrect_ret_ty)]
+pub struct LangStartIncorrectRetTy<'tcx> {
+ #[primary_span]
+ #[suggestion(style = "short", code = "{expected_ty}", applicability = "machine-applicable")]
+ pub ret_span: Span,
+
+ pub expected_ty: Ty<'tcx>,
+ pub found_ty: Ty<'tcx>,
+}
}
hir::BorrowKind::Ref => {
// Note: at this point, we cannot say what the best lifetime
- // is to use for resulting pointer. We want to use the
+ // is to use for resulting pointer. We want to use the
// shortest lifetime possible so as to avoid spurious borrowck
- // errors. Moreover, the longest lifetime will depend on the
+ // errors. Moreover, the longest lifetime will depend on the
// precise details of the value whose address is being taken
// (and how long it is valid), which we don't know yet until
// type inference is complete.
}
} else {
// If `ctxt.coerce` is `None`, we can just ignore
- // the type of the expression. This is because
+ // the type of the expression. This is because
// either this was a break *without* a value, in
// which case it is always a legal type (`()`), or
// else an error would have been flagged by the
// Point any obligations that were registered due to opaque type
// inference at the return expression.
self.select_obligations_where_possible(|errors| {
- self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty);
+ self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty, return_expr.span);
});
}
}
errors: &mut Vec<traits::FulfillmentError<'tcx>>,
span: Span,
return_expr_ty: Ty<'tcx>,
+ return_span: Span,
) {
// Don't point at the whole block if it's empty
- if span == self.tcx.hir().span(self.body_id) {
+ if span == return_span {
return;
}
for err in errors {
let body = self.tcx.hir().body(anon_const.body);
// Create a new function context.
- let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id);
+ let def_id = anon_const.def_id;
+ let fcx = FnCtxt::new(self, self.param_env.with_const(), def_id);
crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
let ty = fcx.check_expr_with_expectation(&body.value, expected);
variant: &'tcx ty::VariantDef,
access_span: Span,
) -> Vec<Symbol> {
+ let body_owner_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
variant
.fields
.iter()
.filter(|field| {
let def_scope = self
.tcx
- .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id)
+ .adjust_ident_and_get_scope(
+ field.ident(self.tcx),
+ variant.def_id,
+ body_owner_hir_id,
+ )
.1;
field.vis.is_accessible_from(def_scope, self.tcx)
&& !matches!(
match deref_base_ty.kind() {
ty::Adt(base_def, substs) if !base_def.is_enum() => {
debug!("struct named {:?}", deref_base_ty);
+ let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
let (ident, def_scope) =
- self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
+ self.tcx.adjust_ident_and_get_scope(field, base_def.did(), body_hir_id);
let fields = &base_def.non_enum_variant().fields;
if let Some(index) = fields
.iter()
}
fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) {
- let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
+ let generics = self.tcx.generics_of(self.body_id);
let generic_param = generics.type_param(¶m, self.tcx);
if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
return;
// Named constants have to be equated with the value
// being matched, so that's a read of the value being matched.
//
- // FIXME: We don't actually reads for ZSTs.
+ // FIXME: We don't actually reads for ZSTs.
needs_to_be_read = true;
}
_ => {
// We now see if we can make progress. This might cause us to
// unify inference variables for opaque types, since we may
// have unified some other type variables during the first
- // phase of fallback. This means that we only replace
+ // phase of fallback. This means that we only replace
// inference variables with their underlying opaque types as a
// last resort.
//
// (and the setting of `#![feature(never_type_fallback)]`).
//
// Fallback becomes very dubious if we have encountered
- // type-checking errors. In that case, fallback to Error.
+ // type-checking errors. In that case, fallback to Error.
//
// Sets `FnCtxt::fallback_has_occurred` if fallback is performed
// during this call.
/// constrained to have some other type).
///
/// However, the fallback used to be `()` (before the `!` type was
- /// added). Moreover, there are cases where the `!` type 'leaks
+ /// added). Moreover, there are cases where the `!` type 'leaks
/// out' from dead code into type variables that affect live
/// code. The most common case is something like this:
///
/// ```
///
/// Here, coercing the type `!` into `?M` will create a diverging
- /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
+ /// type variable `?X` where `?X <: ?M`. We also have that `?D <:
/// ?M`. If `?M` winds up unconstrained, then `?X` will
/// fallback. If it falls back to `!`, then all the type variables
/// will wind up equal to `!` -- this includes the type `?D`
///
/// The algorithm we use:
/// * Identify all variables that are coerced *into* by a
- /// diverging variable. Do this by iterating over each
+ /// diverging variable. Do this by iterating over each
/// diverging, unsolved variable and finding all variables
/// reachable from there. Call that set `D`.
/// * Walk over all unsolved, non-diverging variables, and find
) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> {
debug!("calculate_diverging_fallback({:?})", unsolved_variables);
- let relationships = self.fulfillment_cx.borrow_mut().relationships().clone();
-
// Construct a coercion graph where an edge `A -> B` indicates
// a type variable is that is coerced
let coercion_graph = self.create_coercion_graph();
roots_reachable_from_non_diverging,
);
- debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations());
debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations());
- debug!("relationships: {:#?}", relationships);
// For each diverging variable, figure out whether it can
// reach a member of N. If so, it falls back to `()`. Else
.depth_first_search(root_vid)
.any(|n| roots_reachable_from_non_diverging.visited(n));
- let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false };
+ let mut found_infer_var_info = ty::InferVarInfo { self_in_trait: false, output: false };
- for (vid, rel) in relationships.iter() {
- if self.root_var(*vid) == root_vid {
- relationship.self_in_trait |= rel.self_in_trait;
- relationship.output |= rel.output;
+ for (vid, info) in self.inh.infer_var_info.borrow().iter() {
+ if self.infcx.root_var(*vid) == root_vid {
+ found_infer_var_info.self_in_trait |= info.self_in_trait;
+ found_infer_var_info.output |= info.output;
}
}
- if relationship.self_in_trait && relationship.output {
+ if found_infer_var_info.self_in_trait && found_infer_var_info.output {
// This case falls back to () to ensure that the code pattern in
// tests/ui/never_type/fallback-closure-ret.rs continues to
// compile when never_type_fallback is enabled.
}
}
- pub(in super::super) fn note_need_for_fn_pointer(
- &self,
- err: &mut Diagnostic,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
- (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
- let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
- if sig1 != sig2 {
- return;
- }
- err.note(
- "different `fn` items always have unique types, even if their signatures are \
- the same",
- );
- (sig1, *did1, substs1)
- }
- (ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
- let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
- if sig1 != *sig2 {
- return;
- }
- (sig1, *did, substs)
- }
- _ => return,
- };
- err.help(&format!("change the expected type to be function pointer `{}`", sig));
- err.help(&format!(
- "if the expected type is due to type inference, cast the expected `fn` to a function \
- pointer: `{} as {}`",
- self.tcx.def_path_str_with_substs(did, substs),
- sig
- ));
- }
-
// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
#[instrument(skip(self, span), level = "debug")]
}
GenericParamDefKind::Const { has_default } => {
if !infer_args && has_default {
- tcx.bound_const_param_default(param.def_id)
- .subst(tcx, substs.unwrap())
- .into()
+ tcx.const_param_default(param.def_id).subst(tcx, substs.unwrap()).into()
} else {
self.fcx.var_for_def(self.span, param)
}
match *callee_ty.kind() {
ty::Param(param) => {
let param =
- self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx);
+ self.tcx.generics_of(self.body_id).type_param(¶m, self.tcx);
if param.kind.is_synthetic() {
// if it's `impl Fn() -> ..` then just fall down to the def-id based logic
def_id = param.def_id;
// and point at that.
let instantiated = self
.tcx
- .explicit_predicates_of(self.body_id.owner)
+ .explicit_predicates_of(self.body_id)
.instantiate_identity(self.tcx);
// FIXME(compiler-errors): This could be problematic if something has two
// fn-like predicates with different args, but callable types really never
// do that, so it's OK.
- for (predicate, span) in
- std::iter::zip(instantiated.predicates, instantiated.spans)
+ for (predicate, span) in instantiated
{
if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) = predicate.kind().skip_binder()
&& pred.self_ty().peel_refs() == callee_ty
use crate::coercion::DynamicCoerceMany;
use crate::{Diverges, EnclosingBreakables, Inherited};
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer;
use rustc_infer::infer::error_reporting::TypeErrCtxt;
/// [`ItemCtxt`]: rustc_hir_analysis::collect::ItemCtxt
/// [`InferCtxt`]: infer::InferCtxt
pub struct FnCtxt<'a, 'tcx> {
- pub(super) body_id: hir::HirId,
+ pub(super) body_id: LocalDefId,
/// The parameter environment used for proving trait obligations
/// in this function. This can change when we descend into
pub fn new(
inh: &'a Inherited<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
) -> FnCtxt<'a, 'tcx> {
FnCtxt {
body_id,
}
fn item_def_id(&self) -> DefId {
- self.body_id.owner.to_def_id()
+ self.body_id.to_def_id()
}
fn get_type_parameter_bounds(
Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate,
};
use rustc_hir_analysis::astconv::AstConv;
-use rustc_infer::infer;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{
use rustc_span::symbol::{sym, Ident};
use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::NormalizeExt;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(crate) fn body_fn_sig(&self) -> Option<ty::FnSig<'tcx>> {
self.typeck_results
.borrow()
.liberated_fn_sigs()
- .get(self.tcx.hir().parent_id(self.body_id))
+ .get(self.tcx.hir().local_def_id_to_hir_id(self.body_id))
.copied()
}
found: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
) -> bool {
- let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(found)
else { return false; };
if can_satisfy(output) {
let (sugg_call, mut applicability) = match inputs.len() {
/// because the callable type must also be well-formed to be called.
pub(in super::super) fn extract_callable_info(
&self,
- expr: &Expr<'_>,
- found: Ty<'tcx>,
+ ty: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
- // Autoderef is useful here because sometimes we box callables, etc.
- let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
- match *found.kind() {
- ty::FnPtr(fn_sig) =>
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
- }
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
- && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Param(param) => {
- let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id;
- self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- _ => None,
- }
- }) else { return None; };
-
- let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
- let inputs = inputs
- .skip_binder()
- .iter()
- .map(|ty| {
- self.replace_bound_vars_with_fresh_vars(
- expr.span,
- infer::FnCall,
- inputs.rebind(*ty),
- )
- })
- .collect();
-
- // We don't want to register any extra obligations, which should be
- // implied by wf, but also because that would possibly result in
- // erroneous errors later on.
- let infer::InferOk { value: output, obligations: _ } =
- self.at(&self.misc(expr.span), self.param_env).normalize(output);
-
- if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ self.err_ctxt().extract_callable_info(body_hir_id, self.param_env, ty)
}
pub fn suggest_two_fn_call(
rhs_ty: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
) -> bool {
- let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
+ let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_ty)
else { return false; };
- let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
+ let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_ty)
else { return false; };
if can_satisfy(lhs_output_ty, rhs_output_ty) {
&& method_call_list.contains(&conversion_method.name)
// If receiver is `.clone()` and found type has one of those methods,
// we guess that the user wants to convert from a slice type (`&[]` or `&str`)
- // to an owned type (`Vec` or `String`). These conversions clone internally,
+ // to an owned type (`Vec` or `String`). These conversions clone internally,
// so we remove the user's `clone` call.
{
vec![(
}
}
ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => {
- // Check if the parent expression is a call to Pin::new. If it
+ // Check if the parent expression is a call to Pin::new. If it
// is and we were expecting a Box, ergo Pin<Box<expected>>, we
// can suggest Box::pin.
let parent = self.tcx.hir().parent_id(expr.hir_id);
let mut reinit = None;
match expr.kind {
ExprKind::Assign(lhs, rhs, _) => {
- self.visit_expr(lhs);
self.visit_expr(rhs);
+ self.visit_expr(lhs);
reinit = Some(lhs);
}
self.drop_ranges.add_control_edge(self.expr_index, *target)
}),
- ExprKind::Break(destination, ..) => {
+ ExprKind::Break(destination, value) => {
// destination either points to an expression or to a block. We use
// find_target_expression_from_destination to use the last expression of the block
// if destination points to a block.
// will refer to the end of the block due to the post order traversal.
self.find_target_expression_from_destination(destination).map_or((), |target| {
self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
- })
+ });
+
+ if let Some(value) = value {
+ self.visit_expr(value);
+ }
}
ExprKind::Call(f, args) => {
ExprKind::AddrOf(..)
| ExprKind::Array(..)
+ // FIXME(eholk): We probably need special handling for AssignOps. The ScopeTree builder
+ // in region.rs runs both lhs then rhs and rhs then lhs and then sets all yields to be
+ // the latest they show up in either traversal. With the older scope-based
+ // approximation, this was fine, but it's probably not right now. What we probably want
+ // to do instead is still run both orders, but consider anything that showed up as a
+ // yield in either order.
| ExprKind::AssignOp(..)
| ExprKind::Binary(..)
| ExprKind::Block(..)
// Increment expr_count here to match what InteriorVisitor expects.
self.expr_index = self.expr_index + 1;
+
+ // Save a node mapping to get better CFG visualization
+ self.drop_ranges.add_node_mapping(pat.hir_id, self.expr_index);
}
}
}
});
}
- debug!("hir_id_map: {:?}", tracked_value_map);
+ debug!("hir_id_map: {:#?}", tracked_value_map);
let num_values = tracked_value_map.len();
Self {
tracked_value_map,
//! flow graph when needed for debugging.
use rustc_graphviz as dot;
+use rustc_hir::{Expr, ExprKind, Node};
use rustc_middle::ty::TyCtxt;
use super::{DropRangesBuilder, PostOrderId};
.post_order_map
.iter()
.find(|(_hir_id, &post_order_id)| post_order_id == *n)
- .map_or("<unknown>".into(), |(hir_id, _)| self
- .tcx
- .hir()
- .node_to_string(*hir_id))
+ .map_or("<unknown>".into(), |(hir_id, _)| format!(
+ "{}{}",
+ self.tcx.hir().node_to_string(*hir_id),
+ match self.tcx.hir().find(*hir_id) {
+ Some(Node::Expr(Expr { kind: ExprKind::Yield(..), .. })) => " (yield)",
+ _ => "",
+ }
+ ))
)
.into(),
)
// where the `identity(...)` (the rvalue) produces a return type
// of `&'rv mut A`, where `'a: 'rv`. We then assign this result to
// `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use,
- // `a` will be considered borrowed). Other parts of the code will ensure
+ // `a` will be considered borrowed). Other parts of the code will ensure
// that if `y` is live over a yield, `&'y mut A` appears in the generator
// state. If `'y` is live, then any sound region analysis must conclude
// that `'a` is also live. So if this causes a bug, blame some other
yield_data.expr_and_pat_count, self.expr_count, source_span
);
- if self.fcx.sess().opts.unstable_opts.drop_tracking
- && self
- .drop_ranges
- .is_dropped_at(hir_id, yield_data.expr_and_pat_count)
+ if self
+ .is_dropped_at_yield_location(hir_id, yield_data.expr_and_pat_count)
{
debug!("value is dropped at yield point; not recording");
return false;
}
}
}
+
+ /// If drop tracking is enabled, consult drop_ranges to see if a value is
+ /// known to be dropped at a yield point and therefore can be omitted from
+ /// the generator witness.
+ fn is_dropped_at_yield_location(&self, value_hir_id: HirId, yield_location: usize) -> bool {
+ // short-circuit if drop tracking is not enabled.
+ if !self.fcx.sess().opts.unstable_opts.drop_tracking {
+ return false;
+ }
+
+ self.drop_ranges.is_dropped_at(value_hir_id, yield_location)
+ }
}
pub fn resolve_interior<'a, 'tcx>(
use super::callee::DeferredCallResolution;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::HirIdMap;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::LocalDefIdMap;
use rustc_span::{self, Span};
-use rustc_trait_selection::traits::{self, TraitEngine, TraitEngineExt as _};
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+use rustc_trait_selection::traits::{self, PredicateObligation, TraitEngine, TraitEngineExt as _};
use std::cell::RefCell;
use std::ops::Deref;
/// 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;
+ }
+ }
+ }
}
} else {
err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
.note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
+ let mut should_delay_as_bug = false;
+ if let Err(LayoutError::Unknown(bad_from)) = sk_from && bad_from.references_error() {
+ should_delay_as_bug = true;
+ }
+ if let Err(LayoutError::Unknown(bad_to)) = sk_to && bad_to.references_error() {
+ should_delay_as_bug = true;
+ }
+ if should_delay_as_bug {
+ err.delay_as_bug();
+ }
}
err.emit();
}
let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
let param_env = tcx.param_env(def_id);
- let mut fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+ let mut fcx = FnCtxt::new(&inh, param_env, def_id);
if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
}
PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => {
- // box p1, &p1, &mut p1. we can ignore the mutability of
+ // box p1, &p1, &mut p1. we can ignore the mutability of
// PatKind::Ref since that information is already contained
// in the type.
let subplace = self.cat_deref(pat, place_with_id)?;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::traits;
-use std::iter;
use std::ops::Deref;
struct ConfirmContext<'a, 'tcx> {
let filler_substs = rcvr_substs
.extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def));
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(
- &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
+ self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs),
);
// Unify the (adjusted) self type with what the method expects.
fn predicates_require_illegal_sized_bound(
&self,
- predicates: &ty::InstantiatedPredicates<'tcx>,
+ predicates: ty::InstantiatedPredicates<'tcx>,
) -> Option<Span> {
let sized_def_id = self.tcx.lang_items().sized_trait()?;
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
if trait_pred.def_id() == sized_def_id =>
{
- let span = iter::zip(&predicates.predicates, &predicates.spans)
+ let span = predicates
+ .iter()
.find_map(
|(p, span)| {
- if *p == obligation.predicate { Some(*span) } else { None }
+ if p == obligation.predicate { Some(span) } else { None }
},
)
.unwrap_or(rustc_span::DUMMY_SP);
pub unsatisfied_predicates:
Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>,
pub out_of_scope_traits: Vec<DefId>,
- pub lev_candidate: Option<ty::AssocItem>,
+ pub similar_candidate: Option<ty::AssocItem>,
pub mode: probe::Mode,
}
// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
- // so this also effectively registers `obligation` as well. (We
+ // so this also effectively registers `obligation` as well. (We
// used to register `obligation` explicitly, but that resulted in
// double error messages being reported.)
//
pub enum Mode {
// An expression of the form `receiver.method_name(...)`.
// Autoderefs are performed on `receiver`, lookup is done based on the
- // `self` argument of the method, and static methods aren't considered.
+ // `self` argument of the method, and static methods aren't considered.
MethodCall,
// An expression of the form `Type::item` or `<T>::item`.
// No autoderefs are performed, lookup is done based on the type each
static_candidates: Vec::new(),
unsatisfied_predicates: Vec::new(),
out_of_scope_traits: Vec::new(),
- lev_candidate: None,
+ similar_candidate: None,
mode,
}));
}
probe_cx.assemble_inherent_candidates();
match scope {
ProbeScope::TraitsInScope => {
- probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id)
+ probe_cx.assemble_extension_candidates_for_traits_in_scope()
}
ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
};
let (ref infcx, goal, inference_vars) = tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
let ParamEnvAnd { param_env, value: self_ty } = goal;
- let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty)
- .include_raw_pointers()
- .silence_errors();
+ let mut autoderef =
+ Autoderef::new(infcx, param_env, hir::def_id::CRATE_DEF_ID, DUMMY_SP, self_ty)
+ .include_raw_pointers()
+ .silence_errors();
let mut reached_raw_pointer = false;
let mut steps: Vec<_> = autoderef
.by_ref()
fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) {
let is_accessible = if let Some(name) = self.method_name {
let item = candidate.item;
- let def_scope = self
- .tcx
- .adjust_ident_and_get_scope(name, item.container_id(self.tcx), self.body_id)
- .1;
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(self.body_id);
+ let def_scope =
+ self.tcx.adjust_ident_and_get_scope(name, item.container_id(self.tcx), hir_id).1;
item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx)
} else {
true
}
}
- fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) {
+ fn assemble_extension_candidates_for_traits_in_scope(&mut self) {
let mut duplicates = FxHashSet::default();
- let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id);
+ let opt_applicable_traits = self.tcx.in_scope_traits(self.scope_expr_id);
if let Some(applicable_traits) = opt_applicable_traits {
for trait_candidate in applicable_traits.iter() {
let trait_did = trait_candidate.def_id;
if let Some((kind, def_id)) = private_candidate {
return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits));
}
- let lev_candidate = self.probe_for_lev_candidate()?;
+ let similar_candidate = self.probe_for_similar_candidate()?;
Err(MethodError::NoMatch(NoMatchData {
static_candidates,
unsatisfied_predicates,
out_of_scope_traits,
- lev_candidate,
+ similar_candidate,
mode: self.mode,
}))
}
let o = self.resolve_vars_if_possible(o);
if !self.predicate_may_hold(&o) {
result = ProbeResult::NoMatch;
- possibly_unsatisfied_predicates.push((
- o.predicate,
- None,
- Some(o.cause),
- ));
+ let parent_o = o.clone();
+ let implied_obligations =
+ traits::elaborate_obligations(self.tcx, vec![o]);
+ for o in implied_obligations {
+ let parent = if o == parent_o {
+ None
+ } else {
+ if o.predicate.to_opt_poly_trait_pred().map(|p| p.def_id())
+ == self.tcx.lang_items().sized_trait()
+ {
+ // We don't care to talk about implicit `Sized` bounds.
+ continue;
+ }
+ Some(parent_o.predicate)
+ };
+ if !self.predicate_may_hold(&o) {
+ possibly_unsatisfied_predicates.push((
+ o.predicate,
+ parent,
+ Some(o.cause),
+ ));
+ }
+ }
}
}
}
/// Similarly to `probe_for_return_type`, this method attempts to find the best matching
/// candidate method where the method name may have been misspelled. Similarly to other
/// Levenshtein based suggestions, we provide at most one such suggestion.
- fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
+ fn probe_for_similar_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> {
debug!("probing for method names similar to {:?}", self.method_name);
let steps = self.steps.clone();
None,
)
}
+ .or_else(|| {
+ applicable_close_candidates
+ .iter()
+ .find(|cand| self.matches_by_doc_alias(cand.def_id))
+ .map(|cand| cand.name)
+ })
.unwrap();
Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name))
}
}
}
+ /// Determine if the associated item withe the given DefId matches
+ /// the desired name via a doc alias.
+ fn matches_by_doc_alias(&self, def_id: DefId) -> bool {
+ let Some(name) = self.method_name else { return false; };
+ let Some(local_def_id) = def_id.as_local() else { return false; };
+ let hir_id = self.fcx.tcx.hir().local_def_id_to_hir_id(local_def_id);
+ let attrs = self.fcx.tcx.hir().attrs(hir_id);
+ for attr in attrs {
+ let sym::doc = attr.name_or_empty() else { continue; };
+ let Some(values) = attr.meta_item_list() else { continue; };
+ for v in values {
+ if v.name_or_empty() != sym::alias {
+ continue;
+ }
+ if let Some(nested) = v.meta_item_list() {
+ // #[doc(alias("foo", "bar"))]
+ for n in nested {
+ if let Some(lit) = n.lit() && name.as_str() == lit.symbol.as_str() {
+ return true;
+ }
+ }
+ } else if let Some(meta) = v.meta_item()
+ && let Some(lit) = meta.name_value_literal()
+ && name.as_str() == lit.symbol.as_str() {
+ // #[doc(alias = "foo")]
+ return true;
+ }
+ }
+ }
+ false
+ }
+
/// Finds the method with the appropriate name (or return type, as the case may be). If
/// `allow_similar_names` is set, find methods with close-matching names.
// The length of the returned iterator is nearly always 0 or 1 and this
if !self.is_relevant_kind_for_mode(x.kind) {
return false;
}
+ if self.matches_by_doc_alias(x.def_id) {
+ return true;
+ }
match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist)
{
Some(d) => d > 0,
let ty_str = with_forced_trimmed_paths!(self.ty_to_string(rcvr_ty));
let is_method = mode == Mode::MethodCall;
let unsatisfied_predicates = &no_match_data.unsatisfied_predicates;
- let lev_candidate = no_match_data.lev_candidate;
+ let similar_candidate = no_match_data.similar_candidate;
let item_kind = if is_method {
"method"
} else if rcvr_ty.is_enum() {
let ty_span = match rcvr_ty.kind() {
ty::Param(param_type) => {
- Some(param_type.span_from_generics(self.tcx, self.body_id.owner.to_def_id()))
+ Some(param_type.span_from_generics(self.tcx, self.body_id.to_def_id()))
}
ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())),
_ => None,
args,
sugg_span,
);
-
self.note_candidates_on_method_error(
rcvr_ty,
item_name,
ty::Param(_) => {
// Account for `fn` items like in `issue-35677.rs` to
// suggest restricting its type params.
- let parent_body =
- hir.body_owner(hir::BodyId { hir_id: self.body_id });
- Some(hir.get(parent_body))
+ Some(hir.get_by_def_id(self.body_id))
}
ty::Adt(def, _) => {
def.did().as_local().map(|def_id| hir.get_by_def_id(def_id))
}
_ => None,
};
- if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
- if let Some(g) = kind.generics() {
- let key = (
- g.tail_span_for_predicate_suggestion(),
- g.add_where_or_trailing_comma(),
- );
- type_params
- .entry(key)
- .or_insert_with(FxHashSet::default)
- .insert(obligation.to_owned());
- }
+ if let Some(hir::Node::Item(hir::Item { kind, .. })) = node
+ && let Some(g) = kind.generics()
+ {
+ let key = (
+ g.tail_span_for_predicate_suggestion(),
+ g.add_where_or_trailing_comma(),
+ );
+ type_params
+ .entry(key)
+ .or_insert_with(FxHashSet::default)
+ .insert(obligation.to_owned());
+ return true;
}
}
+ false
};
let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| {
let msg = format!(
"auto trait is invoked with no method error, but no error reported?",
);
}
- Some(_) => unreachable!(),
+ Some(Node::Item(hir::Item {
+ ident, kind: hir::ItemKind::Trait(..), ..
+ })) => {
+ skip_list.insert(p);
+ let entry = spanned_predicates.entry(ident.span);
+ let entry = entry.or_insert_with(|| {
+ (FxHashSet::default(), FxHashSet::default(), Vec::new())
+ });
+ entry.0.insert(cause.span);
+ entry.1.insert((ident.span, ""));
+ entry.1.insert((cause.span, "unsatisfied trait bound introduced here"));
+ entry.2.push(p);
+ }
+ Some(node) => unreachable!("encountered `{node:?}`"),
None => (),
}
}
unsatisfied_bounds = true;
}
+ let mut suggested_bounds = FxHashSet::default();
// The requirements that didn't have an `impl` span to show.
let mut bound_list = unsatisfied_predicates
.iter()
.filter_map(|(pred, parent_pred, _cause)| {
+ let mut suggested = false;
format_pred(*pred).map(|(p, self_ty)| {
- collect_type_param_suggestions(self_ty, *pred, &p);
+ if let Some(parent) = parent_pred && suggested_bounds.contains(parent) {
+ // We don't suggest `PartialEq` when we already suggest `Eq`.
+ } else if !suggested_bounds.contains(pred) {
+ if collect_type_param_suggestions(self_ty, *pred, &p) {
+ suggested = true;
+ suggested_bounds.insert(pred);
+ }
+ }
(
match parent_pred {
None => format!("`{}`", &p),
Some(parent_pred) => match format_pred(*parent_pred) {
None => format!("`{}`", &p),
Some((parent_p, _)) => {
- collect_type_param_suggestions(self_ty, *parent_pred, &p);
+ if !suggested
+ && !suggested_bounds.contains(pred)
+ && !suggested_bounds.contains(parent_pred)
+ {
+ if collect_type_param_suggestions(
+ self_ty,
+ *parent_pred,
+ &p,
+ ) {
+ suggested_bounds.insert(pred);
+ }
+ }
format!("`{}`\nwhich is required by `{}`", p, parent_p)
}
},
// give a helping note that it has to be called as `(x.f)(...)`.
if let SelfSource::MethodCall(expr) = source {
if !self.suggest_calling_field_as_fn(span, rcvr_ty, expr, item_name, &mut err)
- && lev_candidate.is_none()
+ && similar_candidate.is_none()
&& !custom_span_label
{
label_span_not_found(&mut err);
if fallback_span {
err.span_label(span, msg);
}
- } else if let Some(lev_candidate) = lev_candidate {
+ } else if let Some(similar_candidate) = similar_candidate {
// Don't emit a suggestion if we found an actual method
// that had unsatisfied trait bounds
if unsatisfied_predicates.is_empty() {
- let def_kind = lev_candidate.kind.as_def_kind();
+ let def_kind = similar_candidate.kind.as_def_kind();
// Methods are defined within the context of a struct and their first parameter is always self,
// which represents the instance of the struct the method is being called on
// Associated functions don’t take self as a parameter and
// they are not methods because they don’t have an instance of the struct to work with.
- if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
+ if def_kind == DefKind::AssocFn && similar_candidate.fn_has_self_parameter {
err.span_suggestion(
span,
"there is a method with a similar name",
- lev_candidate.name,
+ similar_candidate.name,
Applicability::MaybeIncorrect,
);
} else {
&format!(
"there is {} {} with a similar name",
def_kind.article(),
- def_kind.descr(lev_candidate.def_id),
+ def_kind.descr(similar_candidate.def_id),
),
- lev_candidate.name,
+ similar_candidate.name,
Applicability::MaybeIncorrect,
);
}
// the impl, if local to crate (item may be defaulted), else nothing.
let Some(item) = self.associated_value(impl_did, item_name).or_else(|| {
let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?;
- self.associated_value(impl_trait_ref.def_id, item_name)
+ self.associated_value(impl_trait_ref.skip_binder().def_id, item_name)
}) else {
continue;
};
let insertion = match self.tcx.impl_trait_ref(impl_did) {
None => String::new(),
Some(trait_ref) => {
- format!(" of the trait `{}`", self.tcx.def_path_str(trait_ref.def_id))
+ format!(
+ " of the trait `{}`",
+ self.tcx.def_path_str(trait_ref.skip_binder().def_id)
+ )
}
};
}
if let Some(sugg_span) = sugg_span
&& let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) {
- let path = self.tcx.def_path_str(trait_ref.def_id);
+ let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id);
let ty = match item.kind {
ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty,
_ => 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,
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;
};
// Obtain the span for `param` and use it for a structured suggestion.
if let Some(param) = param_type {
- let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
+ let generics = self.tcx.generics_of(self.body_id.to_def_id());
let type_param = generics.type_param(param, self.tcx);
let hir = self.tcx.hir();
if let Some(def_id) = type_param.def_id.as_local() {
self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative
})
.any(|imp_did| {
- let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
+ let imp = self.tcx.impl_trait_ref(imp_did).unwrap().subst_identity();
let imp_simp =
simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder);
imp_simp.map_or(false, |s| s == simp_rcvr_ty)
found: Ty<'tcx>,
expected: Ty<'tcx>,
) -> bool {
- let Some((_def_id_or_name, output, _inputs)) = self.extract_callable_info(expr, found)
- else { return false; };
+ let Some((_def_id_or_name, output, _inputs)) =
+ self.extract_callable_info(found) else {
+ return false;
+ };
if !self.can_coerce(output, expected) {
return false;
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) {
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);
) -> FxHashSet<DepKind> {
// This is a bit tricky. We want to include a node only if it is:
// (a) reachable from a source and (b) will reach a target. And we
- // have to be careful about cycles etc. Luckily efficiency is not
+ // have to be careful about cycles etc. Luckily efficiency is not
// a big concern!
#[derive(Copy, Clone, PartialEq)]
-//! Debugging code to test fingerprints computed for query results. For each node marked with
+//! Debugging code to test fingerprints computed for query results. For each node marked with
//! `#[rustc_clean]` we will compare the fingerprint from the current and from the previous
//! compilation session as appropriate:
//!
&'a mut self,
range: R,
) -> impl Iterator<Item = (I, T)> + 'a {
- self.raw.drain(range).enumerate().map(|(n, t)| (I::new(n), t))
+ let begin = match range.start_bound() {
+ std::ops::Bound::Included(i) => *i,
+ std::ops::Bound::Excluded(i) => i.checked_add(1).unwrap(),
+ std::ops::Bound::Unbounded => 0,
+ };
+ self.raw.drain(range).enumerate().map(move |(n, t)| (I::new(begin + n), t))
}
#[inline]
#[subdiagnostic]
pub req_introduces_loc: Option<ReqIntroducedLocations>,
+ pub has_param_name: bool,
+ pub param_name: String,
pub spans_empty: bool,
pub has_lifetime: bool,
pub lifetime: String,
debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
// Generalize type of `a_ty` appropriately depending on the
- // direction. As an example, assume:
+ // direction. As an example, assume:
//
// - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an
// inference variable,
// like when you have two references but one is `usize` and the other
// is `f32`. In those cases we still want to show the `note`. If the
// value from `ef` is `Infer(_)`, then we ignore it.
- if !ef.expected.is_ty_infer() {
+ if !ef.expected.is_ty_or_numeric_infer() {
ef.expected != values.expected
- } else if !ef.found.is_ty_infer() {
+ } else if !ef.found.is_ty_or_numeric_infer() {
ef.found != values.found
} else {
false
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.tcx.note_and_explain_type_err(diag, terr, cause, span, cause.body_id.to_def_id());
if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values
&& let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind()
(ty::Tuple(fields), _) => {
self.emit_tuple_wrap_err(&mut err, span, found, fields)
}
+ // If a byte was expected and the found expression is a char literal
+ // containing a single ASCII character, perhaps the user meant to write `b'c'` to
+ // specify a byte literal
+ (ty::Uint(ty::UintTy::U8), ty::Char) => {
+ if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+ && let Some(code) = code.strip_prefix('\'').and_then(|s| s.strip_suffix('\''))
+ && code.chars().next().map_or(false, |c| c.is_ascii())
+ {
+ err.span_suggestion(
+ span,
+ "if you meant to write a byte literal, prefix with `b`",
+ format!("b'{}'", escape_literal(code)),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
// If a character was expected and the found expression is a string literal
// containing a single character, perhaps the user meant to write `'c'` to
// specify a character literal (issue #92479)
let labeled_user_string = match bound_kind {
GenericKind::Param(ref p) => format!("the parameter type `{}`", p),
- GenericKind::Projection(ref p) => format!("the associated type `{}`", p),
- GenericKind::Opaque(def_id, substs) => {
- format!("the opaque type `{}`", self.tcx.def_path_str_with_substs(def_id, substs))
- }
+ GenericKind::Alias(ref p) => match p.kind(self.tcx) {
+ ty::AliasKind::Projection => format!("the associated type `{}`", p),
+ ty::AliasKind::Opaque => format!("the opaque type `{}`", p),
+ },
};
if let Some(SubregionOrigin::CompareImplItemObligation {
/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
/// FloatVar inference type are compatible with themselves or their concrete types (Int and
/// Float types, respectively). When comparing two ADTs, these rules apply recursively.
- pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+ pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
let (a, b) = self.resolve_vars_if_possible((a, b));
SameTypeModuloInfer(self).relate(a, b).is_ok()
}
}
fn where_x_is_kind(&self, in_type: Ty<'_>) -> &'static str {
- if in_type.is_ty_infer() {
+ if in_type.is_ty_or_numeric_infer() {
""
} else if self.name == "_" {
// FIXME: Consider specializing this message if there is a single `_`
// invalid pseudo-syntax, we want the `fn`-pointer output instead.
(ty::FnDef(..), _) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(),
(_, Some(def_id))
- if ty.is_ty_infer()
+ if ty.is_ty_or_numeric_infer()
&& infcx.tcx.get_diagnostic_item(sym::iterator_collect_fn) == Some(def_id) =>
{
"Vec<_>".to_string()
}
- _ if ty.is_ty_infer() => "/* Type */".to_string(),
+ _ if ty.is_ty_or_numeric_infer() => "/* Type */".to_string(),
// FIXME: The same thing for closures, but this only works when the closure
// does not capture anything.
//
| InferSourceKind::ClosureReturn { ty, .. } => {
if ty.is_closure() {
("closure", closure_as_fn_str(infcx, ty))
- } else if !ty.is_ty_infer() {
+ } else if !ty.is_ty_or_numeric_infer() {
("normal", ty_to_string(infcx, ty, None))
} else {
("other", String::new())
self.attempt += 1;
if let Some(InferSource { kind: InferSourceKind::GenericArg { def_id: did, ..}, .. }) = self.infer_source
&& let InferSourceKind::LetBinding { ref ty, ref mut def_id, ..} = new_source.kind
- && ty.is_ty_infer()
+ && ty.is_ty_or_numeric_infer()
{
// Customize the output so we talk about `let x: Vec<_> = iter.collect();` instead of
// `let x: _ = iter.collect();`, as this is a very common case.
// in the types are about to print
// - Meanwhile, the `maybe_highlighting_region` calls set up
// highlights so that, if they do appear, we will replace
- // them `'0` and whatever. (This replacement takes place
+ // them `'0` and whatever. (This replacement takes place
// inside the closure given to `maybe_highlighting_region`.)
//
// There is some duplication between the calls -- i.e., the
let sp = var_origin.span();
let return_sp = sub_origin.span();
let param = self.find_param_with_region(*sup_r, *sub_r)?;
+ let simple_ident = param.param.pat.simple_ident();
let lifetime_name = if sup_r.has_name() { sup_r.to_string() } else { "'_".to_owned() };
let (mention_influencer, influencer_point) =
req_introduces_loc: subdiag,
has_lifetime: sup_r.has_name(),
- lifetime: sup_r.to_string(),
+ lifetime: lifetime_name.clone(),
+ has_param_name: simple_ident.is_some(),
+ param_name: simple_ident.map(|x| x.to_string()).unwrap_or_default(),
spans_empty,
bound,
};
if let Some(def_id) = preds.principal_def_id() {
self.0.insert(def_id);
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => t.super_visit_with(self),
}
RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
.add_to_diagnostic(err);
}
- infer::DataBorrowed(ty, span) => {
- RegionOriginNote::WithName {
- span,
- msg: fluent::infer_data_borrowed,
- name: &self.ty_to_string(ty),
- continues: false,
- }
- .add_to_diagnostic(err);
- }
infer::ReferenceOutlivesReferent(ty, span) => {
RegionOriginNote::WithName {
span,
);
err
}
- infer::DataBorrowed(ty, span) => {
- let mut err = struct_span_err!(
- self.tcx.sess,
- span,
- E0490,
- "a value of type `{}` is borrowed for too long",
- self.ty_to_string(ty)
- );
- note_and_explain_region(
- self.tcx,
- &mut err,
- "the type is valid for ",
- sub,
- "",
- None,
- );
- note_and_explain_region(
- self.tcx,
- &mut err,
- "but the borrow lasts for ",
- sup,
- "",
- None,
- );
- err
- }
infer::ReferenceOutlivesReferent(ty, span) => {
let mut err = struct_span_err!(
self.tcx.sess,
.impl_trait_ref(impl_def_id)
else { return; };
let trait_substs = trait_ref
+ .subst_identity()
// Replace the explicit self type with `Self` for better suggestion rendering
.with_self_ty(self.tcx, self.tcx.mk_ty_param(0, kw::SelfUpper))
.substs;
let Ok(trait_predicates) = self
.tcx
- .bound_explicit_predicates_of(trait_item_def_id)
- .map_bound(|p| p.predicates)
- .subst_iter_copied(self.tcx, trait_item_substs)
+ .explicit_predicates_of(trait_item_def_id)
+ .instantiate_own(self.tcx, trait_item_substs)
.map(|(pred, _)| {
if pred.is_suggestable(self.tcx, false) {
Ok(pred.to_string())
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.bound_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, sugg) = 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, &sugg, Applicability::MaybeIncorrect);
+ }
+ (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+ let expected_sig =
+ &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1));
+ let found_sig =
+ &(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2));
+
+ if self.same_type_modulo_infer(*found_sig, *expected_sig) {
+ diag.note(
+ "different fn items have unique types, even if their signatures are the same",
+ );
+ }
+ }
+ (ty::FnDef(_, _), ty::FnPtr(_)) => {
+ diag.note("fn items are distinct from fn pointers");
+ }
+ _ => {
+ 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 {
//
// Example: if the LHS is a type variable, and RHS is
// `Box<i32>`, then we current compare `v` to the RHS first,
- // which will instantiate `v` with `Box<i32>`. Then when `v`
+ // which will instantiate `v` with `Box<i32>`. Then when `v`
// is compared to the LHS, we instantiate LHS with `Box<i32>`.
// But if we did in reverse order, we would create a `v <:
// LHS` (or vice versa) constraint and then instantiate
#[derive(Copy, Clone, Debug)]
pub(crate) enum VarValue<'tcx> {
- /// Empty lifetime is for data that is never accessed. We tag the
+ /// Empty lifetime is for data that is never accessed. We tag the
/// empty lifetime with a universe -- the idea is that we don't
/// want `exists<'a> { forall<'b> { 'b: 'a } }` to be satisfiable.
/// Therefore, the `'empty` in a universe `U` is less than all
VarValue::Empty(a_universe) => {
let b_data = var_values.value_mut(b_vid);
- let changed = (|| match *b_data {
+ let changed = match *b_data {
VarValue::Empty(b_universe) => {
// Empty regions are ordered according to the universe
// they are associated with.
};
if lub == cur_region {
- return false;
+ false
+ } else {
+ debug!(
+ "Expanding value of {:?} from {:?} to {:?}",
+ b_vid, cur_region, lub
+ );
+
+ *b_data = VarValue::Value(lub);
+ true
}
-
- debug!(
- "Expanding value of {:?} from {:?} to {:?}",
- b_vid, cur_region, lub
- );
-
- *b_data = VarValue::Value(lub);
- true
}
VarValue::ErrorValue => false,
- })();
+ };
if changed {
changes.push(b_vid);
}
// If both `a` and `b` are free, consult the declared
- // relationships. Note that this can be more precise than the
+ // relationships. Note that this can be more precise than the
// `lub` relationship defined below, since sometimes the "lub"
// is actually the `postdom_upper_bound` (see
// `TransitiveRelation` for more details).
// conflicting regions to report to the user. As we walk, we
// trip the flags from false to true, and if we find that
// we've already reported an error involving any particular
- // node we just stop and don't report the current error. The
+ // node we just stop and don't report the current error. The
// idea is to report errors that derive from independent
// regions of the graph, but not those that derive from
// overlapping locations.
// Obtain the spans for all the places that can
// influence the constraints on this value for
// richer diagnostics in `static_impl_trait`.
- let influences: Vec<Span> = self
- .data
- .constraints
- .iter()
- .filter_map(|(constraint, origin)| match (constraint, origin) {
- (
- Constraint::VarSubVar(_, sup),
- SubregionOrigin::DataBorrowed(_, sp),
- ) if sup == &node_vid => Some(*sp),
- _ => None,
- })
- .collect();
-
- self.collect_error_for_expanding_node(
- graph,
- &mut dup_vec,
- node_vid,
- errors,
- influences,
- );
+
+ self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors);
}
}
}
dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>,
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>,
- influences: Vec<Span>,
) {
// Errors in expanding nodes result from a lower-bound that is
// not contained by an upper-bound.
lower_bound.region,
upper_bound.origin.clone(),
upper_bound.region,
- influences,
+ vec![],
));
return;
}
/// Creating a pointer `b` to contents of another reference
Reborrow(Span),
- /// Data with type `Ty<'tcx>` was borrowed
- DataBorrowed(Ty<'tcx>, Span),
-
/// (&'a &'b T) where a >= b
ReferenceOutlivesReferent(Ty<'tcx>, Span),
self.tcx.mk_region(ty::ReVar(region_var))
}
- /// Return the universe that the region `r` was created in. For
+ /// Return the universe that the region `r` was created in. For
/// most regions (e.g., `'static`, named regions from the user,
/// etc) this is the root universe U0. For inference variables or
/// placeholders, however, it will return the universe which they
}
/// Resolve any type variables found in `value` -- but only one
- /// level. So, if the variable `?X` is bound to some type
+ /// level. So, if the variable `?X` is bound to some type
/// `Foo<?Y>`, then this would return `Foo<?Y>` (but `?Y` may
/// itself be bound to a type).
///
if let None = self.tainted_by_errors() {
// As a heuristic, just skip reporting region errors
// altogether if other errors have been reported while
- // this infcx was in use. This is totally hokey but
+ // this infcx was in use. This is totally hokey but
// otherwise we have a hard time separating legit region
// errors from silly ones.
self.report_region_errors(generic_param_scope, &errors);
RelateParamBound(a, ..) => a,
RelateRegionParamBound(a) => a,
Reborrow(a) => a,
- DataBorrowed(_, a) => a,
ReferenceOutlivesReferent(_, a) => a,
CompareImplItemObligation { span, .. } => span,
AscribeUserTypeProvePredicate(span) => span,
fn value_ty(&self) -> Ty<'tcx>;
/// Extract the scopes that apply to whichever side of the tuple
- /// the vid was found on. See the comment where this is called
+ /// the vid was found on. See the comment where this is called
/// for more details on why we want them.
fn vid_scopes<'r, D: TypeRelatingDelegate<'tcx>>(
&self,
/// (these are not explicitly present in the ty representation right
/// now). This visitor handles that: it descends the type, tracking
/// binder depth, and finds late-bound regions targeting the
-/// `for<..`>. For each of those, it creates an entry in
+/// `for<..`>. For each of those, it creates an entry in
/// `bound_region_scope`.
struct ScopeInstantiator<'me, 'tcx> {
next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
t.super_visit_with(self);
self.target_index.shift_out(1);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
_ => {}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
use crate::traits;
use hir::def::DefKind;
use hir::def_id::{DefId, LocalDefId};
-use hir::{HirId, OpaqueTyOrigin};
+use hir::OpaqueTyOrigin;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::vec_map::VecMap;
use rustc_hir as hir;
pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<'tcx>>(
&self,
value: T,
- body_id: HirId,
+ body_id: LocalDefId,
span: Span,
param_env: ty::ParamEnv<'tcx>,
) -> InferOk<'tcx, T> {
DefiningAnchor::Bind(_) => {
// Check that this is `impl Trait` type is
// declared by `parent_def_id` -- i.e., one whose
- // value we are inferring. At present, this is
+ // value we are inferring. At present, this is
// always true during the first phase of
// type-check, but not always true later on during
// NLL. Once we support named opaque types more fully,
};
let item_kind = &self.tcx.hir().expect_item(def_id).kind;
- let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else {
+ let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else {
span_bug!(
span,
"weird opaque type: {:#?}, {:#?}",
t: &ty::Binder<'tcx, T>,
) -> ControlFlow<Self::BreakTy> {
t.super_visit_with(self);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
match *r {
// ignore bound regions, keep visiting
- ty::ReLateBound(_, _) => ControlFlow::CONTINUE,
+ ty::ReLateBound(_, _) => ControlFlow::Continue(()),
_ => {
(self.op)(r);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
// We're only interested in types involving regions
if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match ty.kind() {
}
ty::Alias(ty::Opaque, ty::AliasTy { def_id, ref substs, .. }) => {
- // Skip lifetime paramters that are not captures.
+ // Skip lifetime parameters that are not captures.
let variances = self.tcx.variances_of(*def_id);
for (v, s) in std::iter::zip(variances, substs.iter()) {
ty::Alias(ty::Projection, proj)
if self.tcx.def_kind(proj.def_id) == DefKind::ImplTraitPlaceholder =>
{
- // Skip lifetime paramters that are not captures.
+ // Skip lifetime parameters that are not captures.
let variances = self.tcx.variances_of(proj.def_id);
for (v, s) in std::iter::zip(variances, proj.substs.iter()) {
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
// RFC for reference.
use rustc_data_structures::sso::SsoHashSet;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, SubstsRef, Ty, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
use smallvec::{smallvec, SmallVec};
#[derive(Debug)]
// is not in a position to judge which is the best technique, so
// we just product the projection as a component and leave it to
// the consumer to decide (but see `EscapingProjection` below).
- Projection(ty::AliasTy<'tcx>),
+ Alias(ty::AliasTy<'tcx>),
// In the case where a projection has escaping regions -- meaning
// regions bound within the type itself -- we always use
// projection, so that implied bounds code can avoid relying on
// them. This gives us room to improve the regionck reasoning in
// the future without breaking backwards compat.
- EscapingProjection(Vec<Component<'tcx>>),
-
- Opaque(DefId, SubstsRef<'tcx>),
+ EscapingAlias(Vec<Component<'tcx>>),
}
/// Push onto `out` all the things that must outlive `'a` for the condition
out.push(Component::Param(p));
}
- // Ignore lifetimes found in opaque types. Opaque types can
- // have lifetimes in their substs which their hidden type doesn't
- // actually use. If we inferred that an opaque type is outlived by
- // its parameter lifetimes, then we could prove that any lifetime
- // outlives any other lifetime, which is unsound.
- // See https://github.com/rust-lang/rust/issues/84305 for
- // more details.
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- out.push(Component::Opaque(def_id, substs));
- },
-
// For projections, we prefer to generate an obligation like
// `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
// regionck more ways to prove that it holds. However,
// trait-ref. Therefore, if we see any higher-ranked regions,
// we simply fallback to the most restrictive rule, which
// requires that `Pi: 'a` for all `i`.
- ty::Alias(ty::Projection, ref data) => {
- if !data.has_escaping_bound_vars() {
+ ty::Alias(_, alias_ty) => {
+ if !alias_ty.has_escaping_bound_vars() {
// best case: no escaping regions, so push the
// projection and skip the subtree (thus generating no
// constraints for Pi). This defers the choice between
// the rules OutlivesProjectionEnv,
// OutlivesProjectionTraitDef, and
// OutlivesProjectionComponents to regionck.
- out.push(Component::Projection(*data));
+ out.push(Component::Alias(alias_ty));
} else {
// fallback case: hard code
- // OutlivesProjectionComponents. Continue walking
+ // OutlivesProjectionComponents. Continue walking
// through and constrain Pi.
let mut subcomponents = smallvec![];
let mut subvisited = SsoHashSet::new();
compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited);
- out.push(Component::EscapingProjection(subcomponents.into_iter().collect()));
+ out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
}
}
ty::Error(_) => {
// (*) Function pointers and trait objects are both binders.
// In the RFC, this means we would add the bound regions to
- // the "bound regions list". In our representation, no such
+ // the "bound regions list". In our representation, no such
// list is maintained explicitly, because bound regions
// themselves can be readily identified.
compute_components_recursive(tcx, ty.into(), out, visited);
self.region_bound_pairs
.insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a));
}
- OutlivesBound::RegionSubProjection(r_a, projection_b) => {
+ OutlivesBound::RegionSubAlias(r_a, alias_b) => {
self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a));
- }
- OutlivesBound::RegionSubOpaque(r_a, def_id, substs) => {
- self.region_bound_pairs
- .insert(ty::OutlivesPredicate(GenericKind::Opaque(def_id, substs), r_a));
+ .insert(ty::OutlivesPredicate(GenericKind::Alias(alias_b), r_a));
}
OutlivesBound::RegionSubRegion(r_a, r_b) => {
if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) {
};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
-use rustc_hir::def_id::DefId;
use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, Region, SubstsRef, Ty, TyCtxt, TypeVisitable};
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *param_ty);
}
- Component::Opaque(def_id, substs) => {
- self.opaque_must_outlive(*def_id, substs, origin, region)
- }
- Component::Projection(projection_ty) => {
- self.projection_must_outlive(origin, region, *projection_ty);
- }
- Component::EscapingProjection(subcomponents) => {
+ Component::Alias(alias_ty) => self.alias_ty_must_outlive(origin, region, *alias_ty),
+ Component::EscapingAlias(subcomponents) => {
self.components_must_outlive(origin, &subcomponents, region, category);
}
Component::UnresolvedInferenceVariable(v) => {
}
}
+ #[instrument(level = "debug", skip(self))]
fn param_ty_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
param_ty: ty::ParamTy,
) {
- debug!(
- "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})",
- region, param_ty, origin
- );
-
- let generic = GenericKind::Param(param_ty);
let verify_bound = self.verify_bound.param_bound(param_ty);
- self.delegate.push_verify(origin, generic, region, verify_bound);
- }
-
- #[instrument(level = "debug", skip(self))]
- fn opaque_must_outlive(
- &mut self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- origin: infer::SubregionOrigin<'tcx>,
- region: ty::Region<'tcx>,
- ) {
- self.generic_must_outlive(
- origin,
- region,
- GenericKind::Opaque(def_id, substs),
- def_id,
- substs,
- true,
- |ty| match *ty.kind() {
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => (def_id, substs),
- _ => bug!("expected only projection types from env, not {:?}", ty),
- },
- );
+ self.delegate.push_verify(origin, GenericKind::Param(param_ty), region, verify_bound);
}
#[instrument(level = "debug", skip(self))]
- fn projection_must_outlive(
+ fn alias_ty_must_outlive(
&mut self,
origin: infer::SubregionOrigin<'tcx>,
region: ty::Region<'tcx>,
- projection_ty: ty::AliasTy<'tcx>,
- ) {
- self.generic_must_outlive(
- origin,
- region,
- GenericKind::Projection(projection_ty),
- projection_ty.def_id,
- projection_ty.substs,
- false,
- |ty| match ty.kind() {
- ty::Alias(ty::Projection, projection_ty) => {
- (projection_ty.def_id, projection_ty.substs)
- }
- _ => bug!("expected only projection types from env, not {:?}", ty),
- },
- );
- }
-
- #[instrument(level = "debug", skip(self, filter))]
- fn generic_must_outlive(
- &mut self,
- origin: infer::SubregionOrigin<'tcx>,
- region: ty::Region<'tcx>,
- generic: GenericKind<'tcx>,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
- is_opaque: bool,
- filter: impl Fn(Ty<'tcx>) -> (DefId, SubstsRef<'tcx>),
+ alias_ty: ty::AliasTy<'tcx>,
) {
// An optimization for a common case with opaque types.
- if substs.is_empty() {
+ if alias_ty.substs.is_empty() {
return;
}
// particular). :) First off, we have to choose between using the
// OutlivesProjectionEnv, OutlivesProjectionTraitDef, and
// OutlivesProjectionComponent rules, any one of which is
- // sufficient. If there are no inference variables involved, it's
+ // sufficient. If there are no inference variables involved, it's
// not hard to pick the right rule, but if there are, we're in a
// bit of a catch 22: if we picked which rule we were going to
// use, we could add constraints to the region inference graph
// These are guaranteed to apply, no matter the inference
// results.
let trait_bounds: Vec<_> =
- self.verify_bound.declared_region_bounds(def_id, substs).collect();
+ self.verify_bound.declared_bounds_from_definition(alias_ty).collect();
debug!(?trait_bounds);
// Compute the bounds we can derive from the environment. This
// is an "approximate" match -- in some cases, these bounds
// may not apply.
- let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(generic);
+ let mut approx_env_bounds = self.verify_bound.approx_declared_bounds_from_env(alias_ty);
debug!(?approx_env_bounds);
// Remove outlives bounds that we get from the environment but
// If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait`
// will be invoked with `['b => ^1]` and so we will get `^1` returned.
let bound = bound_outlives.skip_binder();
- let (def_id, substs) = filter(bound.0);
- self.verify_bound.declared_region_bounds(def_id, substs).all(|r| r != bound.1)
+ let ty::Alias(_, alias_ty) = bound.0.kind() else { bug!("expected AliasTy") };
+ self.verify_bound.declared_bounds_from_definition(*alias_ty).all(|r| r != bound.1)
});
// If declared bounds list is empty, the only applicable rule is
// the problem is to add `T: 'r`, which isn't true. So, if there are no
// inference variables, we use a verify constraint instead of adding
// edges, which winds up enforcing the same condition.
- let needs_infer = substs.needs_infer();
- if approx_env_bounds.is_empty() && trait_bounds.is_empty() && (needs_infer || is_opaque) {
+ if approx_env_bounds.is_empty()
+ && trait_bounds.is_empty()
+ && (alias_ty.needs_infer() || alias_ty.kind(self.tcx) == ty::Opaque)
+ {
debug!("no declared bounds");
-
- self.substs_must_outlive(substs, origin, region);
-
+ self.substs_must_outlive(alias_ty.substs, origin, region);
return;
}
// projection outlive; in some cases, this may add insufficient
// edges into the inference graph, leading to inference failures
// even though a satisfactory solution exists.
- let verify_bound = self.verify_bound.projection_opaque_bounds(
- generic,
- def_id,
- substs,
- &mut Default::default(),
- );
- debug!("projection_must_outlive: pushing {:?}", verify_bound);
- self.delegate.push_verify(origin, generic, region, verify_bound);
+ let verify_bound = self.verify_bound.alias_bound(alias_ty, &mut Default::default());
+ debug!("alias_must_outlive: pushing {:?}", verify_bound);
+ self.delegate.push_verify(origin, GenericKind::Alias(alias_ty), region, verify_bound);
}
fn substs_must_outlive(
use crate::infer::outlives::components::{compute_components_recursive, Component};
use crate::infer::outlives::env::RegionBoundPairs;
use crate::infer::region_constraints::VerifyIfEq;
-use crate::infer::{GenericKind, VerifyBound};
+use crate::infer::VerifyBound;
use rustc_data_structures::sso::SsoHashSet;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::GenericArg;
-use rustc_middle::ty::{self, OutlivesPredicate, SubstsRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
use smallvec::smallvec;
/// this list.
pub fn approx_declared_bounds_from_env(
&self,
- generic: GenericKind<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> {
- let projection_ty = generic.to_ty(self.tcx);
- let erased_projection_ty = self.tcx.erase_regions(projection_ty);
- self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty)
+ let erased_alias_ty = self.tcx.erase_regions(alias_ty.to_ty(self.tcx));
+ self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty)
}
#[instrument(level = "debug", skip(self, visited))]
- pub fn projection_opaque_bounds(
+ pub fn alias_bound(
&self,
- generic: GenericKind<'tcx>,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
visited: &mut SsoHashSet<GenericArg<'tcx>>,
) -> VerifyBound<'tcx> {
- let generic_ty = generic.to_ty(self.tcx);
+ let alias_ty_as_ty = alias_ty.to_ty(self.tcx);
// Search the env for where clauses like `P: 'a`.
- let projection_opaque_bounds = self
- .approx_declared_bounds_from_env(generic)
+ let env_bounds = self
+ .approx_declared_bounds_from_env(alias_ty)
.into_iter()
.map(|binder| {
- if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == generic_ty {
+ if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == alias_ty_as_ty {
// Micro-optimize if this is an exact match (this
// occurs often when there are no region variables
// involved).
VerifyBound::IfEq(verify_if_eq_b)
}
});
- // Extend with bounds that we can find from the trait.
- let trait_bounds =
- self.declared_region_bounds(def_id, substs).map(|r| VerifyBound::OutlivedBy(r));
+
+ // Extend with bounds that we can find from the definition.
+ let definition_bounds =
+ self.declared_bounds_from_definition(alias_ty).map(|r| VerifyBound::OutlivedBy(r));
// see the extensive comment in projection_must_outlive
let recursive_bound = {
let mut components = smallvec![];
- compute_components_recursive(self.tcx, generic_ty.into(), &mut components, visited);
+ compute_components_recursive(self.tcx, alias_ty_as_ty.into(), &mut components, visited);
self.bound_from_components(&components, visited)
};
- VerifyBound::AnyBound(projection_opaque_bounds.chain(trait_bounds).collect())
- .or(recursive_bound)
+ VerifyBound::AnyBound(env_bounds.chain(definition_bounds).collect()).or(recursive_bound)
}
fn bound_from_components(
let mut bounds = components
.iter()
.map(|component| self.bound_from_single_component(component, visited))
- .filter(|bound| {
- // Remove bounds that must hold, since they are not interesting.
- !bound.must_hold()
- });
+ // Remove bounds that must hold, since they are not interesting.
+ .filter(|bound| !bound.must_hold());
match (bounds.next(), bounds.next()) {
(Some(first), None) => first,
match *component {
Component::Region(lt) => VerifyBound::OutlivedBy(lt),
Component::Param(param_ty) => self.param_bound(param_ty),
- Component::Opaque(did, substs) => self.projection_opaque_bounds(
- GenericKind::Opaque(did, substs),
- did,
- substs,
- visited,
- ),
- Component::Projection(projection_ty) => self.projection_opaque_bounds(
- GenericKind::Projection(projection_ty),
- projection_ty.def_id,
- projection_ty.substs,
- visited,
- ),
- Component::EscapingProjection(ref components) => {
+ Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
+ Component::EscapingAlias(ref components) => {
self.bound_from_components(components, visited)
}
Component::UnresolvedInferenceVariable(v) => {
///
/// This is for simplicity, and because we are not really smart
/// enough to cope with such bounds anywhere.
- pub fn declared_region_bounds(
+ pub fn declared_bounds_from_definition(
&self,
- def_id: DefId,
- substs: SubstsRef<'tcx>,
+ alias_ty: ty::AliasTy<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
- let bounds = tcx.bound_item_bounds(def_id);
+ let bounds = tcx.item_bounds(alias_ty.def_id);
trace!("{:#?}", bounds.0);
bounds
- .subst_iter(tcx, substs)
+ .subst_iter(tcx, alias_ty.substs)
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
.map(|OutlivesPredicate(_, r)| r)
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::undo_log::UndoLogs;
use rustc_data_structures::unify as ut;
-use rustc_hir::def_id::DefId;
use rustc_index::vec::IndexVec;
use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion};
-use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::ReStatic;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::ty::{ReLateBound, ReVar};
#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)]
pub enum GenericKind<'tcx> {
Param(ty::ParamTy),
- Projection(ty::AliasTy<'tcx>),
- Opaque(DefId, SubstsRef<'tcx>),
+ Alias(ty::AliasTy<'tcx>),
}
/// Describes the things that some `GenericKind` value `G` is known to
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
GenericKind::Param(ref p) => write!(f, "{:?}", p),
- GenericKind::Projection(ref p) => write!(f, "{:?}", p),
- GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| {
- write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap()))
- }),
+ GenericKind::Alias(ref p) => write!(f, "{:?}", p),
}
}
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
GenericKind::Param(ref p) => write!(f, "{}", p),
- GenericKind::Projection(ref p) => write!(f, "{}", p),
- GenericKind::Opaque(def_id, substs) => ty::tls::with(|tcx| {
- write!(f, "{}", tcx.def_path_str_with_substs(def_id, tcx.lift(substs).unwrap()))
- }),
+ GenericKind::Alias(ref p) => write!(f, "{}", p),
}
}
}
pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match *self {
GenericKind::Param(ref p) => p.to_ty(tcx),
- GenericKind::Projection(ref p) => tcx.mk_projection(p.def_id, p.substs),
- GenericKind::Opaque(def_id, substs) => tcx.mk_opaque(def_id, substs),
+ GenericKind::Alias(ref p) => p.to_ty(tcx),
}
}
}
} else if !t.has_non_region_infer() {
// All const/type variables in inference types must already be resolved,
// no need to visit the contents.
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
// Otherwise, keep visiting.
t.super_visit_with(self)
} else if !ct.has_non_region_infer() {
// All const/type variables in inference types must already be resolved,
// no need to visit the contents.
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
// Otherwise, keep visiting.
ct.super_visit_with(self)
fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> {
match (value1, value2) {
// We never equate two type variables, both of which
- // have known types. Instead, we recursively equate
+ // have known types. Instead, we recursively equate
// those types.
(&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => {
bug!("equating two type variables, both of which have known types")
use crate::infer::InferCtxt;
use crate::traits::Obligation;
-use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, ToPredicate, Ty};
fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>>;
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
-
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships>;
}
pub trait TraitEngineExt<'tcx> {
mod structural_impls;
pub mod util;
+use hir::def_id::LocalDefId;
use rustc_hir as hir;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::{self, Const, ToPredicate, Ty, TyCtxt};
pub fn misc(
tcx: TyCtxt<'tcx>,
span: Span,
- body_id: hir::HirId,
+ body_id: LocalDefId,
param_env: ty::ParamEnv<'tcx>,
trait_ref: impl ToPredicate<'tcx, O>,
) -> Obligation<'tcx, O> {
use smallvec::smallvec;
use crate::infer::outlives::components::{push_outlives_components, Component};
-use crate::traits::{Obligation, ObligationCause, PredicateObligation};
+use crate::traits::{self, Obligation, ObligationCause, PredicateObligation};
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_middle::ty::{self, ToPredicate, TyCtxt};
use rustc_span::symbol::Ident;
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());
- let obligations = predicates.predicates.iter().map(|&(mut pred, _)| {
+ let obligations = predicates.predicates.iter().map(|&(mut pred, span)| {
// when parent predicate is non-const, elaborate it to non-const predicates.
if data.constness == ty::BoundConstness::NotConst {
pred = pred.without_const(tcx);
}
+ let cause = obligation.cause.clone().derived_cause(
+ bound_predicate.rebind(data),
+ |derived| {
+ traits::ImplDerivedObligation(Box::new(
+ traits::ImplDerivedObligationCause {
+ derived,
+ impl_def_id: data.def_id(),
+ span,
+ },
+ ))
+ },
+ );
predicate_obligation(
pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
obligation.param_env,
- obligation.cause.clone(),
+ cause,
)
});
debug!(?data, ?obligations, "super_predicates");
Component::UnresolvedInferenceVariable(_) => None,
- Component::Opaque(def_id, substs) => {
- let ty = tcx.mk_opaque(def_id, substs);
- Some(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(ty, r_min),
- )))
- }
-
- Component::Projection(projection) => {
+ Component::Alias(alias_ty) => {
// We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
// With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
- let ty = tcx.mk_projection(projection.def_id, projection.substs);
Some(ty::PredicateKind::Clause(ty::Clause::TypeOutlives(
- ty::OutlivesPredicate(ty, r_min),
+ ty::OutlivesPredicate(alias_ty.to_ty(tcx), r_min),
)))
}
- Component::EscapingProjection(_) => {
+ Component::EscapingAlias(_) => {
// We might be able to do more here, but we don't
// want to deal with escaping vars right now.
None
/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may
/// define the given associated type `assoc_name`. It uses the
/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that
-/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
+/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
/// `T::Item` and helps to avoid cycle errors (see e.g. #35237).
pub fn transitive_bounds_that_define_assoc_type<'tcx>(
tcx: TyCtxt<'tcx>,
rustc_privacy = { path = "../rustc_privacy" }
rustc_query_impl = { path = "../rustc_query_impl" }
rustc_resolve = { path = "../rustc_resolve" }
+rustc_target = { path = "../rustc_target" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_ty_utils = { path = "../rustc_ty_utils" }
pub path: &'a Path,
pub error: io::Error,
}
+
+#[derive(Diagnostic)]
+#[diag(interface_proc_macro_crate_panic_abort)]
+pub struct ProcMacroCratePanicAbort;
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 crate::errors::{
CantEmitMIR, EmojiIdentifier, ErrorWritingDependencies, FerrisIdentifier,
GeneratedFileConflictsWithDirectory, InputFileWouldBeOverWritten, MixedBinCrate,
- MixedProcMacroCrate, OutDirError, ProcMacroDocWithoutArg, TempsDirError,
+ MixedProcMacroCrate, OutDirError, ProcMacroCratePanicAbort, ProcMacroDocWithoutArg,
+ TempsDirError,
};
use crate::interface::{Compiler, Result};
use crate::proc_macro_decls;
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_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::{MetadataLoader, MetadataLoaderDyn, Untracked};
use rustc_session::output::filename_for_input;
use rustc_session::search_paths::PathKind;
use rustc_session::{Limit, Session};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::FileName;
+use rustc_target::spec::PanicStrategy;
use rustc_trait_selection::traits;
use std::any::Any;
use std::sync::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)
}
}
+ if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort {
+ sess.emit_warning(ProcMacroCratePanicAbort);
+ }
+
// For backwards compatibility, we don't try to run proc macro injection
// if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being
// specified. This should only affect users who manually invoke 'rustdoc', as
}
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
writeln!(file)?;
}
}
-
- Ok(())
- })();
+ };
match result {
Ok(_) => {
pub fn prepare_outputs(
sess: &Session,
- compiler: &Compiler,
krate: &ast::Crate,
boxed_resolver: &RefCell<BoxedResolver>,
crate_name: Symbol,
let _timer = sess.timer("prepare_outputs");
// 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 });
}
}
- 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.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);
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(
lint_store,
arena,
hir_arena,
- untracked_resolutions,
untracked,
- krate,
dep_graph,
queries.on_disk_cache.as_ref().map(OnDiskCache::as_dyn),
queries.as_dyn(),
rustc_query_impl::query_callbacks(arena),
- crate_name,
- outputs,
)
})
});
- let mut qcx = QueryContext { gcx };
- qcx.enter(|tcx| {
- tcx.feed_unit_query()
- .resolver_for_lowering(tcx.arena.alloc(Steal::new(untracked_resolver_for_lowering)))
- });
- qcx
+ QueryContext { gcx }
}
/// Runs the resolution, type-checking, region checking and other
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};
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 outputs = passes::prepare_outputs(self.session(), &krate, &resolver, crate_name)?;
+
+ 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.output_filenames(tcx.arena.alloc(std::sync::Arc::new(outputs)));
+ feed.features_query(tcx.sess.features_untracked());
+ let feed = tcx.feed_local_crate();
+ feed.crate_name(crate_name);
+ });
+ Ok(qcx)
})
}
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
use rustc_session::config::rustc_optgroups;
+use rustc_session::config::Input;
use rustc_session::config::TraitSolver;
use rustc_session::config::{build_configuration, build_session_options, to_crate_config};
use rustc_session::config::{
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
+use rustc_session::CompilerIO;
use rustc_session::{build_session, getopts, Session};
use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::symbol::sym;
+use rustc_span::FileName;
use rustc_span::SourceFileHashAlgorithm;
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) {
let registry = registry::Registry::new(&[]);
let (sessopts, cfg) = build_session_options_and_crate_config(matches);
- let sess = build_session(sessopts, None, None, registry, Default::default(), None, None);
+ let temps_dir = sessopts.unstable_opts.temps_dir.as_deref().map(PathBuf::from);
+ let io = CompilerIO {
+ input: Input::Str { name: FileName::Custom(String::new()), input: String::new() },
+ output_dir: None,
+ output_file: None,
+ temps_dir,
+ };
+ let sess = build_session(sessopts, io, None, registry, Default::default(), None, None);
(sess, cfg)
}
tracked!(link_only, true);
tracked!(llvm_plugins, vec![String::from("plugin_name")]);
tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
+ tracked!(log_backtrace, Some("filter".to_string()));
tracked!(maximal_hir_to_mir_coverage, true);
tracked!(merge_functions, Some(MergeFunctions::Disabled));
tracked!(mir_emit_retag, true);
use rustc_session as session;
use rustc_session::config::CheckCfg;
use rustc_session::config::{self, CrateType};
-use rustc_session::config::{ErrorOutputType, Input, OutputFilenames};
+use rustc_session::config::{ErrorOutputType, OutputFilenames};
use rustc_session::filesearch::sysroot_candidates;
use rustc_session::lint::{self, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::CrateConfig;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::source_map::FileLoader;
use rustc_span::symbol::{sym, Symbol};
+use session::CompilerIO;
use std::env;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::mem;
cfg: FxHashSet<(String, Option<String>)>,
check_cfg: CheckCfg,
file_loader: Option<Box<dyn FileLoader + Send + Sync + 'static>>,
- input_path: Option<PathBuf>,
+ io: CompilerIO,
lint_caps: FxHashMap<lint::LintId, lint::Level>,
make_codegen_backend: Option<
Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
let mut sess = session::build_session(
sopts,
- input_path,
+ io,
bundle,
descriptions,
lint_caps,
base
}
-pub fn build_output_filenames(
- input: &Input,
- odir: &Option<PathBuf>,
- ofile: &Option<PathBuf>,
- temps_dir: &Option<PathBuf>,
- attrs: &[ast::Attribute],
- sess: &Session,
-) -> OutputFilenames {
- match *ofile {
+pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> OutputFilenames {
+ match sess.io.output_file {
None => {
// "-" as input file will cause the parser to read from stdin so we
// have to make up a name
// We want to toss everything after the final '.'
- let dirpath = (*odir).as_ref().cloned().unwrap_or_default();
+ let dirpath = sess.io.output_dir.clone().unwrap_or_default();
// If a crate name is present, we use it as the link name
let stem = sess
.crate_name
.clone()
.or_else(|| rustc_attr::find_crate_name(sess, attrs).map(|n| n.to_string()))
- .unwrap_or_else(|| input.filestem().to_owned());
+ .unwrap_or_else(|| sess.io.input.filestem().to_owned());
OutputFilenames::new(
dirpath,
stem,
None,
- temps_dir.clone(),
+ sess.io.temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
)
}
Some(out_file.clone())
};
- if *odir != None {
+ if sess.io.output_dir != None {
sess.warn("ignoring --out-dir flag due to -o flag");
}
out_file.parent().unwrap_or_else(|| Path::new("")).to_path_buf(),
out_file.file_stem().unwrap_or_default().to_str().unwrap().to_string(),
ofile,
- temps_dir.clone(),
+ sess.io.temps_dir.clone(),
sess.opts.cg.extra_filename.clone(),
sess.opts.output_types.clone(),
)
//!
//! The idea with `rustc_lexer` is to make a reusable library,
//! by separating out pure lexing and rustc-specific concerns, like spans,
-//! error reporting, and interning. So, rustc_lexer operates directly on `&str`,
+//! error reporting, and interning. So, rustc_lexer operates directly on `&str`,
//! produces simple tokens which are a pair of type-tag and a bit of original text,
//! and does not report errors, instead storing them as flags on the token.
//!
let tail = &tail[first_non_space..];
if let Some(c) = tail.chars().nth(0) {
// For error reporting, we would like the span to contain the character that was not
- // skipped. The +1 is necessary to account for the leading \ that started the escape.
+ // skipped. The +1 is necessary to account for the leading \ that started the escape.
let end = start + first_non_space + c.len_utf8() + 1;
if c.is_whitespace() {
callback(start..end, Err(EscapeError::UnskippedWhitespaceWarning));
+use crate::lints::{ArrayIntoIterDiag, ArrayIntoIterDiagSub};
use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::{fluent, Applicability};
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
// to an array or to a slice.
_ => bug!("array type coerced to something other than array or slice"),
};
- cx.struct_span_lint(
+ let sub = if self.for_expr_span == expr.span {
+ Some(ArrayIntoIterDiagSub::RemoveIntoIter {
+ span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+ })
+ } else if receiver_ty.is_array() {
+ Some(ArrayIntoIterDiagSub::UseExplicitIntoIter {
+ start_span: expr.span.shrink_to_lo(),
+ end_span: receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+ })
+ } else {
+ None
+ };
+ cx.emit_spanned_lint(
ARRAY_INTO_ITER,
call.ident.span,
- fluent::lint_array_into_iter,
- |diag| {
- diag.set_arg("target", target);
- diag.span_suggestion(
- call.ident.span,
- fluent::use_iter_suggestion,
- "iter",
- Applicability::MachineApplicable,
- );
- if self.for_expr_span == expr.span {
- diag.span_suggestion(
- receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
- fluent::remove_into_iter_suggestion,
- "",
- Applicability::MaybeIncorrect,
- );
- } else if receiver_ty.is_array() {
- diag.multipart_suggestion(
- fluent::use_explicit_into_iter_suggestion,
- vec![
- (expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
- (
- receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
- ")".into(),
- ),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- diag
- },
- )
+ ArrayIntoIterDiag { target, suggestion: call.ident.span, sub },
+ );
}
}
}
use crate::{
errors::BuiltinEllpisisInclusiveRangePatterns,
+ lints::{
+ BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern,
+ BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
+ BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
+ BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
+ BuiltinExplicitOutlivesSuggestion, BuiltinIncompleteFeatures,
+ BuiltinIncompleteFeaturesHelp, BuiltinIncompleteFeaturesNote, BuiltinKeywordIdents,
+ BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
+ BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
+ BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
+ BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
+ BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue,
+ BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit,
+ BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
+ BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
+ BuiltinWhileTrue, SuggestChangingAssocTypes,
+ },
types::{transparent_newtype_field, CItemKind},
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
};
use rustc_ast_pretty::pprust::{self, expr_to_string};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
- fluent, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
- DiagnosticStyledString, MultiSpan,
-};
+use rustc_errors::{fluent, Applicability, DecorateLint, MultiSpan};
use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
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};
&& !cond.span.from_expansion()
{
let condition_span = e.span.with_hi(cond.span.hi());
- cx.struct_span_lint(
- WHILE_TRUE,
- condition_span,
- fluent::lint_builtin_while_true,
- |lint| {
- lint.span_suggestion_short(
- condition_span,
- fluent::suggestion,
- format!(
+ let replace = format!(
"{}loop",
label.map_or_else(String::new, |label| format!(
"{}: ",
label.ident,
))
- ),
- Applicability::MachineApplicable,
- )
- },
- )
+ );
+ cx.emit_spanned_lint(WHILE_TRUE, condition_span, BuiltinWhileTrue {
+ suggestion: condition_span,
+ replace,
+ });
}
}
}
for leaf in ty.walk() {
if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
if leaf_ty.is_box() {
- cx.struct_span_lint(
- BOX_POINTERS,
- span,
- fluent::lint_builtin_box_pointers,
- |lint| lint.set_arg("ty", ty),
- );
+ cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
}
}
}
if cx.tcx.find_field_index(ident, &variant)
== Some(cx.typeck_results().field_index(fieldpat.hir_id))
{
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NON_SHORTHAND_FIELD_PATTERNS,
fieldpat.span,
- fluent::lint_builtin_non_shorthand_field_patterns,
- |lint| {
- let suggested_ident =
- format!("{}{}", binding_annot.prefix_str(), ident);
- lint.set_arg("ident", ident).span_suggestion(
- fieldpat.span,
- fluent::suggestion,
- suggested_ident,
- Applicability::MachineApplicable,
- )
+ BuiltinNonShorthandFieldPatterns {
+ ident,
+ suggestion: fieldpat.span,
+ prefix: binding_annot.prefix_str(),
},
);
}
&self,
cx: &EarlyContext<'_>,
span: Span,
- msg: impl Into<DiagnosticMessage>,
- decorate: impl for<'a, 'b> FnOnce(
- &'b mut DiagnosticBuilder<'a, ()>,
- ) -> &'b mut DiagnosticBuilder<'a, ()>,
+ decorate: impl for<'a> DecorateLint<'a, ()>,
) {
// This comes from a macro that has `#[allow_internal_unsafe]`.
if span.allows_unsafe() {
return;
}
- cx.struct_span_lint(UNSAFE_CODE, span, msg, decorate);
- }
-
- fn report_overridden_symbol_name(
- &self,
- cx: &EarlyContext<'_>,
- span: Span,
- msg: DiagnosticMessage,
- ) {
- self.report_unsafe(cx, span, msg, |lint| {
- lint.note(fluent::lint_builtin_overridden_symbol_name)
- })
- }
-
- fn report_overridden_symbol_section(
- &self,
- cx: &EarlyContext<'_>,
- span: Span,
- msg: DiagnosticMessage,
- ) {
- self.report_unsafe(cx, span, msg, |lint| {
- lint.note(fluent::lint_builtin_overridden_symbol_section)
- })
+ cx.emit_spanned_lint(UNSAFE_CODE, span, decorate);
}
}
impl EarlyLintPass for UnsafeCode {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
if attr.has_name(sym::allow_internal_unsafe) {
- self.report_unsafe(cx, attr.span, fluent::lint_builtin_allow_internal_unsafe, |lint| {
- lint
- });
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe);
}
}
if let ast::ExprKind::Block(ref blk, _) = e.kind {
// Don't warn about generated blocks; that'll just pollute the output.
if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) {
- self.report_unsafe(cx, blk.span, fluent::lint_builtin_unsafe_block, |lint| lint);
+ self.report_unsafe(cx, blk.span, BuiltinUnsafe::UnsafeBlock);
}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
match it.kind {
ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => {
- self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_trait, |lint| lint)
+ self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait);
}
ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => {
- self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_impl, |lint| lint)
+ self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl);
}
ast::ItemKind::Fn(..) => {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_no_mangle_fn,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleFn);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_export_name_fn,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameFn);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) {
- self.report_overridden_symbol_section(
- cx,
- attr.span,
- fluent::lint_builtin_link_section_fn,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionFn);
}
}
ast::ItemKind::Static(..) => {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_no_mangle_static,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleStatic);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_export_name_static,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameStatic);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::link_section) {
- self.report_overridden_symbol_section(
- cx,
- attr.span,
- fluent::lint_builtin_link_section_static,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::LinkSectionStatic);
}
}
fn check_impl_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
if let ast::AssocItemKind::Fn(..) = it.kind {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_no_mangle_method,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::NoMangleMethod);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
- self.report_overridden_symbol_name(
- cx,
- attr.span,
- fluent::lint_builtin_export_name_method,
- );
+ self.report_unsafe(cx, attr.span, BuiltinUnsafe::ExportNameMethod);
}
}
}
body,
) = fk
{
- let msg = match ctxt {
+ let decorator = match ctxt {
FnCtxt::Foreign => return,
- FnCtxt::Free => fluent::lint_builtin_decl_unsafe_fn,
- FnCtxt::Assoc(_) if body.is_none() => fluent::lint_builtin_decl_unsafe_method,
- FnCtxt::Assoc(_) => fluent::lint_builtin_impl_unsafe_method,
+ FnCtxt::Free => BuiltinUnsafe::DeclUnsafeFn,
+ FnCtxt::Assoc(_) if body.is_none() => BuiltinUnsafe::DeclUnsafeMethod,
+ FnCtxt::Assoc(_) => BuiltinUnsafe::ImplUnsafeMethod,
};
- self.report_unsafe(cx, span, msg, |lint| lint);
+ self.report_unsafe(cx, span, decorator);
}
}
}
let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id));
let has_doc = attrs.iter().any(has_doc);
if !has_doc {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
MISSING_DOCS,
cx.tcx.def_span(def_id),
- fluent::lint_builtin_missing_doc,
- |lint| lint.set_arg("article", article).set_arg("desc", desc),
+ BuiltinMissingDoc { article, desc },
);
}
}
// 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()
{
- cx.struct_span_lint(
- MISSING_COPY_IMPLEMENTATIONS,
- item.span,
- fluent::lint_builtin_missing_copy_impl,
- |lint| lint,
- )
+ cx.emit_spanned_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, BuiltinMissingCopyImpl);
}
}
}
}
if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
MISSING_DEBUG_IMPLEMENTATIONS,
item.span,
- fluent::lint_builtin_missing_debug_impl,
- |lint| lint.set_arg("debug", cx.tcx.def_path_str(debug)),
+ BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug },
);
}
}
} else {
("<type>", Applicability::HasPlaceholders)
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
ANONYMOUS_PARAMETERS,
arg.pat.span,
- fluent::lint_builtin_anonymous_params,
- |lint| {
- lint.span_suggestion(
- arg.pat.span,
- fluent::suggestion,
- format!("_: {}", ty_snip),
- appl,
- )
- },
- )
+ BuiltinAnonymousParams { suggestion: (arg.pat.span, appl), ty_snip },
+ );
}
}
}
_,
) = gate
{
- // FIXME(davidtwco) translatable deprecated attr
- cx.struct_span_lint(
+ let suggestion = match suggestion {
+ Some(msg) => {
+ BuiltinDeprecatedAttrLinkSuggestion::Msg { suggestion: attr.span, msg }
+ }
+ None => {
+ BuiltinDeprecatedAttrLinkSuggestion::Default { suggestion: attr.span }
+ }
+ };
+ cx.emit_spanned_lint(
DEPRECATED,
attr.span,
- fluent::lint_builtin_deprecated_attr_link,
- |lint| {
- lint.set_arg("name", name)
- .set_arg("reason", reason)
- .set_arg("link", link)
- .span_suggestion_short(
- attr.span,
- suggestion.map(|s| s.into()).unwrap_or(
- fluent::lint_builtin_deprecated_attr_default_suggestion,
- ),
- "",
- Applicability::MachineApplicable,
- )
- },
+ BuiltinDeprecatedAttrLink { name, reason, link, suggestion },
);
}
return;
}
}
if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DEPRECATED,
attr.span,
- fluent::lint_builtin_deprecated_attr_used,
- |lint| {
- lint.set_arg("name", pprust::path_to_string(&attr.get_normal_item().path))
- .span_suggestion_short(
- attr.span,
- fluent::lint_builtin_deprecated_attr_default_suggestion,
- "",
- Applicability::MachineApplicable,
- )
+ BuiltinDeprecatedAttrUsed {
+ name: pprust::path_to_string(&attr.get_normal_item().path),
+ suggestion: attr.span,
},
);
}
let span = sugared_span.take().unwrap_or(attr.span);
if is_doc_comment || attr.has_name(sym::doc) {
- cx.struct_span_lint(
+ let sub = match attr.kind {
+ AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
+ BuiltinUnusedDocCommentSub::PlainHelp
+ }
+ AttrKind::DocComment(CommentKind::Block, _) => {
+ BuiltinUnusedDocCommentSub::BlockHelp
+ }
+ };
+ cx.emit_spanned_lint(
UNUSED_DOC_COMMENTS,
span,
- fluent::lint_builtin_unused_doc_comment,
- |lint| {
- lint.set_arg("kind", node_kind).span_label(node_span, fluent::label).help(
- match attr.kind {
- AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
- fluent::plain_help
- }
- AttrKind::DocComment(CommentKind::Block, _) => fluent::block_help,
- },
- )
- },
+ BuiltinUnusedDocComment { kind: node_kind, label: node_span, sub },
);
}
}
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NO_MANGLE_GENERIC_ITEMS,
span,
- fluent::lint_builtin_no_mangle_generic,
- |lint| {
- lint.span_suggestion_short(
- no_mangle_attr.span,
- fluent::suggestion,
- "",
- // Use of `#[no_mangle]` suggests FFI intent; correct
- // fix may be to monomorphize source by hand
- Applicability::MaybeIncorrect,
- )
- },
+ BuiltinNoMangleGeneric { suggestion: no_mangle_attr.span },
);
break;
}
}
hir::ItemKind::Const(..) => {
if cx.sess().contains_name(attrs, sym::no_mangle) {
+ // account for "pub const" (#45562)
+ let start = cx
+ .tcx
+ .sess
+ .source_map()
+ .span_to_snippet(it.span)
+ .map(|snippet| snippet.find("const").unwrap_or(0))
+ .unwrap_or(0) as u32;
+ // `const` is 5 chars
+ let suggestion = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
+
// Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NO_MANGLE_CONST_ITEMS,
it.span,
- fluent::lint_builtin_const_no_mangle,
- |lint| {
- // account for "pub const" (#45562)
- let start = cx
- .tcx
- .sess
- .source_map()
- .span_to_snippet(it.span)
- .map(|snippet| snippet.find("const").unwrap_or(0))
- .unwrap_or(0) as u32;
- // `const` is 5 chars
- let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
- lint.span_suggestion(
- const_span,
- fluent::suggestion,
- "pub static",
- Applicability::MachineApplicable,
- )
- },
+ BuiltinConstNoMangle { suggestion },
);
}
}
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
{
if from_mutbl < to_mutbl {
- cx.struct_span_lint(
- MUTABLE_TRANSMUTES,
- expr.span,
- fluent::lint_builtin_mutable_transmutes,
- |lint| lint,
- );
+ cx.emit_spanned_lint(MUTABLE_TRANSMUTES, expr.span, BuiltinMutablesTransmutes);
}
}
if attr.has_name(sym::feature) {
if let Some(items) = attr.meta_item_list() {
for item in items {
- cx.struct_span_lint(
- UNSTABLE_FEATURES,
- item.span(),
- fluent::lint_builtin_unstable_features,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNSTABLE_FEATURES, item.span(), BuiltinUnstableFeatures);
}
}
}
// Now, check if the function has the `#[track_caller]` attribute
&& let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller))
{
- cx.struct_span_lint(
- UNGATED_ASYNC_FN_TRACK_CALLER,
- attr.span,
- fluent::lint_ungated_async_fn_track_caller,
- |lint| {
- lint.span_label(span, fluent::label);
- rustc_session::parse::add_feature_diagnostics(
- lint,
- &cx.tcx.sess.parse_sess,
- sym::closure_track_caller,
- );
- lint
- },
- );
+ cx.emit_spanned_lint(UNGATED_ASYNC_FN_TRACK_CALLER, attr.span, BuiltinUngatedAsyncFnTrackCaller {
+ label: span,
+ parse_sess: &cx.tcx.sess.parse_sess,
+ });
}
}
}
applicability = Applicability::MaybeIncorrect;
}
let def_span = cx.tcx.def_span(def_id);
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNREACHABLE_PUB,
def_span,
- fluent::lint_builtin_unreachable_pub,
- |lint| {
- lint.set_arg("what", what);
-
- lint.span_suggestion(vis_span, fluent::suggestion, "pub(crate)", applicability);
- if exportable {
- lint.help(fluent::help);
- }
- lint
+ BuiltinUnreachablePub {
+ what,
+ suggestion: (vis_span, applicability),
+ help: exportable.then_some(()),
},
);
}
);
impl TypeAliasBounds {
- fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
+ pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
match *qpath {
hir::QPath::TypeRelative(ref ty, _) => {
// If this is a type variable, we found a `T::Assoc`.
hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
}
}
-
- fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) {
- // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
- // bound. Let's see if this type does that.
-
- // We use a HIR visitor to walk the type.
- use rustc_hir::intravisit::{self, Visitor};
- struct WalkAssocTypes<'a> {
- err: &'a mut Diagnostic,
- }
- impl Visitor<'_> for WalkAssocTypes<'_> {
- fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) {
- if TypeAliasBounds::is_type_variable_assoc(qpath) {
- self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
- }
- intravisit::walk_qpath(self, qpath, id)
- }
- }
-
- // Let's go for a walk!
- let mut visitor = WalkAssocTypes { err };
- visitor.visit_ty(ty);
- }
}
impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
let mut suggested_changing_assoc_types = false;
if !where_spans.is_empty() {
- cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_where_clause, |lint| {
- lint.set_span(where_spans);
- lint.span_suggestion(
- type_alias_generics.where_clause_span,
- fluent::suggestion,
- "",
- Applicability::MachineApplicable,
- );
- if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
- suggested_changing_assoc_types = true;
- }
- lint
+ let sub = (!suggested_changing_assoc_types).then(|| {
+ suggested_changing_assoc_types = true;
+ SuggestChangingAssocTypes { ty }
});
+ cx.emit_spanned_lint(
+ TYPE_ALIAS_BOUNDS,
+ where_spans,
+ BuiltinTypeAliasWhereClause {
+ suggestion: type_alias_generics.where_clause_span,
+ sub,
+ },
+ );
}
if !inline_spans.is_empty() {
- cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_generic_bounds, |lint| {
- lint.set_span(inline_spans);
- lint.multipart_suggestion(
- fluent::suggestion,
- inline_sugg,
- Applicability::MachineApplicable,
- );
- if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
- }
- lint
+ let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
+ let sub = (!suggested_changing_assoc_types).then(|| {
+ suggested_changing_assoc_types = true;
+ SuggestChangingAssocTypes { ty }
});
+ cx.emit_spanned_lint(
+ TYPE_ALIAS_BOUNDS,
+ inline_spans,
+ BuiltinTypeAliasGenericBounds { suggestion, sub },
+ );
}
}
}
TypeWellFormedFromEnv(..) => continue,
};
if predicate.is_global() {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
TRIVIAL_BOUNDS,
span,
- fluent::lint_builtin_trivial_bounds,
- |lint| {
- lint.set_arg("predicate_kind_name", predicate_kind_name)
- .set_arg("predicate", predicate)
- },
+ BuiltinTrivialBounds { predicate_kind_name, predicate },
);
}
}
};
if let Some((start, end, join)) = endpoints {
- let msg = fluent::lint_builtin_ellipsis_inclusive_range_patterns;
- let suggestion = fluent::suggestion;
if parenthesise {
self.node_id = Some(pat.id);
let end = expr_to_string(&end);
replace,
});
} else {
- cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg, |lint| {
- lint.span_suggestion(
- pat.span,
- suggestion,
+ cx.emit_spanned_lint(
+ ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
+ pat.span,
+ BuiltinEllipsisInclusiveRangePatternsLint::Parenthesise {
+ suggestion: pat.span,
replace,
- Applicability::MachineApplicable,
- )
- });
+ },
+ );
}
} else {
let replace = "..=";
replace: replace.to_string(),
});
} else {
- cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg, |lint| {
- lint.span_suggestion_short(
- join,
- suggestion,
- replace,
- Applicability::MachineApplicable,
- )
- });
+ cx.emit_spanned_lint(
+ ELLIPSIS_INCLUSIVE_RANGE_PATTERNS,
+ join,
+ BuiltinEllipsisInclusiveRangePatternsLint::NonParenthesise {
+ suggestion: join,
+ },
+ );
}
};
}
let attrs = cx.tcx.hir().attrs(it.hir_id());
if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) {
- cx.struct_span_lint(
- UNNAMEABLE_TEST_ITEMS,
- attr.span,
- fluent::lint_builtin_unnameable_test_items,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems);
}
}
return;
}
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
KEYWORD_IDENTS,
ident.span,
- fluent::lint_builtin_keyword_idents,
- |lint| {
- lint.set_arg("kw", ident).set_arg("next", next_edition).span_suggestion(
- ident.span,
- fluent::suggestion,
- format!("r#{}", ident),
- Applicability::MachineApplicable,
- )
- },
+ BuiltinKeywordIdents { kw: ident, next: next_edition, suggestion: ident.span },
);
}
}
Applicability::MaybeIncorrect
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
EXPLICIT_OUTLIVES_REQUIREMENTS,
lint_spans.clone(),
- fluent::lint_builtin_explicit_outlives,
- |lint| {
- lint.set_arg("count", bound_count).multipart_suggestion(
- fluent::suggestion,
- lint_spans.into_iter().map(|span| (span, String::new())).collect(),
+ BuiltinExplicitOutlives {
+ count: bound_count,
+ suggestion: BuiltinExplicitOutlivesSuggestion {
+ spans: lint_spans,
applicability,
- )
+ },
},
);
}
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
.filter(|(&name, _)| features.incomplete(name))
.for_each(|(&name, &span)| {
- cx.struct_span_lint(
+ let note = rustc_feature::find_feature_issue(name, GateIssue::Language)
+ .map(|n| BuiltinIncompleteFeaturesNote { n });
+ let help = if HAS_MIN_FEATURES.contains(&name) {
+ Some(BuiltinIncompleteFeaturesHelp)
+ } else {
+ None
+ };
+ cx.emit_spanned_lint(
INCOMPLETE_FEATURES,
span,
- fluent::lint_builtin_incomplete_features,
- |lint| {
- lint.set_arg("name", name);
- if let Some(n) =
- rustc_feature::find_feature_issue(name, GateIssue::Language)
- {
- lint.set_arg("n", n);
- lint.note(fluent::note);
- }
- if HAS_MIN_FEATURES.contains(&name) {
- lint.help(fluent::help);
- }
- lint
- },
- )
+ BuiltinIncompleteFeatures { name, note, help },
+ );
});
}
}
declare_lint_pass!(InvalidValue => [INVALID_VALUE]);
+/// Information about why a type cannot be initialized this way.
+pub struct InitError {
+ pub(crate) message: String,
+ /// Spans from struct fields and similar that can be obtained from just the type.
+ pub(crate) span: Option<Span>,
+ /// Used to report a trace through adts.
+ pub(crate) nested: Option<Box<InitError>>,
+}
+impl InitError {
+ fn spanned(self, span: Span) -> InitError {
+ Self { span: Some(span), ..self }
+ }
+
+ fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
+ assert!(self.nested.is_none());
+ Self { nested: nested.into().map(Box::new), ..self }
+ }
+}
+
+impl<'a> From<&'a str> for InitError {
+ fn from(s: &'a str) -> Self {
+ s.to_owned().into()
+ }
+}
+impl From<String> for InitError {
+ fn from(message: String) -> Self {
+ Self { message, span: None, nested: None }
+ }
+}
+
impl<'tcx> LateLintPass<'tcx> for InvalidValue {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) {
#[derive(Debug, Copy, Clone, PartialEq)]
Uninit,
}
- /// Information about why a type cannot be initialized this way.
- struct InitError {
- message: String,
- /// Spans from struct fields and similar that can be obtained from just the type.
- span: Option<Span>,
- /// Used to report a trace through adts.
- nested: Option<Box<InitError>>,
- }
- impl InitError {
- fn spanned(self, span: Span) -> InitError {
- Self { span: Some(span), ..self }
- }
-
- fn nested(self, nested: impl Into<Option<InitError>>) -> InitError {
- assert!(self.nested.is_none());
- Self { nested: nested.into().map(Box::new), ..self }
- }
- }
-
- impl<'a> From<&'a str> for InitError {
- fn from(s: &'a str) -> Self {
- s.to_owned().into()
- }
- }
- impl From<String> for InitError {
- fn from(message: String) -> Self {
- Self { message, span: None, nested: None }
- }
- }
-
/// Test if this constant is all-0.
fn is_zero(expr: &hir::Expr<'_>) -> bool {
use hir::ExprKind::*;
// using zeroed or uninitialized memory.
// We are extremely conservative with what we warn about.
let conjured_ty = cx.typeck_results().expr_ty(expr);
- if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
- {
- // FIXME(davidtwco): make translatable
- cx.struct_span_lint(
+ if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) {
+ let msg = match init {
+ InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed,
+ InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_unint,
+ };
+ let sub = BuiltinUnpermittedTypeInitSub { err };
+ cx.emit_spanned_lint(
INVALID_VALUE,
expr.span,
- DelayDm(|| {
- format!(
- "the type `{}` does not permit {}",
- conjured_ty,
- match init {
- InitKind::Zeroed => "zero-initialization",
- InitKind::Uninit => "being left uninitialized",
- },
- )
- }),
- |lint| {
- lint.span_label(
- expr.span,
- "this code causes undefined behavior when executed",
- );
- lint.span_label(
- expr.span,
- "help: use `MaybeUninit<T>` instead, \
- and only call `assume_init` after initialization is done",
- );
- loop {
- if let Some(span) = err.span {
- lint.span_note(span, &err.message);
- } else {
- lint.note(&err.message);
- }
- if let Some(e) = err.nested {
- err = *e;
- } else {
- break;
- }
- }
- lint
- },
+ BuiltinUnpermittedTypeInit { msg, ty: conjured_ty, label: expr.span, sub },
);
}
}
SymbolName::Normal(_) => fi.span,
SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
};
- // Finally, emit the diagnostic.
- let msg = if orig.get_name() == this_fi.ident.name {
- fluent::lint_builtin_clashing_extern_same_name
+ // Finally, emit the diagnostic.
+ let this = this_fi.ident.name;
+ let orig = orig.get_name();
+ let previous_decl_label = get_relevant_span(orig_fi);
+ let mismatch_label = get_relevant_span(this_fi);
+ let sub = BuiltinClashingExternSub {
+ tcx,
+ expected: existing_decl_ty,
+ found: this_decl_ty,
+ };
+ let decorator = if orig == this {
+ BuiltinClashingExtern::SameName {
+ this,
+ orig,
+ previous_decl_label,
+ mismatch_label,
+ sub,
+ }
} else {
- fluent::lint_builtin_clashing_extern_diff_name
+ BuiltinClashingExtern::DiffName {
+ this,
+ orig,
+ previous_decl_label,
+ mismatch_label,
+ sub,
+ }
};
- tcx.struct_span_lint_hir(
+ tcx.emit_spanned_lint(
CLASHING_EXTERN_DECLARATIONS,
this_fi.hir_id(),
get_relevant_span(this_fi),
- msg,
- |lint| {
- let mut expected_str = DiagnosticStyledString::new();
- expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
- let mut found_str = DiagnosticStyledString::new();
- found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
-
- lint.set_arg("this_fi", this_fi.ident.name)
- .set_arg("orig", orig.get_name())
- .span_label(get_relevant_span(orig_fi), fluent::previous_decl_label)
- .span_label(get_relevant_span(this_fi), fluent::mismatch_label)
- // FIXME(davidtwco): translatable expected/found
- .note_expected_found(&"", expected_str, &"", found_str)
- },
+ decorator,
);
}
}
if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
if is_null_ptr(cx, expr_deref) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DEREF_NULLPTR,
expr.span,
- fluent::lint_builtin_deref_nullptr,
- |lint| lint.span_label(expr.span, fluent::label),
+ BuiltinDerefNullptr { label: expr.span },
);
}
}
declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]);
impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
+ #[allow(rustc::diagnostic_outside_of_impl)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if let hir::Expr {
kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }),
}
match item.ident.name.as_str() {
- "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for lib.rs", |lint| {
- lint
- .note("lib.rs is the root of this crate's library target")
- .help("to refer to it from other targets, use the library's name as the path")
- }),
- "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for main.rs", |lint| {
- lint
- .note("a binary crate cannot be used as library")
- }),
- _ => continue
+ "lib" => cx.emit_spanned_lint(
+ SPECIAL_MODULE_NAME,
+ item.span,
+ BuiltinSpecialModuleNameUsed::Lib,
+ ),
+ "main" => cx.emit_spanned_lint(
+ SPECIAL_MODULE_NAME,
+ item.span,
+ BuiltinSpecialModuleNameUsed::Main,
+ ),
+ _ => continue,
}
}
}
let cfg = &cx.sess().parse_sess.config;
let check_cfg = &cx.sess().parse_sess.check_config;
for &(name, value) in cfg {
- if let Some(names_valid) = &check_cfg.names_valid {
- if !names_valid.contains(&name) {
- cx.lookup(
- UNEXPECTED_CFGS,
- None::<MultiSpan>,
- fluent::lint_builtin_unexpected_cli_config_name,
- |diag| diag.help(fluent::help).set_arg("name", name),
- );
- }
+ if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
+ cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
+ name,
+ });
}
- if let Some(value) = value {
- if let Some(values) = &check_cfg.values_valid.get(&name) {
- if !values.contains(&value) {
- cx.lookup(
- UNEXPECTED_CFGS,
- None::<MultiSpan>,
- fluent::lint_builtin_unexpected_cli_config_value,
- |diag| {
- diag.help(fluent::help)
- .set_arg("name", name)
- .set_arg("value", value)
- },
- );
- }
- }
+ if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
+ cx.emit_lint(
+ UNEXPECTED_CFGS,
+ BuiltinUnexpectedCliConfigValue { name, value },
+ );
}
}
}
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");
/// Note that this function should only be called for [`LintExpectationId`]s
/// retrieved from the current lint pass. Buffered or manually created ids can
/// cause ICEs.
+ #[rustc_lint_diagnostics]
fn fulfill_expectation(&self, expectation: LintExpectationId) {
// We need to make sure that submitted expectation ids are correctly fulfilled suppressed
// and stored between compilation sessions. To not manually do these steps, we simply create
&*self.lint_store
}
+ #[rustc_lint_diagnostics]
fn lookup<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
self.builder.lint_store()
}
+ #[rustc_lint_diagnostics]
fn lookup<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
-use crate::{LateContext, LateLintPass, LintContext};
+use crate::{
+ lints::{SupertraitAsDerefTarget, SupertraitAsDerefTargetLabel},
+ LateContext, LateLintPass, LintContext,
+};
-use rustc_errors::DelayDm;
use rustc_hir as hir;
use rustc_middle::{traits::util::supertraits, ty};
use rustc_span::sym;
&& supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self))
.any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal)
{
- cx.struct_span_lint(
- DEREF_INTO_DYN_SUPERTRAIT,
- cx.tcx.def_span(item.owner_id.def_id),
- DelayDm(|| {
- format!(
- "`{t}` implements `Deref` with supertrait `{target_principal}` as target"
- )
- }),
- |lint| {
- if let Some(target_span) = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) {
- lint.span_label(target_span, "target type is set here");
- }
-
- lint
- },
- )
+ let label = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)).map(|label| SupertraitAsDerefTargetLabel {
+ label,
+ });
+ cx.emit_spanned_lint(DEREF_INTO_DYN_SUPERTRAIT, cx.tcx.def_span(item.owner_id.def_id), SupertraitAsDerefTarget {
+ t,
+ target_principal: target_principal.to_string(),
+ label,
+ });
}
}
}
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
// This always-inlined function is for the hot call site.
#[inline(always)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
fn inlined_check_id(&mut self, id: ast::NodeId) {
for early_lint in self.context.buffered.take(id) {
let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint;
}
fn visit_where_predicate(&mut self, p: &'a ast::WherePredicate) {
+ lint_callback!(self, enter_where_predicate, p);
ast_visit::walk_where_predicate(self, p);
+ lint_callback!(self, exit_where_predicate, p);
}
fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
-use crate::{context::LintContext, LateContext, LateLintPass};
-use rustc_errors::fluent;
+use crate::{
+ context::LintContext,
+ lints::{EnumIntrinsicsMemDiscriminate, EnumIntrinsicsMemVariant},
+ LateContext, LateLintPass,
+};
use rustc_hir as hir;
use rustc_middle::ty::{visit::TypeVisitable, Ty};
use rustc_span::{symbol::sym, Span};
) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
ENUM_INTRINSICS_NON_ENUMS,
expr_span,
- fluent::lint_enum_intrinsics_mem_discriminant,
- |lint| lint.set_arg("ty_param", ty_param).span_note(args_span, fluent::note),
+ EnumIntrinsicsMemDiscriminate { ty_param, note: args_span },
);
}
}
fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
ENUM_INTRINSICS_NON_ENUMS,
span,
- fluent::lint_enum_intrinsics_mem_variant,
- |lint| lint.set_arg("ty_param", ty_param).note(fluent::note),
+ EnumIntrinsicsMemVariant { ty_param },
);
}
}
#[derive(Diagnostic)]
#[diag(lint_overruled_attribute, code = "E0453")]
-pub struct OverruledAttribute {
+pub struct OverruledAttribute<'a> {
#[primary_span]
pub span: Span,
#[label]
pub overruled: Span,
- pub lint_level: String,
+ pub lint_level: &'a str,
pub lint_source: Symbol,
#[subdiagnostic]
pub sub: OverruledAttributeSub,
OverruledAttributeSub::NodeSource { span, reason } => {
diag.span_label(span, fluent::lint_node_source);
if let Some(rationale) = reason {
+ #[allow(rustc::untranslatable_diagnostic)]
diag.note(rationale.as_str());
}
}
-use crate::builtin;
-use rustc_errors::fluent;
-use rustc_hir::HirId;
+use crate::lints::{Expectation, ExpectationNote};
use rustc_middle::ty::query::Providers;
-use rustc_middle::{lint::LintExpectation, ty::TyCtxt};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
use rustc_session::lint::LintExpectationId;
use rustc_span::symbol::sym;
use rustc_span::Symbol;
}
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
- if !tcx.sess.features_untracked().enabled(sym::lint_reasons) {
+ if !tcx.features().enabled(sym::lint_reasons) {
return;
}
if !fulfilled_expectations.contains(&id)
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
{
- emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation);
+ let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale });
+ let note = expectation.is_unfulfilled_lint_expectations.then_some(());
+ tcx.emit_spanned_lint(
+ UNFULFILLED_LINT_EXPECTATIONS,
+ *hir_id,
+ expectation.emission_span,
+ Expectation { rationale, note },
+ );
}
} else {
unreachable!("at this stage all `LintExpectationId`s are stable");
}
}
}
-
-fn emit_unfulfilled_expectation_lint(
- tcx: TyCtxt<'_>,
- hir_id: HirId,
- expectation: &LintExpectation,
-) {
- tcx.struct_span_lint_hir(
- builtin::UNFULFILLED_LINT_EXPECTATIONS,
- hir_id,
- expectation.emission_span,
- fluent::lint_expectation,
- |lint| {
- if let Some(rationale) = expectation.reason {
- lint.note(rationale.as_str());
- }
-
- if expectation.is_unfulfilled_lint_expectations {
- lint.note(fluent::note);
- }
-
- lint
- },
- );
-}
-use crate::{LateContext, LateLintPass, LintContext};
+use crate::{
+ lints::{
+ ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark,
+ ForLoopsOverFalliblesSuggestion,
+ },
+ LateContext, LateLintPass, LintContext,
+};
use hir::{Expr, Pat};
-use rustc_errors::{Applicability, DelayDm};
use rustc_hir as hir;
use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
use rustc_middle::ty::{self, List};
_ => return,
};
- let msg = DelayDm(|| {
- format!(
- "for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
- )
- });
-
- cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
- if let Some(recv) = extract_iterator_next_call(cx, arg)
+ let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{
- lint.span_suggestion(
- recv.span.between(arg.span.shrink_to_hi()),
- format!("to iterate over `{recv_snip}` remove the call to `next`"),
- ".by_ref()",
- Applicability::MaybeIncorrect
- );
+ ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip }
} else {
- lint.multipart_suggestion_verbose(
- "to check pattern in a loop use `while let`",
- vec![
- // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
- (expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
- (pat.span.between(arg.span), ") = ".to_string()),
- ],
- Applicability::MaybeIncorrect
- );
- }
-
- if suggest_question_mark(cx, adt, substs, expr.span) {
- lint.span_suggestion(
- arg.span.shrink_to_hi(),
- "consider unwrapping the `Result` with `?` to iterate over its contents",
- "?",
- Applicability::MaybeIncorrect,
- );
- }
-
- lint.multipart_suggestion_verbose(
- "consider using `if let` to clear intent",
- vec![
- // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
- (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
- (pat.span.between(arg.span), ") = ".to_string()),
- ],
- Applicability::MaybeIncorrect,
- )
- })
+ ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var }
+ } ;
+ let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) {
+ Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() })
+ } else {
+ None
+ };
+ let suggestion = ForLoopsOverFalliblesSuggestion {
+ var,
+ start_span: expr.span.with_hi(pat.span.lo()),
+ end_span: pat.span.between(arg.span),
+ };
+
+ cx.emit_spanned_lint(
+ FOR_LOOPS_OVER_FALLIBLES,
+ arg.span,
+ ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
+ );
}
}
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(
-use crate::{EarlyContext, EarlyLintPass, LintContext};
+use crate::{
+ lints::{
+ HiddenUnicodeCodepointsDiag, HiddenUnicodeCodepointsDiagLabels,
+ HiddenUnicodeCodepointsDiagSub,
+ },
+ EarlyContext, EarlyLintPass, LintContext,
+};
use ast::util::unicode::{contains_text_flow_control_chars, TEXT_FLOW_CONTROL_CHARS};
use rustc_ast as ast;
-use rustc_errors::{fluent, Applicability, SuggestionStyle};
use rustc_span::{BytePos, Span, Symbol};
declare_lint! {
})
.collect();
- cx.struct_span_lint(
+ let count = spans.len();
+ let labels = point_at_inner_spans
+ .then_some(HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() });
+ let sub = if point_at_inner_spans && !spans.is_empty() {
+ HiddenUnicodeCodepointsDiagSub::Escape { spans }
+ } else {
+ HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
+ };
+
+ cx.emit_spanned_lint(
TEXT_DIRECTION_CODEPOINT_IN_LITERAL,
span,
- fluent::lint_hidden_unicode_codepoints,
- |lint| {
- lint.set_arg("label", label);
- lint.set_arg("count", spans.len());
- lint.span_label(span, fluent::label);
- lint.note(fluent::note);
- if point_at_inner_spans {
- for (c, span) in &spans {
- lint.span_label(*span, format!("{:?}", c));
- }
- }
- if point_at_inner_spans && !spans.is_empty() {
- lint.multipart_suggestion_with_style(
- fluent::suggestion_remove,
- spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
- Applicability::MachineApplicable,
- SuggestionStyle::HideCodeAlways,
- );
- lint.multipart_suggestion(
- fluent::suggestion_escape,
- spans
- .into_iter()
- .map(|(c, span)| {
- let c = format!("{:?}", c);
- (span, c[1..c.len() - 1].to_string())
- })
- .collect(),
- Applicability::MachineApplicable,
- );
- } else {
- // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
- // should do the same here to provide the same good suggestions as we do for
- // literals above.
- lint.set_arg(
- "escaped",
- spans
- .into_iter()
- .map(|(c, _)| format!("{:?}", c))
- .collect::<Vec<String>>()
- .join(", "),
- );
- lint.note(fluent::suggestion_remove);
- lint.note(fluent::no_suggestion_note_escape);
- }
- lint
- },
+ HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub },
);
}
}
//! Some lints that are only useful in the compiler or crates that use compiler internals, such as
//! Clippy.
+use crate::lints::{
+ BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistantDocKeyword,
+ QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
+};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
-use rustc_errors::{fluent, Applicability};
use rustc_hir::def::Res;
use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
// don't lint imports, only actual usages
return;
}
- let replace = match cx.tcx.get_diagnostic_name(def_id) {
+ let preferred = match cx.tcx.get_diagnostic_name(def_id) {
Some(sym::HashMap) => "FxHashMap",
Some(sym::HashSet) => "FxHashSet",
_ => return,
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DEFAULT_HASH_TYPES,
path.span,
- fluent::lint_default_hash_types,
- |lint| {
- lint.set_arg("preferred", replace)
- .set_arg("used", cx.tcx.item_name(def_id))
- .note(fluent::note)
- },
+ DefaultHashTypesDiag { preferred, used: cx.tcx.item_name(def_id) },
);
}
}
if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
POTENTIAL_QUERY_INSTABILITY,
span,
- fluent::lint_query_instability,
- |lint| lint.set_arg("query", cx.tcx.item_name(def_id)).note(fluent::note),
- )
+ QueryInstability { query: cx.tcx.item_name(def_id) },
+ );
}
}
}
let span = path.span.with_hi(
segment.args.map_or(segment.ident.span, |a| a.span_ext).hi()
);
- cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, fluent::lint_tykind_kind, |lint| {
- lint
- .span_suggestion(
- span,
- fluent::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
+ cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind {
+ suggestion: span,
});
}
}
match span {
Some(span) => {
- cx.struct_span_lint(
- USAGE_OF_TY_TYKIND,
- path.span,
- fluent::lint_tykind_kind,
- |lint| lint.span_suggestion(
- span,
- fluent::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
- )
+ cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindKind {
+ suggestion: span,
+ });
},
- None => cx.struct_span_lint(
- USAGE_OF_TY_TYKIND,
- path.span,
- fluent::lint_tykind,
- |lint| lint.help(fluent::help)
- )
- }
- } else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
- if path.segments.len() > 1 {
- cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, fluent::lint_ty_qualified, |lint| {
- lint
- .set_arg("ty", t.clone())
- .span_suggestion(
- path.span,
- fluent::suggestion,
- t,
- // The import probably needs to be changed
- Applicability::MaybeIncorrect,
- )
- })
+ None => cx.emit_spanned_lint(USAGE_OF_TY_TYKIND, path.span, TykindDiag),
}
+ } else if !ty.span.from_expansion() && path.segments.len() > 1 && let Some(ty) = is_ty_or_ty_ctxt(cx, &path) {
+ cx.emit_spanned_lint(USAGE_OF_QUALIFIED_TY, path.span, TyQualified {
+ ty,
+ suggestion: path.span,
+ });
}
}
_ => {}
&& call_site.ctxt().outer_expn_data().kind
!= ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
{
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span,
- fluent::lint_lintpass_by_hand,
- |lint| lint.help(fluent::help),
- )
+ LintPassByHand,
+ );
}
}
}
if let Some(list) = attr.meta_item_list() {
for nested in list {
if nested.has_name(sym::keyword) {
- let v = nested
+ let keyword = nested
.value_str()
.expect("#[doc(keyword = \"...\")] expected a value!");
- if is_doc_keyword(v) {
+ if is_doc_keyword(keyword) {
return;
}
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
EXISTING_DOC_KEYWORD,
attr.span,
- fluent::lint_non_existant_doc_keyword,
- |lint| lint.set_arg("keyword", v).help(fluent::help),
+ NonExistantDocKeyword { keyword },
);
}
}
}
debug!(?found_impl);
if !found_parent_with_attr && !found_impl {
- cx.struct_span_lint(
- DIAGNOSTIC_OUTSIDE_OF_IMPL,
- span,
- fluent::lint_diag_out_of_impl,
- |lint| lint,
- )
+ cx.emit_spanned_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
}
let mut found_diagnostic_message = false;
}
debug!(?found_diagnostic_message);
if !found_parent_with_attr && !found_diagnostic_message {
- cx.struct_span_lint(
- UNTRANSLATABLE_DIAGNOSTIC,
- span,
- fluent::lint_untranslatable_diag,
- |lint| lint,
- )
+ cx.emit_spanned_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag);
}
}
}
let Some(lit) = item.lit() &&
let ast::LitKind::Str(val, _) = lit.kind
{
- cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, val.as_str(), |lint|
- lint
- );
+ cx.emit_spanned_lint(BAD_OPT_ACCESS, expr.span, BadOptAccessDiag {
+ msg: val.as_str(),
+ });
}
}
}
-use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan};
+use crate::{
+ lints::{NonBindingLet, NonBindingLetSub},
+ LateContext, LateLintPass, LintContext,
+};
+use rustc_errors::MultiSpan;
use rustc_hir as hir;
use rustc_middle::ty;
use rustc_span::Symbol;
_ => false,
};
+ let sub = NonBindingLetSub {
+ suggestion: local.pat.span,
+ multi_suggestion_start: local.span.until(init.span),
+ multi_suggestion_end: init.span.shrink_to_hi(),
+ };
if is_sync_lock {
let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
span.push_span_label(
init.span,
"this binding will immediately drop the value assigned to it".to_string(),
);
- cx.struct_span_lint(
- LET_UNDERSCORE_LOCK,
- span,
- "non-binding let on a synchronization lock",
- |lint| build_lint(lint, local, init.span),
- )
+ cx.emit_spanned_lint(LET_UNDERSCORE_LOCK, span, NonBindingLet::SyncLock { sub });
} else {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
LET_UNDERSCORE_DROP,
local.span,
- "non-binding let on a type that implements `Drop`",
- |lint| build_lint(lint, local, init.span),
- )
+ NonBindingLet::DropType { sub },
+ );
}
}
}
}
-
-fn build_lint<'a, 'b>(
- lint: &'a mut DiagnosticBuilder<'b, ()>,
- local: &hir::Local<'_>,
- init_span: rustc_span::Span,
-) -> &'a mut DiagnosticBuilder<'b, ()> {
- lint.span_suggestion_verbose(
- local.pat.span,
- "consider binding to an unused variable to avoid immediately dropping the value",
- "_unused",
- Applicability::MachineApplicable,
- )
- .multipart_suggestion(
- "consider immediately dropping the value",
- vec![
- (local.span.until(init_span), "drop(".to_string()),
- (init_span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MachineApplicable,
- )
-}
use crate::context::{CheckLintNameResult, LintStore};
use crate::late::unerased_lint_store;
+use crate::lints::{
+ DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAtributeLint, RenamedOrRemovedLint,
+ RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
+};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
+use rustc_errors::{fluent, DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId;
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
+use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
Level, Lint, LintExpectationId, LintId,
}
}
-/// Specifications found at this position in the stack. This map only represents the lints
+/// Specifications found at this position in the stack. This map only represents the lints
/// found for one set of attributes (like `shallow_lint_levels_on` does).
///
/// We store the level specifications as a linked list.
match attrs.map.range(..) {
// There is only something to do if there are attributes at all.
[] => {}
- // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
+ // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
[(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
// Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do
// a standard visit.
old_src,
id_name
);
-
- let decorate_diag = |diag: &mut Diagnostic| {
- diag.span_label(src.span(), "overruled by previous forbid");
- match old_src {
- LintLevelSource::Default => {
- diag.note(&format!(
- "`forbid` lint level is the default for {}",
- id.to_string()
- ));
- }
- LintLevelSource::Node { span, reason, .. } => {
- diag.span_label(span, "`forbid` level set here");
- if let Some(rationale) = reason {
- diag.note(rationale.as_str());
- }
- }
- LintLevelSource::CommandLine(_, _) => {
- diag.note("`forbid` lint level was set on command line");
- }
+ let sub = match old_src {
+ LintLevelSource::Default => {
+ OverruledAttributeSub::DefaultSource { id: id.to_string() }
}
+ LintLevelSource::Node { span, reason, .. } => {
+ OverruledAttributeSub::NodeSource { span, reason }
+ }
+ LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource,
};
if !fcw_warning {
self.sess.emit_err(OverruledAttribute {
span: src.span(),
overruled: src.span(),
- lint_level: level.as_str().to_string(),
+ lint_level: level.as_str(),
lint_source: src.name(),
- sub: match old_src {
- LintLevelSource::Default => {
- OverruledAttributeSub::DefaultSource { id: id.to_string() }
- }
- LintLevelSource::Node { span, reason, .. } => {
- OverruledAttributeSub::NodeSource { span, reason }
- }
- LintLevelSource::CommandLine(_, _) => {
- OverruledAttributeSub::CommandLineSource
- }
- },
+ sub,
});
} else {
- self.struct_lint(
+ self.emit_spanned_lint(
FORBIDDEN_LINT_GROUPS,
- Some(src.span().into()),
- format!(
- "{}({}) incompatible with previous forbid",
- level.as_str(),
- src.name(),
- ),
- |lint| {
- decorate_diag(lint);
- lint
+ src.span().into(),
+ OverruledAtributeLint {
+ overruled: src.span(),
+ lint_level: level.as_str(),
+ lint_source: src.name(),
+ sub,
},
);
}
}
Err((Some(ids), ref new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (lvl, src) = self.provider.get_lint_level(lint, &sess);
- struct_lint_level(
- self.sess,
+ self.emit_spanned_lint(
lint,
- lvl,
- src,
- Some(sp.into()),
- format!(
- "lint name `{}` is deprecated \
- and may not have an effect in the future.",
- name
- ),
- |lint| {
- lint.span_suggestion(
- sp,
- "change it to",
- new_lint_name,
- Applicability::MachineApplicable,
- )
+ sp.into(),
+ DeprecatedLintName {
+ name,
+ suggestion: sp,
+ replace: &new_lint_name,
},
);
_ if !self.warn_about_weird_lints => {}
CheckLintNameResult::Warning(msg, renamed) => {
- let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess);
- struct_lint_level(
- self.sess,
- lint,
- renamed_lint_level,
- src,
- Some(sp.into()),
- msg,
- |lint| {
- if let Some(new_name) = &renamed {
- lint.span_suggestion(
- sp,
- "use the new name",
- new_name,
- Applicability::MachineApplicable,
- );
- }
- lint
- },
+ let suggestion =
+ renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
+ suggestion: sp,
+ replace: replace.as_str(),
+ });
+ self.emit_spanned_lint(
+ RENAMED_AND_REMOVED_LINTS,
+ sp.into(),
+ RenamedOrRemovedLint { msg, suggestion },
);
}
CheckLintNameResult::NoLint(suggestion) => {
- let lint = builtin::UNKNOWN_LINTS;
- let (level, src) = self.provider.get_lint_level(lint, self.sess);
let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name)
} else {
name.to_string()
};
- struct_lint_level(
- self.sess,
- lint,
- level,
- src,
- Some(sp.into()),
- format!("unknown lint: `{}`", name),
- |lint| {
- if let Some(suggestion) = suggestion {
- lint.span_suggestion(
- sp,
- "did you mean",
- suggestion,
- Applicability::MaybeIncorrect,
- );
- }
- lint
- },
+ let suggestion = suggestion
+ .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
+ self.emit_spanned_lint(
+ UNKNOWN_LINTS,
+ sp.into(),
+ UnknownLint { name, suggestion },
);
}
}
continue
};
- let lint = builtin::UNUSED_ATTRIBUTES;
- let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess);
- struct_lint_level(
- self.sess,
- lint,
- lint_level,
- lint_src,
- Some(lint_attr_span.into()),
- format!(
- "{}({}) is ignored unless specified at crate level",
- level.as_str(),
- lint_attr_name
- ),
- |lint| lint,
+ self.emit_spanned_lint(
+ UNUSED_ATTRIBUTES,
+ lint_attr_span.into(),
+ IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name },
);
// don't set a separate error for every lint in the group
break;
level,
src,
Some(span.into()),
- format!("unknown lint: `{}`", lint_id.lint.name_lower()),
+ fluent::lint_unknown_gated_lint,
|lint| {
- lint.note(
- &format!("the `{}` lint is unstable", lint_id.lint.name_lower(),),
- );
+ lint.set_arg("name", lint_id.lint.name_lower());
+ lint.note(fluent::note);
add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
lint
},
let (level, src) = self.lint_level(lint);
struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
}
+
+ pub fn emit_spanned_lint(
+ &self,
+ lint: &'static Lint,
+ span: MultiSpan,
+ decorate: impl for<'a> DecorateLint<'a, ()>,
+ ) {
+ let (level, src) = self.lint_level(lint);
+ struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| {
+ decorate.decorate_lint(lint)
+ });
+ }
+
+ pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) {
+ let (level, src) = self.lint_level(lint);
+ struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| {
+ decorate.decorate_lint(lint)
+ });
+ }
}
pub(crate) fn provide(providers: &mut Providers) {
#![feature(never_type)]
#![feature(rustc_attrs)]
#![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate rustc_middle;
mod late;
mod let_underscore;
mod levels;
+mod lints;
mod methods;
mod non_ascii_idents;
mod non_fmt_panic;
[
pub BuiltinCombinedEarlyLintPass,
[
- UnusedParens: UnusedParens,
+ UnusedParens: UnusedParens::new(),
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
--- /dev/null
+#![allow(rustc::untranslatable_diagnostic)]
+#![allow(rustc::diagnostic_outside_of_impl)]
+use std::num::NonZeroU32;
+
+use rustc_errors::{
+ fluent, AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage,
+ DiagnosticStyledString, SuggestionStyle,
+};
+use rustc_hir::def_id::DefId;
+use rustc_macros::{LintDiagnostic, Subdiagnostic};
+use rustc_middle::ty::{Predicate, Ty, TyCtxt};
+use rustc_session::parse::ParseSess;
+use rustc_span::{edition::Edition, sym, symbol::Ident, Span, Symbol};
+
+use crate::{
+ builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
+};
+
+// array_into_iter.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_array_into_iter)]
+pub struct ArrayIntoIterDiag<'a> {
+ pub target: &'a str,
+ #[suggestion(use_iter_suggestion, code = "iter", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ #[subdiagnostic]
+ pub sub: Option<ArrayIntoIterDiagSub>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ArrayIntoIterDiagSub {
+ #[suggestion(remove_into_iter_suggestion, code = "", applicability = "maybe-incorrect")]
+ RemoveIntoIter {
+ #[primary_span]
+ span: Span,
+ },
+ #[multipart_suggestion(use_explicit_into_iter_suggestion, applicability = "maybe-incorrect")]
+ UseExplicitIntoIter {
+ #[suggestion_part(code = "IntoIterator::into_iter(")]
+ start_span: Span,
+ #[suggestion_part(code = ")")]
+ end_span: Span,
+ },
+}
+
+// builtin.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_while_true)]
+pub struct BuiltinWhileTrue {
+ #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub replace: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_box_pointers)]
+pub struct BuiltinBoxPointers<'a> {
+ pub ty: Ty<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_non_shorthand_field_patterns)]
+pub struct BuiltinNonShorthandFieldPatterns {
+ pub ident: Ident,
+ #[suggestion(code = "{prefix}{ident}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub prefix: &'static str,
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinUnsafe {
+ #[diag(lint_builtin_allow_internal_unsafe)]
+ AllowInternalUnsafe,
+ #[diag(lint_builtin_unsafe_block)]
+ UnsafeBlock,
+ #[diag(lint_builtin_unsafe_trait)]
+ UnsafeTrait,
+ #[diag(lint_builtin_unsafe_impl)]
+ UnsafeImpl,
+ #[diag(lint_builtin_no_mangle_fn)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ NoMangleFn,
+ #[diag(lint_builtin_export_name_fn)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ ExportNameFn,
+ #[diag(lint_builtin_link_section_fn)]
+ #[note(lint_builtin_overridden_symbol_section)]
+ LinkSectionFn,
+ #[diag(lint_builtin_no_mangle_static)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ NoMangleStatic,
+ #[diag(lint_builtin_export_name_static)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ ExportNameStatic,
+ #[diag(lint_builtin_link_section_static)]
+ #[note(lint_builtin_overridden_symbol_section)]
+ LinkSectionStatic,
+ #[diag(lint_builtin_no_mangle_method)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ NoMangleMethod,
+ #[diag(lint_builtin_export_name_method)]
+ #[note(lint_builtin_overridden_symbol_name)]
+ ExportNameMethod,
+ #[diag(lint_builtin_decl_unsafe_fn)]
+ DeclUnsafeFn,
+ #[diag(lint_builtin_decl_unsafe_method)]
+ DeclUnsafeMethod,
+ #[diag(lint_builtin_impl_unsafe_method)]
+ ImplUnsafeMethod,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_missing_doc)]
+pub struct BuiltinMissingDoc<'a> {
+ pub article: &'a str,
+ pub desc: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_missing_copy_impl)]
+pub struct BuiltinMissingCopyImpl;
+
+pub struct BuiltinMissingDebugImpl<'a> {
+ pub tcx: TyCtxt<'a>,
+ pub def_id: DefId,
+}
+
+// Needed for def_path_str
+impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("debug", self.tcx.def_path_str(self.def_id));
+ diag
+ }
+
+ fn msg(&self) -> DiagnosticMessage {
+ fluent::lint_builtin_missing_debug_impl
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_anonymous_params)]
+pub struct BuiltinAnonymousParams<'a> {
+ #[suggestion(code = "_: {ty_snip}")]
+ pub suggestion: (Span, Applicability),
+ pub ty_snip: &'a str,
+}
+
+// FIXME(davidtwco) translatable deprecated attr
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deprecated_attr_link)]
+pub struct BuiltinDeprecatedAttrLink<'a> {
+ pub name: Symbol,
+ pub reason: &'a str,
+ pub link: &'a str,
+ #[subdiagnostic]
+ pub suggestion: BuiltinDeprecatedAttrLinkSuggestion<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum BuiltinDeprecatedAttrLinkSuggestion<'a> {
+ #[suggestion(msg_suggestion, code = "", applicability = "machine-applicable")]
+ Msg {
+ #[primary_span]
+ suggestion: Span,
+ msg: &'a str,
+ },
+ #[suggestion(default_suggestion, code = "", applicability = "machine-applicable")]
+ Default {
+ #[primary_span]
+ suggestion: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deprecated_attr_used)]
+pub struct BuiltinDeprecatedAttrUsed {
+ pub name: String,
+ #[suggestion(
+ lint_builtin_deprecated_attr_default_suggestion,
+ style = "short",
+ code = "",
+ applicability = "machine-applicable"
+ )]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unused_doc_comment)]
+pub struct BuiltinUnusedDocComment<'a> {
+ pub kind: &'a str,
+ #[label]
+ pub label: Span,
+ #[subdiagnostic]
+ pub sub: BuiltinUnusedDocCommentSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum BuiltinUnusedDocCommentSub {
+ #[help(plain_help)]
+ PlainHelp,
+ #[help(block_help)]
+ BlockHelp,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_no_mangle_generic)]
+pub struct BuiltinNoMangleGeneric {
+ // Use of `#[no_mangle]` suggests FFI intent; correct
+ // fix may be to monomorphize source by hand
+ #[suggestion(style = "short", code = "", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_const_no_mangle)]
+pub struct BuiltinConstNoMangle {
+ #[suggestion(code = "pub static", applicability = "machine-applicable")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_mutable_transmutes)]
+pub struct BuiltinMutablesTransmutes;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unstable_features)]
+pub struct BuiltinUnstableFeatures;
+
+// lint_ungated_async_fn_track_caller
+pub struct BuiltinUngatedAsyncFnTrackCaller<'a> {
+ pub label: Span,
+ pub parse_sess: &'a ParseSess,
+}
+
+impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.span_label(self.label, fluent::label);
+ rustc_session::parse::add_feature_diagnostics(
+ diag,
+ &self.parse_sess,
+ sym::closure_track_caller,
+ );
+ diag
+ }
+
+ fn msg(&self) -> DiagnosticMessage {
+ fluent::lint_ungated_async_fn_track_caller
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unreachable_pub)]
+pub struct BuiltinUnreachablePub<'a> {
+ pub what: &'a str,
+ #[suggestion(code = "pub(crate)")]
+ pub suggestion: (Span, Applicability),
+ #[help]
+ pub help: Option<()>,
+}
+
+pub struct SuggestChangingAssocTypes<'a, 'b> {
+ pub ty: &'a rustc_hir::Ty<'b>,
+}
+
+impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
+ // bound. Let's see if this type does that.
+
+ // We use a HIR visitor to walk the type.
+ use rustc_hir::intravisit::{self, Visitor};
+ struct WalkAssocTypes<'a> {
+ err: &'a mut rustc_errors::Diagnostic,
+ }
+ impl Visitor<'_> for WalkAssocTypes<'_> {
+ fn visit_qpath(
+ &mut self,
+ qpath: &rustc_hir::QPath<'_>,
+ id: rustc_hir::HirId,
+ span: Span,
+ ) {
+ if TypeAliasBounds::is_type_variable_assoc(qpath) {
+ self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
+ }
+ intravisit::walk_qpath(self, qpath, id)
+ }
+ }
+
+ // Let's go for a walk!
+ let mut visitor = WalkAssocTypes { err: diag };
+ visitor.visit_ty(self.ty);
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_type_alias_where_clause)]
+pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
+ #[suggestion(code = "", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ #[subdiagnostic]
+ pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_type_alias_generic_bounds)]
+pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
+ #[subdiagnostic]
+ pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
+ #[subdiagnostic]
+ pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
+}
+
+pub struct BuiltinTypeAliasGenericBoundsSuggestion {
+ pub suggestions: Vec<(Span, String)>,
+}
+
+impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ diag.multipart_suggestion(
+ fluent::suggestion,
+ self.suggestions,
+ Applicability::MachineApplicable,
+ );
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_trivial_bounds)]
+pub struct BuiltinTrivialBounds<'a> {
+ pub predicate_kind_name: &'a str,
+ pub predicate: Predicate<'a>,
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinEllipsisInclusiveRangePatternsLint {
+ #[diag(lint_builtin_ellipsis_inclusive_range_patterns)]
+ Parenthesise {
+ #[suggestion(code = "{replace}", applicability = "machine-applicable")]
+ suggestion: Span,
+ replace: String,
+ },
+ #[diag(lint_builtin_ellipsis_inclusive_range_patterns)]
+ NonParenthesise {
+ #[suggestion(style = "short", code = "..=", applicability = "machine-applicable")]
+ suggestion: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unnameable_test_items)]
+pub struct BuiltinUnnameableTestItems;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_keyword_idents)]
+pub struct BuiltinKeywordIdents {
+ pub kw: Ident,
+ pub next: Edition,
+ #[suggestion(code = "r#{kw}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_explicit_outlives)]
+pub struct BuiltinExplicitOutlives {
+ pub count: usize,
+ #[subdiagnostic]
+ pub suggestion: BuiltinExplicitOutlivesSuggestion,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion)]
+pub struct BuiltinExplicitOutlivesSuggestion {
+ #[suggestion_part(code = "")]
+ pub spans: Vec<Span>,
+ #[applicability]
+ pub applicability: Applicability,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_incomplete_features)]
+pub struct BuiltinIncompleteFeatures {
+ pub name: Symbol,
+ #[subdiagnostic]
+ pub note: Option<BuiltinIncompleteFeaturesNote>,
+ #[subdiagnostic]
+ pub help: Option<BuiltinIncompleteFeaturesHelp>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(help)]
+pub struct BuiltinIncompleteFeaturesHelp;
+
+#[derive(Subdiagnostic)]
+#[note(note)]
+pub struct BuiltinIncompleteFeaturesNote {
+ pub n: NonZeroU32,
+}
+
+pub struct BuiltinUnpermittedTypeInit<'a> {
+ pub msg: DiagnosticMessage,
+ pub ty: Ty<'a>,
+ pub label: Span,
+ pub sub: BuiltinUnpermittedTypeInitSub,
+}
+
+impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("ty", self.ty);
+ diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label);
+ diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label_suggestion);
+ self.sub.add_to_diagnostic(diag);
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ self.msg.clone()
+ }
+}
+
+// FIXME(davidtwco): make translatable
+pub struct BuiltinUnpermittedTypeInitSub {
+ pub err: InitError,
+}
+
+impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ let mut err = self.err;
+ loop {
+ if let Some(span) = err.span {
+ diag.span_note(span, err.message);
+ } else {
+ diag.note(err.message);
+ }
+ if let Some(e) = err.nested {
+ err = *e;
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinClashingExtern<'a> {
+ #[diag(lint_builtin_clashing_extern_same_name)]
+ SameName {
+ this: Symbol,
+ orig: Symbol,
+ #[label(previous_decl_label)]
+ previous_decl_label: Span,
+ #[label(mismatch_label)]
+ mismatch_label: Span,
+ #[subdiagnostic]
+ sub: BuiltinClashingExternSub<'a>,
+ },
+ #[diag(lint_builtin_clashing_extern_diff_name)]
+ DiffName {
+ this: Symbol,
+ orig: Symbol,
+ #[label(previous_decl_label)]
+ previous_decl_label: Span,
+ #[label(mismatch_label)]
+ mismatch_label: Span,
+ #[subdiagnostic]
+ sub: BuiltinClashingExternSub<'a>,
+ },
+}
+
+// FIXME(davidtwco): translatable expected/found
+pub struct BuiltinClashingExternSub<'a> {
+ pub tcx: TyCtxt<'a>,
+ pub expected: Ty<'a>,
+ pub found: Ty<'a>,
+}
+
+impl AddToDiagnostic for BuiltinClashingExternSub<'_> {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ let mut expected_str = DiagnosticStyledString::new();
+ expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
+ let mut found_str = DiagnosticStyledString::new();
+ found_str.push(self.found.fn_sig(self.tcx).to_string(), true);
+ diag.note_expected_found(&"", expected_str, &"", found_str);
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_deref_nullptr)]
+pub struct BuiltinDerefNullptr {
+ #[label]
+ pub label: Span,
+}
+
+// FIXME: migrate fluent::lint::builtin_asm_labels
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinSpecialModuleNameUsed {
+ #[diag(lint_builtin_special_module_name_used_lib)]
+ #[note]
+ #[help]
+ Lib,
+ #[diag(lint_builtin_special_module_name_used_main)]
+ #[note]
+ Main,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unexpected_cli_config_name)]
+#[help]
+pub struct BuiltinUnexpectedCliConfigName {
+ pub name: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_builtin_unexpected_cli_config_value)]
+#[help]
+pub struct BuiltinUnexpectedCliConfigValue {
+ pub name: Symbol,
+ pub value: Symbol,
+}
+
+// deref_into_dyn_supertrait.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_supertrait_as_deref_target)]
+pub struct SupertraitAsDerefTarget<'a> {
+ pub t: Ty<'a>,
+ pub target_principal: String,
+ // pub target_principal: Binder<'a, ExistentialTraitRef<'b>>,
+ #[subdiagnostic]
+ pub label: Option<SupertraitAsDerefTargetLabel>,
+}
+
+#[derive(Subdiagnostic)]
+#[label(label)]
+pub struct SupertraitAsDerefTargetLabel {
+ #[primary_span]
+ pub label: Span,
+}
+
+// enum_intrinsics_non_enums.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_enum_intrinsics_mem_discriminant)]
+pub struct EnumIntrinsicsMemDiscriminate<'a> {
+ pub ty_param: Ty<'a>,
+ #[note]
+ pub note: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_enum_intrinsics_mem_variant)]
+#[note]
+pub struct EnumIntrinsicsMemVariant<'a> {
+ pub ty_param: Ty<'a>,
+}
+
+// expect.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_expectation)]
+pub struct Expectation {
+ #[subdiagnostic]
+ pub rationale: Option<ExpectationNote>,
+ #[note]
+ pub note: Option<()>,
+}
+
+#[derive(Subdiagnostic)]
+#[note(rationale)]
+pub struct ExpectationNote {
+ pub rationale: Symbol,
+}
+
+// for_loops_over_fallibles.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_for_loops_over_fallibles)]
+pub struct ForLoopsOverFalliblesDiag<'a> {
+ pub article: &'static str,
+ pub ty: &'static str,
+ #[subdiagnostic]
+ pub sub: ForLoopsOverFalliblesLoopSub<'a>,
+ #[subdiagnostic]
+ pub question_mark: Option<ForLoopsOverFalliblesQuestionMark>,
+ #[subdiagnostic]
+ pub suggestion: ForLoopsOverFalliblesSuggestion<'a>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum ForLoopsOverFalliblesLoopSub<'a> {
+ #[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
+ RemoveNext {
+ #[primary_span]
+ suggestion: Span,
+ recv_snip: String,
+ },
+ #[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")]
+ UseWhileLet {
+ #[suggestion_part(code = "while let {var}(")]
+ start_span: Span,
+ #[suggestion_part(code = ") = ")]
+ end_span: Span,
+ var: &'a str,
+ },
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")]
+pub struct ForLoopsOverFalliblesQuestionMark {
+ #[primary_span]
+ pub suggestion: Span,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
+pub struct ForLoopsOverFalliblesSuggestion<'a> {
+ pub var: &'a str,
+ #[suggestion_part(code = "if let {var}(")]
+ pub start_span: Span,
+ #[suggestion_part(code = ") = ")]
+ pub end_span: Span,
+}
+
+// hidden_unicode_codepoints.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_hidden_unicode_codepoints)]
+#[note]
+pub struct HiddenUnicodeCodepointsDiag<'a> {
+ pub label: &'a str,
+ pub count: usize,
+ #[label]
+ pub span_label: Span,
+ #[subdiagnostic]
+ pub labels: Option<HiddenUnicodeCodepointsDiagLabels>,
+ #[subdiagnostic]
+ pub sub: HiddenUnicodeCodepointsDiagSub,
+}
+
+pub struct HiddenUnicodeCodepointsDiagLabels {
+ pub spans: Vec<(char, Span)>,
+}
+
+impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ for (c, span) in self.spans {
+ diag.span_label(span, format!("{:?}", c));
+ }
+ }
+}
+
+pub enum HiddenUnicodeCodepointsDiagSub {
+ Escape { spans: Vec<(char, Span)> },
+ NoEscape { spans: Vec<(char, Span)> },
+}
+
+// Used because of multiple multipart_suggestion and note
+impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
+ diag.multipart_suggestion_with_style(
+ fluent::suggestion_remove,
+ spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
+ Applicability::MachineApplicable,
+ SuggestionStyle::HideCodeAlways,
+ );
+ diag.multipart_suggestion(
+ fluent::suggestion_escape,
+ spans
+ .into_iter()
+ .map(|(c, span)| {
+ let c = format!("{:?}", c);
+ (span, c[1..c.len() - 1].to_string())
+ })
+ .collect(),
+ Applicability::MachineApplicable,
+ );
+ }
+ HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => {
+ // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
+ // should do the same here to provide the same good suggestions as we do for
+ // literals above.
+ diag.set_arg(
+ "escaped",
+ spans
+ .into_iter()
+ .map(|(c, _)| format!("{:?}", c))
+ .collect::<Vec<String>>()
+ .join(", "),
+ );
+ diag.note(fluent::suggestion_remove);
+ diag.note(fluent::no_suggestion_note_escape);
+ }
+ }
+ }
+}
+
+// internal.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_default_hash_types)]
+#[note]
+pub struct DefaultHashTypesDiag<'a> {
+ pub preferred: &'a str,
+ pub used: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_query_instability)]
+#[note]
+pub struct QueryInstability {
+ pub query: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_tykind_kind)]
+pub struct TykindKind {
+ #[suggestion(code = "ty", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_tykind)]
+#[help]
+pub struct TykindDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_ty_qualified)]
+pub struct TyQualified {
+ pub ty: String,
+ #[suggestion(code = "{ty}", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_lintpass_by_hand)]
+#[help]
+pub struct LintPassByHand;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_existant_doc_keyword)]
+#[help]
+pub struct NonExistantDocKeyword {
+ pub keyword: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_diag_out_of_impl)]
+pub struct DiagOutOfImpl;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_untranslatable_diag)]
+pub struct UntranslatableDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_bad_opt_access)]
+pub struct BadOptAccessDiag<'a> {
+ pub msg: &'a str,
+}
+
+// let_underscore.rs
+#[derive(LintDiagnostic)]
+pub enum NonBindingLet {
+ #[diag(lint_non_binding_let_on_sync_lock)]
+ SyncLock {
+ #[subdiagnostic]
+ sub: NonBindingLetSub,
+ },
+ #[diag(lint_non_binding_let_on_drop_type)]
+ DropType {
+ #[subdiagnostic]
+ sub: NonBindingLetSub,
+ },
+}
+
+pub struct NonBindingLetSub {
+ pub suggestion: Span,
+ pub multi_suggestion_start: Span,
+ pub multi_suggestion_end: Span,
+}
+
+impl AddToDiagnostic for NonBindingLetSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ diag.span_suggestion_verbose(
+ self.suggestion,
+ fluent::lint_non_binding_let_suggestion,
+ "_unused",
+ Applicability::MachineApplicable,
+ );
+ diag.multipart_suggestion(
+ fluent::lint_non_binding_let_multi_suggestion,
+ vec![
+ (self.multi_suggestion_start, "drop(".to_string()),
+ (self.multi_suggestion_end, ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+}
+
+// levels.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_overruled_attribute)]
+pub struct OverruledAtributeLint<'a> {
+ #[label]
+ pub overruled: Span,
+ pub lint_level: &'a str,
+ pub lint_source: Symbol,
+ #[subdiagnostic]
+ pub sub: OverruledAttributeSub,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_deprecated_lint_name)]
+pub struct DeprecatedLintName<'a> {
+ pub name: String,
+ #[suggestion(code = "{replace}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub replace: &'a str,
+}
+
+// FIXME: Non-translatable msg
+#[derive(LintDiagnostic)]
+#[diag(lint_renamed_or_removed_lint)]
+pub struct RenamedOrRemovedLint<'a> {
+ pub msg: &'a str,
+ #[subdiagnostic]
+ pub suggestion: Option<RenamedOrRemovedLintSuggestion<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(suggestion, code = "{replace}", applicability = "machine-applicable")]
+pub struct RenamedOrRemovedLintSuggestion<'a> {
+ #[primary_span]
+ pub suggestion: Span,
+ pub replace: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unknown_lint)]
+pub struct UnknownLint {
+ pub name: String,
+ #[subdiagnostic]
+ pub suggestion: Option<UnknownLintSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+pub struct UnknownLintSuggestion {
+ #[primary_span]
+ pub suggestion: Span,
+ pub replace: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_ignored_unless_crate_specified)]
+pub struct IgnoredUnlessCrateSpecified<'a> {
+ pub level: &'a str,
+ pub name: Symbol,
+}
+
+// methods.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_cstring_ptr)]
+#[note]
+#[help]
+pub struct CStringPtr {
+ #[label(as_ptr_label)]
+ pub as_ptr: Span,
+ #[label(unwrap_label)]
+ pub unwrap: Span,
+}
+
+// non_ascii_idents.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_identifier_non_ascii_char)]
+pub struct IdentifierNonAsciiChar;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_identifier_uncommon_codepoints)]
+pub struct IdentifierUncommonCodepoints;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_confusable_identifier_pair)]
+pub struct ConfusableIdentifierPair {
+ pub existing_sym: Symbol,
+ pub sym: Symbol,
+ #[label]
+ pub label: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_mixed_script_confusables)]
+#[note(includes_note)]
+#[note]
+pub struct MixedScriptConfusables {
+ pub set: String,
+ pub includes: String,
+}
+
+// non_fmt_panic.rs
+pub struct NonFmtPanicUnused {
+ pub count: usize,
+ pub suggestion: Option<Span>,
+}
+
+// Used because of two suggestions based on one Option<Span>
+impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("count", self.count);
+ diag.note(fluent::note);
+ if let Some(span) = self.suggestion {
+ diag.span_suggestion(
+ span.shrink_to_hi(),
+ fluent::add_args_suggestion,
+ ", ...",
+ Applicability::HasPlaceholders,
+ );
+ diag.span_suggestion(
+ span.shrink_to_lo(),
+ fluent::add_fmt_suggestion,
+ "\"{}\", ",
+ Applicability::MachineApplicable,
+ );
+ }
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_non_fmt_panic_unused
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_fmt_panic_braces)]
+#[note]
+pub struct NonFmtPanicBraces {
+ pub count: usize,
+ #[suggestion(code = "\"{{}}\", ", applicability = "machine-applicable")]
+ pub suggestion: Option<Span>,
+}
+
+// nonstandard_style.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_non_camel_case_type)]
+pub struct NonCamelCaseType<'a> {
+ pub sort: &'a str,
+ pub name: &'a str,
+ #[subdiagnostic]
+ pub sub: NonCamelCaseTypeSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum NonCamelCaseTypeSub {
+ #[label(label)]
+ Label {
+ #[primary_span]
+ span: Span,
+ },
+ #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ replace: String,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_snake_case)]
+pub struct NonSnakeCaseDiag<'a> {
+ pub sort: &'a str,
+ pub name: &'a str,
+ pub sc: String,
+ #[subdiagnostic]
+ pub sub: NonSnakeCaseDiagSub,
+}
+
+pub enum NonSnakeCaseDiagSub {
+ Label { span: Span },
+ Help,
+ RenameOrConvertSuggestion { span: Span, suggestion: Ident },
+ ConvertSuggestion { span: Span, suggestion: String },
+ SuggestionAndNote { span: Span },
+}
+
+impl AddToDiagnostic for NonSnakeCaseDiagSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ NonSnakeCaseDiagSub::Label { span } => {
+ diag.span_label(span, fluent::label);
+ }
+ NonSnakeCaseDiagSub::Help => {
+ diag.help(fluent::help);
+ }
+ NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion } => {
+ diag.span_suggestion(
+ span,
+ fluent::convert_suggestion,
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ NonSnakeCaseDiagSub::RenameOrConvertSuggestion { span, suggestion } => {
+ diag.span_suggestion(
+ span,
+ fluent::rename_or_convert_suggestion,
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ NonSnakeCaseDiagSub::SuggestionAndNote { span } => {
+ diag.note(fluent::cannot_convert_note);
+ diag.span_suggestion(
+ span,
+ fluent::rename_suggestion,
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_non_upper_case_global)]
+pub struct NonUpperCaseGlobal<'a> {
+ pub sort: &'a str,
+ pub name: &'a str,
+ #[subdiagnostic]
+ pub sub: NonUpperCaseGlobalSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum NonUpperCaseGlobalSub {
+ #[label(label)]
+ Label {
+ #[primary_span]
+ span: Span,
+ },
+ #[suggestion(suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ replace: String,
+ },
+}
+
+// noop_method_call.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_noop_method_call)]
+#[note]
+pub struct NoopMethodCallDiag<'a> {
+ pub method: Symbol,
+ pub receiver_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+}
+
+// pass_by_value.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_pass_by_value)]
+pub struct PassByValueDiag {
+ pub ty: String,
+ #[suggestion(code = "{ty}", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+// redundant_semicolon.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_redundant_semicolons)]
+pub struct RedundantSemicolonsDiag {
+ pub multiple: bool,
+ #[suggestion(code = "", applicability = "maybe-incorrect")]
+ pub suggestion: Span,
+}
+
+// traits.rs
+pub struct DropTraitConstraintsDiag<'a> {
+ pub predicate: Predicate<'a>,
+ pub tcx: TyCtxt<'a>,
+ pub def_id: DefId,
+}
+
+// Needed for def_path_str
+impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("predicate", self.predicate);
+ diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id))
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_drop_trait_constraints
+ }
+}
+
+pub struct DropGlue<'a> {
+ pub tcx: TyCtxt<'a>,
+ pub def_id: DefId,
+}
+
+// Needed for def_path_str
+impl<'a> DecorateLint<'a, ()> for DropGlue<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id))
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_drop_glue
+ }
+}
+
+// types.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_range_endpoint_out_of_range)]
+pub struct RangeEndpointOutOfRange<'a> {
+ pub ty: &'a str,
+ #[suggestion(code = "{start}..={literal}{suffix}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub start: String,
+ pub literal: u128,
+ pub suffix: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_bin_hex)]
+pub struct OverflowingBinHex<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+ pub dec: u128,
+ pub actually: String,
+ #[subdiagnostic]
+ pub sign: OverflowingBinHexSign,
+ #[subdiagnostic]
+ pub sub: Option<OverflowingBinHexSub<'a>>,
+}
+
+pub enum OverflowingBinHexSign {
+ Positive,
+ Negative,
+}
+
+impl AddToDiagnostic for OverflowingBinHexSign {
+ fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+ where
+ F: Fn(
+ &mut rustc_errors::Diagnostic,
+ rustc_errors::SubdiagnosticMessage,
+ ) -> rustc_errors::SubdiagnosticMessage,
+ {
+ match self {
+ OverflowingBinHexSign::Positive => {
+ diag.note(fluent::positive_note);
+ }
+ OverflowingBinHexSign::Negative => {
+ diag.note(fluent::negative_note);
+ diag.note(fluent::negative_becomes_note);
+ }
+ }
+ }
+}
+
+#[derive(Subdiagnostic)]
+pub enum OverflowingBinHexSub<'a> {
+ #[suggestion(
+ suggestion,
+ code = "{sans_suffix}{suggestion_ty}",
+ applicability = "machine-applicable"
+ )]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ suggestion_ty: &'a str,
+ sans_suffix: &'a str,
+ },
+ #[help(help)]
+ Help { suggestion_ty: &'a str },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_int)]
+#[note]
+pub struct OverflowingInt<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+ pub min: i128,
+ pub max: u128,
+ #[subdiagnostic]
+ pub help: Option<OverflowingIntHelp<'a>>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(help)]
+pub struct OverflowingIntHelp<'a> {
+ pub suggestion_ty: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_only_cast_u8_to_char)]
+pub struct OnlyCastu8ToChar {
+ #[suggestion(code = "'\\u{{{literal:X}}}'", applicability = "machine-applicable")]
+ pub span: Span,
+ pub literal: u128,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_uint)]
+#[note]
+pub struct OverflowingUInt<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+ pub min: u128,
+ pub max: u128,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_overflowing_literal)]
+#[note]
+pub struct OverflowingLiteral<'a> {
+ pub ty: &'a str,
+ pub lit: String,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_comparisons)]
+pub struct UnusedComparisons;
+
+pub struct ImproperCTypes<'a> {
+ pub ty: Ty<'a>,
+ pub desc: &'a str,
+ pub label: Span,
+ pub help: Option<DiagnosticMessage>,
+ pub note: DiagnosticMessage,
+ pub span_note: Option<Span>,
+}
+
+// Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span>
+impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("ty", self.ty);
+ diag.set_arg("desc", self.desc);
+ diag.span_label(self.label, fluent::label);
+ if let Some(help) = self.help {
+ diag.help(help);
+ }
+ diag.note(self.note);
+ if let Some(note) = self.span_note {
+ diag.span_note(note, fluent::note);
+ }
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_improper_ctypes
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_variant_size_differences)]
+pub struct VariantSizeDifferencesDiag {
+ pub largest: u64,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_load)]
+#[help]
+pub struct AtomicOrderingLoad;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_store)]
+#[help]
+pub struct AtomicOrderingStore;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_fence)]
+#[help]
+pub struct AtomicOrderingFence;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_atomic_ordering_invalid)]
+#[help]
+pub struct InvalidAtomicOrderingDiag {
+ pub method: Symbol,
+ #[label]
+ pub fail_order_arg_span: Span,
+}
+
+// unused.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_op)]
+pub struct UnusedOp<'a> {
+ pub op: &'a str,
+ #[label]
+ pub label: Span,
+ #[suggestion(style = "verbose", code = "let _ = ", applicability = "machine-applicable")]
+ pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_result)]
+pub struct UnusedResult<'a> {
+ pub ty: Ty<'a>,
+}
+
+// FIXME(davidtwco): this isn't properly translatable becauses of the
+// pre/post strings
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_closure)]
+#[note]
+pub struct UnusedClosure<'a> {
+ pub count: usize,
+ pub pre: &'a str,
+ pub post: &'a str,
+}
+
+// FIXME(davidtwco): this isn't properly translatable becauses of the
+// pre/post strings
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_generator)]
+#[note]
+pub struct UnusedGenerator<'a> {
+ pub count: usize,
+ pub pre: &'a str,
+ pub post: &'a str,
+}
+
+// FIXME(davidtwco): this isn't properly translatable becauses of the pre/post
+// strings
+pub struct UnusedDef<'a, 'b> {
+ pub pre: &'a str,
+ pub post: &'a str,
+ pub cx: &'a LateContext<'b>,
+ pub def_id: DefId,
+ pub note: Option<Symbol>,
+}
+
+// Needed because of def_path_str
+impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
+ fn decorate_lint<'b>(
+ self,
+ diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> {
+ diag.set_arg("pre", self.pre);
+ diag.set_arg("post", self.post);
+ diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id));
+ // check for #[must_use = "..."]
+ if let Some(note) = self.note {
+ diag.note(note.as_str());
+ }
+ diag
+ }
+
+ fn msg(&self) -> rustc_errors::DiagnosticMessage {
+ fluent::lint_unused_def
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_path_statement_drop)]
+pub struct PathStatementDrop {
+ #[subdiagnostic]
+ pub sub: PathStatementDropSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum PathStatementDropSub {
+ #[suggestion(suggestion, code = "drop({snippet});", applicability = "machine-applicable")]
+ Suggestion {
+ #[primary_span]
+ span: Span,
+ snippet: String,
+ },
+ #[help(help)]
+ Help {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_path_statement_no_effect)]
+pub struct PathStatementNoEffect;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_delim)]
+pub struct UnusedDelim<'a> {
+ pub delim: &'static str,
+ pub item: &'a str,
+ #[subdiagnostic]
+ pub suggestion: Option<UnusedDelimSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(suggestion, applicability = "machine-applicable")]
+pub struct UnusedDelimSuggestion {
+ #[suggestion_part(code = "{start_replace}")]
+ pub start_span: Span,
+ pub start_replace: &'static str,
+ #[suggestion_part(code = "{end_replace}")]
+ pub end_span: Span,
+ pub end_replace: &'static str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_import_braces)]
+pub struct UnusedImportBracesDiag {
+ pub node: Symbol,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_allocation)]
+pub struct UnusedAllocationDiag;
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unused_allocation_mut)]
+pub struct UnusedAllocationMutDiag;
+use crate::lints::CStringPtr;
use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
-use rustc_errors::fluent;
use rustc_hir::{Expr, ExprKind, PathSegment};
use rustc_middle::ty;
use rustc_span::{symbol::sym, ExpnKind, Span};
if cx.tcx.is_diagnostic_item(sym::Result, def.did()) {
if let ty::Adt(adt, _) = substs.type_at(0).kind() {
if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
TEMPORARY_CSTRING_AS_PTR,
as_ptr_span,
- fluent::lint_cstring_ptr,
- |diag| {
- diag.span_label(as_ptr_span, fluent::as_ptr_label)
- .span_label(unwrap.span, fluent::unwrap_label)
- .note(fluent::note)
- .help(fluent::help)
- },
+ CStringPtr { as_ptr: as_ptr_span, unwrap: unwrap.span },
);
}
}
+use crate::lints::{
+ ConfusableIdentifierPair, IdentifierNonAsciiChar, IdentifierUncommonCodepoints,
+ MixedScriptConfusables,
+};
use crate::{EarlyContext, EarlyLintPass, LintContext};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::fluent;
use rustc_span::symbol::Symbol;
declare_lint! {
continue;
}
has_non_ascii_idents = true;
- cx.struct_span_lint(
- NON_ASCII_IDENTS,
- sp,
- fluent::lint_identifier_non_ascii_char,
- |lint| lint,
- );
+ cx.emit_spanned_lint(NON_ASCII_IDENTS, sp, IdentifierNonAsciiChar);
if check_uncommon_codepoints
&& !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed)
{
- cx.struct_span_lint(
- UNCOMMON_CODEPOINTS,
- sp,
- fluent::lint_identifier_uncommon_codepoints,
- |lint| lint,
- )
+ cx.emit_spanned_lint(UNCOMMON_CODEPOINTS, sp, IdentifierUncommonCodepoints);
}
}
.entry(skeleton_sym)
.and_modify(|(existing_symbol, existing_span, existing_is_ascii)| {
if !*existing_is_ascii || !is_ascii {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
CONFUSABLE_IDENTS,
sp,
- fluent::lint_confusable_identifier_pair,
- |lint| {
- lint.set_arg("existing_sym", *existing_symbol)
- .set_arg("sym", symbol)
- .span_label(*existing_span, fluent::label)
+ ConfusableIdentifierPair {
+ existing_sym: *existing_symbol,
+ sym: symbol,
+ label: *existing_span,
},
);
}
}
for ((sp, ch_list), script_set) in lint_reports {
- cx.struct_span_lint(
+ let mut includes = String::new();
+ for (idx, ch) in ch_list.into_iter().enumerate() {
+ if idx != 0 {
+ includes += ", ";
+ }
+ let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
+ includes += &char_info;
+ }
+ cx.emit_spanned_lint(
MIXED_SCRIPT_CONFUSABLES,
sp,
- fluent::lint_mixed_script_confusables,
- |lint| {
- let mut includes = String::new();
- for (idx, ch) in ch_list.into_iter().enumerate() {
- if idx != 0 {
- includes += ", ";
- }
- let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
- includes += &char_info;
- }
- lint.set_arg("set", script_set.to_string())
- .set_arg("includes", includes)
- .note(fluent::includes_note)
- .note(fluent::note)
- },
+ MixedScriptConfusables { set: script_set.to_string(), includes },
);
}
}
+use crate::lints::{NonFmtPanicBraces, NonFmtPanicUnused};
use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_errors::{fluent, Applicability};
arg_span = expn.call_site;
}
+ #[allow(rustc::diagnostic_outside_of_impl)]
cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
lint.set_arg("name", symbol);
lint.note(fluent::note);
.map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
.collect(),
};
- cx.struct_span_lint(NON_FMT_PANICS, arg_spans, fluent::lint_non_fmt_panic_unused, |lint| {
- lint.set_arg("count", n_arguments);
- lint.note(fluent::note);
- if is_arg_inside_call(arg.span, span) {
- lint.span_suggestion(
- arg.span.shrink_to_hi(),
- fluent::add_args_suggestion,
- ", ...",
- Applicability::HasPlaceholders,
- );
- lint.span_suggestion(
- arg.span.shrink_to_lo(),
- fluent::add_fmt_suggestion,
- "\"{}\", ",
- Applicability::MachineApplicable,
- );
- }
- lint
- });
+ cx.emit_spanned_lint(
+ NON_FMT_PANICS,
+ arg_spans,
+ NonFmtPanicUnused {
+ count: n_arguments,
+ suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span),
+ },
+ );
} else {
let brace_spans: Option<Vec<_>> =
snippet.filter(|s| s.starts_with('"') || s.starts_with("r#")).map(|s| {
.collect()
});
let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2);
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
NON_FMT_PANICS,
brace_spans.unwrap_or_else(|| vec![span]),
- fluent::lint_non_fmt_panic_braces,
- |lint| {
- lint.set_arg("count", count);
- lint.note(fluent::note);
- if is_arg_inside_call(arg.span, span) {
- lint.span_suggestion(
- arg.span.shrink_to_lo(),
- fluent::suggestion,
- "\"{}\", ",
- Applicability::MachineApplicable,
- );
- }
- lint
+ NonFmtPanicBraces {
+ count,
+ suggestion: is_arg_inside_call(arg.span, span).then_some(arg.span.shrink_to_lo()),
},
);
}
+use crate::lints::{
+ NonCamelCaseType, NonCamelCaseTypeSub, NonSnakeCaseDiag, NonSnakeCaseDiagSub,
+ NonUpperCaseGlobal, NonUpperCaseGlobalSub,
+};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_attr as attr;
-use rustc_errors::{fluent, Applicability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::FnKind;
let name = ident.name.as_str();
if !is_camel_case(name) {
- cx.struct_span_lint(
+ let cc = to_camel_case(name);
+ let sub = if *name != cc {
+ NonCamelCaseTypeSub::Suggestion { span: ident.span, replace: cc }
+ } else {
+ NonCamelCaseTypeSub::Label { span: ident.span }
+ };
+ cx.emit_spanned_lint(
NON_CAMEL_CASE_TYPES,
ident.span,
- fluent::lint_non_camel_case_type,
- |lint| {
- let cc = to_camel_case(name);
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Lowercase Letter".
- if *name != cc {
- lint.span_suggestion(
- ident.span,
- fluent::suggestion,
- to_camel_case(name),
- Applicability::MaybeIncorrect,
- );
- } else {
- lint.span_label(ident.span, fluent::label);
- }
-
- lint.set_arg("sort", sort);
- lint.set_arg("name", name);
- lint
- },
- )
+ NonCamelCaseType { sort, name, sub },
+ );
}
}
}
let name = ident.name.as_str();
if !is_snake_case(name) {
- cx.struct_span_lint(NON_SNAKE_CASE, ident.span, fluent::lint_non_snake_case, |lint| {
- let sc = NonSnakeCase::to_snake_case(name);
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Uppercase Letter".
- if name != sc {
- // We have a valid span in almost all cases, but we don't have one when linting a crate
- // name provided via the command line.
- if !ident.span.is_dummy() {
- let sc_ident = Ident::from_str_and_span(&sc, ident.span);
- let (message, suggestion) = if sc_ident.is_reserved() {
- // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers.
- // Instead, recommend renaming the identifier entirely or, if permitted,
- // escaping it to create a raw identifier.
- if sc_ident.name.can_be_raw() {
- (fluent::rename_or_convert_suggestion, sc_ident.to_string())
- } else {
- lint.note(fluent::cannot_convert_note);
- (fluent::rename_suggestion, String::new())
+ let span = ident.span;
+ let sc = NonSnakeCase::to_snake_case(name);
+ // We cannot provide meaningful suggestions
+ // if the characters are in the category of "Uppercase Letter".
+ let sub = if name != sc {
+ // We have a valid span in almost all cases, but we don't have one when linting a crate
+ // name provided via the command line.
+ if !span.is_dummy() {
+ let sc_ident = Ident::from_str_and_span(&sc, span);
+ if sc_ident.is_reserved() {
+ // We shouldn't suggest a reserved identifier to fix non-snake-case identifiers.
+ // Instead, recommend renaming the identifier entirely or, if permitted,
+ // escaping it to create a raw identifier.
+ if sc_ident.name.can_be_raw() {
+ NonSnakeCaseDiagSub::RenameOrConvertSuggestion {
+ span,
+ suggestion: sc_ident,
}
} else {
- (fluent::convert_suggestion, sc.clone())
- };
-
- lint.span_suggestion(
- ident.span,
- message,
- suggestion,
- Applicability::MaybeIncorrect,
- );
+ NonSnakeCaseDiagSub::SuggestionAndNote { span }
+ }
} else {
- lint.help(fluent::help);
+ NonSnakeCaseDiagSub::ConvertSuggestion { span, suggestion: sc.clone() }
}
} else {
- lint.span_label(ident.span, fluent::label);
+ NonSnakeCaseDiagSub::Help
}
-
- lint.set_arg("sort", sort);
- lint.set_arg("name", name);
- lint.set_arg("sc", sc);
- lint
- });
+ } else {
+ NonSnakeCaseDiagSub::Label { span }
+ };
+ cx.emit_spanned_lint(NON_SNAKE_CASE, span, NonSnakeCaseDiag { sort, name, sc, sub });
}
}
}
fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
let name = ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) {
- cx.struct_span_lint(
+ let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
+ // We cannot provide meaningful suggestions
+ // if the characters are in the category of "Lowercase Letter".
+ let sub = if *name != uc {
+ NonUpperCaseGlobalSub::Suggestion { span: ident.span, replace: uc }
+ } else {
+ NonUpperCaseGlobalSub::Label { span: ident.span }
+ };
+ cx.emit_spanned_lint(
NON_UPPER_CASE_GLOBALS,
ident.span,
- fluent::lint_non_upper_case_global,
- |lint| {
- let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Lowercase Letter".
- if *name != uc {
- lint.span_suggestion(
- ident.span,
- fluent::suggestion,
- uc,
- Applicability::MaybeIncorrect,
- );
- } else {
- lint.span_label(ident.span, fluent::label);
- }
-
- lint.set_arg("sort", sort);
- lint.set_arg("name", name);
- lint
- },
- )
+ NonUpperCaseGlobal { sort, name, sub },
+ );
}
}
}
use crate::context::LintContext;
+use crate::lints::NoopMethodCallDiag;
use crate::LateContext;
use crate::LateLintPass;
-use rustc_errors::fluent;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
}
let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi());
- cx.struct_span_lint(NOOP_METHOD_CALL, span, fluent::lint_noop_method_call, |lint| {
- lint.set_arg("method", call.ident.name)
- .set_arg("receiver_ty", receiver_ty)
- .span_label(span, fluent::label)
- .note(fluent::note)
- });
+ cx.emit_spanned_lint(
+ NOOP_METHOD_CALL,
+ span,
+ NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
+ );
}
}
+use crate::lints::PassByValueDiag;
use crate::{LateContext, LateLintPass, LintContext};
-use rustc_errors::{fluent, Applicability};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::{GenericArg, PathSegment, QPath, TyKind};
}
}
if let Some(t) = path_for_pass_by_value(cx, &inner_ty) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
PASS_BY_VALUE,
ty.span,
- fluent::lint_pass_by_value,
- |lint| {
- lint.set_arg("ty", t.clone()).span_suggestion(
- ty.span,
- fluent::suggestion,
- t,
- // Changing type of function argument
- Applicability::MaybeIncorrect,
- )
- },
- )
+ PassByValueDiag { ty: t, suggestion: ty.span },
+ );
}
}
_ => {}
/// Counterpart to `enter_lint_attrs`.
fn exit_lint_attrs(a: &[ast::Attribute]);
+
+ fn enter_where_predicate(a: &ast::WherePredicate);
+ fn exit_where_predicate(a: &ast::WherePredicate);
]);
)
}
-use crate::{EarlyContext, EarlyLintPass, LintContext};
+use crate::{lints::RedundantSemicolonsDiag, EarlyContext, EarlyLintPass, LintContext};
use rustc_ast::{Block, StmtKind};
-use rustc_errors::{fluent, Applicability};
use rustc_span::Span;
declare_lint! {
return;
}
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
REDUNDANT_SEMICOLONS,
span,
- fluent::lint_redundant_semicolons,
- |lint| {
- lint.set_arg("multiple", multiple).span_suggestion(
- span,
- fluent::suggestion,
- "",
- Applicability::MaybeIncorrect,
- )
- },
+ RedundantSemicolonsDiag { multiple, suggestion: span },
);
}
}
+use crate::lints::{DropGlue, DropTraitConstraintsDiag};
use crate::LateContext;
use crate::LateLintPass;
use crate::LintContext;
-use rustc_errors::fluent;
use rustc_hir as hir;
use rustc_span::symbol::sym;
if trait_predicate.trait_ref.self_ty().is_impl_trait() {
continue;
}
- let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
- continue;
+ let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+ return
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
DROP_BOUNDS,
span,
- fluent::lint_drop_trait_constraints,
- |lint| {
- lint.set_arg("predicate", predicate)
- .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
- },
+ DropTraitConstraintsDiag { predicate, tcx: cx.tcx, def_id },
);
}
}
};
for bound in &bounds[..] {
let def_id = bound.trait_ref.trait_def_id();
- if cx.tcx.lang_items().drop_trait() == def_id
- && let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop)
- {
- cx.struct_span_lint(DYN_DROP, bound.span, fluent::lint_drop_glue, |lint| {
- lint.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
- });
+ if cx.tcx.lang_items().drop_trait() == def_id {
+ let Some(def_id) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+ return
+ };
+ cx.emit_spanned_lint(DYN_DROP, bound.span, DropGlue { tcx: cx.tcx, def_id });
}
}
}
+use crate::lints::{
+ AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
+ InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
+ OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
+ RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
+};
use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{fluent, Applicability, DiagnosticMessage};
+use rustc_errors::{fluent, DiagnosticMessage};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
-use rustc_macros::LintDiagnostic;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
};
let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
- cx.struct_span_lint(
+ use rustc_ast::{LitIntType, LitKind};
+ let suffix = match lit.node {
+ LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
+ LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
+ LitKind::Int(_, LitIntType::Unsuffixed) => "",
+ _ => bug!(),
+ };
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
struct_expr.span,
- fluent::lint_range_endpoint_out_of_range,
- |lint| {
- use ast::{LitIntType, LitKind};
-
- lint.set_arg("ty", ty);
-
- // We need to preserve the literal's suffix,
- // as it may determine typing information.
- let suffix = match lit.node {
- LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsuffixed) => "",
- _ => bug!(),
- };
- let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
- lint.span_suggestion(
- struct_expr.span,
- fluent::suggestion,
- suggestion,
- Applicability::MachineApplicable,
- );
-
- lint
+ RangeEndpointOutOfRange {
+ ty,
+ suggestion: struct_expr.span,
+ start,
+ literal: lit_val - 1,
+ suffix,
},
);
val: u128,
negative: bool,
) {
- cx.struct_span_lint(
- OVERFLOWING_LITERALS,
- expr.span,
- fluent::lint_overflowing_bin_hex,
- |lint| {
- let (t, actually) = match ty {
- attr::IntType::SignedInt(t) => {
- let actually = if negative {
- -(size.sign_extend(val) as i128)
- } else {
- size.sign_extend(val) as i128
- };
- (t.name_str(), actually.to_string())
- }
- attr::IntType::UnsignedInt(t) => {
- let actually = size.truncate(val);
- (t.name_str(), actually.to_string())
- }
+ let (t, actually) = match ty {
+ attr::IntType::SignedInt(t) => {
+ let actually = if negative {
+ -(size.sign_extend(val) as i128)
+ } else {
+ size.sign_extend(val) as i128
};
-
- if negative {
- // If the value is negative,
- // emits a note about the value itself, apart from the literal.
- lint.note(fluent::negative_note);
- lint.note(fluent::negative_becomes_note);
+ (t.name_str(), actually.to_string())
+ }
+ attr::IntType::UnsignedInt(t) => {
+ let actually = size.truncate(val);
+ (t.name_str(), actually.to_string())
+ }
+ };
+ let sign =
+ if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
+ let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
+ |suggestion_ty| {
+ if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+ let (sans_suffix, _) = repr_str.split_at(pos);
+ OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
} else {
- lint.note(fluent::positive_note);
- }
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
- {
- lint.set_arg("suggestion_ty", sugg_ty);
- if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
- let (sans_suffix, _) = repr_str.split_at(pos);
- lint.span_suggestion(
- expr.span,
- fluent::suggestion,
- format!("{}{}", sans_suffix, sugg_ty),
- Applicability::MachineApplicable,
- );
- } else {
- lint.help(fluent::help);
- }
+ OverflowingBinHexSub::Help { suggestion_ty }
}
- lint.set_arg("ty", t)
- .set_arg("lit", repr_str)
- .set_arg("dec", val)
- .set_arg("actually", actually);
-
- lint
},
);
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ expr.span,
+ OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub },
+ )
}
// This function finds the next fitting type and generates a suggestion string.
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_int, |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .set_arg("min", min)
- .set_arg("max", max)
- .note(fluent::note);
-
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
- {
- lint.set_arg("suggestion_ty", sugg_ty);
- lint.help(fluent::help);
- }
-
- lint
- });
+ let lit = cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal");
+ let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
+ .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
+
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ OverflowingInt { ty: t.name_str(), lit, min, max, help },
+ );
}
}
match par_e.kind {
hir::ExprKind::Cast(..) => {
if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
par_e.span,
- fluent::lint_only_cast_u8_to_char,
- |lint| {
- lint.span_suggestion(
- par_e.span,
- fluent::suggestion,
- format!("'\\u{{{:X}}}'", lit_val),
- Applicability::MachineApplicable,
- )
- },
+ OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
);
return;
}
);
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_uint, |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .set_arg("min", min)
- .set_arg("max", max)
- .note(fluent::note)
- });
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ OverflowingUInt {
+ ty: t.name_str(),
+ lit: cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
+ min,
+ max,
+ },
+ );
}
}
_ => bug!(),
};
if is_infinite == Ok(true) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
e.span,
- fluent::lint_overflowing_literal,
- |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .note(fluent::note)
+ OverflowingLiteral {
+ ty: t.name_str(),
+ lit: cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
},
);
}
}
hir::ExprKind::Binary(binop, ref l, ref r) => {
if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
- cx.struct_span_lint(
- UNUSED_COMPARISONS,
- e.span,
- fluent::lint_unused_comparisons,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
}
}
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
) -> FfiResult<'tcx> {
use FfiResult::*;
- if def.repr().transparent() {
+ let transparent_safety = def.repr().transparent().then(|| {
// Can assume that at most one field is not a ZST, so only check
// that field's type for FFI-safety.
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
- self.check_field_type_for_ffi(cache, field, substs)
+ return self.check_field_type_for_ffi(cache, field, substs);
} else {
// All fields are ZSTs; this means that the type should behave
- // like (), which is FFI-unsafe
+ // like (), which is FFI-unsafe... except if all fields are PhantomData,
+ // which is tested for below
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
}
- } else {
- // We can't completely trust repr(C) markings; make sure the fields are
- // actually safe.
- let mut all_phantom = !variant.fields.is_empty();
- for field in &variant.fields {
- match self.check_field_type_for_ffi(cache, &field, substs) {
- FfiSafe => {
- all_phantom = false;
- }
- FfiPhantom(..) if def.is_enum() => {
- return FfiUnsafe {
- ty,
- reason: fluent::lint_improper_ctypes_enum_phantomdata,
- help: None,
- };
- }
- FfiPhantom(..) => {}
- r => return r,
+ });
+ // We can't completely trust repr(C) markings; make sure the fields are
+ // actually safe.
+ let mut all_phantom = !variant.fields.is_empty();
+ for field in &variant.fields {
+ match self.check_field_type_for_ffi(cache, &field, substs) {
+ FfiSafe => {
+ all_phantom = false;
+ }
+ FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => {
+ return FfiUnsafe {
+ ty,
+ reason: fluent::lint_improper_ctypes_enum_phantomdata,
+ help: None,
+ };
}
+ FfiPhantom(..) => {}
+ r => return transparent_safety.unwrap_or(r),
}
-
- if all_phantom { FfiPhantom(ty) } else { FfiSafe }
}
+
+ if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) }
}
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
CItemKind::Declaration => IMPROPER_CTYPES,
CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
};
-
- self.cx.struct_span_lint(lint, sp, fluent::lint_improper_ctypes, |lint| {
- let item_description = match self.mode {
- CItemKind::Declaration => "block",
- CItemKind::Definition => "fn",
+ let desc = match self.mode {
+ CItemKind::Declaration => "block",
+ CItemKind::Definition => "fn",
+ };
+ let span_note = if let ty::Adt(def, _) = ty.kind()
+ && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
+ Some(sp)
+ } else {
+ None
};
- lint.set_arg("ty", ty);
- lint.set_arg("desc", item_description);
- lint.span_label(sp, fluent::label);
- if let Some(help) = help {
- lint.help(help);
- }
- lint.note(note);
- if let ty::Adt(def, _) = ty.kind() {
- if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
- lint.span_note(sp, fluent::note);
- }
- }
- lint
- });
+ self.cx.emit_spanned_lint(
+ lint,
+ sp,
+ ImproperCTypes { ty, desc, label: sp, help, note, span_note },
+ );
}
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
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() {
// We only warn if the largest variant is at least thrice as large as
// the second-largest.
if largest > slargest * 3 && slargest > 0 {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
VARIANT_SIZE_DIFFERENCES,
enum_definition.variants[largest_index].span,
- fluent::lint_variant_size_differences,
- |lint| lint.set_arg("largest", largest),
+ VariantSizeDifferencesDiag { largest },
);
}
}
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
- && let Some((ordering_arg, invalid_ordering, msg)) = match method {
- sym::load => Some((&args[0], sym::Release, fluent::lint_atomic_ordering_load)),
- sym::store => Some((&args[1], sym::Acquire, fluent::lint_atomic_ordering_store)),
+ && let Some((ordering_arg, invalid_ordering)) = match method {
+ sym::load => Some((&args[0], sym::Release)),
+ sym::store => Some((&args[1], sym::Acquire)),
_ => None,
}
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
&& (ordering == invalid_ordering || ordering == sym::AcqRel)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, msg, |lint| {
- lint.help(fluent::help)
- });
+ if method == sym::load {
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
+ } else {
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
+ };
}
}
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
&& Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, fluent::lint_atomic_ordering_fence, |lint| {
- lint
- .help(fluent::help)
- });
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
}
}
let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
if matches!(fail_ordering, sym::Release | sym::AcqRel) {
- #[derive(LintDiagnostic)]
- #[diag(lint_atomic_ordering_invalid)]
- #[help]
- struct InvalidAtomicOrderingDiag {
- method: Symbol,
- #[label]
- fail_order_arg_span: Span,
- }
-
cx.emit_spanned_lint(
INVALID_ATOMIC_ORDERING,
fail_order_arg.span,
+use crate::lints::{
+ PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
+ UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDelim, UnusedDelimSuggestion,
+ UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
+};
use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_ast::util::{classify, parser};
use rustc_ast::{ExprKind, StmtKind};
-use rustc_errors::{fluent, pluralize, Applicability, MultiSpan};
+use rustc_errors::{pluralize, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
let mut op_warned = false;
if let Some(must_use_op) = must_use_op {
- cx.struct_span_lint(UNUSED_MUST_USE, expr.span, fluent::lint_unused_op, |lint| {
- lint.set_arg("op", must_use_op)
- .span_label(expr.span, fluent::label)
- .span_suggestion_verbose(
- expr.span.shrink_to_lo(),
- fluent::suggestion,
- "let _ = ",
- Applicability::MachineApplicable,
- )
- });
+ cx.emit_spanned_lint(
+ UNUSED_MUST_USE,
+ expr.span,
+ UnusedOp {
+ op: must_use_op,
+ label: expr.span,
+ suggestion: expr.span.shrink_to_lo(),
+ },
+ );
op_warned = true;
}
if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
- cx.struct_span_lint(UNUSED_RESULTS, s.span, fluent::lint_unused_result, |lint| {
- lint.set_arg("ty", ty)
- });
+ cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
}
fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
);
}
MustUsePath::Closure(span) => {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNUSED_MUST_USE,
*span,
- fluent::lint_unused_closure,
- |lint| {
- // FIXME(davidtwco): this isn't properly translatable because of the
- // pre/post strings
- lint.set_arg("count", plural_len)
- .set_arg("pre", descr_pre)
- .set_arg("post", descr_post)
- .note(fluent::note)
- },
+ UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
);
}
MustUsePath::Generator(span) => {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNUSED_MUST_USE,
*span,
- fluent::lint_unused_generator,
- |lint| {
- // FIXME(davidtwco): this isn't properly translatable because of the
- // pre/post strings
- lint.set_arg("count", plural_len)
- .set_arg("pre", descr_pre)
- .set_arg("post", descr_post)
- .note(fluent::note)
- },
+ UnusedGenerator { count: plural_len, pre: descr_pre, post: descr_post },
);
}
MustUsePath::Def(span, def_id, reason) => {
- cx.struct_span_lint(UNUSED_MUST_USE, *span, fluent::lint_unused_def, |lint| {
- // FIXME(davidtwco): this isn't properly translatable because of the pre/post
- // strings
- lint.set_arg("pre", descr_pre);
- lint.set_arg("post", descr_post);
- lint.set_arg("def", cx.tcx.def_path_str(*def_id));
- if let Some(note) = reason {
- lint.note(note.as_str());
- }
- lint
- });
+ cx.emit_spanned_lint(
+ UNUSED_MUST_USE,
+ *span,
+ UnusedDef {
+ pre: descr_pre,
+ post: descr_post,
+ cx,
+ def_id: *def_id,
+ note: *reason,
+ },
+ );
}
}
}
if let hir::ExprKind::Path(_) = expr.kind {
let ty = cx.typeck_results().expr_ty(expr);
if ty.needs_drop(cx.tcx, cx.param_env) {
- cx.struct_span_lint(
- PATH_STATEMENTS,
- s.span,
- fluent::lint_path_statement_drop,
- |lint| {
- if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
- lint.span_suggestion(
- s.span,
- fluent::suggestion,
- format!("drop({});", snippet),
- Applicability::MachineApplicable,
- );
- } else {
- lint.span_help(s.span, fluent::suggestion);
- }
- lint
- },
- );
+ let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
+ {
+ PathStatementDropSub::Suggestion { span: s.span, snippet }
+ } else {
+ PathStatementDropSub::Help { span: s.span }
+ };
+ cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
} else {
- cx.struct_span_lint(
- PATH_STATEMENTS,
- s.span,
- fluent::lint_path_statement_no_effect,
- |lint| lint,
- );
+ cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
}
}
}
} else {
MultiSpan::from(value_span)
};
- cx.struct_span_lint(self.lint(), primary_span, fluent::lint_unused_delim, |lint| {
- lint.set_arg("delim", Self::DELIM_STR);
- lint.set_arg("item", msg);
- if let Some((lo, hi)) = spans {
- let sm = cx.sess().source_map();
- let lo_replace =
+ let suggestion = spans.map(|(lo, hi)| {
+ let sm = cx.sess().source_map();
+ let lo_replace =
if keep_space.0 &&
let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') {
- " ".to_string()
+ " "
} else {
- "".to_string()
+ ""
};
- let hi_replace =
+ let hi_replace =
if keep_space.1 &&
let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') {
- " ".to_string()
+ " "
} else {
- "".to_string()
+ ""
};
-
- let replacement = vec![(lo, lo_replace), (hi, hi_replace)];
- lint.multipart_suggestion(
- fluent::suggestion,
- replacement,
- Applicability::MachineApplicable,
- );
+ UnusedDelimSuggestion {
+ start_span: lo,
+ start_replace: lo_replace,
+ end_span: hi,
+ end_replace: hi_replace,
}
- lint
});
+ cx.emit_spanned_lint(
+ self.lint(),
+ primary_span,
+ UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
+ );
}
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
"`if`, `match`, `while` and `return` do not need parentheses"
}
-declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
+pub struct UnusedParens {
+ with_self_ty_parens: bool,
+}
+
+impl UnusedParens {
+ pub fn new() -> Self {
+ Self { with_self_ty_parens: false }
+ }
+}
+
+impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
impl UnusedDelimLint for UnusedParens {
const DELIM_STR: &'static str = "parentheses";
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
- if let ast::TyKind::Paren(r) = &ty.kind {
- match &r.kind {
- ast::TyKind::TraitObject(..) => {}
- ast::TyKind::BareFn(b) if b.generic_params.len() > 0 => {}
- ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
- ast::TyKind::Array(_, len) => {
- self.check_unused_delims_expr(
- cx,
- &len.value,
- UnusedDelimsCtx::ArrayLenExpr,
- false,
- None,
- None,
- );
- }
- _ => {
- let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
- Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
- } else {
- None
- };
- self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ match &ty.kind {
+ ast::TyKind::Array(_, len) => {
+ self.check_unused_delims_expr(
+ cx,
+ &len.value,
+ UnusedDelimsCtx::ArrayLenExpr,
+ false,
+ None,
+ None,
+ );
+ }
+ ast::TyKind::Paren(r) => {
+ match &r.kind {
+ ast::TyKind::TraitObject(..) => {}
+ ast::TyKind::BareFn(b)
+ if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
+ ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
+ _ => {
+ let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
+ Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
+ } else {
+ None
+ };
+ self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
+ }
}
+ self.with_self_ty_parens = false;
}
+ _ => {}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
<Self as UnusedDelimLint>::check_item(self, cx, item)
}
+
+ fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
+ use rustc_ast::{WhereBoundPredicate, WherePredicate};
+ if let WherePredicate::BoundPredicate(WhereBoundPredicate {
+ bounded_ty,
+ bound_generic_params,
+ ..
+ }) = pred &&
+ let ast::TyKind::Paren(_) = &bounded_ty.kind &&
+ bound_generic_params.is_empty() {
+ self.with_self_ty_parens = true;
+ }
+ }
+
+ fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
+ assert!(!self.with_self_ty_parens);
+ }
}
declare_lint! {
// ```
// - the block has no attribute and was not created inside a macro
// - if the block is an `anon_const`, the inner expr must be a literal
- // (do not lint `struct A<const N: usize>; let _: A<{ 2 + 3 }>;`)
- //
+ // not created by a macro, i.e. do not lint on:
+ // ```
+ // struct A<const N: usize>;
+ // let _: A<{ 2 + 3 }>;
+ // let _: A<{produces_literal!()}>;
+ // ```
// FIXME(const_generics): handle paths when #67075 is fixed.
if let [stmt] = inner.stmts.as_slice() {
if let ast::StmtKind::Expr(ref expr) = stmt.kind {
if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
&& (ctx != UnusedDelimsCtx::AnonConst
- || matches!(expr.kind, ast::ExprKind::Lit(_)))
+ || (matches!(expr.kind, ast::ExprKind::Lit(_))
+ && !expr.span.from_expansion()))
&& !cx.sess().source_map().is_multiline(value.span)
&& value.attrs.is_empty()
&& !value.span.from_expansion()
ast::UseTreeKind::Nested(_) => return,
};
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
UNUSED_IMPORT_BRACES,
item.span,
- fluent::lint_unused_import_braces,
- |lint| lint.set_arg("node", node_name),
+ UnusedImportBracesDiag { node: node_name },
);
}
}
for adj in cx.typeck_results().expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
- cx.struct_span_lint(
- UNUSED_ALLOCATION,
- e.span,
- match m {
- adjustment::AutoBorrowMutability::Not => fluent::lint_unused_allocation,
- adjustment::AutoBorrowMutability::Mut { .. } => {
- fluent::lint_unused_allocation_mut
- }
- },
- |lint| lint,
- );
+ match m {
+ adjustment::AutoBorrowMutability::Not => {
+ cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
+ }
+ adjustment::AutoBorrowMutability::Mut { .. } => {
+ cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
+ }
+ };
}
}
}
///
/// ### 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,
///
/// This can be used to implement an unsound API if used incorrectly.
pub IMPLIED_BOUNDS_ENTAILMENT,
- Warn,
+ Deny,
"impl method assumes more implied bounds than its corresponding trait method",
@future_incompatible = FutureIncompatibleInfo {
reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
- reason: FutureIncompatibilityReason::FutureReleaseError,
+ reason: FutureIncompatibilityReason::FutureReleaseErrorReportNow,
};
}
extern crate rustc_macros;
pub use self::Level::*;
-use rustc_ast::node_id::{NodeId, NodeMap};
+use rustc_ast::node_id::NodeId;
use rustc_ast::{AttrId, Attribute};
+use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_error_messages::{DiagnosticMessage, MultiSpan};
use rustc_hir::HashStableContext;
param_span: Span,
/// Span of the code that should be removed when eliding this lifetime.
/// This span should include leading or trailing comma.
- deletion_span: Span,
+ deletion_span: Option<Span>,
/// Span of the single use, or None if the lifetime is never used.
/// If true, the lifetime will be fully elided.
use_span: Option<(Span, bool)>,
#[derive(Default)]
pub struct LintBuffer {
- pub map: NodeMap<Vec<BufferedEarlyLint>>,
+ pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
}
impl LintBuffer {
extern "C" void LLVMRustSetLLVMOptions(int Argc, char **Argv) {
// Initializing the command-line options more than once is not allowed. So,
- // check if they've already been initialized. (This could happen if we're
+ // check if they've already been initialized. (This could happen if we're
// being called from rustpkg, for example). If the arguments change, then
// that's just kinda unfortunate.
static bool Initialized = false;
}
// This is what we used to parse upstream bitcode for actual ThinLTO
-// processing. We'll call this once per module optimized through ThinLTO, and
+// processing. We'll call this once per module optimized through ThinLTO, and
// it'll be called concurrently on many threads.
extern "C" LLVMModuleRef
LLVMRustParseBitcodeForLTO(LLVMContextRef Context,
return LLVMBFloatTypeKind;
case Type::X86_AMXTyID:
return LLVMX86_AMXTypeKind;
-#if LLVM_VERSION_GE(15, 0) && LLVM_VERSION_LT(16, 0)
- case Type::DXILPointerTyID:
- report_fatal_error("Rust does not support DirectX typed pointers.");
- break;
-#endif
-#if LLVM_VERSION_GE(16, 0)
- case Type::TypedPointerTyID:
- report_fatal_error("Rust does not support typed pointers.");
- break;
-#endif
+ default:
+ {
+ std::string error;
+ llvm::raw_string_ostream stream(error);
+ stream << "Rust does not support the TypeID: " << unwrap(Ty)->getTypeID()
+ << " for the type: " << *unwrap(Ty);
+ stream.flush();
+ report_fatal_error(error.c_str());
+ }
}
- report_fatal_error("Unhandled TypeID.");
}
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(SMDiagnostic, LLVMSMDiagnosticRef)
tracing = "0.1.28"
tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
tracing-tree = "0.2.0"
+tracing-core = "0.1.28"
[dev-dependencies]
rustc_span = { path = "../rustc_span" }
use std::env::{self, VarError};
use std::fmt::{self, Display};
use std::io::{self, IsTerminal};
+use tracing_core::{Event, Subscriber};
use tracing_subscriber::filter::{Directive, EnvFilter, LevelFilter};
+use tracing_subscriber::fmt::{
+ format::{self, FormatEvent, FormatFields},
+ FmtContext,
+};
use tracing_subscriber::layer::SubscriberExt;
pub fn init_rustc_env_logger() -> Result<(), Error> {
- init_env_logger("RUSTC_LOG")
+ init_rustc_env_logger_with_backtrace_option(&None)
+}
+
+pub fn init_rustc_env_logger_with_backtrace_option(
+ backtrace_target: &Option<String>,
+) -> Result<(), Error> {
+ init_env_logger_with_backtrace_option("RUSTC_LOG", backtrace_target)
}
/// In contrast to `init_rustc_env_logger` this allows you to choose an env var
/// other than `RUSTC_LOG`.
pub fn init_env_logger(env: &str) -> Result<(), Error> {
+ init_env_logger_with_backtrace_option(env, &None)
+}
+
+pub fn init_env_logger_with_backtrace_option(
+ env: &str,
+ backtrace_target: &Option<String>,
+) -> Result<(), Error> {
let filter = match env::var(env) {
Ok(env) => EnvFilter::new(env),
_ => EnvFilter::default().add_directive(Directive::from(LevelFilter::WARN)),
let layer = layer.with_thread_ids(true).with_thread_names(true);
let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
- tracing::subscriber::set_global_default(subscriber).unwrap();
+ match backtrace_target {
+ Some(str) => {
+ let fmt_layer = tracing_subscriber::fmt::layer()
+ .with_writer(io::stderr)
+ .without_time()
+ .event_format(BacktraceFormatter { backtrace_target: str.to_string() });
+ let subscriber = subscriber.with(fmt_layer);
+ tracing::subscriber::set_global_default(subscriber).unwrap();
+ }
+ None => {
+ tracing::subscriber::set_global_default(subscriber).unwrap();
+ }
+ };
Ok(())
}
+struct BacktraceFormatter {
+ backtrace_target: String,
+}
+
+impl<S, N> FormatEvent<S, N> for BacktraceFormatter
+where
+ S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>,
+ N: for<'a> FormatFields<'a> + 'static,
+{
+ fn format_event(
+ &self,
+ _ctx: &FmtContext<'_, S, N>,
+ mut writer: format::Writer<'_>,
+ event: &Event<'_>,
+ ) -> fmt::Result {
+ let target = event.metadata().target();
+ if !target.contains(&self.backtrace_target) {
+ return Ok(());
+ }
+ let backtrace = std::backtrace::Backtrace::capture();
+ writeln!(writer, "stack backtrace: \n{:?}", backtrace)
+ }
+}
+
pub fn stdout_isatty() -> bool {
io::stdout().is_terminal()
}
return Ok(quote! { #diag.subdiagnostic(#binding); });
}
}
- (Meta::List(_), "subdiagnostic") => {
- throw_invalid_attr!(attr, &meta, |diag| {
- diag.help("`subdiagnostic` does not support nested attributes")
- })
+ (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => {
+ if nested.len() == 1
+ && let Some(NestedMeta::Meta(Meta::Path(path))) = nested.first()
+ && path.is_ident("eager") {
+ let handler = match &self.parent.kind {
+ DiagnosticDeriveKind::Diagnostic { handler } => handler,
+ DiagnosticDeriveKind::LintDiagnostic => {
+ throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("eager subdiagnostics are not supported on lints")
+ })
+ }
+ };
+ return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); });
+ } else {
+ throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help(
+ "`eager` is the only supported nested attribute for `subdiagnostic`",
+ )
+ })
+ }
}
_ => (),
}
#![feature(allow_internal_unstable)]
#![feature(if_let_guard)]
+#![feature(let_chains)]
#![feature(never_type)]
#![feature(proc_macro_diagnostic)]
#![feature(proc_macro_span)]
__visitor: &mut __V
) -> ::std::ops::ControlFlow<__V::BreakTy> {
match *self { #body_visit }
- ::std::ops::ControlFlow::CONTINUE
+ ::std::ops::ControlFlow::Continue(())
}
},
)
[lib]
[dependencies]
+bitflags = "1.2.1"
libloading = "0.7.1"
odht = { version = "0.3.1", features = ["nightly"] }
snap = "1"
let _prof_timer = tcx.sess.prof.generic_activity("write_crate_metadata");
// If the user requests metadata as output, rename `metadata_filename`
- // to the expected output `out_filename`. The match above should ensure
+ // to the expected output `out_filename`. The match above should ensure
// this file always exists.
let need_metadata_file = tcx.sess.opts.output_types.contains_key(&OutputType::Metadata);
let (metadata_filename, metadata_tmpdir) = if need_metadata_file {
Err(MetadataError::LoadFailure(err)) => {
info!("no metadata found: {}", err);
// The file was present and created by the same compiler version, but we
- // couldn't load it for some reason. Give a hard error instead of silently
+ // couldn't load it for some reason. Give a hard error instead of silently
// ignoring it, but only if we would have given an error anyway.
self.crate_rejections
.via_invalid
}
// Update kind and, optionally, the name of all native libraries
- // (there may be more than one) with the specified name. If any
+ // (there may be more than one) with the specified name. If any
// library is mentioned more than once, keep the latest mention
// of it, so that any possible dependent libraries appear before
- // it. (This ensures that the linker is able to see symbols from
+ // it. (This ensures that the linker is able to see symbols from
// all possible dependent libraries before linking in the library
// in question.)
for passed_lib in &self.tcx.sess.opts.libs {
let vis = self.get_visibility(id);
let span = self.get_span(id, sess);
let macro_rules = match kind {
- DefKind::Macro(..) => self.root.tables.macro_rules.get(self, id).is_some(),
+ DefKind::Macro(..) => self.root.tables.is_macro_rules.get(self, id),
_ => false,
};
fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
match self.def_kind(id) {
DefKind::Macro(_) => {
- let macro_rules = self.root.tables.macro_rules.get(self, id).is_some();
+ let macro_rules = self.root.tables.is_macro_rules.get(self, id);
let body =
self.root.tables.macro_definition.get(self, id).unwrap().decode((self, sess));
ast::MacroDef { macro_rules, body: ast::ptr::P(body) }
})
}
- fn get_may_have_doc_links(self, index: DefIndex) -> bool {
- self.root.tables.may_have_doc_links.get(self, index).is_some()
+ fn get_attr_flags(self, index: DefIndex) -> AttrFlags {
+ self.root.tables.attr_flags.get(self, index)
}
fn get_is_intrinsic(self, index: DefIndex) -> bool {
- self.root.tables.is_intrinsic.get(self, index).is_some()
+ self.root.tables.is_intrinsic.get(self, index)
}
}
use crate::creader::{CStore, LoadedMacro};
use crate::foreign_modules;
use crate::native_libs;
+use crate::rmeta::AttrFlags;
use rustc_ast as ast;
use rustc_attr::Deprecation;
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)
}
}
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId {
fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
if self.krate == LOCAL_CRATE {
- // We will only write details for local expansions. Non-local expansions will fetch
+ // We will only write details for local expansions. Non-local expansions will fetch
// data from the corresponding crate's metadata.
// FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external
// metadata from proc-macro crates.
self.lazy(DefPathHashMapRef::BorrowedFromTcx(self.tcx.def_path_hash_to_def_index_map()))
}
- fn encode_source_map(&mut self) -> LazyTable<u32, LazyValue<rustc_span::SourceFile>> {
+ fn encode_source_map(&mut self) -> LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>> {
let source_map = self.tcx.sess.source_map();
let all_source_files = source_map.files();
| 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.
let tcx = self.tcx;
let mut is_public: Option<bool> = None;
- let mut attrs = tcx
- .hir()
- .attrs(tcx.hir().local_def_id_to_hir_id(def_id))
+ let hir_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
+ let mut attrs = hir_attrs
.iter()
.filter(move |attr| should_encode_attr(tcx, attr, def_id, &mut is_public));
record_array!(self.tables.attributes[def_id.to_def_id()] <- attrs.clone());
+ let mut attr_flags = AttrFlags::empty();
if attrs.any(|attr| attr.may_have_doc_links()) {
- self.tables.may_have_doc_links.set(def_id.local_def_index, ());
+ attr_flags |= AttrFlags::MAY_HAVE_DOC_LINKS;
+ }
+ if hir_attrs
+ .iter()
+ .filter(|attr| attr.has_name(sym::doc))
+ .filter_map(|attr| attr.meta_item_list())
+ .any(|items| items.iter().any(|item| item.has_name(sym::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.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;
}
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);
}
}
}
}
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);
+ if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) {
+ self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true);
+ }
}
hir::ItemKind::Enum(..) => {
let adt_def = self.tcx.adt_def(def_id);
self.tables.impl_defaultness.set(def_id.index, *defaultness);
self.tables.constness.set(def_id.index, *constness);
- let trait_ref = self.tcx.impl_trait_ref(def_id);
+ let trait_ref = self.tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::skip_binder);
if let Some(trait_ref) = trait_ref {
let trait_def = self.tcx.trait_def(trait_ref.def_id);
if let Ok(mut an) = trait_def.ancestors(self.tcx, def_id) {
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 {
}
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());
}
for id in tcx.hir().items() {
if matches!(tcx.def_kind(id.owner_id), DefKind::Impl) {
if let Some(trait_ref) = tcx.impl_trait_ref(id.owner_id) {
+ let trait_ref = trait_ref.subst_identity();
+
let simplified_self_ty = fast_reject::simplify_type(
self.tcx,
trait_ref.self_ty(),
}
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set(def_id.index, ());
+ self.tables.is_intrinsic.set_nullable(def_id.index, true);
}
}
}
Previous(NonZeroUsize),
}
-type SyntaxContextTable = LazyTable<u32, LazyValue<SyntaxContextData>>;
-type ExpnDataTable = LazyTable<ExpnIndex, LazyValue<ExpnData>>;
-type ExpnHashTable = LazyTable<ExpnIndex, LazyValue<ExpnHash>>;
+type SyntaxContextTable = LazyTable<u32, Option<LazyValue<SyntaxContextData>>>;
+type ExpnDataTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnData>>>;
+type ExpnHashTable = LazyTable<ExpnIndex, Option<LazyValue<ExpnHash>>>;
#[derive(MetadataEncodable, MetadataDecodable)]
pub(crate) struct ProcMacroData {
def_path_hash_map: LazyValue<DefPathHashMapRef<'static>>,
- source_map: LazyTable<u32, LazyValue<rustc_span::SourceFile>>,
+ source_map: LazyTable<u32, Option<LazyValue<rustc_span::SourceFile>>>,
compiler_builtins: bool,
needs_allocator: bool,
/// Define `LazyTables` and `TableBuilders` at the same time.
macro_rules! define_tables {
- ($($name:ident: Table<$IDX:ty, $T:ty>),+ $(,)?) => {
+ (
+ - nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
+ - optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+
+ ) => {
#[derive(MetadataEncodable, MetadataDecodable)]
pub(crate) struct LazyTables {
- $($name: LazyTable<$IDX, $T>),+
+ $($name1: LazyTable<$IDX1, $T1>,)+
+ $($name2: LazyTable<$IDX2, Option<$T2>>,)+
}
#[derive(Default)]
struct TableBuilders {
- $($name: TableBuilder<$IDX, $T>),+
+ $($name1: TableBuilder<$IDX1, $T1>,)+
+ $($name2: TableBuilder<$IDX2, Option<$T2>>,)+
}
impl TableBuilders {
fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
LazyTables {
- $($name: self.$name.encode(buf)),+
+ $($name1: self.$name1.encode(buf),)+
+ $($name2: self.$name2.encode(buf),)+
}
}
}
}
define_tables! {
+- nullable:
+ is_intrinsic: Table<DefIndex, bool>,
+ is_macro_rules: Table<DefIndex, bool>,
+ is_type_alias_impl_trait: Table<DefIndex, bool>,
+ attr_flags: Table<DefIndex, AttrFlags>,
+
+- optional:
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
children: Table<DefIndex, LazyArray<DefIndex>>,
-
opt_def_kind: Table<DefIndex, DefKind>,
visibility: Table<DefIndex, LazyValue<ty::Visibility<DefIndex>>>,
def_span: Table<DefIndex, LazyValue<Span>>,
variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
fn_sig: Table<DefIndex, LazyValue<ty::PolyFnSig<'static>>>,
codegen_fn_attrs: Table<DefIndex, LazyValue<CodegenFnAttrs>>,
- impl_trait_ref: Table<DefIndex, LazyValue<ty::TraitRef<'static>>>,
- const_param_default: Table<DefIndex, LazyValue<rustc_middle::ty::Const<'static>>>,
+ impl_trait_ref: Table<DefIndex, LazyValue<ty::EarlyBinder<ty::TraitRef<'static>>>>,
+ const_param_default: Table<DefIndex, LazyValue<ty::EarlyBinder<rustc_middle::ty::Const<'static>>>>,
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
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
}
}
[decode] typeck_results: rustc_middle::ty::TypeckResults<'tcx>,
[decode] borrowck_result:
rustc_middle::mir::BorrowCheckResult<'tcx>,
- [] resolver: rustc_data_structures::steal::Steal<rustc_middle::ty::ResolverAstLowering>,
+ [] resolver: rustc_data_structures::steal::Steal<(
+ rustc_middle::ty::ResolverAstLowering,
+ rustc_data_structures::sync::Lrc<rustc_ast::Crate>,
+ )>,
+ [] output_filenames: std::sync::Arc<rustc_session::config::OutputFilenames>,
+ [] resolutions: rustc_middle::ty::ResolverGlobalCtxt,
[decode] unsafety_check_result: rustc_middle::mir::UnsafetyCheckResult,
[decode] code_region: rustc_middle::mir::coverage::CodeRegion,
[] const_allocs: rustc_middle::mir::interpret::Allocation,
BodyOwnerKind::Static(mt) => ConstContext::Static(mt),
BodyOwnerKind::Fn if self.tcx.is_constructor(def_id.to_def_id()) => return None,
- BodyOwnerKind::Fn if self.tcx.is_const_fn_raw(def_id.to_def_id()) => {
+ BodyOwnerKind::Fn | BodyOwnerKind::Closure
+ if self.tcx.is_const_fn_raw(def_id.to_def_id()) =>
+ {
ConstContext::ConstFn
}
BodyOwnerKind::Fn if self.tcx.is_const_default_method(def_id.to_def_id()) => {
/// Visits all item-likes in the crate in some deterministic (but unspecified) order. If you
/// need to process every item-like, and don't care about visiting nested items in a particular
- /// order then this method is the best choice. If you do care about this nesting, you should
+ /// order then this method is the best choice. If you do care about this nesting, you should
/// use the `tcx.hir().walk_toplevel_module`.
///
- /// Note that this function will access HIR for all the item-likes in the crate. If you only
+ /// Note that this function will access HIR for all the item-likes in the crate. If you only
/// need to access some of them, it is usually better to manually loop on the iterators
/// provided by `tcx.hir_crate_items(())`.
///
}
/// Gather the LocalDefId for each item-like within a module, including items contained within
-/// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
+/// bodies. The Ids are in visitor order. This is used to partition a pass between modules.
#[derive(Debug, HashStable, Encodable, Decodable)]
pub struct ModuleItems {
submodules: Box<[OwnerId]>,
pub fn impl_subject(self, def_id: DefId) -> ImplSubject<'tcx> {
self.impl_trait_ref(def_id)
+ .map(|t| t.subst_identity())
.map(ImplSubject::Trait)
.unwrap_or_else(|| ImplSubject::Inherent(self.type_of(def_id)))
}
pub var_values: IndexVec<BoundVar, GenericArg<'tcx>>,
}
+impl CanonicalVarValues<'_> {
+ pub fn is_identity(&self) -> bool {
+ self.var_values.iter_enumerated().all(|(bv, arg)| match arg.unpack() {
+ ty::GenericArgKind::Lifetime(r) => {
+ matches!(*r, ty::ReLateBound(ty::INNERMOST, br) if br.var == bv)
+ }
+ ty::GenericArgKind::Type(ty) => {
+ matches!(*ty.kind(), ty::Bound(ty::INNERMOST, bt) if bt.var == bv)
+ }
+ ty::GenericArgKind::Const(ct) => {
+ matches!(ct.kind(), ty::ConstKind::Bound(ty::INNERMOST, bc) if bc == bv)
+ }
+ })
+ }
+}
+
/// When we canonicalize a value to form a query, we wind up replacing
/// various parts of it with canonical variables. This struct stores
/// those replaced bits to remember for when we process the query
}
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()
_: &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,
-}
self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
}
- /// Generates an `AllocId` for a function. Depending on the function type,
+ /// Generates an `AllocId` for a function. Depending on the function type,
/// this might get deduplicated or assigned a new ID each time.
pub fn create_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
// Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
// We thus generate a new `AllocId` for every mention of a function. This means that
// `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true.
// However, formatting code relies on function identity (see #58320), so we only do
- // this for generic functions. Lifetime parameters are ignored.
+ // this for generic functions. Lifetime parameters are ignored.
let is_generic = instance
.substs
.into_iter()
}
}
- /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
+ /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
pub fn create_vtable_alloc(
self,
ty: Ty<'tcx>,
pub mod coverage;
mod generic_graph;
pub mod generic_graphviz;
-mod graph_cyclic_cache;
pub mod graphviz;
pub mod interpret;
pub mod mono;
pub mod patch;
-mod predecessors;
pub mod pretty;
mod query;
pub mod spanview;
mod syntax;
pub use syntax::*;
-mod switch_sources;
pub mod tcx;
pub mod terminator;
pub use terminator::*;
// FIXME(const_generics): We currently have to special case parameters because `min_const_generics`
// does not provide the parents generics to anonymous constants. We still allow generic const
- // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
+ // parameters by themselves however, e.g. `N`. These constants would cause an ICE if we were to
// ever try to substitute the generic parameters in their bodies.
//
// While this doesn't happen as these constants are always used as `ty::ConstKind::Param`, it does
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,
-}
+++ /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,
-}
/// must also be `cleanup`. This is a part of the type system and checked statically, so it is
/// still an error to have such an edge in the CFG even if it's known that it won't be taken at
/// runtime.
+/// 4. The control flow between cleanup blocks must look like an upside down tree. Roughly
+/// speaking, this means that control flow that looks like a V is allowed, while control flow
+/// that looks like a W is not. This is necessary to ensure that landing pad information can be
+/// correctly codegened on MSVC. More precisely:
+///
+/// Begin with the standard control flow graph `G`. Modify `G` as follows: for any two cleanup
+/// vertices `u` and `v` such that `u` dominates `v`, contract `u` and `v` into a single vertex,
+/// deleting self edges and duplicate edges in the process. Now remove all vertices from `G`
+/// that are not cleanup vertices or are not reachable. The resulting graph must be an inverted
+/// tree, that is each vertex may have at most one successor and there may be no cycles.
#[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq, TypeFoldable, TypeVisitable)]
pub enum TerminatorKind<'tcx> {
/// Block has one successor; we continue execution there.
}
/// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
- /// specific value. This cannot fail, as it'll return the `otherwise`
+ /// specific value. This cannot fail, as it'll return the `otherwise`
/// branch if there's not a specific match for the value.
pub fn target_for_value(&self, value: u128) -> BasicBlock {
self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::OnceCell;
use rustc_index::bit_set::BitSet;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use super::*;
let len = blocks.len();
ReversePostorderIter { body, blocks, idx: len }
}
-
-#[derive(Clone, Debug)]
-pub(super) struct PostorderCache {
- cache: OnceCell<Vec<BasicBlock>>,
-}
-
-impl PostorderCache {
- #[inline]
- pub(super) fn new() -> Self {
- PostorderCache { cache: OnceCell::new() }
- }
-
- /// Invalidates the postorder cache.
- #[inline]
- pub(super) fn invalidate(&mut self) {
- self.cache = OnceCell::new();
- }
-
- /// Returns the `&[BasicBlocks]` represents the postorder graph for this MIR.
- #[inline]
- pub(super) fn compute(&self, body: &IndexVec<BasicBlock, BasicBlockData<'_>>) -> &[BasicBlock] {
- self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
- }
-}
-
-impl<S: Encoder> Encodable<S> for PostorderCache {
- #[inline]
- fn encode(&self, _s: &mut S) {}
-}
-
-impl<D: Decoder> Decodable<D> for PostorderCache {
- #[inline]
- fn decode(_: &mut D) -> Self {
- Self::new()
- }
-}
-
-impl<CTX> HashStable<CTX> for PostorderCache {
- #[inline]
- fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
- // do nothing
- }
-}
-
-TrivialTypeTraversalAndLiftImpls! {
- PostorderCache,
-}
impl<'tcx, R: Idx, C: Idx> TypeVisitable<'tcx> for BitMatrix<R, C> {
fn visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
query resolutions(_: ()) -> &'tcx ty::ResolverGlobalCtxt {
- eval_always
+ feedable
no_hash
desc { "getting the resolver outputs" }
}
- query resolver_for_lowering(_: ()) -> &'tcx Steal<ty::ResolverAstLowering> {
+ query resolver_for_lowering(_: ()) -> &'tcx Steal<(ty::ResolverAstLowering, Lrc<ast::Crate>)> {
feedable
no_hash
desc { "getting the resolver for lowering" }
/// Given the def_id of a const-generic parameter, computes the associated default const
/// parameter. e.g. `fn example<const N: usize=3>` called on `N` would return `3`.
- query const_param_default(param: DefId) -> ty::Const<'tcx> {
+ query const_param_default(param: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> {
desc { |tcx| "computing const default for a given parameter `{}`", tcx.def_path_str(param) }
cache_on_disk_if { param.is_local() }
separate_provide_extern
separate_provide_extern
}
+ query is_type_alias_impl_trait(key: DefId) -> bool
+ {
+ desc { "determine whether the opaque is a type-alias impl trait" }
+ separate_provide_extern
+ }
+
query analysis(key: ()) -> Result<(), ErrorGuaranteed> {
eval_always
desc { "running analysis passes on this crate" }
/// ```
///
/// Bounds from the parent (e.g. with nested impl trait) are not included.
- query item_bounds(key: DefId) -> &'tcx ty::List<ty::Predicate<'tcx>> {
+ query item_bounds(key: DefId) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
}
/// Given an `impl_id`, return the trait it implements.
/// Return `None` if this is an inherent impl.
- query impl_trait_ref(impl_id: DefId) -> Option<ty::TraitRef<'tcx>> {
+ query impl_trait_ref(impl_id: DefId) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> {
desc { |tcx| "computing trait implemented by `{}`", tcx.def_path_str(impl_id) }
cache_on_disk_if { impl_id.is_local() }
separate_provide_extern
/// 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)`.
/// Gets the name of the crate.
query crate_name(_: CrateNum) -> Symbol {
- eval_always
+ feedable
desc { "fetching what a crate is named" }
separate_provide_extern
}
/// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt`
/// has been destroyed.
query output_filenames(_: ()) -> &'tcx Arc<OutputFilenames> {
- eval_always
+ feedable
desc { "getting output filenames" }
}
}
query features_query(_: ()) -> &'tcx rustc_feature::Features {
- eval_always
+ feedable
desc { "looking up enabled feature gates" }
}
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;
/// (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 {
use crate::error::DropCheckOverflow;
use crate::infer::canonical::{Canonical, QueryResponse};
use crate::ty::error::TypeError;
-use crate::ty::subst::{GenericArg, SubstsRef};
+use crate::ty::subst::GenericArg;
use crate::ty::{self, Ty, TyCtxt};
-use rustc_hir::def_id::DefId;
use rustc_span::source_map::Span;
pub mod type_op {
pub enum OutlivesBound<'tcx> {
RegionSubRegion(ty::Region<'tcx>, ty::Region<'tcx>),
RegionSubParam(ty::Region<'tcx>, ty::ParamTy),
- RegionSubProjection(ty::Region<'tcx>, ty::AliasTy<'tcx>),
- RegionSubOpaque(ty::Region<'tcx>, DefId, SubstsRef<'tcx>),
+ RegionSubAlias(ty::Region<'tcx>, ty::AliasTy<'tcx>),
}
/// Implementation of a `Fn`-family trait by one of the anonymous types
/// generated for an `||` expression.
- ClosureCandidate,
+ ClosureCandidate {
+ is_const: bool,
+ },
/// Implementation of a `Generator` trait by one of the anonymous types
/// generated for a generator.
Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())
}
+ /// Gets the defaultness of the associated item.
+ /// To get the default associated type, use the [`type_of`] query on the
+ /// [`DefId`] of the type.
+ ///
+ /// [`type_of`]: crate::ty::TyCtxt::type_of
pub fn defaultness(&self, tcx: TyCtxt<'_>) -> hir::Defaultness {
tcx.impl_defaultness(self.def_id)
}
ty::AssocKind::Fn => {
// We skip the binder here because the binder would deanonymize all
// late-bound regions, and we don't want method signatures to show up
- // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
+ // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
tcx.fn_sig(self.def_id).skip_binder().to_string()
}
}
}
-pub fn const_param_default(tcx: TyCtxt<'_>, def_id: DefId) -> Const<'_> {
+pub fn const_param_default(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<Const<'_>> {
let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) {
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Const { default: Some(ac), .. },
"`const_param_default` expected a generic parameter with a constant"
),
};
- Const::from_anon_const(tcx, default_def_id)
+ ty::EarlyBinder(Const::from_anon_const(tcx, default_def_id))
}
use rustc_query_system::dep_graph::DepNodeIndex;
use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
-use rustc_session::config::{CrateType, OutputFilenames};
+use rustc_session::config::CrateType;
use rustc_session::cstore::{CrateStoreDyn, Untracked};
use rustc_session::lint::Lint;
use rustc_session::Limit;
use std::iter;
use std::mem;
use std::ops::{Bound, Deref};
-use std::sync::Arc;
pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync {
/// Creates a new `OnDiskCache` instance from the serialized data in `data`.
pub fn feed_unit_query(self) -> TyCtxtFeed<'tcx, ()> {
TyCtxtFeed { tcx: self, key: () }
}
+ pub fn feed_local_crate(self) -> TyCtxtFeed<'tcx, CrateNum> {
+ TyCtxtFeed { tcx: self, key: LOCAL_CRATE }
+ }
}
impl<'tcx, KEY: Copy> TyCtxtFeed<'tcx, KEY> {
pub consts: CommonConsts<'tcx>,
untracked: Untracked,
- /// Output of the resolver.
- pub(crate) untracked_resolutions: ty::ResolverGlobalCtxt,
- /// The entire crate as AST. This field serves as the input for the hir_crate query,
- /// which lowers it from AST to HIR. It must not be read or used by anything else.
- pub untracked_crate: Steal<Lrc<ast::Crate>>,
/// This provides access to the incremental compilation on-disk cache for query results.
/// Do not access this directly. It is only meant to be used by
/// Merge this with `selection_cache`?
pub evaluation_cache: traits::EvaluationCache<'tcx>,
- /// The definite name of the current crate after taking into account
- /// attributes, commandline parameters, etc.
- crate_name: Symbol,
-
/// Data layout specification for the current target.
pub data_layout: TargetDataLayout,
/// Stores memory for globals (statics/consts).
pub(crate) alloc_map: Lock<interpret::AllocMap<'tcx>>,
-
- output_filenames: Arc<OutputFilenames>,
}
impl<'tcx> TyCtxt<'tcx> {
lint_store: Lrc<dyn Any + sync::Send + sync::Sync>,
arena: &'tcx WorkerLocal<Arena<'tcx>>,
hir_arena: &'tcx WorkerLocal<hir::Arena<'tcx>>,
- untracked_resolutions: ty::ResolverGlobalCtxt,
untracked: Untracked,
- krate: Lrc<ast::Crate>,
dep_graph: DepGraph,
on_disk_cache: Option<&'tcx dyn OnDiskCache<'tcx>>,
queries: &'tcx dyn query::QueryEngine<'tcx>,
query_kinds: &'tcx [DepKindStruct<'tcx>],
- crate_name: Symbol,
- output_filenames: OutputFilenames,
) -> GlobalCtxt<'tcx> {
let data_layout = s.target.parse_data_layout().unwrap_or_else(|err| {
s.emit_fatal(err);
lifetimes: common_lifetimes,
consts: common_consts,
untracked,
- untracked_resolutions,
- untracked_crate: Steal::new(krate),
on_disk_cache,
queries,
query_caches: query::QueryCaches::default(),
pred_rcache: Default::default(),
selection_cache: Default::default(),
evaluation_cache: Default::default(),
- crate_name,
data_layout,
alloc_map: Lock::new(interpret::AllocMap::new()),
- output_filenames: Arc::new(output_filenames),
}
}
// statements within the query system and we'd run into endless
// recursion otherwise.
let (crate_name, stable_crate_id) = if def_id.is_local() {
- (self.crate_name, self.sess.local_stable_crate_id())
+ (self.crate_name(LOCAL_CRATE), self.sess.local_stable_crate_id())
} else {
let cstore = &*self.untracked.cstore;
(cstore.crate_name(def_id.krate), cstore.stable_crate_id(def_id.krate))
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
- // while iterating. If some query needs to add definitions, it should be `ensure`d above.
+ // while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.untracked.definitions.leak();
definitions.def_path_table()
}
// definitions change.
self.ensure().hir_crate(());
// Leak a read lock once we start iterating on definitions, to prevent adding new ones
- // while iterating. If some query needs to add definitions, it should be `ensure`d above.
+ // while iterating. If some query needs to add definitions, it should be `ensure`d above.
let definitions = self.untracked.definitions.leak();
definitions.def_path_hash_to_def_index_map()
}
v.0
}
+ /// Given a `DefId` for an `fn`, return all the `dyn` and `impl` traits in its return type and associated alias span when type alias is used
+ pub fn return_type_impl_or_dyn_traits_with_type_alias(
+ self,
+ scope_def_id: LocalDefId,
+ ) -> Option<(Vec<&'tcx hir::Ty<'tcx>>, Span)> {
+ let hir_id = self.hir().local_def_id_to_hir_id(scope_def_id);
+ let mut v = TraitObjectVisitor(vec![], self.hir());
+ // when the return type is a type alias
+ if let Some(hir::FnDecl { output: hir::FnRetTy::Return(hir_output), .. }) = self.hir().fn_decl_by_hir_id(hir_id)
+ && let hir::TyKind::Path(hir::QPath::Resolved(
+ None,
+ hir::Path { res: hir::def::Res::Def(DefKind::TyAlias, def_id), .. }, )) = hir_output.kind
+ && let Some(local_id) = def_id.as_local()
+ && let Some(alias_ty) = self.hir().get_by_def_id(local_id).alias_ty() // it is type alias
+ && let Some(alias_generics) = self.hir().get_by_def_id(local_id).generics()
+ {
+ v.visit_ty(alias_ty);
+ if !v.0.is_empty() {
+ return Some((v.0, alias_generics.span));
+ }
+ }
+ return None;
+ }
+
pub fn return_type_impl_trait(self, scope_def_id: LocalDefId) -> Option<(Ty<'tcx>, Span)> {
// `type_of()` will fail on these (#55796, #86483), so only allow `fn`s or closures.
match self.hir().get_by_def_id(scope_def_id) {
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_ty_var(self, v: TyVid) -> Ty<'tcx> {
self.mk_ty_infer(TyVar(v))
}
pub fn provide(providers: &mut ty::query::Providers) {
- providers.resolutions = |tcx, ()| &tcx.untracked_resolutions;
providers.module_reexports =
|tcx, id| tcx.resolutions(()).reexport_map.get(&id).map(|v| &v[..]);
- providers.crate_name = |tcx, id| {
- assert_eq!(id, LOCAL_CRATE);
- tcx.crate_name
- };
providers.maybe_unused_trait_imports =
|tcx, ()| &tcx.resolutions(()).maybe_unused_trait_imports;
providers.maybe_unused_extern_crates =
providers.extern_mod_stmt_cnum =
|tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned();
- providers.output_filenames = |tcx, ()| &tcx.output_filenames;
- providers.features_query = |tcx, ()| tcx.sess.features_untracked();
providers.is_panic_runtime = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::panic_runtime)
use crate::ty::{
visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
- PolyTraitPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
+ PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::WherePredicate;
use rustc_span::Span;
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
- format!("{}", self).into_diagnostic_arg()
+ self.to_string().into_diagnostic_arg()
}
}
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- match t.kind() {
+ match *t.kind() {
Infer(InferTy::TyVar(_)) if self.infer_suggestable => {}
FnDef(..)
}
Alias(Opaque, AliasTy { def_id, .. }) => {
- let parent = self.tcx.parent(*def_id);
- if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
- && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = self.tcx.type_of(parent).kind()
+ let parent = self.tcx.parent(def_id);
+ if let DefKind::TyAlias | DefKind::AssocTy = self.tcx.def_kind(parent)
+ && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
&& parent_opaque_def_id == def_id
{
// Okay
}
}
+ Alias(Projection, AliasTy { def_id, .. }) => {
+ if self.tcx.def_kind(def_id) != DefKind::AssocTy {
+ return ControlFlow::Break(());
+ }
+ }
+
Param(param) => {
// FIXME: It would be nice to make this not use string manipulation,
// but it's pretty hard to do this, since `ty::ParamTy` is missing
tcx: TyCtxt<'tcx>,
/// Stores the index of a binder *just outside* the stuff we have
- /// visited. So this begins as INNERMOST; when we pass through a
+ /// visited. So this begins as INNERMOST; when we pass through a
/// binder, it is incremented (via `shift_in`).
current_index: ty::DebruijnIndex,
Some(tcx.bound_type_of(self.def_id).map_bound(|t| t.into()))
}
GenericParamDefKind::Const { has_default } if has_default => {
- Some(tcx.bound_const_param_default(self.def_id).map_bound(|c| c.into()))
+ Some(tcx.const_param_default(self.def_id).map_bound(|c| c.into()))
}
_ => None,
}
&self,
tcx: TyCtxt<'tcx>,
substs: SubstsRef<'tcx>,
- ) -> InstantiatedPredicates<'tcx> {
- InstantiatedPredicates {
- predicates: self
- .predicates
- .iter()
- .map(|(p, _)| EarlyBinder(*p).subst(tcx, substs))
- .collect(),
- spans: self.predicates.iter().map(|(_, sp)| *sp).collect(),
- }
+ ) -> impl Iterator<Item = (Predicate<'tcx>, Span)> + DoubleEndedIterator + ExactSizeIterator
+ {
+ EarlyBinder(self.predicates).subst_iter_copied(tcx, substs)
}
#[instrument(level = "debug", skip(self, tcx))]
Ok(false)
}
(ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => {
- // The closure fn `llfn` is a `fn(&self, ...)`. We want a
+ // The closure fn `llfn` is a `fn(&self, ...)`. We want a
// `fn(&mut self, ...)`. In fact, at codegen time, these are
// basically the same thing, so we can just return llfn.
Ok(false)
}
(ty::ClosureKind::Fn | ty::ClosureKind::FnMut, ty::ClosureKind::FnOnce) => {
// The closure fn `llfn` is a `fn(&self, ...)` or `fn(&mut
- // self, ...)`. We want a `fn(self, ...)`. We can produce
+ // self, ...)`. We want a `fn(self, ...)`. We can produce
// this by doing something like:
//
// fn call_once(self, ...) { call_mut(&self, ...) }
//
// 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
+ // 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
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
//
// In terms of why this is sound, the idea is that whenever there
// is an impl of `T:Foo<'a>`, it must show that `T:Bar<'a,'a>`
- // holds. So if there is an impl of `T:Foo<'a>` that applies to
+ // holds. So if there is an impl of `T:Foo<'a>` that applies to
// all `'a`, then we must know that `T:Bar<'a,'a>` holds for all
// `'a`.
//
// Here, if we have `for<'x> T: Foo1<'x>`, then what do we know?
// The answer is that we know `for<'x,'b> T: Bar1<'x,'b>`. The
// reason is similar to the previous example: any impl of
- // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So
+ // `T:Foo1<'x>` must show that `for<'b> T: Bar1<'x, 'b>`. So
// basically we would want to collapse the bound lifetimes from
// the input (`trait_ref`) and the supertraits.
//
pub fn is_empty(&self) -> bool {
self.predicates.is_empty()
}
+
+ pub fn iter(&self) -> <&Self as IntoIterator>::IntoIter {
+ (&self).into_iter()
+ }
+}
+
+impl<'tcx> IntoIterator for InstantiatedPredicates<'tcx> {
+ type Item = (Predicate<'tcx>, Span);
+
+ type IntoIter = std::iter::Zip<std::vec::IntoIter<Predicate<'tcx>>, std::vec::IntoIter<Span>>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ debug_assert_eq!(self.predicates.len(), self.spans.len());
+ std::iter::zip(self.predicates, self.spans)
+ }
+}
+
+impl<'a, 'tcx> IntoIterator for &'a InstantiatedPredicates<'tcx> {
+ type Item = (Predicate<'tcx>, Span);
+
+ type IntoIter = std::iter::Zip<
+ std::iter::Copied<std::slice::Iter<'a, Predicate<'tcx>>>,
+ std::iter::Copied<std::slice::Iter<'a, Span>>,
+ >;
+
+ fn into_iter(self) -> Self::IntoIter {
+ debug_assert_eq!(self.predicates.len(), self.spans.len());
+ std::iter::zip(self.predicates.iter().copied(), self.spans.iter().copied())
+ }
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable, TyEncodable, TyDecodable, Lift)]
tcx: TyCtxt<'tcx>,
// typeck errors have subpar spans for opaque types, so delay error reporting until borrowck.
ignore_errors: bool,
- origin: OpaqueTyOrigin,
) -> Self {
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
debug!(?id_substs);
// This zip may have several times the same lifetime in `substs` paired with a different
- // lifetime from `id_substs`. Simply `collect`ing the iterator is the correct behaviour:
+ // lifetime from `id_substs`. Simply `collect`ing the iterator is the correct behaviour:
// it will pick the last one, which is the one we introduced in the impl-trait desugaring.
- let map = substs.iter().zip(id_substs);
-
- let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = match origin {
- // HACK: The HIR lowering for async fn does not generate
- // any `+ Captures<'x>` bounds for the `impl Future<...>`, so all async fns with lifetimes
- // would now fail to compile. We should probably just make hir lowering fill this in properly.
- OpaqueTyOrigin::AsyncFn(_) => map.collect(),
- OpaqueTyOrigin::FnReturn(_) | OpaqueTyOrigin::TyAlias => {
- // Opaque types may only use regions that are bound. So for
- // ```rust
- // type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
- // ```
- // we may not use `'c` in the hidden type.
- let variances = tcx.variances_of(def_id);
- debug!(?variances);
-
- map.filter(|(_, v)| {
- let ty::GenericArgKind::Lifetime(lt) = v.unpack() else { return true };
- let ty::ReEarlyBound(ebr) = lt.kind() else { bug!() };
- variances[ebr.index as usize] == ty::Variance::Invariant
- })
- .collect()
- }
- };
+ let map = substs.iter().zip(id_substs).collect();
debug!("map = {:#?}", map);
// Convert the type from the function into a type valid outside
/// Look up the name of a definition across crates. This does not look at HIR.
///
- /// This method will ICE if the corresponding item does not have a name. In these cases, use
+ /// This method will ICE if the corresponding item does not have a name. In these cases, use
/// [`opt_item_name`] instead.
///
/// [`opt_item_name`]: Self::opt_item_name
) -> Option<ImplOverlapKind> {
// If either trait impl references an error, they're allowed to overlap,
// as one of them essentially doesn't exist.
- if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.references_error())
- || self.impl_trait_ref(def_id2).map_or(false, |tr| tr.references_error())
+ if self.impl_trait_ref(def_id1).map_or(false, |tr| tr.subst_identity().references_error())
+ || self
+ .impl_trait_ref(def_id2)
+ .map_or(false, |tr| tr.subst_identity().references_error())
{
return Some(ImplOverlapKind::Permitted { marker: false });
}
let is_marker_overlap = {
let is_marker_impl = |def_id: DefId| -> bool {
let trait_ref = self.impl_trait_ref(def_id);
- trait_ref.map_or(false, |tr| self.trait_def(tr.def_id).is_marker)
+ trait_ref.map_or(false, |tr| self.trait_def(tr.skip_binder().def_id).is_marker)
};
is_marker_impl(def_id1) && is_marker_impl(def_id2)
};
self.trait_def(trait_def_id).has_auto_impl
}
+ /// Returns `true` if this is a trait alias.
+ pub fn trait_is_alias(self, trait_def_id: DefId) -> bool {
+ self.def_kind(trait_def_id) == DefKind::TraitAlias
+ }
+
pub fn trait_is_coinductive(self, trait_def_id: DefId) -> bool {
self.trait_is_auto(trait_def_id) || self.lang_items().sized_trait() == Some(trait_def_id)
}
/// Given the `DefId` of an impl, returns the `DefId` of the trait it implements.
/// If it implements no trait, returns `None`.
pub fn trait_id_of_impl(self, def_id: DefId) -> Option<DefId> {
- self.impl_trait_ref(def_id).map(|tr| tr.def_id)
+ self.impl_trait_ref(def_id).map(|tr| tr.skip_binder().def_id)
}
/// If the given `DefId` describes an item belonging to a trait,
ident
}
+ // FIXME(vincenzoapalzzo): move the HirId to a LocalDefId
pub fn adjust_ident_and_get_scope(
self,
mut ident: Ident,
#[inline]
pub fn is_const_fn_raw(self, def_id: DefId) -> bool {
- matches!(self.def_kind(def_id), DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..))
- && self.constness(def_id) == hir::Constness::Const
+ matches!(
+ self.def_kind(def_id),
+ DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(..) | DefKind::Closure
+ ) && self.constness(def_id) == hir::Constness::Const
}
#[inline]
}
#[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:
///
type Value<'tcx> = ty::Binder<'tcx, T::Value<'tcx>>;
}
+impl<T: ParameterizedOverTcx> ParameterizedOverTcx for ty::EarlyBinder<T> {
+ type Value<'tcx> = ty::EarlyBinder<T::Value<'tcx>>;
+}
+
#[macro_export]
macro_rules! trivially_parameterized_over_tcx {
($($ty:ty),+ $(,)?) => {
usize,
(),
u32,
+ bool,
std::string::String,
crate::metadata::ModChild,
crate::middle::codegen_fn_attrs::CodegenFnAttrs,
DefPathData::Impl => {
let generics = self.tcx().generics_of(def_id);
let self_ty = self.tcx().bound_type_of(def_id);
- let impl_trait_ref = self.tcx().bound_impl_trait_ref(def_id);
+ let impl_trait_ref = self.tcx().impl_trait_ref(def_id);
let (self_ty, impl_trait_ref) = if substs.len() >= generics.count() {
(
self_ty.subst(self.tcx(), substs),
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::Array(ty, sz) => {
- p!("[", print(ty), "; ");
- if self.should_print_verbose() {
- p!(write("{:?}", sz));
- } else if let ty::ConstKind::Unevaluated(..) = sz.kind() {
- // Do not try to evaluate unevaluated constants. If we are const evaluating an
- // array length anon const, rustc will (with debug assertions) print the
- // constant's path. Which will end up here again.
- p!("_");
- } else if let Some(n) = sz.kind().try_to_bits(self.tcx().data_layout.pointer_size) {
- p!(write("{}", n));
- } else if let ty::ConstKind::Param(param) = sz.kind() {
- p!(print(param));
- } else {
- p!("_");
- }
- p!("]")
- }
+ ty::Array(ty, sz) => p!("[", print(ty), "; ", print(sz), "]"),
ty::Slice(ty) => p!("[", print(ty), "]"),
}
match ct.kind() {
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs }) => {
match self.tcx().def_kind(def.did) {
- DefKind::Static(..) | DefKind::Const | DefKind::AssocConst => {
+ DefKind::Const | DefKind::AssocConst => {
p!(print_value_path(def.did, substs))
}
- _ => {
- if def.is_local() {
- let span = self.tcx().def_span(def.did);
- if let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span) {
- p!(write("{}", snip))
- } else {
- print_underscore!()
- }
+ DefKind::AnonConst => {
+ if def.is_local()
+ && let span = self.tcx().def_span(def.did)
+ && let Ok(snip) = self.tcx().sess.source_map().span_to_snippet(span)
+ {
+ p!(write("{}", snip))
} else {
- print_underscore!()
+ // Do not call `print_value_path` as if a parent of this anon const is an impl it will
+ // attempt to print out the impl trait ref i.e. `<T as Trait>::{constant#0}`. This would
+ // cause printing to enter an infinite recursion if the anon const is in the self type i.e.
+ // `impl<T: Default> Default for [T; 32 - 1 - 1 - 1] {`
+ // where we would try to print `<[T; /* print `constant#0` again */] as Default>::{constant#0}`
+ p!(write("{}::{}", self.tcx().crate_name(def.did.krate), self.tcx().def_path(def.did).to_string_no_crate_verbose()))
}
}
+ defkind => bug!("`{:?}` has unexpcted defkind {:?}", ct, defkind),
}
}
ty::ConstKind::Infer(infer_ct) => {
ty::ConstKind::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
// FIXME(generic_const_exprs):
// write out some legible representation of an abstract const?
- ty::ConstKind::Expr(_) => p!("[Const Expr]"),
+ ty::ConstKind::Expr(_) => p!("[const expr]"),
ty::ConstKind::Error(_) => p!("[const error]"),
};
Ok(self)
let identify_regions = self.tcx.sess.opts.unstable_opts.identify_regions;
- // These printouts are concise. They do not contain all the information
+ // These printouts are concise. They do not contain all the information
// the user might want to diagnose an error, but there is basically no way
- // to fit that into a short string. Hence the recommendation to use
+ // to fit that into a short string. Hence the recommendation to use
// `explain_region()` or `note_and_explain_region()`.
match *region {
ty::ReEarlyBound(ref data) => {
if not_previously_inserted {
ty.super_visit_with(self)
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable};
use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
use crate::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use crate::ty::{self, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
+use crate::ty::{self, AliasTy, InferConst, Lift, Term, TermKind, Ty, TyCtxt};
use rustc_data_structures::functor::IdFunctor;
use rustc_hir::def::Namespace;
use rustc_index::vec::{Idx, IndexVec};
+use rustc_target::abi::TyAndLayout;
use std::fmt;
use std::mem::ManuallyDrop;
}
}
+impl<'tcx> fmt::Debug for AliasTy<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("AliasTy")
+ .field("substs", &self.substs)
+ .field("def_id", &self.def_id)
+ .finish()
+ }
+}
+
///////////////////////////////////////////////////////////////////////////
// Atomic structs
//
crate::ty::BoundRegionKind,
crate::ty::AssocItem,
crate::ty::AssocKind,
+ crate::ty::AliasKind,
crate::ty::Placeholder<crate::ty::BoundRegionKind>,
crate::ty::ClosureKind,
crate::ty::FreeRegion,
impl<'tcx> TypeVisitable<'tcx> for ty::AdtDef<'tcx> {
fn visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
let slot = Rc::get_mut_unchecked(&mut unique);
// Semantically move the contained type out from `unique`, fold
- // it, then move the folded value back into `unique`. Should
+ // it, then move the folded value back into `unique`. Should
// folding fail, `ManuallyDrop` ensures that the "moved-out"
// value is not re-dropped.
let owned = ManuallyDrop::take(slot);
let slot = Arc::get_mut_unchecked(&mut unique);
// Semantically move the contained type out from `unique`, fold
- // it, then move the folded value back into `unique`. Should
+ // it, then move the folded value back into `unique`. Should
// folding fail, `ManuallyDrop` ensures that the "moved-out"
// value is not re-dropped.
let owned = ManuallyDrop::take(slot);
| ty::Placeholder(..)
| ty::Param(..)
| ty::Never
- | ty::Foreign(..) => ControlFlow::CONTINUE,
+ | ty::Foreign(..) => ControlFlow::Continue(()),
}
}
}
impl<'tcx> TypeSuperVisitable<'tcx> for ty::Region<'tcx> {
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
impl<'tcx> TypeVisitable<'tcx> for InferConst<'tcx> {
fn visit_with<V: TypeVisitor<'tcx>>(&self, _visitor: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
self.substs.visit_with(visitor)
}
}
+
+impl<'tcx> TypeVisitable<'tcx> for TyAndLayout<'tcx, Ty<'tcx>> {
+ fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
+ visitor.visit_ty(self.ty)
+ }
+}
use crate::ty::visit::ValidateBoundVars;
use crate::ty::InferTy::*;
use crate::ty::{
- self, AdtDef, DefIdTree, Discr, Term, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable,
- TypeVisitor,
+ self, AdtDef, DefIdTree, Discr, FallibleTypeFolder, Term, Ty, TyCtxt, TypeFlags, TypeFoldable,
+ TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use crate::ty::{List, ParamEnv};
use hir::def::DefKind;
None
}
+
+ pub fn get_id(&self) -> Option<DefId> {
+ match *self {
+ BoundRegionKind::BrNamed(id, _) => return Some(id),
+ _ => None,
+ }
+ }
}
pub trait Article {
///
/// ## Generators
///
-/// Generators are handled similarly in `GeneratorSubsts`. The set of
+/// Generators are handled similarly in `GeneratorSubsts`. The set of
/// type parameters is similar, but `CK` and `CS` are replaced by the
/// following type parameters:
///
}
}
+struct SkipBindersAt<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ index: ty::DebruijnIndex,
+}
+
+impl<'tcx> FallibleTypeFolder<'tcx> for SkipBindersAt<'tcx> {
+ type Error = ();
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn try_fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Result<Binder<'tcx, T>, Self::Error>
+ where
+ T: ty::TypeFoldable<'tcx>,
+ {
+ self.index.shift_in(1);
+ let value = t.try_map_bound(|t| t.try_fold_with(self));
+ self.index.shift_out(1);
+ value
+ }
+
+ fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+ if !ty.has_escaping_bound_vars() {
+ Ok(ty)
+ } else if let ty::Bound(index, bv) = *ty.kind() {
+ if index == self.index {
+ Err(())
+ } else {
+ Ok(self.tcx().mk_ty(ty::Bound(index.shifted_out(1), bv)))
+ }
+ } else {
+ ty.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> {
+ if !r.has_escaping_bound_vars() {
+ Ok(r)
+ } else if let ty::ReLateBound(index, bv) = r.kind() {
+ if index == self.index {
+ Err(())
+ } else {
+ Ok(self.tcx().mk_region(ty::ReLateBound(index.shifted_out(1), bv)))
+ }
+ } else {
+ r.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
+ if !ct.has_escaping_bound_vars() {
+ Ok(ct)
+ } else if let ty::ConstKind::Bound(index, bv) = ct.kind() {
+ if index == self.index {
+ Err(())
+ } else {
+ Ok(self.tcx().mk_const(
+ ty::ConstKind::Bound(index.shifted_out(1), bv),
+ ct.ty().try_fold_with(self)?,
+ ))
+ }
+ } else {
+ ct.try_super_fold_with(self)
+ }
+ }
+
+ fn try_fold_predicate(
+ &mut self,
+ p: ty::Predicate<'tcx>,
+ ) -> Result<ty::Predicate<'tcx>, Self::Error> {
+ if !p.has_escaping_bound_vars() { Ok(p) } else { p.try_super_fold_with(self) }
+ }
+}
+
/// Represents the projection of an associated type.
///
/// For a projection, this would be `<Ty as Trait<...>>::N`.
///
/// For an opaque type, there is no explicit syntax.
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct AliasTy<'tcx> {
/// The parameters of the associated or opaque item.
/// aka. `tcx.parent(def_id)`.
pub def_id: DefId,
- /// This field exists to prevent the creation of `ProjectionTy` without using
+ /// This field exists to prevent the creation of `AliasTy` without using
/// [TyCtxt::mk_alias_ty].
pub(super) _use_mk_alias_ty_instead: (),
}
+impl<'tcx> AliasTy<'tcx> {
+ pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind {
+ match tcx.def_kind(self.def_id) {
+ DefKind::AssocTy | DefKind::ImplTraitPlaceholder => ty::Projection,
+ DefKind::OpaqueTy => ty::Opaque,
+ kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
+ }
+ }
+
+ pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ tcx.mk_ty(ty::Alias(self.kind(tcx), self))
+ }
+}
+
+/// The following methods work only with associated type projections.
impl<'tcx> AliasTy<'tcx> {
pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
match tcx.def_kind(self.def_id) {
DefKind::ImplTraitPlaceholder => {
tcx.parent(tcx.impl_trait_in_trait_parent(self.def_id))
}
- kind => bug!("unexpected DefKind in ProjectionTy: {kind:?}"),
+ kind => bug!("expected a projection AliasTy; found {kind:?}"),
}
}
}
#[inline]
- pub fn is_ty_infer(self) -> bool {
+ pub fn is_ty_or_numeric_infer(self) -> bool {
matches!(self.kind(), Infer(_))
}
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) }
}
}
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
use rustc_data_structures::intern::Interned;
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_serialize::{self, Decodable, Encodable};
marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>,
}
+impl<'tcx> IntoDiagnosticArg for GenericArg<'tcx> {
+ fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+ self.to_string().into_diagnostic_arg()
+ }
+}
+
const TAG_MASK: usize = 0b11;
const TYPE_TAG: usize = 0b00;
const REGION_TAG: usize = 0b01;
pub fn is_non_region_infer(self) -> bool {
match self.unpack() {
GenericArgKind::Lifetime(_) => false,
- GenericArgKind::Type(ty) => ty.is_ty_infer(),
+ GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(),
GenericArgKind::Const(ct) => ct.is_ct_infer(),
}
}
/// Similar to [`super::Binder`] except that it tracks early bound generics, i.e. `struct Foo<T>(T)`
/// needs `T` substituted immediately. This type primarily exists to avoid forgetting to call
/// `subst`.
+///
+/// If you don't have anything to `subst`, you may be looking for
+/// [`subst_identity`](EarlyBinder::subst_identity) or [`skip_binder`](EarlyBinder::skip_binder).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[derive(Encodable, Decodable, HashStable)]
pub struct EarlyBinder<T>(pub T);
EarlyBinder(value)
}
+ /// Skips the binder and returns the "bound" value.
+ /// This can be used to extract data that does not depend on generic parameters
+ /// (e.g., getting the `DefId` of the inner value or getting the number of
+ /// arguments of an `FnSig`). Otherwise, consider using
+ /// [`subst_identity`](EarlyBinder::subst_identity).
+ ///
+ /// See also [`Binder::skip_binder`](super::Binder::skip_binder), which is
+ /// the analogous operation on [`super::Binder`].
pub fn skip_binder(self) -> T {
self.0
}
}
}
+impl<'tcx, I: IntoIterator> ExactSizeIterator for SubstIter<'_, 'tcx, I>
+where
+ I::IntoIter: ExactSizeIterator,
+ I::Item: TypeFoldable<'tcx>,
+{
+}
+
impl<'tcx, 's, I: IntoIterator> EarlyBinder<I>
where
I::Item: Deref,
}
}
+impl<'tcx, I: IntoIterator> ExactSizeIterator for SubstIterCopied<'_, 'tcx, I>
+where
+ I::IntoIter: ExactSizeIterator,
+ I::Item: Deref,
+ <I::Item as Deref>::Target: Copy + TypeFoldable<'tcx>,
+{
+}
+
pub struct EarlyBinderIter<T> {
t: T,
}
let mut folder = SubstFolder { tcx, substs, binders_passed: 0 };
self.0.fold_with(&mut folder)
}
+
+ /// Makes the identity substitution `T0 => T0, ..., TN => TN`.
+ /// Conceptually, this converts universally bound variables into placeholders
+ /// when inside of a given item.
+ ///
+ /// For example, consider `for<T> fn foo<T>(){ .. }`:
+ /// - Outside of `foo`, `T` is bound (represented by the presence of `EarlyBinder`).
+ /// - Inside of the body of `foo`, we treat `T` as a placeholder by calling
+ /// `subst_identity` to discharge the `EarlyBinder`.
+ pub fn subst_identity(self) -> T {
+ self.0
+ }
}
///////////////////////////////////////////////////////////////////////////
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;
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
/// We sometimes treat byte string literals (which are of type `&[u8; N]`)
- /// as `&[u8]`, depending on the pattern in which they are used.
+ /// as `&[u8]`, depending on the pattern in which they are used.
/// This hashset records all instances where we behave
/// like this to allow `const_to_pat` to reliably handle this situation.
pub treat_byte_string_as_slice: ItemLocalSet,
/// Returns the type of an expression as a monotype.
///
- /// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
+ /// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
/// some cases, we insert `Adjustment` annotations such as auto-deref or
- /// auto-ref. The type returned by this function does not consider such
- /// adjustments. See `expr_ty_adjusted()` instead.
+ /// auto-ref. The type returned by this function does not consider such
+ /// adjustments. See `expr_ty_adjusted()` instead.
///
/// NB (2): This type doesn't provide type parameter substitutions; e.g., if you
/// ask for the type of `id` in `id(3)`, it will return `fn(&isize) -> isize`
self.data.get(&id.local_id)
}
- pub fn iter(&self) -> hash_map::Iter<'_, hir::ItemLocalId, V> {
- self.data.iter()
+ pub fn items(
+ &'a self,
+ ) -> UnordItems<(hir::ItemLocalId, &'a V), impl Iterator<Item = (hir::ItemLocalId, &'a V)>>
+ {
+ self.data.items().map(|(id, value)| (*id, value))
+ }
+
+ pub fn items_in_stable_order(&self) -> Vec<(ItemLocalId, &'a V)> {
+ self.data.to_sorted_stable_ord()
}
}
validate_hir_id_for_typeck_results(self.hir_owner, id);
self.data.remove(&id.local_id)
}
+
+ pub fn extend(
+ &mut self,
+ items: UnordItems<(hir::HirId, V), impl Iterator<Item = (hir::HirId, V)>>,
+ ) {
+ self.data.extend(items.map(|(id, value)| {
+ validate_hir_id_for_typeck_results(self.hir_owner, id);
+ (id.local_id, value)
+ }))
+ }
}
rustc_index::newtype_index! {
ty::EarlyBinder(self.fn_sig(def_id))
}
- pub fn bound_impl_trait_ref(
- self,
- def_id: DefId,
- ) -> Option<ty::EarlyBinder<ty::TraitRef<'tcx>>> {
- self.impl_trait_ref(def_id).map(|i| ty::EarlyBinder(i))
- }
-
pub fn bound_explicit_item_bounds(
self,
def_id: DefId,
ty::EarlyBinder(self.explicit_item_bounds(def_id))
}
- pub fn bound_item_bounds(
- self,
- def_id: DefId,
- ) -> ty::EarlyBinder<&'tcx ty::List<ty::Predicate<'tcx>>> {
- ty::EarlyBinder(self.item_bounds(def_id))
- }
-
- pub fn bound_const_param_default(self, def_id: DefId) -> ty::EarlyBinder<ty::Const<'tcx>> {
- ty::EarlyBinder(self.const_param_default(def_id))
- }
-
- pub fn bound_predicates_of(
- self,
- def_id: DefId,
- ) -> ty::EarlyBinder<ty::generics::GenericPredicates<'tcx>> {
- ty::EarlyBinder(self.predicates_of(def_id))
- }
-
- pub fn bound_explicit_predicates_of(
- self,
- def_id: DefId,
- ) -> ty::EarlyBinder<ty::generics::GenericPredicates<'tcx>> {
- ty::EarlyBinder(self.explicit_predicates_of(def_id))
- }
-
pub fn bound_impl_subject(self, def_id: DefId) -> ty::EarlyBinder<ty::ImplSubject<'tcx>> {
ty::EarlyBinder(self.impl_subject(def_id))
}
}
/// 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)))
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(())
}
}
}
}
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<Ty<'_>> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
+ ty::EarlyBinder(Ty::from_cycle_error(tcx, cycle))
+ }
+}
+
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
+ ty::EarlyBinder(ty::Binder::from_cycle_error(tcx, cycle))
+ }
+}
+
// item_and_field_ids should form a cycle where each field contains the
// type in the next element in the list
pub fn recursive_type_error(
block_map: FxHashMap::default(),
};
- let res = (|| {
+ let res: PResult<_> = try {
pctxt.parse_args(¶ms)?;
- pctxt.parse_body(expr)
- })();
+ pctxt.parse_body(expr)?;
+ };
if let Err(err) = res {
tcx.sess.diagnostic().span_fatal(
err.span,
impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
pub fn parse_statement(&self, expr_id: ExprId) -> PResult<StatementKind<'tcx>> {
parse_by_kind!(self, expr_id, _, "statement",
+ @call("mir_storage_live", args) => {
+ Ok(StatementKind::StorageLive(self.parse_local(args[0])?))
+ },
+ @call("mir_storage_dead", args) => {
+ Ok(StatementKind::StorageDead(self.parse_local(args[0])?))
+ },
@call("mir_retag", args) => {
Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?)))
},
/// suitable also to be passed as function arguments.
///
/// The operand returned from this function will *not be valid* after an ExprKind::Scope is
- /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
+ /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
/// operand suitable for use as a call argument. This is almost always equivalent to
/// `as_operand`, except for the particular case of passing values of (potentially) unsized
/// types "by value" (see details below).
// question raised here -- should we "freeze" the
// value of the lhs here? I'm inclined to think not,
// since it seems closer to the semantics of the
- // overloaded version, which takes `&mut self`. This
+ // overloaded version, which takes `&mut self`. This
// only affects weird things like `x += {x += 1; x}`
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?
//
// it is usually better to focus on `the_value` rather
// than the entirety of block(s) surrounding it.
- let adjusted_span = (|| {
+ let adjusted_span =
if let ExprKind::Block { block } = expr.kind
&& let Some(tail_ex) = this.thir[block].expr
{
tail_result_is_ignored: true,
span: expr.span,
});
- return Some(expr.span);
- }
- None
- })();
+ Some(expr.span)
+ } else {
+ None
+ };
let temp =
unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not));
// ```
// let place = Foo::new();
// match place { foo if inspect(foo)
- // => feed(foo), ... }
+ // => feed(foo), ... }
// ```
//
// will be treated as if it were really something like:
// ```
// let place = Foo::new();
// match place { ref mut foo if inspect(foo)
- // => feed(foo), ... }
+ // => feed(foo), ... }
// ```
//
// will be treated as if it were really something like:
span: source_info.span,
// FIXME(#54571): This constant comes from user input (a
- // constant in a pattern). Are there forms where users can add
+ // constant in a pattern). Are there forms where users can add
// type annotations here? For example, an associated constant?
// Need to experiment.
user_ty: None,
/// This is used by the overall `match_candidates` algorithm to structure
/// the match as a whole. See `match_candidates` for more details.
///
- /// FIXME(#29623). In some cases, we have some tricky choices to make. for
+ /// FIXME(#29623). In some cases, we have some tricky choices to make. for
/// example, if we are testing that `x == 22`, but the candidate is `x @
/// 13..55`, what should we do? In the event that the test is true, we know
/// that the candidate applies, but in the event of false, we don't know
```
When processing the `let x`, we will add one drop to the scope for
-`x`. The break will then insert a drop for `x`. When we process `let
+`x`. The break will then insert a drop for `x`. When we process `let
y`, we will add another drop (in fact, to a subscope, but let's ignore
that for now); any later drops would also drop `y`.
if self.tcx.sess.opts.unstable_opts.maximal_hir_to_mir_coverage {
// Some consumers of rustc need to map MIR locations back to HIR nodes. Currently the
// the only part of rustc that tracks MIR -> HIR is the `SourceScopeLocalData::lint_root`
- // field that tracks lint levels for MIR locations. Normally the number of source scopes
+ // field that tracks lint levels for MIR locations. Normally the number of source scopes
// is limited to the set of nodes with lint annotations. The -Zmaximal-hir-to-mir-coverage
// flag changes this behavior to maximize the number of source scopes, increasing the
// granularity of the MIR->HIR mapping.
#[subdiagnostic]
pub let_suggestion: Option<SuggestLet>,
#[subdiagnostic]
+ pub misc_suggestion: Option<MiscPatternSuggestion>,
+ #[subdiagnostic]
pub res_defined_here: Option<ResDefinedHere>,
}
count: usize,
},
}
+
+#[derive(Subdiagnostic)]
+pub enum MiscPatternSuggestion {
+ #[suggestion(
+ mir_build_suggest_attempted_int_lit,
+ code = "_",
+ applicability = "maybe-incorrect"
+ )]
+ AttemptedIntegerLiteral {
+ #[primary_span]
+ start_span: Span,
+ },
+}
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
-#![feature(control_flow_enum)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(min_specialization)]
/// Returns `true` if `func` refers to the function we are searching in.
fn is_recursive_call(&self, func: &Operand<'tcx>, args: &[Operand<'tcx>]) -> bool {
let Search { tcx, body, trait_substs, .. } = *self;
- // Resolving function type to a specific instance that is being called is expensive. To
+ // Resolving function type to a specific instance that is being called is expensive. To
// avoid the cost we check the number of arguments first, which is sufficient to reject
// most of calls as non-recursive.
if args.len() != body.arg_count {
// A diverging InlineAsm is treated as non-recursing
TerminatorKind::InlineAsm { destination, .. } => {
if destination.is_some() {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
ControlFlow::Break(NonRecursive)
}
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
- | TerminatorKind::SwitchInt { .. } => ControlFlow::CONTINUE,
+ | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()),
}
}
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn ignore_edge(&mut self, bb: BasicBlock, target: BasicBlock) -> bool {
use crate::errors::*;
+use hir::{ExprKind, PatKind};
use rustc_arena::TypedArena;
-use rustc_ast::Mutability;
+use rustc_ast::{LitKind, Mutability};
use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
return;
}
- let (inform, interpreted_as_const, res_defined_here,let_suggestion) =
+ let (inform, interpreted_as_const, res_defined_here,let_suggestion, misc_suggestion) =
if let hir::PatKind::Path(hir::QPath::Resolved(
None,
hir::Path {
}
},
None,
+ None,
)
} else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) {
let mut bindings = vec![];
let end_span = semi_span.shrink_to_lo();
let count = witnesses.len();
+ // If the pattern to match is an integer literal:
+ let int_suggestion = if
+ let PatKind::Lit(expr) = &pat.kind
+ && bindings.is_empty()
+ && let ExprKind::Lit(Spanned { node: LitKind::Int(_, _), span }) = expr.kind {
+ // Then give a suggestion, the user might've meant to create a binding instead.
+ Some(MiscPatternSuggestion::AttemptedIntegerLiteral { start_span: span.shrink_to_lo() })
+ } else { None };
+
let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }};
- (sp.map(|_|Inform), None, None, Some(let_suggestion))
+ (sp.map(|_|Inform), None, None, Some(let_suggestion), int_suggestion)
} else{
- (sp.map(|_|Inform), None, None, None)
+ (sp.map(|_|Inform), None, None, None, None)
};
let adt_defined_here = try {
_p: (),
pattern_ty,
let_suggestion,
+ misc_suggestion,
res_defined_here,
adt_defined_here,
});
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 {
/// Calls `f` for each mutable borrow or raw reference in the program.
///
-/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
+/// This DOES NOT call `f` for a shared borrow of a type with interior mutability. That's okay for
/// initializedness, because we cannot move from an `UnsafeCell` (outside of `core::cell`), but
/// other analyses will likely need to check for `!Freeze`.
fn for_each_mut_borrow<'tcx>(
// PART 3
// Add retag after assignments where data "enters" this function: the RHS is behind a deref and the LHS is not.
for block_data in basic_blocks {
- // We want to insert statements as we iterate. To this end, we
+ // We want to insert statements as we iterate. To this end, we
// iterate backwards using indices.
for i in (0..block_data.statements.len()).rev() {
let (retag_kind, place) = match block_data.statements[i].kind {
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)
}
}
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,
}
};
- 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);
destination
};
+ // Always create a local to hold the destination, as `RETURN_PLACE` may appear
+ // where a full `Place` is not allowed.
+ let (remap_destination, destination_local) = if let Some(d) = dest.as_local() {
+ (false, d)
+ } else {
+ (
+ true,
+ self.new_call_temp(
+ caller_body,
+ &callsite,
+ destination.ty(caller_body, self.tcx).ty,
+ ),
+ )
+ };
+
// Copy the arguments if needed.
let args: Vec<_> = self.make_call_args(args, &callsite, caller_body, &callee_body);
new_locals: Local::new(caller_body.local_decls.len())..,
new_scopes: SourceScope::new(caller_body.source_scopes.len())..,
new_blocks: BasicBlock::new(caller_body.basic_blocks.len())..,
- destination: dest,
+ destination: destination_local,
callsite_scope: caller_body.source_scopes[callsite.source_info.scope].clone(),
callsite,
cleanup_block: cleanup,
// To avoid repeated O(n) insert, push any new statements to the end and rotate
// the slice once.
let mut n = 0;
+ if remap_destination {
+ caller_body[block].statements.push(Statement {
+ source_info: callsite.source_info,
+ kind: StatementKind::Assign(Box::new((
+ dest,
+ Rvalue::Use(Operand::Move(destination_local.into())),
+ ))),
+ });
+ n += 1;
+ }
for local in callee_body.vars_and_temps_iter().rev() {
if !callee_body.local_decls[local].internal
&& integrator.always_live_locals.contains(local)
new_locals: RangeFrom<Local>,
new_scopes: RangeFrom<SourceScope>,
new_blocks: RangeFrom<BasicBlock>,
- destination: Place<'tcx>,
+ destination: Local,
callsite_scope: SourceScopeData<'tcx>,
callsite: &'a CallSite<'tcx>,
cleanup_block: Option<BasicBlock>,
impl Integrator<'_, '_> {
fn map_local(&self, local: Local) -> Local {
let new = if local == RETURN_PLACE {
- self.destination.local
+ self.destination
} else {
let idx = local.index() - 1;
if idx < self.args.len() {
*span = span.fresh_expansion(self.expn_data);
}
- fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- for elem in place.projection {
- // FIXME: Make sure that return place is not used in an indexing projection, since it
- // won't be rebased as it is supposed to be.
- assert_ne!(ProjectionElem::Index(RETURN_PLACE), elem);
- }
-
- // If this is the `RETURN_PLACE`, we need to rebase any projections onto it.
- let dest_proj_len = self.destination.projection.len();
- if place.local == RETURN_PLACE && dest_proj_len > 0 {
- let mut projs = Vec::with_capacity(dest_proj_len + place.projection.len());
- projs.extend(self.destination.projection);
- projs.extend(place.projection);
-
- place.projection = self.tcx.intern_place_elems(&*projs);
- }
- // Handles integrating any locals that occur in the base
- // or projections
- self.super_place(place, context, location)
- }
-
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &mut BasicBlockData<'tcx>) {
self.in_cleanup_block = data.is_cleanup;
self.super_basic_block_data(block, data);
BinOp, Body, Constant, ConstantKind, LocalDecls, Operand, Place, ProjectionElem, Rvalue,
SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnOp,
};
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, layout::TyAndLayout, ParamEnv, ParamEnvAnd, SubstsRef, Ty, TyCtxt};
+use rustc_span::symbol::{sym, Symbol};
pub struct InstCombine;
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let ctx = InstCombineContext { tcx, local_decls: &body.local_decls };
+ let ctx = InstCombineContext {
+ tcx,
+ local_decls: &body.local_decls,
+ param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()),
+ };
for block in body.basic_blocks.as_mut() {
for statement in block.statements.iter_mut() {
match statement.kind {
&mut block.terminator.as_mut().unwrap(),
&mut block.statements,
);
+ ctx.combine_intrinsic_assert(
+ &mut block.terminator.as_mut().unwrap(),
+ &mut block.statements,
+ );
}
}
}
struct InstCombineContext<'tcx, 'a> {
tcx: TyCtxt<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
+ param_env: ParamEnv<'tcx>,
}
impl<'tcx> InstCombineContext<'tcx, '_> {
});
terminator.kind = TerminatorKind::Goto { target: destination_block };
}
+
+ fn combine_intrinsic_assert(
+ &self,
+ terminator: &mut Terminator<'tcx>,
+ _statements: &mut Vec<Statement<'tcx>>,
+ ) {
+ let TerminatorKind::Call { func, target, .. } = &mut terminator.kind else { return; };
+ let Some(target_block) = target else { return; };
+ let func_ty = func.ty(self.local_decls, self.tcx);
+ let Some((intrinsic_name, substs)) = resolve_rust_intrinsic(self.tcx, func_ty) else {
+ return;
+ };
+ // The intrinsics we are interested in have one generic parameter
+ if substs.is_empty() {
+ return;
+ }
+ let ty = substs.type_at(0);
+
+ // Check this is a foldable intrinsic before we query the layout of our generic parameter
+ let Some(assert_panics) = intrinsic_assert_panics(intrinsic_name) else { return; };
+ let Ok(layout) = self.tcx.layout_of(self.param_env.and(ty)) else { return; };
+ if assert_panics(self.tcx, self.param_env.and(layout)) {
+ // If we know the assert panics, indicate to later opts that the call diverges
+ *target = None;
+ } else {
+ // If we know the assert does not panic, turn the call into a Goto
+ terminator.kind = TerminatorKind::Goto { target: *target_block };
+ }
+ }
+}
+
+fn intrinsic_assert_panics<'tcx>(
+ intrinsic_name: Symbol,
+) -> Option<fn(TyCtxt<'tcx>, ParamEnvAnd<'tcx, TyAndLayout<'tcx>>) -> bool> {
+ fn inhabited_predicate<'tcx>(
+ _tcx: TyCtxt<'tcx>,
+ param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
+ ) -> bool {
+ let (_param_env, layout) = param_env_and_layout.into_parts();
+ layout.abi.is_uninhabited()
+ }
+ fn zero_valid_predicate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
+ ) -> bool {
+ !tcx.permits_zero_init(param_env_and_layout)
+ }
+ fn mem_uninitialized_valid_predicate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env_and_layout: ParamEnvAnd<'tcx, TyAndLayout<'tcx>>,
+ ) -> bool {
+ !tcx.permits_uninit_init(param_env_and_layout)
+ }
+
+ match intrinsic_name {
+ sym::assert_inhabited => Some(inhabited_predicate),
+ sym::assert_zero_valid => Some(zero_valid_predicate),
+ sym::assert_mem_uninitialized_valid => Some(mem_uninitialized_valid_predicate),
+ _ => None,
+ }
+}
+
+fn resolve_rust_intrinsic<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ func_ty: Ty<'tcx>,
+) -> Option<(Symbol, SubstsRef<'tcx>)> {
+ if let ty::FnDef(def_id, substs) = *func_ty.kind() {
+ if tcx.is_intrinsic(def_id) {
+ return Some((tcx.item_name(def_id), substs));
+ }
+ }
+ None
}
pub mod simplify;
mod simplify_branches;
mod simplify_comparison_integral;
-mod simplify_try;
mod sroa;
mod uninhabited_enum_branching;
mod unreachable_prop;
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
&cleanup_post_borrowck::CleanupPostBorrowck,
- &simplify_branches::SimplifyConstCondition::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
&simplify::SimplifyCfg::new("early-opt"),
&deref_separator::Derefer,
&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")),
+++ /dev/null
-//! The general point of the optimizations provided here is to simplify something like:
-//!
-//! ```rust
-//! # fn foo<T, E>(x: Result<T, E>) -> Result<T, E> {
-//! match x {
-//! Ok(x) => Ok(x),
-//! Err(x) => Err(x)
-//! }
-//! # }
-//! ```
-//!
-//! into just `x`.
-
-use crate::{simplify, MirPass};
-use itertools::Itertools as _;
-use rustc_index::{bit_set::BitSet, vec::IndexVec};
-use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor};
-use rustc_middle::mir::*;
-use rustc_middle::ty::{self, List, Ty, TyCtxt};
-use rustc_target::abi::VariantIdx;
-use std::iter::{once, Enumerate, Peekable};
-use std::slice::Iter;
-
-/// Simplifies arms of form `Variant(x) => Variant(x)` to just a move.
-///
-/// This is done by transforming basic blocks where the statements match:
-///
-/// ```ignore (MIR)
-/// _LOCAL_TMP = ((_LOCAL_1 as Variant ).FIELD: TY );
-/// _TMP_2 = _LOCAL_TMP;
-/// ((_LOCAL_0 as Variant).FIELD: TY) = move _TMP_2;
-/// discriminant(_LOCAL_0) = VAR_IDX;
-/// ```
-///
-/// into:
-///
-/// ```ignore (MIR)
-/// _LOCAL_0 = move _LOCAL_1
-/// ```
-pub struct SimplifyArmIdentity;
-
-#[derive(Debug)]
-struct ArmIdentityInfo<'tcx> {
- /// Storage location for the variant's field
- local_temp_0: Local,
- /// Storage location holding the variant being read from
- local_1: Local,
- /// The variant field being read from
- vf_s0: VarField<'tcx>,
- /// Index of the statement which loads the variant being read
- get_variant_field_stmt: usize,
-
- /// Tracks each assignment to a temporary of the variant's field
- field_tmp_assignments: Vec<(Local, Local)>,
-
- /// Storage location holding the variant's field that was read from
- local_tmp_s1: Local,
- /// Storage location holding the enum that we are writing to
- local_0: Local,
- /// The variant field being written to
- vf_s1: VarField<'tcx>,
-
- /// Storage location that the discriminant is being written to
- set_discr_local: Local,
- /// The variant being written
- set_discr_var_idx: VariantIdx,
-
- /// Index of the statement that should be overwritten as a move
- stmt_to_overwrite: usize,
- /// SourceInfo for the new move
- source_info: SourceInfo,
-
- /// Indices of matching Storage{Live,Dead} statements encountered.
- /// (StorageLive index,, StorageDead index, Local)
- storage_stmts: Vec<(usize, usize, Local)>,
-
- /// The statements that should be removed (turned into nops)
- stmts_to_remove: Vec<usize>,
-
- /// Indices of debug variables that need to be adjusted to point to
- // `{local_0}.{dbg_projection}`.
- dbg_info_to_adjust: Vec<usize>,
-
- /// The projection used to rewrite debug info.
- dbg_projection: &'tcx List<PlaceElem<'tcx>>,
-}
-
-fn get_arm_identity_info<'a, 'tcx>(
- stmts: &'a [Statement<'tcx>],
- locals_count: usize,
- debug_info: &'a [VarDebugInfo<'tcx>],
-) -> Option<ArmIdentityInfo<'tcx>> {
- // This can't possibly match unless there are at least 3 statements in the block
- // so fail fast on tiny blocks.
- if stmts.len() < 3 {
- return None;
- }
-
- let mut tmp_assigns = Vec::new();
- let mut nop_stmts = Vec::new();
- let mut storage_stmts = Vec::new();
- let mut storage_live_stmts = Vec::new();
- let mut storage_dead_stmts = Vec::new();
-
- type StmtIter<'a, 'tcx> = Peekable<Enumerate<Iter<'a, Statement<'tcx>>>>;
-
- fn is_storage_stmt(stmt: &Statement<'_>) -> bool {
- matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_))
- }
-
- /// Eats consecutive Statements which match `test`, performing the specified `action` for each.
- /// The iterator `stmt_iter` is not advanced if none were matched.
- fn try_eat<'a, 'tcx>(
- stmt_iter: &mut StmtIter<'a, 'tcx>,
- test: impl Fn(&'a Statement<'tcx>) -> bool,
- mut action: impl FnMut(usize, &'a Statement<'tcx>),
- ) {
- while stmt_iter.peek().map_or(false, |(_, stmt)| test(stmt)) {
- let (idx, stmt) = stmt_iter.next().unwrap();
-
- action(idx, stmt);
- }
- }
-
- /// Eats consecutive `StorageLive` and `StorageDead` Statements.
- /// The iterator `stmt_iter` is not advanced if none were found.
- fn try_eat_storage_stmts(
- stmt_iter: &mut StmtIter<'_, '_>,
- storage_live_stmts: &mut Vec<(usize, Local)>,
- storage_dead_stmts: &mut Vec<(usize, Local)>,
- ) {
- try_eat(stmt_iter, is_storage_stmt, |idx, stmt| {
- if let StatementKind::StorageLive(l) = stmt.kind {
- storage_live_stmts.push((idx, l));
- } else if let StatementKind::StorageDead(l) = stmt.kind {
- storage_dead_stmts.push((idx, l));
- }
- })
- }
-
- fn is_tmp_storage_stmt(stmt: &Statement<'_>) -> bool {
- use rustc_middle::mir::StatementKind::Assign;
- if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind {
- place.as_local().is_some() && p.as_local().is_some()
- } else {
- false
- }
- }
-
- /// Eats consecutive `Assign` Statements.
- // The iterator `stmt_iter` is not advanced if none were found.
- fn try_eat_assign_tmp_stmts(
- stmt_iter: &mut StmtIter<'_, '_>,
- tmp_assigns: &mut Vec<(Local, Local)>,
- nop_stmts: &mut Vec<usize>,
- ) {
- try_eat(stmt_iter, is_tmp_storage_stmt, |idx, stmt| {
- use rustc_middle::mir::StatementKind::Assign;
- if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) =
- &stmt.kind
- {
- tmp_assigns.push((place.as_local().unwrap(), p.as_local().unwrap()));
- nop_stmts.push(idx);
- }
- })
- }
-
- fn find_storage_live_dead_stmts_for_local(
- local: Local,
- stmts: &[Statement<'_>],
- ) -> Option<(usize, usize)> {
- trace!("looking for {:?}", local);
- let mut storage_live_stmt = None;
- let mut storage_dead_stmt = None;
- for (idx, stmt) in stmts.iter().enumerate() {
- if stmt.kind == StatementKind::StorageLive(local) {
- storage_live_stmt = Some(idx);
- } else if stmt.kind == StatementKind::StorageDead(local) {
- storage_dead_stmt = Some(idx);
- }
- }
-
- Some((storage_live_stmt?, storage_dead_stmt.unwrap_or(usize::MAX)))
- }
-
- // Try to match the expected MIR structure with the basic block we're processing.
- // We want to see something that looks like:
- // ```
- // (StorageLive(_) | StorageDead(_));*
- // _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
- // (StorageLive(_) | StorageDead(_));*
- // (tmp_n+1 = tmp_n);*
- // (StorageLive(_) | StorageDead(_));*
- // (tmp_n+1 = tmp_n);*
- // ((LOCAL_FROM as Variant).FIELD: TY) = move tmp;
- // discriminant(LOCAL_FROM) = VariantIdx;
- // (StorageLive(_) | StorageDead(_));*
- // ```
- let mut stmt_iter = stmts.iter().enumerate().peekable();
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- let (get_variant_field_stmt, stmt) = stmt_iter.next()?;
- let (local_tmp_s0, local_1, vf_s0, dbg_projection) = match_get_variant_field(stmt)?;
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts);
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- try_eat_assign_tmp_stmts(&mut stmt_iter, &mut tmp_assigns, &mut nop_stmts);
-
- let (idx, stmt) = stmt_iter.next()?;
- let (local_tmp_s1, local_0, vf_s1) = match_set_variant_field(stmt)?;
- nop_stmts.push(idx);
-
- let (idx, stmt) = stmt_iter.next()?;
- let (set_discr_local, set_discr_var_idx) = match_set_discr(stmt)?;
- let discr_stmt_source_info = stmt.source_info;
- nop_stmts.push(idx);
-
- try_eat_storage_stmts(&mut stmt_iter, &mut storage_live_stmts, &mut storage_dead_stmts);
-
- for (live_idx, live_local) in storage_live_stmts {
- if let Some(i) = storage_dead_stmts.iter().rposition(|(_, l)| *l == live_local) {
- let (dead_idx, _) = storage_dead_stmts.swap_remove(i);
- storage_stmts.push((live_idx, dead_idx, live_local));
-
- if live_local == local_tmp_s0 {
- nop_stmts.push(get_variant_field_stmt);
- }
- }
- }
- // We sort primitive usize here so we can use unstable sort
- nop_stmts.sort_unstable();
-
- // Use one of the statements we're going to discard between the point
- // where the storage location for the variant field becomes live and
- // is killed.
- let (live_idx, dead_idx) = find_storage_live_dead_stmts_for_local(local_tmp_s0, stmts)?;
- let stmt_to_overwrite =
- nop_stmts.iter().find(|stmt_idx| live_idx < **stmt_idx && **stmt_idx < dead_idx);
-
- let mut tmp_assigned_vars = BitSet::new_empty(locals_count);
- for (l, r) in &tmp_assigns {
- tmp_assigned_vars.insert(*l);
- tmp_assigned_vars.insert(*r);
- }
-
- let dbg_info_to_adjust: Vec<_> = debug_info
- .iter()
- .enumerate()
- .filter_map(|(i, var_info)| {
- if let VarDebugInfoContents::Place(p) = var_info.value {
- if tmp_assigned_vars.contains(p.local) {
- return Some(i);
- }
- }
-
- None
- })
- .collect();
-
- Some(ArmIdentityInfo {
- local_temp_0: local_tmp_s0,
- local_1,
- vf_s0,
- get_variant_field_stmt,
- field_tmp_assignments: tmp_assigns,
- local_tmp_s1,
- local_0,
- vf_s1,
- set_discr_local,
- set_discr_var_idx,
- stmt_to_overwrite: *stmt_to_overwrite?,
- source_info: discr_stmt_source_info,
- storage_stmts,
- stmts_to_remove: nop_stmts,
- dbg_info_to_adjust,
- dbg_projection,
- })
-}
-
-fn optimization_applies<'tcx>(
- opt_info: &ArmIdentityInfo<'tcx>,
- local_decls: &IndexVec<Local, LocalDecl<'tcx>>,
- local_uses: &IndexVec<Local, usize>,
- var_debug_info: &[VarDebugInfo<'tcx>],
-) -> bool {
- trace!("testing if optimization applies...");
-
- // FIXME(wesleywiser): possibly relax this restriction?
- if opt_info.local_0 == opt_info.local_1 {
- trace!("NO: moving into ourselves");
- return false;
- } else if opt_info.vf_s0 != opt_info.vf_s1 {
- trace!("NO: the field-and-variant information do not match");
- return false;
- } else if local_decls[opt_info.local_0].ty != local_decls[opt_info.local_1].ty {
- // FIXME(Centril,oli-obk): possibly relax to same layout?
- trace!("NO: source and target locals have different types");
- return false;
- } else if (opt_info.local_0, opt_info.vf_s0.var_idx)
- != (opt_info.set_discr_local, opt_info.set_discr_var_idx)
- {
- trace!("NO: the discriminants do not match");
- return false;
- }
-
- // Verify the assignment chain consists of the form b = a; c = b; d = c; etc...
- if opt_info.field_tmp_assignments.is_empty() {
- trace!("NO: no assignments found");
- return false;
- }
- let mut last_assigned_to = opt_info.field_tmp_assignments[0].1;
- let source_local = last_assigned_to;
- for (l, r) in &opt_info.field_tmp_assignments {
- if *r != last_assigned_to {
- trace!("NO: found unexpected assignment {:?} = {:?}", l, r);
- return false;
- }
-
- last_assigned_to = *l;
- }
-
- // Check that the first and last used locals are only used twice
- // since they are of the form:
- //
- // ```
- // _first = ((_x as Variant).n: ty);
- // _n = _first;
- // ...
- // ((_y as Variant).n: ty) = _n;
- // discriminant(_y) = z;
- // ```
- for (l, r) in &opt_info.field_tmp_assignments {
- if local_uses[*l] != 2 {
- warn!("NO: FAILED assignment chain local {:?} was used more than twice", l);
- return false;
- } else if local_uses[*r] != 2 {
- warn!("NO: FAILED assignment chain local {:?} was used more than twice", r);
- return false;
- }
- }
-
- // Check that debug info only points to full Locals and not projections.
- for dbg_idx in &opt_info.dbg_info_to_adjust {
- let dbg_info = &var_debug_info[*dbg_idx];
- if let VarDebugInfoContents::Place(p) = dbg_info.value {
- if !p.projection.is_empty() {
- trace!("NO: debug info for {:?} had a projection {:?}", dbg_info.name, p);
- return false;
- }
- }
- }
-
- if source_local != opt_info.local_temp_0 {
- trace!(
- "NO: start of assignment chain does not match enum variant temp: {:?} != {:?}",
- source_local,
- opt_info.local_temp_0
- );
- return false;
- } else if last_assigned_to != opt_info.local_tmp_s1 {
- trace!(
- "NO: end of assignment chain does not match written enum temp: {:?} != {:?}",
- last_assigned_to,
- opt_info.local_tmp_s1
- );
- return false;
- }
-
- trace!("SUCCESS: optimization applies!");
- true
-}
-
-impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- // FIXME(77359): This optimization can result in unsoundness.
- if !tcx.sess.opts.unstable_opts.unsound_mir_opts {
- return;
- }
-
- let source = body.source;
- trace!("running SimplifyArmIdentity on {:?}", source);
-
- let local_uses = LocalUseCounter::get_local_uses(body);
- for bb in body.basic_blocks.as_mut() {
- if let Some(opt_info) =
- get_arm_identity_info(&bb.statements, body.local_decls.len(), &body.var_debug_info)
- {
- trace!("got opt_info = {:#?}", opt_info);
- if !optimization_applies(
- &opt_info,
- &body.local_decls,
- &local_uses,
- &body.var_debug_info,
- ) {
- debug!("optimization skipped for {:?}", source);
- continue;
- }
-
- // Also remove unused Storage{Live,Dead} statements which correspond
- // to temps used previously.
- for (live_idx, dead_idx, local) in &opt_info.storage_stmts {
- // The temporary that we've read the variant field into is scoped to this block,
- // so we can remove the assignment.
- if *local == opt_info.local_temp_0 {
- bb.statements[opt_info.get_variant_field_stmt].make_nop();
- }
-
- for (left, right) in &opt_info.field_tmp_assignments {
- if local == left || local == right {
- bb.statements[*live_idx].make_nop();
- bb.statements[*dead_idx].make_nop();
- }
- }
- }
-
- // Right shape; transform
- for stmt_idx in opt_info.stmts_to_remove {
- bb.statements[stmt_idx].make_nop();
- }
-
- let stmt = &mut bb.statements[opt_info.stmt_to_overwrite];
- stmt.source_info = opt_info.source_info;
- stmt.kind = StatementKind::Assign(Box::new((
- opt_info.local_0.into(),
- Rvalue::Use(Operand::Move(opt_info.local_1.into())),
- )));
-
- bb.statements.retain(|stmt| stmt.kind != StatementKind::Nop);
-
- // Fix the debug info to point to the right local
- for dbg_index in opt_info.dbg_info_to_adjust {
- let dbg_info = &mut body.var_debug_info[dbg_index];
- assert!(
- matches!(dbg_info.value, VarDebugInfoContents::Place(_)),
- "value was not a Place"
- );
- if let VarDebugInfoContents::Place(p) = &mut dbg_info.value {
- assert!(p.projection.is_empty());
- p.local = opt_info.local_0;
- p.projection = opt_info.dbg_projection;
- }
- }
-
- trace!("block is now {:?}", bb.statements);
- }
- }
- }
-}
-
-struct LocalUseCounter {
- local_uses: IndexVec<Local, usize>,
-}
-
-impl LocalUseCounter {
- fn get_local_uses(body: &Body<'_>) -> IndexVec<Local, usize> {
- let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) };
- counter.visit_body(body);
- counter.local_uses
- }
-}
-
-impl Visitor<'_> for LocalUseCounter {
- fn visit_local(&mut self, local: Local, context: PlaceContext, _location: Location) {
- if context.is_storage_marker()
- || context == PlaceContext::NonUse(NonUseContext::VarDebugInfo)
- {
- return;
- }
-
- self.local_uses[local] += 1;
- }
-}
-
-/// Match on:
-/// ```ignore (MIR)
-/// _LOCAL_INTO = ((_LOCAL_FROM as Variant).FIELD: TY);
-/// ```
-fn match_get_variant_field<'tcx>(
- stmt: &Statement<'tcx>,
-) -> Option<(Local, Local, VarField<'tcx>, &'tcx List<PlaceElem<'tcx>>)> {
- match &stmt.kind {
- StatementKind::Assign(box (
- place_into,
- Rvalue::Use(Operand::Copy(pf) | Operand::Move(pf)),
- )) => {
- let local_into = place_into.as_local()?;
- let (local_from, vf) = match_variant_field_place(*pf)?;
- Some((local_into, local_from, vf, pf.projection))
- }
- _ => None,
- }
-}
-
-/// Match on:
-/// ```ignore (MIR)
-/// ((_LOCAL_FROM as Variant).FIELD: TY) = move _LOCAL_INTO;
-/// ```
-fn match_set_variant_field<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, Local, VarField<'tcx>)> {
- match &stmt.kind {
- StatementKind::Assign(box (place_from, Rvalue::Use(Operand::Move(place_into)))) => {
- let local_into = place_into.as_local()?;
- let (local_from, vf) = match_variant_field_place(*place_from)?;
- Some((local_into, local_from, vf))
- }
- _ => None,
- }
-}
-
-/// Match on:
-/// ```ignore (MIR)
-/// discriminant(_LOCAL_TO_SET) = VAR_IDX;
-/// ```
-fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> {
- match &stmt.kind {
- StatementKind::SetDiscriminant { place, variant_index } => {
- Some((place.as_local()?, *variant_index))
- }
- _ => None,
- }
-}
-
-#[derive(PartialEq, Debug)]
-struct VarField<'tcx> {
- field: Field,
- field_ty: Ty<'tcx>,
- var_idx: VariantIdx,
-}
-
-/// Match on `((_LOCAL as Variant).FIELD: TY)`.
-fn match_variant_field_place(place: Place<'_>) -> Option<(Local, VarField<'_>)> {
- match place.as_ref() {
- PlaceRef {
- local,
- projection: &[ProjectionElem::Downcast(_, var_idx), ProjectionElem::Field(field, ty)],
- } => Some((local, VarField { field, field_ty: ty, var_idx })),
- _ => None,
- }
-}
-
-/// Simplifies `SwitchInt(_) -> [targets]`,
-/// where all the `targets` have the same form,
-/// into `goto -> target_first`.
-pub struct SimplifyBranchSame;
-
-impl<'tcx> MirPass<'tcx> for SimplifyBranchSame {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- // This optimization is disabled by default for now due to
- // soundness concerns; see issue #89485 and PR #89489.
- if !tcx.sess.opts.unstable_opts.unsound_mir_opts {
- return;
- }
-
- trace!("Running SimplifyBranchSame on {:?}", body.source);
- let finder = SimplifyBranchSameOptimizationFinder { body, tcx };
- let opts = finder.find();
-
- let did_remove_blocks = opts.len() > 0;
- for opt in opts.iter() {
- trace!("SUCCESS: Applying optimization {:?}", opt);
- // Replace `SwitchInt(..) -> [bb_first, ..];` with a `goto -> bb_first;`.
- body.basic_blocks_mut()[opt.bb_to_opt_terminator].terminator_mut().kind =
- TerminatorKind::Goto { target: opt.bb_to_goto };
- }
-
- if did_remove_blocks {
- // We have dead blocks now, so remove those.
- simplify::remove_dead_blocks(tcx, body);
- }
- }
-}
-
-#[derive(Debug)]
-struct SimplifyBranchSameOptimization {
- /// All basic blocks are equal so go to this one
- bb_to_goto: BasicBlock,
- /// Basic block where the terminator can be simplified to a goto
- bb_to_opt_terminator: BasicBlock,
-}
-
-struct SwitchTargetAndValue {
- target: BasicBlock,
- // None in case of the `otherwise` case
- value: Option<u128>,
-}
-
-struct SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
- body: &'a Body<'tcx>,
- tcx: TyCtxt<'tcx>,
-}
-
-impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> {
- fn find(&self) -> Vec<SimplifyBranchSameOptimization> {
- self.body
- .basic_blocks
- .iter_enumerated()
- .filter_map(|(bb_idx, bb)| {
- let (discr_switched_on, targets_and_values) = match &bb.terminator().kind {
- TerminatorKind::SwitchInt { targets, discr, .. } => {
- let targets_and_values: Vec<_> = targets.iter()
- .map(|(val, target)| SwitchTargetAndValue { target, value: Some(val) })
- .chain(once(SwitchTargetAndValue { target: targets.otherwise(), value: None }))
- .collect();
- (discr, targets_and_values)
- },
- _ => return None,
- };
-
- // find the adt that has its discriminant read
- // assuming this must be the last statement of the block
- let adt_matched_on = match &bb.statements.last()?.kind {
- StatementKind::Assign(box (place, rhs))
- if Some(*place) == discr_switched_on.place() =>
- {
- match rhs {
- Rvalue::Discriminant(adt_place) if adt_place.ty(self.body, self.tcx).ty.is_enum() => adt_place,
- _ => {
- trace!("NO: expected a discriminant read of an enum instead of: {:?}", rhs);
- return None;
- }
- }
- }
- other => {
- trace!("NO: expected an assignment of a discriminant read to a place. Found: {:?}", other);
- return None
- },
- };
-
- let mut iter_bbs_reachable = targets_and_values
- .iter()
- .map(|target_and_value| (target_and_value, &self.body.basic_blocks[target_and_value.target]))
- .filter(|(_, bb)| {
- // Reaching `unreachable` is UB so assume it doesn't happen.
- bb.terminator().kind != TerminatorKind::Unreachable
- })
- .peekable();
-
- let bb_first = iter_bbs_reachable.peek().map_or(&targets_and_values[0], |(idx, _)| *idx);
- let mut all_successors_equivalent = StatementEquality::TrivialEqual;
-
- // All successor basic blocks must be equal or contain statements that are pairwise considered equal.
- for ((target_and_value_l,bb_l), (target_and_value_r,bb_r)) in iter_bbs_reachable.tuple_windows() {
- let trivial_checks = bb_l.is_cleanup == bb_r.is_cleanup
- && bb_l.terminator().kind == bb_r.terminator().kind
- && bb_l.statements.len() == bb_r.statements.len();
- let statement_check = || {
- bb_l.statements.iter().zip(&bb_r.statements).try_fold(StatementEquality::TrivialEqual, |acc,(l,r)| {
- let stmt_equality = self.statement_equality(*adt_matched_on, &l, target_and_value_l, &r, target_and_value_r);
- if matches!(stmt_equality, StatementEquality::NotEqual) {
- // short circuit
- None
- } else {
- Some(acc.combine(&stmt_equality))
- }
- })
- .unwrap_or(StatementEquality::NotEqual)
- };
- if !trivial_checks {
- all_successors_equivalent = StatementEquality::NotEqual;
- break;
- }
- all_successors_equivalent = all_successors_equivalent.combine(&statement_check());
- };
-
- match all_successors_equivalent{
- StatementEquality::TrivialEqual => {
- // statements are trivially equal, so just take first
- trace!("Statements are trivially equal");
- Some(SimplifyBranchSameOptimization {
- bb_to_goto: bb_first.target,
- bb_to_opt_terminator: bb_idx,
- })
- }
- StatementEquality::ConsideredEqual(bb_to_choose) => {
- trace!("Statements are considered equal");
- Some(SimplifyBranchSameOptimization {
- bb_to_goto: bb_to_choose,
- bb_to_opt_terminator: bb_idx,
- })
- }
- StatementEquality::NotEqual => {
- trace!("NO: not all successors of basic block {:?} were equivalent", bb_idx);
- None
- }
- }
- })
- .collect()
- }
-
- /// Tests if two statements can be considered equal
- ///
- /// Statements can be trivially equal if the kinds match.
- /// But they can also be considered equal in the following case A:
- /// ```ignore (MIR)
- /// discriminant(_0) = 0; // bb1
- /// _0 = move _1; // bb2
- /// ```
- /// In this case the two statements are equal iff
- /// - `_0` is an enum where the variant index 0 is fieldless, and
- /// - bb1 was targeted by a switch where the discriminant of `_1` was switched on
- fn statement_equality(
- &self,
- adt_matched_on: Place<'tcx>,
- x: &Statement<'tcx>,
- x_target_and_value: &SwitchTargetAndValue,
- y: &Statement<'tcx>,
- y_target_and_value: &SwitchTargetAndValue,
- ) -> StatementEquality {
- let helper = |rhs: &Rvalue<'tcx>,
- place: &Place<'tcx>,
- variant_index: VariantIdx,
- switch_value: u128,
- side_to_choose| {
- let place_type = place.ty(self.body, self.tcx).ty;
- let adt = match *place_type.kind() {
- ty::Adt(adt, _) if adt.is_enum() => adt,
- _ => return StatementEquality::NotEqual,
- };
- // We need to make sure that the switch value that targets the bb with
- // SetDiscriminant is the same as the variant discriminant.
- let variant_discr = adt.discriminant_for_variant(self.tcx, variant_index).val;
- if variant_discr != switch_value {
- trace!(
- "NO: variant discriminant {} does not equal switch value {}",
- variant_discr,
- switch_value
- );
- return StatementEquality::NotEqual;
- }
- let variant_is_fieldless = adt.variant(variant_index).fields.is_empty();
- if !variant_is_fieldless {
- trace!("NO: variant {:?} was not fieldless", variant_index);
- return StatementEquality::NotEqual;
- }
-
- match rhs {
- Rvalue::Use(operand) if operand.place() == Some(adt_matched_on) => {
- StatementEquality::ConsideredEqual(side_to_choose)
- }
- _ => {
- trace!(
- "NO: RHS of assignment was {:?}, but expected it to match the adt being matched on in the switch, which is {:?}",
- rhs,
- adt_matched_on
- );
- StatementEquality::NotEqual
- }
- }
- };
- match (&x.kind, &y.kind) {
- // trivial case
- (x, y) if x == y => StatementEquality::TrivialEqual,
-
- // check for case A
- (
- StatementKind::Assign(box (_, rhs)),
- &StatementKind::SetDiscriminant { ref place, variant_index },
- ) if y_target_and_value.value.is_some() => {
- // choose basic block of x, as that has the assign
- helper(
- rhs,
- place,
- variant_index,
- y_target_and_value.value.unwrap(),
- x_target_and_value.target,
- )
- }
- (
- &StatementKind::SetDiscriminant { ref place, variant_index },
- &StatementKind::Assign(box (_, ref rhs)),
- ) if x_target_and_value.value.is_some() => {
- // choose basic block of y, as that has the assign
- helper(
- rhs,
- place,
- variant_index,
- x_target_and_value.value.unwrap(),
- y_target_and_value.target,
- )
- }
- _ => {
- trace!("NO: statements `{:?}` and `{:?}` not considered equal", x, y);
- StatementEquality::NotEqual
- }
- }
- }
-}
-
-#[derive(Copy, Clone, Eq, PartialEq)]
-enum StatementEquality {
- /// The two statements are trivially equal; same kind
- TrivialEqual,
- /// The two statements are considered equal, but may be of different kinds. The BasicBlock field is the basic block to jump to when performing the branch-same optimization.
- /// For example, `_0 = _1` and `discriminant(_0) = discriminant(0)` are considered equal if 0 is a fieldless variant of an enum. But we don't want to jump to the basic block with the SetDiscriminant, as that is not legal if _1 is not the 0 variant index
- ConsideredEqual(BasicBlock),
- /// The two statements are not equal
- NotEqual,
-}
-
-impl StatementEquality {
- fn combine(&self, other: &StatementEquality) -> StatementEquality {
- use StatementEquality::*;
- match (self, other) {
- (TrivialEqual, TrivialEqual) => TrivialEqual,
- (TrivialEqual, ConsideredEqual(b)) | (ConsideredEqual(b), TrivialEqual) => {
- ConsideredEqual(*b)
- }
- (ConsideredEqual(b1), ConsideredEqual(b2)) => {
- if b1 == b2 {
- ConsideredEqual(*b1)
- } else {
- NotEqual
- }
- }
- (_, NotEqual) | (NotEqual, _) => NotEqual,
- }
- }
-}
replacements: ReplacementMap<'tcx>,
/// This is used to check that we are not leaving references to replaced locals behind.
all_dead_locals: BitSet<Local>,
- /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
+ /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
/// and deinit statement and debuginfo.
fragments: IndexVec<Local, Vec<(&'tcx [PlaceElem<'tcx>], Local)>>,
}
);
if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) {
+ let trait_ref = trait_ref.subst_identity();
+
let param_env = ty::ParamEnv::reveal_all();
let trait_ref = tcx.normalize_erasing_regions(param_env, trait_ref);
let overridden_methods = tcx.impl_item_implementor_ids(item.owner_id);
#![feature(array_windows)]
-#![feature(control_flow_enum)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
#![deny(rustc::untranslatable_diagnostic)]
#[instrument(level = "debug", skip(self))]
fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if !c.has_non_region_param() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match c.kind() {
ty::ConstKind::Param(param) => {
debug!(?param);
self.unused_parameters.mark_used(param.index);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, substs })
if matches!(self.tcx.def_kind(def.did), DefKind::AnonConst) =>
{
self.visit_child_body(def.did, substs);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => c.super_visit_with(self),
}
#[instrument(level = "debug", skip(self))]
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.has_non_region_param() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
match *ty.kind() {
debug!(?def_id);
// Avoid cycle errors with generators.
if def_id == self.def_id {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
// Consider any generic parameters used by any closures/generators as used in the
// parent.
self.visit_child_body(def_id, substs);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
ty::Param(param) => {
debug!(?param);
self.unused_parameters.mark_used(param.index);
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
_ => ty.super_visit_with(self),
}
#[primary_span]
pub if_span: Span,
#[subdiagnostic]
- pub sub: IfExpressionMissingThenBlockSub,
+ pub missing_then_block_sub: IfExpressionMissingThenBlockSub,
+ #[subdiagnostic]
+ pub let_else_sub: Option<IfExpressionLetSomeSub>,
}
#[derive(Subdiagnostic)]
AddThenBlock(#[primary_span] Span),
}
+#[derive(Subdiagnostic)]
+#[help(parse_extra_if_in_let_else)]
+pub(crate) struct IfExpressionLetSomeSub {
+ #[primary_span]
+ pub if_span: Span,
+}
+
#[derive(Diagnostic)]
#[diag(parse_if_expression_missing_condition)]
pub(crate) struct IfExpressionMissingCondition {
}
let cursor = Cursor::new(src);
- let string_reader =
- StringReader { sess, start_pos, pos: start_pos, src, cursor, override_span };
+ let string_reader = StringReader {
+ sess,
+ start_pos,
+ pos: start_pos,
+ src,
+ cursor,
+ override_span,
+ nbsp_is_whitespace: false,
+ };
tokentrees::TokenTreesReader::parse_all_token_trees(string_reader)
}
/// Cursor for getting lexer tokens.
cursor: Cursor<'a>,
override_span: Option<Span>,
+ /// When a "unknown start of token: \u{a0}" has already been emitted earlier
+ /// in this file, it's safe to treat further occurrences of the non-breaking
+ /// space character as whitespace.
+ nbsp_is_whitespace: bool,
}
impl<'a> StringReader<'a> {
/// preceded by whitespace.
fn next_token(&mut self) -> (Token, bool) {
let mut preceded_by_whitespace = false;
-
+ let mut swallow_next_invalid = 0;
// Skip trivial (whitespace & comments) tokens
loop {
let token = self.cursor.advance_token();
rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
- let c = self.str_from(start).chars().next().unwrap();
+ // Don't emit diagnostics for sequences of the same invalid token
+ if swallow_next_invalid > 0 {
+ swallow_next_invalid -= 1;
+ continue;
+ }
+ let mut it = self.str_from_to_end(start).chars();
+ let c = it.next().unwrap();
+ if c == '\u{00a0}' {
+ // If an error has already been reported on non-breaking
+ // space characters earlier in the file, treat all
+ // subsequent occurrences as whitespace.
+ if self.nbsp_is_whitespace {
+ preceded_by_whitespace = true;
+ continue;
+ }
+ self.nbsp_is_whitespace = true;
+ }
+ let repeats = it.take_while(|c1| *c1 == c).count();
let mut err =
- self.struct_err_span_char(start, self.pos, "unknown start of token", c);
+ self.struct_err_span_char(start, self.pos + Pos::from_usize(repeats * c.len_utf8()), "unknown start of token", c);
// FIXME: the lexer could be used to turn the ASCII version of unicode
// homoglyphs, instead of keeping a table in `check_for_substitution`into the
// token. Ideally, this should be inside `rustc_lexer`. However, we should
// first remove compound tokens like `<<` from `rustc_lexer`, and then add
// fancier error recovery to it, as there will be less overall work to do this
// way.
- let token = unicode_chars::check_for_substitution(self, start, c, &mut err);
+ let token = unicode_chars::check_for_substitution(self, start, c, &mut err, repeats+1);
if c == '\x00' {
err.help("source files must contain UTF-8 encoded text, unexpected null bytes might occur when a different encoding is used");
}
+ if repeats > 0 {
+ if repeats == 1 {
+ err.note(format!("character appears once more"));
+ } else {
+ err.note(format!("character appears {repeats} more times"));
+ }
+ swallow_next_invalid = repeats;
+ }
err.emit();
if let Some(token) = token {
token
/// Slice of the source text from `start` up to but excluding `self.pos`,
/// meaning the slice does not include the character `self.ch`.
- fn str_from(&self, start: BytePos) -> &str {
+ fn str_from(&self, start: BytePos) -> &'a str {
self.str_from_to(start, self.pos)
}
}
/// Slice of the source text spanning from `start` up to but excluding `end`.
- fn str_from_to(&self, start: BytePos, end: BytePos) -> &str {
+ fn str_from_to(&self, start: BytePos, end: BytePos) -> &'a str {
&self.src[self.src_index(start)..self.src_index(end)]
}
+ /// Slice of the source text spanning from `start` until the end
+ fn str_from_to_end(&self, start: BytePos) -> &'a str {
+ &self.src[self.src_index(start)..]
+ }
+
fn report_raw_str_error(&self, start: BytePos, prefix_len: u32) -> ! {
match rustc_lexer::validate_raw_str(self.str_from(start), prefix_len) {
Err(RawStrError::InvalidStarter { bad_char }) => {
use rustc_span::{symbol::kw, BytePos, Pos, Span};
#[rustfmt::skip] // for line breaks
-pub(crate) const UNICODE_ARRAY: &[(char, &str, char)] = &[
- ('
', "Line Separator", ' '),
- ('
', "Paragraph Separator", ' '),
- (' ', "Ogham Space mark", ' '),
- (' ', "En Quad", ' '),
- (' ', "Em Quad", ' '),
- (' ', "En Space", ' '),
- (' ', "Em Space", ' '),
- (' ', "Three-Per-Em Space", ' '),
- (' ', "Four-Per-Em Space", ' '),
- (' ', "Six-Per-Em Space", ' '),
- (' ', "Punctuation Space", ' '),
- (' ', "Thin Space", ' '),
- (' ', "Hair Space", ' '),
- (' ', "Medium Mathematical Space", ' '),
- (' ', "No-Break Space", ' '),
- (' ', "Figure Space", ' '),
- (' ', "Narrow No-Break Space", ' '),
- (' ', "Ideographic Space", ' '),
-
- ('ߺ', "Nko Lajanyalan", '_'),
- ('﹍', "Dashed Low Line", '_'),
- ('﹎', "Centreline Low Line", '_'),
- ('﹏', "Wavy Low Line", '_'),
- ('_', "Fullwidth Low Line", '_'),
-
- ('‐', "Hyphen", '-'),
- ('‑', "Non-Breaking Hyphen", '-'),
- ('‒', "Figure Dash", '-'),
- ('–', "En Dash", '-'),
- ('—', "Em Dash", '-'),
- ('﹘', "Small Em Dash", '-'),
- ('۔', "Arabic Full Stop", '-'),
- ('⁃', "Hyphen Bullet", '-'),
- ('˗', "Modifier Letter Minus Sign", '-'),
- ('−', "Minus Sign", '-'),
- ('➖', "Heavy Minus Sign", '-'),
- ('Ⲻ', "Coptic Letter Dialect-P Ni", '-'),
- ('ー', "Katakana-Hiragana Prolonged Sound Mark", '-'),
- ('-', "Fullwidth Hyphen-Minus", '-'),
- ('―', "Horizontal Bar", '-'),
- ('─', "Box Drawings Light Horizontal", '-'),
- ('━', "Box Drawings Heavy Horizontal", '-'),
- ('㇐', "CJK Stroke H", '-'),
- ('ꟷ', "Latin Epigraphic Letter Sideways I", '-'),
- ('ᅳ', "Hangul Jungseong Eu", '-'),
- ('ㅡ', "Hangul Letter Eu", '-'),
- ('一', "CJK Unified Ideograph-4E00", '-'),
- ('⼀', "Kangxi Radical One", '-'),
-
- ('؍', "Arabic Date Separator", ','),
- ('٫', "Arabic Decimal Separator", ','),
- ('‚', "Single Low-9 Quotation Mark", ','),
- ('¸', "Cedilla", ','),
- ('ꓹ', "Lisu Letter Tone Na Po", ','),
- (',', "Fullwidth Comma", ','),
-
- (';', "Greek Question Mark", ';'),
- (';', "Fullwidth Semicolon", ';'),
- ('︔', "Presentation Form For Vertical Semicolon", ';'),
-
- ('ः', "Devanagari Sign Visarga", ':'),
- ('ઃ', "Gujarati Sign Visarga", ':'),
- (':', "Fullwidth Colon", ':'),
- ('։', "Armenian Full Stop", ':'),
- ('܃', "Syriac Supralinear Colon", ':'),
- ('܄', "Syriac Sublinear Colon", ':'),
- ('᛬', "Runic Multiple Punctuation", ':'),
- ('︰', "Presentation Form For Vertical Two Dot Leader", ':'),
- ('᠃', "Mongolian Full Stop", ':'),
- ('᠉', "Mongolian Manchu Full Stop", ':'),
- ('⁚', "Two Dot Punctuation", ':'),
- ('׃', "Hebrew Punctuation Sof Pasuq", ':'),
- ('˸', "Modifier Letter Raised Colon", ':'),
- ('꞉', "Modifier Letter Colon", ':'),
- ('∶', "Ratio", ':'),
- ('ː', "Modifier Letter Triangular Colon", ':'),
- ('ꓽ', "Lisu Letter Tone Mya Jeu", ':'),
- ('︓', "Presentation Form For Vertical Colon", ':'),
-
- ('!', "Fullwidth Exclamation Mark", '!'),
- ('ǃ', "Latin Letter Retroflex Click", '!'),
- ('ⵑ', "Tifinagh Letter Tuareg Yang", '!'),
- ('︕', "Presentation Form For Vertical Exclamation Mark", '!'),
-
- ('ʔ', "Latin Letter Glottal Stop", '?'),
- ('Ɂ', "Latin Capital Letter Glottal Stop", '?'),
- ('ॽ', "Devanagari Letter Glottal Stop", '?'),
- ('Ꭾ', "Cherokee Letter He", '?'),
- ('ꛫ', "Bamum Letter Ntuu", '?'),
- ('?', "Fullwidth Question Mark", '?'),
- ('︖', "Presentation Form For Vertical Question Mark", '?'),
-
- ('𝅭', "Musical Symbol Combining Augmentation Dot", '.'),
- ('․', "One Dot Leader", '.'),
- ('܁', "Syriac Supralinear Full Stop", '.'),
- ('܂', "Syriac Sublinear Full Stop", '.'),
- ('꘎', "Vai Full Stop", '.'),
- ('𐩐', "Kharoshthi Punctuation Dot", '.'),
- ('٠', "Arabic-Indic Digit Zero", '.'),
- ('۰', "Extended Arabic-Indic Digit Zero", '.'),
- ('ꓸ', "Lisu Letter Tone Mya Ti", '.'),
- ('·', "Middle Dot", '.'),
- ('・', "Katakana Middle Dot", '.'),
- ('・', "Halfwidth Katakana Middle Dot", '.'),
- ('᛫', "Runic Single Punctuation", '.'),
- ('·', "Greek Ano Teleia", '.'),
- ('⸱', "Word Separator Middle Dot", '.'),
- ('𐄁', "Aegean Word Separator Dot", '.'),
- ('•', "Bullet", '.'),
- ('‧', "Hyphenation Point", '.'),
- ('∙', "Bullet Operator", '.'),
- ('⋅', "Dot Operator", '.'),
- ('ꞏ', "Latin Letter Sinological Dot", '.'),
- ('ᐧ', "Canadian Syllabics Final Middle Dot", '.'),
- ('ᐧ', "Canadian Syllabics Final Middle Dot", '.'),
- ('.', "Fullwidth Full Stop", '.'),
- ('。', "Ideographic Full Stop", '.'),
- ('︒', "Presentation Form For Vertical Ideographic Full Stop", '.'),
-
- ('՝', "Armenian Comma", '\''),
- (''', "Fullwidth Apostrophe", '\''),
- ('‘', "Left Single Quotation Mark", '\''),
- ('’', "Right Single Quotation Mark", '\''),
- ('‛', "Single High-Reversed-9 Quotation Mark", '\''),
- ('′', "Prime", '\''),
- ('‵', "Reversed Prime", '\''),
- ('՚', "Armenian Apostrophe", '\''),
- ('׳', "Hebrew Punctuation Geresh", '\''),
- ('`', "Grave Accent", '\''),
- ('`', "Greek Varia", '\''),
- ('`', "Fullwidth Grave Accent", '\''),
- ('´', "Acute Accent", '\''),
- ('΄', "Greek Tonos", '\''),
- ('´', "Greek Oxia", '\''),
- ('᾽', "Greek Koronis", '\''),
- ('᾿', "Greek Psili", '\''),
- ('῾', "Greek Dasia", '\''),
- ('ʹ', "Modifier Letter Prime", '\''),
- ('ʹ', "Greek Numeral Sign", '\''),
- ('ˈ', "Modifier Letter Vertical Line", '\''),
- ('ˊ', "Modifier Letter Acute Accent", '\''),
- ('ˋ', "Modifier Letter Grave Accent", '\''),
- ('˴', "Modifier Letter Middle Grave Accent", '\''),
- ('ʻ', "Modifier Letter Turned Comma", '\''),
- ('ʽ', "Modifier Letter Reversed Comma", '\''),
- ('ʼ', "Modifier Letter Apostrophe", '\''),
- ('ʾ', "Modifier Letter Right Half Ring", '\''),
- ('ꞌ', "Latin Small Letter Saltillo", '\''),
- ('י', "Hebrew Letter Yod", '\''),
- ('ߴ', "Nko High Tone Apostrophe", '\''),
- ('ߵ', "Nko Low Tone Apostrophe", '\''),
- ('ᑊ', "Canadian Syllabics West-Cree P", '\''),
- ('ᛌ', "Runic Letter Short-Twig-Sol S", '\''),
- ('𖽑', "Miao Sign Aspiration", '\''),
- ('𖽒', "Miao Sign Reformed Voicing", '\''),
-
- ('᳓', "Vedic Sign Nihshvasa", '"'),
- ('"', "Fullwidth Quotation Mark", '"'),
- ('“', "Left Double Quotation Mark", '"'),
- ('”', "Right Double Quotation Mark", '"'),
- ('‟', "Double High-Reversed-9 Quotation Mark", '"'),
- ('″', "Double Prime", '"'),
- ('‶', "Reversed Double Prime", '"'),
- ('〃', "Ditto Mark", '"'),
- ('״', "Hebrew Punctuation Gershayim", '"'),
- ('˝', "Double Acute Accent", '"'),
- ('ʺ', "Modifier Letter Double Prime", '"'),
- ('˶', "Modifier Letter Middle Double Acute Accent", '"'),
- ('˵', "Modifier Letter Middle Double Grave Accent", '"'),
- ('ˮ', "Modifier Letter Double Apostrophe", '"'),
- ('ײ', "Hebrew Ligature Yiddish Double Yod", '"'),
- ('❞', "Heavy Double Comma Quotation Mark Ornament", '"'),
- ('❝', "Heavy Double Turned Comma Quotation Mark Ornament", '"'),
-
- ('(', "Fullwidth Left Parenthesis", '('),
- ('❨', "Medium Left Parenthesis Ornament", '('),
- ('﴾', "Ornate Left Parenthesis", '('),
-
- (')', "Fullwidth Right Parenthesis", ')'),
- ('❩', "Medium Right Parenthesis Ornament", ')'),
- ('﴿', "Ornate Right Parenthesis", ')'),
-
- ('[', "Fullwidth Left Square Bracket", '['),
- ('❲', "Light Left Tortoise Shell Bracket Ornament", '['),
- ('「', "Left Corner Bracket", '['),
- ('『', "Left White Corner Bracket", '['),
- ('【', "Left Black Lenticular Bracket", '['),
- ('〔', "Left Tortoise Shell Bracket", '['),
- ('〖', "Left White Lenticular Bracket", '['),
- ('〘', "Left White Tortoise Shell Bracket", '['),
- ('〚', "Left White Square Bracket", '['),
-
- (']', "Fullwidth Right Square Bracket", ']'),
- ('❳', "Light Right Tortoise Shell Bracket Ornament", ']'),
- ('」', "Right Corner Bracket", ']'),
- ('』', "Right White Corner Bracket", ']'),
- ('】', "Right Black Lenticular Bracket", ']'),
- ('〕', "Right Tortoise Shell Bracket", ']'),
- ('〗', "Right White Lenticular Bracket", ']'),
- ('〙', "Right White Tortoise Shell Bracket", ']'),
- ('〛', "Right White Square Bracket", ']'),
-
- ('❴', "Medium Left Curly Bracket Ornament", '{'),
- ('𝄔', "Musical Symbol Brace", '{'),
- ('{', "Fullwidth Left Curly Bracket", '{'),
-
- ('❵', "Medium Right Curly Bracket Ornament", '}'),
- ('}', "Fullwidth Right Curly Bracket", '}'),
-
- ('⁎', "Low Asterisk", '*'),
- ('٭', "Arabic Five Pointed Star", '*'),
- ('∗', "Asterisk Operator", '*'),
- ('𐌟', "Old Italic Letter Ess", '*'),
- ('*', "Fullwidth Asterisk", '*'),
-
- ('᜵', "Philippine Single Punctuation", '/'),
- ('⁁', "Caret Insertion Point", '/'),
- ('∕', "Division Slash", '/'),
- ('⁄', "Fraction Slash", '/'),
- ('╱', "Box Drawings Light Diagonal Upper Right To Lower Left", '/'),
- ('⟋', "Mathematical Rising Diagonal", '/'),
- ('⧸', "Big Solidus", '/'),
- ('𝈺', "Greek Instrumental Notation Symbol-47", '/'),
- ('㇓', "CJK Stroke Sp", '/'),
- ('〳', "Vertical Kana Repeat Mark Upper Half", '/'),
- ('Ⳇ', "Coptic Capital Letter Old Coptic Esh", '/'),
- ('ノ', "Katakana Letter No", '/'),
- ('丿', "CJK Unified Ideograph-4E3F", '/'),
- ('⼃', "Kangxi Radical Slash", '/'),
- ('/', "Fullwidth Solidus", '/'),
-
- ('\', "Fullwidth Reverse Solidus", '\\'),
- ('﹨', "Small Reverse Solidus", '\\'),
- ('∖', "Set Minus", '\\'),
- ('⟍', "Mathematical Falling Diagonal", '\\'),
- ('⧵', "Reverse Solidus Operator", '\\'),
- ('⧹', "Big Reverse Solidus", '\\'),
- ('⧹', "Greek Vocal Notation Symbol-16", '\\'),
- ('⧹', "Greek Instrumental Symbol-48", '\\'),
- ('㇔', "CJK Stroke D", '\\'),
- ('丶', "CJK Unified Ideograph-4E36", '\\'),
- ('⼂', "Kangxi Radical Dot", '\\'),
- ('、', "Ideographic Comma", '\\'),
- ('ヽ', "Katakana Iteration Mark", '\\'),
-
- ('ꝸ', "Latin Small Letter Um", '&'),
- ('&', "Fullwidth Ampersand", '&'),
-
- ('᛭', "Runic Cross Punctuation", '+'),
- ('➕', "Heavy Plus Sign", '+'),
- ('𐊛', "Lycian Letter H", '+'),
- ('﬩', "Hebrew Letter Alternative Plus Sign", '+'),
- ('+', "Fullwidth Plus Sign", '+'),
-
- ('‹', "Single Left-Pointing Angle Quotation Mark", '<'),
- ('❮', "Heavy Left-Pointing Angle Quotation Mark Ornament", '<'),
- ('˂', "Modifier Letter Left Arrowhead", '<'),
- ('𝈶', "Greek Instrumental Symbol-40", '<'),
- ('ᐸ', "Canadian Syllabics Pa", '<'),
- ('ᚲ', "Runic Letter Kauna", '<'),
- ('❬', "Medium Left-Pointing Angle Bracket Ornament", '<'),
- ('⟨', "Mathematical Left Angle Bracket", '<'),
- ('〈', "Left-Pointing Angle Bracket", '<'),
- ('〈', "Left Angle Bracket", '<'),
- ('㇛', "CJK Stroke Pd", '<'),
- ('く', "Hiragana Letter Ku", '<'),
- ('𡿨', "CJK Unified Ideograph-21FE8", '<'),
- ('《', "Left Double Angle Bracket", '<'),
- ('<', "Fullwidth Less-Than Sign", '<'),
-
- ('᐀', "Canadian Syllabics Hyphen", '='),
- ('⹀', "Double Hyphen", '='),
- ('゠', "Katakana-Hiragana Double Hyphen", '='),
- ('꓿', "Lisu Punctuation Full Stop", '='),
- ('=', "Fullwidth Equals Sign", '='),
-
- ('›', "Single Right-Pointing Angle Quotation Mark", '>'),
- ('❯', "Heavy Right-Pointing Angle Quotation Mark Ornament", '>'),
- ('˃', "Modifier Letter Right Arrowhead", '>'),
- ('𝈷', "Greek Instrumental Symbol-42", '>'),
- ('ᐳ', "Canadian Syllabics Po", '>'),
- ('𖼿', "Miao Letter Archaic Zza", '>'),
- ('❭', "Medium Right-Pointing Angle Bracket Ornament", '>'),
- ('⟩', "Mathematical Right Angle Bracket", '>'),
- ('〉', "Right-Pointing Angle Bracket", '>'),
- ('〉', "Right Angle Bracket", '>'),
- ('》', "Right Double Angle Bracket", '>'),
- ('>', "Fullwidth Greater-Than Sign", '>'),
+pub(crate) const UNICODE_ARRAY: &[(char, &str, &str)] = &[
+ ('
', "Line Separator", " "),
+ ('
', "Paragraph Separator", " "),
+ (' ', "Ogham Space mark", " "),
+ (' ', "En Quad", " "),
+ (' ', "Em Quad", " "),
+ (' ', "En Space", " "),
+ (' ', "Em Space", " "),
+ (' ', "Three-Per-Em Space", " "),
+ (' ', "Four-Per-Em Space", " "),
+ (' ', "Six-Per-Em Space", " "),
+ (' ', "Punctuation Space", " "),
+ (' ', "Thin Space", " "),
+ (' ', "Hair Space", " "),
+ (' ', "Medium Mathematical Space", " "),
+ (' ', "No-Break Space", " "),
+ (' ', "Figure Space", " "),
+ (' ', "Narrow No-Break Space", " "),
+ (' ', "Ideographic Space", " "),
+
+ ('ߺ', "Nko Lajanyalan", "_"),
+ ('﹍', "Dashed Low Line", "_"),
+ ('﹎', "Centreline Low Line", "_"),
+ ('﹏', "Wavy Low Line", "_"),
+ ('_', "Fullwidth Low Line", "_"),
+
+ ('‐', "Hyphen", "-"),
+ ('‑', "Non-Breaking Hyphen", "-"),
+ ('‒', "Figure Dash", "-"),
+ ('–', "En Dash", "-"),
+ ('—', "Em Dash", "-"),
+ ('﹘', "Small Em Dash", "-"),
+ ('۔', "Arabic Full Stop", "-"),
+ ('⁃', "Hyphen Bullet", "-"),
+ ('˗', "Modifier Letter Minus Sign", "-"),
+ ('−', "Minus Sign", "-"),
+ ('➖', "Heavy Minus Sign", "-"),
+ ('Ⲻ', "Coptic Letter Dialect-P Ni", "-"),
+ ('ー', "Katakana-Hiragana Prolonged Sound Mark", "-"),
+ ('-', "Fullwidth Hyphen-Minus", "-"),
+ ('―', "Horizontal Bar", "-"),
+ ('─', "Box Drawings Light Horizontal", "-"),
+ ('━', "Box Drawings Heavy Horizontal", "-"),
+ ('㇐', "CJK Stroke H", "-"),
+ ('ꟷ', "Latin Epigraphic Letter Sideways I", "-"),
+ ('ᅳ', "Hangul Jungseong Eu", "-"),
+ ('ㅡ', "Hangul Letter Eu", "-"),
+ ('一', "CJK Unified Ideograph-4E00", "-"),
+ ('⼀', "Kangxi Radical One", "-"),
+
+ ('؍', "Arabic Date Separator", ","),
+ ('٫', "Arabic Decimal Separator", ","),
+ ('‚', "Single Low-9 Quotation Mark", ","),
+ ('¸', "Cedilla", ","),
+ ('ꓹ', "Lisu Letter Tone Na Po", ","),
+ (',', "Fullwidth Comma", ","),
+
+ (';', "Greek Question Mark", ";"),
+ (';', "Fullwidth Semicolon", ";"),
+ ('︔', "Presentation Form For Vertical Semicolon", ";"),
+
+ ('ः', "Devanagari Sign Visarga", ":"),
+ ('ઃ', "Gujarati Sign Visarga", ":"),
+ (':', "Fullwidth Colon", ":"),
+ ('։', "Armenian Full Stop", ":"),
+ ('܃', "Syriac Supralinear Colon", ":"),
+ ('܄', "Syriac Sublinear Colon", ":"),
+ ('᛬', "Runic Multiple Punctuation", ":"),
+ ('︰', "Presentation Form For Vertical Two Dot Leader", ":"),
+ ('᠃', "Mongolian Full Stop", ":"),
+ ('᠉', "Mongolian Manchu Full Stop", ":"),
+ ('⁚', "Two Dot Punctuation", ":"),
+ ('׃', "Hebrew Punctuation Sof Pasuq", ":"),
+ ('˸', "Modifier Letter Raised Colon", ":"),
+ ('꞉', "Modifier Letter Colon", ":"),
+ ('∶', "Ratio", ":"),
+ ('ː', "Modifier Letter Triangular Colon", ":"),
+ ('ꓽ', "Lisu Letter Tone Mya Jeu", ":"),
+ ('︓', "Presentation Form For Vertical Colon", ":"),
+
+ ('!', "Fullwidth Exclamation Mark", "!"),
+ ('ǃ', "Latin Letter Retroflex Click", "!"),
+ ('ⵑ', "Tifinagh Letter Tuareg Yang", "!"),
+ ('︕', "Presentation Form For Vertical Exclamation Mark", "!"),
+
+ ('ʔ', "Latin Letter Glottal Stop", "?"),
+ ('Ɂ', "Latin Capital Letter Glottal Stop", "?"),
+ ('ॽ', "Devanagari Letter Glottal Stop", "?"),
+ ('Ꭾ', "Cherokee Letter He", "?"),
+ ('ꛫ', "Bamum Letter Ntuu", "?"),
+ ('?', "Fullwidth Question Mark", "?"),
+ ('︖', "Presentation Form For Vertical Question Mark", "?"),
+
+ ('𝅭', "Musical Symbol Combining Augmentation Dot", "."),
+ ('․', "One Dot Leader", "."),
+ ('܁', "Syriac Supralinear Full Stop", "."),
+ ('܂', "Syriac Sublinear Full Stop", "."),
+ ('꘎', "Vai Full Stop", "."),
+ ('𐩐', "Kharoshthi Punctuation Dot", "."),
+ ('٠', "Arabic-Indic Digit Zero", "."),
+ ('۰', "Extended Arabic-Indic Digit Zero", "."),
+ ('ꓸ', "Lisu Letter Tone Mya Ti", "."),
+ ('·', "Middle Dot", "."),
+ ('・', "Katakana Middle Dot", "."),
+ ('・', "Halfwidth Katakana Middle Dot", "."),
+ ('᛫', "Runic Single Punctuation", "."),
+ ('·', "Greek Ano Teleia", "."),
+ ('⸱', "Word Separator Middle Dot", "."),
+ ('𐄁', "Aegean Word Separator Dot", "."),
+ ('•', "Bullet", "."),
+ ('‧', "Hyphenation Point", "."),
+ ('∙', "Bullet Operator", "."),
+ ('⋅', "Dot Operator", "."),
+ ('ꞏ', "Latin Letter Sinological Dot", "."),
+ ('ᐧ', "Canadian Syllabics Final Middle Dot", "."),
+ ('ᐧ', "Canadian Syllabics Final Middle Dot", "."),
+ ('.', "Fullwidth Full Stop", "."),
+ ('。', "Ideographic Full Stop", "."),
+ ('︒', "Presentation Form For Vertical Ideographic Full Stop", "."),
+
+ ('՝', "Armenian Comma", "\'"),
+ (''', "Fullwidth Apostrophe", "\'"),
+ ('‘', "Left Single Quotation Mark", "\'"),
+ ('’', "Right Single Quotation Mark", "\'"),
+ ('‛', "Single High-Reversed-9 Quotation Mark", "\'"),
+ ('′', "Prime", "\'"),
+ ('‵', "Reversed Prime", "\'"),
+ ('՚', "Armenian Apostrophe", "\'"),
+ ('׳', "Hebrew Punctuation Geresh", "\'"),
+ ('`', "Grave Accent", "\'"),
+ ('`', "Greek Varia", "\'"),
+ ('`', "Fullwidth Grave Accent", "\'"),
+ ('´', "Acute Accent", "\'"),
+ ('΄', "Greek Tonos", "\'"),
+ ('´', "Greek Oxia", "\'"),
+ ('᾽', "Greek Koronis", "\'"),
+ ('᾿', "Greek Psili", "\'"),
+ ('῾', "Greek Dasia", "\'"),
+ ('ʹ', "Modifier Letter Prime", "\'"),
+ ('ʹ', "Greek Numeral Sign", "\'"),
+ ('ˈ', "Modifier Letter Vertical Line", "\'"),
+ ('ˊ', "Modifier Letter Acute Accent", "\'"),
+ ('ˋ', "Modifier Letter Grave Accent", "\'"),
+ ('˴', "Modifier Letter Middle Grave Accent", "\'"),
+ ('ʻ', "Modifier Letter Turned Comma", "\'"),
+ ('ʽ', "Modifier Letter Reversed Comma", "\'"),
+ ('ʼ', "Modifier Letter Apostrophe", "\'"),
+ ('ʾ', "Modifier Letter Right Half Ring", "\'"),
+ ('ꞌ', "Latin Small Letter Saltillo", "\'"),
+ ('י', "Hebrew Letter Yod", "\'"),
+ ('ߴ', "Nko High Tone Apostrophe", "\'"),
+ ('ߵ', "Nko Low Tone Apostrophe", "\'"),
+ ('ᑊ', "Canadian Syllabics West-Cree P", "\'"),
+ ('ᛌ', "Runic Letter Short-Twig-Sol S", "\'"),
+ ('𖽑', "Miao Sign Aspiration", "\'"),
+ ('𖽒', "Miao Sign Reformed Voicing", "\'"),
+
+ ('᳓', "Vedic Sign Nihshvasa", "\""),
+ ('"', "Fullwidth Quotation Mark", "\""),
+ ('“', "Left Double Quotation Mark", "\""),
+ ('”', "Right Double Quotation Mark", "\""),
+ ('‟', "Double High-Reversed-9 Quotation Mark", "\""),
+ ('″', "Double Prime", "\""),
+ ('‶', "Reversed Double Prime", "\""),
+ ('〃', "Ditto Mark", "\""),
+ ('״', "Hebrew Punctuation Gershayim", "\""),
+ ('˝', "Double Acute Accent", "\""),
+ ('ʺ', "Modifier Letter Double Prime", "\""),
+ ('˶', "Modifier Letter Middle Double Acute Accent", "\""),
+ ('˵', "Modifier Letter Middle Double Grave Accent", "\""),
+ ('ˮ', "Modifier Letter Double Apostrophe", "\""),
+ ('ײ', "Hebrew Ligature Yiddish Double Yod", "\""),
+ ('❞', "Heavy Double Comma Quotation Mark Ornament", "\""),
+ ('❝', "Heavy Double Turned Comma Quotation Mark Ornament", "\""),
+
+ ('(', "Fullwidth Left Parenthesis", "("),
+ ('❨', "Medium Left Parenthesis Ornament", "("),
+ ('﴾', "Ornate Left Parenthesis", "("),
+
+ (')', "Fullwidth Right Parenthesis", ")"),
+ ('❩', "Medium Right Parenthesis Ornament", ")"),
+ ('﴿', "Ornate Right Parenthesis", ")"),
+
+ ('[', "Fullwidth Left Square Bracket", "["),
+ ('❲', "Light Left Tortoise Shell Bracket Ornament", "["),
+ ('「', "Left Corner Bracket", "["),
+ ('『', "Left White Corner Bracket", "["),
+ ('【', "Left Black Lenticular Bracket", "["),
+ ('〔', "Left Tortoise Shell Bracket", "["),
+ ('〖', "Left White Lenticular Bracket", "["),
+ ('〘', "Left White Tortoise Shell Bracket", "["),
+ ('〚', "Left White Square Bracket", "["),
+
+ (']', "Fullwidth Right Square Bracket", "]"),
+ ('❳', "Light Right Tortoise Shell Bracket Ornament", "]"),
+ ('」', "Right Corner Bracket", "]"),
+ ('』', "Right White Corner Bracket", "]"),
+ ('】', "Right Black Lenticular Bracket", "]"),
+ ('〕', "Right Tortoise Shell Bracket", "]"),
+ ('〗', "Right White Lenticular Bracket", "]"),
+ ('〙', "Right White Tortoise Shell Bracket", "]"),
+ ('〛', "Right White Square Bracket", "]"),
+
+ ('❴', "Medium Left Curly Bracket Ornament", "{"),
+ ('𝄔', "Musical Symbol Brace", "{"),
+ ('{', "Fullwidth Left Curly Bracket", "{"),
+
+ ('❵', "Medium Right Curly Bracket Ornament", "}"),
+ ('}', "Fullwidth Right Curly Bracket", "}"),
+
+ ('⁎', "Low Asterisk", "*"),
+ ('٭', "Arabic Five Pointed Star", "*"),
+ ('∗', "Asterisk Operator", "*"),
+ ('𐌟', "Old Italic Letter Ess", "*"),
+ ('*', "Fullwidth Asterisk", "*"),
+
+ ('᜵', "Philippine Single Punctuation", "/"),
+ ('⁁', "Caret Insertion Point", "/"),
+ ('∕', "Division Slash", "/"),
+ ('⁄', "Fraction Slash", "/"),
+ ('╱', "Box Drawings Light Diagonal Upper Right To Lower Left", "/"),
+ ('⟋', "Mathematical Rising Diagonal", "/"),
+ ('⧸', "Big Solidus", "/"),
+ ('𝈺', "Greek Instrumental Notation Symbol-47", "/"),
+ ('㇓', "CJK Stroke Sp", "/"),
+ ('〳', "Vertical Kana Repeat Mark Upper Half", "/"),
+ ('Ⳇ', "Coptic Capital Letter Old Coptic Esh", "/"),
+ ('ノ', "Katakana Letter No", "/"),
+ ('丿', "CJK Unified Ideograph-4E3F", "/"),
+ ('⼃', "Kangxi Radical Slash", "/"),
+ ('/', "Fullwidth Solidus", "/"),
+
+ ('\', "Fullwidth Reverse Solidus", "\\"),
+ ('﹨', "Small Reverse Solidus", "\\"),
+ ('∖', "Set Minus", "\\"),
+ ('⟍', "Mathematical Falling Diagonal", "\\"),
+ ('⧵', "Reverse Solidus Operator", "\\"),
+ ('⧹', "Big Reverse Solidus", "\\"),
+ ('⧹', "Greek Vocal Notation Symbol-16", "\\"),
+ ('⧹', "Greek Instrumental Symbol-48", "\\"),
+ ('㇔', "CJK Stroke D", "\\"),
+ ('丶', "CJK Unified Ideograph-4E36", "\\"),
+ ('⼂', "Kangxi Radical Dot", "\\"),
+ ('、', "Ideographic Comma", "\\"),
+ ('ヽ', "Katakana Iteration Mark", "\\"),
+
+ ('ꝸ', "Latin Small Letter Um", "&"),
+ ('&', "Fullwidth Ampersand", "&"),
+
+ ('᛭', "Runic Cross Punctuation", "+"),
+ ('➕', "Heavy Plus Sign", "+"),
+ ('𐊛', "Lycian Letter H", "+"),
+ ('﬩', "Hebrew Letter Alternative Plus Sign", "+"),
+ ('+', "Fullwidth Plus Sign", "+"),
+
+ ('‹', "Single Left-Pointing Angle Quotation Mark", "<"),
+ ('❮', "Heavy Left-Pointing Angle Quotation Mark Ornament", "<"),
+ ('˂', "Modifier Letter Left Arrowhead", "<"),
+ ('𝈶', "Greek Instrumental Symbol-40", "<"),
+ ('ᐸ', "Canadian Syllabics Pa", "<"),
+ ('ᚲ', "Runic Letter Kauna", "<"),
+ ('❬', "Medium Left-Pointing Angle Bracket Ornament", "<"),
+ ('⟨', "Mathematical Left Angle Bracket", "<"),
+ ('〈', "Left-Pointing Angle Bracket", "<"),
+ ('〈', "Left Angle Bracket", "<"),
+ ('㇛', "CJK Stroke Pd", "<"),
+ ('く', "Hiragana Letter Ku", "<"),
+ ('𡿨', "CJK Unified Ideograph-21FE8", "<"),
+ ('《', "Left Double Angle Bracket", "<"),
+ ('<', "Fullwidth Less-Than Sign", "<"),
+
+ ('᐀', "Canadian Syllabics Hyphen", "="),
+ ('⹀', "Double Hyphen", "="),
+ ('゠', "Katakana-Hiragana Double Hyphen", "="),
+ ('꓿', "Lisu Punctuation Full Stop", "="),
+ ('=', "Fullwidth Equals Sign", "="),
+
+ ('›', "Single Right-Pointing Angle Quotation Mark", ">"),
+ ('❯', "Heavy Right-Pointing Angle Quotation Mark Ornament", ">"),
+ ('˃', "Modifier Letter Right Arrowhead", ">"),
+ ('𝈷', "Greek Instrumental Symbol-42", ">"),
+ ('ᐳ', "Canadian Syllabics Po", ">"),
+ ('𖼿', "Miao Letter Archaic Zza", ">"),
+ ('❭', "Medium Right-Pointing Angle Bracket Ornament", ">"),
+ ('⟩', "Mathematical Right Angle Bracket", ">"),
+ ('〉', "Right-Pointing Angle Bracket", ">"),
+ ('〉', "Right Angle Bracket", ">"),
+ ('》', "Right Double Angle Bracket", ">"),
+ ('>', "Fullwidth Greater-Than Sign", ">"),
+ ('⩵', "Two Consecutive Equals Signs", "==")
];
// FIXME: the lexer could be used to turn the ASCII version of unicode homoglyphs, instead of
// keeping the substitution token in this table. Ideally, this should be inside `rustc_lexer`.
// However, we should first remove compound tokens like `<<` from `rustc_lexer`, and then add
// fancier error recovery to it, as there will be less overall work to do this way.
-const ASCII_ARRAY: &[(char, &str, Option<token::TokenKind>)] = &[
- (' ', "Space", None),
- ('_', "Underscore", Some(token::Ident(kw::Underscore, false))),
- ('-', "Minus/Hyphen", Some(token::BinOp(token::Minus))),
- (',', "Comma", Some(token::Comma)),
- (';', "Semicolon", Some(token::Semi)),
- (':', "Colon", Some(token::Colon)),
- ('!', "Exclamation Mark", Some(token::Not)),
- ('?', "Question Mark", Some(token::Question)),
- ('.', "Period", Some(token::Dot)),
- ('(', "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
- (')', "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
- ('[', "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
- (']', "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
- ('{', "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
- ('}', "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
- ('*', "Asterisk", Some(token::BinOp(token::Star))),
- ('/', "Slash", Some(token::BinOp(token::Slash))),
- ('\\', "Backslash", None),
- ('&', "Ampersand", Some(token::BinOp(token::And))),
- ('+', "Plus Sign", Some(token::BinOp(token::Plus))),
- ('<', "Less-Than Sign", Some(token::Lt)),
- ('=', "Equals Sign", Some(token::Eq)),
- ('>', "Greater-Than Sign", Some(token::Gt)),
+const ASCII_ARRAY: &[(&str, &str, Option<token::TokenKind>)] = &[
+ (" ", "Space", None),
+ ("_", "Underscore", Some(token::Ident(kw::Underscore, false))),
+ ("-", "Minus/Hyphen", Some(token::BinOp(token::Minus))),
+ (",", "Comma", Some(token::Comma)),
+ (";", "Semicolon", Some(token::Semi)),
+ (":", "Colon", Some(token::Colon)),
+ ("!", "Exclamation Mark", Some(token::Not)),
+ ("?", "Question Mark", Some(token::Question)),
+ (".", "Period", Some(token::Dot)),
+ ("(", "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
+ (")", "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
+ ("[", "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
+ ("]", "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
+ ("{", "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
+ ("}", "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
+ ("*", "Asterisk", Some(token::BinOp(token::Star))),
+ ("/", "Slash", Some(token::BinOp(token::Slash))),
+ ("\\", "Backslash", None),
+ ("&", "Ampersand", Some(token::BinOp(token::And))),
+ ("+", "Plus Sign", Some(token::BinOp(token::Plus))),
+ ("<", "Less-Than Sign", Some(token::Lt)),
+ ("=", "Equals Sign", Some(token::Eq)),
+ ("==", "Double Equals Sign", Some(token::EqEq)),
+ (">", "Greater-Than Sign", Some(token::Gt)),
// FIXME: Literals are already lexed by this point, so we can't recover gracefully just by
// spitting the correct token out.
- ('\'', "Single Quote", None),
- ('"', "Quotation Mark", None),
+ ("\'", "Single Quote", None),
+ ("\"", "Quotation Mark", None),
];
pub(super) fn check_for_substitution<'a>(
pos: BytePos,
ch: char,
err: &mut Diagnostic,
+ count: usize,
) -> Option<token::TokenKind> {
- let &(_u_char, u_name, ascii_char) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
+ let &(_, u_name, ascii_str) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
- let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8()));
+ let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8() * count));
- let Some((_ascii_char, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(c, _, _)| c == ascii_char) else {
+ let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
let msg = format!("substitution character not found for '{}'", ch);
reader.sess.span_diagnostic.span_bug_no_panic(span, &msg);
return None;
let msg = format!(
"Unicode characters '“' (Left Double Quotation Mark) and \
'”' (Right Double Quotation Mark) look like '{}' ({}), but are not",
- ascii_char, ascii_name
+ ascii_str, ascii_name
);
err.span_suggestion(
Span::with_root_ctxt(
} else {
let msg = format!(
"Unicode character '{}' ({}) looks like '{}' ({}), but it is not",
- ch, u_name, ascii_char, ascii_name
+ ch, u_name, ascii_str, ascii_name
+ );
+ err.span_suggestion(
+ span,
+ &msg,
+ ascii_str.to_string().repeat(count),
+ Applicability::MaybeIncorrect,
);
- err.span_suggestion(span, &msg, ascii_char, Applicability::MaybeIncorrect);
}
token.clone()
}
ComparisonOrShiftInterpretedAsGenericSugg, DoCatchSyntaxRemoved, DotDotDot, EqFieldInit,
ExpectedElseBlock, ExpectedEqForLetExpr, ExpectedExpressionFoundLet,
FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart, FoundExprWouldBeStmt,
- IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
- InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
- InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
- InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
- LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
- MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
- MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
- NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
- OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
+ IfExpressionLetSomeSub, IfExpressionMissingCondition, IfExpressionMissingThenBlock,
+ IfExpressionMissingThenBlockSub, InvalidBlockMacroSegment, InvalidComparisonOperator,
+ InvalidComparisonOperatorSub, InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex,
+ InvalidLogicalOperator, InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported,
+ LeftArrowOperator, LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath,
+ MalformedLoopLabel, MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg,
+ MissingCommaAfterMatchArm, MissingDotDot, MissingInInForLoop, MissingInInForLoopSub,
+ MissingSemicolonBeforeArray, NoFieldsForFnCall, NotAsNegationOperator,
+ NotAsNegationOperatorSub, OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
pub(super) enum LhsExpr {
NotYetParsed,
AttributesParsed(AttrWrapper),
- AlreadyParsed(P<Expr>, bool), // (expr, starts_statement)
+ AlreadyParsed { expr: P<Expr>, starts_statement: bool },
}
impl From<Option<AttrWrapper>> for LhsExpr {
}
impl From<P<Expr>> for LhsExpr {
- /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed(expr)`.
+ /// Converts the `expr: P<Expr>` into `LhsExpr::AlreadyParsed { expr, starts_statement: false }`.
///
/// This conversion does not allocate.
fn from(expr: P<Expr>) -> Self {
- LhsExpr::AlreadyParsed(expr, false)
+ LhsExpr::AlreadyParsed { expr, starts_statement: false }
}
}
lhs: LhsExpr,
) -> PResult<'a, P<Expr>> {
let mut starts_stmt = false;
- let mut lhs = if let LhsExpr::AlreadyParsed(expr, starts_statement) = lhs {
+ let mut lhs = if let LhsExpr::AlreadyParsed { expr, starts_statement } = lhs {
starts_stmt = starts_statement;
expr
} else {
// Note: when adding new unary operators, don't forget to adjust TokenKind::can_begin_expr()
match this.token.uninterpolate().kind {
- token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)), // `!expr`
- token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)), // `~expr`
+ // `!expr`
+ token::Not => make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Not)),
+ // `~expr`
+ token::Tilde => make_it!(this, attrs, |this, _| this.recover_tilde_expr(lo)),
+ // `-expr`
token::BinOp(token::Minus) => {
make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Neg))
- } // `-expr`
+ }
+ // `*expr`
token::BinOp(token::Star) => {
make_it!(this, attrs, |this, _| this.parse_unary_expr(lo, UnOp::Deref))
- } // `*expr`
+ }
+ // `&expr` and `&&expr`
token::BinOp(token::And) | token::AndAnd => {
make_it!(this, attrs, |this, _| this.parse_borrow_expr(lo))
}
+ // `+lit`
token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {
let mut err =
LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None };
this.bump();
this.parse_prefix_expr(None)
- } // `+expr`
+ }
// Recover from `++x`:
token::BinOp(token::Plus)
if this.look_ahead(1, |t| *t == token::BinOp(token::Plus)) =>
Ok((span, self.mk_unary(op, expr)))
}
- // Recover on `!` suggesting for bitwise negation instead.
+ /// Recover on `~expr` in favor of `!expr`.
fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
self.sess.emit_err(TildeAsUnaryOperator(lo));
/// Recover on `not expr` in favor of `!expr`.
fn recover_not_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
- // Emit the error...
let negated_token = self.look_ahead(1, |t| t.clone());
let sub_diag = if negated_token.is_numeric_lit() {
),
});
- // ...and recover!
self.parse_unary_expr(lo, UnOp::Not)
}
self.parse_array_or_repeat_expr(Delimiter::Bracket)
} else if self.check_path() {
self.parse_path_start_expr()
- } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
+ } else if self.check_keyword(kw::Move)
+ || self.check_keyword(kw::Static)
+ || self.check_const_closure()
+ {
self.parse_closure_expr()
} else if self.eat_keyword(kw::If) {
self.parse_if_expr()
err.span_label(sp, "while parsing this `loop` expression");
err
})
- } else if self.eat_keyword(kw::Continue) {
- let kind = ExprKind::Continue(self.eat_label());
- Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
} else if self.eat_keyword(kw::Match) {
let match_sp = self.prev_token.span;
self.parse_match_expr().map_err(|mut err| {
self.parse_try_block(lo)
} else if self.eat_keyword(kw::Return) {
self.parse_return_expr()
+ } else if self.eat_keyword(kw::Continue) {
+ self.parse_continue_expr(lo)
} else if self.eat_keyword(kw::Break) {
self.parse_break_expr()
} else if self.eat_keyword(kw::Yield) {
} else if self.eat(&token::Comma) {
// Vector with two or more elements.
let sep = SeqSep::trailing_allowed(token::Comma);
- let (remaining_exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;
- let mut exprs = vec![first_expr];
- exprs.extend(remaining_exprs);
+ let (mut exprs, _) = self.parse_seq_to_end(close, sep, |p| p.parse_expr())?;
+ exprs.insert(0, first_expr);
ExprKind::Array(exprs)
} else {
// Vector with one element
vis.0
};
- // Suggestion involves adding a (as of time of writing this, unstable) labeled block.
+ // Suggestion involves adding a labeled block.
//
// If there are no breaks that may use this label, suggest removing the label and
// recover to the unmodified expression.
fn parse_break_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
let mut label = self.eat_label();
- let kind = if label.is_some() && self.token == token::Colon {
+ let kind = if self.token == token::Colon && let Some(label) = label.take() {
// The value expression can be a labeled loop, see issue #86948, e.g.:
// `loop { break 'label: loop { break 'label 42; }; }`
- let lexpr = self.parse_labeled_expr(label.take().unwrap(), true)?;
+ let lexpr = self.parse_labeled_expr(label, true)?;
self.sess.emit_err(LabeledLoopInBreak {
span: lexpr.span,
sub: WrapExpressionInParentheses {
} else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
- let expr = self.parse_expr_opt()?;
- if let Some(expr) = &expr {
+ let mut expr = self.parse_expr_opt()?;
+ if let Some(expr) = &mut expr {
if label.is_some()
&& matches!(
expr.kind,
BuiltinLintDiagnostics::BreakWithLabelAndLoop(expr.span),
);
}
+
+ // Recover `break label aaaaa`
+ if self.may_recover()
+ && let ExprKind::Path(None, p) = &expr.kind
+ && let [segment] = &*p.segments
+ && let &ast::PathSegment { ident, args: None, .. } = segment
+ && let Some(next) = self.parse_expr_opt()?
+ {
+ label = Some(self.recover_ident_into_label(ident));
+ *expr = next;
+ }
}
+
expr
} else {
None
self.maybe_recover_from_bad_qpath(expr)
}
+ /// Parse `"continue" label?`.
+ fn parse_continue_expr(&mut self, lo: Span) -> PResult<'a, P<Expr>> {
+ let mut label = self.eat_label();
+
+ // Recover `continue label` -> `continue 'label`
+ if self.may_recover()
+ && label.is_none()
+ && let Some((ident, _)) = self.token.ident()
+ {
+ self.bump();
+ label = Some(self.recover_ident_into_label(ident));
+ }
+
+ let kind = ExprKind::Continue(label);
+ Ok(self.mk_expr(lo.to(self.prev_token.span), kind))
+ }
+
/// Parse `"yield" expr?`.
fn parse_yield_expr(&mut self) -> PResult<'a, P<Expr>> {
let lo = self.prev_token.span;
ClosureBinder::NotPresent
};
+ let constness = self.parse_constness(Case::Sensitive);
+
let movability =
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
ExprKind::Closure(Box::new(ast::Closure {
binder,
capture_clause,
+ constness,
asyncness,
movability,
fn_decl,
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()?;
}
}
- /// Expect next token to be edible or inedible token. If edible,
+ /// Expect next token to be edible or inedible token. If edible,
/// then consume it; if inedible, then return without consuming
- /// anything. Signal a fatal error if next token is unexpected.
+ /// anything. Signal a fatal error if next token is unexpected.
pub fn expect_one_of(
&mut self,
edible: &[TokenKind],
self.check_or_expected(self.token.can_begin_const_arg(), TokenType::Const)
}
+ fn check_const_closure(&self) -> bool {
+ self.is_keyword_ahead(0, &[kw::Const])
+ && self.look_ahead(1, |t| match &t.kind {
+ token::Ident(kw::Move | kw::Static | kw::Async, _)
+ | token::OrOr
+ | token::BinOp(token::Or) => true,
+ _ => false,
+ })
+ }
+
fn check_inline_const(&self, dist: usize) -> bool {
self.is_keyword_ahead(dist, &[kw::Const])
&& self.look_ahead(dist + 1, |t| match &t.kind {
/// Try to recover the more general form `intersect ::= $pat_lhs @ $pat_rhs`.
///
/// Allowed binding patterns generated by `binding ::= ref? mut? $ident @ $pat_rhs`
- /// should already have been parsed by now at this point,
+ /// should already have been parsed by now at this point,
/// if the next token is `@` then we can try to parse the more general form.
///
/// Consult `parse_pat_ident` for the `binding` grammar.
// Perform this outside of the `collect_tokens_trailing_token` closure,
// since our outer attributes do not apply to this part of the expression
let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
- this.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(expr, true))
+ this.parse_assoc_expr_with(
+ 0,
+ LhsExpr::AlreadyParsed { expr, starts_statement: true },
+ )
})?;
Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
} else {
let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
let e = self.maybe_recover_from_bad_qpath(e)?;
let e = self.parse_dot_or_call_expr_with(e, lo, attrs)?;
- let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e, false))?;
+ let e = self.parse_assoc_expr_with(
+ 0,
+ LhsExpr::AlreadyParsed { expr: e, starts_statement: false },
+ )?;
StmtKind::Expr(e)
};
Ok(self.mk_stmt(lo.to(hi), kind))
let mut bounds = Vec::new();
let mut negative_bounds = Vec::new();
+ // In addition to looping while we find generic bounds:
+ // We continue even if we find a keyword. This is necessary for error recovery on,
+ // for example, `impl fn()`. The only keyword that can go after generic bounds is
+ // `where`, so stop if it's it.
+ // We also continue if we find types (not traits), again for error recovery.
while self.can_begin_bound()
- // Continue even if we find a keyword.
- // This is necessary for error recover on, for example, `impl fn()`.
- //
- // The only keyword that can go after generic bounds is `where`, so stop if it's it.
+ || self.token.can_begin_type()
|| (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
{
if self.token.is_keyword(kw::Dyn) {
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
&& let Some(path) = self.recover_path_from_fn()
{
+ path
+ } else if !self.token.is_path_start() && self.token.can_begin_type() {
+ let ty = self.parse_ty_no_plus()?;
+ // Instead of finding a path (a trait), we found a type.
+ let mut err = self.struct_span_err(ty.span, "expected a trait, found type");
+
+ // If we can recover, try to extract a path from the type. Note
+ // that we do not use the try operator when parsing the type because
+ // if it fails then we get a parser error which we don't want (we're trying
+ // to recover from errors, not make more).
+ let path = if self.may_recover()
+ && matches!(ty.kind, TyKind::Ptr(..) | TyKind::Ref(..))
+ && let TyKind::Path(_, path) = &ty.peel_refs().kind {
+ // Just get the indirection part of the type.
+ let span = ty.span.until(path.span);
+
+ err.span_suggestion_verbose(
+ span,
+ "consider removing the indirection",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+
+ path.clone()
+ } else {
+ return Err(err);
+ };
+
+ err.emit();
+
path
} else {
self.parse_path(PathStyle::Type)?
pub use Piece::*;
pub use Position::*;
-use rustc_lexer::unescape;
use std::iter;
use std::str;
use std::string;
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,
/// 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);
let attrs = self.tcx.hir().attrs(hir_id);
for attr in attrs {
let attr_is_valid = match attr.name_or_empty() {
+ sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
sym::inline => self.check_inline(hir_id, attr, span, target),
sym::no_coverage => self.check_no_coverage(hir_id, attr, span, target),
sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
);
}
+ /// Checks if `#[do_not_recommend]` is applied on a trait impl.
+ fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool {
+ if let Target::Impl = target {
+ true
+ } else {
+ self.tcx.sess.emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span });
+ false
+ }
+ }
+
/// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
fn check_inline(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) -> bool {
match target {
if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
&& self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
{
- let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap();
+ let trait_ref = self.tcx.impl_trait_ref(impl_of).unwrap().subst_identity();
if let ty::Adt(adt_def, _) = trait_ref.self_ty().kind()
&& let Some(adt_def_id) = adt_def.did().as_local()
{
// 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 crate::lang_items::Duplicate;
+#[derive(Diagnostic)]
+#[diag(passes_incorrect_do_not_recommend_location)]
+pub struct IncorrectDoNotRecommendLocation {
+ #[primary_span]
+ pub span: Span,
+}
+
#[derive(LintDiagnostic)]
#[diag(passes_outer_crate_level_attr)]
pub struct OuterCrateLevelAttr;
}
fn lib_features(tcx: TyCtxt<'_>, (): ()) -> LibFeatures {
+ // If `staged_api` is not enabled then we aren't allowed to define lib
+ // features; there is no point collecting them.
+ if !tcx.features().staged_api {
+ return new_lib_features();
+ }
+
let mut collector = LibFeatureCollector::new(tcx);
tcx.hir().walk_attributes(&mut collector);
collector.lib_features
// Creating ir_maps
//
// This is the first pass and the one that drives the main
-// computation. It walks up and down the IR once. On the way down,
+// computation. It walks up and down the IR once. On the way down,
// we count for each function the number of variables as well as
-// liveness nodes. A liveness node is basically an expression or
+// liveness nodes. A liveness node is basically an expression or
// capture clause that does something of interest: either it has
// interesting control flow or it uses/defines a local variable.
//
// of live variables at each program point.
//
// Finally, we run back over the IR one last time and, using the
-// computed liveness, check various safety conditions. For example,
+// computed liveness, check various safety conditions. For example,
// there must be no live nodes at the definition site for a variable
-// unless it has an initializer. Similarly, each non-mutable local
+// unless it has an initializer. Similarly, each non-mutable local
// variable must not be assigned if there is some successor
-// assignment. And so forth.
+// assignment. And so forth.
struct CaptureInfo {
ln: LiveNode,
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span, expr.hir_id));
// Make a live_node for each mentioned variable, with the span
- // being the location that the variable is used. This results
+ // being the location that the variable is used. This results
// in better error messages than just pointing at the closure
// construction site.
let mut call_caps = Vec::new();
match stmt.kind {
hir::StmtKind::Local(ref local) => {
// Note: we mark the variable as defined regardless of whether
- // there is an initializer. Initially I had thought to only mark
+ // there is an initializer. Initially I had thought to only mark
// the live variable as defined if it was initialized, and then we
// could check for uninit variables just by scanning what is live
// at the start of the function. But that doesn't work so well for
//
// # Tracked places
//
- // A tracked place is a local variable/argument `x`. In
+ // A tracked place is a local variable/argument `x`. In
// these cases, the link_node where the write occurs is linked
- // to node id of `x`. The `write_place()` routine generates
- // the contents of this node. There are no subcomponents to
+ // to node id of `x`. The `write_place()` routine generates
+ // the contents of this node. There are no subcomponents to
// consider.
//
// # Non-tracked places
//
- // These are places like `x[5]` or `x.f`. In that case, we
+ // These are places like `x[5]` or `x.f`. In that case, we
// basically ignore the value which is written to but generate
- // reads for the components---`x` in these two examples. The
+ // reads for the components---`x` in these two examples. The
// components reads are generated by
// `propagate_through_place_components()` (this fn).
//
// # Illegal places
//
// It is still possible to observe assignments to non-places;
- // these errors are detected in the later pass borrowck. We
+ // these errors are detected in the later pass borrowck. We
// just ignore such cases and treat them as reads.
match expr.kind {
}
// We do not track other places, so just propagate through
- // to their subcomponents. Also, it may happen that
+ // to their subcomponents. Also, it may happen that
// non-places occur here, because those are detected in the
// later pass borrowck.
_ => succ,
}
if !self.tcx.features().staged_api {
- // Propagate unstability. This can happen even for non-staged-api crates in case
+ // Propagate unstability. This can happen even for non-staged-api crates in case
// -Zforce-unstable-if-unmarked is set.
if let Some(stab) = self.parent_stab {
if inherit_deprecation.yes() && stab.is_unstable() {
#![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
// 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);
}
if self.def_id_visitor.shallow() {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
ty.super_visit_with(self)
}
if let Some(def_id) = def_id.as_local() {
self.min = VL::new_min(self, def_id);
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
let mut find = FindMin { tcx, effective_visibilities, min: Self::MAX };
find.visit(tcx.type_of(def_id));
if let Some(trait_ref) = tcx.impl_trait_ref(def_id) {
- find.visit_trait(trait_ref);
+ find.visit_trait(trait_ref.subst_identity());
}
find.min
}
GenericParamDefKind::Const { has_default } => {
self.visit(self.ev.tcx.type_of(param.def_id));
if has_default {
- self.visit(self.ev.tcx.const_param_default(param.def_id));
+ self.visit(self.ev.tcx.const_param_default(param.def_id).subst_identity());
}
}
}
fn trait_ref(&mut self) -> &mut Self {
if let Some(trait_ref) = self.ev.tcx.impl_trait_ref(self.item_def_id) {
- self.visit_trait(trait_ref);
+ self.visit_trait(trait_ref.subst_identity());
}
self
}
self.ev.update(def_id, self.level);
}
}
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if self.check_def_id(def_id, kind, descr) {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
descr: &dyn fmt::Display,
) -> ControlFlow<Self::BreakTy> {
if self.check_def_id(def_id, kind, descr) {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
/// This is used to remove cycles during type-checking const generic parameters.
///
/// As usual in the query system, we consider the current state of the calling query
- /// only depends on the list of dependencies up to now. As a consequence, the value
- /// that this query gives us can only depend on those dependencies too. Therefore,
+ /// only depends on the list of dependencies up to now. As a consequence, the value
+ /// that this query gives us can only depend on those dependencies too. Therefore,
/// it is sound to use the current dependency set for the created node.
///
/// During replay, the order of the nodes is relevant in the dependency graph.
hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
) -> DepNodeIndex {
if let Some(data) = self.data.as_ref() {
- // The caller query has more dependencies than the node we are creating. We may
+ // The caller query has more dependencies than the node we are creating. We may
// encounter a case where this created node is marked as green, but the caller query is
- // subsequently marked as red or recomputed. In this case, we will end up feeding a
+ // subsequently marked as red or recomputed. In this case, we will end up feeding a
// value to an existing node.
//
// For sanity, we still check that the loaded stable hash and the new one match.
/// graph: they are only added.
///
/// The nodes in it are identified by a `DepNodeIndex`. We avoid keeping the nodes
-/// in memory. This is important, because these graph structures are some of the
+/// in memory. This is important, because these graph structures are some of the
/// largest in the compiler.
///
/// For this reason, we avoid storing `DepNode`s more than once as map
//! The data that we will serialize and deserialize.
//!
//! The dep-graph is serialized as a sequence of NodeInfo, with the dependencies
-//! specified inline. The total number of nodes and edges are stored as the last
+//! specified inline. The total number of nodes and edges are stored as the last
//! 16 bytes of the file, so we can find them easily at decoding time.
//!
//! The serialisation is performed on-demand when each node is emitted. Using this
//! scheme, we do not need to keep the current graph in memory.
//!
//! The deserialization is performed manually, in order to convert from the stored
-//! sequence of NodeInfos to the different arrays in SerializedDepGraph. Since the
+//! sequence of NodeInfos to the different arrays in SerializedDepGraph. Since the
//! node and edge count are stored at the end of the file, all the arrays can be
//! pre-allocated with the right length.
let mut lock = self.cache.get_shard_by_value(&key).lock();
#[cfg(not(parallel_compiler))]
let mut lock = self.cache.lock();
- // We may be overwriting another value. This is all right, since the dep-graph
+ // We may be overwriting another value. This is all right, since the dep-graph
// will check that the fingerprint matches.
lock.insert(key, (value.clone(), index));
value
let mut lock = self.cache.get_shard_by_value(&key).lock();
#[cfg(not(parallel_compiler))]
let mut lock = self.cache.lock();
- // We may be overwriting another value. This is all right, since the dep-graph
+ // We may be overwriting another value. This is all right, since the dep-graph
// will check that the fingerprint matches.
lock.insert(key, value);
&value.0
self.r.field_names.insert(def_id, field_names);
}
+ fn insert_field_visibilities_local(&mut self, def_id: DefId, vdata: &ast::VariantData) {
+ let field_vis = vdata
+ .fields()
+ .iter()
+ .map(|field| field.vis.span.until(field.ident.map_or(field.ty.span, |i| i.span)))
+ .collect();
+ self.r.field_visibility_spans.insert(def_id, field_vis);
+ }
+
fn insert_field_names_extern(&mut self, def_id: DefId) {
let field_names =
self.r.cstore().struct_field_names_untracked(def_id, self.r.session).collect();
// Record field names for error reporting.
self.insert_field_names_local(def_id, vdata);
+ self.insert_field_visibilities_local(def_id, vdata);
// If this is a tuple or unit struct, define a name
// in the value namespace as well.
Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id.to_def_id());
self.r.define(parent, ident, ValueNS, (ctor_res, ctor_vis, sp, expansion));
self.r.visibilities.insert(ctor_def_id, ctor_vis);
+ // We need the field visibility spans also for the constructor for E0603.
+ self.insert_field_visibilities_local(ctor_def_id.to_def_id(), vdata);
self.r
.struct_constructors
// Record field names for error reporting.
self.insert_field_names_local(def_id, vdata);
+ self.insert_field_visibilities_local(def_id, vdata);
}
ItemKind::Trait(..) => {
// Record field names for error reporting.
self.insert_field_names_local(def_id.to_def_id(), &variant.data);
+ self.insert_field_visibilities_local(def_id.to_def_id(), &variant.data);
visit::walk_variant(self, variant);
}
use crate::Resolver;
use rustc_ast as ast;
-use rustc_ast::node_id::NodeMap;
use rustc_ast::visit::{self, Visitor};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::UnordSet;
use rustc_errors::{pluralize, MultiSpan};
use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_IMPORTS};
use rustc_session::lint::BuiltinLintDiagnostics;
use_tree: &'a ast::UseTree,
use_tree_id: ast::NodeId,
item_span: Span,
- unused: FxHashSet<ast::NodeId>,
+ unused: UnordSet<ast::NodeId>,
}
impl<'a> UnusedImport<'a> {
struct UnusedImportCheckVisitor<'a, 'b> {
r: &'a mut Resolver<'b>,
/// All the (so far) unused imports, grouped path list
- unused_imports: NodeMap<UnusedImport<'a>>,
+ unused_imports: FxIndexMap<ast::NodeId, UnusedImport<'a>>,
base_use_tree: Option<&'a ast::UseTree>,
base_id: ast::NodeId,
item_span: Span,
use_tree,
use_tree_id,
item_span,
- unused: FxHashSet::default(),
+ unused: Default::default(),
})
}
}
use rustc_ast::{self as ast, Crate, ItemKind, ModKind, NodeId, Path, CRATE_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{
+ pluralize, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
+};
+use rustc_errors::{struct_span_err, SuggestionStyle};
use rustc_feature::BUILTIN_ATTRIBUTES;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS};
);
err.emit();
} else if let Some((span, msg, sugg, appl)) = suggestion {
- err.span_suggestion(span, msg, sugg, appl);
+ err.span_suggestion_verbose(span, msg, sugg, appl);
err.emit();
} else if let [segment] = path.as_slice() && is_call {
err.stash(segment.ident.span, rustc_errors::StashKey::CallIntoMethod);
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 => {
err.span_label(ident.span, &format!("private {}", descr));
if let Some(span) = ctor_fields_span {
err.span_label(span, "a constructor is private if any of the fields is private");
+ if let Res::Def(_, d) = res && let Some(fields) = self.field_visibility_spans.get(&d) {
+ err.multipart_suggestion_verbose(
+ &format!(
+ "consider making the field{} publicly accessible",
+ pluralize!(fields.len())
+ ),
+ fields.iter().map(|span| (*span, "pub ".to_string())).collect(),
+ Applicability::MaybeIncorrect,
+ );
+ }
}
// Print the whole import chain to make it easier to see what happens.
let source_map = self.r.session.source_map();
+ // Make sure this is actually crate-relative.
+ let is_definitely_crate = import
+ .module_path
+ .first()
+ .map_or(false, |f| f.ident.name != kw::SelfLower && f.ident.name != kw::Super);
+
// Add the import to the start, with a `{` if required.
let start_point = source_map.start_point(after_crate_name);
- if let Ok(start_snippet) = source_map.span_to_snippet(start_point) {
+ if is_definitely_crate && let Ok(start_snippet) = source_map.span_to_snippet(start_point) {
corrections.push((
start_point,
if has_nested {
format!("{{{}, {}", import_snippet, start_snippet)
},
));
- }
- // Add a `};` to the end if nested, matching the `{` added at the start.
- if !has_nested {
- corrections.push((source_map.end_point(after_crate_name), "};".to_string()));
+ // Add a `};` to the end if nested, matching the `{` added at the start.
+ if !has_nested {
+ corrections.push((source_map.end_point(after_crate_name), "};".to_string()));
+ }
+ } else {
+ // If the root import is module-relative, add the import separately
+ corrections.push((
+ import.use_span.shrink_to_lo(),
+ format!("use {module_name}::{import_snippet};\n"),
+ ));
}
}
}
if let Some(span) = use_placement_span {
- let add_use = match mode {
+ let (add_use, trailing) = match mode {
DiagnosticMode::Pattern => {
err.span_suggestions(
span,
);
return;
}
- DiagnosticMode::Import => "",
- DiagnosticMode::Normal => "use ",
+ DiagnosticMode::Import => ("", ""),
+ DiagnosticMode::Normal => ("use ", ";\n"),
};
for candidate in &mut accessible_path_strings {
// produce an additional newline to separate the new use statement
// from the directly following item.
- let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
- candidate.0 = format!("{add_use}{}{append};\n{additional_newline}", &candidate.0);
+ let additional_newline = if let FoundUse::No = found_use && let DiagnosticMode::Normal = mode { "\n" } else { "" };
+ candidate.0 =
+ format!("{add_use}{}{append}{trailing}{additional_newline}", &candidate.0);
}
- err.span_suggestions(
+ err.span_suggestions_with_style(
span,
&msg,
accessible_path_strings.into_iter().map(|a| a.0),
Applicability::MaybeIncorrect,
+ SuggestionStyle::ShowAlways,
);
if let [first, .., last] = &path[..] {
let sp = first.ident.span.until(last.ident.span);
msg.push_str(&candidate.0);
}
- err.note(&msg);
+ err.help(&msg);
}
} else if !matches!(mode, DiagnosticMode::Import) {
assert!(!inaccessible_path_strings.is_empty());
-use rustc_ast as ast;
+use rustc_ast::{self as ast, NodeId};
use rustc_feature::is_builtin_attr_name;
use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS};
use rustc_hir::PrimTy;
use rustc_middle::bug;
use rustc_middle::ty;
+use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK;
+use rustc_session::lint::BuiltinLintDiagnostics;
use rustc_span::def_id::LocalDefId;
use rustc_span::edition::Edition;
use rustc_span::hygiene::{ExpnId, ExpnKind, LocalExpnId, MacroKind, SyntaxContext};
};
let mut scope = match ns {
_ if is_absolute_path => Scope::CrateRoot,
- TypeNS | ValueNS => Scope::Module(module),
+ TypeNS | ValueNS => Scope::Module(module, None),
MacroNS => Scope::DeriveHelpers(parent_scope.expansion),
};
let mut ctxt = ctxt.normalize_to_macros_2_0();
MacroRulesScope::Invocation(invoc_id) => {
Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
}
- MacroRulesScope::Empty => Scope::Module(module),
+ MacroRulesScope::Empty => Scope::Module(module, None),
},
Scope::CrateRoot => match ns {
TypeNS => {
}
ValueNS | MacroNS => break,
},
- Scope::Module(module) => {
+ Scope::Module(module, prev_lint_id) => {
use_prelude = !module.no_implicit_prelude;
- match self.hygienic_lexical_parent(module, &mut ctxt) {
- Some(parent_module) => Scope::Module(parent_module),
+ let derive_fallback_lint_id = match scope_set {
+ ScopeSet::Late(.., lint_id) => lint_id,
+ _ => None,
+ };
+ match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
+ Some((parent_module, lint_id)) => {
+ Scope::Module(parent_module, lint_id.or(prev_lint_id))
+ }
None => {
ctxt.adjust(ExpnId::root());
match ns {
&mut self,
module: Module<'a>,
ctxt: &mut SyntaxContext,
- ) -> Option<Module<'a>> {
+ derive_fallback_lint_id: Option<NodeId>,
+ ) -> Option<(Module<'a>, Option<NodeId>)> {
if !module.expansion.outer_expn_is_descendant_of(*ctxt) {
- return Some(self.expn_def_scope(ctxt.remove_mark()));
+ return Some((self.expn_def_scope(ctxt.remove_mark()), None));
}
if let ModuleKind::Block = module.kind {
- return Some(module.parent.unwrap().nearest_item_scope());
+ return Some((module.parent.unwrap().nearest_item_scope(), None));
+ }
+
+ // We need to support the next case under a deprecation warning
+ // ```
+ // struct MyStruct;
+ // ---- begin: this comes from a proc macro derive
+ // mod implementation_details {
+ // // Note that `MyStruct` is not in scope here.
+ // impl SomeTrait for MyStruct { ... }
+ // }
+ // ---- end
+ // ```
+ // So we have to fall back to the module's parent during lexical resolution in this case.
+ if derive_fallback_lint_id.is_some() {
+ if let Some(parent) = module.parent {
+ // Inner module is inside the macro, parent module is outside of the macro.
+ if module.expansion != parent.expansion
+ && module.expansion.is_descendant_of(parent.expansion)
+ {
+ // The macro is a proc macro derive
+ if let Some(def_id) = module.expansion.expn_data().macro_def_id {
+ let ext = self.get_macro_by_def_id(def_id).ext;
+ if ext.builtin_name.is_none()
+ && ext.macro_kind() == MacroKind::Derive
+ && parent.expansion.outer_expn_is_descendant_of(*ctxt)
+ {
+ return Some((parent, derive_fallback_lint_id));
+ }
+ }
+ }
+ }
}
None
Err((Determinacy::Determined, _)) => Err(Determinacy::Determined),
}
}
- Scope::Module(module) => {
+ Scope::Module(module, derive_fallback_lint_id) => {
let adjusted_parent_scope = &ParentScope { module, ..*parent_scope };
let binding = this.resolve_ident_in_module_unadjusted_ext(
ModuleOrUniformRoot::Module(module),
);
match binding {
Ok(binding) => {
+ if let Some(lint_id) = derive_fallback_lint_id {
+ this.lint_buffer.buffer_lint_with_diagnostic(
+ PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+ lint_id,
+ orig_ident.span,
+ &format!(
+ "cannot find {} `{}` in this scope",
+ ns.descr(),
+ ident
+ ),
+ BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(
+ orig_ident.span,
+ ),
+ );
+ }
let misc_flags = if ptr::eq(module, this.graph_root) {
Flags::MISC_SUGGEST_CRATE
} else if module.is_normal() {
&& let Some(partial_res) = self.r.partial_res_map.get(&ty.id)
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) = partial_res.full_res()
{
- // This path is actually a bare trait object. In case of a bare `Fn`-trait
+ // This path is actually a bare trait object. In case of a bare `Fn`-trait
// object with anonymous lifetimes, we need this rib to correctly place the
// synthetic lifetimes.
let span = ty.span.shrink_to_lo().to(path.span.shrink_to_lo());
// Probe the lifetime ribs to know how to behave.
for rib in self.lifetime_ribs.iter().rev() {
match rib.kind {
- // We are inside a `PolyTraitRef`. The lifetimes are
+ // We are inside a `PolyTraitRef`. The lifetimes are
// to be intoduced in that (maybe implicit) `for<>` binder.
LifetimeRibKind::Generics {
binder,
);
break;
}
- // We have nowhere to introduce generics. Code is malformed,
+ // We have nowhere to introduce generics. Code is malformed,
// so use regular lifetime resolution to avoid spurious errors.
LifetimeRibKind::Item | LifetimeRibKind::Generics { .. } => {
visit::walk_generic_args(self, args);
break;
}
// `LifetimeRes::Error`, which would usually be used in the case of
- // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead,
+ // `ReportError`, is unsuitable here, as we don't emit an error yet. Instead,
// we simply resolve to an implicit lifetime, which will be checked later, at
// which point a suitable error will be emitted.
LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
sugg.to_string(),
Applicability::MaybeIncorrect,
))
- } else if res.is_none() {
+ } else if res.is_none() && matches!(source, PathSource::Type) {
this.report_missing_type_error(path)
} else {
None
if let Some(qself) = qself {
if qself.position == 0 {
// This is a case like `<T>::B`, where there is no
- // trait to resolve. In that case, we leave the `B`
+ // trait to resolve. In that case, we leave the `B`
// segment to be resolved by type-check.
return Ok(Some(PartialRes::with_unresolved_segments(
Res::Def(DefKind::Mod, CRATE_DEF_ID.to_def_id()),
// Make sure `A::B` in `<T as A::B>::C` is a trait item.
//
// Currently, `path` names the full item (`A::B::C`, in
- // our example). so we extract the prefix of that that is
+ // our example). so we extract the prefix of that that is
// the trait (the slice upto and including
// `qself.position`). And then we recursively resolve that,
// but with `qself` set to `None`.
.collect();
if non_visible_spans.len() > 0 {
+ if let Some(fields) = self.r.field_visibility_spans.get(&def_id) {
+ err.multipart_suggestion_verbose(
+ &format!(
+ "consider making the field{} publicly accessible",
+ pluralize!(fields.len())
+ ),
+ fields.iter().map(|span| (*span, "pub ".to_string())).collect(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
let mut m: MultiSpan = non_visible_spans.clone().into();
non_visible_spans
.into_iter()
path: &[Segment],
) -> Option<(Span, &'static str, String, Applicability)> {
let (ident, span) = match path {
- [segment] if !segment.has_generic_args && segment.ident.name != kw::SelfUpper => {
+ [segment]
+ if !segment.has_generic_args
+ && segment.ident.name != kw::SelfUpper
+ && segment.ident.name != kw::Dyn =>
+ {
(segment.ident.to_string(), segment.ident.span)
}
_ => return None,
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,
/// Used for hints during error reporting.
field_names: FxHashMap<DefId, Vec<Spanned<Symbol>>>,
+ /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax.
+ /// Used for hints during error reporting.
+ field_visibility_spans: FxHashMap<DefId, Vec<Span>>,
+
/// All imports known to succeed or fail.
determined_imports: Vec<&'a Import<'a>>,
}
}
-impl Resolver<'_> {
+impl<'a> Resolver<'a> {
fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
self.node_id_to_def_id.get(&node).copied()
}
self.cstore().item_generics_num_lifetimes(def_id, self.session)
}
}
+
+ pub fn sess(&self) -> &'a Session {
+ self.session
+ }
}
impl<'a> Resolver<'a> {
has_self: FxHashSet::default(),
field_names: FxHashMap::default(),
+ field_visibility_spans: FxHashMap::default(),
determined_imports: Vec::new(),
indeterminate_imports: Vec::new(),
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)]
pub fn host_triple() -> &'static str {
// Get the host triple out of the build environment. This ensures that our
// idea of the host triple is the same as for the set of libraries we've
- // actually built. We can't just take LLVM's host triple because they
+ // actually built. We can't just take LLVM's host triple because they
// normalize all ix86 architectures to i386.
//
// Instead of grabbing the host triple (for the current host), we grab (at
// The `opt` local module holds wrappers around the `getopts` API that
// adds extra rustc-specific metadata to each option; such metadata
-// is exposed by . The public
+// is exposed by . The public
// functions below ending with `_u` are the functions that return
// *unstable* options, i.e., options that are only enabled when the
// user also passes the `-Z unstable-options` debugging flag.
.map(|s| {
// Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
// where KIND is one of "dylib", "framework", "static", "link-arg" and
- // where MODIFIERS are a comma separated list of supported modifiers
+ // where MODIFIERS are a comma separated list of supported modifiers
// (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed
// with either + or - to indicate whether it is enabled or disabled.
// The last value specified for a given modifier wins.
let pretty = parse_pretty(&unstable_opts, error_format);
+ // query-dep-graph is required if dump-dep-graph is given #106736
+ if unstable_opts.dump_dep_graph && !unstable_opts.query_dep_graph {
+ early_error(error_format, "can't dump dependency graph without `-Z query-dep-graph`");
+ }
+
// Try to find a directory containing the Rust `src`, for more details see
// the doc comment on the `real_rust_source_base_dir` field.
let tmp_buf;
early_error(error_format, &format!("Current directory is invalid: {e}"));
});
- let (path, remapped) =
- FilePathMapping::new(remap_path_prefix.clone()).map_prefix(working_dir.clone());
+ let remap = FilePathMapping::new(remap_path_prefix.clone());
+ let (path, remapped) = remap.map_prefix(&working_dir);
let working_dir = if remapped {
- RealFileName::Remapped { local_path: Some(working_dir), virtual_name: path }
+ RealFileName::Remapped { virtual_name: path.into_owned(), local_path: Some(working_dir) }
} else {
- RealFileName::LocalPath(path)
+ RealFileName::LocalPath(path.into_owned())
};
Options {
fn def_path_hash(&self, def: DefId) -> DefPathHash;
// This information is safe to access, since it's hashed as part of the StableCrateId, which
- // incr. comp. uses to identify a CrateNum.
+ // incr. comp. uses to identify a CrateNum.
fn crate_name(&self, cnum: CrateNum) -> Symbol;
fn stable_crate_id(&self, cnum: CrateNum) -> StableCrateId;
fn stable_crate_id_to_crate_num(&self, stable_crate_id: StableCrateId) -> CrateNum;
/// This function checks if sysroot is found using env::args().next(), and if it
/// is not found, finds sysroot from current rustc_driver dll.
pub fn get_or_default_sysroot() -> Result<PathBuf, String> {
- // Follow symlinks. If the resolved path is relative, make it absolute.
+ // Follow symlinks. If the resolved path is relative, make it absolute.
fn canonicalize(path: PathBuf) -> PathBuf {
let path = fs::canonicalize(&path).unwrap_or(path);
// See comments on this target function, but the gist is that
"what location details should be tracked when using caller_location, either \
`none`, or a comma separated list of location details, for which \
valid options are `file`, `line`, and `column` (default: `file,line,column`)"),
+ log_backtrace: Option<String> = (None, parse_opt_string, [TRACKED],
+ "add a backtrace along with logging"),
ls: bool = (false, parse_bool, [UNTRACKED],
"list the symbols defined by a library crate (default: no)"),
macro_backtrace: bool = (false, parse_bool, [UNTRACKED],
out_filename
}
-/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
+/// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers
/// check this already -- however, the Linux linker will happily overwrite a
-/// read-only file. We should be consistent.
+/// read-only file. We should be consistent.
pub fn check_file_is_writeable(file: &Path, sess: &Session) {
if !is_writeable(file) {
sess.emit_fatal(FileIsNotWriteable { file });
}
}
-pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> Symbol {
+pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute]) -> Symbol {
let validate = |s: Symbol, span: Option<Span>| {
validate_crate_name(sess, s, span);
s
if let Some((attr, s)) = attr_crate_name {
return validate(s, Some(attr.span));
}
- if let Input::File(ref path) = *input {
+ if let Input::File(ref path) = sess.io.input {
if let Some(s) = path.file_stem().and_then(|s| s.to_str()) {
if s.starts_with('-') {
sess.emit_err(CrateNameInvalid { s });
use crate::cgu_reuse_tracker::CguReuseTracker;
use crate::code_stats::CodeStats;
pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
+use crate::config::Input;
use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
use crate::errors::{
BranchProtectionRequiresAArch64, CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers,
pub const_eval_limit: Limit,
}
+pub struct CompilerIO {
+ pub input: Input,
+ pub output_dir: Option<PathBuf>,
+ pub output_file: Option<PathBuf>,
+ pub temps_dir: Option<PathBuf>,
+}
+
/// Represents the data associated with a compilation
/// session for a single crate.
pub struct Session {
pub target_tlib_path: Lrc<SearchPath>,
pub parse_sess: ParseSess,
pub sysroot: PathBuf,
- /// The name of the root source file of the crate, in the local file system.
- /// `None` means that there is no source file.
- pub local_crate_source_file: Option<PathBuf>,
+ /// Input, input file path and output file path to this compilation process.
+ pub io: CompilerIO,
crate_types: OnceCell<Vec<CrateType>>,
/// The `stable_crate_id` is constructed out of the crate name and all the
pub ctfe_backtrace: Lock<CtfeBacktrace>,
/// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a
- /// const check, optionally with the relevant feature gate. We use this to
+ /// const check, optionally with the relevant feature gate. We use this to
/// warn about unleashing, but with a single diagnostic instead of dozens that
/// drown everything else in noise.
miri_unleashed_features: Lock<Vec<(Span, Option<Symbol>)>>,
self.miri_unleashed_features.lock().push((span, feature_gate));
}
+ pub fn local_crate_source_file(&self) -> Option<PathBuf> {
+ let path = self.io.input.opt_path()?;
+ Some(self.opts.file_path_mapping().map_prefix(path).0.into_owned())
+ }
+
fn check_miri_unleashed_features(&self) {
let unleashed_features = self.miri_unleashed_features.lock();
if !unleashed_features.is_empty() {
#[allow(rustc::bad_opt_access)]
pub fn build_session(
sopts: config::Options,
- local_crate_source_file: Option<PathBuf>,
+ io: CompilerIO,
bundle: Option<Lrc<rustc_errors::FluentBundle>>,
registry: rustc_errors::registry::Registry,
driver_lint_caps: FxHashMap<lint::LintId, lint::Level>,
Lrc::new(SearchPath::from_sysroot_and_triple(&sysroot, target_triple))
};
- let file_path_mapping = sopts.file_path_mapping();
-
- let local_crate_source_file =
- local_crate_source_file.map(|path| file_path_mapping.map_prefix(path).0);
-
let optimization_fuel = Lock::new(OptimizationFuel {
remaining: sopts.unstable_opts.fuel.as_ref().map_or(0, |&(_, i)| i),
out_of_fuel: false,
target_tlib_path,
parse_sess,
sysroot,
- local_crate_source_file,
+ io,
crate_types: OnceCell::new(),
stable_crate_id: OnceCell::new(),
features: OnceCell::new(),
unicode-width = "0.1.4"
cfg-if = "1.0"
tracing = "0.1"
-sha1 = { package = "sha-1", version = "0.10.0" }
+sha1 = "0.10.0"
sha2 = "0.10.1"
md5 = { package = "md-5", version = "0.10.0" }
/// first and then resolved later), so we use an `Option` here.
local_expn_data: IndexVec<LocalExpnId, Option<ExpnData>>,
local_expn_hashes: IndexVec<LocalExpnId, ExpnHash>,
- /// Data and hash information from external crates. We may eventually want to remove these
+ /// Data and hash information from external crates. We may eventually want to remove these
/// maps, and fetch the information directly from the other crate's metadata like DefIds do.
foreign_expn_data: FxHashMap<ExpnId, ExpnData>,
foreign_expn_hashes: FxHashMap<ExpnId, ExpnHash>,
use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
use std::cmp;
use std::hash::Hash;
-use std::path::{Path, PathBuf};
+use std::path::{self, Path, PathBuf};
use std::sync::atomic::Ordering;
use std::fs;
pub fn ensure_source_file_source_present(&self, source_file: Lrc<SourceFile>) -> bool {
source_file.add_external_src(|| {
- match source_file.name {
- FileName::Real(ref name) if let Some(local_path) = name.local_path() => {
- self.file_loader.read_file(local_path).ok()
+ let FileName::Real(ref name) = source_file.name else {
+ return None;
+ };
+
+ let local_path: Cow<'_, Path> = match name {
+ RealFileName::LocalPath(local_path) => local_path.into(),
+ RealFileName::Remapped { local_path: Some(local_path), .. } => local_path.into(),
+ RealFileName::Remapped { local_path: None, virtual_name } => {
+ // The compiler produces better error messages if the sources of dependencies
+ // are available. Attempt to undo any path mapping so we can find remapped
+ // dependencies.
+ // We can only use the heuristic because `add_external_src` checks the file
+ // content hash.
+ self.path_mapping.reverse_map_prefix_heuristically(virtual_name)?.into()
}
- _ => None,
- }
+ };
+
+ self.file_loader.read_file(&local_path).ok()
})
}
/// Applies any path prefix substitution as defined by the mapping.
/// The return value is the remapped path and a boolean indicating whether
/// the path was affected by the mapping.
- pub fn map_prefix(&self, path: PathBuf) -> (PathBuf, bool) {
+ pub fn map_prefix<'a>(&'a self, path: impl Into<Cow<'a, Path>>) -> (Cow<'a, Path>, bool) {
+ let path = path.into();
if path.as_os_str().is_empty() {
// Exit early if the path is empty and therefore there's nothing to remap.
// This is mostly to reduce spam for `RUSTC_LOG=[remap_path_prefix]`.
return remap_path_prefix(&self.mapping, path);
#[instrument(level = "debug", skip(mapping), ret)]
- fn remap_path_prefix(mapping: &[(PathBuf, PathBuf)], path: PathBuf) -> (PathBuf, bool) {
+ fn remap_path_prefix<'a>(
+ mapping: &'a [(PathBuf, PathBuf)],
+ path: Cow<'a, Path>,
+ ) -> (Cow<'a, Path>, bool) {
// NOTE: We are iterating over the mapping entries from last to first
// because entries specified later on the command line should
// take precedence.
// in remapped paths down the line.
// So, if we have an exact match, we just return that without a call
// to `Path::join()`.
- to.clone()
+ to.into()
} else {
- to.join(rest)
+ to.join(rest).into()
};
debug!("Match - remapped");
fn map_filename_prefix(&self, file: &FileName) -> (FileName, bool) {
match file {
FileName::Real(realfile) if let RealFileName::LocalPath(local_path) = realfile => {
- let (mapped_path, mapped) = self.map_prefix(local_path.to_path_buf());
+ let (mapped_path, mapped) = self.map_prefix(local_path);
let realfile = if mapped {
RealFileName::Remapped {
local_path: Some(local_path.clone()),
- virtual_name: mapped_path,
+ virtual_name: mapped_path.into_owned(),
}
} else {
realfile.clone()
let (new_path, was_remapped) = self.map_prefix(unmapped_file_path);
if was_remapped {
// It was remapped, so don't modify further
- return RealFileName::Remapped { local_path: None, virtual_name: new_path };
+ return RealFileName::Remapped {
+ local_path: None,
+ virtual_name: new_path.into_owned(),
+ };
}
if new_path.is_absolute() {
// No remapping has applied to this path and it is absolute,
// so the working directory cannot influence it either, so
// we are done.
- return RealFileName::LocalPath(new_path);
+ return RealFileName::LocalPath(new_path.into_owned());
}
debug_assert!(new_path.is_relative());
RealFileName::Remapped {
// Erase the actual path
local_path: None,
- virtual_name: file_path_abs,
+ virtual_name: file_path_abs.into_owned(),
}
} else {
// No kind of remapping applied to this path, so
// we leave it as it is.
- RealFileName::LocalPath(file_path_abs)
+ RealFileName::LocalPath(file_path_abs.into_owned())
}
}
RealFileName::Remapped {
}
}
}
+
+ /// Attempts to (heuristically) reverse a prefix mapping.
+ ///
+ /// Returns [`Some`] if there is exactly one mapping where the "to" part is
+ /// a prefix of `path` and has at least one non-empty
+ /// [`Normal`](path::Component::Normal) component. The component
+ /// restriction exists to avoid reverse mapping overly generic paths like
+ /// `/` or `.`).
+ ///
+ /// This is a heuristic and not guaranteed to return the actual original
+ /// path! Do not rely on the result unless you have other means to verify
+ /// that the mapping is correct (e.g. by checking the file content hash).
+ #[instrument(level = "debug", skip(self), ret)]
+ fn reverse_map_prefix_heuristically(&self, path: &Path) -> Option<PathBuf> {
+ let mut found = None;
+
+ for (from, to) in self.mapping.iter() {
+ let has_normal_component = to.components().any(|c| match c {
+ path::Component::Normal(s) => !s.is_empty(),
+ _ => false,
+ });
+
+ if !has_normal_component {
+ continue;
+ }
+
+ let Ok(rest) = path.strip_prefix(to) else {
+ continue;
+ };
+
+ if found.is_some() {
+ return None;
+ }
+
+ found = Some(from.join(rest));
+ }
+
+ found
+ }
}
mapping.map_prefix(path(p)).0.to_string_lossy().to_string()
}
+fn reverse_map_prefix(mapping: &FilePathMapping, p: &str) -> Option<String> {
+ mapping.reverse_map_prefix_heuristically(&path(p)).map(|q| q.to_string_lossy().to_string())
+}
+
#[test]
fn path_prefix_remapping() {
// Relative to relative
let working_directory = path("/foo");
let working_directory = RealFileName::Remapped {
local_path: Some(working_directory.clone()),
- virtual_name: mapping.map_prefix(working_directory).0,
+ virtual_name: mapping.map_prefix(working_directory).0.into_owned(),
};
assert_eq!(working_directory.remapped_path_if_available(), path("FOO"));
);
}
+#[test]
+fn path_prefix_remapping_reverse() {
+ // Ignores options without alphanumeric chars.
+ {
+ let mapping =
+ &FilePathMapping::new(vec![(path("abc"), path("/")), (path("def"), path("."))]);
+
+ assert_eq!(reverse_map_prefix(mapping, "/hello.rs"), None);
+ assert_eq!(reverse_map_prefix(mapping, "./hello.rs"), None);
+ }
+
+ // Returns `None` if multiple options match.
+ {
+ let mapping = &FilePathMapping::new(vec![
+ (path("abc"), path("/redacted")),
+ (path("def"), path("/redacted")),
+ ]);
+
+ assert_eq!(reverse_map_prefix(mapping, "/redacted/hello.rs"), None);
+ }
+
+ // Distinct reverse mappings.
+ {
+ let mapping = &FilePathMapping::new(vec![
+ (path("abc"), path("/redacted")),
+ (path("def/ghi"), path("/fake/dir")),
+ ]);
+
+ assert_eq!(
+ reverse_map_prefix(mapping, "/redacted/path/hello.rs"),
+ Some(path_str("abc/path/hello.rs"))
+ );
+ assert_eq!(
+ reverse_map_prefix(mapping, "/fake/dir/hello.rs"),
+ Some(path_str("def/ghi/hello.rs"))
+ );
+ }
+}
+
#[test]
fn test_next_point() {
let sm = SourceMap::new(FilePathMapping::empty());
Capture,
Center,
Clone,
+ Context,
Continue,
Copy,
Count,
console,
const_allocate,
const_async_blocks,
+ const_closures,
const_compare_raw_pointers,
const_constructor,
const_deallocate,
}
/// Returns a suggested template modifier to use for this type and an
- /// example of a register named formatted with it.
+ /// example of a register named formatted with it.
///
/// Such suggestions are useful if a type smaller than the full register
/// size is used and a modifier can be used to point to the subregister of
Target {
// Clang automatically chooses a more specific target based on
- // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
// correctly, we do too.
llvm_target: macos_llvm_target(arch).into(),
pointer_width: 64,
match name {
// Stable
"Rust" | "C" | "cdecl" | "stdcall" | "fastcall" | "aapcs" | "win64" | "sysv64"
- | "system" => Ok(()),
+ | "system" | "efiapi" => Ok(()),
"rust-intrinsic" => Err(AbiDisabled::Unstable {
feature: sym::intrinsics,
explain: "intrinsics are subject to change",
feature: sym::abi_avr_interrupt,
explain: "avr-interrupt and avr-non-blocking-interrupt ABIs are experimental and subject to change",
}),
- "efiapi" => Err(AbiDisabled::Unstable {
- feature: sym::abi_efiapi,
- explain: "efiapi ABI is experimental and subject to change",
- }),
"C-cmse-nonsecure-call" => Err(AbiDisabled::Unstable {
feature: sym::abi_c_cmse_nonsecure_call,
explain: "C-cmse-nonsecure-call ABI is experimental and subject to change",
allow_asm: true,
endian,
linker_flavor: LinkerFlavor::Bpf,
- atomic_cas: true,
+ atomic_cas: false,
dynamic_linking: true,
no_builtins: true,
panic_strategy: PanicStrategy::Abort,
Target {
// Clang automatically chooses a more specific target based on
- // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
// correctly, we do too.
//
// While ld64 doesn't understand i686, LLVM does.
LinkerFlavor::Unix(Cc::Yes),
&[
// The illumos libc contains a stack unwinding implementation, as
- // does libgcc_s. The latter implementation includes several
- // additional symbols that are not always in base libc. To force
+ // does libgcc_s. The latter implementation includes several
+ // additional symbols that are not always in base libc. To force
// the consistent use of just one unwinder, we ensure libc appears
// after libgcc_s in the NEEDED list for the resultant binary by
// ignoring any attempts to add it as a dynamic dependency until the
"-lc",
// LLVM will insert calls to the stack protector functions
// "__stack_chk_fail" and "__stack_chk_guard" into code in native
- // object files. Some platforms include these symbols directly in
+ // object files. Some platforms include these symbols directly in
// libc, but at least historically these have been provided in
// libssp.so on illumos and Solaris systems.
"-lssp",
// cleanup handlers (in C, this would be something along the lines of:
// void register_callback(void (*fn)(void *), void *arg);
// (see src/libstd/sys/unix/fast_thread_local.rs) that is currently
- // missing in illumos. For now at least, we must fallback to using
+ // missing in illumos. For now at least, we must fallback to using
// pthread_{get,set}specific.
//has_thread_local: true,
// FIXME: Currently, rust is invoking cc to link, which ends up
- // causing these to get included twice. We should eventually transition
+ // causing these to get included twice. We should eventually transition
// to having rustc invoke ld directly, in which case these will need to
// be uncommented.
//
- // We want XPG6 behavior from libc and libm. See standards(5)
+ // We want XPG6 behavior from libc and libm. See standards(5)
//pre_link_objects_exe: vec![
// "/usr/lib/amd64/values-Xc.o".into(),
// "/usr/lib/amd64/values-xpg6.o".into(),
/// Search for a JSON file specifying the given target triple.
///
/// If none is found in `$RUST_TARGET_PATH`, look for a file called `target.json` inside the
- /// sysroot under the target-triple's `rustlib` directory. Note that it could also just be a
+ /// sysroot under the target-triple's `rustlib` directory. Note that it could also just be a
/// bare filename already, so also check for that. If one of the hardcoded targets we know
/// about, just return it directly.
///
use crate::abi::Endian;
-use crate::spec::{StackProbeType, Target};
+use crate::spec::{SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_gnu_base::opts();
base.max_atomic_width = Some(64);
base.min_global_align = Some(16);
base.stack_probes = StackProbeType::Inline;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD;
Target {
llvm_target: "s390x-unknown-linux-gnu".into(),
use crate::abi::Endian;
-use crate::spec::{StackProbeType, Target};
+use crate::spec::{SanitizerSet, StackProbeType, Target};
pub fn target() -> Target {
let mut base = super::linux_musl_base::opts();
base.min_global_align = Some(16);
base.static_position_independent_executables = true;
base.stack_probes = StackProbeType::Inline;
+ base.supported_sanitizers =
+ SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::MEMORY | SanitizerSet::THREAD;
Target {
llvm_target: "s390x-unknown-linux-musl".into(),
pointer_width: 64,
data_layout: "E-m:e-i64:64-n32:64-S128".into(),
// Use "sparc64" instead of "sparcv9" here, since the former is already
- // used widely in the source base. If we ever needed ABI
+ // used widely in the source base. If we ever needed ABI
// differentiation from the sparc64, we could, but that would probably
// just be confusing.
arch: "sparc64".into(),
Target {
// Clang automatically chooses a more specific target based on
- // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
+ // MACOSX_DEPLOYMENT_TARGET. To enable cross-language LTO to work
// correctly, we do too.
llvm_target: macos_llvm_target(arch).into(),
pointer_width: 64,
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_query_system = { path = "../rustc_query_system" }
+rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
#![feature(never_type)]
#![feature(result_option_inspect)]
#![feature(type_alias_impl_trait)]
+#![feature(min_specialization)]
#![recursion_limit = "512"] // For rustdoc
#[macro_use]
//! Code shared by trait and projection goals for candidate assembly.
use super::infcx_ext::InferCtxtExt;
-use super::{
- fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
- EvalCtxt, Goal,
-};
+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>;
+
+ 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>;
+}
- pub(super) fn try_insert_candidate(
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+ pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
&mut self,
- source: G::CandidateSource,
- certainty: Certainty,
- ) {
- match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
- Ok(result) => self.candidates.push(Candidate { source, result }),
- Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
+ goal: Goal<'tcx, G>,
+ ) -> Vec<Candidate<'tcx>> {
+ debug_assert_eq!(goal, self.infcx.resolve_vars_if_possible(goal));
+
+ // HACK: `_: Trait` is ambiguous, because it may be satisfied via a builtin rule,
+ // object bound, alias bound, etc. We are unable to determine this until we can at
+ // least structually resolve the type one layer.
+ if goal.predicate.self_ty().is_ty_var() {
+ return vec![Candidate {
+ source: CandidateSource::BuiltinImpl,
+ result: self.make_canonical_response(Certainty::AMBIGUOUS).unwrap(),
+ }];
}
+
+ let mut candidates = Vec::new();
+
+ self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates);
+
+ self.assemble_impl_candidates(goal, &mut candidates);
+
+ self.assemble_builtin_impl_candidates(goal, &mut candidates);
+
+ self.assemble_param_env_candidates(goal, &mut candidates);
+
+ self.assemble_alias_bound_candidates(goal, &mut candidates);
+
+ self.assemble_object_bound_candidates(goal, &mut candidates);
+
+ candidates
}
/// If the self type of a goal is a projection, computing the relevant candidates is difficult.
/// To deal with this, we first try to normalize the self type and add the candidates for the normalized
/// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
/// this case as projections as self types add `
- fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) {
- let tcx = self.cx.tcx;
+ fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
+ &mut self,
+ goal: Goal<'tcx, G>,
+ candidates: &mut Vec<Candidate<'tcx>>,
+ ) {
+ let tcx = self.tcx();
// FIXME: We also have to normalize opaque types, not sure where to best fit that in.
let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
return
term: normalized_ty.into(),
}),
);
- let normalization_certainty =
- match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
- Ok((_, certainty)) => certainty,
- Err(NoSolution) => return,
- };
+ let normalization_certainty = match self.evaluate_goal(normalizes_to_goal) {
+ Ok((_, certainty)) => certainty,
+ Err(NoSolution) => return,
+ };
+ let normalized_ty = self.infcx.resolve_vars_if_possible(normalized_ty);
// NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
- // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
+ // This doesn't work as long as we use `CandidateSource` in winnowing.
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
- let mut orig_values = OriginalQueryValues::default();
- let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
- let normalized_candidates =
- AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal);
-
- // Map each candidate from being canonical wrt the current inference context to being
- // canonical wrt the caller.
- for Candidate { source, result } in normalized_candidates {
- self.infcx.probe(|_| {
- let candidate_certainty = fixme_instantiate_canonical_query_response(
- &self.infcx,
- &orig_values,
- result,
- );
-
- // FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
- //
- // If we have an ambiguous candidate it hides that normalization
- // caused an overflow which may cause issues.
- self.try_insert_candidate(
- source,
- normalization_certainty.unify_and(candidate_certainty),
- )
- })
+ // 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 {
+ 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::Never
+ | ty::Tuple(_)
+ | ty::Param(_)
+ | ty::Placeholder(..)
+ | ty::Infer(_)
+ | ty::Error(_) => return,
+ ty::Bound(..) => bug!("unexpected bound type: {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::Never
+ | ty::Tuple(_)
+ | ty::Param(_)
+ | ty::Placeholder(..)
+ | ty::Infer(_)
+ | ty::Error(_) => return,
+ ty::Bound(..) => bug!("unexpected bound type: {goal:?}"),
+ ty::Dynamic(bounds, ..) => bounds,
+ };
+
+ let tcx = self.tcx();
+ for assumption in
+ elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
+ {
+ match G::consider_assumption(self, goal, assumption.predicate) {
+ Ok(result) => {
+ candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
+ }
+ Err(NoSolution) => (),
+ }
+ }
+ }
}
+++ /dev/null
-//! This module both handles the global cache which stores "finished" goals,
-//! and the provisional cache which contains partially computed goals.
-//!
-//! The provisional cache is necessary when dealing with coinductive cycles.
-//!
-//! For more information about the provisional cache and coinduction in general,
-//! check out the relevant section of the rustc-dev-guide.
-//!
-//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
-//! before then or if I still haven't done that before January 2023.
-use super::overflow::OverflowData;
-use super::CanonicalGoal;
-use super::{EvalCtxt, QueryResult};
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty::TyCtxt;
-use std::{cmp::Ordering, collections::hash_map::Entry};
-
-#[derive(Debug, Clone)]
-struct ProvisionalEntry<'tcx> {
- // In case we have a coinductive cycle, this is the
- // the currently least restrictive result of this goal.
- response: QueryResult<'tcx>,
- // The lowest element on the stack on which this result
- // relies on. Starts out as just being the depth at which
- // we've proven this obligation, but gets lowered to the
- // depth of another goal if we rely on it in a cycle.
- depth: usize,
-}
-
-struct StackElem<'tcx> {
- goal: CanonicalGoal<'tcx>,
- has_been_used: bool,
-}
-
-/// The cache used for goals which are currently in progress or which depend
-/// on in progress results.
-///
-/// Once we're done with a goal we can store it in the global trait solver
-/// cache of the `TyCtxt`. For goals which we're currently proving, or which
-/// have only been proven via a coinductive cycle using a goal still on our stack
-/// we have to use this separate data structure.
-///
-/// The current data structure is not perfect, so there may still be room for
-/// improvement here. We have the following requirements:
-///
-/// ## Is there is a provisional entry for the given goal:
-///
-/// ```ignore (for syntax highlighting)
-/// self.entries.get(goal)
-/// ```
-///
-/// ## Get all goals on the stack involved in a cycle:
-///
-/// ```ignore (for syntax highlighting)
-/// let entry = self.entries.get(goal).unwrap();
-/// let involved_goals = self.stack.iter().skip(entry.depth);
-/// ```
-///
-/// ## Capping the depth of all entries
-///
-/// Needed whenever we encounter a cycle. The current implementation always
-/// iterates over all entries instead of only the ones with a larger depth.
-/// Changing this may result in notable performance improvements.
-///
-/// ```ignore (for syntax highlighting)
-/// let cycle_depth = self.entries.get(goal).unwrap().depth;
-/// for e in &mut self.entries {
-/// e.depth = e.depth.min(cycle_depth);
-/// }
-/// ```
-///
-/// ## Checking whether we have to rerun the current goal
-///
-/// A goal has to be rerun if its provisional result was used in a cycle
-/// and that result is different from its final result. We update
-/// [StackElem::has_been_used] for the deepest stack element involved in a cycle.
-///
-/// ## Moving all finished goals into the global cache
-///
-/// If `stack_elem.has_been_used` is true, iterate over all entries, moving the ones
-/// with equal depth. If not, simply move this single entry.
-pub(super) struct ProvisionalCache<'tcx> {
- stack: Vec<StackElem<'tcx>>,
- entries: FxHashMap<CanonicalGoal<'tcx>, ProvisionalEntry<'tcx>>,
-}
-
-impl<'tcx> ProvisionalCache<'tcx> {
- pub(super) fn empty() -> ProvisionalCache<'tcx> {
- ProvisionalCache { stack: Vec::new(), entries: Default::default() }
- }
-
- pub(super) fn current_depth(&self) -> usize {
- self.stack.len()
- }
-}
-
-impl<'tcx> EvalCtxt<'tcx> {
- /// Tries putting the new goal on the stack, returning an error if it is already cached.
- ///
- /// This correctly updates the provisional cache if there is a cycle.
- pub(super) fn try_push_stack(
- &mut self,
- goal: CanonicalGoal<'tcx>,
- ) -> Result<(), QueryResult<'tcx>> {
- // FIXME: start by checking the global cache
-
- // Look at the provisional cache to check for cycles.
- let cache = &mut self.provisional_cache;
- match cache.entries.entry(goal) {
- // No entry, simply push this goal on the stack after dealing with overflow.
- Entry::Vacant(v) => {
- if self.overflow_data.has_overflow(cache.stack.len()) {
- return Err(self.deal_with_overflow());
- }
-
- v.insert(ProvisionalEntry {
- response: fixme_response_yes_no_constraints(),
- depth: cache.stack.len(),
- });
- cache.stack.push(StackElem { goal, has_been_used: false });
- Ok(())
- }
- // We have a nested goal which relies on a goal `root` deeper in the stack.
- //
- // We first store that we may have to rerun `evaluate_goal` for `root` in case the
- // provisional response is not equal to the final response. We also update the depth
- // of all goals which recursively depend on our current goal to depend on `root`
- // instead.
- //
- // Finally we can return either the provisional response for that goal if we have a
- // coinductive cycle or an ambiguous result if the cycle is inductive.
- Entry::Occupied(entry) => {
- // FIXME: `ProvisionalEntry` should be `Copy`.
- let entry = entry.get().clone();
- cache.stack[entry.depth].has_been_used = true;
- for provisional_entry in cache.entries.values_mut() {
- provisional_entry.depth = provisional_entry.depth.min(entry.depth);
- }
-
- // NOTE: The goals on the stack aren't the only goals involved in this cycle.
- // We can also depend on goals which aren't part of the stack but coinductively
- // depend on the stack themselves. We already checked whether all the goals
- // between these goals and their root on the stack. This means that as long as
- // each goal in a cycle is checked for coinductivity by itself simply checking
- // the stack is enough.
- if cache.stack[entry.depth..]
- .iter()
- .all(|g| g.goal.value.predicate.is_coinductive(self.tcx))
- {
- Err(entry.response)
- } else {
- Err(fixme_response_maybe_no_constraints())
- }
- }
- }
- }
-
- /// We cannot simply store the result of [EvalCtxt::compute_goal] as we have to deal with
- /// coinductive cycles.
- ///
- /// When we encounter a coinductive cycle, we have to prove the final result of that cycle
- /// while we are still computing that result. Because of this we continously recompute the
- /// cycle until the result of the previous iteration is equal to the final result, at which
- /// point we are done.
- ///
- /// This function returns `true` if we were able to finalize the goal and `false` if it has
- /// updated the provisional cache and we have to recompute the current goal.
- ///
- /// FIXME: Refer to the rustc-dev-guide entry once it exists.
- pub(super) fn try_finalize_goal(
- &mut self,
- actual_goal: CanonicalGoal<'tcx>,
- response: QueryResult<'tcx>,
- ) -> bool {
- let cache = &mut self.provisional_cache;
- let StackElem { goal, has_been_used } = cache.stack.pop().unwrap();
- assert_eq!(goal, actual_goal);
-
- let provisional_entry = cache.entries.get_mut(&goal).unwrap();
- // Check whether the current stack entry is the root of a cycle.
- //
- // If so, we either move all participants of that cycle to the global cache
- // or, in case the provisional response used in the cycle is not equal to the
- // final response, have to recompute the goal after updating the provisional
- // response to the final response of this iteration.
- if has_been_used {
- if provisional_entry.response == response {
- // We simply drop all entries according to an immutable condition, so
- // query instability is not a concern here.
- #[allow(rustc::potential_query_instability)]
- cache.entries.retain(|goal, entry| match entry.depth.cmp(&cache.stack.len()) {
- Ordering::Less => true,
- Ordering::Equal => {
- Self::try_move_finished_goal_to_global_cache(
- self.tcx,
- &mut self.overflow_data,
- &cache.stack,
- // FIXME: these should be `Copy` :(
- goal.clone(),
- entry.response.clone(),
- );
- false
- }
- Ordering::Greater => bug!("entry with greater depth than the current leaf"),
- });
-
- true
- } else {
- provisional_entry.response = response;
- cache.stack.push(StackElem { goal, has_been_used: false });
- false
- }
- } else {
- Self::try_move_finished_goal_to_global_cache(
- self.tcx,
- &mut self.overflow_data,
- &cache.stack,
- goal,
- response,
- );
- cache.entries.remove(&goal);
- true
- }
- }
-
- fn try_move_finished_goal_to_global_cache(
- tcx: TyCtxt<'tcx>,
- overflow_data: &mut OverflowData,
- stack: &[StackElem<'tcx>],
- goal: CanonicalGoal<'tcx>,
- response: QueryResult<'tcx>,
- ) {
- // We move goals to the global cache if we either did not hit an overflow or if it's
- // the root goal as that will now always hit the same overflow limit.
- //
- // NOTE: We cannot move any non-root goals to the global cache even if their final result
- // isn't impacted by the overflow as that goal still has unstable query dependencies
- // because it didn't go its full depth.
- //
- // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though.
- // Tracking that info correctly isn't trivial, so I haven't implemented it for now.
- let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty();
- if should_cache_globally {
- // FIXME: move the provisional entry to the global cache.
- let _ = (tcx, goal, response);
- }
- }
-}
-
-fn fixme_response_yes_no_constraints<'tcx>() -> QueryResult<'tcx> {
- unimplemented!()
-}
-
-fn fixme_response_maybe_no_constraints<'tcx>() -> QueryResult<'tcx> {
- unimplemented!()
-}
use std::mem;
-use rustc_data_structures::fx::FxHashMap;
+use super::{Certainty, InferCtxtEvalExt};
use rustc_infer::{
infer::InferCtxt,
traits::{
SelectionError, TraitEngine,
},
};
-use rustc_middle::ty;
-
-use super::{Certainty, EvalCtxt};
/// A trait engine using the new trait solver.
///
.drain(..)
.map(|obligation| FulfillmentError {
obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeSelectionError(SelectionError::Unimplemented),
+ code: FulfillmentErrorCode::CodeAmbiguity,
root_obligation: obligation,
})
.collect()
let mut errors = Vec::new();
for i in 0.. {
if !infcx.tcx.recursion_limit().value_within_limit(i) {
- unimplemented!("overflow")
+ unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
}
let mut has_changed = false;
for obligation in mem::take(&mut self.obligations) {
- let mut cx = EvalCtxt::new(infcx.tcx);
- let (changed, certainty) = match cx.evaluate_goal(infcx, obligation.clone().into())
- {
+ let goal = obligation.clone().into();
+ let (changed, certainty) = match infcx.evaluate_root_goal(goal) {
Ok(result) => result,
Err(NoSolution) => {
errors.push(FulfillmentError {
obligation: obligation.clone(),
- code: FulfillmentErrorCode::CodeAmbiguity,
+ code: FulfillmentErrorCode::CodeSelectionError(
+ SelectionError::Unimplemented,
+ ),
root_obligation: obligation,
});
continue;
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.clone()
}
-
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
- unimplemented!("Should be moved out of `TraitEngine`")
- }
}
-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_infer::infer::canonical::OriginalQueryValues;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+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::{Canonical, CanonicalVarValues};
+use rustc_middle::infer::canonical::Certainty as OldCertainty;
use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate};
+use rustc_middle::ty::{
+ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
+};
use rustc_span::DUMMY_SP;
-use self::infcx_ext::InferCtxtExt;
+use crate::traits::ObligationCause;
mod assembly;
-mod cache;
mod fulfill;
mod infcx_ext;
-mod overflow;
mod project_goals;
+mod search_graph;
mod trait_goals;
pub use fulfill::FulfillmentCtxt;
}
impl Certainty {
+ pub const AMBIGUOUS: Certainty = Certainty::Maybe(MaybeCause::Ambiguity);
+
/// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
/// use this function to unify the certainty of these goals
pub fn unify_and(self, other: Certainty) -> Certainty {
}
/// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable, Default)]
pub struct ExternalConstraints<'tcx> {
// FIXME: implement this.
regions: (),
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)
+ let mut search_graph = search_graph::SearchGraph::new(self);
+ EvalCtxt::evaluate_canonical_goal(self, &mut search_graph, goal)
}
}
-struct EvalCtxt<'tcx> {
- tcx: TyCtxt<'tcx>,
-
- provisional_cache: cache::ProvisionalCache<'tcx>,
- overflow_data: overflow::OverflowData,
+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> EvalCtxt<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> EvalCtxt<'tcx> {
- EvalCtxt {
- tcx,
- provisional_cache: cache::ProvisionalCache::empty(),
- overflow_data: overflow::OverflowData::new(tcx),
+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
}
+}
- /// Recursively evaluates `goal`, returning whether any inference vars have
- /// been constrained and the certainty of the result.
- fn evaluate_goal(
- &mut self,
- infcx: &InferCtxt<'tcx>,
- goal: Goal<'tcx, ty::Predicate<'tcx>>,
- ) -> Result<(bool, Certainty), NoSolution> {
- let mut orig_values = OriginalQueryValues::default();
- let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
- let canonical_response = self.evaluate_canonical_goal(canonical_goal)?;
- Ok((
- true, // FIXME: check whether `var_values` are an identity substitution.
- fixme_instantiate_canonical_query_response(infcx, &orig_values, canonical_response),
- ))
+struct EvalCtxt<'a, 'tcx> {
+ infcx: &'a InferCtxt<'tcx>,
+ var_values: CanonicalVarValues<'tcx>,
+
+ search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+}
+
+impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
}
- fn evaluate_canonical_goal(&mut self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
- match self.try_push_stack(goal) {
+ #[instrument(level = "debug", skip(tcx, search_graph), ret)]
+ fn evaluate_canonical_goal(
+ tcx: TyCtxt<'tcx>,
+ search_graph: &'a mut search_graph::SearchGraph<'tcx>,
+ canonical_goal: CanonicalGoal<'tcx>,
+ ) -> QueryResult<'tcx> {
+ match search_graph.try_push_stack(tcx, canonical_goal) {
Ok(()) => {}
// Our goal is already on the stack, eager return.
Err(response) => return response,
//
// FIXME: Similar to `evaluate_all`, this has to check for overflow.
loop {
- let result = self.compute_goal(goal);
+ let (ref infcx, goal, var_values) =
+ tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
+ let mut ecx = EvalCtxt { infcx, var_values, search_graph };
+ let result = ecx.compute_goal(goal);
// FIXME: `Response` should be `Copy`
- if self.try_finalize_goal(goal, result.clone()) {
+ if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
return result;
}
}
}
- fn compute_goal(&mut self, canonical_goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
- // WARNING: We're looking at a canonical value without instantiating it here.
- //
- // We have to be incredibly careful to not change the order of bound variables or
- // remove any. As we go from `Goal<'tcx, Predicate>` to `Goal` with the variants
- // of `PredicateKind` this is the case and it is and faster than instantiating and
- // recanonicalizing.
- let Goal { param_env, predicate } = canonical_goal.value;
- if let Some(kind) = predicate.kind().no_bound_vars() {
+ fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
+ let external_constraints = take_external_constraints(self.infcx)?;
+
+ Ok(self.infcx.canonicalize_response(Response {
+ var_values: self.var_values.clone(),
+ external_constraints,
+ certainty,
+ }))
+ }
+
+ /// Recursively evaluates `goal`, returning whether any inference vars have
+ /// been constrained and the certainty of the result.
+ fn evaluate_goal(
+ &mut self,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ ) -> Result<(bool, Certainty), NoSolution> {
+ let mut orig_values = OriginalQueryValues::default();
+ let canonical_goal = self.infcx.canonicalize_query(goal, &mut orig_values);
+ let canonical_response =
+ EvalCtxt::evaluate_canonical_goal(self.tcx(), self.search_graph, canonical_goal)?;
+ Ok((
+ !canonical_response.value.var_values.is_identity(),
+ instantiate_canonical_query_response(self.infcx, &orig_values, canonical_response),
+ ))
+ }
+
+ fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
+ let Goal { param_env, predicate } = goal;
+ let kind = predicate.kind();
+ if let Some(kind) = kind.no_bound_vars() {
match kind {
- ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => self.compute_trait_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
- ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => self
- .compute_projection_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
- ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => self
- .compute_type_outlives_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
- ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => self
- .compute_region_outlives_goal(
- canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
- ),
+ ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => {
+ self.compute_trait_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => {
+ self.compute_projection_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => {
+ self.compute_type_outlives_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => {
+ self.compute_region_outlives_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Subtype(predicate) => {
+ self.compute_subtype_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::Coerce(predicate) => {
+ self.compute_coerce_goal(Goal { param_env, predicate })
+ }
+ ty::PredicateKind::ClosureKind(def_id, substs, kind) => self
+ .compute_closure_kind_goal(Goal {
+ param_env,
+ predicate: (def_id, substs, kind),
+ }),
+ ty::PredicateKind::Ambiguous => self.make_canonical_response(Certainty::AMBIGUOUS),
// FIXME: implement these predicates :)
ty::PredicateKind::WellFormed(_)
| ty::PredicateKind::ObjectSafe(_)
- | ty::PredicateKind::ClosureKind(_, _, _)
- | ty::PredicateKind::Subtype(_)
- | ty::PredicateKind::Coerce(_)
| ty::PredicateKind::ConstEvaluatable(_)
- | ty::PredicateKind::ConstEquate(_, _)
- | ty::PredicateKind::TypeWellFormedFromEnv(_)
- | ty::PredicateKind::Ambiguous => unimplemented!(),
+ | ty::PredicateKind::ConstEquate(_, _) => {
+ self.make_canonical_response(Certainty::Yes)
+ }
+ ty::PredicateKind::TypeWellFormedFromEnv(..) => {
+ bug!("TypeWellFormedFromEnv is only used for Chalk")
+ }
}
} else {
- let (infcx, goal, var_values) =
- self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
- let kind = infcx.replace_bound_vars_with_placeholders(goal.predicate.kind());
- let goal = goal.with(self.tcx, ty::Binder::dummy(kind));
- let (_, certainty) = self.evaluate_goal(&infcx, goal)?;
- infcx.make_canonical_response(var_values, certainty)
+ let kind = self.infcx.replace_bound_vars_with_placeholders(kind);
+ let goal = goal.with(self.tcx(), ty::Binder::dummy(kind));
+ let (_, certainty) = self.evaluate_goal(goal)?;
+ self.make_canonical_response(certainty)
}
}
fn compute_type_outlives_goal(
&mut self,
- _goal: CanonicalGoal<'tcx, TypeOutlivesPredicate<'tcx>>,
+ _goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- todo!()
+ self.make_canonical_response(Certainty::Yes)
}
fn compute_region_outlives_goal(
&mut self,
- _goal: CanonicalGoal<'tcx, RegionOutlivesPredicate<'tcx>>,
+ _goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ self.make_canonical_response(Certainty::Yes)
+ }
+
+ fn compute_coerce_goal(
+ &mut self,
+ goal: Goal<'tcx, CoercePredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ self.compute_subtype_goal(Goal {
+ param_env: goal.param_env,
+ predicate: SubtypePredicate {
+ a_is_expected: false,
+ a: goal.predicate.a,
+ b: goal.predicate.b,
+ },
+ })
+ }
+
+ fn compute_subtype_goal(
+ &mut self,
+ goal: Goal<'tcx, SubtypePredicate<'tcx>>,
) -> QueryResult<'tcx> {
- todo!()
+ 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)
+ }
}
}
-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))
+ }
}
-fn fixme_instantiate_canonical_query_response<'tcx>(
- _: &InferCtxt<'tcx>,
- _: &OriginalQueryValues<'tcx>,
- _: CanonicalResponse<'tcx>,
+#[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>(
+ infcx: &InferCtxt<'tcx>,
+ original_values: &OriginalQueryValues<'tcx>,
+ response: CanonicalResponse<'tcx>,
) -> Certainty {
- unimplemented!()
+ let Ok(InferOk { value, obligations }) = infcx
+ .instantiate_query_response_and_region_obligations(
+ &ObligationCause::dummy(),
+ ty::ParamEnv::empty(),
+ original_values,
+ &response.unchecked_map(|resp| QueryResponse {
+ var_values: resp.var_values,
+ region_constraints: QueryRegionConstraints::default(),
+ certainty: match resp.certainty {
+ Certainty::Yes => OldCertainty::Proven,
+ Certainty::Maybe(_) => OldCertainty::Ambiguous,
+ },
+ opaque_types: resp.external_constraints.opaque_types,
+ value: resp.certainty,
+ }),
+ ) else { bug!(); };
+ assert!(obligations.is_empty());
+ value
+}
+
+pub(super) fn response_no_constraints<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ goal: Canonical<'tcx, impl Sized>,
+ certainty: Certainty,
+) -> QueryResult<'tcx> {
+ 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::traits::query::NoSolution;
-use rustc_middle::ty::TyCtxt;
-use rustc_session::Limit;
-
-use super::{Certainty, EvalCtxt, MaybeCause, QueryResult};
-
-/// When detecting a solver overflow, we return ambiguity. Overflow can be
-/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
-///
-/// This is in issue in case of exponential blowup, e.g. if each goal on the stack
-/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit
-/// used by the solver when hitting the default limit for the first time.
-///
-/// FIXME: Get tests where always using the `default_limit` results in a hang and refer
-/// to them here. We can also improve the overflow strategy if necessary.
-pub(super) struct OverflowData {
- default_limit: Limit,
- current_limit: Limit,
- /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals.
- ///
- /// Because of this each iteration also increases the depth in addition to the stack
- /// depth.
- additional_depth: usize,
-}
-
-impl OverflowData {
- pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData {
- let default_limit = tcx.recursion_limit();
- OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 }
- }
-
- #[inline]
- pub(super) fn did_overflow(&self) -> bool {
- self.default_limit.0 != self.current_limit.0
- }
-
- #[inline]
- pub(super) fn has_overflow(&self, depth: usize) -> bool {
- !self.current_limit.value_within_limit(depth + self.additional_depth)
- }
-
- /// Updating the current limit when hitting overflow.
- fn deal_with_overflow(&mut self) {
- // When first hitting overflow we reduce the overflow limit
- // for all future goals to prevent hangs if there's an exponental
- // blowup.
- self.current_limit.0 = self.default_limit.0 / 8;
- }
-}
-
-impl<'tcx> EvalCtxt<'tcx> {
- pub(super) fn deal_with_overflow(&mut self) -> QueryResult<'tcx> {
- self.overflow_data.deal_with_overflow();
- fixme_response_overflow_no_constraints()
- }
-
- /// A `while`-loop which tracks overflow.
- pub(super) fn repeat_while_none(
- &mut self,
- mut loop_body: impl FnMut(&mut Self) -> Option<Result<Certainty, NoSolution>>,
- ) -> Result<Certainty, NoSolution> {
- let start_depth = self.overflow_data.additional_depth;
- let depth = self.provisional_cache.current_depth();
- while !self.overflow_data.has_overflow(depth) {
- if let Some(result) = loop_body(self) {
- self.overflow_data.additional_depth = start_depth;
- return result;
- }
-
- self.overflow_data.additional_depth += 1;
- }
- self.overflow_data.additional_depth = start_depth;
- self.overflow_data.deal_with_overflow();
- Ok(Certainty::Maybe(MaybeCause::Overflow))
- }
-}
-
-fn fixme_response_overflow_no_constraints<'tcx>() -> QueryResult<'tcx> {
- unimplemented!()
-}
use crate::traits::{specialization_graph, translate_substs};
-use super::assembly::{self, AssemblyCtxt};
-use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
+use super::assembly::{self, Candidate, CandidateSource};
+use super::infcx_ext::InferCtxtExt;
+use super::trait_goals::structural_traits;
+use super::{Certainty, EvalCtxt, Goal, QueryResult};
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_hir::LangItem;
+use rustc_infer::infer::InferCtxt;
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::specialization_graph::LeafDef;
-use rustc_infer::traits::{ObligationCause, Reveal};
+use rustc_infer::traits::Reveal;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::ProjectionPredicate;
-use rustc_middle::ty::TypeVisitable;
use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{ProjectionPredicate, TypeSuperVisitable, TypeVisitor};
+use rustc_middle::ty::{ToPredicate, TypeVisitable};
use rustc_span::DUMMY_SP;
use std::iter;
+use std::ops::ControlFlow;
-#[allow(dead_code)] // FIXME: implement and use all variants.
-#[derive(Debug, Clone, Copy)]
-pub(super) enum CandidateSource {
- Impl(DefId),
- ParamEnv(usize),
- Builtin,
-}
-
-type Candidate<'tcx> = assembly::Candidate<'tcx, ProjectionPredicate<'tcx>>;
-
-impl<'tcx> EvalCtxt<'tcx> {
+impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn compute_projection_goal(
&mut self,
- goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>,
+ goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
) -> QueryResult<'tcx> {
- let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
- self.merge_project_candidates(candidates)
+ // To only compute normalization once for each projection we only
+ // normalize if the expected term is an unconstrained inference variable.
+ //
+ // E.g. for `<T as Trait>::Assoc = u32` we recursively compute the goal
+ // `exists<U> <T as Trait>::Assoc = U` and then take the resulting type for
+ // `U` and equate it with `u32`. This means that we don't need a separate
+ // projection cache in the solver.
+ if self.term_is_fully_unconstrained(goal) {
+ let candidates = self.assemble_and_evaluate_candidates(goal);
+ self.merge_project_candidates(candidates)
+ } else {
+ let predicate = goal.predicate;
+ let unconstrained_rhs = match predicate.term.unpack() {
+ ty::TermKind::Ty(_) => self.infcx.next_ty_infer().into(),
+ ty::TermKind::Const(ct) => self.infcx.next_const_infer(ct.ty()).into(),
+ };
+ let unconstrained_predicate = ty::Clause::Projection(ProjectionPredicate {
+ projection_ty: goal.predicate.projection_ty,
+ term: unconstrained_rhs,
+ });
+ let (_has_changed, normalize_certainty) =
+ self.evaluate_goal(goal.with(self.tcx(), unconstrained_predicate))?;
+
+ let nested_eq_goals =
+ self.infcx.eq(goal.param_env, unconstrained_rhs, predicate.term)?;
+ let eval_certainty = self.evaluate_all(nested_eq_goals)?;
+ self.make_canonical_response(normalize_certainty.unify_and(eval_certainty))
+ }
+ }
+
+ /// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
+ ///
+ /// This is the case if the `term` is an inference variable in the innermost universe
+ /// and does not occur in any other part of the predicate.
+ fn term_is_fully_unconstrained(&self, goal: Goal<'tcx, ProjectionPredicate<'tcx>>) -> bool {
+ let infcx = self.infcx;
+ let term_is_infer = match goal.predicate.term.unpack() {
+ ty::TermKind::Ty(ty) => {
+ if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
+ match infcx.probe_ty_var(vid) {
+ Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"),
+ Err(universe) => universe == infcx.universe(),
+ }
+ } else {
+ false
+ }
+ }
+ ty::TermKind::Const(ct) => {
+ if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
+ match self.infcx.probe_const_var(vid) {
+ Ok(value) => bug!("resolved var in query: {goal:?} {value:?}"),
+ Err(universe) => universe == infcx.universe(),
+ }
+ } else {
+ false
+ }
+ }
+ };
+
+ // Guard against `<T as Trait<?0>>::Assoc = ?0>`.
+ struct ContainsTerm<'tcx> {
+ term: ty::Term<'tcx>,
+ }
+ impl<'tcx> TypeVisitor<'tcx> for ContainsTerm<'tcx> {
+ type BreakTy = ();
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if t.needs_infer() {
+ if ty::Term::from(t) == self.term {
+ ControlFlow::BREAK
+ } else {
+ t.super_visit_with(self)
+ }
+ } else {
+ ControlFlow::CONTINUE
+ }
+ }
+
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if c.needs_infer() {
+ if ty::Term::from(c) == self.term {
+ ControlFlow::BREAK
+ } else {
+ c.super_visit_with(self)
+ }
+ } else {
+ ControlFlow::CONTINUE
+ }
+ }
+ }
+
+ let mut visitor = ContainsTerm { term: goal.predicate.term };
+
+ term_is_infer
+ && goal.predicate.projection_ty.visit_with(&mut visitor).is_continue()
+ && goal.param_env.visit_with(&mut visitor).is_continue()
}
fn merge_project_candidates(
match (candidate.source, other.source) {
(CandidateSource::Impl(_), _)
| (CandidateSource::ParamEnv(_), _)
- | (CandidateSource::Builtin, _) => unimplemented!(),
+ | (CandidateSource::BuiltinImpl, _)
+ | (CandidateSource::AliasBound(_), _) => unimplemented!(),
}
}
}
impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
- type CandidateSource = CandidateSource;
-
fn self_ty(self) -> Ty<'tcx> {
self.self_ty()
}
}
fn consider_impl_candidate(
- acx: &mut AssemblyCtxt<'_, 'tcx, ProjectionPredicate<'tcx>>,
+ ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
impl_def_id: DefId,
- ) {
- let tcx = acx.cx.tcx;
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
+
let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
- let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs)
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
{
- return;
+ return Err(NoSolution);
}
- acx.infcx.probe(|_| {
- let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ ecx.infcx.probe(|_| {
+ let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
- let Ok(InferOk { obligations, .. }) = acx
- .infcx
- .at(&ObligationCause::dummy(), goal.param_env)
- .define_opaque_types(false)
- .eq(goal_trait_ref, impl_trait_ref)
- .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
- else {
- return
- };
+ let mut nested_goals = ecx.infcx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
.instantiate(tcx, impl_substs)
.into_iter()
.map(|pred| goal.with(tcx, pred));
- let nested_goals = obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect();
- let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+ nested_goals.extend(where_clause_bounds);
+ let trait_ref_certainty = ecx.evaluate_all(nested_goals)?;
+ // In case the associated item is hidden due to specialization, we have to
+ // return ambiguity this would otherwise be incomplete, resulting in
+ // unsoundness during coherence (#105782).
let Some(assoc_def) = fetch_eligible_assoc_item_def(
- acx.infcx,
+ ecx.infcx,
goal.param_env,
goal_trait_ref,
goal.predicate.def_id(),
impl_def_id
- ) else {
- return
+ )? else {
+ return ecx.make_canonical_response(trait_ref_certainty.unify_and(Certainty::AMBIGUOUS));
};
if !assoc_def.item.defaultness(tcx).has_value() {
let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
tcx,
goal_trait_ref.def_id,
- impl_trait_ref.substs,
+ impl_substs,
);
let substs = translate_substs(
- acx.infcx,
+ ecx.infcx,
goal.param_env,
impl_def_id,
impl_substs_with_gat,
let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst);
let ty = tcx.bound_type_of(assoc_def.item.def_id);
let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
- let identity_substs = ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
+ let identity_substs =
+ ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
let did = ty::WithOptConstParam::unknown(assoc_def.item.def_id);
let kind =
ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
ty.map_bound(|ty| ty.into())
};
- let Ok(InferOk { obligations, .. }) = acx
+ // The term of our goal should be fully unconstrained, so this should never fail.
+ //
+ // It can however be ambiguous when the resolved type is a projection.
+ let nested_goals = ecx
.infcx
- .at(&ObligationCause::dummy(), goal.param_env)
- .define_opaque_types(false)
- .eq(goal.predicate.term, term.subst(tcx, substs))
- .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
- else {
- return
- };
+ .eq(goal.param_env, goal.predicate.term, term.subst(tcx, substs))
+ .expect("failed to unify with unconstrained term");
+ let rhs_certainty =
+ ecx.evaluate_all(nested_goals).expect("failed to unify with unconstrained term");
+
+ ecx.make_canonical_response(trait_ref_certainty.unify_and(rhs_certainty))
+ })
+ }
- 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)?;
+
+ // 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::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::Infer(ty::TyVar(..)) | 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::FreshTy(..) | ty::FreshIntTy(..) | ty::FreshFloatTy(..))
+ | ty::Bound(..) => bug!(
+ "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
+ goal.predicate.self_ty()
+ ),
+ };
- let certainty = trait_ref_certainty.unify_and(rhs_certainty);
- acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ 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)
})
}
}
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.bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
.any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
{
- return;
+ return Err(NoSolution);
}
- acx.infcx.probe(|_| {
- let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ ecx.infcx.probe(|_| {
+ let impl_substs = ecx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
- let Ok(InferOk { obligations, .. }) = acx
- .infcx
- .at(&ObligationCause::dummy(), goal.param_env)
- .define_opaque_types(false)
- .eq(goal.predicate.trait_ref, impl_trait_ref)
- .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
- else {
- return
- };
+ let mut nested_goals =
+ ecx.infcx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
let where_clause_bounds = tcx
.predicates_of(impl_def_id)
.instantiate(tcx, impl_substs)
.predicates
.into_iter()
.map(|pred| goal.with(tcx, pred));
+ nested_goals.extend(where_clause_bounds);
+ ecx.evaluate_all_and_make_canonical_response(nested_goals)
+ })
+ }
+
+ 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 nested_goals =
- obligations.into_iter().map(|o| o.into()).chain(where_clause_bounds).collect();
+ fn consider_trait_alias_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let tcx = ecx.tcx();
- let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
- acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ ecx.infcx.probe(|_| {
+ let nested_obligations = tcx
+ .predicates_of(goal.predicate.def_id())
+ .instantiate(tcx, goal.predicate.trait_ref.substs);
+ ecx.evaluate_all_and_make_canonical_response(
+ nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)).collect(),
+ )
})
}
+
+ fn consider_builtin_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.probe_and_evaluate_goal_for_constituent_tys(
+ goal,
+ structural_traits::instantiate_constituent_tys_for_sized_trait,
+ )
+ }
+
+ fn consider_builtin_copy_clone_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.probe_and_evaluate_goal_for_constituent_tys(
+ goal,
+ structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
+ )
+ }
+
+ fn consider_builtin_pointer_sized_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ if goal.predicate.self_ty().has_non_region_infer() {
+ return ecx.make_canonical_response(Certainty::AMBIGUOUS);
+ }
+
+ let tcx = ecx.tcx();
+ let self_ty = tcx.erase_regions(goal.predicate.self_ty());
+
+ if let Ok(layout) = tcx.layout_of(goal.param_env.and(self_ty))
+ && let usize_layout = tcx.layout_of(ty::ParamEnv::empty().and(tcx.types.usize)).unwrap().layout
+ && layout.layout.size() == usize_layout.size()
+ && layout.layout.align().abi == usize_layout.align().abi
+ {
+ // FIXME: We could make this faster by making a no-constraints response
+ ecx.make_canonical_response(Certainty::Yes)
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_builtin_fn_trait_candidates(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ goal_kind: ty::ClosureKind,
+ ) -> QueryResult<'tcx> {
+ if let Some(tupled_inputs_and_output) =
+ structural_traits::extract_tupled_inputs_and_output_from_callable(
+ ecx.tcx(),
+ goal.predicate.self_ty(),
+ goal_kind,
+ )?
+ {
+ let pred = tupled_inputs_and_output
+ .map_bound(|(inputs, _)| {
+ ecx.tcx()
+ .mk_trait_ref(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
+ })
+ .to_predicate(ecx.tcx());
+ Self::consider_assumption(ecx, goal, pred)
+ } else {
+ ecx.make_canonical_response(Certainty::AMBIGUOUS)
+ }
+ }
+
+ fn consider_builtin_tuple_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
+ ecx.make_canonical_response(Certainty::Yes)
+ } else {
+ Err(NoSolution)
+ }
+ }
+
+ fn consider_builtin_pointee_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ _goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ ecx.make_canonical_response(Certainty::Yes)
+ }
}
-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::Placeholder(..)
+ | ty::Dynamic(..)
+ | ty::Param(..)
+ | ty::Foreign(..)
+ | ty::Alias(ty::Projection, ..)
+ | ty::Bound(..)
+ | ty::Infer(ty::TyVar(_)) => Err(NoSolution),
+
+ ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
+
+ 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())
+ }
+
+ // 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::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::Infer(ty::TyVar(_)) => Err(NoSolution),
+
+ ty::Placeholder(..)
+ | ty::Bound(..)
+ | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
+
+ 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::Infer(ty::TyVar(_)) => Err(NoSolution),
+
+ ty::Placeholder(..)
+ | ty::Bound(..)
+ | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(),
+
+ 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())
+ }
+ }
+}
+
+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.bound_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::Never
+ | ty::Tuple(_)
+ | ty::Alias(_, _)
+ | ty::Param(_)
+ | ty::Placeholder(_)
+ | ty::Bound(_, _)
+ | ty::Infer(_)
+ | ty::Error(_) => Err(NoSolution),
+ }
+}
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 pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.obligations.iter().cloned().collect()
}
-
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
- &mut self.relationships
- }
}
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::Diagnostic;
use rustc_hir::def_id::{DefId, CRATE_DEF_ID, LOCAL_CRATE};
-use rustc_hir::CRATE_HIR_ID;
use rustc_infer::infer::{DefiningAnchor, InferCtxt, TyCtxtInferExt};
use rustc_infer::traits::util;
use rustc_middle::traits::specialization_graph::OverlapMode;
let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
let may_overlap = match (impl1_ref, impl2_ref) {
- (Some(a), Some(b)) => iter::zip(a.substs, b.substs)
+ (Some(a), Some(b)) => iter::zip(a.skip_binder().substs, b.skip_binder().substs)
.all(|(arg1, arg2)| drcx.generic_args_may_unify(arg1, arg2)),
(None, None) => {
let self_ty1 = tcx.type_of(impl1_def_id);
let header = ty::ImplHeader {
impl_def_id,
self_ty: tcx.bound_type_of(impl_def_id).subst(tcx, impl_substs),
- trait_ref: tcx.bound_impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
+ trait_ref: tcx.impl_trait_ref(impl_def_id).map(|i| i.subst(tcx, impl_substs)),
predicates: tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs).predicates,
};
return false;
}
- let (body_id, body_def_id) = if let Some(body_def_id) = body_def_id.as_local() {
- (tcx.hir().local_def_id_to_hir_id(body_def_id), body_def_id)
- } else {
- (CRATE_HIR_ID, CRATE_DEF_ID)
- };
+ let body_def_id = body_def_id.as_local().unwrap_or(CRATE_DEF_ID);
let ocx = ObligationCtxt::new(&infcx);
let wf_tys = ocx.assumed_wf_types(param_env, DUMMY_SP, body_def_id);
let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
Some(&infcx),
- infcx.implied_bounds_tys(param_env, body_id, wf_tys),
+ infcx.implied_bounds_tys(param_env, body_def_id, wf_tys),
);
infcx.process_registered_region_obligations(outlives_env.region_bound_pairs(), param_env);
infcx.resolve_regions(&outlives_env).is_empty()
}
+#[instrument(level = "debug", skip(tcx), ret)]
pub fn trait_ref_is_knowable<'tcx>(
tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
) -> Result<(), Conflict> {
- debug!("trait_ref_is_knowable(trait_ref={:?})", trait_ref);
- if orphan_check_trait_ref(tcx, trait_ref, InCrate::Remote).is_ok() {
+ if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
// A downstream or cousin crate is allowed to implement some
// substitution of this trait-ref.
return Err(Conflict::Downstream);
// and if we are an intermediate owner, then we don't care
// about future-compatibility, which means that we're OK if
// we are an owner.
- if orphan_check_trait_ref(tcx, trait_ref, InCrate::Local).is_ok() {
- debug!("trait_ref_is_knowable: orphan check passed");
+ if orphan_check_trait_ref(trait_ref, InCrate::Local).is_ok() {
Ok(())
} else {
- debug!("trait_ref_is_knowable: nonlocal, nonfundamental, unowned");
Err(Conflict::Upstream)
}
}
trait_ref.def_id.krate == LOCAL_CRATE || tcx.has_attr(trait_ref.def_id, sym::fundamental)
}
+#[derive(Debug)]
pub enum OrphanCheckErr<'tcx> {
NonLocalInputType(Vec<(Ty<'tcx>, bool /* Is this the first input type? */)>),
UncoveredTy(Ty<'tcx>, Option<Ty<'tcx>>),
///
/// 1. All type parameters in `Self` must be "covered" by some local type constructor.
/// 2. Some local type must appear in `Self`.
+#[instrument(level = "debug", skip(tcx), ret)]
pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanCheckErr<'_>> {
- debug!("orphan_check({:?})", impl_def_id);
-
// We only except this routine to be invoked on implementations
// of a trait, not inherent implementations.
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
- debug!("orphan_check: trait_ref={:?}", trait_ref);
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
+ debug!(?trait_ref);
// If the *trait* is local to the crate, ok.
if trait_ref.def_id.is_local() {
return Ok(());
}
- orphan_check_trait_ref(tcx, trait_ref, InCrate::Local)
+ orphan_check_trait_ref(trait_ref, InCrate::Local)
}
/// Checks whether a trait-ref is potentially implementable by a crate.
///
/// Note that this function is never called for types that have both type
/// parameters and inference variables.
+#[instrument(level = "trace", ret)]
fn orphan_check_trait_ref<'tcx>(
- tcx: TyCtxt<'tcx>,
trait_ref: ty::TraitRef<'tcx>,
in_crate: InCrate,
) -> Result<(), OrphanCheckErr<'tcx>> {
- debug!("orphan_check_trait_ref(trait_ref={:?}, in_crate={:?})", trait_ref, in_crate);
-
if trait_ref.needs_infer() && trait_ref.needs_subst() {
bug!(
"can't orphan check a trait ref with both params and inference variables {:?}",
);
}
- let mut checker = OrphanChecker::new(tcx, in_crate);
+ let mut checker = OrphanChecker::new(in_crate);
match trait_ref.visit_with(&mut checker) {
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
}
struct OrphanChecker<'tcx> {
- tcx: TyCtxt<'tcx>,
in_crate: InCrate,
in_self_ty: bool,
/// Ignore orphan check failures and exclusively search for the first
}
impl<'tcx> OrphanChecker<'tcx> {
- fn new(tcx: TyCtxt<'tcx>, in_crate: InCrate) -> Self {
+ fn new(in_crate: InCrate) -> Self {
OrphanChecker {
- tcx,
in_crate,
in_self_ty: true,
search_first_local_ty: false,
fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
self.non_local_tys.push((t, self.in_self_ty));
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
if self.search_first_local_ty {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
} else {
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(t))
}
impl<'tcx> TypeVisitor<'tcx> for OrphanChecker<'tcx> {
type BreakTy = OrphanCheckEarlyExit<'tcx>;
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
}
}
ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
- ty::Closure(..) | ty::Generator(..) | ty::GeneratorWitness(..) => {
- self.tcx.sess.delay_span_bug(
- DUMMY_SP,
- format!("ty_is_local invoked on closure or generator: {:?}", ty),
- );
- ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(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(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
ty::Alias(ty::Opaque, ..) => {
// This merits some explanation.
// Normally, opaque types are not involved when performing
/// parameters, allowing uncovered const parameters in impls seems more useful
/// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile.
fn visit_const(&mut self, _c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
// If we start allowing directly writing `ConstKind::Expr` without an intermediate anon const
// this will be incorrect. It might be worth investigating making `predicates_of` elaborate
// all of the `ConstEvaluatable` bounds rather than having a visitor here.
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
let tcx = self.infcx.tcx;
let assumed_wf_types = tcx.assumed_wf_types(def_id);
let mut implied_bounds = FxIndexSet::default();
- let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- let cause = ObligationCause::misc(span, hir_id);
+ let cause = ObligationCause::misc(span, def_id);
for ty in assumed_wf_types {
// FIXME(@lcnr): rustc currently does not check wf for types
// pre-normalization, meaning that implied bounds are sometimes
ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
- let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
if let Err(_) =
);
let predicates =
- tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx);
- for obligation in
- elaborate_predicates_with_span(tcx, std::iter::zip(predicates.predicates, predicates.spans))
- {
+ tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
+ for obligation in elaborate_predicates_with_span(tcx, predicates.into_iter()) {
let kind = obligation.predicate.kind();
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
&& param_env_candidate_may_apply(kind.rebind(trait_pred))
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
self.infcx.probe(|_| {
- if a.is_ty_infer() || b.is_ty_infer() {
+ if a.is_ty_var() || b.is_ty_var() {
Ok(a)
} else {
self.infcx.super_combine_tys(self, a, b).or_else(|e| {
a: ty::Const<'tcx>,
b: ty::Const<'tcx>,
) -> RelateResult<'tcx, ty::Const<'tcx>> {
- if a == b {
- return Ok(a);
- }
- relate::super_relate_consts(self, a, b) // could do something similar here for constants!
+ self.infcx.probe(|_| {
+ if a.is_ct_infer() || b.is_ct_infer() {
+ Ok(a)
+ } else {
+ relate::super_relate_consts(self, a, b) // could do something similar here for constants!
+ }
+ })
}
fn binders<T: Relate<'tcx>>(
})
}
}
+
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn report_fulfillment_errors(
&self,
}
}
- for (error, suppressed) in iter::zip(errors, is_suppressed) {
- if !suppressed {
- self.report_fulfillment_error(error, body_id);
+ for from_expansion in [false, true] {
+ for (error, suppressed) in iter::zip(errors, &is_suppressed) {
+ if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
+ self.report_fulfillment_error(error, body_id);
+ }
}
}
err.note(s.as_str());
}
if let Some(ref s) = parent_label {
- let body = tcx
- .hir()
- .opt_local_def_id(obligation.cause.body_id)
- .unwrap_or_else(|| {
- tcx.hir().body_owner_def_id(hir::BodyId {
- hir_id: obligation.cause.body_id,
- })
- });
+ let body = obligation.cause.body_id;
err.span_label(tcx.def_span(body), s);
}
let mut suggested =
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
+ let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
+ suggested = if let &[cand] = &impl_candidates[..] {
+ let cand = cand.trait_ref;
+ if let (ty::FnPtr(_), ty::FnDef(..)) =
+ (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
+ {
+ err.span_suggestion(
+ span.shrink_to_hi(),
+ format!(
+ "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`",
+ cand.print_only_trait_path(),
+ cand.self_ty(),
+ ),
+ format!(" as {}", cand.self_ty()),
+ Applicability::MaybeIncorrect,
+ );
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ } || suggested;
suggested |=
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_semicolon_removal(
);
}
+ let body_hir_id =
+ self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
// Try to report a help message
if is_fn_trait
&& let Ok((implemented_kind, params)) = self.type_implements_fn_trait(
if !self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
- obligation.cause.body_id,
+ body_hir_id,
&mut err,
true,
) {
self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
- obligation.cause.body_id,
+ body_hir_id,
&mut err,
true,
);
expected_trait_ref,
obligation.cause.code(),
found_node,
+ obligation.param_env,
)
} else {
let (closure_span, closure_arg_span, found) = found_did
return None;
}
- let imp = self.tcx.impl_trait_ref(def_id).unwrap();
+ let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder();
self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false)
.map(|similarity| ImplCandidate { trait_ref: imp, similarity })
candidates.sort();
candidates.dedup();
let len = candidates.len();
- if candidates.len() == 0 {
+ if candidates.is_empty() {
return false;
}
- if candidates.len() == 1 {
- let ty_desc = match candidates[0].self_ty().kind() {
- ty::FnPtr(_) => Some("fn pointer"),
- _ => None,
- };
- let the_desc = match ty_desc {
- Some(desc) => format!(" implemented for {} `", desc),
- None => " implemented for `".to_string(),
- };
+ if let &[cand] = &candidates[..] {
+ let (desc, mention_castable) =
+ match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
+ (ty::FnPtr(_), ty::FnDef(..)) => {
+ (" implemented for fn pointer `", ", cast using `as`")
+ }
+ (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
+ _ => (" implemented for `", ""),
+ };
err.highlighted_help(vec![
- (
- format!("the trait `{}` ", candidates[0].print_only_trait_path()),
- Style::NoStyle,
- ),
+ (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle),
("is".to_string(), Style::Highlight),
- (the_desc, Style::NoStyle),
- (candidates[0].self_ty().to_string(), Style::Highlight),
+ (desc.to_string(), Style::NoStyle),
+ (cand.self_ty().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
+ (mention_castable.to_string(), Style::NoStyle),
]);
return true;
}
|| self.tcx.is_builtin_derive(def_id)
})
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
+ .map(ty::EarlyBinder::subst_identity)
.filter(|trait_ref| {
let self_ty = trait_ref.self_ty();
// Avoid mentioning type parameters.
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
- // #0> doesn't implement Sized". It may even be true that we
+ // #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
Ok(None) => {
let ambiguities =
ambiguity::recompute_applicable_impls(self.infcx, &obligation);
- let has_non_region_infer =
- trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer());
+ let has_non_region_infer = trait_ref
+ .skip_binder()
+ .substs
+ .types()
+ .any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more
// than a handful of them.
if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
predicate.to_opt_poly_trait_pred().unwrap(),
);
if impl_candidates.len() < 10 {
+ let hir =
+ self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
self.report_similar_impl_candidates(
impl_candidates,
trait_ref,
- body_id.map(|id| id.hir_id).unwrap_or(obligation.cause.body_id),
+ body_id.map(|id| id.hir_id).unwrap_or(hir),
&mut err,
false,
);
}
impl<'hir> FindExprBySpan<'hir> {
- fn new(span: Span) -> Self {
+ pub fn new(span: Span) -> Self {
Self { span, result: None, ty_result: None }
}
}
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
+#[derive(Copy, Clone)]
pub enum DefIdOrName {
DefId(DefId),
Name(&'static str),
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
- let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
let impl_self_ty = impl_trait_ref.self_ty();
.unwrap_or_else(|| (trait_ref.def_id(), trait_ref.skip_binder().substs));
let trait_ref = trait_ref.skip_binder();
- let mut flags = vec![(
- sym::ItemContext,
- self.describe_enclosure(obligation.cause.body_id).map(|s| s.to_owned()),
- )];
+ let body_hir = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
+ let mut flags =
+ vec![(sym::ItemContext, self.describe_enclosure(body_hir).map(|s| s.to_owned()))];
match obligation.cause.code() {
ObligationCauseCode::BuiltinDerivedObligation(..)
use crate::traits::{NormalizeExt, ObligationCtxt};
use hir::def::CtorOf;
-use hir::{Expr, HirId};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
+use rustc_hir::{Expr, HirId};
use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime};
IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder,
TypeSuperFoldable, TypeVisitable, TypeckResults,
};
+use rustc_span::def_id::LocalDefId;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
associated_item: Option<(&'static str, Ty<'tcx>)>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
);
fn suggest_dereferences(
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
+ fn extract_callable_info(
+ &self,
+ hir_id: HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>;
+
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
fn note_conflicting_closure_bounds(
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);
}
}
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
+ // It doesn't make sense to make this suggestion outside of typeck...
+ // (also autoderef will ICE...)
+ if self.typeck_results.is_none() {
+ return false;
+ }
+
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
{
return false;
}
- // This is duplicated from `extract_callable_info` in typeck, which
- // relies on autoderef, so we can't use it here.
- let found = trait_pred.self_ty().skip_binder().peel_refs();
- let Some((def_id_or_name, output, inputs)) = (match *found.kind()
- {
- ty::FnPtr(fn_sig) => {
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs()))
- }
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((
- DefIdOrName::DefId(def_id),
- fn_sig.output(),
- fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
- ))
- }
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
- && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Param(_) => {
- obligation.param_env.caller_bounds().iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::Name("type parameter"),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- _ => None,
- }) else { return false; };
- let output = self.replace_bound_vars_with_fresh_vars(
- obligation.cause.span,
+ let self_ty = self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
LateBoundRegionConversionTime::FnCall,
- output,
+ trait_pred.self_ty(),
);
- let inputs = inputs.skip_binder().iter().map(|ty| {
- self.replace_bound_vars_with_fresh_vars(
- obligation.cause.span,
- LateBoundRegionConversionTime::FnCall,
- inputs.rebind(*ty),
- )
- });
+
+ let body_hir_id = self.tcx.hir().local_def_id_to_hir_id(obligation.cause.body_id);
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(
+ body_hir_id,
+ obligation.param_env,
+ self_ty,
+ ) else { return false; };
// Remapping bound vars here
let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));
};
let args = inputs
+ .into_iter()
.map(|ty| {
if ty.is_suggestable(self.tcx, false) {
format!("/* {ty} */")
span.remove_mark();
}
let mut expr_finder = FindExprBySpan::new(span);
- let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return; };
- expr_finder.visit_expr(&body);
+ let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { return; };
+ let body = self.tcx.hir().body(body_id);
+ expr_finder.visit_expr(body.value);
let Some(expr) = expr_finder.result else { return; };
let Some(typeck) = &self.typeck_results else { return; };
let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; };
) -> bool {
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
let ty = self.tcx.erase_late_bound_regions(self_ty);
- let owner = self.tcx.hir().get_parent_item(obligation.cause.body_id);
- let Some(generics) = self.tcx.hir().get_generics(owner.def_id) else { return false };
+ let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_id) else { return false };
let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
let ty::Param(param) = inner_ty.kind() else { return false };
let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else { return false };
false
}
+ /// Extracts information about a callable type for diagnostics. This is a
+ /// heuristic -- it doesn't necessarily mean that a type is always callable,
+ /// because the callable type must also be well-formed to be called.
+ // FIXME(vincenzopalazzo): move the HirId to a LocalDefId
+ fn extract_callable_info(
+ &self,
+ hir_id: HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
+ // Autoderef is useful here because sometimes we box callables, etc.
+ let Some((def_id_or_name, output, inputs)) = (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
+ match *found.kind() {
+ ty::FnPtr(fn_sig) =>
+ Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
+ ty::FnDef(def_id, _) => {
+ let fn_sig = found.fn_sig(self.tcx);
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+ }
+ ty::Closure(def_id, substs) => {
+ let fn_sig = substs.as_closure().sig();
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
+ }
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ DefIdOrName::DefId(def_id),
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Dynamic(data, _, ty::Dyn) => {
+ data.iter().find_map(|pred| {
+ if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+ && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
+ // for existential projection, substs are shifted over by 1
+ && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+ {
+ Some((
+ DefIdOrName::Name("trait object"),
+ pred.rebind(proj.term.ty().unwrap()),
+ pred.rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Param(param) => {
+ let generics = self.tcx.generics_of(hir_id.owner.to_def_id());
+ let name = if generics.count() > param.index as usize
+ && let def = generics.param_at(param.index as usize, self.tcx)
+ && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
+ && def.name == param.name
+ {
+ DefIdOrName::DefId(def.def_id)
+ } else {
+ DefIdOrName::Name("type parameter")
+ };
+ param_env.caller_bounds().iter().find_map(|pred| {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+ && proj.projection_ty.self_ty() == found
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ name,
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ _ => None,
+ }
+ }) else { return None; };
+
+ let output = self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::FnCall,
+ output,
+ );
+ let inputs = inputs
+ .skip_binder()
+ .iter()
+ .map(|ty| {
+ self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::FnCall,
+ inputs.rebind(*ty),
+ )
+ })
+ .collect();
+
+ // We don't want to register any extra obligations, which should be
+ // implied by wf, but also because that would possibly result in
+ // erroneous errors later on.
+ let InferOk { value: output, obligations: _ } =
+ self.at(&ObligationCause::dummy(), param_env).normalize(output);
+
+ if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ }
+
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
span.remove_mark();
}
let mut expr_finder = super::FindExprBySpan::new(span);
- let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else {
+ let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else {
return false;
};
- expr_finder.visit_expr(&body);
+ let body = self.tcx.hir().body(body_id);
+ expr_finder.visit_expr(body.value);
let mut maybe_suggest = |suggested_ty, count, suggestions| {
// Remapping bound vars here
let trait_pred_and_suggested_ty =
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
let hir = self.tcx.hir();
- let parent_node = hir.parent_id(obligation.cause.body_id);
- let node = hir.find(parent_node);
+ let node = hir.find_by_def_id(obligation.cause.body_id);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node
&& let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind
&& sig.decl.output.span().overlaps(span)
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
let hir = self.tcx.hir();
- let parent_node = hir.parent_id(obligation.cause.body_id);
- let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find(parent_node) else {
+ let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find_by_def_id(obligation.cause.body_id) else {
return None;
};
}
let hir = self.tcx.hir();
- let fn_hir_id = hir.parent_id(obligation.cause.body_id);
- let node = hir.find(fn_hir_id);
+ let fn_hir_id = hir.local_def_id_to_hir_id(obligation.cause.body_id);
+ let node = hir.find_by_def_id(obligation.cause.body_id);
let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(sig, _, body_id),
..
match liberated_sig.output().kind() {
ty::Dynamic(predicates, _, ty::Dyn) => {
- let cause = ObligationCause::misc(ret_ty.span, fn_hir_id);
+ let cause = ObligationCause::misc(ret_ty.span, obligation.cause.body_id);
let param_env = ty::ParamEnv::empty();
if !only_never_return {
}
let hir = self.tcx.hir();
- let parent_node = hir.parent_id(obligation.cause.body_id);
- let node = hir.find(parent_node);
+ let node = hir.find_by_def_id(obligation.cause.body_id);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) =
node
{
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
pub(crate) fn build_fn_sig_ty<'tcx>(
infcx: &InferCtxt<'tcx>,
self.note_conflicting_closure_bounds(cause, &mut err);
if let Some(found_node) = found_node {
- hint_missing_borrow(span, found, expected, found_node, &mut err);
+ hint_missing_borrow(self, param_env, span, found, expected, found_node, &mut err);
}
err
// Find another predicate whose self-type is equal to the expected self type,
// but whose substs don't match.
- let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans)
+ let other_pred = predicates.into_iter()
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
// If we found one, then it's very likely the cause of the error.
if let Some((_, (_, other_pred_span))) = other_pred {
err.span_note(
- *other_pred_span,
+ other_pred_span,
"closure inferred to have a different signature due to this bound",
);
}
// generator interior are not generally known, so we
// want to erase them when comparing (and anyway,
// `Send` and other bounds are generally unaffected by
- // the choice of region). When erasing regions, we
+ // the choice of region). When erasing regions, we
// also have to erase late-bound regions. This is
// because the types that appear in the generator
// interior generally contain "bound regions" to
};
// Get the typeck results from the infcx if the generator is the function we are currently
- // type-checking; otherwise, get them by performing a query. This is needed to avoid
+ // type-checking; otherwise, get them by performing a query. This is needed to avoid
// cycles. If we can't use resolved types because the generator comes from another crate,
// we still provide a targeted error but without all the relevant spans.
let generator_data = match &self.typeck_results {
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 pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
self.predicates.map_pending_obligations(|o| o.obligation.clone())
}
-
- fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
- &mut self.relationships
- }
}
struct FulfillProcessor<'a, 'tcx> {
//! Miscellaneous type-system utilities that are too small to deserve their own modules.
-use crate::infer::InferCtxtExt as _;
use crate::traits::{self, ObligationCause};
+use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
+use rustc_infer::{infer::outlives::env::OutlivesEnvironment, traits::FulfillmentError};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
-use crate::traits::error_reporting::TypeErrCtxtExt;
+use super::outlives_bounds::InferCtxtExt;
-#[derive(Clone)]
pub enum CopyImplementationError<'tcx> {
- InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>)>),
+ InfrigingFields(Vec<(&'tcx ty::FieldDef, Ty<'tcx>, InfringingFieldsReason<'tcx>)>),
NotAnAdt,
HasDestructor,
}
-pub fn can_type_implement_copy<'tcx>(
+pub enum InfringingFieldsReason<'tcx> {
+ Fulfill(Vec<FulfillmentError<'tcx>>),
+ Regions(Vec<RegionResolutionError<'tcx>>),
+}
+
+/// Checks that the fields of the type (an ADT) all implement copy.
+///
+/// If fields don't implement copy, return an error containing a list of
+/// those violating fields. If it's not an ADT, returns `Err(NotAnAdt)`.
+pub fn type_allowed_to_implement_copy<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
self_type: Ty<'tcx>,
parent_cause: ObligationCause<'tcx>,
) -> Result<(), CopyImplementationError<'tcx>> {
- // FIXME: (@jroesch) float this code up
- let infcx = tcx.infer_ctxt().build();
let (adt, substs) = match self_type.kind() {
// These types used to have a builtin impl.
// Now libcore provides that impl.
_ => return Err(CopyImplementationError::NotAnAdt),
};
+ let copy_def_id = tcx.require_lang_item(hir::LangItem::Copy, Some(parent_cause.span));
+
let mut infringing = Vec::new();
for variant in adt.variants() {
for field in &variant.fields {
- let ty = field.ty(tcx, substs);
- if ty.references_error() {
+ // Do this per-field to get better error messages.
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = traits::ObligationCtxt::new(&infcx);
+
+ let unnormalized_ty = field.ty(tcx, substs);
+ if unnormalized_ty.references_error() {
continue;
}
- let span = tcx.def_span(field.did);
+
+ let field_span = tcx.def_span(field.did);
+ let field_ty_span = match tcx.hir().get_if_local(field.did) {
+ Some(hir::Node::Field(field_def)) => field_def.ty.span,
+ _ => field_span,
+ };
+
// FIXME(compiler-errors): This gives us better spans for bad
// projection types like in issue-50480.
// If the ADT has substs, point to the cause we are given.
// If it does not, then this field probably doesn't normalize
// to begin with, and point to the bad field's span instead.
- let cause = if field
+ let normalization_cause = if field
.ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did()))
.has_non_region_param()
{
parent_cause.clone()
} else {
- ObligationCause::dummy_with_span(span)
- };
- match traits::fully_normalize(&infcx, cause, param_env, ty) {
- Ok(ty) => {
- if !infcx.type_is_copy_modulo_regions(param_env, ty, span) {
- infringing.push((field, ty));
- }
- }
- Err(errors) => {
- infcx.err_ctxt().report_fulfillment_errors(&errors, None);
- }
+ ObligationCause::dummy_with_span(field_ty_span)
};
+ let ty = ocx.normalize(&normalization_cause, param_env, unnormalized_ty);
+ let normalization_errors = ocx.select_where_possible();
+ if !normalization_errors.is_empty() {
+ tcx.sess.delay_span_bug(field_span, format!("couldn't normalize struct field `{unnormalized_ty}` when checking Copy implementation"));
+ continue;
+ }
+
+ ocx.register_bound(
+ ObligationCause::dummy_with_span(field_ty_span),
+ param_env,
+ ty,
+ copy_def_id,
+ );
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ infringing.push((field, ty, InfringingFieldsReason::Fulfill(errors)));
+ }
+
+ // Check regions assuming the self type of the impl is WF
+ let outlives_env = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(&infcx),
+ infcx.implied_bounds_tys(
+ param_env,
+ parent_cause.body_id,
+ FxIndexSet::from_iter([self_type]),
+ ),
+ );
+ infcx.process_registered_region_obligations(
+ outlives_env.region_bound_pairs(),
+ param_env,
+ );
+ let errors = infcx.resolve_regions(&outlives_env);
+ if !errors.is_empty() {
+ infringing.push((field, ty, InfringingFieldsReason::Regions(errors)));
+ }
}
}
+
if !infringing.is_empty() {
return Err(CopyImplementationError::InfrigingFields(infringing));
}
+
if adt.has_dtor(tcx) {
return Err(CopyImplementationError::HasDestructor);
}
pub mod outlives_bounds;
mod project;
pub mod query;
-pub(crate) mod relationships;
mod select;
mod specialize;
mod structural_match;
use crate::traits::error_reporting::TypeErrCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeSuperVisitable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_span::def_id::{DefId, CRATE_DEF_ID};
use rustc_span::Span;
use std::fmt::Debug;
param_env: ty::ParamEnv<'tcx>,
generic_bounds: ty::InstantiatedPredicates<'tcx>,
) -> impl Iterator<Item = PredicateObligation<'tcx>> {
- std::iter::zip(generic_bounds.predicates, generic_bounds.spans).enumerate().map(
- move |(idx, (predicate, span))| Obligation {
- cause: cause(idx, span),
- recursion_depth: 0,
- param_env,
- predicate,
- },
- )
+ generic_bounds.into_iter().enumerate().map(move |(idx, (predicate, span))| Obligation {
+ cause: cause(idx, span),
+ recursion_depth: 0,
+ param_env,
+ predicate,
+ })
}
/// Determines whether the type `ty` is known to meet `bound` and
// We can use a dummy node-id here because we won't pay any mind
// to region obligations that arise (there shouldn't really be any
// anyhow).
- cause: ObligationCause::misc(span, hir::CRATE_HIR_ID),
+ cause: ObligationCause::misc(span, CRATE_DEF_ID),
recursion_depth: 0,
predicate: pred.to_predicate(infcx.tcx),
};
// that guess. While imperfect, I believe this is sound.
// FIXME(@lcnr): this function doesn't seem right.
+ //
// The handling of regions in this area of the code is terrible,
// see issue #29149. We should be able to improve on this with
// NLL.
let errors = fully_solve_obligation(infcx, obligation);
- // Note: we only assume something is `Copy` if we can
- // *definitively* show that it implements `Copy`. Otherwise,
- // assume it is move; linear is always ok.
match &errors[..] {
[] => true,
errors => {
// the `TypeOutlives` predicates first inside the unnormalized parameter environment, and
// then we normalize the `TypeOutlives` bounds inside the normalized parameter environment.
//
- // This works fairly well because trait matching does not actually care about param-env
+ // This works fairly well because trait matching does not actually care about param-env
// TypeOutlives predicates - these are normally used by regionck.
let outlives_predicates: Vec<_> = predicates
.drain_filter(|predicate| {
&& let param_def_id = self.generics.type_param(param, self.tcx).def_id
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
t.super_visit_with(self)
}
&& let param_def_id = self.generics.region_param(¶m, self.tcx).def_id
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
r.super_visit_with(self)
}
&& let param_def_id = self.generics.const_param(¶m, self.tcx).def_id
&& self.tcx.parent(param_def_id) == self.trait_item_def_id
{
- return ControlFlow::BREAK;
+ return ControlFlow::Break(());
}
ct.super_visit_with(self)
}
let generics = tcx.generics_of(trait_item_def_id);
let predicates = tcx.predicates_of(trait_item_def_id);
- let impl_trait_ref =
- tcx.impl_trait_ref(impl_def_id).expect("expected impl to correspond to trait");
+ let impl_trait_ref = tcx
+ .impl_trait_ref(impl_def_id)
+ .expect("expected impl to correspond to trait")
+ .subst_identity();
let param_env = tcx.param_env(impl_def_id);
let mut visitor = ReferencesOnlyParentGenerics { tcx, generics, trait_item_def_id };
match t.kind() {
ty::Param(_) => {
if t == self.tcx.types.self_param {
- ControlFlow::BREAK
+ ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
ty::Alias(ty::Projection, ref data)
if self.tcx.def_kind(data.def_id) == DefKind::ImplTraitPlaceholder =>
{
// We'll deny these later in their own pass
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
ty::Alias(ty::Projection, ref data) => {
// This is a projected type `<Foo as SomeTrait>::X`.
// SomeTrait` is in fact a supertrait of the
// current trait. In that case, this type is
// legal, because the type `X` will be specified
- // in the object type. Note that we can just use
+ // in the object type. Note that we can just use
// direct equality here because all of these types
// are part of the formal parameter listing, and
// hence there should be no inference variables.
.contains(&data.trait_ref(self.tcx).def_id);
if is_supertrait_of_current_trait {
- ControlFlow::CONTINUE // do not walk contained types, do not report error, do collect $200
+ ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200
} else {
t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
}
use crate::traits::query::NoSolution;
use crate::traits::ObligationCause;
use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir as hir;
-use rustc_hir::HirId;
use rustc_middle::ty::{self, ParamEnv, Ty};
+use rustc_span::def_id::LocalDefId;
pub use rustc_middle::traits::query::OutlivesBound;
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
ty: Ty<'tcx>,
) -> Vec<OutlivesBound<'tcx>>;
fn implied_bounds_tys(
&'a self,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx>;
}
fn implied_outlives_bounds(
&self,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
ty: Ty<'tcx>,
) -> Vec<OutlivesBound<'tcx>> {
- let span = self.tcx.hir().span(body_id);
+ let span = self.tcx.def_span(body_id);
let result = param_env
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
.fully_perform(self);
fn implied_bounds_tys(
&'a self,
param_env: ParamEnv<'tcx>,
- body_id: HirId,
+ body_id: LocalDefId,
tys: FxIndexSet<Ty<'tcx>>,
) -> Bounds<'a, 'tcx> {
tys.into_iter()
}
// Prefer where-clauses. As in select, if there are multiple
- // candidates, we prefer where-clause candidates over impls. This
+ // candidates, we prefer where-clause candidates over impls. This
// may seem a bit surprising, since impls are the source of
// "truth" in some sense, but in fact some of the impls that SEEM
// applicable are not, because of nested obligations. Where
}
Err(ProjectionCacheEntry::InProgress) => {
// Under lazy normalization, this can arise when
- // bootstrapping. That is, imagine an environment with a
+ // bootstrapping. That is, imagine an environment with a
// where-clause like `A::B == u32`. Now, if we are asked
// to normalize `A::B`, we will want to check the
// where-clauses in scope. So we will try to unify `A::B`
// Check whether the self-type is itself a projection.
// If so, extract what we know from the trait and try to come up with a good answer.
let bounds = match *obligation.predicate.self_ty().kind() {
- ty::Alias(_, ref data) => tcx.bound_item_bounds(data.def_id).subst(tcx, data.substs),
+ ty::Alias(_, ref data) => tcx.item_bounds(data.def_id).subst(tcx, data.substs),
ty::Infer(ty::TyVar(_)) => {
// If the self-type is an inference variable, then it MAY wind up
// being a projected type, so induce an ambiguity.
tcx.predicates_of(impl_fn_def_id).instantiate(tcx, impl_fn_substs),
&mut obligations,
);
- obligations.extend(std::iter::zip(predicates.predicates, predicates.spans).map(
- |(pred, span)| {
- Obligation::with_depth(
- tcx,
- ObligationCause::new(
- obligation.cause.span,
- obligation.cause.body_id,
- if span.is_dummy() {
- super::ItemObligation(impl_fn_def_id)
- } else {
- super::BindingObligation(impl_fn_def_id, span)
- },
- ),
- obligation.recursion_depth + 1,
- obligation.param_env,
- pred,
- )
- },
- ));
+ obligations.extend(predicates.into_iter().map(|(pred, span)| {
+ Obligation::with_depth(
+ tcx,
+ ObligationCause::new(
+ obligation.cause.span,
+ obligation.cause.body_id,
+ if span.is_dummy() {
+ super::ItemObligation(impl_fn_def_id)
+ } else {
+ super::BindingObligation(impl_fn_def_id, span)
+ },
+ ),
+ obligation.recursion_depth + 1,
+ obligation.param_env,
+ pred,
+ )
+ }));
let ty = normalize_with_depth_to(
selcx,
nested: &mut Vec<PredicateObligation<'tcx>>,
) {
let tcx = selcx.tcx();
- let own = tcx
+ let predicates = tcx
.predicates_of(obligation.predicate.def_id)
.instantiate_own(tcx, obligation.predicate.substs);
- for (predicate, span) in std::iter::zip(own.predicates, own.spans) {
+ for (predicate, span) in predicates {
let normalized = normalize_with_depth_to(
selcx,
obligation.param_env,
.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 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 =
// touch bound regions, they just capture the in-scope
// type/region parameters
match *obligation.self_ty().skip_binder().kind() {
- ty::Closure(_, closure_substs) => {
+ ty::Closure(def_id, closure_substs) => {
+ let is_const = self.tcx().is_const_fn_raw(def_id);
debug!(?kind, ?obligation, "assemble_unboxed_candidates");
match self.infcx.closure_kind(closure_substs) {
Some(closure_kind) => {
debug!(?closure_kind, "assemble_unboxed_candidates");
if closure_kind.extends(kind) {
- candidates.vec.push(ClosureCandidate);
+ candidates.vec.push(ClosureCandidate { is_const });
}
}
None => {
debug!("assemble_unboxed_candidates: closure_kind not yet known");
- candidates.vec.push(ClosureCandidate);
+ candidates.vec.push(ClosureCandidate { is_const });
}
}
}
// Before we create the substitutions and everything, first
// consider a "quick reject". This avoids creating more types
// and so forth that we need to.
- let impl_trait_ref = self.tcx().bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
if self.fast_reject_trait_refs(obligation, &impl_trait_ref.0) {
return;
}
}
ty::Param(..) | ty::Alias(ty::Projection, ..) => {
// In these cases, we don't know what the actual
- // type is. Therefore, we cannot break it down
+ // type is. Therefore, we cannot break it down
// into its constituent types. So we don't
// consider the `..` impl but instead just add no
// candidates: this means that typeck will only
//!
//! Confirmation unifies the output type parameters of the trait
//! with the values found in the obligation, possibly yielding a
-//! type error. See the [rustc dev guide] for more details.
+//! type error. See the [rustc dev guide] for more details.
//!
//! [rustc dev guide]:
//! https://rustc-dev-guide.rust-lang.org/traits/resolution.html#confirmation
ImplSource::Object(data)
}
- ClosureCandidate => {
+ ClosureCandidate { .. } => {
let vtable_closure = self.confirm_closure_candidate(obligation)?;
ImplSource::Closure(vtable_closure)
}
_ => bug!("projection candidate for unexpected type: {:?}", placeholder_self_ty),
};
- let candidate_predicate =
- tcx.bound_item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
+ let candidate_predicate = tcx.item_bounds(def_id).map_bound(|i| i[idx]).subst(tcx, substs);
let candidate = candidate_predicate
.to_opt_poly_trait_pred()
.expect("projection candidate is not a trait predicate")
})?);
if let ty::Alias(ty::Projection, ..) = placeholder_self_ty.kind() {
- let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates;
- debug!(?predicates, "projection predicates");
- for predicate in predicates {
+ let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
+ for (predicate, _) in predicates {
let normalized = normalize_with_depth_to(
self,
obligation.param_env,
nested,
);
- // Adds the predicates from the trait. Note that this contains a `Self: Trait`
- // predicate as usual. It won't have any effect since auto traits are coinductive.
+ // Adds the predicates from the trait. Note that this contains a `Self: Trait`
+ // predicate as usual. It won't have any effect since auto traits are coinductive.
obligations.extend(trait_obligations);
debug!(?obligations, "vtable_auto_impl");
// This maybe belongs in wf, but that can't (doesn't) handle
// higher-ranked things.
// Prevent, e.g., `dyn Iterator<Item = str>`.
- for bound in self.tcx().bound_item_bounds(assoc_type).transpose_iter() {
+ for bound in self.tcx().item_bounds(assoc_type).transpose_iter() {
let subst_bound =
if defs.count() == 0 {
bound.subst(tcx, trait_predicate.trait_ref.substs)
// impl<T:Clone> Vec<T> { fn push_clone(...) { ... } }
//
// and we were to see some code `foo.push_clone()` where `boo`
- // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
+ // is a `Vec<Bar>` and `Bar` does not implement `Clone`. If
// we were to winnow, we'd wind up with zero candidates.
// Instead, we select the right impl now but report "`Bar` does
// not implement `Clone`".
// const param
ParamCandidate(trait_pred) if trait_pred.is_const_if_const() => {}
// const projection
- ProjectionCandidate(_, ty::BoundConstness::ConstIfConst) => {}
+ ProjectionCandidate(_, ty::BoundConstness::ConstIfConst)
// auto trait impl
- AutoImplCandidate => {}
+ | AutoImplCandidate
// generator / future, this will raise error in other places
// or ignore error with const_async_blocks feature
- GeneratorCandidate => {}
- FutureCandidate => {}
+ | GeneratorCandidate
+ | FutureCandidate
// FnDef where the function is const
- FnPointerCandidate { is_const: true } => {}
+ | FnPointerCandidate { is_const: true }
+ | ConstDestructCandidate(_)
+ | ClosureCandidate { is_const: true } => {}
+
FnPointerCandidate { is_const: false } => {
if let ty::FnDef(def_id, _) = obligation.self_ty().skip_binder().kind() && tcx.trait_of_item(*def_id).is_some() {
// Trait methods are not seen as const unless the trait is implemented as const.
continue
}
}
- ConstDestructCandidate(_) => {}
+
_ => {
// reject all other types of candidates
continue;
);
}
};
- let bounds = tcx.bound_item_bounds(def_id).subst(tcx, substs);
+ let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
// The bounds returned by `item_bounds` may contain duplicates after
// normalization, so try to deduplicate when possible to avoid
(
ParamCandidate(ref cand),
ImplCandidate(..)
- | ClosureCandidate
+ | ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
}
(
ImplCandidate(_)
- | ClosureCandidate
+ | ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
(
ObjectCandidate(_) | ProjectionCandidate(..),
ImplCandidate(..)
- | ClosureCandidate
+ | ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
(
ImplCandidate(..)
- | ClosureCandidate
+ | ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
// Everything else is ambiguous
(
ImplCandidate(_)
- | ClosureCandidate
+ | ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
| BuiltinCandidate { has_nested: true }
| TraitAliasCandidate,
ImplCandidate(_)
- | ClosureCandidate
+ | ClosureCandidate { .. }
| GeneratorCandidate
| FutureCandidate
| FnPointerCandidate { .. }
// Matching
//
// Matching is a common path used for both evaluation and
- // confirmation. It basically unifies types that appear in impls
+ // confirmation. It basically unifies types that appear in impls
// and traits. This does affect the surrounding environment;
// therefore, when used during evaluation, match routines must be
// run inside of a `probe()` so that their side-effects are
impl_def_id: DefId,
obligation: &TraitObligation<'tcx>,
) -> Normalized<'tcx, SubstsRef<'tcx>> {
- let impl_trait_ref = self.tcx().bound_impl_trait_ref(impl_def_id).unwrap();
+ let impl_trait_ref = self.tcx().impl_trait_ref(impl_def_id).unwrap();
match self.match_impl(impl_def_id, impl_trait_ref, obligation) {
Ok(substs) => substs,
Err(()) => {
// obligation will normalize to `<$0 as Iterator>::Item = $1` and
// `$1: Copy`, so we must ensure the obligations are emitted in
// that order.
- let predicates = tcx.bound_predicates_of(def_id);
- debug!(?predicates);
- assert_eq!(predicates.0.parent, None);
- let mut obligations = Vec::with_capacity(predicates.0.predicates.len());
- for (predicate, span) in predicates.0.predicates {
- let span = *span;
+ let predicates = tcx.predicates_of(def_id);
+ assert_eq!(predicates.parent, None);
+ let predicates = predicates.instantiate_own(tcx, substs);
+ let mut obligations = Vec::with_capacity(predicates.len());
+ for (predicate, span) in predicates {
let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
derived,
param_env,
cause.clone(),
recursion_depth,
- predicates.rebind(*predicate).subst(tcx, substs),
+ predicate,
&mut obligations,
);
obligations.push(Obligation { cause, recursion_depth, param_env, predicate });
/// In Issue #60010, we found a bug in rustc where it would cache
/// these intermediate results. This was fixed in #60444 by disabling
/// *all* caching for things involved in a cycle -- in our example,
-/// that would mean we don't cache that `Bar<T>: Send`. But this led
+/// that would mean we don't cache that `Bar<T>: Send`. But this led
/// to large slowdowns.
///
/// Specifically, imagine this scenario, where proving `Baz<T>: Send`
/// a result at `reached_depth`, so it marks the *current* solution as
/// provisional as well. If an error is encountered, we toss out any
/// provisional results added from the subtree that encountered the
-/// error. When we pop the node at `reached_depth` from the stack, we
+/// error. When we pop the node at `reached_depth` from the stack, we
/// can commit all the things that remain in the provisional cache.
struct ProvisionalEvaluationCache<'tcx> {
/// next "depth first number" to issue -- just a counter
}
/// Invoked when the node with dfn `dfn` does not get a successful
- /// result. This will clear out any provisional cache entries
+ /// result. This will clear out any provisional cache entries
/// that were added since `dfn` was created. This is because the
/// provisional entries are things which must assume that the
/// things on the stack at the time of their creation succeeded --
param_env, source_impl, source_substs, target_node
);
let source_trait_ref =
- infcx.tcx.bound_impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
+ infcx.tcx.impl_trait_ref(source_impl).unwrap().subst(infcx.tcx, &source_substs);
// translate the Self and Param parts of the substitution, since those
// vary across impls
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let penv = tcx.param_env(impl1_def_id);
- let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap();
+ let impl1_trait_ref = tcx.impl_trait_ref(impl1_def_id).unwrap().subst_identity();
// Create an infcx, taking the predicates of impl1 as assumptions:
let infcx = tcx.infer_ctxt().build();
pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
use std::fmt::Write;
- let trait_ref = tcx.impl_trait_ref(impl_def_id)?;
+ let trait_ref = tcx.impl_trait_ref(impl_def_id)?.subst_identity();
let mut w = "impl".to_owned();
let substs = InternalSubsts::identity_for_item(tcx, impl_def_id);
impl<'tcx> ChildrenExt<'tcx> for Children {
/// Insert an impl into this set of children without comparing to any existing impls.
fn insert_blindly(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer)
{
debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
/// an impl with a parent. The impl must be present in the list of
/// children already.
fn remove_existing(&mut self, tcx: TyCtxt<'tcx>, impl_def_id: DefId) {
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
let vec: &mut Vec<DefId>;
if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), TreatParams::AsInfer)
{
if le && !ge {
debug!(
"descending as child of TraitRef {:?}",
- tcx.impl_trait_ref(possible_sibling).unwrap()
+ tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
);
// The impl specializes `possible_sibling`.
} else if ge && !le {
debug!(
"placing as parent of TraitRef {:?}",
- tcx.impl_trait_ref(possible_sibling).unwrap()
+ tcx.impl_trait_ref(possible_sibling).unwrap().subst_identity()
);
replace_children.push(possible_sibling);
) -> Result<Option<FutureCompatOverlapError<'tcx>>, OverlapError<'tcx>> {
assert!(impl_def_id.is_local());
- let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+ // FIXME: use `EarlyBinder` in `self.children`
+ let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().skip_binder();
let trait_def_id = trait_ref.def_id;
debug!(
impl_def_id: DefId,
assoc_def_id: DefId,
) -> Result<LeafDef, ErrorGuaranteed> {
- let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
+ let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
let trait_def = tcx.trait_def(trait_def_id);
// This function may be called while we are still building the
} else {
// This is saying that neither the trait nor
// the impl contain a definition for this
- // associated type. Normally this situation
+ // associated type. Normally this situation
// could only arise through a compiler bug --
// if the user wrote a bad item name, it
// should have failed in astconv.
ty::FnDef(..) => {
// Types of formals and return in `fn(_) -> _` are also irrelevant;
// so we do not recur into them via `super_visit_with`
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::Array(_, n)
if { n.try_eval_usize(self.tcx, ty::ParamEnv::reveal_all()) == Some(0) } =>
{
// rust-lang/rust#62336: ignore type of contents
// for empty array.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Str | ty::Never => {
// These primitive types are always structural match.
//
// `Never` is kind of special here, but as it is not inhabitable, this should be fine.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
ty::FnPtr(..) => {
if !self.adt_const_param {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
} else {
return ControlFlow::Break(ty);
}
// Even though `NonStructural` does not implement `PartialEq`,
// structural equality on `T` does not recur into the raw
// pointer. Therefore, one can still use `C` in a pattern.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
} else {
return ControlFlow::Break(ty);
}
ty::Float(_) => {
if !self.adt_const_param {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
} else {
return ControlFlow::Break(ty);
}
self.tcx.sess.delay_span_bug(self.span, "ty::Error in structural-match check");
// We still want to check other types after encountering an error,
// as this may still emit relevant errors.
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
};
if !self.seen.insert(adt_def.did()) {
debug!("Search already seen adt_def: {:?}", adt_def);
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
if !self.type_marked_structural(ty) {
// Note that this method could then never be called, so we
// do not want to try and codegen it, in that case (see #23435).
let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs);
- if impossible_predicates(tcx, predicates.predicates) {
+ if impossible_predicates(
+ tcx,
+ predicates.map(|(predicate, _)| predicate).collect(),
+ ) {
debug!("vtable_entries: predicates do not hold");
return VtblEntry::Vacant;
}
use crate::infer::InferCtxt;
use crate::traits;
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
+use rustc_span::def_id::{DefId, LocalDefId};
use rustc_span::Span;
use std::iter;
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,
}
/// Returns the obligations that make this trait reference
-/// well-formed. For example, if there is a trait `Set` defined like
+/// well-formed. For example, if there is a trait `Set` defined like
/// `trait Set<K:Eq>`, then the trait reference `Foo: Set<Bar>` is WF
/// if `Bar: Eq`.
pub fn trait_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
trait_pred: &ty::TraitPredicate<'tcx>,
span: Span,
item: &'tcx hir::Item<'tcx>,
pub fn predicate_obligations<'tcx>(
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
predicate: ty::Predicate<'tcx>,
span: Span,
) -> Vec<traits::PredicateObligation<'tcx>> {
struct WfPredicates<'tcx> {
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body_id: hir::HirId,
+ body_id: LocalDefId,
span: Span,
out: Vec<traits::PredicateObligation<'tcx>>,
recursion_depth: usize,
// All of the requirements on type parameters
// have already been checked for `impl Trait` in
// return position. We do need to check type-alias-impl-trait though.
- if ty::is_impl_trait_defn(self.tcx, def_id).is_none() {
+ if self.tcx.is_type_alias_impl_trait(def_id) {
let obligations = self.nominal_obligations(def_id, substs);
self.out.extend(obligations);
}
trace!("{:#?}", predicates);
debug_assert_eq!(predicates.predicates.len(), origins.len());
- iter::zip(iter::zip(predicates.predicates, predicates.spans), origins.into_iter().rev())
+ iter::zip(predicates, origins.into_iter().rev())
.map(|((mut pred, span), origin_def_id)| {
let code = if span.is_dummy() {
traits::ItemObligation(origin_def_id)
//! `crate::chalk::lowering` (to lower rustc types into Chalk types).
use rustc_middle::traits::ChalkRustInterner as RustInterner;
-use rustc_middle::ty::{self, AssocKind, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
+use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
use rustc_middle::ty::{InternalSubsts, SubstsRef};
use rustc_target::abi::{Integer, IntegerType};
def_id: DefId,
bound_vars: SubstsRef<'tcx>,
) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
- let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates;
- predicates
- .iter()
- .map(|(wc, _)| EarlyBinder(*wc).subst(self.interner.tcx, bound_vars))
- .filter_map(|wc| LowerInto::<
- Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>
- >::lower_into(wc, self.interner)).collect()
+ self.interner
+ .tcx
+ .predicates_defined_on(def_id)
+ .instantiate_own(self.interner.tcx, bound_vars)
+ .filter_map(|(wc, _)| LowerInto::lower_into(wc, self.interner))
+ .collect()
}
fn bounds_for<T>(&self, def_id: DefId, bound_vars: SubstsRef<'tcx>) -> Vec<T>
let bound_vars = bound_vars_for_item(self.interner.tcx, def_id);
let binders = binders_for(self.interner, bound_vars);
- let trait_ref = self.interner.tcx.bound_impl_trait_ref(def_id).expect("not an impl");
+ let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl");
let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars);
let where_clauses = self.where_clauses_for(def_id, bound_vars);
let all_impls = self.interner.tcx.all_impls(def_id);
let matched_impls = all_impls.filter(|impl_def_id| {
use chalk_ir::could_match::CouldMatch;
- let trait_ref = self.interner.tcx.bound_impl_trait_ref(*impl_def_id).unwrap();
+ let trait_ref = self.interner.tcx.impl_trait_ref(*impl_def_id).unwrap();
let bound_vars = bound_vars_for_item(self.interner.tcx, *impl_def_id);
let self_ty = trait_ref.map_bound(|t| t.self_ty());
let trait_def_id = auto_trait_id.0;
let all_impls = self.interner.tcx.all_impls(trait_def_id);
for impl_def_id in all_impls {
- let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = self.interner.tcx.impl_trait_ref(impl_def_id).unwrap().subst_identity();
let self_ty = trait_ref.self_ty();
let provides = match (self_ty.kind(), chalk_ty) {
(&ty::Adt(impl_adt_def, ..), Adt(id, ..)) => impl_adt_def.did() == id.0.did(),
// This file contains various trait resolution methods used by codegen.
-// They all assume regions can be erased and monomorphic types. It
+// They all assume regions can be erased and monomorphic types. It
// seems likely that they should eventually be merged into more
// general routines.
//! Do not call this query directory. See
//! [`rustc_trait_selection::traits::query::type_op::implied_outlives_bounds`].
-use rustc_hir as hir;
use rustc_infer::infer::canonical::{self, Canonical};
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::query::OutlivesBound;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable};
+use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::source_map::DUMMY_SP;
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::query::{CanonicalTyGoal, Fallible, NoSolution};
// FIXME(@lcnr): It's not really "always fine", having fewer implied
// bounds can be backward incompatible, e.g. #101951 was caused by
// us not dealing with inference vars in `TypeOutlives` predicates.
- let obligations =
- wf::obligations(ocx.infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP)
- .unwrap_or_default();
+ let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
+ .unwrap_or_default();
// While these predicates should all be implied by other parts of
// the program, they are still relevant as they may constrain
match component {
Component::Region(r) => Some(OutlivesBound::RegionSubRegion(sub_region, r)),
Component::Param(p) => Some(OutlivesBound::RegionSubParam(sub_region, p)),
- Component::Projection(p) => Some(OutlivesBound::RegionSubProjection(sub_region, p)),
- Component::Opaque(def_id, substs) => {
- Some(OutlivesBound::RegionSubOpaque(sub_region, def_id, substs))
- }
- Component::EscapingProjection(_) =>
+ Component::Alias(p) => Some(OutlivesBound::RegionSubAlias(sub_region, p)),
+ Component::EscapingAlias(_) =>
// If the projection has escaping regions, don't
// try to infer any implied bounds even for its
// free components. This is conservative, because
use rustc_middle::ty::{self, FnSig, Lift, PolyFnSig, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{ParamEnvAnd, Predicate};
use rustc_middle::ty::{UserSelfTy, UserSubsts, UserType};
+use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtBuilderExt;
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::query::{Fallible, NoSolution};
use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt};
use std::fmt;
-use std::iter::zip;
pub(crate) fn provide(p: &mut Providers) {
*p = Providers {
// FIXME(#104764): We should check well-formedness before normalization.
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(user_ty.into()));
ocx.register_obligation(Obligation::new(ocx.infcx.tcx, cause, param_env, predicate));
-
Ok(())
}
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, substs);
debug!(?instantiated_predicates);
- for (instantiated_predicate, predicate_span) in
- zip(instantiated_predicates.predicates, instantiated_predicates.spans)
- {
+ for (instantiated_predicate, predicate_span) in instantiated_predicates {
let span = if span == DUMMY_SP { predicate_span } else { span };
let cause = ObligationCause::new(
span,
- hir::CRATE_HIR_ID,
+ CRATE_DEF_ID,
ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
);
let instantiated_predicate =
let impl_self_ty = ocx.normalize(&cause, param_env, impl_self_ty);
ocx.eq(&cause, param_env, self_ty, impl_self_ty)?;
-
let predicate = ty::Binder::dummy(ty::PredicateKind::WellFormed(impl_self_ty.into()));
ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate));
}
-#![feature(alloc_layout_extra, control_flow_enum, decl_macro, iterator_try_reduce, never_type)]
+#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type)]
#![allow(dead_code, unused_variables)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
// `Generator::resume(...) -> GeneratorState` function in case we
// have an ordinary generator, or the `Future::poll(...) -> Poll`
// function in case this is a special generator backing an async construct.
- let ret_ty = if tcx.generator_is_async(did) {
- let state_did = tcx.require_lang_item(LangItem::Poll, None);
- let state_adt_ref = tcx.adt_def(state_did);
- let state_substs = tcx.intern_substs(&[sig.return_ty.into()]);
- tcx.mk_adt(state_adt_ref, state_substs)
+ let (resume_ty, ret_ty) = if tcx.generator_is_async(did) {
+ // The signature should be `Future::poll(_, &mut Context<'_>) -> Poll<Output>`
+ let poll_did = tcx.require_lang_item(LangItem::Poll, None);
+ let poll_adt_ref = tcx.adt_def(poll_did);
+ let poll_substs = tcx.intern_substs(&[sig.return_ty.into()]);
+ let ret_ty = tcx.mk_adt(poll_adt_ref, poll_substs);
+
+ // We have to replace the `ResumeTy` that is used for type and borrow checking
+ // with `&mut Context<'_>` which is used in codegen.
+ #[cfg(debug_assertions)]
+ {
+ if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() {
+ let expected_adt =
+ tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None));
+ assert_eq!(*resume_ty_adt, expected_adt);
+ } else {
+ panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
+ };
+ }
+ let context_mut_ref = tcx.mk_task_context();
+
+ (context_mut_ref, ret_ty)
} else {
+ // The signature should be `Generator::resume(_, Resume) -> GeneratorState<Yield, Return>`
let state_did = tcx.require_lang_item(LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs = tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
- tcx.mk_adt(state_adt_ref, state_substs)
+ let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
+
+ (sig.resume_ty, ret_ty)
};
ty::Binder::bind_with_vars(
tcx.mk_fn_sig(
- [env_ty, sig.resume_ty].iter(),
+ [env_ty, resume_ty].iter(),
&ret_ty,
false,
hir::Unsafety::Normal,
return;
}
- // Scalars which have invalid values cannot be undef.
- if !scalar.is_always_valid(&cx) {
+ if !scalar.is_uninit_valid() {
attrs.set(ArgAttribute::NoUndef);
}
PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
};
- // `Box`, `&T`, and `&mut T` cannot be undef.
- // Note that this only applies to the value of the pointer itself;
- // this attribute doesn't make it UB for the pointed-to data to be undef.
- attrs.set(ArgAttribute::NoUndef);
-
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
// `noalias` for it. This can be turned off using an unstable flag.
// See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
}
match expr.kind {
- thir::ExprKind::NamedConst { substs, .. } => substs.has_non_region_param(),
+ thir::ExprKind::NamedConst { substs, .. }
+ | thir::ExprKind::ConstBlock { substs, .. } => substs.has_non_region_param(),
thir::ExprKind::ConstParam { .. } => true,
thir::ExprKind::Repeat { value, count } => {
self.visit_expr(&self.thir()[value]);
count.has_non_region_param()
}
- _ => false,
+ thir::ExprKind::Scope { .. }
+ | thir::ExprKind::Box { .. }
+ | thir::ExprKind::If { .. }
+ | thir::ExprKind::Call { .. }
+ | thir::ExprKind::Deref { .. }
+ | thir::ExprKind::Binary { .. }
+ | thir::ExprKind::LogicalOp { .. }
+ | thir::ExprKind::Unary { .. }
+ | thir::ExprKind::Cast { .. }
+ | thir::ExprKind::Use { .. }
+ | thir::ExprKind::NeverToAny { .. }
+ | thir::ExprKind::Pointer { .. }
+ | thir::ExprKind::Loop { .. }
+ | thir::ExprKind::Let { .. }
+ | thir::ExprKind::Match { .. }
+ | thir::ExprKind::Block { .. }
+ | thir::ExprKind::Assign { .. }
+ | thir::ExprKind::AssignOp { .. }
+ | thir::ExprKind::Field { .. }
+ | thir::ExprKind::Index { .. }
+ | thir::ExprKind::VarRef { .. }
+ | thir::ExprKind::UpvarRef { .. }
+ | thir::ExprKind::Borrow { .. }
+ | thir::ExprKind::AddressOf { .. }
+ | thir::ExprKind::Break { .. }
+ | thir::ExprKind::Continue { .. }
+ | thir::ExprKind::Return { .. }
+ | thir::ExprKind::Array { .. }
+ | thir::ExprKind::Tuple { .. }
+ | thir::ExprKind::Adt(_)
+ | thir::ExprKind::PlaceTypeAscription { .. }
+ | thir::ExprKind::ValueTypeAscription { .. }
+ | thir::ExprKind::Closure(_)
+ | thir::ExprKind::Literal { .. }
+ | thir::ExprKind::NonHirLiteral { .. }
+ | thir::ExprKind::ZstLiteral { .. }
+ | thir::ExprKind::StaticRef { .. }
+ | thir::ExprKind::InlineAsm(_)
+ | thir::ExprKind::ThreadLocalRef(_)
+ | thir::ExprKind::Yield { .. } => false,
}
}
fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
assumed_wf_types.extend(liberated_sig.inputs_and_output);
tcx.intern_type_list(&assumed_wf_types)
}
- DefKind::Impl => match tcx.impl_trait_ref(def_id) {
- Some(trait_ref) => {
- let types: Vec<_> = trait_ref.substs.types().collect();
- tcx.intern_type_list(&types)
+ DefKind::Impl => {
+ match tcx.impl_trait_ref(def_id) {
+ Some(trait_ref) => {
+ let types: Vec<_> = trait_ref.skip_binder().substs.types().collect();
+ tcx.intern_type_list(&types)
+ }
+ // Only the impl self type
+ None => tcx.intern_type_list(&[tcx.type_of(def_id)]),
}
- // Only the impl self type
- None => tcx.intern_type_list(&[tcx.type_of(def_id)]),
- },
+ }
DefKind::AssocConst | DefKind::AssocTy => tcx.assumed_wf_types(tcx.parent(def_id)),
DefKind::Mod
| DefKind::Struct
#![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"]
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>(
constness,
);
- let body_id =
- local_did.and_then(|id| tcx.hir().maybe_body_owned_by(id).map(|body| body.hir_id));
- let body_id = match body_id {
- Some(id) => id,
- None if hir_id.is_some() => hir_id.unwrap(),
- _ => hir::CRATE_HIR_ID,
- };
-
+ let body_id = local_did.unwrap_or(CRATE_DEF_ID);
let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id);
traits::normalize_param_env_or_error(tcx, unnormalized_env, cause)
}
// In a trait impl, we assume that the header trait ref and all its
// constituents are well-formed.
NodeKind::TraitImpl => {
- let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl");
+ let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl").subst_identity();
// FIXME(chalk): this has problems because of late-bound regions
//inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk()));
let trait_ref = tcx
.impl_trait_ref(def_id)
- .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id));
+ .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id))
+ .skip_binder();
debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref);
///
/// Note that inference variables and bound regions are not included
/// in this diagram. In the case of inference variables, they should
-/// be inferred to some other region from the diagram. In the case of
+/// be inferred to some other region from the diagram. In the case of
/// bound regions, they are excluded because they don't make sense to
/// include -- the diagram indicates the relationship between free
/// regions.
# be built if `extended = true`.
#extended = false
-# Installs chosen set of extended tools if `extended = true`. By default builds
-# all extended tools except `rust-demangler`, unless the target is also being
-# built with `profiler = true`. If chosen tool failed to build the installation
-# fails. If `extended = false`, this option is ignored.
-#tools = ["cargo", "rls", "clippy", "rustfmt", "analysis", "src"] # + "rust-demangler" if `profiler`
+# Set of tools to be included in the installation.
+#
+# If `extended = false`, the only one of these built by default is rustdoc.
+#
+# If `extended = true`, they're all included, with the exception of
+# rust-demangler which additionally requires `profiler = true` to be set.
+#
+# If any enabled tool fails to build, the installation fails.
+#tools = [
+# "cargo",
+# "clippy",
+# "rustdoc",
+# "rustfmt",
+# "rust-analyzer",
+# "analysis",
+# "src",
+# "rust-demangler", # if profiler = true
+#]
# Verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
#verbose = 0
mod tests;
extern "Rust" {
- // These are the magic symbols to call the global allocator. rustc generates
+ // These are the magic symbols to call the global allocator. rustc generates
// them to call `__rg_alloc` etc. if there is a `#[global_allocator]` attribute
// (the code expanding that attribute macro generates those functions), or to call
// the default implementations in std (`__rdl_alloc` etc. in `library/std/src/alloc.rs`)
#[cfg(not(no_global_oom_handling))]
extern "Rust" {
- // This is the magic symbol to call the global alloc error handler. rustc generates
+ // This is the magic symbol to call the global alloc error handler. rustc generates
// it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
// default implementations below (`__rdl_oom`) otherwise.
fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
#[unstable(feature = "thin_box", issue = "92791")]
impl<T> ThinBox<T> {
- /// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on
+ /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
/// the stack.
///
/// # Examples
///
/// let five = ThinBox::new(5);
/// ```
+ ///
+ /// [`Metadata`]: core::ptr::Pointee::Metadata
#[cfg(not(no_global_oom_handling))]
pub fn new(value: T) -> Self {
let meta = ptr::metadata(&value);
#[unstable(feature = "thin_box", issue = "92791")]
impl<Dyn: ?Sized> ThinBox<Dyn> {
- /// Moves a type to the heap with its `Metadata` stored in the heap allocation instead of on
+ /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
/// the stack.
///
/// # Examples
///
/// let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]);
/// ```
+ ///
+ /// [`Metadata`]: core::ptr::Pointee::Metadata
#[cfg(not(no_global_oom_handling))]
pub fn new_unsize<T>(value: T) -> Self
where
+++ /dev/null
-//! A priority queue implemented with a binary heap.
-//!
-//! Insertion and popping the largest element have *O*(log(*n*)) time complexity.
-//! Checking the largest element is *O*(1). Converting a vector to a binary heap
-//! can be done in-place, and has *O*(*n*) complexity. A binary heap can also be
-//! converted to a sorted vector in-place, allowing it to be used for an *O*(*n* * log(*n*))
-//! in-place heapsort.
-//!
-//! # Examples
-//!
-//! This is a larger example that implements [Dijkstra's algorithm][dijkstra]
-//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph].
-//! It shows how to use [`BinaryHeap`] with custom types.
-//!
-//! [dijkstra]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
-//! [sssp]: https://en.wikipedia.org/wiki/Shortest_path_problem
-//! [dir_graph]: https://en.wikipedia.org/wiki/Directed_graph
-//!
-//! ```
-//! use std::cmp::Ordering;
-//! use std::collections::BinaryHeap;
-//!
-//! #[derive(Copy, Clone, Eq, PartialEq)]
-//! struct State {
-//! cost: usize,
-//! position: usize,
-//! }
-//!
-//! // The priority queue depends on `Ord`.
-//! // Explicitly implement the trait so the queue becomes a min-heap
-//! // instead of a max-heap.
-//! impl Ord for State {
-//! fn cmp(&self, other: &Self) -> Ordering {
-//! // Notice that the we flip the ordering on costs.
-//! // In case of a tie we compare positions - this step is necessary
-//! // to make implementations of `PartialEq` and `Ord` consistent.
-//! other.cost.cmp(&self.cost)
-//! .then_with(|| self.position.cmp(&other.position))
-//! }
-//! }
-//!
-//! // `PartialOrd` needs to be implemented as well.
-//! impl PartialOrd for State {
-//! fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-//! Some(self.cmp(other))
-//! }
-//! }
-//!
-//! // Each node is represented as a `usize`, for a shorter implementation.
-//! struct Edge {
-//! node: usize,
-//! cost: usize,
-//! }
-//!
-//! // Dijkstra's shortest path algorithm.
-//!
-//! // Start at `start` and use `dist` to track the current shortest distance
-//! // to each node. This implementation isn't memory-efficient as it may leave duplicate
-//! // nodes in the queue. It also uses `usize::MAX` as a sentinel value,
-//! // for a simpler implementation.
-//! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: usize, goal: usize) -> Option<usize> {
-//! // dist[node] = current shortest distance from `start` to `node`
-//! let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect();
-//!
-//! let mut heap = BinaryHeap::new();
-//!
-//! // We're at `start`, with a zero cost
-//! dist[start] = 0;
-//! heap.push(State { cost: 0, position: start });
-//!
-//! // Examine the frontier with lower cost nodes first (min-heap)
-//! while let Some(State { cost, position }) = heap.pop() {
-//! // Alternatively we could have continued to find all shortest paths
-//! if position == goal { return Some(cost); }
-//!
-//! // Important as we may have already found a better way
-//! if cost > dist[position] { continue; }
-//!
-//! // For each node we can reach, see if we can find a way with
-//! // a lower cost going through this node
-//! for edge in &adj_list[position] {
-//! let next = State { cost: cost + edge.cost, position: edge.node };
-//!
-//! // If so, add it to the frontier and continue
-//! if next.cost < dist[next.position] {
-//! heap.push(next);
-//! // Relaxation, we have now found a better way
-//! dist[next.position] = next.cost;
-//! }
-//! }
-//! }
-//!
-//! // Goal not reachable
-//! None
-//! }
-//!
-//! fn main() {
-//! // This is the directed graph we're going to use.
-//! // The node numbers correspond to the different states,
-//! // and the edge weights symbolize the cost of moving
-//! // from one node to another.
-//! // Note that the edges are one-way.
-//! //
-//! // 7
-//! // +-----------------+
-//! // | |
-//! // v 1 2 | 2
-//! // 0 -----> 1 -----> 3 ---> 4
-//! // | ^ ^ ^
-//! // | | 1 | |
-//! // | | | 3 | 1
-//! // +------> 2 -------+ |
-//! // 10 | |
-//! // +---------------+
-//! //
-//! // The graph is represented as an adjacency list where each index,
-//! // corresponding to a node value, has a list of outgoing edges.
-//! // Chosen for its efficiency.
-//! let graph = vec![
-//! // Node 0
-//! vec![Edge { node: 2, cost: 10 },
-//! Edge { node: 1, cost: 1 }],
-//! // Node 1
-//! vec![Edge { node: 3, cost: 2 }],
-//! // Node 2
-//! vec![Edge { node: 1, cost: 1 },
-//! Edge { node: 3, cost: 3 },
-//! Edge { node: 4, cost: 1 }],
-//! // Node 3
-//! vec![Edge { node: 0, cost: 7 },
-//! Edge { node: 4, cost: 2 }],
-//! // Node 4
-//! vec![]];
-//!
-//! assert_eq!(shortest_path(&graph, 0, 1), Some(1));
-//! assert_eq!(shortest_path(&graph, 0, 3), Some(3));
-//! assert_eq!(shortest_path(&graph, 3, 0), Some(7));
-//! assert_eq!(shortest_path(&graph, 0, 4), Some(5));
-//! assert_eq!(shortest_path(&graph, 4, 0), None);
-//! }
-//! ```
-
-#![allow(missing_docs)]
-#![stable(feature = "rust1", since = "1.0.0")]
-
-use core::fmt;
-use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
-use core::mem::{self, swap, ManuallyDrop};
-use core::ops::{Deref, DerefMut};
-use core::ptr;
-
-use crate::collections::TryReserveError;
-use crate::slice;
-use crate::vec::{self, AsVecIntoIter, Vec};
-
-use super::SpecExtend;
-
-#[cfg(test)]
-mod tests;
-
-/// A priority queue implemented with a binary heap.
-///
-/// This will be a max-heap.
-///
-/// It is a logic error for an item to be modified in such a way that the
-/// item's ordering relative to any other item, as determined by the [`Ord`]
-/// trait, changes while it is in the heap. This is normally only possible
-/// through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. The
-/// behavior resulting from such a logic error is not specified, but will
-/// be encapsulated to the `BinaryHeap` that observed the logic error and not
-/// result in undefined behavior. This could include panics, incorrect results,
-/// aborts, memory leaks, and non-termination.
-///
-/// # Examples
-///
-/// ```
-/// use std::collections::BinaryHeap;
-///
-/// // Type inference lets us omit an explicit type signature (which
-/// // would be `BinaryHeap<i32>` in this example).
-/// let mut heap = BinaryHeap::new();
-///
-/// // We can use peek to look at the next item in the heap. In this case,
-/// // there's no items in there yet so we get None.
-/// assert_eq!(heap.peek(), None);
-///
-/// // Let's add some scores...
-/// heap.push(1);
-/// heap.push(5);
-/// heap.push(2);
-///
-/// // Now peek shows the most important item in the heap.
-/// assert_eq!(heap.peek(), Some(&5));
-///
-/// // We can check the length of a heap.
-/// assert_eq!(heap.len(), 3);
-///
-/// // We can iterate over the items in the heap, although they are returned in
-/// // a random order.
-/// for x in &heap {
-/// println!("{x}");
-/// }
-///
-/// // If we instead pop these scores, they should come back in order.
-/// assert_eq!(heap.pop(), Some(5));
-/// assert_eq!(heap.pop(), Some(2));
-/// assert_eq!(heap.pop(), Some(1));
-/// assert_eq!(heap.pop(), None);
-///
-/// // We can clear the heap of any remaining items.
-/// heap.clear();
-///
-/// // The heap should now be empty.
-/// assert!(heap.is_empty())
-/// ```
-///
-/// A `BinaryHeap` with a known list of items can be initialized from an array:
-///
-/// ```
-/// use std::collections::BinaryHeap;
-///
-/// let heap = BinaryHeap::from([1, 5, 2]);
-/// ```
-///
-/// ## Min-heap
-///
-/// Either [`core::cmp::Reverse`] or a custom [`Ord`] implementation can be used to
-/// make `BinaryHeap` a min-heap. This makes `heap.pop()` return the smallest
-/// value instead of the greatest one.
-///
-/// ```
-/// use std::collections::BinaryHeap;
-/// use std::cmp::Reverse;
-///
-/// let mut heap = BinaryHeap::new();
-///
-/// // Wrap values in `Reverse`
-/// heap.push(Reverse(1));
-/// heap.push(Reverse(5));
-/// heap.push(Reverse(2));
-///
-/// // If we pop these scores now, they should come back in the reverse order.
-/// assert_eq!(heap.pop(), Some(Reverse(1)));
-/// assert_eq!(heap.pop(), Some(Reverse(2)));
-/// assert_eq!(heap.pop(), Some(Reverse(5)));
-/// assert_eq!(heap.pop(), None);
-/// ```
-///
-/// # Time complexity
-///
-/// | [push] | [pop] | [peek]/[peek\_mut] |
-/// |---------|---------------|--------------------|
-/// | *O*(1)~ | *O*(log(*n*)) | *O*(1) |
-///
-/// The value for `push` is an expected cost; the method documentation gives a
-/// more detailed analysis.
-///
-/// [`core::cmp::Reverse`]: core::cmp::Reverse
-/// [`Ord`]: core::cmp::Ord
-/// [`Cell`]: core::cell::Cell
-/// [`RefCell`]: core::cell::RefCell
-/// [push]: BinaryHeap::push
-/// [pop]: BinaryHeap::pop
-/// [peek]: BinaryHeap::peek
-/// [peek\_mut]: BinaryHeap::peek_mut
-#[stable(feature = "rust1", since = "1.0.0")]
-#[cfg_attr(not(test), rustc_diagnostic_item = "BinaryHeap")]
-pub struct BinaryHeap<T> {
- data: Vec<T>,
-}
-
-/// Structure wrapping a mutable reference to the greatest item on a
-/// `BinaryHeap`.
-///
-/// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See
-/// its documentation for more.
-///
-/// [`peek_mut`]: BinaryHeap::peek_mut
-#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
-pub struct PeekMut<'a, T: 'a + Ord> {
- heap: &'a mut BinaryHeap<T>,
- sift: bool,
-}
-
-#[stable(feature = "collection_debug", since = "1.17.0")]
-impl<T: Ord + fmt::Debug> fmt::Debug for PeekMut<'_, T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("PeekMut").field(&self.heap.data[0]).finish()
- }
-}
-
-#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
-impl<T: Ord> Drop for PeekMut<'_, T> {
- fn drop(&mut self) {
- if self.sift {
- // SAFETY: PeekMut is only instantiated for non-empty heaps.
- unsafe { self.heap.sift_down(0) };
- }
- }
-}
-
-#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
-impl<T: Ord> Deref for PeekMut<'_, T> {
- type Target = T;
- fn deref(&self) -> &T {
- debug_assert!(!self.heap.is_empty());
- // SAFE: PeekMut is only instantiated for non-empty heaps
- unsafe { self.heap.data.get_unchecked(0) }
- }
-}
-
-#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
-impl<T: Ord> DerefMut for PeekMut<'_, T> {
- fn deref_mut(&mut self) -> &mut T {
- debug_assert!(!self.heap.is_empty());
- self.sift = true;
- // SAFE: PeekMut is only instantiated for non-empty heaps
- unsafe { self.heap.data.get_unchecked_mut(0) }
- }
-}
-
-impl<'a, T: Ord> PeekMut<'a, T> {
- /// Removes the peeked value from the heap and returns it.
- #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")]
- pub fn pop(mut this: PeekMut<'a, T>) -> T {
- let value = this.heap.pop().unwrap();
- this.sift = false;
- value
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Clone> Clone for BinaryHeap<T> {
- fn clone(&self) -> Self {
- BinaryHeap { data: self.data.clone() }
- }
-
- fn clone_from(&mut self, source: &Self) {
- self.data.clone_from(&source.data);
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Ord> Default for BinaryHeap<T> {
- /// Creates an empty `BinaryHeap<T>`.
- #[inline]
- fn default() -> BinaryHeap<T> {
- BinaryHeap::new()
- }
-}
-
-#[stable(feature = "binaryheap_debug", since = "1.4.0")]
-impl<T: fmt::Debug> fmt::Debug for BinaryHeap<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_list().entries(self.iter()).finish()
- }
-}
-
-impl<T: Ord> BinaryHeap<T> {
- /// Creates an empty `BinaryHeap` as a max-heap.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- /// heap.push(4);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- pub fn new() -> BinaryHeap<T> {
- BinaryHeap { data: vec![] }
- }
-
- /// Creates an empty `BinaryHeap` with at least the specified capacity.
- ///
- /// The binary heap will be able to hold at least `capacity` elements without
- /// reallocating. This method is allowed to allocate for more elements than
- /// `capacity`. If `capacity` is 0, the binary heap will not allocate.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::with_capacity(10);
- /// heap.push(4);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- #[must_use]
- pub fn with_capacity(capacity: usize) -> BinaryHeap<T> {
- BinaryHeap { data: Vec::with_capacity(capacity) }
- }
-
- /// Returns a mutable reference to the greatest item in the binary heap, or
- /// `None` if it is empty.
- ///
- /// Note: If the `PeekMut` value is leaked, the heap may be in an
- /// inconsistent state.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- /// assert!(heap.peek_mut().is_none());
- ///
- /// heap.push(1);
- /// heap.push(5);
- /// heap.push(2);
- /// {
- /// let mut val = heap.peek_mut().unwrap();
- /// *val = 0;
- /// }
- /// assert_eq!(heap.peek(), Some(&2));
- /// ```
- ///
- /// # Time complexity
- ///
- /// If the item is modified then the worst case time complexity is *O*(log(*n*)),
- /// otherwise it's *O*(1).
- #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
- pub fn peek_mut(&mut self) -> Option<PeekMut<'_, T>> {
- if self.is_empty() { None } else { Some(PeekMut { heap: self, sift: false }) }
- }
-
- /// Removes the greatest item from the binary heap and returns it, or `None` if it
- /// is empty.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::from([1, 3]);
- ///
- /// assert_eq!(heap.pop(), Some(3));
- /// assert_eq!(heap.pop(), Some(1));
- /// assert_eq!(heap.pop(), None);
- /// ```
- ///
- /// # Time complexity
- ///
- /// The worst case cost of `pop` on a heap containing *n* elements is *O*(log(*n*)).
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn pop(&mut self) -> Option<T> {
- self.data.pop().map(|mut item| {
- if !self.is_empty() {
- swap(&mut item, &mut self.data[0]);
- // SAFETY: !self.is_empty() means that self.len() > 0
- unsafe { self.sift_down_to_bottom(0) };
- }
- item
- })
- }
-
- /// Pushes an item onto the binary heap.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- /// heap.push(3);
- /// heap.push(5);
- /// heap.push(1);
- ///
- /// assert_eq!(heap.len(), 3);
- /// assert_eq!(heap.peek(), Some(&5));
- /// ```
- ///
- /// # Time complexity
- ///
- /// The expected cost of `push`, averaged over every possible ordering of
- /// the elements being pushed, and over a sufficiently large number of
- /// pushes, is *O*(1). This is the most meaningful cost metric when pushing
- /// elements that are *not* already in any sorted pattern.
- ///
- /// The time complexity degrades if elements are pushed in predominantly
- /// ascending order. In the worst case, elements are pushed in ascending
- /// sorted order and the amortized cost per push is *O*(log(*n*)) against a heap
- /// containing *n* elements.
- ///
- /// The worst case cost of a *single* call to `push` is *O*(*n*). The worst case
- /// occurs when capacity is exhausted and needs a resize. The resize cost
- /// has been amortized in the previous figures.
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn push(&mut self, item: T) {
- let old_len = self.len();
- self.data.push(item);
- // SAFETY: Since we pushed a new item it means that
- // old_len = self.len() - 1 < self.len()
- unsafe { self.sift_up(0, old_len) };
- }
-
- /// Consumes the `BinaryHeap` and returns a vector in sorted
- /// (ascending) order.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- ///
- /// let mut heap = BinaryHeap::from([1, 2, 4, 5, 7]);
- /// heap.push(6);
- /// heap.push(3);
- ///
- /// let vec = heap.into_sorted_vec();
- /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]);
- /// ```
- #[must_use = "`self` will be dropped if the result is not used"]
- #[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
- pub fn into_sorted_vec(mut self) -> Vec<T> {
- let mut end = self.len();
- while end > 1 {
- end -= 1;
- // SAFETY: `end` goes from `self.len() - 1` to 1 (both included),
- // so it's always a valid index to access.
- // It is safe to access index 0 (i.e. `ptr`), because
- // 1 <= end < self.len(), which means self.len() >= 2.
- unsafe {
- let ptr = self.data.as_mut_ptr();
- ptr::swap(ptr, ptr.add(end));
- }
- // SAFETY: `end` goes from `self.len() - 1` to 1 (both included) so:
- // 0 < 1 <= end <= self.len() - 1 < self.len()
- // Which means 0 < end and end < self.len().
- unsafe { self.sift_down_range(0, end) };
- }
- self.into_vec()
- }
-
- // The implementations of sift_up and sift_down use unsafe blocks in
- // order to move an element out of the vector (leaving behind a
- // hole), shift along the others and move the removed element back into the
- // vector at the final location of the hole.
- // The `Hole` type is used to represent this, and make sure
- // the hole is filled back at the end of its scope, even on panic.
- // Using a hole reduces the constant factor compared to using swaps,
- // which involves twice as many moves.
-
- /// # Safety
- ///
- /// The caller must guarantee that `pos < self.len()`.
- unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize {
- // Take out the value at `pos` and create a hole.
- // SAFETY: The caller guarantees that pos < self.len()
- let mut hole = unsafe { Hole::new(&mut self.data, pos) };
-
- while hole.pos() > start {
- let parent = (hole.pos() - 1) / 2;
-
- // SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0
- // and so hole.pos() - 1 can't underflow.
- // This guarantees that parent < hole.pos() so
- // it's a valid index and also != hole.pos().
- if hole.element() <= unsafe { hole.get(parent) } {
- break;
- }
-
- // SAFETY: Same as above
- unsafe { hole.move_to(parent) };
- }
-
- hole.pos()
- }
-
- /// Take an element at `pos` and move it down the heap,
- /// while its children are larger.
- ///
- /// # Safety
- ///
- /// The caller must guarantee that `pos < end <= self.len()`.
- unsafe fn sift_down_range(&mut self, pos: usize, end: usize) {
- // SAFETY: The caller guarantees that pos < end <= self.len().
- let mut hole = unsafe { Hole::new(&mut self.data, pos) };
- let mut child = 2 * hole.pos() + 1;
-
- // Loop invariant: child == 2 * hole.pos() + 1.
- while child <= end.saturating_sub(2) {
- // compare with the greater of the two children
- // SAFETY: child < end - 1 < self.len() and
- // child + 1 < end <= self.len(), so they're valid indexes.
- // child == 2 * hole.pos() + 1 != hole.pos() and
- // child + 1 == 2 * hole.pos() + 2 != hole.pos().
- // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
- // if T is a ZST
- child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
-
- // if we are already in order, stop.
- // SAFETY: child is now either the old child or the old child+1
- // We already proven that both are < self.len() and != hole.pos()
- if hole.element() >= unsafe { hole.get(child) } {
- return;
- }
-
- // SAFETY: same as above.
- unsafe { hole.move_to(child) };
- child = 2 * hole.pos() + 1;
- }
-
- // SAFETY: && short circuit, which means that in the
- // second condition it's already true that child == end - 1 < self.len().
- if child == end - 1 && hole.element() < unsafe { hole.get(child) } {
- // SAFETY: child is already proven to be a valid index and
- // child == 2 * hole.pos() + 1 != hole.pos().
- unsafe { hole.move_to(child) };
- }
- }
-
- /// # Safety
- ///
- /// The caller must guarantee that `pos < self.len()`.
- unsafe fn sift_down(&mut self, pos: usize) {
- let len = self.len();
- // SAFETY: pos < len is guaranteed by the caller and
- // obviously len = self.len() <= self.len().
- unsafe { self.sift_down_range(pos, len) };
- }
-
- /// Take an element at `pos` and move it all the way down the heap,
- /// then sift it up to its position.
- ///
- /// Note: This is faster when the element is known to be large / should
- /// be closer to the bottom.
- ///
- /// # Safety
- ///
- /// The caller must guarantee that `pos < self.len()`.
- unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) {
- let end = self.len();
- let start = pos;
-
- // SAFETY: The caller guarantees that pos < self.len().
- let mut hole = unsafe { Hole::new(&mut self.data, pos) };
- let mut child = 2 * hole.pos() + 1;
-
- // Loop invariant: child == 2 * hole.pos() + 1.
- while child <= end.saturating_sub(2) {
- // SAFETY: child < end - 1 < self.len() and
- // child + 1 < end <= self.len(), so they're valid indexes.
- // child == 2 * hole.pos() + 1 != hole.pos() and
- // child + 1 == 2 * hole.pos() + 2 != hole.pos().
- // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
- // if T is a ZST
- child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
-
- // SAFETY: Same as above
- unsafe { hole.move_to(child) };
- child = 2 * hole.pos() + 1;
- }
-
- if child == end - 1 {
- // SAFETY: child == end - 1 < self.len(), so it's a valid index
- // and child == 2 * hole.pos() + 1 != hole.pos().
- unsafe { hole.move_to(child) };
- }
- pos = hole.pos();
- drop(hole);
-
- // SAFETY: pos is the position in the hole and was already proven
- // to be a valid index.
- unsafe { self.sift_up(start, pos) };
- }
-
- /// Rebuild assuming data[0..start] is still a proper heap.
- fn rebuild_tail(&mut self, start: usize) {
- if start == self.len() {
- return;
- }
-
- let tail_len = self.len() - start;
-
- #[inline(always)]
- fn log2_fast(x: usize) -> usize {
- (usize::BITS - x.leading_zeros() - 1) as usize
- }
-
- // `rebuild` takes O(self.len()) operations
- // and about 2 * self.len() comparisons in the worst case
- // while repeating `sift_up` takes O(tail_len * log(start)) operations
- // and about 1 * tail_len * log_2(start) comparisons in the worst case,
- // assuming start >= tail_len. For larger heaps, the crossover point
- // no longer follows this reasoning and was determined empirically.
- let better_to_rebuild = if start < tail_len {
- true
- } else if self.len() <= 2048 {
- 2 * self.len() < tail_len * log2_fast(start)
- } else {
- 2 * self.len() < tail_len * 11
- };
-
- if better_to_rebuild {
- self.rebuild();
- } else {
- for i in start..self.len() {
- // SAFETY: The index `i` is always less than self.len().
- unsafe { self.sift_up(0, i) };
- }
- }
- }
-
- fn rebuild(&mut self) {
- let mut n = self.len() / 2;
- while n > 0 {
- n -= 1;
- // SAFETY: n starts from self.len() / 2 and goes down to 0.
- // The only case when !(n < self.len()) is if
- // self.len() == 0, but it's ruled out by the loop condition.
- unsafe { self.sift_down(n) };
- }
- }
-
- /// Moves all the elements of `other` into `self`, leaving `other` empty.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- ///
- /// let mut a = BinaryHeap::from([-10, 1, 2, 3, 3]);
- /// let mut b = BinaryHeap::from([-20, 5, 43]);
- ///
- /// a.append(&mut b);
- ///
- /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
- /// assert!(b.is_empty());
- /// ```
- #[stable(feature = "binary_heap_append", since = "1.11.0")]
- pub fn append(&mut self, other: &mut Self) {
- if self.len() < other.len() {
- swap(self, other);
- }
-
- let start = self.data.len();
-
- self.data.append(&mut other.data);
-
- self.rebuild_tail(start);
- }
-
- /// Clears the binary heap, returning an iterator over the removed elements
- /// in heap order. If the iterator is dropped before being fully consumed,
- /// it drops the remaining elements in heap order.
- ///
- /// The returned iterator keeps a mutable borrow on the heap to optimize
- /// its implementation.
- ///
- /// Note:
- /// * `.drain_sorted()` is *O*(*n* \* log(*n*)); much slower than `.drain()`.
- /// You should use the latter for most cases.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// #![feature(binary_heap_drain_sorted)]
- /// use std::collections::BinaryHeap;
- ///
- /// let mut heap = BinaryHeap::from([1, 2, 3, 4, 5]);
- /// assert_eq!(heap.len(), 5);
- ///
- /// drop(heap.drain_sorted()); // removes all elements in heap order
- /// assert_eq!(heap.len(), 0);
- /// ```
- #[inline]
- #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
- pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> {
- DrainSorted { inner: self }
- }
-
- /// Retains only the elements specified by the predicate.
- ///
- /// In other words, remove all elements `e` for which `f(&e)` returns
- /// `false`. The elements are visited in unsorted (and unspecified) order.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// #![feature(binary_heap_retain)]
- /// use std::collections::BinaryHeap;
- ///
- /// let mut heap = BinaryHeap::from([-10, -5, 1, 2, 4, 13]);
- ///
- /// heap.retain(|x| x % 2 == 0); // only keep even numbers
- ///
- /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4])
- /// ```
- #[unstable(feature = "binary_heap_retain", issue = "71503")]
- pub fn retain<F>(&mut self, mut f: F)
- where
- F: FnMut(&T) -> bool,
- {
- let mut first_removed = self.len();
- let mut i = 0;
- self.data.retain(|e| {
- let keep = f(e);
- if !keep && i < first_removed {
- first_removed = i;
- }
- i += 1;
- keep
- });
- // data[0..first_removed] is untouched, so we only need to rebuild the tail:
- self.rebuild_tail(first_removed);
- }
-}
-
-impl<T> BinaryHeap<T> {
- /// Returns an iterator visiting all values in the underlying vector, in
- /// arbitrary order.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let heap = BinaryHeap::from([1, 2, 3, 4]);
- ///
- /// // Print 1, 2, 3, 4 in arbitrary order
- /// for x in heap.iter() {
- /// println!("{x}");
- /// }
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn iter(&self) -> Iter<'_, T> {
- Iter { iter: self.data.iter() }
- }
-
- /// Returns an iterator which retrieves elements in heap order.
- /// This method consumes the original heap.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// #![feature(binary_heap_into_iter_sorted)]
- /// use std::collections::BinaryHeap;
- /// let heap = BinaryHeap::from([1, 2, 3, 4, 5]);
- ///
- /// assert_eq!(heap.into_iter_sorted().take(2).collect::<Vec<_>>(), [5, 4]);
- /// ```
- #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
- pub fn into_iter_sorted(self) -> IntoIterSorted<T> {
- IntoIterSorted { inner: self }
- }
-
- /// Returns the greatest item in the binary heap, or `None` if it is empty.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- /// assert_eq!(heap.peek(), None);
- ///
- /// heap.push(1);
- /// heap.push(5);
- /// heap.push(2);
- /// assert_eq!(heap.peek(), Some(&5));
- ///
- /// ```
- ///
- /// # Time complexity
- ///
- /// Cost is *O*(1) in the worst case.
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn peek(&self) -> Option<&T> {
- self.data.get(0)
- }
-
- /// Returns the number of elements the binary heap can hold without reallocating.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::with_capacity(100);
- /// assert!(heap.capacity() >= 100);
- /// heap.push(4);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn capacity(&self) -> usize {
- self.data.capacity()
- }
-
- /// Reserves the minimum capacity for at least `additional` elements more than
- /// the current length. Unlike [`reserve`], this will not
- /// deliberately over-allocate to speculatively avoid frequent allocations.
- /// After calling `reserve_exact`, capacity will be greater than or equal to
- /// `self.len() + additional`. Does nothing if the capacity is already
- /// sufficient.
- ///
- /// [`reserve`]: BinaryHeap::reserve
- ///
- /// # Panics
- ///
- /// Panics if the new capacity overflows [`usize`].
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- /// heap.reserve_exact(100);
- /// assert!(heap.capacity() >= 100);
- /// heap.push(4);
- /// ```
- ///
- /// [`reserve`]: BinaryHeap::reserve
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn reserve_exact(&mut self, additional: usize) {
- self.data.reserve_exact(additional);
- }
-
- /// Reserves capacity for at least `additional` elements more than the
- /// current length. The allocator may reserve more space to speculatively
- /// avoid frequent allocations. After calling `reserve`,
- /// capacity will be greater than or equal to `self.len() + additional`.
- /// Does nothing if capacity is already sufficient.
- ///
- /// # Panics
- ///
- /// Panics if the new capacity overflows [`usize`].
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- /// heap.reserve(100);
- /// assert!(heap.capacity() >= 100);
- /// heap.push(4);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn reserve(&mut self, additional: usize) {
- self.data.reserve(additional);
- }
-
- /// Tries to reserve the minimum capacity for at least `additional` elements
- /// more than the current length. Unlike [`try_reserve`], this will not
- /// deliberately over-allocate to speculatively avoid frequent allocations.
- /// After calling `try_reserve_exact`, capacity will be greater than or
- /// equal to `self.len() + additional` if it returns `Ok(())`.
- /// Does nothing if the capacity is already sufficient.
- ///
- /// Note that the allocator may give the collection more space than it
- /// requests. Therefore, capacity can not be relied upon to be precisely
- /// minimal. Prefer [`try_reserve`] if future insertions are expected.
- ///
- /// [`try_reserve`]: BinaryHeap::try_reserve
- ///
- /// # Errors
- ///
- /// If the capacity overflows, or the allocator reports a failure, then an error
- /// is returned.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// use std::collections::TryReserveError;
- ///
- /// fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
- /// let mut heap = BinaryHeap::new();
- ///
- /// // Pre-reserve the memory, exiting if we can't
- /// heap.try_reserve_exact(data.len())?;
- ///
- /// // Now we know this can't OOM in the middle of our complex work
- /// heap.extend(data.iter());
- ///
- /// Ok(heap.pop())
- /// }
- /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
- /// ```
- #[stable(feature = "try_reserve_2", since = "1.63.0")]
- pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
- self.data.try_reserve_exact(additional)
- }
-
- /// Tries to reserve capacity for at least `additional` elements more than the
- /// current length. The allocator may reserve more space to speculatively
- /// avoid frequent allocations. After calling `try_reserve`, capacity will be
- /// greater than or equal to `self.len() + additional` if it returns
- /// `Ok(())`. Does nothing if capacity is already sufficient. This method
- /// preserves the contents even if an error occurs.
- ///
- /// # Errors
- ///
- /// If the capacity overflows, or the allocator reports a failure, then an error
- /// is returned.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// use std::collections::TryReserveError;
- ///
- /// fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
- /// let mut heap = BinaryHeap::new();
- ///
- /// // Pre-reserve the memory, exiting if we can't
- /// heap.try_reserve(data.len())?;
- ///
- /// // Now we know this can't OOM in the middle of our complex work
- /// heap.extend(data.iter());
- ///
- /// Ok(heap.pop())
- /// }
- /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
- /// ```
- #[stable(feature = "try_reserve_2", since = "1.63.0")]
- pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
- self.data.try_reserve(additional)
- }
-
- /// Discards as much additional capacity as possible.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap: BinaryHeap<i32> = BinaryHeap::with_capacity(100);
- ///
- /// assert!(heap.capacity() >= 100);
- /// heap.shrink_to_fit();
- /// assert!(heap.capacity() == 0);
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn shrink_to_fit(&mut self) {
- self.data.shrink_to_fit();
- }
-
- /// Discards capacity with a lower bound.
- ///
- /// The capacity will remain at least as large as both the length
- /// and the supplied value.
- ///
- /// If the current capacity is less than the lower limit, this is a no-op.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap: BinaryHeap<i32> = BinaryHeap::with_capacity(100);
- ///
- /// assert!(heap.capacity() >= 100);
- /// heap.shrink_to(10);
- /// assert!(heap.capacity() >= 10);
- /// ```
- #[inline]
- #[stable(feature = "shrink_to", since = "1.56.0")]
- pub fn shrink_to(&mut self, min_capacity: usize) {
- self.data.shrink_to(min_capacity)
- }
-
- /// Returns a slice of all values in the underlying vector, in arbitrary
- /// order.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// #![feature(binary_heap_as_slice)]
- /// use std::collections::BinaryHeap;
- /// use std::io::{self, Write};
- ///
- /// let heap = BinaryHeap::from([1, 2, 3, 4, 5, 6, 7]);
- ///
- /// io::sink().write(heap.as_slice()).unwrap();
- /// ```
- #[must_use]
- #[unstable(feature = "binary_heap_as_slice", issue = "83659")]
- pub fn as_slice(&self) -> &[T] {
- self.data.as_slice()
- }
-
- /// Consumes the `BinaryHeap` and returns the underlying vector
- /// in arbitrary order.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let heap = BinaryHeap::from([1, 2, 3, 4, 5, 6, 7]);
- /// let vec = heap.into_vec();
- ///
- /// // Will print in some order
- /// for x in vec {
- /// println!("{x}");
- /// }
- /// ```
- #[must_use = "`self` will be dropped if the result is not used"]
- #[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
- pub fn into_vec(self) -> Vec<T> {
- self.into()
- }
-
- /// Returns the length of the binary heap.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let heap = BinaryHeap::from([1, 3]);
- ///
- /// assert_eq!(heap.len(), 2);
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn len(&self) -> usize {
- self.data.len()
- }
-
- /// Checks if the binary heap is empty.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::new();
- ///
- /// assert!(heap.is_empty());
- ///
- /// heap.push(3);
- /// heap.push(5);
- /// heap.push(1);
- ///
- /// assert!(!heap.is_empty());
- /// ```
- #[must_use]
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Clears the binary heap, returning an iterator over the removed elements
- /// in arbitrary order. If the iterator is dropped before being fully
- /// consumed, it drops the remaining elements in arbitrary order.
- ///
- /// The returned iterator keeps a mutable borrow on the heap to optimize
- /// its implementation.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::from([1, 3]);
- ///
- /// assert!(!heap.is_empty());
- ///
- /// for x in heap.drain() {
- /// println!("{x}");
- /// }
- ///
- /// assert!(heap.is_empty());
- /// ```
- #[inline]
- #[stable(feature = "drain", since = "1.6.0")]
- pub fn drain(&mut self) -> Drain<'_, T> {
- Drain { iter: self.data.drain(..) }
- }
-
- /// Drops all items from the binary heap.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let mut heap = BinaryHeap::from([1, 3]);
- ///
- /// assert!(!heap.is_empty());
- ///
- /// heap.clear();
- ///
- /// assert!(heap.is_empty());
- /// ```
- #[stable(feature = "rust1", since = "1.0.0")]
- pub fn clear(&mut self) {
- self.drain();
- }
-}
-
-/// Hole represents a hole in a slice i.e., an index without valid value
-/// (because it was moved from or duplicated).
-/// In drop, `Hole` will restore the slice by filling the hole
-/// position with the value that was originally removed.
-struct Hole<'a, T: 'a> {
- data: &'a mut [T],
- elt: ManuallyDrop<T>,
- pos: usize,
-}
-
-impl<'a, T> Hole<'a, T> {
- /// Create a new `Hole` at index `pos`.
- ///
- /// Unsafe because pos must be within the data slice.
- #[inline]
- unsafe fn new(data: &'a mut [T], pos: usize) -> Self {
- debug_assert!(pos < data.len());
- // SAFE: pos should be inside the slice
- let elt = unsafe { ptr::read(data.get_unchecked(pos)) };
- Hole { data, elt: ManuallyDrop::new(elt), pos }
- }
-
- #[inline]
- fn pos(&self) -> usize {
- self.pos
- }
-
- /// Returns a reference to the element removed.
- #[inline]
- fn element(&self) -> &T {
- &self.elt
- }
-
- /// Returns a reference to the element at `index`.
- ///
- /// Unsafe because index must be within the data slice and not equal to pos.
- #[inline]
- unsafe fn get(&self, index: usize) -> &T {
- debug_assert!(index != self.pos);
- debug_assert!(index < self.data.len());
- unsafe { self.data.get_unchecked(index) }
- }
-
- /// Move hole to new location
- ///
- /// Unsafe because index must be within the data slice and not equal to pos.
- #[inline]
- unsafe fn move_to(&mut self, index: usize) {
- debug_assert!(index != self.pos);
- debug_assert!(index < self.data.len());
- unsafe {
- let ptr = self.data.as_mut_ptr();
- let index_ptr: *const _ = ptr.add(index);
- let hole_ptr = ptr.add(self.pos);
- ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1);
- }
- self.pos = index;
- }
-}
-
-impl<T> Drop for Hole<'_, T> {
- #[inline]
- fn drop(&mut self) {
- // fill the hole again
- unsafe {
- let pos = self.pos;
- ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1);
- }
- }
-}
-
-/// An iterator over the elements of a `BinaryHeap`.
-///
-/// This `struct` is created by [`BinaryHeap::iter()`]. See its
-/// documentation for more.
-///
-/// [`iter`]: BinaryHeap::iter
-#[must_use = "iterators are lazy and do nothing unless consumed"]
-#[stable(feature = "rust1", since = "1.0.0")]
-pub struct Iter<'a, T: 'a> {
- iter: slice::Iter<'a, T>,
-}
-
-#[stable(feature = "collection_debug", since = "1.17.0")]
-impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("Iter").field(&self.iter.as_slice()).finish()
- }
-}
-
-// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Clone for Iter<'_, T> {
- fn clone(&self) -> Self {
- Iter { iter: self.iter.clone() }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, T> Iterator for Iter<'a, T> {
- type Item = &'a T;
-
- #[inline]
- fn next(&mut self) -> Option<&'a T> {
- self.iter.next()
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-
- #[inline]
- fn last(self) -> Option<&'a T> {
- self.iter.last()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
- #[inline]
- fn next_back(&mut self) -> Option<&'a T> {
- self.iter.next_back()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> ExactSizeIterator for Iter<'_, T> {
- fn is_empty(&self) -> bool {
- self.iter.is_empty()
- }
-}
-
-#[stable(feature = "fused", since = "1.26.0")]
-impl<T> FusedIterator for Iter<'_, T> {}
-
-/// An owning iterator over the elements of a `BinaryHeap`.
-///
-/// This `struct` is created by [`BinaryHeap::into_iter()`]
-/// (provided by the [`IntoIterator`] trait). See its documentation for more.
-///
-/// [`into_iter`]: BinaryHeap::into_iter
-/// [`IntoIterator`]: core::iter::IntoIterator
-#[stable(feature = "rust1", since = "1.0.0")]
-#[derive(Clone)]
-pub struct IntoIter<T> {
- iter: vec::IntoIter<T>,
-}
-
-#[stable(feature = "collection_debug", since = "1.17.0")]
-impl<T: fmt::Debug> fmt::Debug for IntoIter<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("IntoIter").field(&self.iter.as_slice()).finish()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> Iterator for IntoIter<T> {
- type Item = T;
-
- #[inline]
- fn next(&mut self) -> Option<T> {
- self.iter.next()
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> DoubleEndedIterator for IntoIter<T> {
- #[inline]
- fn next_back(&mut self) -> Option<T> {
- self.iter.next_back()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> ExactSizeIterator for IntoIter<T> {
- fn is_empty(&self) -> bool {
- self.iter.is_empty()
- }
-}
-
-#[stable(feature = "fused", since = "1.26.0")]
-impl<T> FusedIterator for IntoIter<T> {}
-
-// In addition to the SAFETY invariants of the following three unsafe traits
-// also refer to the vec::in_place_collect module documentation to get an overview
-#[unstable(issue = "none", feature = "inplace_iteration")]
-#[doc(hidden)]
-unsafe impl<T> SourceIter for IntoIter<T> {
- type Source = IntoIter<T>;
-
- #[inline]
- unsafe fn as_inner(&mut self) -> &mut Self::Source {
- self
- }
-}
-
-#[unstable(issue = "none", feature = "inplace_iteration")]
-#[doc(hidden)]
-unsafe impl<I> InPlaceIterable for IntoIter<I> {}
-
-unsafe impl<I> AsVecIntoIter for IntoIter<I> {
- type Item = I;
-
- fn as_into_iter(&mut self) -> &mut vec::IntoIter<Self::Item> {
- &mut self.iter
- }
-}
-
-#[must_use = "iterators are lazy and do nothing unless consumed"]
-#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
-#[derive(Clone, Debug)]
-pub struct IntoIterSorted<T> {
- inner: BinaryHeap<T>,
-}
-
-#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
-impl<T: Ord> Iterator for IntoIterSorted<T> {
- type Item = T;
-
- #[inline]
- fn next(&mut self) -> Option<T> {
- self.inner.pop()
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- let exact = self.inner.len();
- (exact, Some(exact))
- }
-}
-
-#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
-impl<T: Ord> ExactSizeIterator for IntoIterSorted<T> {}
-
-#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
-impl<T: Ord> FusedIterator for IntoIterSorted<T> {}
-
-#[unstable(feature = "trusted_len", issue = "37572")]
-unsafe impl<T: Ord> TrustedLen for IntoIterSorted<T> {}
-
-/// A draining iterator over the elements of a `BinaryHeap`.
-///
-/// This `struct` is created by [`BinaryHeap::drain()`]. See its
-/// documentation for more.
-///
-/// [`drain`]: BinaryHeap::drain
-#[stable(feature = "drain", since = "1.6.0")]
-#[derive(Debug)]
-pub struct Drain<'a, T: 'a> {
- iter: vec::Drain<'a, T>,
-}
-
-#[stable(feature = "drain", since = "1.6.0")]
-impl<T> Iterator for Drain<'_, T> {
- type Item = T;
-
- #[inline]
- fn next(&mut self) -> Option<T> {
- self.iter.next()
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- self.iter.size_hint()
- }
-}
-
-#[stable(feature = "drain", since = "1.6.0")]
-impl<T> DoubleEndedIterator for Drain<'_, T> {
- #[inline]
- fn next_back(&mut self) -> Option<T> {
- self.iter.next_back()
- }
-}
-
-#[stable(feature = "drain", since = "1.6.0")]
-impl<T> ExactSizeIterator for Drain<'_, T> {
- fn is_empty(&self) -> bool {
- self.iter.is_empty()
- }
-}
-
-#[stable(feature = "fused", since = "1.26.0")]
-impl<T> FusedIterator for Drain<'_, T> {}
-
-/// A draining iterator over the elements of a `BinaryHeap`.
-///
-/// This `struct` is created by [`BinaryHeap::drain_sorted()`]. See its
-/// documentation for more.
-///
-/// [`drain_sorted`]: BinaryHeap::drain_sorted
-#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
-#[derive(Debug)]
-pub struct DrainSorted<'a, T: Ord> {
- inner: &'a mut BinaryHeap<T>,
-}
-
-#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
-impl<'a, T: Ord> Drop for DrainSorted<'a, T> {
- /// Removes heap elements in heap order.
- fn drop(&mut self) {
- struct DropGuard<'r, 'a, T: Ord>(&'r mut DrainSorted<'a, T>);
-
- impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> {
- fn drop(&mut self) {
- while self.0.inner.pop().is_some() {}
- }
- }
-
- while let Some(item) = self.inner.pop() {
- let guard = DropGuard(self);
- drop(item);
- mem::forget(guard);
- }
- }
-}
-
-#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
-impl<T: Ord> Iterator for DrainSorted<'_, T> {
- type Item = T;
-
- #[inline]
- fn next(&mut self) -> Option<T> {
- self.inner.pop()
- }
-
- #[inline]
- fn size_hint(&self) -> (usize, Option<usize>) {
- let exact = self.inner.len();
- (exact, Some(exact))
- }
-}
-
-#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
-impl<T: Ord> ExactSizeIterator for DrainSorted<'_, T> {}
-
-#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
-impl<T: Ord> FusedIterator for DrainSorted<'_, T> {}
-
-#[unstable(feature = "trusted_len", issue = "37572")]
-unsafe impl<T: Ord> TrustedLen for DrainSorted<'_, T> {}
-
-#[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
-impl<T: Ord> From<Vec<T>> for BinaryHeap<T> {
- /// Converts a `Vec<T>` into a `BinaryHeap<T>`.
- ///
- /// This conversion happens in-place, and has *O*(*n*) time complexity.
- fn from(vec: Vec<T>) -> BinaryHeap<T> {
- let mut heap = BinaryHeap { data: vec };
- heap.rebuild();
- heap
- }
-}
-
-#[stable(feature = "std_collections_from_array", since = "1.56.0")]
-impl<T: Ord, const N: usize> From<[T; N]> for BinaryHeap<T> {
- /// ```
- /// use std::collections::BinaryHeap;
- ///
- /// let mut h1 = BinaryHeap::from([1, 4, 2, 3]);
- /// let mut h2: BinaryHeap<_> = [1, 4, 2, 3].into();
- /// while let Some((a, b)) = h1.pop().zip(h2.pop()) {
- /// assert_eq!(a, b);
- /// }
- /// ```
- fn from(arr: [T; N]) -> Self {
- Self::from_iter(arr)
- }
-}
-
-#[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
-impl<T> From<BinaryHeap<T>> for Vec<T> {
- /// Converts a `BinaryHeap<T>` into a `Vec<T>`.
- ///
- /// This conversion requires no data movement or allocation, and has
- /// constant time complexity.
- fn from(heap: BinaryHeap<T>) -> Vec<T> {
- heap.data
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Ord> FromIterator<T> for BinaryHeap<T> {
- fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> BinaryHeap<T> {
- BinaryHeap::from(iter.into_iter().collect::<Vec<_>>())
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T> IntoIterator for BinaryHeap<T> {
- type Item = T;
- type IntoIter = IntoIter<T>;
-
- /// Creates a consuming iterator, that is, one that moves each value out of
- /// the binary heap in arbitrary order. The binary heap cannot be used
- /// after calling this.
- ///
- /// # Examples
- ///
- /// Basic usage:
- ///
- /// ```
- /// use std::collections::BinaryHeap;
- /// let heap = BinaryHeap::from([1, 2, 3, 4]);
- ///
- /// // Print 1, 2, 3, 4 in arbitrary order
- /// for x in heap.into_iter() {
- /// // x has type i32, not &i32
- /// println!("{x}");
- /// }
- /// ```
- fn into_iter(self) -> IntoIter<T> {
- IntoIter { iter: self.data.into_iter() }
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, T> IntoIterator for &'a BinaryHeap<T> {
- type Item = &'a T;
- type IntoIter = Iter<'a, T>;
-
- fn into_iter(self) -> Iter<'a, T> {
- self.iter()
- }
-}
-
-#[stable(feature = "rust1", since = "1.0.0")]
-impl<T: Ord> Extend<T> for BinaryHeap<T> {
- #[inline]
- fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
- <Self as SpecExtend<I>>::spec_extend(self, iter);
- }
-
- #[inline]
- fn extend_one(&mut self, item: T) {
- self.push(item);
- }
-
- #[inline]
- fn extend_reserve(&mut self, additional: usize) {
- self.reserve(additional);
- }
-}
-
-impl<T: Ord, I: IntoIterator<Item = T>> SpecExtend<I> for BinaryHeap<T> {
- default fn spec_extend(&mut self, iter: I) {
- self.extend_desugared(iter.into_iter());
- }
-}
-
-impl<T: Ord> SpecExtend<Vec<T>> for BinaryHeap<T> {
- fn spec_extend(&mut self, ref mut other: Vec<T>) {
- let start = self.data.len();
- self.data.append(other);
- self.rebuild_tail(start);
- }
-}
-
-impl<T: Ord> SpecExtend<BinaryHeap<T>> for BinaryHeap<T> {
- fn spec_extend(&mut self, ref mut other: BinaryHeap<T>) {
- self.append(other);
- }
-}
-
-impl<T: Ord> BinaryHeap<T> {
- fn extend_desugared<I: IntoIterator<Item = T>>(&mut self, iter: I) {
- let iterator = iter.into_iter();
- let (lower, _) = iterator.size_hint();
-
- self.reserve(lower);
-
- iterator.for_each(move |elem| self.push(elem));
- }
-}
-
-#[stable(feature = "extend_ref", since = "1.2.0")]
-impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap<T> {
- fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
- self.extend(iter.into_iter().cloned());
- }
-
- #[inline]
- fn extend_one(&mut self, &item: &'a T) {
- self.push(item);
- }
-
- #[inline]
- fn extend_reserve(&mut self, additional: usize) {
- self.reserve(additional);
- }
-}
--- /dev/null
+//! A priority queue implemented with a binary heap.
+//!
+//! Insertion and popping the largest element have *O*(log(*n*)) time complexity.
+//! Checking the largest element is *O*(1). Converting a vector to a binary heap
+//! can be done in-place, and has *O*(*n*) complexity. A binary heap can also be
+//! converted to a sorted vector in-place, allowing it to be used for an *O*(*n* * log(*n*))
+//! in-place heapsort.
+//!
+//! # Examples
+//!
+//! This is a larger example that implements [Dijkstra's algorithm][dijkstra]
+//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph].
+//! It shows how to use [`BinaryHeap`] with custom types.
+//!
+//! [dijkstra]: https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
+//! [sssp]: https://en.wikipedia.org/wiki/Shortest_path_problem
+//! [dir_graph]: https://en.wikipedia.org/wiki/Directed_graph
+//!
+//! ```
+//! use std::cmp::Ordering;
+//! use std::collections::BinaryHeap;
+//!
+//! #[derive(Copy, Clone, Eq, PartialEq)]
+//! struct State {
+//! cost: usize,
+//! position: usize,
+//! }
+//!
+//! // The priority queue depends on `Ord`.
+//! // Explicitly implement the trait so the queue becomes a min-heap
+//! // instead of a max-heap.
+//! impl Ord for State {
+//! fn cmp(&self, other: &Self) -> Ordering {
+//! // Notice that the we flip the ordering on costs.
+//! // In case of a tie we compare positions - this step is necessary
+//! // to make implementations of `PartialEq` and `Ord` consistent.
+//! other.cost.cmp(&self.cost)
+//! .then_with(|| self.position.cmp(&other.position))
+//! }
+//! }
+//!
+//! // `PartialOrd` needs to be implemented as well.
+//! impl PartialOrd for State {
+//! fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+//! Some(self.cmp(other))
+//! }
+//! }
+//!
+//! // Each node is represented as a `usize`, for a shorter implementation.
+//! struct Edge {
+//! node: usize,
+//! cost: usize,
+//! }
+//!
+//! // Dijkstra's shortest path algorithm.
+//!
+//! // Start at `start` and use `dist` to track the current shortest distance
+//! // to each node. This implementation isn't memory-efficient as it may leave duplicate
+//! // nodes in the queue. It also uses `usize::MAX` as a sentinel value,
+//! // for a simpler implementation.
+//! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: usize, goal: usize) -> Option<usize> {
+//! // dist[node] = current shortest distance from `start` to `node`
+//! let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect();
+//!
+//! let mut heap = BinaryHeap::new();
+//!
+//! // We're at `start`, with a zero cost
+//! dist[start] = 0;
+//! heap.push(State { cost: 0, position: start });
+//!
+//! // Examine the frontier with lower cost nodes first (min-heap)
+//! while let Some(State { cost, position }) = heap.pop() {
+//! // Alternatively we could have continued to find all shortest paths
+//! if position == goal { return Some(cost); }
+//!
+//! // Important as we may have already found a better way
+//! if cost > dist[position] { continue; }
+//!
+//! // For each node we can reach, see if we can find a way with
+//! // a lower cost going through this node
+//! for edge in &adj_list[position] {
+//! let next = State { cost: cost + edge.cost, position: edge.node };
+//!
+//! // If so, add it to the frontier and continue
+//! if next.cost < dist[next.position] {
+//! heap.push(next);
+//! // Relaxation, we have now found a better way
+//! dist[next.position] = next.cost;
+//! }
+//! }
+//! }
+//!
+//! // Goal not reachable
+//! None
+//! }
+//!
+//! fn main() {
+//! // This is the directed graph we're going to use.
+//! // The node numbers correspond to the different states,
+//! // and the edge weights symbolize the cost of moving
+//! // from one node to another.
+//! // Note that the edges are one-way.
+//! //
+//! // 7
+//! // +-----------------+
+//! // | |
+//! // v 1 2 | 2
+//! // 0 -----> 1 -----> 3 ---> 4
+//! // | ^ ^ ^
+//! // | | 1 | |
+//! // | | | 3 | 1
+//! // +------> 2 -------+ |
+//! // 10 | |
+//! // +---------------+
+//! //
+//! // The graph is represented as an adjacency list where each index,
+//! // corresponding to a node value, has a list of outgoing edges.
+//! // Chosen for its efficiency.
+//! let graph = vec![
+//! // Node 0
+//! vec![Edge { node: 2, cost: 10 },
+//! Edge { node: 1, cost: 1 }],
+//! // Node 1
+//! vec![Edge { node: 3, cost: 2 }],
+//! // Node 2
+//! vec![Edge { node: 1, cost: 1 },
+//! Edge { node: 3, cost: 3 },
+//! Edge { node: 4, cost: 1 }],
+//! // Node 3
+//! vec![Edge { node: 0, cost: 7 },
+//! Edge { node: 4, cost: 2 }],
+//! // Node 4
+//! vec![]];
+//!
+//! assert_eq!(shortest_path(&graph, 0, 1), Some(1));
+//! assert_eq!(shortest_path(&graph, 0, 3), Some(3));
+//! assert_eq!(shortest_path(&graph, 3, 0), Some(7));
+//! assert_eq!(shortest_path(&graph, 0, 4), Some(5));
+//! assert_eq!(shortest_path(&graph, 4, 0), None);
+//! }
+//! ```
+
+#![allow(missing_docs)]
+#![stable(feature = "rust1", since = "1.0.0")]
+
+use core::fmt;
+use core::iter::{FromIterator, FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
+use core::mem::{self, swap, ManuallyDrop};
+use core::num::NonZeroUsize;
+use core::ops::{Deref, DerefMut};
+use core::ptr;
+
+use crate::collections::TryReserveError;
+use crate::slice;
+use crate::vec::{self, AsVecIntoIter, Vec};
+
+use super::SpecExtend;
+
+#[cfg(test)]
+mod tests;
+
+/// A priority queue implemented with a binary heap.
+///
+/// This will be a max-heap.
+///
+/// It is a logic error for an item to be modified in such a way that the
+/// item's ordering relative to any other item, as determined by the [`Ord`]
+/// trait, changes while it is in the heap. This is normally only possible
+/// through interior mutability, global state, I/O, or unsafe code. The
+/// behavior resulting from such a logic error is not specified, but will
+/// be encapsulated to the `BinaryHeap` that observed the logic error and not
+/// result in undefined behavior. This could include panics, incorrect results,
+/// aborts, memory leaks, and non-termination.
+///
+/// As long as no elements change their relative order while being in the heap
+/// as described above, the API of `BinaryHeap` guarantees that the heap
+/// invariant remains intact i.e. its methods all behave as documented. For
+/// example if a method is documented as iterating in sorted order, that's
+/// guaranteed to work as long as elements in the heap have not changed order,
+/// even in the presence of closures getting unwinded out of, iterators getting
+/// leaked, and similar foolishness.
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::BinaryHeap;
+///
+/// // Type inference lets us omit an explicit type signature (which
+/// // would be `BinaryHeap<i32>` in this example).
+/// let mut heap = BinaryHeap::new();
+///
+/// // We can use peek to look at the next item in the heap. In this case,
+/// // there's no items in there yet so we get None.
+/// assert_eq!(heap.peek(), None);
+///
+/// // Let's add some scores...
+/// heap.push(1);
+/// heap.push(5);
+/// heap.push(2);
+///
+/// // Now peek shows the most important item in the heap.
+/// assert_eq!(heap.peek(), Some(&5));
+///
+/// // We can check the length of a heap.
+/// assert_eq!(heap.len(), 3);
+///
+/// // We can iterate over the items in the heap, although they are returned in
+/// // a random order.
+/// for x in &heap {
+/// println!("{x}");
+/// }
+///
+/// // If we instead pop these scores, they should come back in order.
+/// assert_eq!(heap.pop(), Some(5));
+/// assert_eq!(heap.pop(), Some(2));
+/// assert_eq!(heap.pop(), Some(1));
+/// assert_eq!(heap.pop(), None);
+///
+/// // We can clear the heap of any remaining items.
+/// heap.clear();
+///
+/// // The heap should now be empty.
+/// assert!(heap.is_empty())
+/// ```
+///
+/// A `BinaryHeap` with a known list of items can be initialized from an array:
+///
+/// ```
+/// use std::collections::BinaryHeap;
+///
+/// let heap = BinaryHeap::from([1, 5, 2]);
+/// ```
+///
+/// ## Min-heap
+///
+/// Either [`core::cmp::Reverse`] or a custom [`Ord`] implementation can be used to
+/// make `BinaryHeap` a min-heap. This makes `heap.pop()` return the smallest
+/// value instead of the greatest one.
+///
+/// ```
+/// use std::collections::BinaryHeap;
+/// use std::cmp::Reverse;
+///
+/// let mut heap = BinaryHeap::new();
+///
+/// // Wrap values in `Reverse`
+/// heap.push(Reverse(1));
+/// heap.push(Reverse(5));
+/// heap.push(Reverse(2));
+///
+/// // If we pop these scores now, they should come back in the reverse order.
+/// assert_eq!(heap.pop(), Some(Reverse(1)));
+/// assert_eq!(heap.pop(), Some(Reverse(2)));
+/// assert_eq!(heap.pop(), Some(Reverse(5)));
+/// assert_eq!(heap.pop(), None);
+/// ```
+///
+/// # Time complexity
+///
+/// | [push] | [pop] | [peek]/[peek\_mut] |
+/// |---------|---------------|--------------------|
+/// | *O*(1)~ | *O*(log(*n*)) | *O*(1) |
+///
+/// The value for `push` is an expected cost; the method documentation gives a
+/// more detailed analysis.
+///
+/// [`core::cmp::Reverse`]: core::cmp::Reverse
+/// [`Ord`]: core::cmp::Ord
+/// [`Cell`]: core::cell::Cell
+/// [`RefCell`]: core::cell::RefCell
+/// [push]: BinaryHeap::push
+/// [pop]: BinaryHeap::pop
+/// [peek]: BinaryHeap::peek
+/// [peek\_mut]: BinaryHeap::peek_mut
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "BinaryHeap")]
+pub struct BinaryHeap<T> {
+ data: Vec<T>,
+}
+
+/// Structure wrapping a mutable reference to the greatest item on a
+/// `BinaryHeap`.
+///
+/// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See
+/// its documentation for more.
+///
+/// [`peek_mut`]: BinaryHeap::peek_mut
+#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
+pub struct PeekMut<'a, T: 'a + Ord> {
+ heap: &'a mut BinaryHeap<T>,
+ // If a set_len + sift_down are required, this is Some. If a &mut T has not
+ // yet been exposed to peek_mut()'s caller, it's None.
+ original_len: Option<NonZeroUsize>,
+}
+
+#[stable(feature = "collection_debug", since = "1.17.0")]
+impl<T: Ord + fmt::Debug> fmt::Debug for PeekMut<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("PeekMut").field(&self.heap.data[0]).finish()
+ }
+}
+
+#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
+impl<T: Ord> Drop for PeekMut<'_, T> {
+ fn drop(&mut self) {
+ if let Some(original_len) = self.original_len {
+ // SAFETY: That's how many elements were in the Vec at the time of
+ // the PeekMut::deref_mut call, and therefore also at the time of
+ // the BinaryHeap::peek_mut call. Since the PeekMut did not end up
+ // getting leaked, we are now undoing the leak amplification that
+ // the DerefMut prepared for.
+ unsafe { self.heap.data.set_len(original_len.get()) };
+
+ // SAFETY: PeekMut is only instantiated for non-empty heaps.
+ unsafe { self.heap.sift_down(0) };
+ }
+ }
+}
+
+#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
+impl<T: Ord> Deref for PeekMut<'_, T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ debug_assert!(!self.heap.is_empty());
+ // SAFE: PeekMut is only instantiated for non-empty heaps
+ unsafe { self.heap.data.get_unchecked(0) }
+ }
+}
+
+#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
+impl<T: Ord> DerefMut for PeekMut<'_, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ debug_assert!(!self.heap.is_empty());
+
+ let len = self.heap.len();
+ if len > 1 {
+ // Here we preemptively leak all the rest of the underlying vector
+ // after the currently max element. If the caller mutates the &mut T
+ // we're about to give them, and then leaks the PeekMut, all these
+ // elements will remain leaked. If they don't leak the PeekMut, then
+ // either Drop or PeekMut::pop will un-leak the vector elements.
+ //
+ // This is technique is described throughout several other places in
+ // the standard library as "leak amplification".
+ unsafe {
+ // SAFETY: len > 1 so len != 0.
+ self.original_len = Some(NonZeroUsize::new_unchecked(len));
+ // SAFETY: len > 1 so all this does for now is leak elements,
+ // which is safe.
+ self.heap.data.set_len(1);
+ }
+ }
+
+ // SAFE: PeekMut is only instantiated for non-empty heaps
+ unsafe { self.heap.data.get_unchecked_mut(0) }
+ }
+}
+
+impl<'a, T: Ord> PeekMut<'a, T> {
+ /// Removes the peeked value from the heap and returns it.
+ #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")]
+ pub fn pop(mut this: PeekMut<'a, T>) -> T {
+ if let Some(original_len) = this.original_len.take() {
+ // SAFETY: This is how many elements were in the Vec at the time of
+ // the BinaryHeap::peek_mut call.
+ unsafe { this.heap.data.set_len(original_len.get()) };
+
+ // Unlike in Drop, here we don't also need to do a sift_down even if
+ // the caller could've mutated the element. It is removed from the
+ // heap on the next line and pop() is not sensitive to its value.
+ }
+ this.heap.pop().unwrap()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: Clone> Clone for BinaryHeap<T> {
+ fn clone(&self) -> Self {
+ BinaryHeap { data: self.data.clone() }
+ }
+
+ fn clone_from(&mut self, source: &Self) {
+ self.data.clone_from(&source.data);
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: Ord> Default for BinaryHeap<T> {
+ /// Creates an empty `BinaryHeap<T>`.
+ #[inline]
+ fn default() -> BinaryHeap<T> {
+ BinaryHeap::new()
+ }
+}
+
+#[stable(feature = "binaryheap_debug", since = "1.4.0")]
+impl<T: fmt::Debug> fmt::Debug for BinaryHeap<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_list().entries(self.iter()).finish()
+ }
+}
+
+impl<T: Ord> BinaryHeap<T> {
+ /// Creates an empty `BinaryHeap` as a max-heap.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ /// heap.push(4);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ pub fn new() -> BinaryHeap<T> {
+ BinaryHeap { data: vec![] }
+ }
+
+ /// Creates an empty `BinaryHeap` with at least the specified capacity.
+ ///
+ /// The binary heap will be able to hold at least `capacity` elements without
+ /// reallocating. This method is allowed to allocate for more elements than
+ /// `capacity`. If `capacity` is 0, the binary heap will not allocate.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::with_capacity(10);
+ /// heap.push(4);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ #[must_use]
+ pub fn with_capacity(capacity: usize) -> BinaryHeap<T> {
+ BinaryHeap { data: Vec::with_capacity(capacity) }
+ }
+
+ /// Returns a mutable reference to the greatest item in the binary heap, or
+ /// `None` if it is empty.
+ ///
+ /// Note: If the `PeekMut` value is leaked, some heap elements might get
+ /// leaked along with it, but the remaining elements will remain a valid
+ /// heap.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ /// assert!(heap.peek_mut().is_none());
+ ///
+ /// heap.push(1);
+ /// heap.push(5);
+ /// heap.push(2);
+ /// {
+ /// let mut val = heap.peek_mut().unwrap();
+ /// *val = 0;
+ /// }
+ /// assert_eq!(heap.peek(), Some(&2));
+ /// ```
+ ///
+ /// # Time complexity
+ ///
+ /// If the item is modified then the worst case time complexity is *O*(log(*n*)),
+ /// otherwise it's *O*(1).
+ #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")]
+ pub fn peek_mut(&mut self) -> Option<PeekMut<'_, T>> {
+ if self.is_empty() { None } else { Some(PeekMut { heap: self, original_len: None }) }
+ }
+
+ /// Removes the greatest item from the binary heap and returns it, or `None` if it
+ /// is empty.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::from([1, 3]);
+ ///
+ /// assert_eq!(heap.pop(), Some(3));
+ /// assert_eq!(heap.pop(), Some(1));
+ /// assert_eq!(heap.pop(), None);
+ /// ```
+ ///
+ /// # Time complexity
+ ///
+ /// The worst case cost of `pop` on a heap containing *n* elements is *O*(log(*n*)).
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn pop(&mut self) -> Option<T> {
+ self.data.pop().map(|mut item| {
+ if !self.is_empty() {
+ swap(&mut item, &mut self.data[0]);
+ // SAFETY: !self.is_empty() means that self.len() > 0
+ unsafe { self.sift_down_to_bottom(0) };
+ }
+ item
+ })
+ }
+
+ /// Pushes an item onto the binary heap.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ /// heap.push(3);
+ /// heap.push(5);
+ /// heap.push(1);
+ ///
+ /// assert_eq!(heap.len(), 3);
+ /// assert_eq!(heap.peek(), Some(&5));
+ /// ```
+ ///
+ /// # Time complexity
+ ///
+ /// The expected cost of `push`, averaged over every possible ordering of
+ /// the elements being pushed, and over a sufficiently large number of
+ /// pushes, is *O*(1). This is the most meaningful cost metric when pushing
+ /// elements that are *not* already in any sorted pattern.
+ ///
+ /// The time complexity degrades if elements are pushed in predominantly
+ /// ascending order. In the worst case, elements are pushed in ascending
+ /// sorted order and the amortized cost per push is *O*(log(*n*)) against a heap
+ /// containing *n* elements.
+ ///
+ /// The worst case cost of a *single* call to `push` is *O*(*n*). The worst case
+ /// occurs when capacity is exhausted and needs a resize. The resize cost
+ /// has been amortized in the previous figures.
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn push(&mut self, item: T) {
+ let old_len = self.len();
+ self.data.push(item);
+ // SAFETY: Since we pushed a new item it means that
+ // old_len = self.len() - 1 < self.len()
+ unsafe { self.sift_up(0, old_len) };
+ }
+
+ /// Consumes the `BinaryHeap` and returns a vector in sorted
+ /// (ascending) order.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ ///
+ /// let mut heap = BinaryHeap::from([1, 2, 4, 5, 7]);
+ /// heap.push(6);
+ /// heap.push(3);
+ ///
+ /// let vec = heap.into_sorted_vec();
+ /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]);
+ /// ```
+ #[must_use = "`self` will be dropped if the result is not used"]
+ #[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
+ pub fn into_sorted_vec(mut self) -> Vec<T> {
+ let mut end = self.len();
+ while end > 1 {
+ end -= 1;
+ // SAFETY: `end` goes from `self.len() - 1` to 1 (both included),
+ // so it's always a valid index to access.
+ // It is safe to access index 0 (i.e. `ptr`), because
+ // 1 <= end < self.len(), which means self.len() >= 2.
+ unsafe {
+ let ptr = self.data.as_mut_ptr();
+ ptr::swap(ptr, ptr.add(end));
+ }
+ // SAFETY: `end` goes from `self.len() - 1` to 1 (both included) so:
+ // 0 < 1 <= end <= self.len() - 1 < self.len()
+ // Which means 0 < end and end < self.len().
+ unsafe { self.sift_down_range(0, end) };
+ }
+ self.into_vec()
+ }
+
+ // The implementations of sift_up and sift_down use unsafe blocks in
+ // order to move an element out of the vector (leaving behind a
+ // hole), shift along the others and move the removed element back into the
+ // vector at the final location of the hole.
+ // The `Hole` type is used to represent this, and make sure
+ // the hole is filled back at the end of its scope, even on panic.
+ // Using a hole reduces the constant factor compared to using swaps,
+ // which involves twice as many moves.
+
+ /// # Safety
+ ///
+ /// The caller must guarantee that `pos < self.len()`.
+ unsafe fn sift_up(&mut self, start: usize, pos: usize) -> usize {
+ // Take out the value at `pos` and create a hole.
+ // SAFETY: The caller guarantees that pos < self.len()
+ let mut hole = unsafe { Hole::new(&mut self.data, pos) };
+
+ while hole.pos() > start {
+ let parent = (hole.pos() - 1) / 2;
+
+ // SAFETY: hole.pos() > start >= 0, which means hole.pos() > 0
+ // and so hole.pos() - 1 can't underflow.
+ // This guarantees that parent < hole.pos() so
+ // it's a valid index and also != hole.pos().
+ if hole.element() <= unsafe { hole.get(parent) } {
+ break;
+ }
+
+ // SAFETY: Same as above
+ unsafe { hole.move_to(parent) };
+ }
+
+ hole.pos()
+ }
+
+ /// Take an element at `pos` and move it down the heap,
+ /// while its children are larger.
+ ///
+ /// # Safety
+ ///
+ /// The caller must guarantee that `pos < end <= self.len()`.
+ unsafe fn sift_down_range(&mut self, pos: usize, end: usize) {
+ // SAFETY: The caller guarantees that pos < end <= self.len().
+ let mut hole = unsafe { Hole::new(&mut self.data, pos) };
+ let mut child = 2 * hole.pos() + 1;
+
+ // Loop invariant: child == 2 * hole.pos() + 1.
+ while child <= end.saturating_sub(2) {
+ // compare with the greater of the two children
+ // SAFETY: child < end - 1 < self.len() and
+ // child + 1 < end <= self.len(), so they're valid indexes.
+ // child == 2 * hole.pos() + 1 != hole.pos() and
+ // child + 1 == 2 * hole.pos() + 2 != hole.pos().
+ // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
+ // if T is a ZST
+ child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
+
+ // if we are already in order, stop.
+ // SAFETY: child is now either the old child or the old child+1
+ // We already proven that both are < self.len() and != hole.pos()
+ if hole.element() >= unsafe { hole.get(child) } {
+ return;
+ }
+
+ // SAFETY: same as above.
+ unsafe { hole.move_to(child) };
+ child = 2 * hole.pos() + 1;
+ }
+
+ // SAFETY: && short circuit, which means that in the
+ // second condition it's already true that child == end - 1 < self.len().
+ if child == end - 1 && hole.element() < unsafe { hole.get(child) } {
+ // SAFETY: child is already proven to be a valid index and
+ // child == 2 * hole.pos() + 1 != hole.pos().
+ unsafe { hole.move_to(child) };
+ }
+ }
+
+ /// # Safety
+ ///
+ /// The caller must guarantee that `pos < self.len()`.
+ unsafe fn sift_down(&mut self, pos: usize) {
+ let len = self.len();
+ // SAFETY: pos < len is guaranteed by the caller and
+ // obviously len = self.len() <= self.len().
+ unsafe { self.sift_down_range(pos, len) };
+ }
+
+ /// Take an element at `pos` and move it all the way down the heap,
+ /// then sift it up to its position.
+ ///
+ /// Note: This is faster when the element is known to be large / should
+ /// be closer to the bottom.
+ ///
+ /// # Safety
+ ///
+ /// The caller must guarantee that `pos < self.len()`.
+ unsafe fn sift_down_to_bottom(&mut self, mut pos: usize) {
+ let end = self.len();
+ let start = pos;
+
+ // SAFETY: The caller guarantees that pos < self.len().
+ let mut hole = unsafe { Hole::new(&mut self.data, pos) };
+ let mut child = 2 * hole.pos() + 1;
+
+ // Loop invariant: child == 2 * hole.pos() + 1.
+ while child <= end.saturating_sub(2) {
+ // SAFETY: child < end - 1 < self.len() and
+ // child + 1 < end <= self.len(), so they're valid indexes.
+ // child == 2 * hole.pos() + 1 != hole.pos() and
+ // child + 1 == 2 * hole.pos() + 2 != hole.pos().
+ // FIXME: 2 * hole.pos() + 1 or 2 * hole.pos() + 2 could overflow
+ // if T is a ZST
+ child += unsafe { hole.get(child) <= hole.get(child + 1) } as usize;
+
+ // SAFETY: Same as above
+ unsafe { hole.move_to(child) };
+ child = 2 * hole.pos() + 1;
+ }
+
+ if child == end - 1 {
+ // SAFETY: child == end - 1 < self.len(), so it's a valid index
+ // and child == 2 * hole.pos() + 1 != hole.pos().
+ unsafe { hole.move_to(child) };
+ }
+ pos = hole.pos();
+ drop(hole);
+
+ // SAFETY: pos is the position in the hole and was already proven
+ // to be a valid index.
+ unsafe { self.sift_up(start, pos) };
+ }
+
+ /// Rebuild assuming data[0..start] is still a proper heap.
+ fn rebuild_tail(&mut self, start: usize) {
+ if start == self.len() {
+ return;
+ }
+
+ let tail_len = self.len() - start;
+
+ #[inline(always)]
+ fn log2_fast(x: usize) -> usize {
+ (usize::BITS - x.leading_zeros() - 1) as usize
+ }
+
+ // `rebuild` takes O(self.len()) operations
+ // and about 2 * self.len() comparisons in the worst case
+ // while repeating `sift_up` takes O(tail_len * log(start)) operations
+ // and about 1 * tail_len * log_2(start) comparisons in the worst case,
+ // assuming start >= tail_len. For larger heaps, the crossover point
+ // no longer follows this reasoning and was determined empirically.
+ let better_to_rebuild = if start < tail_len {
+ true
+ } else if self.len() <= 2048 {
+ 2 * self.len() < tail_len * log2_fast(start)
+ } else {
+ 2 * self.len() < tail_len * 11
+ };
+
+ if better_to_rebuild {
+ self.rebuild();
+ } else {
+ for i in start..self.len() {
+ // SAFETY: The index `i` is always less than self.len().
+ unsafe { self.sift_up(0, i) };
+ }
+ }
+ }
+
+ fn rebuild(&mut self) {
+ let mut n = self.len() / 2;
+ while n > 0 {
+ n -= 1;
+ // SAFETY: n starts from self.len() / 2 and goes down to 0.
+ // The only case when !(n < self.len()) is if
+ // self.len() == 0, but it's ruled out by the loop condition.
+ unsafe { self.sift_down(n) };
+ }
+ }
+
+ /// Moves all the elements of `other` into `self`, leaving `other` empty.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ ///
+ /// let mut a = BinaryHeap::from([-10, 1, 2, 3, 3]);
+ /// let mut b = BinaryHeap::from([-20, 5, 43]);
+ ///
+ /// a.append(&mut b);
+ ///
+ /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
+ /// assert!(b.is_empty());
+ /// ```
+ #[stable(feature = "binary_heap_append", since = "1.11.0")]
+ pub fn append(&mut self, other: &mut Self) {
+ if self.len() < other.len() {
+ swap(self, other);
+ }
+
+ let start = self.data.len();
+
+ self.data.append(&mut other.data);
+
+ self.rebuild_tail(start);
+ }
+
+ /// Clears the binary heap, returning an iterator over the removed elements
+ /// in heap order. If the iterator is dropped before being fully consumed,
+ /// it drops the remaining elements in heap order.
+ ///
+ /// The returned iterator keeps a mutable borrow on the heap to optimize
+ /// its implementation.
+ ///
+ /// Note:
+ /// * `.drain_sorted()` is *O*(*n* \* log(*n*)); much slower than `.drain()`.
+ /// You should use the latter for most cases.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(binary_heap_drain_sorted)]
+ /// use std::collections::BinaryHeap;
+ ///
+ /// let mut heap = BinaryHeap::from([1, 2, 3, 4, 5]);
+ /// assert_eq!(heap.len(), 5);
+ ///
+ /// drop(heap.drain_sorted()); // removes all elements in heap order
+ /// assert_eq!(heap.len(), 0);
+ /// ```
+ #[inline]
+ #[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
+ pub fn drain_sorted(&mut self) -> DrainSorted<'_, T> {
+ DrainSorted { inner: self }
+ }
+
+ /// Retains only the elements specified by the predicate.
+ ///
+ /// In other words, remove all elements `e` for which `f(&e)` returns
+ /// `false`. The elements are visited in unsorted (and unspecified) order.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(binary_heap_retain)]
+ /// use std::collections::BinaryHeap;
+ ///
+ /// let mut heap = BinaryHeap::from([-10, -5, 1, 2, 4, 13]);
+ ///
+ /// heap.retain(|x| x % 2 == 0); // only keep even numbers
+ ///
+ /// assert_eq!(heap.into_sorted_vec(), [-10, 2, 4])
+ /// ```
+ #[unstable(feature = "binary_heap_retain", issue = "71503")]
+ pub fn retain<F>(&mut self, mut f: F)
+ where
+ F: FnMut(&T) -> bool,
+ {
+ let mut first_removed = self.len();
+ let mut i = 0;
+ self.data.retain(|e| {
+ let keep = f(e);
+ if !keep && i < first_removed {
+ first_removed = i;
+ }
+ i += 1;
+ keep
+ });
+ // data[0..first_removed] is untouched, so we only need to rebuild the tail:
+ self.rebuild_tail(first_removed);
+ }
+}
+
+impl<T> BinaryHeap<T> {
+ /// Returns an iterator visiting all values in the underlying vector, in
+ /// arbitrary order.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let heap = BinaryHeap::from([1, 2, 3, 4]);
+ ///
+ /// // Print 1, 2, 3, 4 in arbitrary order
+ /// for x in heap.iter() {
+ /// println!("{x}");
+ /// }
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn iter(&self) -> Iter<'_, T> {
+ Iter { iter: self.data.iter() }
+ }
+
+ /// Returns an iterator which retrieves elements in heap order.
+ /// This method consumes the original heap.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(binary_heap_into_iter_sorted)]
+ /// use std::collections::BinaryHeap;
+ /// let heap = BinaryHeap::from([1, 2, 3, 4, 5]);
+ ///
+ /// assert_eq!(heap.into_iter_sorted().take(2).collect::<Vec<_>>(), [5, 4]);
+ /// ```
+ #[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
+ pub fn into_iter_sorted(self) -> IntoIterSorted<T> {
+ IntoIterSorted { inner: self }
+ }
+
+ /// Returns the greatest item in the binary heap, or `None` if it is empty.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ /// assert_eq!(heap.peek(), None);
+ ///
+ /// heap.push(1);
+ /// heap.push(5);
+ /// heap.push(2);
+ /// assert_eq!(heap.peek(), Some(&5));
+ ///
+ /// ```
+ ///
+ /// # Time complexity
+ ///
+ /// Cost is *O*(1) in the worst case.
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn peek(&self) -> Option<&T> {
+ self.data.get(0)
+ }
+
+ /// Returns the number of elements the binary heap can hold without reallocating.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::with_capacity(100);
+ /// assert!(heap.capacity() >= 100);
+ /// heap.push(4);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn capacity(&self) -> usize {
+ self.data.capacity()
+ }
+
+ /// Reserves the minimum capacity for at least `additional` elements more than
+ /// the current length. Unlike [`reserve`], this will not
+ /// deliberately over-allocate to speculatively avoid frequent allocations.
+ /// After calling `reserve_exact`, capacity will be greater than or equal to
+ /// `self.len() + additional`. Does nothing if the capacity is already
+ /// sufficient.
+ ///
+ /// [`reserve`]: BinaryHeap::reserve
+ ///
+ /// # Panics
+ ///
+ /// Panics if the new capacity overflows [`usize`].
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ /// heap.reserve_exact(100);
+ /// assert!(heap.capacity() >= 100);
+ /// heap.push(4);
+ /// ```
+ ///
+ /// [`reserve`]: BinaryHeap::reserve
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn reserve_exact(&mut self, additional: usize) {
+ self.data.reserve_exact(additional);
+ }
+
+ /// Reserves capacity for at least `additional` elements more than the
+ /// current length. The allocator may reserve more space to speculatively
+ /// avoid frequent allocations. After calling `reserve`,
+ /// capacity will be greater than or equal to `self.len() + additional`.
+ /// Does nothing if capacity is already sufficient.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the new capacity overflows [`usize`].
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ /// heap.reserve(100);
+ /// assert!(heap.capacity() >= 100);
+ /// heap.push(4);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn reserve(&mut self, additional: usize) {
+ self.data.reserve(additional);
+ }
+
+ /// Tries to reserve the minimum capacity for at least `additional` elements
+ /// more than the current length. Unlike [`try_reserve`], this will not
+ /// deliberately over-allocate to speculatively avoid frequent allocations.
+ /// After calling `try_reserve_exact`, capacity will be greater than or
+ /// equal to `self.len() + additional` if it returns `Ok(())`.
+ /// Does nothing if the capacity is already sufficient.
+ ///
+ /// Note that the allocator may give the collection more space than it
+ /// requests. Therefore, capacity can not be relied upon to be precisely
+ /// minimal. Prefer [`try_reserve`] if future insertions are expected.
+ ///
+ /// [`try_reserve`]: BinaryHeap::try_reserve
+ ///
+ /// # Errors
+ ///
+ /// If the capacity overflows, or the allocator reports a failure, then an error
+ /// is returned.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// use std::collections::TryReserveError;
+ ///
+ /// fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
+ /// let mut heap = BinaryHeap::new();
+ ///
+ /// // Pre-reserve the memory, exiting if we can't
+ /// heap.try_reserve_exact(data.len())?;
+ ///
+ /// // Now we know this can't OOM in the middle of our complex work
+ /// heap.extend(data.iter());
+ ///
+ /// Ok(heap.pop())
+ /// }
+ /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
+ /// ```
+ #[stable(feature = "try_reserve_2", since = "1.63.0")]
+ pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.data.try_reserve_exact(additional)
+ }
+
+ /// Tries to reserve capacity for at least `additional` elements more than the
+ /// current length. The allocator may reserve more space to speculatively
+ /// avoid frequent allocations. After calling `try_reserve`, capacity will be
+ /// greater than or equal to `self.len() + additional` if it returns
+ /// `Ok(())`. Does nothing if capacity is already sufficient. This method
+ /// preserves the contents even if an error occurs.
+ ///
+ /// # Errors
+ ///
+ /// If the capacity overflows, or the allocator reports a failure, then an error
+ /// is returned.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// use std::collections::TryReserveError;
+ ///
+ /// fn find_max_slow(data: &[u32]) -> Result<Option<u32>, TryReserveError> {
+ /// let mut heap = BinaryHeap::new();
+ ///
+ /// // Pre-reserve the memory, exiting if we can't
+ /// heap.try_reserve(data.len())?;
+ ///
+ /// // Now we know this can't OOM in the middle of our complex work
+ /// heap.extend(data.iter());
+ ///
+ /// Ok(heap.pop())
+ /// }
+ /// # find_max_slow(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?");
+ /// ```
+ #[stable(feature = "try_reserve_2", since = "1.63.0")]
+ pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
+ self.data.try_reserve(additional)
+ }
+
+ /// Discards as much additional capacity as possible.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap: BinaryHeap<i32> = BinaryHeap::with_capacity(100);
+ ///
+ /// assert!(heap.capacity() >= 100);
+ /// heap.shrink_to_fit();
+ /// assert!(heap.capacity() == 0);
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn shrink_to_fit(&mut self) {
+ self.data.shrink_to_fit();
+ }
+
+ /// Discards capacity with a lower bound.
+ ///
+ /// The capacity will remain at least as large as both the length
+ /// and the supplied value.
+ ///
+ /// If the current capacity is less than the lower limit, this is a no-op.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap: BinaryHeap<i32> = BinaryHeap::with_capacity(100);
+ ///
+ /// assert!(heap.capacity() >= 100);
+ /// heap.shrink_to(10);
+ /// assert!(heap.capacity() >= 10);
+ /// ```
+ #[inline]
+ #[stable(feature = "shrink_to", since = "1.56.0")]
+ pub fn shrink_to(&mut self, min_capacity: usize) {
+ self.data.shrink_to(min_capacity)
+ }
+
+ /// Returns a slice of all values in the underlying vector, in arbitrary
+ /// order.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(binary_heap_as_slice)]
+ /// use std::collections::BinaryHeap;
+ /// use std::io::{self, Write};
+ ///
+ /// let heap = BinaryHeap::from([1, 2, 3, 4, 5, 6, 7]);
+ ///
+ /// io::sink().write(heap.as_slice()).unwrap();
+ /// ```
+ #[must_use]
+ #[unstable(feature = "binary_heap_as_slice", issue = "83659")]
+ pub fn as_slice(&self) -> &[T] {
+ self.data.as_slice()
+ }
+
+ /// Consumes the `BinaryHeap` and returns the underlying vector
+ /// in arbitrary order.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let heap = BinaryHeap::from([1, 2, 3, 4, 5, 6, 7]);
+ /// let vec = heap.into_vec();
+ ///
+ /// // Will print in some order
+ /// for x in vec {
+ /// println!("{x}");
+ /// }
+ /// ```
+ #[must_use = "`self` will be dropped if the result is not used"]
+ #[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
+ pub fn into_vec(self) -> Vec<T> {
+ self.into()
+ }
+
+ /// Returns the length of the binary heap.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let heap = BinaryHeap::from([1, 3]);
+ ///
+ /// assert_eq!(heap.len(), 2);
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn len(&self) -> usize {
+ self.data.len()
+ }
+
+ /// Checks if the binary heap is empty.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::new();
+ ///
+ /// assert!(heap.is_empty());
+ ///
+ /// heap.push(3);
+ /// heap.push(5);
+ /// heap.push(1);
+ ///
+ /// assert!(!heap.is_empty());
+ /// ```
+ #[must_use]
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
+
+ /// Clears the binary heap, returning an iterator over the removed elements
+ /// in arbitrary order. If the iterator is dropped before being fully
+ /// consumed, it drops the remaining elements in arbitrary order.
+ ///
+ /// The returned iterator keeps a mutable borrow on the heap to optimize
+ /// its implementation.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::from([1, 3]);
+ ///
+ /// assert!(!heap.is_empty());
+ ///
+ /// for x in heap.drain() {
+ /// println!("{x}");
+ /// }
+ ///
+ /// assert!(heap.is_empty());
+ /// ```
+ #[inline]
+ #[stable(feature = "drain", since = "1.6.0")]
+ pub fn drain(&mut self) -> Drain<'_, T> {
+ Drain { iter: self.data.drain(..) }
+ }
+
+ /// Drops all items from the binary heap.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let mut heap = BinaryHeap::from([1, 3]);
+ ///
+ /// assert!(!heap.is_empty());
+ ///
+ /// heap.clear();
+ ///
+ /// assert!(heap.is_empty());
+ /// ```
+ #[stable(feature = "rust1", since = "1.0.0")]
+ pub fn clear(&mut self) {
+ self.drain();
+ }
+}
+
+/// Hole represents a hole in a slice i.e., an index without valid value
+/// (because it was moved from or duplicated).
+/// In drop, `Hole` will restore the slice by filling the hole
+/// position with the value that was originally removed.
+struct Hole<'a, T: 'a> {
+ data: &'a mut [T],
+ elt: ManuallyDrop<T>,
+ pos: usize,
+}
+
+impl<'a, T> Hole<'a, T> {
+ /// Create a new `Hole` at index `pos`.
+ ///
+ /// Unsafe because pos must be within the data slice.
+ #[inline]
+ unsafe fn new(data: &'a mut [T], pos: usize) -> Self {
+ debug_assert!(pos < data.len());
+ // SAFE: pos should be inside the slice
+ let elt = unsafe { ptr::read(data.get_unchecked(pos)) };
+ Hole { data, elt: ManuallyDrop::new(elt), pos }
+ }
+
+ #[inline]
+ fn pos(&self) -> usize {
+ self.pos
+ }
+
+ /// Returns a reference to the element removed.
+ #[inline]
+ fn element(&self) -> &T {
+ &self.elt
+ }
+
+ /// Returns a reference to the element at `index`.
+ ///
+ /// Unsafe because index must be within the data slice and not equal to pos.
+ #[inline]
+ unsafe fn get(&self, index: usize) -> &T {
+ debug_assert!(index != self.pos);
+ debug_assert!(index < self.data.len());
+ unsafe { self.data.get_unchecked(index) }
+ }
+
+ /// Move hole to new location
+ ///
+ /// Unsafe because index must be within the data slice and not equal to pos.
+ #[inline]
+ unsafe fn move_to(&mut self, index: usize) {
+ debug_assert!(index != self.pos);
+ debug_assert!(index < self.data.len());
+ unsafe {
+ let ptr = self.data.as_mut_ptr();
+ let index_ptr: *const _ = ptr.add(index);
+ let hole_ptr = ptr.add(self.pos);
+ ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1);
+ }
+ self.pos = index;
+ }
+}
+
+impl<T> Drop for Hole<'_, T> {
+ #[inline]
+ fn drop(&mut self) {
+ // fill the hole again
+ unsafe {
+ let pos = self.pos;
+ ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1);
+ }
+ }
+}
+
+/// An iterator over the elements of a `BinaryHeap`.
+///
+/// This `struct` is created by [`BinaryHeap::iter()`]. See its
+/// documentation for more.
+///
+/// [`iter`]: BinaryHeap::iter
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+#[stable(feature = "rust1", since = "1.0.0")]
+pub struct Iter<'a, T: 'a> {
+ iter: slice::Iter<'a, T>,
+}
+
+#[stable(feature = "collection_debug", since = "1.17.0")]
+impl<T: fmt::Debug> fmt::Debug for Iter<'_, T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("Iter").field(&self.iter.as_slice()).finish()
+ }
+}
+
+// FIXME(#26925) Remove in favor of `#[derive(Clone)]`
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Clone for Iter<'_, T> {
+ fn clone(&self) -> Self {
+ Iter { iter: self.iter.clone() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, T> Iterator for Iter<'a, T> {
+ type Item = &'a T;
+
+ #[inline]
+ fn next(&mut self) -> Option<&'a T> {
+ self.iter.next()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+
+ #[inline]
+ fn last(self) -> Option<&'a T> {
+ self.iter.last()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, T> DoubleEndedIterator for Iter<'a, T> {
+ #[inline]
+ fn next_back(&mut self) -> Option<&'a T> {
+ self.iter.next_back()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> ExactSizeIterator for Iter<'_, T> {
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
+
+#[stable(feature = "fused", since = "1.26.0")]
+impl<T> FusedIterator for Iter<'_, T> {}
+
+/// An owning iterator over the elements of a `BinaryHeap`.
+///
+/// This `struct` is created by [`BinaryHeap::into_iter()`]
+/// (provided by the [`IntoIterator`] trait). See its documentation for more.
+///
+/// [`into_iter`]: BinaryHeap::into_iter
+/// [`IntoIterator`]: core::iter::IntoIterator
+#[stable(feature = "rust1", since = "1.0.0")]
+#[derive(Clone)]
+pub struct IntoIter<T> {
+ iter: vec::IntoIter<T>,
+}
+
+#[stable(feature = "collection_debug", since = "1.17.0")]
+impl<T: fmt::Debug> fmt::Debug for IntoIter<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("IntoIter").field(&self.iter.as_slice()).finish()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> Iterator for IntoIter<T> {
+ type Item = T;
+
+ #[inline]
+ fn next(&mut self) -> Option<T> {
+ self.iter.next()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> DoubleEndedIterator for IntoIter<T> {
+ #[inline]
+ fn next_back(&mut self) -> Option<T> {
+ self.iter.next_back()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> ExactSizeIterator for IntoIter<T> {
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
+
+#[stable(feature = "fused", since = "1.26.0")]
+impl<T> FusedIterator for IntoIter<T> {}
+
+// In addition to the SAFETY invariants of the following three unsafe traits
+// also refer to the vec::in_place_collect module documentation to get an overview
+#[unstable(issue = "none", feature = "inplace_iteration")]
+#[doc(hidden)]
+unsafe impl<T> SourceIter for IntoIter<T> {
+ type Source = IntoIter<T>;
+
+ #[inline]
+ unsafe fn as_inner(&mut self) -> &mut Self::Source {
+ self
+ }
+}
+
+#[unstable(issue = "none", feature = "inplace_iteration")]
+#[doc(hidden)]
+unsafe impl<I> InPlaceIterable for IntoIter<I> {}
+
+unsafe impl<I> AsVecIntoIter for IntoIter<I> {
+ type Item = I;
+
+ fn as_into_iter(&mut self) -> &mut vec::IntoIter<Self::Item> {
+ &mut self.iter
+ }
+}
+
+#[must_use = "iterators are lazy and do nothing unless consumed"]
+#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
+#[derive(Clone, Debug)]
+pub struct IntoIterSorted<T> {
+ inner: BinaryHeap<T>,
+}
+
+#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
+impl<T: Ord> Iterator for IntoIterSorted<T> {
+ type Item = T;
+
+ #[inline]
+ fn next(&mut self) -> Option<T> {
+ self.inner.pop()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let exact = self.inner.len();
+ (exact, Some(exact))
+ }
+}
+
+#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
+impl<T: Ord> ExactSizeIterator for IntoIterSorted<T> {}
+
+#[unstable(feature = "binary_heap_into_iter_sorted", issue = "59278")]
+impl<T: Ord> FusedIterator for IntoIterSorted<T> {}
+
+#[unstable(feature = "trusted_len", issue = "37572")]
+unsafe impl<T: Ord> TrustedLen for IntoIterSorted<T> {}
+
+/// A draining iterator over the elements of a `BinaryHeap`.
+///
+/// This `struct` is created by [`BinaryHeap::drain()`]. See its
+/// documentation for more.
+///
+/// [`drain`]: BinaryHeap::drain
+#[stable(feature = "drain", since = "1.6.0")]
+#[derive(Debug)]
+pub struct Drain<'a, T: 'a> {
+ iter: vec::Drain<'a, T>,
+}
+
+#[stable(feature = "drain", since = "1.6.0")]
+impl<T> Iterator for Drain<'_, T> {
+ type Item = T;
+
+ #[inline]
+ fn next(&mut self) -> Option<T> {
+ self.iter.next()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+#[stable(feature = "drain", since = "1.6.0")]
+impl<T> DoubleEndedIterator for Drain<'_, T> {
+ #[inline]
+ fn next_back(&mut self) -> Option<T> {
+ self.iter.next_back()
+ }
+}
+
+#[stable(feature = "drain", since = "1.6.0")]
+impl<T> ExactSizeIterator for Drain<'_, T> {
+ fn is_empty(&self) -> bool {
+ self.iter.is_empty()
+ }
+}
+
+#[stable(feature = "fused", since = "1.26.0")]
+impl<T> FusedIterator for Drain<'_, T> {}
+
+/// A draining iterator over the elements of a `BinaryHeap`.
+///
+/// This `struct` is created by [`BinaryHeap::drain_sorted()`]. See its
+/// documentation for more.
+///
+/// [`drain_sorted`]: BinaryHeap::drain_sorted
+#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
+#[derive(Debug)]
+pub struct DrainSorted<'a, T: Ord> {
+ inner: &'a mut BinaryHeap<T>,
+}
+
+#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
+impl<'a, T: Ord> Drop for DrainSorted<'a, T> {
+ /// Removes heap elements in heap order.
+ fn drop(&mut self) {
+ struct DropGuard<'r, 'a, T: Ord>(&'r mut DrainSorted<'a, T>);
+
+ impl<'r, 'a, T: Ord> Drop for DropGuard<'r, 'a, T> {
+ fn drop(&mut self) {
+ while self.0.inner.pop().is_some() {}
+ }
+ }
+
+ while let Some(item) = self.inner.pop() {
+ let guard = DropGuard(self);
+ drop(item);
+ mem::forget(guard);
+ }
+ }
+}
+
+#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
+impl<T: Ord> Iterator for DrainSorted<'_, T> {
+ type Item = T;
+
+ #[inline]
+ fn next(&mut self) -> Option<T> {
+ self.inner.pop()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let exact = self.inner.len();
+ (exact, Some(exact))
+ }
+}
+
+#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
+impl<T: Ord> ExactSizeIterator for DrainSorted<'_, T> {}
+
+#[unstable(feature = "binary_heap_drain_sorted", issue = "59278")]
+impl<T: Ord> FusedIterator for DrainSorted<'_, T> {}
+
+#[unstable(feature = "trusted_len", issue = "37572")]
+unsafe impl<T: Ord> TrustedLen for DrainSorted<'_, T> {}
+
+#[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
+impl<T: Ord> From<Vec<T>> for BinaryHeap<T> {
+ /// Converts a `Vec<T>` into a `BinaryHeap<T>`.
+ ///
+ /// This conversion happens in-place, and has *O*(*n*) time complexity.
+ fn from(vec: Vec<T>) -> BinaryHeap<T> {
+ let mut heap = BinaryHeap { data: vec };
+ heap.rebuild();
+ heap
+ }
+}
+
+#[stable(feature = "std_collections_from_array", since = "1.56.0")]
+impl<T: Ord, const N: usize> From<[T; N]> for BinaryHeap<T> {
+ /// ```
+ /// use std::collections::BinaryHeap;
+ ///
+ /// let mut h1 = BinaryHeap::from([1, 4, 2, 3]);
+ /// let mut h2: BinaryHeap<_> = [1, 4, 2, 3].into();
+ /// while let Some((a, b)) = h1.pop().zip(h2.pop()) {
+ /// assert_eq!(a, b);
+ /// }
+ /// ```
+ fn from(arr: [T; N]) -> Self {
+ Self::from_iter(arr)
+ }
+}
+
+#[stable(feature = "binary_heap_extras_15", since = "1.5.0")]
+impl<T> From<BinaryHeap<T>> for Vec<T> {
+ /// Converts a `BinaryHeap<T>` into a `Vec<T>`.
+ ///
+ /// This conversion requires no data movement or allocation, and has
+ /// constant time complexity.
+ fn from(heap: BinaryHeap<T>) -> Vec<T> {
+ heap.data
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: Ord> FromIterator<T> for BinaryHeap<T> {
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> BinaryHeap<T> {
+ BinaryHeap::from(iter.into_iter().collect::<Vec<_>>())
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T> IntoIterator for BinaryHeap<T> {
+ type Item = T;
+ type IntoIter = IntoIter<T>;
+
+ /// Creates a consuming iterator, that is, one that moves each value out of
+ /// the binary heap in arbitrary order. The binary heap cannot be used
+ /// after calling this.
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// use std::collections::BinaryHeap;
+ /// let heap = BinaryHeap::from([1, 2, 3, 4]);
+ ///
+ /// // Print 1, 2, 3, 4 in arbitrary order
+ /// for x in heap.into_iter() {
+ /// // x has type i32, not &i32
+ /// println!("{x}");
+ /// }
+ /// ```
+ fn into_iter(self) -> IntoIter<T> {
+ IntoIter { iter: self.data.into_iter() }
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<'a, T> IntoIterator for &'a BinaryHeap<T> {
+ type Item = &'a T;
+ type IntoIter = Iter<'a, T>;
+
+ fn into_iter(self) -> Iter<'a, T> {
+ self.iter()
+ }
+}
+
+#[stable(feature = "rust1", since = "1.0.0")]
+impl<T: Ord> Extend<T> for BinaryHeap<T> {
+ #[inline]
+ fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
+ <Self as SpecExtend<I>>::spec_extend(self, iter);
+ }
+
+ #[inline]
+ fn extend_one(&mut self, item: T) {
+ self.push(item);
+ }
+
+ #[inline]
+ fn extend_reserve(&mut self, additional: usize) {
+ self.reserve(additional);
+ }
+}
+
+impl<T: Ord, I: IntoIterator<Item = T>> SpecExtend<I> for BinaryHeap<T> {
+ default fn spec_extend(&mut self, iter: I) {
+ self.extend_desugared(iter.into_iter());
+ }
+}
+
+impl<T: Ord> SpecExtend<Vec<T>> for BinaryHeap<T> {
+ fn spec_extend(&mut self, ref mut other: Vec<T>) {
+ let start = self.data.len();
+ self.data.append(other);
+ self.rebuild_tail(start);
+ }
+}
+
+impl<T: Ord> SpecExtend<BinaryHeap<T>> for BinaryHeap<T> {
+ fn spec_extend(&mut self, ref mut other: BinaryHeap<T>) {
+ self.append(other);
+ }
+}
+
+impl<T: Ord> BinaryHeap<T> {
+ fn extend_desugared<I: IntoIterator<Item = T>>(&mut self, iter: I) {
+ let iterator = iter.into_iter();
+ let (lower, _) = iterator.size_hint();
+
+ self.reserve(lower);
+
+ iterator.for_each(move |elem| self.push(elem));
+ }
+}
+
+#[stable(feature = "extend_ref", since = "1.2.0")]
+impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap<T> {
+ fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
+ self.extend(iter.into_iter().cloned());
+ }
+
+ #[inline]
+ fn extend_one(&mut self, &item: &'a T) {
+ self.push(item);
+ }
+
+ #[inline]
+ fn extend_reserve(&mut self, additional: usize) {
+ self.reserve(additional);
+ }
+}
use super::*;
use crate::boxed::Box;
use crate::testing::crash_test::{CrashTestDummy, Panic};
+use core::mem;
use std::iter::TrustedLen;
use std::panic::{catch_unwind, AssertUnwindSafe};
assert_eq!(heap.peek(), Some(&9));
}
+#[test]
+fn test_peek_mut_leek() {
+ let data = vec![4, 2, 7];
+ let mut heap = BinaryHeap::from(data);
+ let mut max = heap.peek_mut().unwrap();
+ *max = -1;
+
+ // The PeekMut object's Drop impl would have been responsible for moving the
+ // -1 out of the max position of the BinaryHeap, but we don't run it.
+ mem::forget(max);
+
+ // Absent some mitigation like leak amplification, the -1 would incorrectly
+ // end up in the last position of the returned Vec, with the rest of the
+ // heap's original contents in front of it in sorted order.
+ let sorted_vec = heap.into_sorted_vec();
+ assert!(sorted_vec.is_sorted(), "{:?}", sorted_vec);
+}
+
#[test]
fn test_peek_mut_pop() {
let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
#![feature(const_size_of_val)]
#![feature(const_align_of_val)]
#![feature(const_ptr_read)]
+#![feature(const_maybe_uninit_zeroed)]
#![feature(const_maybe_uninit_write)]
#![feature(const_maybe_uninit_as_mut_ptr)]
#![feature(const_refs_to_cell)]
#![feature(hasher_prefixfree_extras)]
#![feature(inline_const)]
#![feature(inplace_iteration)]
+#![cfg_attr(test, feature(is_sorted))]
#![feature(iter_advance_by)]
#![feature(iter_next_chunk)]
#![feature(iter_repeat_n)]
// This is a `NonNull` to allow optimizing the size of this type in enums,
// but it is not necessarily a valid pointer.
// `Weak::new` sets this to `usize::MAX` so that it doesn’t need
- // to allocate space on the heap. That's not a value a real pointer
+ // to allocate space on the heap. That's not a value a real pointer
// will ever have because RcBox has alignment at least 2.
// This is only possible when `T: Sized`; unsized `T` never dangle.
ptr: NonNull<RcBox<T>>,
use core::mem::{self, SizedTypeProperties};
#[cfg(not(no_global_oom_handling))]
use core::ptr;
+#[cfg(not(no_global_oom_handling))]
+use core::slice::sort;
use crate::alloc::Allocator;
#[cfg(not(no_global_oom_handling))]
-use crate::alloc::Global;
+use crate::alloc::{self, Global};
#[cfg(not(no_global_oom_handling))]
use crate::borrow::ToOwned;
use crate::boxed::Box;
where
T: Ord,
{
- merge_sort(self, T::lt);
+ stable_sort(self, T::lt);
}
/// Sorts the slice with a comparator function.
where
F: FnMut(&T, &T) -> Ordering,
{
- merge_sort(self, |a, b| compare(a, b) == Less);
+ stable_sort(self, |a, b| compare(a, b) == Less);
}
/// Sorts the slice with a key extraction function.
F: FnMut(&T) -> K,
K: Ord,
{
- merge_sort(self, |a, b| f(a).lt(&f(b)));
+ stable_sort(self, |a, b| f(a).lt(&f(b)));
}
/// Sorts the slice with a key extraction function.
// Sorting
////////////////////////////////////////////////////////////////////////////////
-/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.
-///
-/// This is the integral subroutine of insertion sort.
-#[cfg(not(no_global_oom_handling))]
-fn insert_head<T, F>(v: &mut [T], is_less: &mut F)
-where
- F: FnMut(&T, &T) -> bool,
-{
- if v.len() >= 2 && is_less(&v[1], &v[0]) {
- unsafe {
- // There are three ways to implement insertion here:
- //
- // 1. Swap adjacent elements until the first one gets to its final destination.
- // However, this way we copy data around more than is necessary. If elements are big
- // structures (costly to copy), this method will be slow.
- //
- // 2. Iterate until the right place for the first element is found. Then shift the
- // elements succeeding it to make room for it and finally place it into the
- // remaining hole. This is a good method.
- //
- // 3. Copy the first element into a temporary variable. Iterate until the right place
- // for it is found. As we go along, copy every traversed element into the slot
- // preceding it. Finally, copy data from the temporary variable into the remaining
- // hole. This method is very good. Benchmarks demonstrated slightly better
- // performance than with the 2nd method.
- //
- // All methods were benchmarked, and the 3rd showed best results. So we chose that one.
- let tmp = mem::ManuallyDrop::new(ptr::read(&v[0]));
-
- // Intermediate state of the insertion process is always tracked by `hole`, which
- // serves two purposes:
- // 1. Protects integrity of `v` from panics in `is_less`.
- // 2. Fills the remaining hole in `v` in the end.
- //
- // Panic safety:
- //
- // If `is_less` panics at any point during the process, `hole` will get dropped and
- // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it
- // initially held exactly once.
- let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] };
- ptr::copy_nonoverlapping(&v[1], &mut v[0], 1);
-
- for i in 2..v.len() {
- if !is_less(&v[i], &*tmp) {
- break;
- }
- ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1);
- hole.dest = &mut v[i];
- }
- // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`.
- }
- }
-
- // When dropped, copies from `src` into `dest`.
- struct InsertionHole<T> {
- src: *const T,
- dest: *mut T,
- }
-
- impl<T> Drop for InsertionHole<T> {
- fn drop(&mut self) {
- unsafe {
- ptr::copy_nonoverlapping(self.src, self.dest, 1);
- }
- }
- }
-}
-
-/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and
-/// stores the result into `v[..]`.
-///
-/// # Safety
-///
-/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough
-/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type.
-#[cfg(not(no_global_oom_handling))]
-unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F)
-where
- F: FnMut(&T, &T) -> bool,
-{
- let len = v.len();
- let v = v.as_mut_ptr();
- let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) };
-
- // The merge process first copies the shorter run into `buf`. Then it traces the newly copied
- // run and the longer run forwards (or backwards), comparing their next unconsumed elements and
- // copying the lesser (or greater) one into `v`.
- //
- // As soon as the shorter run is fully consumed, the process is done. If the longer run gets
- // consumed first, then we must copy whatever is left of the shorter run into the remaining
- // hole in `v`.
- //
- // Intermediate state of the process is always tracked by `hole`, which serves two purposes:
- // 1. Protects integrity of `v` from panics in `is_less`.
- // 2. Fills the remaining hole in `v` if the longer run gets consumed first.
- //
- // Panic safety:
- //
- // If `is_less` panics at any point during the process, `hole` will get dropped and fill the
- // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every
- // object it initially held exactly once.
- let mut hole;
-
- if mid <= len - mid {
- // The left run is shorter.
- unsafe {
- ptr::copy_nonoverlapping(v, buf, mid);
- hole = MergeHole { start: buf, end: buf.add(mid), dest: v };
- }
-
- // Initially, these pointers point to the beginnings of their arrays.
- let left = &mut hole.start;
- let mut right = v_mid;
- let out = &mut hole.dest;
-
- while *left < hole.end && right < v_end {
- // Consume the lesser side.
- // If equal, prefer the left run to maintain stability.
- unsafe {
- let to_copy = if is_less(&*right, &**left) {
- get_and_increment(&mut right)
- } else {
- get_and_increment(left)
- };
- ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1);
- }
- }
- } else {
- // The right run is shorter.
- unsafe {
- ptr::copy_nonoverlapping(v_mid, buf, len - mid);
- hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid };
- }
-
- // Initially, these pointers point past the ends of their arrays.
- let left = &mut hole.dest;
- let right = &mut hole.end;
- let mut out = v_end;
-
- while v < *left && buf < *right {
- // Consume the greater side.
- // If equal, prefer the right run to maintain stability.
- unsafe {
- let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) {
- decrement_and_get(left)
- } else {
- decrement_and_get(right)
- };
- ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1);
- }
- }
- }
- // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of
- // it will now be copied into the hole in `v`.
-
- unsafe fn get_and_increment<T>(ptr: &mut *mut T) -> *mut T {
- let old = *ptr;
- *ptr = unsafe { ptr.add(1) };
- old
- }
-
- unsafe fn decrement_and_get<T>(ptr: &mut *mut T) -> *mut T {
- *ptr = unsafe { ptr.sub(1) };
- *ptr
- }
-
- // When dropped, copies the range `start..end` into `dest..`.
- struct MergeHole<T> {
- start: *mut T,
- end: *mut T,
- dest: *mut T,
- }
-
- impl<T> Drop for MergeHole<T> {
- fn drop(&mut self) {
- // `T` is not a zero-sized type, and these are pointers into a slice's elements.
- unsafe {
- let len = self.end.sub_ptr(self.start);
- ptr::copy_nonoverlapping(self.start, self.dest, len);
- }
- }
- }
-}
-
-/// This merge sort borrows some (but not all) ideas from TimSort, which is described in detail
-/// [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt).
-///
-/// The algorithm identifies strictly descending and non-descending subsequences, which are called
-/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed
-/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are
-/// satisfied:
-///
-/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len`
-/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len`
-///
-/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case.
+#[inline]
#[cfg(not(no_global_oom_handling))]
-fn merge_sort<T, F>(v: &mut [T], mut is_less: F)
+fn stable_sort<T, F>(v: &mut [T], mut is_less: F)
where
F: FnMut(&T, &T) -> bool,
{
- // Slices of up to this length get sorted using insertion sort.
- const MAX_INSERTION: usize = 20;
- // Very short runs are extended using insertion sort to span at least this many elements.
- const MIN_RUN: usize = 10;
-
- // Sorting has no meaningful behavior on zero-sized types.
if T::IS_ZST {
+ // Sorting has no meaningful behavior on zero-sized types. Do nothing.
return;
}
- let len = v.len();
-
- // Short arrays get sorted in-place via insertion sort to avoid allocations.
- if len <= MAX_INSERTION {
- if len >= 2 {
- for i in (0..len - 1).rev() {
- insert_head(&mut v[i..], &mut is_less);
- }
- }
- return;
- }
-
- // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it
- // shallow copies of the contents of `v` without risking the dtors running on copies if
- // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run,
- // which will always have length at most `len / 2`.
- let mut buf = Vec::with_capacity(len / 2);
+ let elem_alloc_fn = |len: usize| -> *mut T {
+ // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len >
+ // v.len(). Alloc in general will only be used as 'shadow-region' to store temporary swap
+ // elements.
+ unsafe { alloc::alloc(alloc::Layout::array::<T>(len).unwrap_unchecked()) as *mut T }
+ };
- // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a
- // strange decision, but consider the fact that merges more often go in the opposite direction
- // (forwards). According to benchmarks, merging forwards is slightly faster than merging
- // backwards. To conclude, identifying runs by traversing backwards improves performance.
- let mut runs = vec![];
- let mut end = len;
- while end > 0 {
- // Find the next natural run, and reverse it if it's strictly descending.
- let mut start = end - 1;
- if start > 0 {
- start -= 1;
- unsafe {
- if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) {
- while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) {
- start -= 1;
- }
- v[start..end].reverse();
- } else {
- while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1))
- {
- start -= 1;
- }
- }
- }
- }
-
- // Insert some more elements into the run if it's too short. Insertion sort is faster than
- // merge sort on short sequences, so this significantly improves performance.
- while start > 0 && end - start < MIN_RUN {
- start -= 1;
- insert_head(&mut v[start..end], &mut is_less);
+ let elem_dealloc_fn = |buf_ptr: *mut T, len: usize| {
+ // SAFETY: Creating the layout is safe as long as merge_sort never calls this with len >
+ // v.len(). The caller must ensure that buf_ptr was created by elem_alloc_fn with the same
+ // len.
+ unsafe {
+ alloc::dealloc(buf_ptr as *mut u8, alloc::Layout::array::<T>(len).unwrap_unchecked());
}
+ };
- // Push this run onto the stack.
- runs.push(Run { start, len: end - start });
- end = start;
-
- // Merge some pairs of adjacent runs to satisfy the invariants.
- while let Some(r) = collapse(&runs) {
- let left = runs[r + 1];
- let right = runs[r];
- unsafe {
- merge(
- &mut v[left.start..right.start + right.len],
- left.len,
- buf.as_mut_ptr(),
- &mut is_less,
- );
- }
- runs[r] = Run { start: left.start, len: left.len + right.len };
- runs.remove(r + 1);
+ let run_alloc_fn = |len: usize| -> *mut sort::TimSortRun {
+ // SAFETY: Creating the layout is safe as long as merge_sort never calls this with an
+ // obscene length or 0.
+ unsafe {
+ alloc::alloc(alloc::Layout::array::<sort::TimSortRun>(len).unwrap_unchecked())
+ as *mut sort::TimSortRun
}
- }
-
- // Finally, exactly one run must remain in the stack.
- debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len);
+ };
- // Examines the stack of runs and identifies the next pair of runs to merge. More specifically,
- // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the
- // algorithm should continue building a new run instead, `None` is returned.
- //
- // TimSort is infamous for its buggy implementations, as described here:
- // http://envisage-project.eu/timsort-specification-and-verification/
- //
- // The gist of the story is: we must enforce the invariants on the top four runs on the stack.
- // Enforcing them on just top three is not sufficient to ensure that the invariants will still
- // hold for *all* runs in the stack.
- //
- // This function correctly checks invariants for the top four runs. Additionally, if the top
- // run starts at index 0, it will always demand a merge operation until the stack is fully
- // collapsed, in order to complete the sort.
- #[inline]
- fn collapse(runs: &[Run]) -> Option<usize> {
- let n = runs.len();
- if n >= 2
- && (runs[n - 1].start == 0
- || runs[n - 2].len <= runs[n - 1].len
- || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len)
- || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len))
- {
- if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) }
- } else {
- None
+ let run_dealloc_fn = |buf_ptr: *mut sort::TimSortRun, len: usize| {
+ // SAFETY: The caller must ensure that buf_ptr was created by elem_alloc_fn with the same
+ // len.
+ unsafe {
+ alloc::dealloc(
+ buf_ptr as *mut u8,
+ alloc::Layout::array::<sort::TimSortRun>(len).unwrap_unchecked(),
+ );
}
- }
+ };
- #[derive(Clone, Copy)]
- struct Run {
- start: usize,
- len: usize,
- }
+ sort::merge_sort(v, &mut is_less, elem_alloc_fn, elem_dealloc_fn, run_alloc_fn, run_dealloc_fn);
}
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[inline]
pub fn to_ascii_uppercase(&self) -> String {
- let mut bytes = self.as_bytes().to_vec();
- bytes.make_ascii_uppercase();
- // make_ascii_uppercase() preserves the UTF-8 invariant.
- unsafe { String::from_utf8_unchecked(bytes) }
+ let mut s = self.to_owned();
+ s.make_ascii_uppercase();
+ s
}
/// Returns a copy of this string where each character is mapped to its
#[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")]
#[inline]
pub fn to_ascii_lowercase(&self) -> String {
- let mut bytes = self.as_bytes().to_vec();
- bytes.make_ascii_lowercase();
- // make_ascii_lowercase() preserves the UTF-8 invariant.
- unsafe { String::from_utf8_unchecked(bytes) }
+ let mut s = self.to_owned();
+ s.make_ascii_lowercase();
+ s
}
}
// This is a `NonNull` to allow optimizing the size of this type in enums,
// but it is not necessarily a valid pointer.
// `Weak::new` sets this to `usize::MAX` so that it doesn’t need
- // to allocate space on the heap. That's not a value a real pointer
+ // to allocate space on the heap. That's not a value a real pointer
// will ever have because RcBox has alignment at least 2.
// This is only possible when `T: Sized`; unsized `T` never dangle.
ptr: NonNull<ArcInner<T>>,
///
/// This will succeed even if there are outstanding weak references.
///
+ // FIXME: when `Arc::into_inner` is stabilized, add this paragraph:
+ /*
+ /// It is strongly recommended to use [`Arc::into_inner`] instead if you don't
+ /// want to keep the `Arc` in the [`Err`] case.
+ /// Immediately dropping the [`Err`] payload, like in the expression
+ /// `Arc::try_unwrap(this).ok()`, can still cause the strong count to
+ /// drop to zero and the inner value of the `Arc` to be dropped:
+ /// For instance if two threads execute this expression in parallel, then
+ /// there is a race condition. The threads could first both check whether they
+ /// have the last clone of their `Arc` via `Arc::try_unwrap`, and then
+ /// both drop their `Arc` in the call to [`ok`][`Result::ok`],
+ /// taking the strong count from two down to zero.
+ ///
+ */
/// # Examples
///
/// ```
Ok(elem)
}
}
+
+ /// Returns the inner value, if the `Arc` has exactly one strong reference.
+ ///
+ /// Otherwise, [`None`] is returned and the `Arc` is dropped.
+ ///
+ /// This will succeed even if there are outstanding weak references.
+ ///
+ /// If `Arc::into_inner` is called on every clone of this `Arc`,
+ /// it is guaranteed that exactly one of the calls returns the inner value.
+ /// This means in particular that the inner value is not dropped.
+ ///
+ /// The similar expression `Arc::try_unwrap(this).ok()` does not
+ /// offer such a guarantee. See the last example below.
+ //
+ // FIXME: when `Arc::into_inner` is stabilized, add this to end
+ // of the previous sentence:
+ /*
+ /// and the documentation of [`Arc::try_unwrap`].
+ */
+ ///
+ /// # Examples
+ ///
+ /// Minimal example demonstrating the guarantee that `Arc::into_inner` gives.
+ /// ```
+ /// #![feature(arc_into_inner)]
+ ///
+ /// use std::sync::Arc;
+ ///
+ /// let x = Arc::new(3);
+ /// let y = Arc::clone(&x);
+ ///
+ /// // Two threads calling `Arc::into_inner` on both clones of an `Arc`:
+ /// let x_thread = std::thread::spawn(|| Arc::into_inner(x));
+ /// let y_thread = std::thread::spawn(|| Arc::into_inner(y));
+ ///
+ /// let x_inner_value = x_thread.join().unwrap();
+ /// let y_inner_value = y_thread.join().unwrap();
+ ///
+ /// // One of the threads is guaranteed to receive the inner value:
+ /// assert!(matches!(
+ /// (x_inner_value, y_inner_value),
+ /// (None, Some(3)) | (Some(3), None)
+ /// ));
+ /// // The result could also be `(None, None)` if the threads called
+ /// // `Arc::try_unwrap(x).ok()` and `Arc::try_unwrap(y).ok()` instead.
+ /// ```
+ ///
+ /// A more practical example demonstrating the need for `Arc::into_inner`:
+ /// ```
+ /// #![feature(arc_into_inner)]
+ ///
+ /// use std::sync::Arc;
+ ///
+ /// // Definition of a simple singly linked list using `Arc`:
+ /// #[derive(Clone)]
+ /// struct LinkedList<T>(Option<Arc<Node<T>>>);
+ /// struct Node<T>(T, Option<Arc<Node<T>>>);
+ ///
+ /// // Dropping a long `LinkedList<T>` relying on the destructor of `Arc`
+ /// // can cause a stack overflow. To prevent this, we can provide a
+ /// // manual `Drop` implementation that does the destruction in a loop:
+ /// impl<T> Drop for LinkedList<T> {
+ /// fn drop(&mut self) {
+ /// let mut link = self.0.take();
+ /// while let Some(arc_node) = link.take() {
+ /// if let Some(Node(_value, next)) = Arc::into_inner(arc_node) {
+ /// link = next;
+ /// }
+ /// }
+ /// }
+ /// }
+ ///
+ /// // Implementation of `new` and `push` omitted
+ /// impl<T> LinkedList<T> {
+ /// /* ... */
+ /// # fn new() -> Self {
+ /// # LinkedList(None)
+ /// # }
+ /// # fn push(&mut self, x: T) {
+ /// # self.0 = Some(Arc::new(Node(x, self.0.take())));
+ /// # }
+ /// }
+ ///
+ /// // The following code could have still caused a stack overflow
+ /// // despite the manual `Drop` impl if that `Drop` impl had used
+ /// // `Arc::try_unwrap(arc).ok()` instead of `Arc::into_inner(arc)`.
+ ///
+ /// // Create a long list and clone it
+ /// let mut x = LinkedList::new();
+ /// for i in 0..100000 {
+ /// x.push(i); // Adds i to the front of x
+ /// }
+ /// let y = x.clone();
+ ///
+ /// // Drop the clones in parallel
+ /// let x_thread = std::thread::spawn(|| drop(x));
+ /// let y_thread = std::thread::spawn(|| drop(y));
+ /// x_thread.join().unwrap();
+ /// y_thread.join().unwrap();
+ /// ```
+
+ // FIXME: when `Arc::into_inner` is stabilized, adjust above documentation
+ // and the documentation of `Arc::try_unwrap` according to the `FIXME`s. Also
+ // open an issue on rust-lang/rust-clippy, asking for a lint against
+ // `Arc::try_unwrap(...).ok()`.
+ #[inline]
+ #[unstable(feature = "arc_into_inner", issue = "106894")]
+ pub fn into_inner(this: Self) -> Option<T> {
+ // Make sure that the ordinary `Drop` implementation isn’t called as well
+ let mut this = mem::ManuallyDrop::new(this);
+
+ // Following the implementation of `drop` and `drop_slow`
+ if this.inner().strong.fetch_sub(1, Release) != 1 {
+ return None;
+ }
+
+ acquire!(this.inner().strong);
+
+ // SAFETY: This mirrors the line
+ //
+ // unsafe { ptr::drop_in_place(Self::get_mut_unchecked(self)) };
+ //
+ // in `drop_slow`. Instead of dropping the value behind the pointer,
+ // it is read and eventually returned; `ptr::read` has the same
+ // safety conditions as `ptr::drop_in_place`.
+ let inner = unsafe { ptr::read(Self::get_mut_unchecked(&mut this)) };
+
+ drop(Weak { ptr: this.ptr });
+
+ Some(inner)
+ }
}
impl<T> Arc<[T]> {
//
// The acquire label here ensures a happens-before relationship with any
// writes to `strong` (in particular in `Weak::upgrade`) prior to decrements
- // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded
+ // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded
// weak ref was never dropped, the CAS here will fail so we do not care to synchronize.
if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() {
// This needs to be an `Acquire` to synchronize with the decrement of the `strong`
}
// This fence is needed to prevent reordering of use of the data and
- // deletion of the data. Because it is marked `Release`, the decreasing
+ // deletion of the data. Because it is marked `Release`, the decreasing
// of the reference count synchronizes with this `Acquire` fence. This
// means that use of the data happens before decreasing the reference
// count, which happens before this fence, which happens before the
} else {
return Weak { ptr: self.ptr };
};
- // See comments in Arc::clone() for why this is relaxed. This can use a
+ // See comments in Arc::clone() for why this is relaxed. This can use a
// fetch_add (ignoring the lock) because the weak count is only locked
// where are *no other* weak pointers in existence. (So we can't be
// running this code in that case).
assert_eq!(Arc::try_unwrap(x), Ok(5));
}
+#[test]
+fn into_inner() {
+ for _ in 0..100
+ // ^ Increase chances of hitting potential race conditions
+ {
+ let x = Arc::new(3);
+ let y = Arc::clone(&x);
+ let r_thread = std::thread::spawn(|| Arc::into_inner(x));
+ let s_thread = std::thread::spawn(|| Arc::into_inner(y));
+ let r = r_thread.join().expect("r_thread panicked");
+ let s = s_thread.join().expect("s_thread panicked");
+ assert!(
+ matches!((r, s), (None, Some(3)) | (Some(3), None)),
+ "assertion failed: unexpected result `{:?}`\
+ \n expected `(None, Some(3))` or `(Some(3), None)`",
+ (r, s),
+ );
+ }
+
+ let x = Arc::new(3);
+ assert_eq!(Arc::into_inner(x), Some(3));
+
+ let x = Arc::new(4);
+ let y = Arc::clone(&x);
+ assert_eq!(Arc::into_inner(x), None);
+ assert_eq!(Arc::into_inner(y), Some(4));
+
+ let x = Arc::new(5);
+ let _w = Arc::downgrade(&x);
+ assert_eq!(Arc::into_inner(x), Some(5));
+}
+
#[test]
fn into_from_raw() {
let x = Arc::new(Box::new("hello"));
}
// as_slice() must only be called when iter.len() is > 0 because
- // vec::Splice modifies vec::Drain fields and may grow the vec which would invalidate
- // the iterator's internal pointers. Creating a reference to deallocated memory
- // is invalid even when it is zero-length
+ // it also gets touched by vec::Splice which may turn it into a dangling pointer
+ // which would make it and the vec pointer point to different allocations which would
+ // lead to invalid pointer arithmetic below.
let drop_ptr = iter.as_slice().as_ptr();
unsafe {
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
pub(super) alloc: ManuallyDrop<A>,
pub(super) ptr: *const T,
- pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
}
let mut this = ManuallyDrop::new(self);
// SAFETY: This allocation originally came from a `Vec`, so it passes
- // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`,
+ // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`,
// so the `sub_ptr`s below cannot wrap, and will produce a well-formed
- // range. `end` ≤ `buf + cap`, so the range will be in-bounds.
+ // range. `end` ≤ `buf + cap`, so the range will be in-bounds.
// Taking `alloc` is ok because nothing else is going to look at it,
// since our `Drop` impl isn't going to run so there's no more code.
unsafe {
#[rustc_specialization_trait]
pub(super) unsafe trait IsZero {
- /// Whether this value's representation is all zeros
+ /// Whether this value's representation is all zeros,
+ /// or can be represented with all zeroes.
fn is_zero(&self) -> bool;
}
#[inline]
fn is_zero(&self) -> bool {
// Because this is generated as a runtime check, it's not obvious that
- // it's worth doing if the array is really long. The threshold here
+ // it's worth doing if the array is really long. The threshold here
// is largely arbitrary, but was picked because as of 2022-07-01 LLVM
// fails to const-fold the check in `vec![[1; 32]; n]`
// See https://github.com/rust-lang/rust/pull/97581#issuecomment-1166628022
NonZeroIsize,
);
+macro_rules! impl_is_zero_option_of_num {
+ ($($t:ty,)+) => {$(
+ unsafe impl IsZero for Option<$t> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ const {
+ let none: Self = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+ assert!(none.is_none());
+ }
+ self.is_none()
+ }
+ }
+ )+};
+}
+
+impl_is_zero_option_of_num!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, usize, isize,);
+
unsafe impl<T: IsZero> IsZero for Wrapping<T> {
#[inline]
fn is_zero(&self) -> bool {
self.reserve(range.len());
// SAFETY:
- // - `slice::range` guarantees that the given range is valid for indexing self
+ // - `slice::range` guarantees that the given range is valid for indexing self
unsafe {
self.spec_extend_from_within(range);
}
// HACK(japaric): with cfg(test) the inherent `[T]::to_vec` method, which is
// required for this method definition, is not available. Instead use the
- // `slice::to_vec` function which is only available with cfg(test)
+ // `slice::to_vec` function which is only available with cfg(test)
// NB see the slice::hack module in slice.rs for more information
#[cfg(test)]
fn clone(&self) -> Self {
impl<I: Iterator, A: Allocator> Drop for Splice<'_, I, A> {
fn drop(&mut self) {
self.drain.by_ref().for_each(drop);
+ // At this point draining is done and the only remaining tasks are splicing
+ // and moving things into the final place.
+ // Which means we can replace the slice::Iter with pointers that won't point to deallocated
+ // memory, so that Drain::drop is still allowed to call iter.len(), otherwise it would break
+ // the ptr.sub_ptr contract.
+ self.drain.iter = (&[]).iter();
unsafe {
if self.drain.tail_len == 0 {
#![feature(allocator_api)]
#![feature(alloc_layout_extra)]
#![feature(assert_matches)]
-#![feature(box_syntax)]
#![feature(btree_drain_filter)]
#![feature(cow_is_borrowed)]
#![feature(const_box)]
}
// Test that, if we reserved enough space, adding and removing elements does not
- // invalidate references into the vector (such as `v0`). This test also
+ // invalidate references into the vector (such as `v0`). This test also
// runs in Miri, which would detect such problems.
// Note that this test does *not* constitute a stable guarantee that all these functions do not
// reallocate! Only what is explicitly documented at
/// While `TypeId` implements `Hash`, `PartialOrd`, and `Ord`, it is worth
/// noting that the hashes and ordering will vary between Rust releases. Beware
/// of relying on them inside of your code!
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+#[derive(Clone, Copy, Debug, Hash, Eq)]
+#[derive_const(PartialEq, PartialOrd, Ord)]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct TypeId {
t: u64,
/// use std::array::IntoIter;
/// use std::mem::MaybeUninit;
///
- /// # // Hi! Thanks for reading the code. This is restricted to `Copy` because
- /// # // otherwise it could leak. A fully-general version this would need a drop
+ /// # // Hi! Thanks for reading the code. This is restricted to `Copy` because
+ /// # // otherwise it could leak. A fully-general version this would need a drop
/// # // guard to handle panics from the iterator, but this works for an example.
/// fn next_chunk<T: Copy, const N: usize>(
/// it: &mut impl Iterator<Item = T>,
let initialized = 0..0;
// SAFETY: We're telling it that none of the elements are initialized,
- // which is trivially true. And ∀N: usize, 0 <= N.
+ // which is trivially true. And ∀N: usize, 0 <= N.
unsafe { Self::new_unchecked(buffer, initialized) }
}
use crate::fmt::{self, Debug, Display};
use crate::marker::{PhantomData, Unsize};
use crate::mem;
-use crate::ops::{CoerceUnsized, Deref, DerefMut};
+use crate::ops::{CoerceUnsized, Deref, DerefMut, DispatchFromDyn};
use crate::ptr::{self, NonNull};
mod lazy;
#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<Cell<U>> for Cell<T> {}
+// Allow types that wrap `Cell` to also implement `DispatchFromDyn`
+// and become object safe method receivers.
+// Note that currently `Cell` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: Cell<&Self>` won't work
+// `self: CellWrapper<Self>` becomes possible
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Cell<U>> for Cell<T> {}
+
impl<T> Cell<[T]> {
/// Returns a `&[Cell<T>]` from a `&Cell<[T]>`
///
#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<UnsafeCell<U>> for UnsafeCell<T> {}
+// Allow types that wrap `UnsafeCell` to also implement `DispatchFromDyn`
+// and become object safe method receivers.
+// Note that currently `UnsafeCell` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: UnsafeCell<&Self>` won't work
+// `self: UnsafeCellWrapper<Self>` becomes possible
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<UnsafeCell<U>> for UnsafeCell<T> {}
+
/// [`UnsafeCell`], but [`Sync`].
///
/// This is just an `UnsafeCell`, except it implements `Sync`
//#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<SyncUnsafeCell<U>> for SyncUnsafeCell<T> {}
+// Allow types that wrap `SyncUnsafeCell` to also implement `DispatchFromDyn`
+// and become object safe method receivers.
+// Note that currently `SyncUnsafeCell` itself cannot be a method receiver
+// because it does not implement Deref.
+// In other words:
+// `self: SyncUnsafeCell<&Self>` won't work
+// `self: SyncUnsafeCellWrapper<Self>` becomes possible
+#[unstable(feature = "dispatch_from_dyn", issue = "none")]
+//#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
+impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<SyncUnsafeCell<U>> for SyncUnsafeCell<T> {}
+
#[allow(unused)]
fn assert_coerce_unsized(
a: UnsafeCell<&i32>,
OnceCell { inner: UnsafeCell::new(Some(value)) }
}
}
+
+// Just like for `Cell<T>` this isn't needed, but results in nicer error messages.
+#[unstable(feature = "once_cell", issue = "74465")]
+impl<T> !Sync for OnceCell<T> {}
F: ~const Destruct,
K: ~const Destruct,
{
- const fn imp<T, F: ~const FnMut(&T) -> K, K: ~const Ord>(
- f: &mut F,
- (v1, v2): (&T, &T),
- ) -> Ordering
- where
- T: ~const Destruct,
- K: ~const Destruct,
- {
- f(v1).cmp(&f(v2))
+ cfg_if! {
+ if #[cfg(bootstrap)] {
+ const fn imp<T, F: ~const FnMut(&T) -> K, K: ~const Ord>(
+ f: &mut F,
+ (v1, v2): (&T, &T),
+ ) -> Ordering
+ where
+ T: ~const Destruct,
+ K: ~const Destruct,
+ {
+ f(v1).cmp(&f(v2))
+ }
+ min_by(v1, v2, ConstFnMutClosure::new(&mut f, imp))
+ } else {
+ min_by(v1, v2, const |v1, v2| f(v1).cmp(&f(v2)))
+ }
}
- min_by(v1, v2, ConstFnMutClosure::new(&mut f, imp))
}
/// Compares and returns the maximum of two values.
/// This method should generally not be invoked manually, but rather through
/// the [`write!`] macro itself.
///
+ /// # Errors
+ ///
+ /// This function will return an instance of [`Error`] on error. Please see
+ /// [write_str](Write::write_str) for details.
+ ///
/// # Examples
///
/// ```
}
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 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!")`.
///
- /// This can be used to avoid allocations in the most trivial case.
+ /// 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")]
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")]
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_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);
where
Self: TrustedRandomAccessNoCoerce,
{
- // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index
+ // SAFETY: The TrustedRandomAccess contract requires that callers only pass an index
// that is in bounds.
// Additionally Self: TrustedRandomAccess is only implemented for Copy types
// which means even repeated reads of the same index would be safe.
+use crate::fmt;
use crate::ops::{Generator, GeneratorState};
use crate::pin::Pin;
/// ```
#[inline]
#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
-pub fn from_generator<G: Generator<Return = ()> + Unpin>(
- generator: G,
-) -> impl Iterator<Item = G::Yield> {
+pub fn from_generator<G: Generator<Return = ()> + Unpin>(generator: G) -> FromGenerator<G> {
FromGenerator(generator)
}
-struct FromGenerator<G>(G);
+/// An iterator over the values yielded by an underlying generator.
+///
+/// This `struct` is created by the [`iter::from_generator()`] function. See its documentation for
+/// more.
+///
+/// [`iter::from_generator()`]: from_generator
+#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
+#[derive(Clone)]
+pub struct FromGenerator<G>(G);
+#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
impl<G: Generator<Return = ()> + Unpin> Iterator for FromGenerator<G> {
type Item = G::Yield;
}
}
}
+
+#[unstable(feature = "iter_from_generator", issue = "43122", reason = "generators are unstable")]
+impl<G> fmt::Debug for FromGenerator<G> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FromGenerator").finish()
+ }
+}
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`"
),
+ on(
+ _Self = "{float}",
+ 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`"
+ ),
label = "`{Self}` is not an iterator",
message = "`{Self}` is not an iterator"
)]
#![feature(cfg_sanitize)]
#![feature(cfg_target_has_atomic)]
#![feature(cfg_target_has_atomic_equal_alignment)]
+#![cfg_attr(not(bootstrap), feature(const_closures))]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_mut_refs)]
#![feature(const_precise_live_drops)]
#[cfg_attr(not(test), rustc_diagnostic_item = "Sync")]
#[lang = "sync"]
#[rustc_on_unimplemented(
+ on(
+ _Self = "std::cell::OnceCell<T>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead"
+ ),
+ on(
+ _Self = "std::cell::Cell<u8>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<u16>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU16` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<u32>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU32` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<u64>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU64` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<usize>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicUsize` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i8>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI8` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i16>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI16` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i32>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<i64>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI64` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<isize>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicIsize` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<bool>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead",
+ ),
+ on(
+ _Self = "std::cell::Cell<T>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`",
+ ),
+ on(
+ _Self = "std::cell::RefCell<T>",
+ note = "if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead",
+ ),
message = "`{Self}` cannot be shared between threads safely",
label = "`{Self}` cannot be shared between threads safely"
)]
/// Developer's Manual (Volume 1).
///
/// The only field which is relevant for the following code is PC, Precision Control. This
- /// field determines the precision of the operations performed by the FPU. It can be set to:
+ /// field determines the precision of the operations performed by the FPU. It can be set to:
/// - 0b00, single precision i.e., 32-bits
/// - 0b10, double precision i.e., 64-bits
/// - 0b11, double extended precision i.e., 80-bits (default state)
///
/// ```
/// #![feature(bigint_helper_methods)]
- /// // Only the most significant word is signed.
+ /// // Only the most significant word is signed.
/// //
#[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")]
#[doc = concat!("// + -5 9 (b = -5 × 2^", stringify!($BITS), " + 9)")]
/// overflow.
///
/// Performs "ternary subtraction" by subtracting both an integer
- /// operandand a borrow-in bit from `self`, and returns a tuple of the
+ /// operand and a borrow-in bit from `self`, and returns a tuple of the
/// difference along with a boolean indicating whether an arithmetic
/// overflow would occur. On overflow, the wrapped value is returned.
///
///
/// ```
/// #![feature(bigint_helper_methods)]
- /// // Only the most significant word is signed.
+ /// // Only the most significant word is signed.
/// //
#[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")]
#[doc = concat!("// - -5 9 (b = -5 × 2^", stringify!($BITS), " + 9)")]
///
/// # Examples
///
- /// Converts an <code>Option<[String]></code> into an <code>Option<[usize]></code>, preserving
- /// the original. The [`map`] method takes the `self` argument by value, consuming the original,
- /// so this technique uses `as_ref` to first take an `Option` to a reference
- /// to the value inside the original.
+ /// Calculates the length of an <code>Option<[String]></code> as an <code>Option<[usize]></code>
+ /// without moving the [`String`]. The [`map`] method takes the `self` argument by value,
+ /// consuming the original, so this technique uses `as_ref` to first take an `Option` to a
+ /// reference to the value inside the original.
///
/// [`map`]: Option::map
/// [String]: ../../std/string/struct.String.html "String"
+ /// [`String`]: ../../std/string/struct.String.html "String"
///
/// ```
/// let text: Option<String> = Some("Hello, world!".to_string());
///
/// # Examples
///
- /// Converts an <code>Option<[String]></code> into an <code>Option<[usize]></code>, consuming
- /// the original:
+ /// Calculates the length of an <code>Option<[String]></code> as an
+ /// <code>Option<[usize]></code>, consuming the original:
///
/// [String]: ../../std/string/struct.String.html "String"
/// ```
impl<'a, T: ?Sized> Pin<&'a T> {
/// Constructs a new pin by mapping the interior value.
///
- /// For example, if you wanted to get a `Pin` of a field of something,
+ /// For example, if you wanted to get a `Pin` of a field of something,
/// you could use this to get access to that field in one line of code.
/// However, there are several gotchas with these "pinning projections";
/// see the [`pin` module] documentation for further details on that topic.
/// Construct a new pin by mapping the interior value.
///
- /// For example, if you wanted to get a `Pin` of a field of something,
+ /// For example, if you wanted to get a `Pin` of a field of something,
/// you could use this to get access to that field in one line of code.
/// However, there are several gotchas with these "pinning projections";
/// see the [`pin` module] documentation for further details on that topic.
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
// provenance).
- unsafe { mem::transmute(self) }
+ unsafe { mem::transmute(self.cast::<()>()) }
}
/// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn expose_addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn expose_addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
- self as usize
+ self.cast::<()>() as usize
}
/// Creates a new pointer with the given address.
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn with_addr(self, addr: usize) -> Self
- where
- T: Sized,
- {
+ pub fn with_addr(self, addr: usize) -> Self {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
//
// In the mean-time, this operation is defined to be "as if" it was
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
- where
- T: Sized,
- {
+ pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
self.with_addr(f(self.addr()))
}
/// This computes the same value that [`offset_from`](#method.offset_from)
/// would compute, but with the added precondition that the offset is
/// guaranteed to be non-negative. This method is equivalent to
- /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+ /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
/// but it provides slightly more information to the optimizer, which can
/// sometimes allow it to optimize slightly better with some backends.
///
// offset is not a multiple of `stride`, the input pointer was misaligned and no pointer
// offset will be able to produce a `p` aligned to the specified `a`.
//
- // The naive `-p (mod a)` equation inhibits LLVM's ability to select instructions
+ // The naive `-p (mod a)` equation inhibits LLVM's ability to select instructions
// like `lea`. We compute `(round_up_to_next_alignment(p, a) - p)` instead. This
// redistributes operations around the load-bearing, but pessimizing `and` instruction
// sufficiently for LLVM to be able to utilize the various optimizations it knows about.
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
// SAFETY: Pointer-to-integer transmutes are valid (if you are okay with losing the
// provenance).
- unsafe { mem::transmute(self) }
+ unsafe { mem::transmute(self.cast::<()>()) }
}
/// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future
#[must_use]
#[inline(always)]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn expose_addr(self) -> usize
- where
- T: Sized,
- {
+ pub fn expose_addr(self) -> usize {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
- self as usize
+ self.cast::<()>() as usize
}
/// Creates a new pointer with the given address.
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn with_addr(self, addr: usize) -> Self
- where
- T: Sized,
- {
+ pub fn with_addr(self, addr: usize) -> Self {
// FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic.
//
// In the mean-time, this operation is defined to be "as if" it was
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self
- where
- T: Sized,
- {
+ pub fn map_addr(self, f: impl FnOnce(usize) -> usize) -> Self {
self.with_addr(f(self.addr()))
}
/// This computes the same value that [`offset_from`](#method.offset_from)
/// would compute, but with the added precondition that the offset is
/// guaranteed to be non-negative. This method is equivalent to
- /// `usize::from(self.offset_from(origin)).unwrap_unchecked()`,
+ /// `usize::try_from(self.offset_from(origin)).unwrap_unchecked()`,
/// but it provides slightly more information to the optimizer, which can
/// sometimes allow it to optimize slightly better with some backends.
///
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn addr(self) -> NonZeroUsize
- where
- T: Sized,
- {
+ pub fn addr(self) -> NonZeroUsize {
// SAFETY: The pointer is guaranteed by the type to be non-null,
// meaning that the address will be non-zero.
unsafe { NonZeroUsize::new_unchecked(self.pointer.addr()) }
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn with_addr(self, addr: NonZeroUsize) -> Self
- where
- T: Sized,
- {
+ pub fn with_addr(self, addr: NonZeroUsize) -> Self {
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
unsafe { NonNull::new_unchecked(self.pointer.with_addr(addr.get()) as *mut _) }
}
#[must_use]
#[inline]
#[unstable(feature = "strict_provenance", issue = "95228")]
- pub fn map_addr(self, f: impl FnOnce(NonZeroUsize) -> NonZeroUsize) -> Self
- where
- T: Sized,
- {
+ pub fn map_addr(self, f: impl FnOnce(NonZeroUsize) -> NonZeroUsize) -> Self {
self.with_addr(f(self.addr()))
}
use crate::cmp;
use crate::cmp::Ordering;
use crate::fmt;
-use crate::intrinsics::{assume, exact_div, unchecked_sub};
+use crate::intrinsics::assume;
use crate::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce};
use crate::marker::{PhantomData, Send, Sized, Sync};
use crate::mem::{self, SizedTypeProperties};
}
}
-// Macro helper functions
-#[inline(always)]
-fn size_from_ptr<T>(_: *const T) -> usize {
- mem::size_of::<T>()
-}
-
/// Immutable slice iterator
///
/// This struct is created by the [`iter`] method on [slices].
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a, T: 'a> {
ptr: NonNull<T>,
- end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
_marker: PhantomData<&'a T>,
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IterMut<'a, T: 'a> {
ptr: NonNull<T>,
- end: *mut T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ end: *mut T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
// ptr == end is a quick test for the Iterator being empty, that works
// for both ZST and non-ZST.
_marker: PhantomData<&'a mut T>,
};
}
-// To get rid of some bounds checks (see `position`), we compute the length in a somewhat
-// unexpected way. (Tested by `codegen/slice-position-bounds-check`.)
macro_rules! len {
($self: ident) => {{
#![allow(unused_unsafe)] // we're sometimes used within an unsafe block
let start = $self.ptr;
- let size = size_from_ptr(start.as_ptr());
- if size == 0 {
- // This _cannot_ use `unchecked_sub` because we depend on wrapping
+ if T::IS_ZST {
+ // This _cannot_ use `ptr_sub` because we depend on wrapping
// to represent the length of long ZST slice iterators.
$self.end.addr().wrapping_sub(start.as_ptr().addr())
} else {
- // We know that `start <= end`, so can do better than `offset_from`,
- // which needs to deal in signed. By setting appropriate flags here
- // we can tell LLVM this, which helps it remove bounds checks.
- // SAFETY: By the type invariant, `start <= end`
- let diff = unsafe { unchecked_sub($self.end.addr(), start.as_ptr().addr()) };
- // By also telling LLVM that the pointers are apart by an exact
- // multiple of the type size, it can optimize `len() == 0` down to
- // `start == end` instead of `(end - start) < size`.
- // SAFETY: By the type invariant, the pointers are aligned so the
- // distance between them must be a multiple of pointee size
- unsafe { exact_div(diff, size) }
+ // To get rid of some bounds checks (see `position`), we use ptr_sub instead of
+ // offset_from (Tested by `codegen/slice-position-bounds-check`.)
+ // SAFETY: by the type invariant pointers are aligned and `start <= end`
+ unsafe { $self.end.sub_ptr(start.as_ptr()) }
}
}};
}
/// Pure rust memchr implementation, taken from rust-memchr
pub mod memchr;
+#[unstable(
+ feature = "slice_internals",
+ issue = "none",
+ reason = "exposed from core to be reused in std;"
+)]
+pub mod sort;
+
mod ascii;
mod cmp;
mod index;
mod iter;
mod raw;
mod rotate;
-mod sort;
mod specialize;
#[stable(feature = "rust1", since = "1.0.0")]
// Because this function is first compiled in isolation,
// this check tells LLVM that the indexing below is
- // in-bounds. Then after inlining -- once the actual
+ // in-bounds. Then after inlining -- once the actual
// lengths of the slices are known -- it's removed.
let (a, b) = (&mut a[..n], &mut b[..n]);
/// let mut iter = slice.windows(4);
/// assert!(iter.next().is_none());
/// ```
+ ///
+ /// There's no `windows_mut`, as that existing would let safe code violate the
+ /// "only one `&mut` at a time to the same thing" rule. However, you can sometimes
+ /// use [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in
+ /// conjunction with `windows` to accomplish something similar:
+ /// ```
+ /// use std::cell::Cell;
+ ///
+ /// let mut array = ['R', 'u', 's', 't', ' ', '2', '0', '1', '5'];
+ /// let slice = &mut array[..];
+ /// let slice_of_cells: &[Cell<char>] = Cell::from_mut(slice).as_slice_of_cells();
+ /// for w in slice_of_cells.windows(3) {
+ /// Cell::swap(&w[0], &w[2]);
+ /// }
+ /// assert_eq!(array, ['s', 't', ' ', '2', '0', '1', '5', 'u', 'R']);
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn windows(&self, size: usize) -> Windows<'_, T> {
ArrayChunksMut::new(self)
}
- /// Returns an iterator over overlapping windows of `N` elements of a slice,
+ /// Returns an iterator over overlapping windows of `N` elements of a slice,
/// starting at the beginning of the slice.
///
/// This is the const generic equivalent of [`windows`].
let mid = left + size / 2;
// SAFETY: the while condition means `size` is strictly positive, so
- // `size/2 < size`. Thus `left + size/2 < left + size`, which
+ // `size/2 < size`. Thus `left + size/2 < left + size`, which
// coupled with the `left + size <= self.len()` invariant means
// we have `left + size/2 < self.len()`, and this is in-bounds.
let cmp = f(unsafe { self.get_unchecked(mid) });
//!
//! Unstable sorting is compatible with core because it doesn't allocate memory, unlike our
//! stable sorting implementation.
+//!
+//! In addition it also contains the core logic of the stable sort used by `slice::sort` based on
+//! TimSort.
use crate::cmp;
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
impl<T> Drop for CopyOnDrop<T> {
fn drop(&mut self) {
- // SAFETY: This is a helper class.
- // Please refer to its usage for correctness.
- // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`.
+ // SAFETY: This is a helper class.
+ // Please refer to its usage for correctness.
+ // Namely, one must be sure that `src` and `dst` does not overlap as required by `ptr::copy_nonoverlapping`.
unsafe {
ptr::copy_nonoverlapping(self.src, self.dest, 1);
}
) where
F: FnMut(&T, &T) -> bool,
{
+ // Limit the amount of iterations and fall back to heapsort, similarly to `slice::sort_unstable`.
+ // This lowers the worst case running time from O(n^2) to O(n log n).
+ // FIXME: Investigate whether it would be better to use something like Median of Medians
+ // or Fast Deterministic Selection to guarantee O(n) worst case.
+ let mut limit = usize::BITS - v.len().leading_zeros();
+
+ // True if the last partitioning was reasonably balanced.
+ let mut was_balanced = true;
+
loop {
// For slices of up to this length it's probably faster to simply sort them.
const MAX_INSERTION: usize = 10;
return;
}
+ if limit == 0 {
+ heapsort(v, is_less);
+ return;
+ }
+
+ // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling
+ // some elements around. Hopefully we'll choose a better pivot this time.
+ if !was_balanced {
+ break_patterns(v);
+ limit -= 1;
+ }
+
// Choose a pivot
let (pivot, _) = choose_pivot(v, is_less);
}
let (mid, _) = partition(v, pivot, is_less);
+ was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8;
// Split the slice into `left`, `pivot`, and `right`.
let (left, right) = v.split_at_mut(mid);
}
}
+/// Reorder the slice such that the element at `index` is at its final sorted position.
pub fn partition_at_index<T, F>(
v: &mut [T],
index: usize,
let pivot = &mut pivot[0];
(left, pivot, right)
}
+
+/// Inserts `v[0]` into pre-sorted sequence `v[1..]` so that whole `v[..]` becomes sorted.
+///
+/// This is the integral subroutine of insertion sort.
+fn insert_head<T, F>(v: &mut [T], is_less: &mut F)
+where
+ F: FnMut(&T, &T) -> bool,
+{
+ if v.len() >= 2 && is_less(&v[1], &v[0]) {
+ // SAFETY: Copy tmp back even if panic, and ensure unique observation.
+ unsafe {
+ // There are three ways to implement insertion here:
+ //
+ // 1. Swap adjacent elements until the first one gets to its final destination.
+ // However, this way we copy data around more than is necessary. If elements are big
+ // structures (costly to copy), this method will be slow.
+ //
+ // 2. Iterate until the right place for the first element is found. Then shift the
+ // elements succeeding it to make room for it and finally place it into the
+ // remaining hole. This is a good method.
+ //
+ // 3. Copy the first element into a temporary variable. Iterate until the right place
+ // for it is found. As we go along, copy every traversed element into the slot
+ // preceding it. Finally, copy data from the temporary variable into the remaining
+ // hole. This method is very good. Benchmarks demonstrated slightly better
+ // performance than with the 2nd method.
+ //
+ // All methods were benchmarked, and the 3rd showed best results. So we chose that one.
+ let tmp = mem::ManuallyDrop::new(ptr::read(&v[0]));
+
+ // Intermediate state of the insertion process is always tracked by `hole`, which
+ // serves two purposes:
+ // 1. Protects integrity of `v` from panics in `is_less`.
+ // 2. Fills the remaining hole in `v` in the end.
+ //
+ // Panic safety:
+ //
+ // If `is_less` panics at any point during the process, `hole` will get dropped and
+ // fill the hole in `v` with `tmp`, thus ensuring that `v` still holds every object it
+ // initially held exactly once.
+ let mut hole = InsertionHole { src: &*tmp, dest: &mut v[1] };
+ ptr::copy_nonoverlapping(&v[1], &mut v[0], 1);
+
+ for i in 2..v.len() {
+ if !is_less(&v[i], &*tmp) {
+ break;
+ }
+ ptr::copy_nonoverlapping(&v[i], &mut v[i - 1], 1);
+ hole.dest = &mut v[i];
+ }
+ // `hole` gets dropped and thus copies `tmp` into the remaining hole in `v`.
+ }
+ }
+
+ // When dropped, copies from `src` into `dest`.
+ struct InsertionHole<T> {
+ src: *const T,
+ dest: *mut T,
+ }
+
+ impl<T> Drop for InsertionHole<T> {
+ fn drop(&mut self) {
+ // SAFETY: The caller must ensure that src and dest are correctly set.
+ unsafe {
+ ptr::copy_nonoverlapping(self.src, self.dest, 1);
+ }
+ }
+ }
+}
+
+/// Merges non-decreasing runs `v[..mid]` and `v[mid..]` using `buf` as temporary storage, and
+/// stores the result into `v[..]`.
+///
+/// # Safety
+///
+/// The two slices must be non-empty and `mid` must be in bounds. Buffer `buf` must be long enough
+/// to hold a copy of the shorter slice. Also, `T` must not be a zero-sized type.
+unsafe fn merge<T, F>(v: &mut [T], mid: usize, buf: *mut T, is_less: &mut F)
+where
+ F: FnMut(&T, &T) -> bool,
+{
+ let len = v.len();
+ let v = v.as_mut_ptr();
+
+ // SAFETY: mid and len must be in-bounds of v.
+ let (v_mid, v_end) = unsafe { (v.add(mid), v.add(len)) };
+
+ // The merge process first copies the shorter run into `buf`. Then it traces the newly copied
+ // run and the longer run forwards (or backwards), comparing their next unconsumed elements and
+ // copying the lesser (or greater) one into `v`.
+ //
+ // As soon as the shorter run is fully consumed, the process is done. If the longer run gets
+ // consumed first, then we must copy whatever is left of the shorter run into the remaining
+ // hole in `v`.
+ //
+ // Intermediate state of the process is always tracked by `hole`, which serves two purposes:
+ // 1. Protects integrity of `v` from panics in `is_less`.
+ // 2. Fills the remaining hole in `v` if the longer run gets consumed first.
+ //
+ // Panic safety:
+ //
+ // If `is_less` panics at any point during the process, `hole` will get dropped and fill the
+ // hole in `v` with the unconsumed range in `buf`, thus ensuring that `v` still holds every
+ // object it initially held exactly once.
+ let mut hole;
+
+ if mid <= len - mid {
+ // The left run is shorter.
+
+ // SAFETY: buf must have enough capacity for `v[..mid]`.
+ unsafe {
+ ptr::copy_nonoverlapping(v, buf, mid);
+ hole = MergeHole { start: buf, end: buf.add(mid), dest: v };
+ }
+
+ // Initially, these pointers point to the beginnings of their arrays.
+ let left = &mut hole.start;
+ let mut right = v_mid;
+ let out = &mut hole.dest;
+
+ while *left < hole.end && right < v_end {
+ // Consume the lesser side.
+ // If equal, prefer the left run to maintain stability.
+
+ // SAFETY: left and right must be valid and part of v same for out.
+ unsafe {
+ let to_copy = if is_less(&*right, &**left) {
+ get_and_increment(&mut right)
+ } else {
+ get_and_increment(left)
+ };
+ ptr::copy_nonoverlapping(to_copy, get_and_increment(out), 1);
+ }
+ }
+ } else {
+ // The right run is shorter.
+
+ // SAFETY: buf must have enough capacity for `v[mid..]`.
+ unsafe {
+ ptr::copy_nonoverlapping(v_mid, buf, len - mid);
+ hole = MergeHole { start: buf, end: buf.add(len - mid), dest: v_mid };
+ }
+
+ // Initially, these pointers point past the ends of their arrays.
+ let left = &mut hole.dest;
+ let right = &mut hole.end;
+ let mut out = v_end;
+
+ while v < *left && buf < *right {
+ // Consume the greater side.
+ // If equal, prefer the right run to maintain stability.
+
+ // SAFETY: left and right must be valid and part of v same for out.
+ unsafe {
+ let to_copy = if is_less(&*right.sub(1), &*left.sub(1)) {
+ decrement_and_get(left)
+ } else {
+ decrement_and_get(right)
+ };
+ ptr::copy_nonoverlapping(to_copy, decrement_and_get(&mut out), 1);
+ }
+ }
+ }
+ // Finally, `hole` gets dropped. If the shorter run was not fully consumed, whatever remains of
+ // it will now be copied into the hole in `v`.
+
+ unsafe fn get_and_increment<T>(ptr: &mut *mut T) -> *mut T {
+ let old = *ptr;
+
+ // SAFETY: ptr.add(1) must still be a valid pointer and part of `v`.
+ *ptr = unsafe { ptr.add(1) };
+ old
+ }
+
+ unsafe fn decrement_and_get<T>(ptr: &mut *mut T) -> *mut T {
+ // SAFETY: ptr.sub(1) must still be a valid pointer and part of `v`.
+ *ptr = unsafe { ptr.sub(1) };
+ *ptr
+ }
+
+ // When dropped, copies the range `start..end` into `dest..`.
+ struct MergeHole<T> {
+ start: *mut T,
+ end: *mut T,
+ dest: *mut T,
+ }
+
+ impl<T> Drop for MergeHole<T> {
+ fn drop(&mut self) {
+ // SAFETY: `T` is not a zero-sized type, and these are pointers into a slice's elements.
+ unsafe {
+ let len = self.end.sub_ptr(self.start);
+ ptr::copy_nonoverlapping(self.start, self.dest, len);
+ }
+ }
+ }
+}
+
+/// This merge sort borrows some (but not all) ideas from TimSort, which used to be described in
+/// detail [here](https://github.com/python/cpython/blob/main/Objects/listsort.txt). However Python
+/// has switched to a Powersort based implementation.
+///
+/// The algorithm identifies strictly descending and non-descending subsequences, which are called
+/// natural runs. There is a stack of pending runs yet to be merged. Each newly found run is pushed
+/// onto the stack, and then some pairs of adjacent runs are merged until these two invariants are
+/// satisfied:
+///
+/// 1. for every `i` in `1..runs.len()`: `runs[i - 1].len > runs[i].len`
+/// 2. for every `i` in `2..runs.len()`: `runs[i - 2].len > runs[i - 1].len + runs[i].len`
+///
+/// The invariants ensure that the total running time is *O*(*n* \* log(*n*)) worst-case.
+pub fn merge_sort<T, CmpF, ElemAllocF, ElemDeallocF, RunAllocF, RunDeallocF>(
+ v: &mut [T],
+ is_less: &mut CmpF,
+ elem_alloc_fn: ElemAllocF,
+ elem_dealloc_fn: ElemDeallocF,
+ run_alloc_fn: RunAllocF,
+ run_dealloc_fn: RunDeallocF,
+) where
+ CmpF: FnMut(&T, &T) -> bool,
+ ElemAllocF: Fn(usize) -> *mut T,
+ ElemDeallocF: Fn(*mut T, usize),
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+{
+ // Slices of up to this length get sorted using insertion sort.
+ const MAX_INSERTION: usize = 20;
+ // Very short runs are extended using insertion sort to span at least this many elements.
+ const MIN_RUN: usize = 10;
+
+ // The caller should have already checked that.
+ debug_assert!(!T::IS_ZST);
+
+ let len = v.len();
+
+ // Short arrays get sorted in-place via insertion sort to avoid allocations.
+ if len <= MAX_INSERTION {
+ if len >= 2 {
+ for i in (0..len - 1).rev() {
+ insert_head(&mut v[i..], is_less);
+ }
+ }
+ return;
+ }
+
+ // Allocate a buffer to use as scratch memory. We keep the length 0 so we can keep in it
+ // shallow copies of the contents of `v` without risking the dtors running on copies if
+ // `is_less` panics. When merging two sorted runs, this buffer holds a copy of the shorter run,
+ // which will always have length at most `len / 2`.
+ let buf = BufGuard::new(len / 2, elem_alloc_fn, elem_dealloc_fn);
+ let buf_ptr = buf.buf_ptr;
+
+ let mut runs = RunVec::new(run_alloc_fn, run_dealloc_fn);
+
+ // In order to identify natural runs in `v`, we traverse it backwards. That might seem like a
+ // strange decision, but consider the fact that merges more often go in the opposite direction
+ // (forwards). According to benchmarks, merging forwards is slightly faster than merging
+ // backwards. To conclude, identifying runs by traversing backwards improves performance.
+ let mut end = len;
+ while end > 0 {
+ // Find the next natural run, and reverse it if it's strictly descending.
+ let mut start = end - 1;
+ if start > 0 {
+ start -= 1;
+
+ // SAFETY: The v.get_unchecked must be fed with correct inbound indicies.
+ unsafe {
+ if is_less(v.get_unchecked(start + 1), v.get_unchecked(start)) {
+ while start > 0 && is_less(v.get_unchecked(start), v.get_unchecked(start - 1)) {
+ start -= 1;
+ }
+ v[start..end].reverse();
+ } else {
+ while start > 0 && !is_less(v.get_unchecked(start), v.get_unchecked(start - 1))
+ {
+ start -= 1;
+ }
+ }
+ }
+ }
+
+ // Insert some more elements into the run if it's too short. Insertion sort is faster than
+ // merge sort on short sequences, so this significantly improves performance.
+ while start > 0 && end - start < MIN_RUN {
+ start -= 1;
+ insert_head(&mut v[start..end], is_less);
+ }
+
+ // Push this run onto the stack.
+ runs.push(TimSortRun { start, len: end - start });
+ end = start;
+
+ // Merge some pairs of adjacent runs to satisfy the invariants.
+ while let Some(r) = collapse(runs.as_slice()) {
+ let left = runs[r + 1];
+ let right = runs[r];
+ // SAFETY: `buf_ptr` must hold enough capacity for the shorter of the two sides, and
+ // neither side may be on length 0.
+ unsafe {
+ merge(&mut v[left.start..right.start + right.len], left.len, buf_ptr, is_less);
+ }
+ runs[r] = TimSortRun { start: left.start, len: left.len + right.len };
+ runs.remove(r + 1);
+ }
+ }
+
+ // Finally, exactly one run must remain in the stack.
+ debug_assert!(runs.len() == 1 && runs[0].start == 0 && runs[0].len == len);
+
+ // Examines the stack of runs and identifies the next pair of runs to merge. More specifically,
+ // if `Some(r)` is returned, that means `runs[r]` and `runs[r + 1]` must be merged next. If the
+ // algorithm should continue building a new run instead, `None` is returned.
+ //
+ // TimSort is infamous for its buggy implementations, as described here:
+ // http://envisage-project.eu/timsort-specification-and-verification/
+ //
+ // The gist of the story is: we must enforce the invariants on the top four runs on the stack.
+ // Enforcing them on just top three is not sufficient to ensure that the invariants will still
+ // hold for *all* runs in the stack.
+ //
+ // This function correctly checks invariants for the top four runs. Additionally, if the top
+ // run starts at index 0, it will always demand a merge operation until the stack is fully
+ // collapsed, in order to complete the sort.
+ #[inline]
+ fn collapse(runs: &[TimSortRun]) -> Option<usize> {
+ let n = runs.len();
+ if n >= 2
+ && (runs[n - 1].start == 0
+ || runs[n - 2].len <= runs[n - 1].len
+ || (n >= 3 && runs[n - 3].len <= runs[n - 2].len + runs[n - 1].len)
+ || (n >= 4 && runs[n - 4].len <= runs[n - 3].len + runs[n - 2].len))
+ {
+ if n >= 3 && runs[n - 3].len < runs[n - 1].len { Some(n - 3) } else { Some(n - 2) }
+ } else {
+ None
+ }
+ }
+
+ // Extremely basic versions of Vec.
+ // Their use is super limited and by having the code here, it allows reuse between the sort
+ // implementations.
+ struct BufGuard<T, ElemDeallocF>
+ where
+ ElemDeallocF: Fn(*mut T, usize),
+ {
+ buf_ptr: *mut T,
+ capacity: usize,
+ elem_dealloc_fn: ElemDeallocF,
+ }
+
+ impl<T, ElemDeallocF> BufGuard<T, ElemDeallocF>
+ where
+ ElemDeallocF: Fn(*mut T, usize),
+ {
+ fn new<ElemAllocF>(
+ len: usize,
+ elem_alloc_fn: ElemAllocF,
+ elem_dealloc_fn: ElemDeallocF,
+ ) -> Self
+ where
+ ElemAllocF: Fn(usize) -> *mut T,
+ {
+ Self { buf_ptr: elem_alloc_fn(len), capacity: len, elem_dealloc_fn }
+ }
+ }
+
+ impl<T, ElemDeallocF> Drop for BufGuard<T, ElemDeallocF>
+ where
+ ElemDeallocF: Fn(*mut T, usize),
+ {
+ fn drop(&mut self) {
+ (self.elem_dealloc_fn)(self.buf_ptr, self.capacity);
+ }
+ }
+
+ struct RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ buf_ptr: *mut TimSortRun,
+ capacity: usize,
+ len: usize,
+ run_alloc_fn: RunAllocF,
+ run_dealloc_fn: RunDeallocF,
+ }
+
+ impl<RunAllocF, RunDeallocF> RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ fn new(run_alloc_fn: RunAllocF, run_dealloc_fn: RunDeallocF) -> Self {
+ // Most slices can be sorted with at most 16 runs in-flight.
+ const START_RUN_CAPACITY: usize = 16;
+
+ Self {
+ buf_ptr: run_alloc_fn(START_RUN_CAPACITY),
+ capacity: START_RUN_CAPACITY,
+ len: 0,
+ run_alloc_fn,
+ run_dealloc_fn,
+ }
+ }
+
+ fn push(&mut self, val: TimSortRun) {
+ if self.len == self.capacity {
+ let old_capacity = self.capacity;
+ let old_buf_ptr = self.buf_ptr;
+
+ self.capacity = self.capacity * 2;
+ self.buf_ptr = (self.run_alloc_fn)(self.capacity);
+
+ // SAFETY: buf_ptr new and old were correctly allocated and old_buf_ptr has
+ // old_capacity valid elements.
+ unsafe {
+ ptr::copy_nonoverlapping(old_buf_ptr, self.buf_ptr, old_capacity);
+ }
+
+ (self.run_dealloc_fn)(old_buf_ptr, old_capacity);
+ }
+
+ // SAFETY: The invariant was just checked.
+ unsafe {
+ self.buf_ptr.add(self.len).write(val);
+ }
+ self.len += 1;
+ }
+
+ fn remove(&mut self, index: usize) {
+ if index >= self.len {
+ panic!("Index out of bounds");
+ }
+
+ // SAFETY: buf_ptr needs to be valid and len invariant upheld.
+ unsafe {
+ // the place we are taking from.
+ let ptr = self.buf_ptr.add(index);
+
+ // Shift everything down to fill in that spot.
+ ptr::copy(ptr.add(1), ptr, self.len - index - 1);
+ }
+ self.len -= 1;
+ }
+
+ fn as_slice(&self) -> &[TimSortRun] {
+ // SAFETY: Safe as long as buf_ptr is valid and len invariant was upheld.
+ unsafe { &*ptr::slice_from_raw_parts(self.buf_ptr, self.len) }
+ }
+
+ fn len(&self) -> usize {
+ self.len
+ }
+ }
+
+ impl<RunAllocF, RunDeallocF> core::ops::Index<usize> for RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ type Output = TimSortRun;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ if index < self.len {
+ // SAFETY: buf_ptr and len invariant must be upheld.
+ unsafe {
+ return &*(self.buf_ptr.add(index));
+ }
+ }
+
+ panic!("Index out of bounds");
+ }
+ }
+
+ impl<RunAllocF, RunDeallocF> core::ops::IndexMut<usize> for RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ if index < self.len {
+ // SAFETY: buf_ptr and len invariant must be upheld.
+ unsafe {
+ return &mut *(self.buf_ptr.add(index));
+ }
+ }
+
+ panic!("Index out of bounds");
+ }
+ }
+
+ impl<RunAllocF, RunDeallocF> Drop for RunVec<RunAllocF, RunDeallocF>
+ where
+ RunAllocF: Fn(usize) -> *mut TimSortRun,
+ RunDeallocF: Fn(*mut TimSortRun, usize),
+ {
+ fn drop(&mut self) {
+ // As long as TimSortRun is Copy we don't need to drop them individually but just the
+ // whole allocation.
+ (self.run_dealloc_fn)(self.buf_ptr, self.capacity);
+ }
+ }
+}
+
+/// Internal type used by merge_sort.
+#[derive(Clone, Copy, Debug)]
+pub struct TimSortRun {
+ len: usize,
+ start: usize,
+}
// SAFETY: data races are prevented by atomic intrinsics.
unsafe { atomic_xor(self.p.get(), core::ptr::invalid_mut(val), order).cast() }
}
+
+ /// Returns a mutable pointer to the underlying pointer.
+ ///
+ /// Doing non-atomic reads and writes on the resulting integer can be a data race.
+ /// This method is mostly useful for FFI, where the function signature may use
+ /// `*mut *mut T` instead of `&AtomicPtr<T>`.
+ ///
+ /// Returning an `*mut` pointer from a shared reference to this atomic is safe because the
+ /// atomic types work with interior mutability. All modifications of an atomic change the value
+ /// through a shared reference, and can do so safely as long as they use atomic operations. Any
+ /// use of the returned raw pointer requires an `unsafe` block and still has to uphold the same
+ /// restriction: operations on it must be atomic.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore (extern-declaration)
+ /// #![feature(atomic_mut_ptr)]
+ //// use std::sync::atomic::AtomicPtr;
+ ///
+ /// extern "C" {
+ /// fn my_atomic_op(arg: *mut *mut u32);
+ /// }
+ ///
+ /// let mut value = 17;
+ /// let atomic = AtomicPtr::new(&mut value);
+ ///
+ /// // SAFETY: Safe as long as `my_atomic_op` is atomic.
+ /// unsafe {
+ /// my_atomic_op(atomic.as_mut_ptr());
+ /// }
+ /// ```
+ #[inline]
+ #[unstable(feature = "atomic_mut_ptr", reason = "recently added", issue = "66893")]
+ pub fn as_mut_ptr(&self) -> *mut *mut T {
+ self.p.get()
+ }
}
#[cfg(target_has_atomic_load_store = "8")]
#[doc = concat!(" fn my_atomic_op(arg: *mut ", stringify!($int_type), ");")]
/// }
///
- #[doc = concat!("let mut atomic = ", stringify!($atomic_type), "::new(1);")]
+ #[doc = concat!("let atomic = ", stringify!($atomic_type), "::new(1);")]
///
- // SAFETY: Safe as long as `my_atomic_op` is atomic.
+ /// // SAFETY: Safe as long as `my_atomic_op` is atomic.
/// unsafe {
/// my_atomic_op(atomic.as_mut_ptr());
/// }
/// Currently, `Context` only serves to provide access to a [`&Waker`](Waker)
/// which can be used to wake the current task.
#[stable(feature = "futures_api", since = "1.36.0")]
+#[cfg_attr(not(bootstrap), lang = "Context")]
pub struct Context<'a> {
waker: &'a Waker,
// Ensure we future-proof against variance changes by forcing
for input in inputs {
assert_eq!(input.parse(), Ok(x64));
assert_eq!(input.parse(), Ok(x32));
- let neg_input = &format!("-{input}");
+ let neg_input = format!("-{input}");
assert_eq!(neg_input.parse(), Ok(-x64));
assert_eq!(neg_input.parse(), Ok(-x32));
}
// optional:
//
// one or more similar inputs for which data[input] succeeds,
- // and the corresponding output as an array. This helps validate
+ // and the corresponding output as an array. This helps validate
// "critical points" where an input range straddles the boundary
// between valid and invalid.
// (such as the input `len..len`, which is just barely valid)
#[cfg(test)]
#[test]
fn test() {
- assert_eq!(&format!("{:.9}", spectral_norm(100)), "1.274219991");
+ assert_eq!(format!("{:.9}", spectral_norm(100)), "1.274219991");
}
fn main() {
}
/// Tests whether this file type represents a regular file.
- /// The result is mutually exclusive to the results of
+ /// The result is mutually exclusive to the results of
/// [`is_dir`] and [`is_symlink`]; only zero or one of these
/// tests may pass.
///
let mut reader = BufReader::with_capacity(5, ErrAfterFirstSeekReader { first_seek: true });
assert_eq!(reader.fill_buf().ok(), Some(&[0, 0, 0, 0, 0][..]));
- // The following seek will require two underlying seeks. The first will
- // succeed but the second will fail. This should still invalidate the
+ // The following seek will require two underlying seeks. The first will
+ // succeed but the second will fail. This should still invalidate the
// buffer.
assert!(reader.seek(SeekFrom::Current(i64::MIN)).is_err());
assert_eq!(reader.buffer().len(), 0);
static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
-static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE_MESSAGE), TAG_SIMPLE_MESSAGE);
-static_assert!(@usize_eq: (TAG_MASK & TAG_CUSTOM), TAG_CUSTOM);
-static_assert!(@usize_eq: (TAG_MASK & TAG_OS), TAG_OS);
-static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE), TAG_SIMPLE);
+static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE);
+static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM);
+static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS);
+static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE, TAG_SIMPLE);
// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we
// offset a pointer by this value, and expect it to both be within the same
let io_error = io_error.downcast::<E>().unwrap_err();
assert_eq!(SIMPLE_MESSAGE.kind, io_error.kind());
- assert_eq!(SIMPLE_MESSAGE.message, &*format!("{io_error}"));
+ assert_eq!(SIMPLE_MESSAGE.message, format!("{io_error}"));
}
#![feature(allocator_internals)]
#![feature(allow_internal_unsafe)]
#![feature(allow_internal_unstable)]
-#![feature(box_syntax)]
#![feature(c_unwind)]
#![feature(cfg_target_thread_local)]
#![feature(concat_idents)]
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"
);
}
//! This module is supported on Unix platforms and WASI, which both use a
//! similar file descriptor system for referencing OS resources.
-#![stable(feature = "io_safety", since = "1.63.0")]
+#![stable(feature = "os_fd", since = "1.66.0")]
#![deny(unsafe_op_in_unsafe_fn)]
// `RawFd`, `AsRawFd`, etc.
mod tests;
// Export the types and traits for the public API.
-#[unstable(feature = "os_fd", issue = "98699")]
+#[stable(feature = "os_fd", since = "1.66.0")]
pub use owned::*;
-#[unstable(feature = "os_fd", issue = "98699")]
+#[stable(feature = "os_fd", since = "1.66.0")]
pub use raw::*;
// For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
// will never be supported, as this is a bare metal framework with
- // no capabilities for multi-process execution. While F_DUPFD is also
+ // no capabilities for multi-process execution. While F_DUPFD is also
// not supported yet, it might be (currently it returns ENOSYS).
#[cfg(target_os = "espidf")]
let cmd = libc::F_DUPFD;
/// Ok(())
/// }
/// ```
- fn from_abstract_name<N>(name: &N) -> crate::io::Result<SocketAddr>
+ fn from_abstract_name<N>(name: N) -> crate::io::Result<SocketAddr>
where
N: AsRef<[u8]>;
if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None }
}
- fn from_abstract_name<N>(name: &N) -> crate::io::Result<Self>
+ fn from_abstract_name<N>(name: N) -> crate::io::Result<Self>
where
N: AsRef<[u8]>,
{
// and after increase and decrease, but not necessarily during their execution.
//
// Additionally, the top bit of GLOBAL_PANIC_COUNT (GLOBAL_ALWAYS_ABORT_FLAG)
- // records whether panic::always_abort() has been called. This can only be
+ // records whether panic::always_abort() has been called. This can only be
// set, never cleared.
// panic::always_abort() is usually called to prevent memory allocations done by
// the panic handling in the child created by `libc::fork`.
- // Memory allocations performed in a child created with `libc::fork` are undefined
+ // Memory allocations performed in a child created with `libc::fork` are undefined
// behavior in most operating systems.
// Accessing LOCAL_PANIC_COUNT in a child created by `libc::fork` would lead to a memory
// allocation. Only GLOBAL_PANIC_COUNT can be accessed in this situation. This is
// true if path *physically* has a root separator; for most Windows
// prefixes, it may have a "logical" root separator for the purposes of
- // normalization, e.g., \\server\share == \\server\share\.
+ // normalization, e.g., \\server\share == \\server\share\.
has_physical_root: bool,
// The iterator is double-ended, and these two states keep track of what has
/// Creates an owned [`PathBuf`] with `path` adjoined to `self`.
///
+ /// If `path` is absolute, it replaces the current path.
+ ///
/// See [`PathBuf::push`] for more details on what it means to adjoin a path.
///
/// # Examples
/// use std::path::{Path, PathBuf};
///
/// assert_eq!(Path::new("/etc").join("passwd"), PathBuf::from("/etc/passwd"));
+ /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh"));
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
}
macro_rules! impl_cmp {
- ($lhs:ty, $rhs: ty) => {
+ (<$($life:lifetime),*> $lhs:ty, $rhs: ty) => {
#[stable(feature = "partialeq_path", since = "1.6.0")]
- impl<'a, 'b> PartialEq<$rhs> for $lhs {
+ impl<$($life),*> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
<Path as PartialEq>::eq(self, other)
}
#[stable(feature = "partialeq_path", since = "1.6.0")]
- impl<'a, 'b> PartialEq<$lhs> for $rhs {
+ impl<$($life),*> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
<Path as PartialEq>::eq(self, other)
}
#[stable(feature = "cmp_path", since = "1.8.0")]
- impl<'a, 'b> PartialOrd<$rhs> for $lhs {
+ impl<$($life),*> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other)
}
#[stable(feature = "cmp_path", since = "1.8.0")]
- impl<'a, 'b> PartialOrd<$lhs> for $rhs {
+ impl<$($life),*> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other)
};
}
-impl_cmp!(PathBuf, Path);
-impl_cmp!(PathBuf, &'a Path);
-impl_cmp!(Cow<'a, Path>, Path);
-impl_cmp!(Cow<'a, Path>, &'b Path);
-impl_cmp!(Cow<'a, Path>, PathBuf);
+impl_cmp!(<> PathBuf, Path);
+impl_cmp!(<'a> PathBuf, &'a Path);
+impl_cmp!(<'a> Cow<'a, Path>, Path);
+impl_cmp!(<'a, 'b> Cow<'a, Path>, &'b Path);
+impl_cmp!(<'a> Cow<'a, Path>, PathBuf);
macro_rules! impl_cmp_os_str {
- ($lhs:ty, $rhs: ty) => {
+ (<$($life:lifetime),*> $lhs:ty, $rhs: ty) => {
#[stable(feature = "cmp_path", since = "1.8.0")]
- impl<'a, 'b> PartialEq<$rhs> for $lhs {
+ impl<$($life),*> PartialEq<$rhs> for $lhs {
#[inline]
fn eq(&self, other: &$rhs) -> bool {
<Path as PartialEq>::eq(self, other.as_ref())
}
#[stable(feature = "cmp_path", since = "1.8.0")]
- impl<'a, 'b> PartialEq<$lhs> for $rhs {
+ impl<$($life),*> PartialEq<$lhs> for $rhs {
#[inline]
fn eq(&self, other: &$lhs) -> bool {
<Path as PartialEq>::eq(self.as_ref(), other)
}
#[stable(feature = "cmp_path", since = "1.8.0")]
- impl<'a, 'b> PartialOrd<$rhs> for $lhs {
+ impl<$($life),*> PartialOrd<$rhs> for $lhs {
#[inline]
fn partial_cmp(&self, other: &$rhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self, other.as_ref())
}
#[stable(feature = "cmp_path", since = "1.8.0")]
- impl<'a, 'b> PartialOrd<$lhs> for $rhs {
+ impl<$($life),*> PartialOrd<$lhs> for $rhs {
#[inline]
fn partial_cmp(&self, other: &$lhs) -> Option<cmp::Ordering> {
<Path as PartialOrd>::partial_cmp(self.as_ref(), other)
};
}
-impl_cmp_os_str!(PathBuf, OsStr);
-impl_cmp_os_str!(PathBuf, &'a OsStr);
-impl_cmp_os_str!(PathBuf, Cow<'a, OsStr>);
-impl_cmp_os_str!(PathBuf, OsString);
-impl_cmp_os_str!(Path, OsStr);
-impl_cmp_os_str!(Path, &'a OsStr);
-impl_cmp_os_str!(Path, Cow<'a, OsStr>);
-impl_cmp_os_str!(Path, OsString);
-impl_cmp_os_str!(&'a Path, OsStr);
-impl_cmp_os_str!(&'a Path, Cow<'b, OsStr>);
-impl_cmp_os_str!(&'a Path, OsString);
-impl_cmp_os_str!(Cow<'a, Path>, OsStr);
-impl_cmp_os_str!(Cow<'a, Path>, &'b OsStr);
-impl_cmp_os_str!(Cow<'a, Path>, OsString);
+impl_cmp_os_str!(<> PathBuf, OsStr);
+impl_cmp_os_str!(<'a> PathBuf, &'a OsStr);
+impl_cmp_os_str!(<'a> PathBuf, Cow<'a, OsStr>);
+impl_cmp_os_str!(<> PathBuf, OsString);
+impl_cmp_os_str!(<> Path, OsStr);
+impl_cmp_os_str!(<'a> Path, &'a OsStr);
+impl_cmp_os_str!(<'a> Path, Cow<'a, OsStr>);
+impl_cmp_os_str!(<> Path, OsString);
+impl_cmp_os_str!(<'a> &'a Path, OsStr);
+impl_cmp_os_str!(<'a, 'b> &'a Path, Cow<'b, OsStr>);
+impl_cmp_os_str!(<'a> &'a Path, OsString);
+impl_cmp_os_str!(<'a> Cow<'a, Path>, OsStr);
+impl_cmp_os_str!(<'a, 'b> Cow<'a, Path>, &'b OsStr);
+impl_cmp_os_str!(<'a> Cow<'a, Path>, OsString);
#[stable(since = "1.7.0", feature = "strip_prefix")]
impl fmt::Display for StripPrefixError {
let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding)?;
- let cs_action = reader.read_uleb128();
+ let cs_action_entry = reader.read_uleb128();
// Callsite table is sorted by cs_start, so if we've passed the ip, we
// may stop searching.
if ip < func_start + cs_start {
return Ok(EHAction::None);
} else {
let lpad = lpad_base + cs_lpad;
- return Ok(interpret_cs_action(cs_action, lpad));
+ return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad));
}
}
}
let mut idx = ip;
loop {
let cs_lpad = reader.read_uleb128();
- let cs_action = reader.read_uleb128();
+ let cs_action_entry = reader.read_uleb128();
idx -= 1;
if idx == 0 {
// Can never have null landing pad for sjlj -- that would have
// been indicated by a -1 call site index.
let lpad = (cs_lpad + 1) as usize;
- return Ok(interpret_cs_action(cs_action, lpad));
+ return Ok(interpret_cs_action(action_table as *mut u8, cs_action_entry, lpad));
}
}
}
}
-fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
- if cs_action == 0 {
- // If cs_action is 0 then this is a cleanup (Drop::drop). We run these
+unsafe fn interpret_cs_action(
+ action_table: *mut u8,
+ cs_action_entry: u64,
+ lpad: usize,
+) -> EHAction {
+ if cs_action_entry == 0 {
+ // If cs_action_entry is 0 then this is a cleanup (Drop::drop). We run these
// for both Rust panics and foreign exceptions.
EHAction::Cleanup(lpad)
} else {
- // Stop unwinding Rust panics at catch_unwind.
- EHAction::Catch(lpad)
+ // If lpad != 0 and cs_action_entry != 0, we have to check ttype_index.
+ // If ttype_index == 0 under the condition, we take cleanup action.
+ let action_record = (action_table as *mut u8).offset(cs_action_entry as isize - 1);
+ let mut action_reader = DwarfReader::new(action_record);
+ let ttype_index = action_reader.read_sleb128();
+ if ttype_index == 0 {
+ EHAction::Cleanup(lpad)
+ } else {
+ // Stop unwinding Rust panics at catch_unwind.
+ EHAction::Catch(lpad)
+ }
}
}
return true;
}
Err(_) => {
- backoff.spin();
+ backoff.spin_light();
tail = self.tail.load(Ordering::Relaxed);
}
}
return false;
}
- backoff.spin();
+ backoff.spin_light();
tail = self.tail.load(Ordering::Relaxed);
} else {
// Snooze because we need to wait for the stamp to get updated.
- backoff.snooze();
+ backoff.spin_heavy();
tail = self.tail.load(Ordering::Relaxed);
}
}
return true;
}
Err(_) => {
- backoff.spin();
+ backoff.spin_light();
head = self.head.load(Ordering::Relaxed);
}
}
}
}
- backoff.spin();
+ backoff.spin_light();
head = self.head.load(Ordering::Relaxed);
} else {
// Snooze because we need to wait for the stamp to get updated.
- backoff.snooze();
+ backoff.spin_heavy();
head = self.head.load(Ordering::Relaxed);
}
}
if backoff.is_completed() {
break;
} else {
- backoff.spin();
+ backoff.spin_light();
}
}
fn wait_write(&self) {
let backoff = Backoff::new();
while self.state.load(Ordering::Acquire) & WRITE == 0 {
- backoff.snooze();
+ backoff.spin_heavy();
}
}
}
if !next.is_null() {
return next;
}
- backoff.snooze();
+ backoff.spin_heavy();
}
}
// If we reached the end of the block, wait until the next one is installed.
if offset == BLOCK_CAP {
- backoff.snooze();
+ backoff.spin_heavy();
tail = self.tail.index.load(Ordering::Acquire);
block = self.tail.block.load(Ordering::Acquire);
continue;
return true;
},
Err(_) => {
- backoff.spin();
+ backoff.spin_light();
tail = self.tail.index.load(Ordering::Acquire);
block = self.tail.block.load(Ordering::Acquire);
}
// If we reached the end of the block, wait until the next one is installed.
if offset == BLOCK_CAP {
- backoff.snooze();
+ backoff.spin_heavy();
head = self.head.index.load(Ordering::Acquire);
block = self.head.block.load(Ordering::Acquire);
continue;
// The block can be null here only if the first message is being sent into the channel.
// In that case, just wait until it gets initialized.
if block.is_null() {
- backoff.snooze();
+ backoff.spin_heavy();
head = self.head.index.load(Ordering::Acquire);
block = self.head.block.load(Ordering::Acquire);
continue;
return true;
},
Err(_) => {
- backoff.spin();
+ backoff.spin_light();
head = self.head.index.load(Ordering::Acquire);
block = self.head.block.load(Ordering::Acquire);
}
// New updates to tail will be rejected by MARK_BIT and aborted unless it's
// at boundary. We need to wait for the updates take affect otherwise there
// can be memory leaks.
- backoff.snooze();
+ backoff.spin_heavy();
tail = self.tail.index.load(Ordering::Acquire);
}
use crate::fmt;
use crate::panic::{RefUnwindSafe, UnwindSafe};
use crate::time::{Duration, Instant};
-use error::*;
+pub use error::*;
/// Creates a channel of unbounded capacity.
///
}
const SPIN_LIMIT: u32 = 6;
-const YIELD_LIMIT: u32 = 10;
-/// Performs exponential backoff in spin loops.
+/// Performs quadratic backoff in spin loops.
pub struct Backoff {
step: Cell<u32>,
}
Backoff { step: Cell::new(0) }
}
- /// Backs off in a lock-free loop.
+ /// Backs off using lightweight spinning.
///
- /// This method should be used when we need to retry an operation because another thread made
- /// progress.
+ /// 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`).
#[inline]
- pub fn spin(&self) {
+ pub fn spin_light(&self) {
let step = self.step.get().min(SPIN_LIMIT);
for _ in 0..step.pow(2) {
crate::hint::spin_loop();
}
- if self.step.get() <= SPIN_LIMIT {
- self.step.set(self.step.get() + 1);
- }
+ self.step.set(self.step.get() + 1);
}
- /// Backs off in a blocking loop.
+ /// Backs off using heavyweight spinning.
+ ///
+ /// This method should be used in blocking loops where parking the thread is not an option.
#[inline]
- pub fn snooze(&self) {
+ pub fn spin_heavy(&self) {
if self.step.get() <= SPIN_LIMIT {
for _ in 0..self.step.get().pow(2) {
crate::hint::spin_loop()
crate::thread::yield_now();
}
- if self.step.get() <= YIELD_LIMIT {
- self.step.set(self.step.get() + 1);
- }
+ self.step.set(self.step.get() + 1);
}
- /// Returns `true` if quadratic backoff has completed and blocking the thread is advised.
+ /// Returns `true` if quadratic backoff has completed and parking the thread is advised.
#[inline]
pub fn is_completed(&self) -> bool {
- self.step.get() > YIELD_LIMIT
+ self.step.get() > SPIN_LIMIT
}
}
fn wait_ready(&self) {
let backoff = Backoff::new();
while !self.ready.load(Ordering::Acquire) {
- backoff.snooze();
+ backoff.spin_heavy();
}
}
}
pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> {
self.inner.try_send(t)
}
+
+ // Attempts to send for a value on this receiver, returning an error if the
+ // corresponding channel has hung up, or if it waits more than `timeout`.
+ //
+ // This method is currently private and only used for tests.
+ #[allow(unused)]
+ fn send_timeout(&self, t: T, timeout: Duration) -> Result<(), mpmc::SendTimeoutError<T>> {
+ self.inner.send_timeout(t, timeout)
+ }
}
#[stable(feature = "rust1", since = "1.0.0")]
use super::*;
use crate::env;
+use crate::sync::mpmc::SendTimeoutError;
use crate::thread;
use crate::time::Duration;
assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1));
}
+#[test]
+fn send_timeout() {
+ let (tx, _rx) = sync_channel::<i32>(1);
+ assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Ok(()));
+ assert_eq!(tx.send_timeout(1, Duration::from_millis(1)), Err(SendTimeoutError::Timeout(1)));
+}
+
#[test]
fn smoke_threads() {
let (tx, rx) = sync_channel::<i32>(0);
p: Box<dyn FnOnce()>,
core_id: isize,
) -> io::Result<Thread> {
- let p = Box::into_raw(box p);
+ let p = Box::into_raw(Box::new(p));
let tid = abi::spawn2(
thread_start,
- p as usize,
+ p.expose_addr(),
abi::Priority::into(abi::NORMAL_PRIO),
stack,
core_id,
// The this solution works like the implementation of macOS and
// doesn't additional OS support
-use crate::cell::Cell;
-use crate::ptr;
+use crate::mem;
#[thread_local]
-static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
-
-type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- if DTORS.get().is_null() {
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v));
- }
-
- let list: &mut List = &mut *DTORS.get();
+ let list = &mut DTORS;
list.push((t, dtor));
}
// every thread call this function to run through all possible destructors
pub unsafe fn run_dtors() {
- let mut ptr = DTORS.replace(ptr::null_mut());
- while !ptr.is_null() {
- let list = Box::from_raw(ptr);
- for (ptr, dtor) in list.into_iter() {
+ let mut list = mem::take(&mut DTORS);
+ while !list.is_empty() {
+ for (ptr, dtor) in list {
dtor(ptr);
}
- ptr = DTORS.replace(ptr::null_mut());
+ list = mem::take(&mut DTORS);
}
}
// Terminate and delete the task
// Safety: `self.task` still represents a task we own (because
// this method or `join_inner` is called only once for
- // each `Thread`). The task indicated that it's safe to
+ // each `Thread`). The task indicated that it's safe to
// delete by entering the `FINISHED` state.
unsafe { terminate_and_delete_task(self.task) };
use super::{abi, itron::task};
use crate::cell::Cell;
-use crate::ptr;
+use crate::mem;
#[thread_local]
-static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+static REGISTERED: Cell<bool> = Cell::new(false);
-type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+#[thread_local]
+static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- if DTORS.get().is_null() {
+ if !REGISTERED.get() {
let tid = task::current_task_id_aborting();
- let v: Box<List> = box Vec::new();
- DTORS.set(Box::into_raw(v));
-
// Register `tls_dtor` to make sure the TLS destructors are called
// for tasks created by other means than `std::thread`
unsafe { abi::SOLID_TLS_AddDestructor(tid as i32, tls_dtor) };
+ REGISTERED.set(true);
}
- let list: &mut List = unsafe { &mut *DTORS.get() };
+ let list = unsafe { &mut DTORS };
list.push((t, dtor));
}
pub unsafe fn run_dtors() {
- let ptr = DTORS.get();
- if !ptr.is_null() {
- // Swap the destructor list, call all registered destructors,
- // and repeat this until the list becomes permanently empty.
- while let Some(list) = Some(crate::mem::replace(unsafe { &mut *ptr }, Vec::new()))
- .filter(|list| !list.is_empty())
- {
- for (ptr, dtor) in list.into_iter() {
- unsafe { dtor(ptr) };
- }
+ let mut list = mem::take(unsafe { &mut DTORS });
+ while !list.is_empty() {
+ for (ptr, dtor) in list {
+ unsafe { dtor(ptr) };
}
- // Drop the destructor list
- unsafe { Box::from_raw(DTORS.replace(ptr::null_mut())) };
+ list = mem::take(unsafe { &mut DTORS });
}
}
) -> Option<io::Result<FileAttr>> {
use crate::sync::atomic::{AtomicU8, Ordering};
- // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
- // We store the availability in global to avoid unnecessary syscalls.
- // 0: Unknown
- // 1: Not available
- // 2: Available
- static STATX_STATE: AtomicU8 = AtomicU8::new(0);
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
+ // We check for it on first failure and remember availability to avoid having to
+ // do it again.
+ #[repr(u8)]
+ enum STATX_STATE{ Unknown = 0, Present, Unavailable }
+ static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8);
+
syscall! {
fn statx(
fd: c_int,
) -> c_int
}
- match STATX_STATE.load(Ordering::Relaxed) {
- 0 => {
- // It is a trick to call `statx` with null pointers to check if the syscall
- // is available. According to the manual, it is expected to fail with EFAULT.
- // We do this mainly for performance, since it is nearly hundreds times
- // faster than a normal successful call.
- let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
- .err()
- .and_then(|e| e.raw_os_error());
- // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
- // and returns `EPERM`. Listing all possible errors seems not a good idea.
- // See: https://github.com/rust-lang/rust/issues/65662
- if err != Some(libc::EFAULT) {
- STATX_STATE.store(1, Ordering::Relaxed);
- return None;
- }
- STATX_STATE.store(2, Ordering::Relaxed);
- }
- 1 => return None,
- _ => {}
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
+ return None;
}
let mut buf: libc::statx = mem::zeroed();
if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
- return Some(Err(err));
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
+ return Some(Err(err));
+ }
+
+ // Availability not checked yet.
+ //
+ // First try the cheap way.
+ if err.raw_os_error() == Some(libc::ENOSYS) {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
+
+ // Error other than `ENOSYS` is not a good enough indicator -- it is
+ // known that `EPERM` can be returned as a result of using seccomp to
+ // block the syscall.
+ // Availability is checked by performing a call which expects `EFAULT`
+ // if the syscall is usable.
+ // See: https://github.com/rust-lang/rust/issues/65662
+ // FIXME this can probably just do the call if `EPERM` was received, but
+ // previous iteration of the code checked it for all errors and for now
+ // this is retained.
+ // FIXME what about transient conditions like `ENOMEM`?
+ let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
+ .err()
+ .and_then(|e| e.raw_os_error());
+ if err2 == Some(libc::EFAULT) {
+ STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
+ return Some(Err(err));
+ } else {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
}
// We cannot fill `stat64` exhaustively because of private padding fields.
loop {
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
- // with unlimited or variable NAME_MAX. Many modern platforms guarantee
+ // with unlimited or variable NAME_MAX. Many modern platforms guarantee
// thread safety for readdir() as long an individual DIR* is not accessed
// concurrently, which is sufficient for Rust.
super::os::set_errno(0);
let entry_ptr = readdir64(self.inner.dirp.0);
if entry_ptr.is_null() {
- // We either encountered an error, or reached the end. Either way,
+ // We either encountered an error, or reached the end. Either way,
// the next call to next() should return None.
self.end_of_stream = true;
// - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
// - the writer fd was opened with O_APPEND (EBADF²)
- // and no bytes were written successfully yet. (All these errnos should
+ // and no bytes were written successfully yet. (All these errnos should
// not be returned if something was already written, but they happen in
// the wild, see #91152.)
//
// available on Fuchsia.
//
// It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many
- // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
+ // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
// while I don't know if these implementations are actually correct, I think they will do for
// now at least.
pub fn core_dumped(&self) -> bool {
pub fn into_raw(&self) -> c_int {
// We don't know what someone who calls into_raw() will do with this value, but it should
- // have the conventional Unix representation. Despite the fact that this is not
+ // have the conventional Unix representation. Despite the fact that this is not
// standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
- // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
+ // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
// Unix.)
//
// The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may
// different Unix variant.
//
// The other view would be to say that the caller on Fuchsia ought to know that `into_raw`
- // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
+ // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
// not possible here because we must return a c_int because that's what Unix (including
// SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
// necessarily fit.
//
// It seems to me that the right answer would be to provide std::os::fuchsia with its
// own ExitStatusExt, rather that trying to provide a not very convincing imitation of
- // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
+ // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
// fixing this up that is beyond the scope of my efforts now.
let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255.");
let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8;
}
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
- // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
// true for a platform pretending to be Unix, the tests (our doctests, and also
- // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
match NonZero_c_int::try_from(self.0) {
/* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
/* was zero, couldn't convert */ Err(_) => Ok(()),
libc::SIGWINCH => " (SIGWINCH)",
#[cfg(not(target_os = "haiku"))]
libc::SIGIO => " (SIGIO)",
+ #[cfg(target_os = "haiku")]
+ libc::SIGPOLL => " (SIGPOLL)",
libc::SIGSYS => " (SIGSYS)",
// For information on Linux signals, run `man 7 signal`
#[cfg(all(
t(0x00000, "exit status: 0");
t(0x0ff00, "exit status: 255");
- // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
+ // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
// https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956
// The purpose of this test is to test our string formatting, not our understanding of the wait
- // status magic numbers. So restrict these to Linux.
+ // status magic numbers. So restrict these to Linux.
if cfg!(target_os = "linux") {
t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)");
t(0x0ffff, "continued (WIFCONTINUED)");
}
// Testing "unrecognised wait status" is hard because the wait.h macros typically
- // assume that the value came from wait and isn't mad. With the glibc I have here
+ // assume that the value came from wait and isn't mad. With the glibc I have here
// this works:
if cfg!(all(target_os = "linux", target_env = "gnu")) {
t(0x000ff, "unrecognised wait status: 255 0xff");
}
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
- // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
// true for a platform pretending to be Unix, the tests (our doctests, and also
- // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
match NonZero_c_int::try_from(self.0) {
Ok(failure) => Err(ExitStatusError(failure)),
Err(_) => Ok(()),
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
- let p = Box::into_raw(box p);
+ let p = Box::into_raw(Box::new(p));
let mut native: libc::pthread_t = mem::zeroed();
let mut attr: libc::pthread_attr_t = mem::zeroed();
assert_eq!(libc::pthread_attr_init(&mut attr), 0);
n => {
assert_eq!(n, libc::EINVAL);
// EINVAL means |stack_size| is either too small or not a
- // multiple of the system page size. Because it's definitely
+ // multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = os::page_size();
if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
// Linux doesn't allocate the whole stack right away, and
// the kernel has its own stack-guard mechanism to fault
- // when growing too close to an existing mapping. If we map
+ // when growing too close to an existing mapping. If we map
// our own guard, then the kernel starts enforcing a rather
// large gap above that, rendering much of the possible
- // stack space useless. See #43052.
+ // stack space useless. See #43052.
//
// Instead, we'll just note where we expect rlimit to start
// faulting, so our handler can report "stack overflow", and
None
} else if cfg!(target_os = "freebsd") {
// FreeBSD's stack autogrows, and optionally includes a guard page
- // at the bottom. If we try to remap the bottom of the stack
- // ourselves, FreeBSD's guard page moves upwards. So we'll just use
+ // at the bottom. If we try to remap the bottom of the stack
+ // ourselves, FreeBSD's guard page moves upwards. So we'll just use
// the builtin guard page.
let stackptr = get_stack_start_aligned()?;
let guardaddr = stackptr.addr();
// Technically the number of guard pages is tunable and controlled
// by the security.bsd.stack_guard_page sysctl, but there are
- // few reasons to change it from the default. The default value has
+ // few reasons to change it from the default. The default value has
// been 1 ever since FreeBSD 11.1 and 10.4.
const GUARD_PAGES: usize = 1;
let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
} else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
{
// glibc used to include the guard area within the stack, as noted in the BUGS
- // section of `man pthread_attr_getguardsize`. This has been corrected starting
+ // section of `man pthread_attr_getguardsize`. This has been corrected starting
// with glibc 2.27, and in some distro backports, so the guard is now placed at the
- // end (below) the stack. There's no easy way for us to know which we have at
+ // end (below) the stack. There's no easy way for us to know which we have at
// runtime, so we'll just match any fault in the range right above or below the
// stack base to call that fault a stack overflow.
Some(stackaddr - guardsize..stackaddr + guardsize)
#[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);
}
}
}
// Double quotes are used as a way of introducing literal semicolons
// (since c:\some;dir is a valid Windows path). Double quotes are not
// themselves permitted in path names, so there is no way to escape a
- // double quote. Quoted regions can appear in arbitrary locations, so
+ // double quote. Quoted regions can appear in arbitrary locations, so
//
// c:\foo;c:\som"e;di"r;c:\bar
//
impl Thread {
// unsafe: see thread::Builder::spawn_unchecked for safety requirements
pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
- let p = Box::into_raw(box p);
+ let p = Box::into_raw(Box::new(p));
// FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least
- // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
+ // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
// just that below a certain threshold you can't do anything useful.
// That threshold is application and architecture-specific, however.
let ret = c::CreateThread(
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 {
// 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();
} else {
if !opts.nocapture {
// If we encounter a non-unwinding panic, flush any captured output from the current test,
- // and stop capturing output to ensure that the non-unwinding panic message is visible.
+ // and stop capturing output to ensure that the non-unwinding panic message is visible.
// We also acquire the locks for both output streams to prevent output from other threads
// from interleaving with the panic message or appearing after it.
let builtin_panic_hook = panic::take_hook();
}
} else {
// Found nothing in TERMINFO_DIRS, use the default paths:
- // According to /etc/terminfo/README, after looking at
+ // According to /etc/terminfo/README, after looking at
// ~/.terminfo, ncurses will search /etc/terminfo, then
// /lib/terminfo, and eventually /usr/share/terminfo.
// On Haiku the database can be found at /boot/system/data/terminfo
-This directory contains the source code of the rust project, including:
+This directory contains some source code for the Rust project, including:
-- The test suite
- The bootstrapping build system
-- Various submodules for tools, like cargo, etc.
+- Various submodules for tools, like cargo, tidy, etc.
For more information on how various parts of the compiler work, see the [rustc dev guide].
cmd.arg("-Z").arg("force-unstable-if-unmarked");
}
+ // allow-features is handled from within this rustc wrapper because of
+ // issues with build scripts. Some packages use build scripts to
+ // dynamically detect if certain nightly features are available.
+ // There are different ways this causes problems:
+ //
+ // * rustix runs `rustc` on a small test program to see if the feature is
+ // available (and sets a `cfg` if it is). It does not honor
+ // CARGO_ENCODED_RUSTFLAGS.
+ // * proc-macro2 detects if `rustc -vV` says "nighty" or "dev" and enables
+ // nightly features. It will scan CARGO_ENCODED_RUSTFLAGS for
+ // -Zallow-features. Unfortunately CARGO_ENCODED_RUSTFLAGS is not set
+ // for build-dependencies when --target is used.
+ //
+ // The issues above means we can't just use RUSTFLAGS, and we can't use
+ // `cargo -Zallow-features=…`. Passing it through here ensures that it
+ // always gets set. Unfortunately that also means we need to enable more
+ // features than we really want (like those for proc-macro2), but there
+ // isn't much of a way around it.
+ //
+ // I think it is unfortunate that build scripts are doing this at all,
+ // since changes to nightly features can cause crates to break even if the
+ // user didn't want or care about the use of the nightly features. I think
+ // nightly features should be opt-in only. Unfortunately the dynamic
+ // checks are now too wide spread that we just need to deal with it.
+ //
+ // If you want to try to remove this, I suggest working with the crate
+ // authors to remove the dynamic checking. Another option is to pursue
+ // https://github.com/rust-lang/cargo/issues/11244 and
+ // https://github.com/rust-lang/cargo/issues/4423, which will likely be
+ // very difficult, but could help expose -Zallow-features into build
+ // scripts so they could try to honor them.
+ if let Ok(allow_features) = env::var("RUSTC_ALLOW_FEATURES") {
+ cmd.arg(format!("-Zallow-features={allow_features}"));
+ }
+
if let Ok(flags) = env::var("MAGIC_EXTRA_RUSTFLAGS") {
for flag in flags.split(' ') {
cmd.arg(flag);
// this), as well as #63012 which is the tracking issue for this
// feature on the rustc side.
cargo.arg("-Zbinary-dep-depinfo");
- match mode {
- Mode::ToolBootstrap => {
- // Restrict the allowed features to those passed by rustbuild, so we don't depend on nightly accidentally.
- rustflags.arg("-Zallow-features=binary-dep-depinfo");
- }
- Mode::ToolStd => {
- // Right now this is just compiletest and a few other tools that build on stable.
- // Allow them to use `feature(test)`, but nothing else.
- rustflags.arg("-Zallow-features=binary-dep-depinfo,test,proc_macro_internals,proc_macro_diagnostic,proc_macro_span");
+ let allow_features = match mode {
+ Mode::ToolBootstrap | Mode::ToolStd => {
+ // Restrict the allowed features so we don't depend on nightly
+ // accidentally.
+ //
+ // binary-dep-depinfo is used by rustbuild itself for all
+ // compilations.
+ //
+ // Lots of tools depend on proc_macro2 and proc-macro-error.
+ // Those have build scripts which assume nightly features are
+ // available if the `rustc` version is "nighty" or "dev". See
+ // bin/rustc.rs for why that is a problem. Instead of labeling
+ // those features for each individual tool that needs them,
+ // just blanket allow them here.
+ //
+ // If this is ever removed, be sure to add something else in
+ // its place to keep the restrictions in place (or make a way
+ // to unset RUSTC_BOOTSTRAP).
+ "binary-dep-depinfo,proc_macro_span,proc_macro_span_shrink,proc_macro_diagnostic"
+ .to_string()
}
- Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {}
- }
+ Mode::Std | Mode::Rustc | Mode::Codegen | Mode::ToolRustc => String::new(),
+ };
cargo.arg("-j").arg(self.jobs().to_string());
}
}
- Cargo { command: cargo, rustflags, rustdocflags }
+ Cargo { command: cargo, rustflags, rustdocflags, allow_features }
}
/// Ensure that a given step is built, returning its output. This will
command: Command,
rustflags: Rustflags,
rustdocflags: Rustflags,
+ allow_features: String,
}
impl Cargo {
self.command.current_dir(dir);
self
}
+
+ /// Adds nightly-only features that this invocation is allowed to use.
+ ///
+ /// By default, all nightly features are allowed. Once this is called, it
+ /// will be restricted to the given set.
+ pub fn allow_features(&mut self, features: &str) -> &mut Cargo {
+ if !self.allow_features.is_empty() {
+ self.allow_features.push(',');
+ }
+ self.allow_features.push_str(features);
+ self
+ }
}
impl From<Cargo> for Command {
cargo.command.env("RUSTDOCFLAGS", rustdocflags);
}
+ if !cargo.allow_features.is_empty() {
+ cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
+ }
+
cargo.command
}
}
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)
}
&["rust-analyzer/in-rust-tree".to_owned()],
);
- cargo.rustflag(
- "-Zallow-features=proc_macro_internals,proc_macro_diagnostic,proc_macro_span",
- );
+ cargo.allow_features(crate::tool::RustAnalyzer::ALLOW_FEATURES);
// For ./x.py clippy, don't check those targets because
// linting tests and benchmarks can produce very noisy results
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");
"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(),
}
}
SourceType::InTree,
&["sysroot-abi".to_owned()],
);
+ cargo.allow_features(tool::RustAnalyzer::ALLOW_FEATURES);
let dir = builder.src.join(workspace_path);
// needed by rust-analyzer to find its own text fixtures, cf.
// We need `ToolStd` for the locally-built sysroot because
// compiletest uses unstable features of the `test` crate.
builder.ensure(compile::Std::new(compiler, host));
- let cargo = tool::prepare_tool_cargo(
+ let mut cargo = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolStd,
SourceType::InTree,
&[],
);
+ cargo.allow_features("test");
try_run(builder, &mut cargo.into());
}
is_optional_tool: bool,
source_type: SourceType,
extra_features: Vec<String>,
+ /// Nightly-only features that are allowed (comma-separated list).
+ allow_features: &'static str,
}
impl Step for ToolBuild {
_ => panic!("unexpected Mode for tool build"),
}
- let cargo = prepare_tool_cargo(
+ let mut cargo = prepare_tool_cargo(
builder,
compiler,
self.mode,
self.source_type,
&self.extra_features,
);
+ if !self.allow_features.is_empty() {
+ cargo.allow_features(self.allow_features);
+ }
builder.info(&format!("Building stage{} tool {} ({})", compiler.stage, tool, target));
let mut duplicates = Vec::new();
$name:ident, $path:expr, $tool_name:expr
$(,is_external_tool = $external:expr)*
$(,is_unstable_tool = $unstable:expr)*
+ $(,allow_features = $allow_features:expr)?
;
)+) => {
#[derive(Copy, PartialEq, Eq, Clone)]
SourceType::InTree
},
extra_features: vec![],
+ allow_features: concat!($($allow_features)*),
}).expect("expected to build -- essential tool")
}
}
Tidy, "src/tools/tidy", "tidy";
Linkchecker, "src/tools/linkchecker", "linkchecker";
CargoTest, "src/tools/cargotest", "cargotest";
- Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true;
+ Compiletest, "src/tools/compiletest", "compiletest", is_unstable_tool = true, allow_features = "test";
BuildManifest, "src/tools/build-manifest", "build-manifest";
RemoteTestClient, "src/tools/remote-test-client", "remote-test-client";
RustInstaller, "src/tools/rust-installer", "rust-installer", is_external_tool = true;
is_optional_tool: false,
source_type: SourceType::InTree,
extra_features: Vec::new(),
+ allow_features: "",
})
.expect("expected to build -- essential tool")
}
is_optional_tool: false,
source_type: SourceType::InTree,
extra_features: Vec::new(),
+ allow_features: "",
})
.expect("expected to build -- essential tool")
}
is_optional_tool: false,
source_type: SourceType::Submodule,
extra_features: Vec::new(),
+ allow_features: "",
})
.expect("expected to build -- essential tool");
is_optional_tool: true,
source_type: SourceType::Submodule,
extra_features: Vec::new(),
+ allow_features: "",
});
};
is_optional_tool: false,
source_type: SourceType::InTree,
extra_features: Vec::new(),
+ allow_features: "",
})
.expect("expected to build -- essential tool");
pub target: TargetSelection,
}
+impl RustAnalyzer {
+ pub const ALLOW_FEATURES: &str =
+ "proc_macro_internals,proc_macro_diagnostic,proc_macro_span,proc_macro_span_shrink";
+}
+
impl Step for RustAnalyzer {
type Output = Option<PathBuf>;
const DEFAULT: bool = true;
extra_features: vec!["rust-analyzer/in-rust-tree".to_owned()],
is_optional_tool: false,
source_type: SourceType::InTree,
+ allow_features: RustAnalyzer::ALLOW_FEATURES,
})
}
}
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<'_>) {
extra_features: vec!["proc-macro-srv/sysroot-abi".to_owned()],
is_optional_tool: false,
source_type: SourceType::InTree,
+ allow_features: RustAnalyzer::ALLOW_FEATURES,
})?;
// Copy `rust-analyzer-proc-macro-srv` to `<sysroot>/libexec/`
$tool_name:expr,
stable = $stable:expr
$(,tool_std = $tool_std:literal)?
+ $(,allow_features = $allow_features:expr)?
;)+) => {
$(
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
extra_features: $sel.extra_features,
is_optional_tool: true,
source_type: SourceType::InTree,
+ allow_features: concat!($($allow_features)*),
})
}
}
-FROM ubuntu:22.04
+FROM ubuntu:22.10
ARG DEBIAN_FRONTEND=noninteractive
COPY scripts/android-base-apt-get.sh /scripts/
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
- download_and_make_toolchain android-ndk-r15c-linux-x86_64.zip arm 14
+ download_ndk android-ndk-r25b-linux.zip
RUN dpkg --add-architecture i386 && \
apt-get update && \
ENV TARGETS=arm-linux-androideabi
-ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/arm-14
+ENV RUST_CONFIGURE_ARGS --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/
ENV SCRIPT python3 ../x.py --stage 2 test --host='' --target $TARGETS
-FROM ubuntu:22.04
+FROM ubuntu:22.10
COPY scripts/android-base-apt-get.sh /scripts/
RUN sh /scripts/android-base-apt-get.sh
# ndk
COPY scripts/android-ndk.sh /scripts/
RUN . /scripts/android-ndk.sh && \
- download_ndk android-ndk-r15c-linux-x86_64.zip && \
- make_standalone_toolchain arm 14 && \
- make_standalone_toolchain x86 14 && \
- make_standalone_toolchain arm 21 && \
- make_standalone_toolchain x86 21 && \
- make_standalone_toolchain arm64 21 && \
- make_standalone_toolchain x86_64 21 && \
- remove_ndk
+ download_ndk android-ndk-r25b-linux.zip
# env
ENV TARGETS=arm-linux-androideabi
ENV RUST_CONFIGURE_ARGS \
--enable-extended \
--enable-profiler \
- --arm-linux-androideabi-ndk=/android/ndk/arm-14 \
- --armv7-linux-androideabi-ndk=/android/ndk/arm-14 \
- --thumbv7neon-linux-androideabi-ndk=/android/ndk/arm-14 \
- --i686-linux-android-ndk=/android/ndk/x86-14 \
- --aarch64-linux-android-ndk=/android/ndk/arm64-21 \
- --x86_64-linux-android-ndk=/android/ndk/x86_64-21 \
+ --arm-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --armv7-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --thumbv7neon-linux-androideabi-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --i686-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --aarch64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
+ --x86_64-linux-android-ndk=/android/ndk/toolchains/llvm/prebuilt/linux-x86_64/ \
--disable-docs
ENV SCRIPT python3 ../x.py dist --host='' --target $TARGETS
ENV HOSTS=s390x-unknown-linux-gnu
-ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-profiler --disable-docs
+ENV RUST_CONFIGURE_ARGS --enable-extended --enable-lld --enable-sanitizers --enable-profiler --disable-docs
ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
git clone https://github.com/WebAssembly/wasi-libc
cd wasi-libc
-git reset --hard 8b7148f69ae241a2749b3defe4606da8143b72e0
+git reset --hard 4362b1885fd369e042a7c0ecd8df3b6cd47fb4e8
make -j$(nproc) \
CC="$bin/clang" \
NM="$bin/llvm-nm" \
-FROM ubuntu:18.04
-# FIXME: when bumping the version, remove the Python 3.6-specific changes in
-# the reuse-requirements.in file, regenerate reuse-requirements.txt and remove
-# this comment.
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
-FROM ubuntu:18.04
-# FIXME: when bumping the version, remove the Python 3.6-specific changes in
-# the reuse-requirements.in file, regenerate reuse-requirements.txt and remove
-# this comment.
+FROM ubuntu:22.04
+ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
python3 ../x.py test --stage 0 src/tools/compiletest && \
python3 ../x.py test --stage 0 core alloc std test proc_macro && \
# Build both public and internal documentation.
+ RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library/test && \
/scripts/validate-toolstate.sh && \
/scripts/validate-error-codes.sh && \
#
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
-Subproject commit 2bd5d42c9956369132228da6409f0e68da56c51a
+Subproject commit 2cd1b5593d26dc6a03c20f8619187ad4b2485552
-Subproject commit 8ca261268068d80c0969260fff15199bad87b587
+Subproject commit 960d610e7f33889a2577f5f17c26f0d5c82b30df
-Subproject commit 3ae62681ff236d5528ef7c8c28ba7c6b2ecc6731
+Subproject commit 2cb0ed9ba56360949f492f9866afe8c293f9f9da
-Subproject commit 8888f9428fe9a48f31de6bd2cef9b9bf80791edc
+Subproject commit a9fb7d13eadfcc5f457962731f105b97f9a7474a
-Subproject commit b3e2a6e6c8a3aae5b5d950c63046f23bae07096d
+Subproject commit 7352353ae91c48b136d2ca7d03822e1448165e1e
cargo test --tests
```
+> **Note**: The default for `LLVM_PROFILE_FILE` is `default_%m_%p.profraw`. Versions prior to 1.65 had a default of `default.profraw`, so if using those earlier versions, it is recommended to explicitly set `LLVM_PROFILE_FILE="default_%m_%p.profraw"` to avoid having multiple tests overwrite the `.profraw` files.
+
Make note of the test binary file paths, displayed after the word "`Running`" in the test output:
```text
## `invalid_html_tags`
-This lint is **allowed by default** and is **nightly-only**. It detects unclosed
+This lint **warns by default**. It detects unclosed
or invalid HTML tags. For example:
```rust
+++ /dev/null
-# `abi_efiapi`
-
-The tracking issue for this feature is: [#65815]
-
-[#65815]: https://github.com/rust-lang/rust/issues/65815
-
-------------------------
-
-The `efiapi` calling convention can be used for defining a function with
-an ABI compatible with the UEFI Interfaces as defined in the [UEFI
-Specification].
-
-Example:
-
-```rust,ignore (not-all-targets-support-uefi)
-#![feature(abi_efiapi)]
-
-extern "efiapi" { fn f1(); }
-
-extern "efiapi" fn f2() { todo!() }
-```
-
-[UEFI Specification]: https://uefi.org/specs/UEFI/2.10/
bound_params: Vec::new(),
})
})
- .chain(
- lifetime_to_bounds.into_iter().filter(|&(_, ref bounds)| !bounds.is_empty()).map(
- |(lifetime, bounds)| {
- let mut bounds_vec = bounds.into_iter().collect();
- self.sort_where_bounds(&mut bounds_vec);
- WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec }
- },
- ),
- )
+ .chain(lifetime_to_bounds.into_iter().filter(|(_, bounds)| !bounds.is_empty()).map(
+ |(lifetime, bounds)| {
+ let mut bounds_vec = bounds.into_iter().collect();
+ self.sort_where_bounds(&mut bounds_vec);
+ WherePredicate::RegionPredicate { lifetime, bounds: bounds_vec }
+ },
+ ))
.collect()
}
trait_def_id,
impl_def_id
);
- let trait_ref = cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let trait_ref = cx.tcx.impl_trait_ref(impl_def_id).unwrap();
if !matches!(trait_ref.0.self_ty().kind(), ty::Param(_)) {
continue;
}
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId};
use rustc_hir::Mutability;
use rustc_metadata::creader::{CStore, LoadedMacro};
use rustc_middle::ty::{self, TyCtxt};
res: Res,
name: Symbol,
attrs: Option<&[ast::Attribute]>,
- visited: &mut FxHashSet<DefId>,
+ visited: &mut DefIdSet,
) -> Option<Vec<clean::Item>> {
let did = res.opt_def_id()?;
if did.is_local() {
pub(crate) fn try_inline_glob(
cx: &mut DocContext<'_>,
res: Res,
- visited: &mut FxHashSet<DefId>,
+ current_mod: LocalDefId,
+ visited: &mut DefIdSet,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
) -> Option<Vec<clean::Item>> {
let did = res.opt_def_id()?;
match res {
Res::Def(DefKind::Mod, did) => {
- let mut items = build_module_items(cx, did, visited, inlined_names);
+ // Use the set of module reexports to filter away names that are not actually
+ // reexported by the glob, e.g. because they are shadowed by something else.
+ let reexports = cx
+ .tcx
+ .module_reexports(current_mod)
+ .unwrap_or_default()
+ .iter()
+ .filter_map(|child| child.res.opt_def_id())
+ .collect();
+ let mut items = build_module_items(cx, did, visited, inlined_names, Some(&reexports));
items.drain_filter(|item| {
if let Some(name) = item.name {
// If an item with the same type and name already exists,
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_impl");
let tcx = cx.tcx;
- let associated_trait = tcx.impl_trait_ref(did);
+ let associated_trait = tcx.impl_trait_ref(did).map(ty::EarlyBinder::skip_binder);
// Only inline impl if the implemented trait is
// reachable in rustdoc generated documentation
));
}
-fn build_module(
- cx: &mut DocContext<'_>,
- did: DefId,
- visited: &mut FxHashSet<DefId>,
-) -> clean::Module {
- let items = build_module_items(cx, did, visited, &mut FxHashSet::default());
+fn build_module(cx: &mut DocContext<'_>, did: DefId, visited: &mut DefIdSet) -> clean::Module {
+ let items = build_module_items(cx, did, visited, &mut FxHashSet::default(), None);
let span = clean::Span::new(cx.tcx.def_span(did));
clean::Module { items, span }
fn build_module_items(
cx: &mut DocContext<'_>,
did: DefId,
- visited: &mut FxHashSet<DefId>,
+ visited: &mut DefIdSet,
inlined_names: &mut FxHashSet<(ItemType, Symbol)>,
+ allowed_def_ids: Option<&DefIdSet>,
) -> Vec<clean::Item> {
let mut items = Vec::new();
for &item in cx.tcx.module_children(did).iter() {
if item.vis.is_public() {
let res = item.res.expect_non_local();
+ if let Some(def_id) = res.opt_def_id()
+ && let Some(allowed_def_ids) = allowed_def_ids
+ && !allowed_def_ids.contains(&def_id) {
+ continue;
+ }
if let Some(def_id) = res.mod_def_id() {
// If we're inlining a glob import, it's possible to have
// two distinct modules with the same name. We don't want to
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, IndexEntry};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
use rustc_hir::PredicateOrigin;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_infer::infer::region_constraints::{Constraint, RegionConstraintData};
Some(def.def_id),
)),
default: match has_default {
- true => Some(Box::new(cx.tcx.const_param_default(def.def_id).to_string())),
+ true => Some(Box::new(
+ cx.tcx.const_param_default(def.def_id).subst_identity().to_string(),
+ )),
false => None,
},
},
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();
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(
// Look for equality predicates on associated types that can be merged into
// general bound predicates.
- equalities.retain(|&(ref lhs, ref rhs, ref bound_params)| {
+ equalities.retain(|(lhs, rhs, bound_params)| {
let Some((ty, trait_did, name)) = lhs.projection() else { return true; };
let Some((bounds, _)) = tybounds.get_mut(ty) else { return true };
let bound_params = bound_params
}
let header = match *self.kind {
ItemKind::ForeignFunctionItem(_) => {
- let abi = tcx.fn_sig(self.item_id.as_def_id().unwrap()).abi();
+ let def_id = self.item_id.as_def_id().unwrap();
+ let abi = tcx.fn_sig(def_id).abi();
hir::FnHeader {
unsafety: if abi == Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx, self.item_id.as_def_id().unwrap())
hir::Unsafety::Unsafe
},
abi,
- constness: hir::Constness::NotConst,
+ constness: if abi == Abi::RustIntrinsic
+ && tcx.is_const_fn(def_id)
+ && is_unstable_const_fn(tcx, def_id).is_none()
+ {
+ hir::Constness::Const
+ } else {
+ hir::Constness::NotConst
+ },
asyncness: hir::IsAsync::NotAsync,
}
}
}
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))
}
}
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) 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,
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,
) {
let ast_attrs = self.tcx.hir().attrs(hir_id);
if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
- if !cfg.matches(&self.sess.parse_sess, Some(self.sess.features_untracked())) {
+ if !cfg.matches(&self.sess.parse_sess, Some(self.tcx.features())) {
return;
}
}
use std::mem;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::{CrateNum, DefId};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::Symbol;
///
/// The values of the map are a list of implementations and documentation
/// found on that implementation.
- pub(crate) impls: FxHashMap<DefId, Vec<Impl>>,
+ pub(crate) impls: DefIdMap<Vec<Impl>>,
/// Maintains a mapping of local crate `DefId`s to the fully qualified name
/// and "short type description" of that node. This is used when generating
/// to the path used if the corresponding type is inlined. By
/// doing this, we can detect duplicate impls on a trait page, and only display
/// the impl for the inlined type.
- pub(crate) exact_paths: FxHashMap<DefId, Vec<Symbol>>,
+ pub(crate) exact_paths: DefIdMap<Vec<Symbol>>,
/// This map contains information about all known traits of this crate.
/// Implementations of a crate should inherit the documentation of the
struct CacheBuilder<'a, 'tcx> {
cache: &'a mut Cache,
/// This field is used to prevent duplicated impl blocks.
- impl_ids: FxHashMap<DefId, FxHashSet<DefId>>,
+ impl_ids: DefIdMap<DefIdSet>,
tcx: TyCtxt<'tcx>,
}
let (krate, mut impl_ids) = {
let mut cache_builder =
- CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: FxHashMap::default() };
+ CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() };
krate = cache_builder.fold_crate(krate);
(krate, cache_builder.impl_ids)
};
}
// Index this method for searching later on.
- if let Some(ref s) = item.name.or_else(|| {
+ if let Some(s) = item.name.or_else(|| {
if item.is_stripped() {
None
} else if let clean::ImportItem(ref i) = *item.kind &&
// for where the type was defined. On the other
// hand, `paths` always has the right
// information if present.
- Some(&(ref fqp, _)) => Some(&fqp[..fqp.len() - 1]),
+ Some((fqp, _)) => Some(&fqp[..fqp.len() - 1]),
None => None,
};
((did, path), true)
short_markdown_summary(x.as_str(), &item.link_names(self.cache))
});
let ty = item.type_();
- let name = s.to_string();
- if ty != ItemType::StructField || u16::from_str_radix(&name, 10).is_err() {
+ if ty != ItemType::StructField
+ || u16::from_str_radix(s.as_str(), 10).is_err()
+ {
// In case this is a field from a tuple struct, we don't add it into
// the search index because its name is something like "0", which is
// not useful for rustdoc search.
self.cache.search_index.push(IndexItem {
ty,
- name,
+ name: s,
path: join_with_double_colon(path),
desc,
parent,
root_path: Option<&str>,
) -> Result<(String, ItemType, Vec<Symbol>), HrefError> {
let tcx = cx.shared.tcx;
- let crate_name = tcx.crate_name(def_id.krate).to_string();
+ let crate_name = tcx.crate_name(def_id.krate);
let cache = cx.cache();
let fqp: Vec<Symbol> = tcx
}
})
.collect();
- let mut relative = fqp.iter().map(|elem| elem.to_string());
+ let mut relative = fqp.iter().copied();
let cstore = CStore::from_tcx(tcx);
// We need this to prevent a `panic` when this function is used from intra doc links...
if !cstore.has_crate_data(def_id.krate) {
};
let mut path = if is_macro_2 {
- once(crate_name.clone()).chain(relative).collect()
+ once(crate_name).chain(relative).collect()
} else {
- vec![crate_name.clone(), relative.next_back().unwrap()]
+ vec![crate_name, relative.next_back().unwrap()]
};
if path.len() < 2 {
// The minimum we can have is the crate name followed by the macro name. If shorter, then
}
if let Some(last) = path.last_mut() {
- *last = format!("macro.{}.html", last);
+ *last = Symbol::intern(&format!("macro.{}.html", last.as_str()));
}
let url = match cache.extern_locations[&def_id.krate] {
ExternalLocation::Remote(ref s) => {
// `ExternalLocation::Remote` always end with a `/`.
- format!("{}{}", s, path.join("/"))
+ format!("{}{}", s, path.iter().map(|p| p.as_str()).join("/"))
}
ExternalLocation::Local => {
// `root_path` always end with a `/`.
- format!("{}{}/{}", root_path.unwrap_or(""), crate_name, path.join("/"))
+ format!(
+ "{}{}/{}",
+ root_path.unwrap_or(""),
+ crate_name,
+ path.iter().map(|p| p.as_str()).join("/")
+ )
}
ExternalLocation::Unknown => {
debug!("crate {} not in cache when linkifying macros", crate_name);
clean::Tuple(ref typs) => {
match &typs[..] {
&[] => primitive_link(f, PrimitiveType::Unit, "()", cx),
- &[ref one] => {
+ [one] => {
if let clean::Generic(name) = one {
primitive_link(f, PrimitiveType::Tuple, &format!("({name},)"), cx)
} else {
_ => String::new(),
};
let m = mutability.print_with_space();
- let amp = if f.alternate() { "&".to_string() } else { "&".to_string() };
+ let amp = if f.alternate() { "&" } else { "&" };
match **ty {
clean::DynTrait(ref bounds, ref trait_lt)
if bounds.len() > 1 || trait_lt.is_some() =>
fmt_type(ty, f, use_absolute, cx)?;
write!(f, ")")
}
- clean::Generic(..) => {
- primitive_link(
- f,
- PrimitiveType::Reference,
- &format!("{}{}{}", amp, lt, m),
- cx,
- )?;
- fmt_type(ty, f, use_absolute, cx)
+ clean::Generic(name) => {
+ primitive_link(f, PrimitiveType::Reference, &format!("{amp}{lt}{m}{name}"), cx)
}
_ => {
write!(f, "{}{}{}", amp, lt, m)?;
use rustc_hir::HirId;
use rustc_middle::ty::TyCtxt;
use rustc_span::edition::Edition;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
use once_cell::sync::Lazy;
use std::borrow::Cow;
#[derive(Clone, Debug)]
pub struct Playground {
- pub crate_name: Option<String>,
+ pub crate_name: Option<Symbol>,
pub url: String,
}
.map(|l| map_line(l).for_code())
.intersperse("\n".into())
.collect::<String>();
- let krate = krate.as_ref().map(|s| &**s);
+ let krate = krate.as_ref().map(|s| s.as_str());
let (test, _, _) =
doctest::make_test(&test, krate, false, &Default::default(), edition, None);
let channel = if test.contains("#![feature(") { "&version=nightly" } else { "" };
use std::sync::mpsc::{channel, Receiver};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_span::edition::Edition;
pub(super) render_redirect_pages: bool,
/// Tracks section IDs for `Deref` targets so they match in both the main
/// body and the sidebar.
- pub(super) deref_id_map: FxHashMap<DefId, String>,
+ pub(super) deref_id_map: DefIdMap<String>,
/// The map used to ensure all generated 'id=' attributes are unique.
pub(super) id_map: IdMap,
/// Shared mutable state.
// If user passed in `--playground-url` arg, we fill in crate name here
let mut playground = None;
if let Some(url) = playground_url {
- playground =
- Some(markdown::Playground { crate_name: Some(krate.name(tcx).to_string()), url });
+ playground = Some(markdown::Playground { crate_name: Some(krate.name(tcx)), url });
}
let mut layout = layout::Layout {
logo: String::new(),
}
(sym::html_playground_url, Some(s)) => {
playground = Some(markdown::Playground {
- crate_name: Some(krate.name(tcx).to_string()),
+ crate_name: Some(krate.name(tcx)),
url: s.to_string(),
});
}
dst,
render_redirect_pages: false,
id_map,
- deref_id_map: FxHashMap::default(),
+ deref_id_map: Default::default(),
shared: Rc::new(scx),
include_sources,
types_with_notable_traits: FxHashSet::default(),
current: self.current.clone(),
dst: self.dst.clone(),
render_redirect_pages: self.render_redirect_pages,
- deref_id_map: FxHashMap::default(),
+ deref_id_map: Default::default(),
id_map: IdMap::new(),
shared: Rc::clone(&self.shared),
include_sources: self.include_sources,
write!(
buf,
"<div class=\"main-heading\">\
- <h1 class=\"fqn\">Rustdoc settings</h1>\
+ <h1>Rustdoc settings</h1>\
<span class=\"out-of-band\">\
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
Back\
write!(
buf,
"<div class=\"main-heading\">\
- <h1 class=\"fqn\">Rustdoc help</h1>\
+ <h1>Rustdoc help</h1>\
<span class=\"out-of-band\">\
<a id=\"back\" href=\"javascript:void(0)\" onclick=\"history.back();\">\
Back\
use rustc_attr::{ConstStability, Deprecation, StabilityLevel};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::CtorKind;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, DefIdSet};
use rustc_hir::Mutability;
use rustc_middle::middle::stability;
use rustc_middle::ty;
#[derive(Debug)]
pub(crate) struct IndexItem {
pub(crate) ty: ItemType,
- pub(crate) name: String,
+ pub(crate) name: Symbol,
pub(crate) path: String,
pub(crate) desc: String,
pub(crate) parent: Option<DefId>,
}
}
- f.write_str("<h1 class=\"fqn\">List of all items</h1>");
+ f.write_str("<h1>List of all items</h1>");
// Note: print_entries does not escape the title, because we know the current set of titles
// doesn't require escaping.
print_entries(f, &self.structs, ItemSection::Structs);
let mut ids = IdMap::default();
format!(
"<div class=\"main-heading\">\
- <h1 class=\"fqn\">About scraped examples</h1>\
+ <h1>About scraped examples</h1>\
</div>\
<div>{}</div>",
Markdown {
debug!("Doc block: =====\n{}\n=====", s);
if is_collapsible {
w.write_str(
- "<details class=\"rustdoc-toggle top-doc\" open>\
+ "<details class=\"toggle top-doc\" open>\
<summary class=\"hideme\">\
<span>Expand description</span>\
</summary>",
it: DefId,
what: AssocItemRender<'_>,
) {
- let mut derefs = FxHashSet::default();
+ let mut derefs = DefIdSet::default();
derefs.insert(it);
render_assoc_items_inner(w, cx, containing_item, it, what, &mut derefs)
}
containing_item: &clean::Item,
it: DefId,
what: AssocItemRender<'_>,
- derefs: &mut FxHashSet<DefId>,
+ derefs: &mut DefIdSet,
) {
info!("Documenting associated items of {:?}", containing_item.name);
let shared = Rc::clone(&cx.shared);
impl_: &Impl,
container_item: &clean::Item,
deref_mut: bool,
- derefs: &mut FxHashSet<DefId>,
+ derefs: &mut DefIdSet,
) {
let cache = cx.cache();
let deref_type = impl_.inner_impl().trait_.as_ref().unwrap();
write!(
&mut out,
"<h3>Notable traits for <code>{}</code></h3>\
- <pre class=\"content\"><code>",
+ <pre><code>",
impl_.for_.print(cx)
);
}
let toggled = !doc_buffer.is_empty();
if toggled {
let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
- write!(w, "<details class=\"rustdoc-toggle{}\" open><summary>", method_toggle_class);
+ write!(w, "<details class=\"toggle{}\" open><summary>", method_toggle_class);
}
match &*item.kind {
clean::MethodItem(..) | clean::TyMethodItem(_) => {
close_tags.insert_str(0, "</details>");
write!(
w,
- "<details class=\"rustdoc-toggle implementors-toggle\"{}>",
+ "<details class=\"toggle implementors-toggle\"{}>",
if rendering_params.toggle_open_by_default { " open" } else { "" }
);
write!(w, "<summary>")
if let Some(impl_) =
v.iter().find(|i| i.trait_did() == cx.tcx().lang_items().deref_trait())
{
- let mut derefs = FxHashSet::default();
+ let mut derefs = DefIdSet::default();
derefs.insert(did);
sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
}
out: &mut Buffer,
impl_: &Impl,
v: &[Impl],
- derefs: &mut FxHashSet<DefId>,
+ derefs: &mut DefIdSet,
used_links: &mut FxHashSet<String>,
) {
let c = cx.cache();
let mut work = VecDeque::new();
let mut process_path = |did: DefId| {
- let get_extern = || cache.external_paths.get(&did).map(|s| s.0.clone());
- let fqp = cache.exact_paths.get(&did).cloned().or_else(get_extern);
+ let get_extern = || cache.external_paths.get(&did).map(|s| &s.0);
+ let fqp = cache.exact_paths.get(&did).or_else(get_extern);
if let Some(path) = fqp {
out.push(join_with_double_colon(&path));
// Look for the example file in the source map if it exists, otherwise return a dummy span
let file_span = (|| {
let source_map = tcx.sess.source_map();
- let crate_src = tcx.sess.local_crate_source_file.as_ref()?;
+ let crate_src = tcx.sess.local_crate_source_file()?;
let abs_crate_src = crate_src.canonicalize().ok()?;
let crate_root = abs_crate_src.parent()?.parent()?;
let rel_path = path.strip_prefix(crate_root).ok()?;
if it.peek().is_some() {
write!(
w,
- "<details class=\"rustdoc-toggle more-examples-toggle\">\
+ "<details class=\"toggle more-examples-toggle\">\
<summary class=\"hideme\">\
<span>More examples</span>\
</summary>\
fn toggle_open(w: &mut Buffer, text: impl fmt::Display) {
write!(
w,
- "<details class=\"rustdoc-toggle type-contents-toggle\">\
+ "<details class=\"toggle type-contents-toggle\">\
<summary class=\"hideme\">\
<span>Show {}</span>\
</summary>",
f.decl.output.as_return().and_then(|output| notable_traits_button(output, cx));
wrap_into_item_decl(w, |w| {
- wrap_item(w, "fn", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
w.reserve(header_len);
write!(
// Output the trait definition
wrap_into_item_decl(w, |w| {
- wrap_item(w, "trait", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
let toggled = !content.is_empty();
if toggled {
let method_toggle_class = if item_type.is_method() { " method-toggle" } else { "" };
- write!(w, "<details class=\"rustdoc-toggle{method_toggle_class}\" open><summary>");
+ write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
}
write!(w, "<section id=\"{}\" class=\"method has-srclink\">", id);
render_rightside(w, cx, m, t, RenderMode::Normal);
.chain(std::iter::once("implementors"))
.collect();
if let Some(did) = it.item_id.as_def_id() &&
- let get_extern = { || cache.external_paths.get(&did).map(|s| s.0.clone()) } &&
- let Some(fqp) = cache.exact_paths.get(&did).cloned().or_else(get_extern) {
+ let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } &&
+ let Some(fqp) = cache.exact_paths.get(&did).or_else(get_extern) {
js_src_path.extend(fqp[..fqp.len() - 1].iter().copied());
js_src_path.push_fmt(format_args!("{}.{}.js", it.type_(), fqp.last().unwrap()));
} else {
fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "trait-alias", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "opaque", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) {
fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) {
- wrap_item(w, "typedef", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(w, "{}", visibility_print_with_space(it.visibility(cx.tcx()), it.item_id, cx));
write!(
fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "union", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
render_union(w, it, Some(&s.generics), &s.fields, "", cx);
});
let tcx = cx.tcx();
let count_variants = e.variants().count();
wrap_into_item_decl(w, |w| {
- wrap_item(w, "enum", |w| {
+ wrap_item(w, |w| {
render_attributes_in_pre(w, it, "");
write!(
w,
let name = it.name.expect("proc-macros always have names");
match m.kind {
MacroKind::Bang => {
- wrap_item(w, "macro", |w| {
+ wrap_item(w, |w| {
write!(w, "{}!() {{ /* proc-macro */ }}", name);
});
}
MacroKind::Attr => {
- wrap_item(w, "attr", |w| {
+ wrap_item(w, |w| {
write!(w, "#[{}]", name);
});
}
MacroKind::Derive => {
- wrap_item(w, "derive", |w| {
+ wrap_item(w, |w| {
write!(w, "#[derive({})]", name);
if !m.helpers.is_empty() {
w.push_str("\n{\n");
fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "const", |w| {
+ wrap_item(w, |w| {
let tcx = cx.tcx();
render_attributes_in_code(w, it);
fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "struct", |w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it);
render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx);
});
fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "static", |w| {
+ wrap_item(w, |w| {
render_attributes_in_code(w, it);
write!(
w,
fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
wrap_into_item_decl(w, |w| {
- wrap_item(w, "foreigntype", |w| {
+ wrap_item(w, |w| {
w.write_str("extern {\n");
render_attributes_in_code(w, it);
write!(
w.write_str("</div>")
}
-fn wrap_item<F>(w: &mut Buffer, item_name: &str, f: F)
+fn wrap_item<F>(w: &mut Buffer, f: F)
where
F: FnOnce(&mut Buffer),
{
- w.write_fmt(format_args!("<pre class=\"rust {}\"><code>", item_name));
+ w.write_str(r#"<pre class="rust"><code>"#);
f(w);
w.write_str("</code></pre>");
}
if item.is_non_exhaustive() {
write!(
w,
- "<details class=\"rustdoc-toggle non-exhaustive\">\
+ "<details class=\"toggle non-exhaustive\">\
<summary class=\"hideme\"><span>{}</span></summary>\
<div class=\"docblock\">",
{
// Attach all orphan items to the type's definition if the type
// has since been learned.
for &OrphanImplItem { parent, ref item, ref impl_generics } in &cache.orphan_impl_items {
- if let Some(&(ref fqp, _)) = cache.paths.get(&parent) {
+ if let Some((fqp, _)) = cache.paths.get(&parent) {
let desc = item
.doc_value()
.map_or_else(String::new, |s| short_markdown_summary(&s, &item.link_names(cache)));
cache.search_index.push(IndexItem {
ty: item.type_(),
- name: item.name.unwrap().to_string(),
+ name: item.name.unwrap(),
path: join_with_double_colon(&fqp[..fqp.len() - 1]),
desc,
parent: Some(parent),
// Sort search index items. This improves the compressibility of the search index.
cache.search_index.sort_unstable_by(|k1, k2| {
// `sort_unstable_by_key` produces lifetime errors
- let k1 = (&k1.path, &k1.name, &k1.ty, &k1.parent);
- let k2 = (&k2.path, &k2.name, &k2.ty, &k2.parent);
+ let k1 = (&k1.path, k1.name.as_str(), &k1.ty, &k1.parent);
+ let k2 = (&k2.path, k2.name.as_str(), &k2.ty, &k2.parent);
std::cmp::Ord::cmp(&k1, &k2)
});
)?;
crate_data.serialize_field(
"n",
- &self.items.iter().map(|item| &item.name).collect::<Vec<_>>(),
+ &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
)?;
crate_data.serialize_field(
"q",
)?;
crate_data.serialize_field(
"p",
- &self.paths.iter().map(|(it, s)| (it, s.to_string())).collect::<Vec<_>>(),
+ &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
)?;
if has_aliases {
crate_data.serialize_field("a", &self.aliases)?;
let decl = &func.decl;
let combined_generics;
- let (self_, generics) = if let Some(&(ref impl_self, ref impl_generics)) = impl_generics {
+ let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_generics {
match (impl_generics.is_empty(), func.generics.is_empty()) {
(true, _) => (Some(impl_self), &func.generics),
(_, true) => (Some(impl_self), impl_generics),
Ok((ret, krates))
}
- /// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
+ /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
/// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
///
/// This forms the payload of files that look like this:
};
let content = format!(
- "<h1 class=\"fqn\">List of all crates</h1><ul class=\"all-items\">{}</ul>",
+ "<h1>List of all crates</h1><ul class=\"all-items\">{}</ul>",
krates
.iter()
.map(|s| {
.docblock > h6:first-child {
margin-top: 0;
}
-h1.fqn {
+.main-heading h1 {
margin: 0;
padding: 0;
flex-grow: 1;
margin-right: auto;
}
-details:not(.rustdoc-toggle) summary {
+details:not(.toggle) summary {
margin-bottom: .6em;
}
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,
content: "\00a0";
}
-.notable .docblock {
+.notable .content {
margin: 0.25em 0.5em;
}
-.notable .docblock pre, .notable .docblock code {
+.notable .content pre, .notable .content code {
background: transparent;
margin: 0;
padding: 0;
white-space: pre-wrap;
}
+.notable .content > h3:first-child {
+ margin: 0 0 5px 0;
+}
+
.search-failed {
text-align: center;
margin-top: 20px;
Unfortunately we can't yet specify contain: content or contain: strict
because the [-]/[+] toggles extend past the boundaries of the <details>
https://developer.mozilla.org/en-US/docs/Web/CSS/contain */
-details.rustdoc-toggle {
+details.toggle {
contain: layout;
position: relative;
}
/* The hideme class is used on summary tags that contain a span with
placeholder text shown only when the toggle is closed. For instance,
"Expand description" or "Show methods". */
-details.rustdoc-toggle > summary.hideme {
+details.toggle > summary.hideme {
cursor: pointer;
font-size: 1rem;
}
-details.rustdoc-toggle > summary {
+details.toggle > summary {
list-style: none;
/* focus outline is shown on `::before` instead of this */
outline: none;
}
-details.rustdoc-toggle > summary::-webkit-details-marker,
-details.rustdoc-toggle > summary::marker {
+details.toggle > summary::-webkit-details-marker,
+details.toggle > summary::marker {
display: none;
}
-details.rustdoc-toggle > summary.hideme > span {
+details.toggle > summary.hideme > span {
margin-left: 9px;
}
-details.rustdoc-toggle > summary::before {
+details.toggle > summary::before {
background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left;
content: "";
cursor: pointer;
filter: var(--toggle-filter);
}
-details.rustdoc-toggle > summary.hideme > span,
+details.toggle > summary.hideme > span,
.more-examples-toggle summary, .more-examples-toggle .hide-more {
color: var(--toggles-color);
}
/* Screen readers see the text version at the end the line.
Visual readers see the icon at the start of the line, but small and transparent. */
-details.rustdoc-toggle > summary::after {
+details.toggle > summary::after {
content: "Expand";
overflow: hidden;
width: 0;
position: absolute;
}
-details.rustdoc-toggle > summary.hideme::after {
+details.toggle > summary.hideme::after {
/* "hideme" toggles already have a description when they're contracted */
content: "";
}
-details.rustdoc-toggle > summary:focus::before,
-details.rustdoc-toggle > summary:hover::before {
+details.toggle > summary:focus::before,
+details.toggle > summary:hover::before {
opacity: 1;
}
-details.rustdoc-toggle > summary:focus-visible::before {
+details.toggle > summary:focus-visible::before {
/* The SVG is black, and gets turned white using a filter in the dark themes.
Do the same with the outline.
The dotted 1px style is copied from Firefox's focus ring style.
margin-bottom: 8px;
}
-details.rustdoc-toggle > summary.hideme::before {
+details.toggle > summary.hideme::before {
position: relative;
}
-details.rustdoc-toggle > summary:not(.hideme)::before {
+details.toggle > summary:not(.hideme)::before {
position: absolute;
left: -24px;
top: 4px;
}
-.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before {
+.impl-items > details.toggle > summary:not(.hideme)::before {
position: absolute;
left: -24px;
}
affect the layout of the items to its right. To do that, we use
absolute positioning. Note that we also set position: relative
on the parent <details> to make this work properly. */
-details.rustdoc-toggle[open] > summary.hideme {
+details.toggle[open] > summary.hideme {
position: absolute;
}
-details.rustdoc-toggle[open] > summary.hideme > span {
+details.toggle[open] > summary.hideme > span {
display: none;
}
-details.rustdoc-toggle[open] > summary::before {
+details.toggle[open] > summary::before {
background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
}
-details.rustdoc-toggle[open] > summary::after {
+details.toggle[open] > summary::after {
content: "Collapse";
}
display: block;
}
- #main-content > details.rustdoc-toggle > summary::before,
- #main-content > div > details.rustdoc-toggle > summary::before {
+ #main-content > details.toggle > summary::before,
+ #main-content > div > details.toggle > summary::before {
left: -11px;
}
}
/* Position of the "[-]" element. */
- details.rustdoc-toggle:not(.top-doc) > summary {
+ details.toggle:not(.top-doc) > summary {
margin-left: 10px;
}
- .impl-items > details.rustdoc-toggle > summary:not(.hideme)::before,
- #main-content > details.rustdoc-toggle:not(.top-doc) > summary::before,
- #main-content > div > details.rustdoc-toggle > summary::before {
+ .impl-items > details.toggle > summary:not(.hideme)::before,
+ #main-content > details.toggle:not(.top-doc) > summary::before,
+ #main-content > div > details.toggle > summary::before {
left: -11px;
}
@media print {
nav.sidebar, nav.sub, .out-of-band, a.srclink, #copy-path,
- details.rustdoc-toggle[open] > summary::before, details.rustdoc-toggle > summary::before,
- details.rustdoc-toggle.top-doc > summary {
+ details.toggle[open] > summary::before, details.toggle > summary::before,
+ details.toggle.top-doc > summary {
display: none;
}
.impl,
#implementors-list > .docblock,
.impl-items > section,
-.impl-items > .rustdoc-toggle > summary,
+.impl-items > .toggle > summary,
.methods > section,
-.methods > .rustdoc-toggle > summary
+.methods > .toggle > summary
{
margin-bottom: 0.75em;
}
.variants > .docblock,
.implementors-toggle > .docblock,
-.impl-items > .rustdoc-toggle[open]:not(:last-child),
-.methods > .rustdoc-toggle[open]:not(:last-child),
+.impl-items > .toggle[open]:not(:last-child),
+.methods > .toggle[open]:not(:last-child),
.implementors-toggle[open]:not(:last-child) {
margin-bottom: 2em;
}
-#trait-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
-#synthetic-implementations-list .impl-items > .rustdoc-toggle:not(:last-child),
-#blanket-implementations-list .impl-items > .rustdoc-toggle:not(:last-child) {
+#trait-implementations-list .impl-items > .toggle:not(:last-child),
+#synthetic-implementations-list .impl-items > .toggle:not(:last-child),
+#blanket-implementations-list .impl-items > .toggle:not(:last-child) {
margin-bottom: 1em;
}
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 .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 .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 .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;
}
-.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 .toggle input:checked {
+.setting-check input:checked {
background-color: var(--settings-input-color);
}
-.setting-line .radio-line input:focus,
-.setting-line .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 .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);
}
let currentNbImpls = implementors.getElementsByClassName("impl").length;
- const traitName = document.querySelector("h1.fqn > .trait").textContent;
+ const traitName = document.querySelector(".main-heading h1 > .trait").textContent;
const baseIdName = "impl-" + traitName + "-";
const libs = Object.getOwnPropertyNames(imp);
// We don't want to include impls from this JS file, when the HTML already has them.
function expandAllDocs() {
const innerToggle = document.getElementById(toggleAllDocsId);
removeClass(innerToggle, "will-expand");
- onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
+ onEachLazy(document.getElementsByClassName("toggle"), e => {
if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) {
e.open = true;
}
function collapseAllDocs() {
const innerToggle = document.getElementById(toggleAllDocsId);
addClass(innerToggle, "will-expand");
- onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
+ onEachLazy(document.getElementsByClassName("toggle"), e => {
if (e.parentNode.id !== "implementations-list" ||
(!hasClass(e, "implementors-toggle") &&
!hasClass(e, "type-contents-toggle"))
setImplementorsTogglesOpen("blanket-implementations-list", false);
}
- onEachLazy(document.getElementsByClassName("rustdoc-toggle"), e => {
+ onEachLazy(document.getElementsByClassName("toggle"), e => {
if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) {
e.open = true;
}
}
});
- 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
});
});
- onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => {
+ onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => {
el.addEventListener("click", e => {
if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") {
e.preventDefault();
window.hideAllModals(false);
const ty = e.getAttribute("data-ty");
const wrapper = document.createElement("div");
- wrapper.innerHTML = "<div class=\"docblock\">" + window.NOTABLE_TRAITS[ty] + "</div>";
+ wrapper.innerHTML = "<div class=\"content\">" + window.NOTABLE_TRAITS[ty] + "</div>";
wrapper.className = "notable popover";
const focusCatcher = document.createElement("div");
focusCatcher.setAttribute("tabindex", "0");
return;
}
if (!this.NOTABLE_FORCE_VISIBLE &&
- !elemIsInParent(event.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
+ !elemIsInParent(ev.relatedTarget, window.CURRENT_NOTABLE_ELEMENT)) {
hideNotable(true);
}
};
* Show the help popup menu.
*/
function showHelp() {
+ // Prevent `blur` events from being dispatched as a result of closing
+ // other modals.
+ getHelpButton().querySelector("a").focus();
const menu = getHelpMenu(true);
if (menu.style.display === "none") {
window.hideAllModals();
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 toggle.
+ // This is a checkbox toggle.
const checked = setting["default"] === true ? " checked" : "";
output += `\
-<label class="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;
}
<div class="main-heading"> {#- -#}
- <h1 class="fqn"> {#- -#}
+ <h1> {#- -#}
{{-typ-}}
{#- The breadcrumbs of the item path, like std::string -#}
{%- for component in path_components -%}
-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> {
let crate_version = options.crate_version.clone();
let output_format = options.output_format;
- let externs = options.externs.clone();
let scrape_examples_options = options.scrape_examples_options.clone();
let bin_crate = options.bin_crate;
let resolver_caches = resolver.borrow_mut().access(|resolver| {
collect_intra_doc_links::early_resolve_intra_doc_links(
resolver,
- sess,
krate,
- externs,
render_options.document_private,
)
});
tcx.find_map_relevant_impl(trait_, ty, |impl_| {
let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
// Check if these are the same type.
- let impl_type = trait_ref.self_ty();
+ let impl_type = trait_ref.skip_binder().self_ty();
trace!(
"comparing type {} with kind {:?} against type {:?}",
impl_type,
use rustc_hir::TraitCandidate;
use rustc_middle::ty::{DefIdTree, Visibility};
use rustc_resolve::{ParentScope, Resolver};
-use rustc_session::config::Externs;
-use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::{Symbol, SyntaxContext};
pub(crate) fn early_resolve_intra_doc_links(
resolver: &mut Resolver<'_>,
- sess: &Session,
krate: &ast::Crate,
- externs: Externs,
document_private_items: bool,
) -> ResolverCaches {
let parent_scope =
ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
let mut link_resolver = EarlyDocLinkResolver {
resolver,
- sess,
parent_scope,
visited_mods: Default::default(),
markdown_links: Default::default(),
// the known necessary crates. Load them all unconditionally until we find a way to fix this.
// DO NOT REMOVE THIS without first testing on the reproducer in
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
- for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
+ for (extern_name, _) in
+ link_resolver.resolver.sess().opts.externs.iter().filter(|(_, entry)| entry.add_prelude)
+ {
link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
}
struct EarlyDocLinkResolver<'r, 'ra> {
resolver: &'r mut Resolver<'ra>,
- sess: &'r Session,
parent_scope: ParentScope<'ra>,
visited_mods: DefIdSet,
markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
self.resolve_doc_links_extern_outer_fixme(def_id, def_id);
let assoc_item_def_ids = Vec::from_iter(
- self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess),
+ self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.resolver.sess()),
);
for assoc_def_id in assoc_item_def_ids {
if !is_inherent || self.resolver.cstore().visibility_untracked(assoc_def_id).is_public()
if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
return;
}
- let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess));
+ let attrs = Vec::from_iter(
+ self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
+ );
let parent_scope = ParentScope::module(
self.resolver.get_nearest_non_block_module(
self.resolver.opt_parent(scope_id).unwrap_or(scope_id),
if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
return;
}
- let attrs = Vec::from_iter(self.resolver.cstore().item_attrs_untracked(def_id, self.sess));
+ let attrs = Vec::from_iter(
+ self.resolver.cstore().item_attrs_untracked(def_id, self.resolver.sess()),
+ );
let parent_scope = ParentScope::module(self.resolver.expect_module(def_id), self.resolver);
self.resolve_doc_links(doc_attrs(attrs.iter()), parent_scope);
}
let field_def_ids = Vec::from_iter(
self.resolver
.cstore()
- .associated_item_def_ids_untracked(def_id, self.sess),
+ .associated_item_def_ids_untracked(def_id, self.resolver.sess()),
);
for field_def_id in field_def_ids {
self.resolve_doc_links_extern_outer(field_def_id, scope_id);
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 strip_priv_imports(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
- ImportStripper { tcx: cx.tcx }.fold_crate(krate)
+ let is_json_output = cx.output_format.is_json() && !cx.show_coverage;
+ ImportStripper { tcx: cx.tcx, is_json_output }.fold_crate(krate)
}
is_json_output,
tcx: cx.tcx,
};
- krate = ImportStripper { tcx: cx.tcx }.fold_crate(stripper.fold_crate(krate));
+ krate =
+ ImportStripper { tcx: cx.tcx, is_json_output }.fold_crate(stripper.fold_crate(krate));
}
// strip all impls referencing private items
}
// handled in the `strip-priv-imports` pass
- clean::ExternCrateItem { .. } => {}
- clean::ImportItem(ref imp) => {
- // Because json doesn't inline imports from private modules, we need to mark
- // the imported item as retained so it's impls won't be stripped.
- //
- // FIXME: Is it necessary to check for json output here: See
- // https://github.com/rust-lang/rust/pull/100325#discussion_r941495215
- if let Some(did) = imp.source.did && self.is_json_output {
- self.retained.insert(did.into());
- }
- }
+ clean::ExternCrateItem { .. } | clean::ImportItem(_) => {}
clean::ImplItem(..) => {}
/// This stripper discards all private import statements (`use`, `extern crate`)
pub(crate) struct ImportStripper<'tcx> {
pub(crate) tcx: TyCtxt<'tcx>,
+ pub(crate) is_json_output: bool,
+}
+
+impl<'tcx> ImportStripper<'tcx> {
+ fn import_should_be_hidden(&self, i: &Item, imp: &clean::Import) -> bool {
+ if self.is_json_output {
+ // FIXME: This should be handled the same way as for HTML output.
+ imp.imported_item_is_doc_hidden(self.tcx)
+ } else {
+ i.attrs.lists(sym::doc).has_word(sym::hidden)
+ }
+ }
}
impl<'tcx> DocFolder for ImportStripper<'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
match *i.kind {
- clean::ImportItem(imp) if imp.imported_item_is_doc_hidden(self.tcx) => None,
+ clean::ImportItem(imp) if self.import_should_be_hidden(&i, &imp) => None,
+ clean::ImportItem(_) if i.attrs.lists(sym::doc).has_word(sym::hidden) => None,
clean::ExternCrateItem { .. } | clean::ImportItem(..)
if i.visibility(self.tcx) != Some(Visibility::Public) =>
{
//! 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_hir::def_id::{DefId, DefIdMap};
+use rustc_hir::{HirIdSet, Node, CRATE_HIR_ID};
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
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: HirIdSet,
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();
+ let mut stack = HirIdSet::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,
- );
-
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(
+ hir::CRATE_HIR_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);
+ fn visit_mod_contents(
+ &mut self,
+ id: hir::HirId,
+ m: &'tcx hir::Mod<'tcx>,
+ name: Symbol,
+ parent_id: Option<hir::HirId>,
+ ) -> Module<'tcx> {
+ let mut om = Module::new(name, id, m.spans.inner_span);
let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
// 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):
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
res: Res,
renamed: Option<Symbol>,
glob: bool,
+ om: &mut Module<'tcx>,
please_inline: bool,
) -> bool {
debug!("maybe_inline_local res: {:?}", res);
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(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(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
}
ret
}
- #[inline]
- fn add_to_current_mod(
+ fn visit_item(
&mut self,
item: &'tcx hir::Item<'_>,
renamed: Option<Symbol>,
+ om: &mut Module<'tcx>,
parent_id: Option<hir::HirId>,
) {
- 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 "_".
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.hir_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_id::{DefId, DefIdSet};
use rustc_middle::ty::TyCtxt;
// 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 {
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: FxHashSet<DefId>,
+ visited_mods: DefIdSet,
}
impl LibEmbargoVisitor<'_, '_> {
-Subproject commit 9ad24035fea8d309753f5e39e6eb53d1d0eb39ce
+Subproject commit 477e7285b12f876ad105188cfcfc8adda7dc29aa
-Subproject commit d992ab4e9034930e7809749f04077045af8f4d79
+Subproject commit 3c5af6bed9a1a243a693e8e22ee2486bd5b82a6c
sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
test "$sysroot" = $desired_sysroot
+# Check that the --sysroot argument is only passed once (SYSROOT is ignored)
+(
+ cd rustc_tools_util
+ touch src/lib.rs
+ SYSROOT=/tmp RUSTFLAGS="--sysroot=$(rustc --print sysroot)" ../target/debug/cargo-clippy clippy --verbose
+)
+
# Make sure this isn't set - clippy-driver should cope without it
unset CARGO_MANIFEST_DIR
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
+[`derived_hash_with_manual_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derived_hash_with_manual_eq
[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros
[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
# Installation
-If you're using `rustup` to install and manage you're Rust toolchains, Clippy is
+If you're using `rustup` to install and manage your Rust toolchains, Clippy is
usually **already installed**. In that case you can skip this chapter and go to
the [Usage] chapter.
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
+// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate.
+#[allow(unused_extern_crates)]
+extern crate rustc_driver;
extern crate rustc_lexer;
use std::path::PathBuf;
fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
// Check for the feature
- if !cx.tcx.sess.features_untracked().lint_reasons {
+ if !cx.tcx.features().lint_reasons {
return;
}
Block, Expr, ExprKind, Local, Node, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
+use rustc_middle::{lint::in_external_macro, ty::print::with_forced_trimmed_paths};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
if is_plain_default(arg_path) || given_type(cx, expr) {
"Box::default()".into()
} else {
- format!("Box::<{arg_ty}>::default()")
+ with_forced_trimmed_paths!(format!("Box::<{arg_ty}>::default()"))
},
Applicability::MachineApplicable
);
.iter()
.filter(|&&(_, name)| !name.as_str().starts_with('_'))
.any(|&(_, name)| {
- let mut walker = ContainsName { name, result: false };
+ let mut walker = ContainsName {
+ name,
+ result: false,
+ cx,
+ };
// Scan block
block
declare_clippy_lint! {
/// ### What it does
- /// Checks for usage of dbg!() macro.
+ /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro.
///
/// ### Why is this bad?
- /// `dbg!` macro is intended as a debugging tool. It
- /// should not be in version control.
+ /// The `dbg!` macro is intended as a debugging tool. It should not be present in released
+ /// software or committed to a version control system.
///
/// ### Example
/// ```rust,ignore
cx,
DBG_MACRO,
macro_call.span,
- "`dbg!` macro is intended as a debugging tool",
- "ensure to avoid having uses of it in version control",
+ "the `dbg!` macro is intended as a debugging tool",
+ "remove the invocation before committing it to a version control system",
suggestion,
applicability,
);
crate::dereference::NEEDLESS_BORROW_INFO,
crate::dereference::REF_BINDING_TO_REFERENCE_INFO,
crate::derivable_impls::DERIVABLE_IMPLS_INFO,
- crate::derive::DERIVE_HASH_XOR_EQ_INFO,
+ crate::derive::DERIVED_HASH_WITH_MANUAL_EQ_INFO,
crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO,
crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO,
crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO,
use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
+use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
if let ty::Adt(def, ..) = expr_ty.kind();
if !is_from_proc_macro(cx, expr);
then {
- // TODO: Work out a way to put "whatever the imported way of referencing
- // this type in this file" rather than a fully-qualified type.
- let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did()));
+ let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did())));
span_lint_and_sugg(
cx,
DEFAULT_TRAIT_ACCESS,
// find out if and which field was set by this `consecutive_statement`
if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
// interrupt and cancel lint if assign_rhs references the original binding
- if contains_name(binding_name, assign_rhs) {
+ if contains_name(binding_name, assign_rhs, cx) {
cancel_lint = true;
break;
}
possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
}
let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
- // If `place.local` were not included here, the `copyable_iterator::warn` test would fail. The
- // reason is that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible
- // borrower of itself. See the comment in that method for an explanation as to why.
- possible_borrower.at_most_borrowers(cx, &[local, place.local], place.local, location)
+ // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
+ // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
+ // itself. See the comment in that method for an explanation as to why.
+ possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
&& used_exactly_once(mir, place.local).unwrap_or(false)
} else {
false
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::indent_of;
use clippy_utils::{is_default_equivalent, peel_blocks};
use rustc_errors::Applicability;
use rustc_hir::{
- def::{DefKind, Res},
- Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
+ def::{CtorKind, CtorOf, DefKind, Res},
+ Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, Ty, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_middle::ty::{AdtDef, DefIdTree};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! {
"manual implementation of the `Default` trait which is equal to a derive"
}
-declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
+pub struct DerivableImpls {
+ msrv: Msrv,
+}
+
+impl DerivableImpls {
+ #[must_use]
+ pub fn new(msrv: Msrv) -> Self {
+ DerivableImpls { msrv }
+ }
+}
+
+impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
fn is_path_self(e: &Expr<'_>) -> bool {
if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
}
}
+fn check_struct<'tcx>(
+ cx: &LateContext<'tcx>,
+ item: &'tcx Item<'_>,
+ self_ty: &Ty<'_>,
+ func_expr: &Expr<'_>,
+ adt_def: AdtDef<'_>,
+) {
+ if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
+ if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
+ for arg in a.args {
+ if !matches!(arg, GenericArg::Lifetime(_)) {
+ return;
+ }
+ }
+ }
+ }
+ let should_emit = match peel_blocks(func_expr).kind {
+ ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
+ ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
+ ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
+ _ => false,
+ };
+
+ if should_emit {
+ let struct_span = cx.tcx.def_span(adt_def.did());
+ span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
+ diag.span_suggestion_hidden(
+ item.span,
+ "remove the manual implementation...",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ diag.span_suggestion(
+ struct_span.shrink_to_lo(),
+ "...and instead derive it",
+ "#[derive(Default)]\n".to_string(),
+ Applicability::MachineApplicable,
+ );
+ });
+ }
+}
+
+fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) {
+ if_chain! {
+ if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind;
+ if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res;
+ if let variant_id = cx.tcx.parent(id);
+ if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id);
+ if variant_def.fields.is_empty();
+ if !variant_def.is_field_list_non_exhaustive();
+
+ then {
+ let enum_span = cx.tcx.def_span(adt_def.did());
+ let indent_enum = indent_of(cx, enum_span).unwrap_or(0);
+ let variant_span = cx.tcx.def_span(variant_def.def_id);
+ let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
+ span_lint_and_then(
+ cx,
+ DERIVABLE_IMPLS,
+ item.span,
+ "this `impl` can be derived",
+ |diag| {
+ diag.span_suggestion_hidden(
+ item.span,
+ "remove the manual implementation...",
+ String::new(),
+ Applicability::MachineApplicable
+ );
+ diag.span_suggestion(
+ enum_span.shrink_to_lo(),
+ "...and instead derive it...",
+ format!(
+ "#[derive(Default)]\n{indent}",
+ indent = " ".repeat(indent_enum),
+ ),
+ Applicability::MachineApplicable
+ );
+ diag.span_suggestion(
+ variant_span.shrink_to_lo(),
+ "...and mark the default variant",
+ format!(
+ "#[default]\n{indent}",
+ indent = " ".repeat(indent_variant),
+ ),
+ Applicability::MachineApplicable
+ );
+ }
+ );
+ }
+ }
+}
+
impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if !attrs.iter().any(|attr| attr.doc_str().is_some());
if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
- if adt_def.is_struct();
- then {
- if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
- if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
- for arg in a.args {
- if !matches!(arg, GenericArg::Lifetime(_)) {
- return;
- }
- }
- }
- }
- let should_emit = match peel_blocks(func_expr).kind {
- ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
- ExprKind::Call(callee, args)
- if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
- ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
- _ => false,
- };
- if should_emit {
- let struct_span = cx.tcx.def_span(adt_def.did());
- span_lint_and_then(
- cx,
- DERIVABLE_IMPLS,
- item.span,
- "this `impl` can be derived",
- |diag| {
- diag.span_suggestion_hidden(
- item.span,
- "remove the manual implementation...",
- String::new(),
- Applicability::MachineApplicable
- );
- diag.span_suggestion(
- struct_span.shrink_to_lo(),
- "...and instead derive it",
- "#[derive(Default)]\n".to_string(),
- Applicability::MachineApplicable
- );
- }
- );
+ then {
+ if adt_def.is_struct() {
+ check_struct(cx, item, self_ty, func_expr, adt_def);
+ } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
+ check_enum(cx, item, func_expr, adt_def);
}
}
}
}
+
+ extract_msrv_attr!(LateContext);
}
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
- self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate,
- Ty, TyCtxt,
+ self, Binder, BoundConstness, Clause, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind,
+ TraitPredicate, Ty, TyCtxt,
};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
/// }
/// ```
#[clippy::version = "pre 1.29.0"]
- pub DERIVE_HASH_XOR_EQ,
+ pub DERIVED_HASH_WITH_MANUAL_EQ,
correctness,
"deriving `Hash` but implementing `PartialEq` explicitly"
}
declare_lint_pass!(Derive => [
EXPL_IMPL_CLONE_ON_COPY,
- DERIVE_HASH_XOR_EQ,
+ DERIVED_HASH_WITH_MANUAL_EQ,
DERIVE_ORD_XOR_PARTIAL_ORD,
UNSAFE_DERIVE_DESERIALIZE,
DERIVE_PARTIAL_EQ_WITHOUT_EQ
}
}
-/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
+/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint.
fn check_hash_peq<'tcx>(
cx: &LateContext<'tcx>,
span: Span,
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
- if peq_is_automatically_derived == hash_is_automatically_derived {
+ if !hash_is_automatically_derived || peq_is_automatically_derived {
return;
}
// Only care about `impl PartialEq<Foo> for Foo`
// For `impl PartialEq<B> for A, input_types is [A, B]
- if trait_ref.substs.type_at(1) == ty {
- let mess = if peq_is_automatically_derived {
- "you are implementing `Hash` explicitly but have derived `PartialEq`"
- } else {
- "you are deriving `Hash` but have implemented `PartialEq` explicitly"
- };
-
+ if trait_ref.subst_identity().substs.type_at(1) == ty {
span_lint_and_then(
cx,
- DERIVE_HASH_XOR_EQ,
+ DERIVED_HASH_WITH_MANUAL_EQ,
span,
- mess,
+ "you are deriving `Hash` but have implemented `PartialEq` explicitly",
|diag| {
if let Some(local_def_id) = impl_id.as_local() {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
// Only care about `impl PartialOrd<Foo> for Foo`
// For `impl PartialOrd<B> for A, input_types is [A, B]
- if trait_ref.substs.type_at(1) == ty {
+ if trait_ref.subst_identity().substs.type_at(1) == ty {
let mess = if partial_ord_is_automatically_derived {
"you are implementing `Ord` explicitly but have derived `PartialOrd`"
} else {
if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
return;
}
+ // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`.
+ // https://github.com/rust-lang/rust-clippy/issues/10188
+ if ty_adt.repr().packed()
+ && ty_subs
+ .iter()
+ .any(|arg| matches!(arg.unpack(), GenericArgKind::Type(_) | GenericArgKind::Const(_)))
+ {
+ return;
+ }
span_lint_and_note(
cx,
let is_copy = is_copy(cx, arg_ty);
let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
let (lint, msg) = match fn_name {
- sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY),
+ sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => (DROP_REF, DROP_REF_SUMMARY),
sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY),
sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY),
span_after_ident,
"remove the brackets",
";",
- Applicability::MachineApplicable);
+ Applicability::Unspecified);
},
);
}
if_chain! {
if let hir::ItemKind::Impl(impl_) = &item.kind;
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
- if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id);
+ if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id);
then {
lint_impl_body(cx, item.span, impl_.items);
}
&& let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
// `impl Into<target_ty> for self_ty`
&& let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
- && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+ && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(ty::EarlyBinder::subst_identity)
&& cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
&& !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _))
{
let 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) => {
/// [`Instant::now()`]: std::time::Instant::now;
#[clippy::version = "1.65.0"]
pub UNCHECKED_DURATION_SUBTRACTION,
- suspicious,
+ pedantic,
"finds unchecked subtraction of a 'Duration' from an 'Instant'"
}
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
store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
store.register_late_pass(|_| Box::new(derive::Derive));
- store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls));
+ store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv())));
store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
let skip = if starts_at_zero {
String::new()
- } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
+ } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start, cx) {
return;
} else {
format!(".skip({})", snippet(cx, start.span, ".."))
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
String::new()
- } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
+ } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr, cx) {
return;
} else {
match limits {
use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, snippet_with_applicability};
+use clippy_utils::visitors::contains_break_or_continue;
use if_chain::if_chain;
use rustc_ast::util::parser::PREC_PREFIX;
use rustc_ast::Mutability;
if_chain! {
if let ExprKind::Block(block, _) = body.kind;
if !block.stmts.is_empty();
+ if !contains_break_or_continue(body);
then {
let mut applicability = Applicability::MachineApplicable;
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
} else {
return;
};
- let mutable_static_in_cond = var_visitor.def_ids.iter().any(|(_, v)| *v);
+ let mutable_static_in_cond = var_visitor.def_ids.items().any(|(_, v)| *v);
let mut has_break_or_return_visitor = HasBreakOrReturnVisitor {
has_break_or_return: false,
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{indent_of, reindent_multiline};
use clippy_utils::ty::is_type_lang_item;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::LateContext;
use rustc_span::{source_map::Spanned, Span};
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
) {
+ if let ExprKind::MethodCall(path_segment, ..) = recv.kind {
+ if matches!(
+ path_segment.ident.name.as_str(),
+ "to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase"
+ ) {
+ return;
+ }
+ }
+
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
if recv_ty.is_str() || is_type_lang_item(cx, recv_ty, LangItem::String);
then {
- span_lint_and_help(
+ span_lint_and_then(
cx,
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
- call_span,
+ recv.span.to(call_span),
"case-sensitive file extension comparison",
- None,
- "consider using a case-insensitive comparison instead",
+ |diag| {
+ diag.help("consider using a case-insensitive comparison instead");
+ if let Some(mut recv_source) = snippet_opt(cx, recv.span) {
+
+ if !cx.typeck_results().expr_ty(recv).is_ref() {
+ recv_source = format!("&{recv_source}");
+ }
+
+ let suggestion_source = reindent_multiline(
+ format!(
+ "std::path::Path::new({})
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case(\"{}\"))",
+ recv_source, ext_str.strip_prefix('.').unwrap()).into(),
+ true,
+ Some(indent_of(cx, call_span).unwrap_or(0) + 4)
+ );
+
+ diag.span_suggestion(
+ recv.span.to(call_span),
+ "use std::path::Path",
+ suggestion_source,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
);
}
}
use rustc_errors::Applicability;
use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, MatchSource, Node, PatKind, QPath};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_middle::ty::{self, adjustment::Adjust, print::with_forced_trimmed_paths};
use rustc_span::symbol::{sym, Symbol};
use super::CLONE_DOUBLE_REF;
cx,
CLONE_DOUBLE_REF,
expr.span,
- &format!(
+ &with_forced_trimmed_paths!(format!(
"using `clone` on a double-reference; \
this will copy the reference of type `{ty}` instead of cloning the inner type"
- ),
+ )),
|diag| {
if let Some(snip) = sugg::Sugg::hir_opt(cx, arg) {
let mut ty = innermost;
}
let refs = "&".repeat(n + 1);
let derefs = "*".repeat(n);
- let explicit = format!("<{refs}{ty}>::clone({snip})");
+ let explicit = with_forced_trimmed_paths!(format!("<{refs}{ty}>::clone({snip})"));
diag.span_suggestion(
expr.span,
"try dereferencing it",
- format!("{refs}({derefs}{}).clone()", snip.deref()),
+ with_forced_trimmed_paths!(format!("{refs}({derefs}{}).clone()", snip.deref())),
Applicability::MaybeIncorrect,
);
diag.span_suggestion(
cx,
CLONE_ON_COPY,
expr.span,
- &format!("using `clone` on type `{ty}` which implements the `Copy` trait"),
+ &with_forced_trimmed_paths!(format!(
+ "using `clone` on type `{ty}` which implements the `Copy` trait"
+ )),
help,
sugg,
app,
match closure_expr.kind {
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
if_chain! {
- if ident.name == method_name;
- if let hir::ExprKind::Path(path) = &receiver.kind;
- if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id);
- then {
- return arg_id == *local
- }
+ if ident.name == method_name;
+ if let hir::ExprKind::Path(path) = &receiver.kind;
+ if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id);
+ then {
+ return arg_id == *local
+ }
}
false
},
}
if_chain! {
- if is_trait_method(cx, map_recv, sym::Iterator);
-
- // filter(|x| ...is_some())...
- if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind;
- let filter_body = cx.tcx.hir().body(filter_body_id);
- if let [filter_param] = filter_body.params;
- // optional ref pattern: `filter(|&x| ..)`
- let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
- (ref_pat, true)
- } else {
- (filter_param.pat, false)
+ if is_trait_method(cx, map_recv, sym::Iterator);
+
+ // filter(|x| ...is_some())...
+ if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind;
+ let filter_body = cx.tcx.hir().body(filter_body_id);
+ if let [filter_param] = filter_body.params;
+ // optional ref pattern: `filter(|&x| ..)`
+ let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
+ (ref_pat, true)
+ } else {
+ (filter_param.pat, false)
+ };
+ // closure ends with is_some() or is_ok()
+ if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
+ if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind;
+ if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
+ if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
+ Some(false)
+ } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
+ Some(true)
+ } else {
+ None
+ };
+ if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
+
+ // ...map(|x| ...unwrap())
+ if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind;
+ let map_body = cx.tcx.hir().body(map_body_id);
+ if let [map_param] = map_body.params;
+ if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
+ // closure ends with expect() or unwrap()
+ if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind;
+ if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
+
+ // .filter(..).map(|y| f(y).copied().unwrap())
+ // ~~~~
+ let map_arg_peeled = match map_arg.kind {
+ ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
+ original_arg
+ },
+ _ => map_arg,
+ };
+
+ // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
+ let simple_equal = path_to_local_id(filter_arg, filter_param_id)
+ && path_to_local_id(map_arg_peeled, map_param_id);
+
+ let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
+ // in `filter(|x| ..)`, replace `*x` with `x`
+ let a_path = if_chain! {
+ if !is_filter_param_ref;
+ if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
+ then { expr_path } else { a }
};
- // closure ends with is_some() or is_ok()
- if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
- if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind;
- if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
- if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
- Some(false)
- } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
- Some(true)
+ // let the filter closure arg and the map closure arg be equal
+ path_to_local_id(a_path, filter_param_id)
+ && path_to_local_id(b, map_param_id)
+ && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
+ };
+
+ if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
+ then {
+ let span = filter_span.with_hi(expr.span.hi());
+ let (filter_name, lint) = if is_find {
+ ("find", MANUAL_FIND_MAP)
} else {
- None
- };
- if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
-
- // ...map(|x| ...unwrap())
- if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind;
- let map_body = cx.tcx.hir().body(map_body_id);
- if let [map_param] = map_body.params;
- if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
- // closure ends with expect() or unwrap()
- if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind;
- if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
-
- // .filter(..).map(|y| f(y).copied().unwrap())
- // ~~~~
- let map_arg_peeled = match map_arg.kind {
- ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
- original_arg
- },
- _ => map_arg,
+ ("filter", MANUAL_FILTER_MAP)
};
+ let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
+ let (to_opt, deref) = if is_result {
+ (".ok()", String::new())
+ } else {
+ let derefs = cx.typeck_results()
+ .expr_adjustments(map_arg)
+ .iter()
+ .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+ .count();
- // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
- let simple_equal = path_to_local_id(filter_arg, filter_param_id)
- && path_to_local_id(map_arg_peeled, map_param_id);
-
- let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
- // in `filter(|x| ..)`, replace `*x` with `x`
- let a_path = if_chain! {
- if !is_filter_param_ref;
- if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
- then { expr_path } else { a }
- };
- // let the filter closure arg and the map closure arg be equal
- path_to_local_id(a_path, filter_param_id)
- && path_to_local_id(b, map_param_id)
- && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
+ ("", "*".repeat(derefs))
};
-
- if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
- then {
- let span = filter_span.with_hi(expr.span.hi());
- let (filter_name, lint) = if is_find {
- ("find", MANUAL_FIND_MAP)
- } else {
- ("filter", MANUAL_FILTER_MAP)
- };
- let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
- let (to_opt, deref) = if is_result {
- (".ok()", String::new())
- } else {
- let derefs = cx.typeck_results()
- .expr_adjustments(map_arg)
- .iter()
- .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
- .count();
-
- ("", "*".repeat(derefs))
- };
- let sugg = format!(
- "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
- snippet(cx, map_arg.span, ".."),
- );
- span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
- }
+ let sugg = format!(
+ "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
+ snippet(cx, map_arg.span, ".."),
+ );
+ span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
+ }
}
}
use clippy_utils::sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::is_local_used;
-use rustc_hir::{BindingAnnotation, Body, BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
+use rustc_hir::{BindingAnnotation, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
use rustc_span::sym;
if let Body {params: [p], value: body_expr, generator_kind: _ } = cx.tcx.hir().body(c.body);
if let PatKind::Tuple([key_pat, val_pat], _) = p.pat.kind;
- let (replacement_kind, binded_ident) = match (&key_pat.kind, &val_pat.kind) {
- (key, PatKind::Binding(_, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", value),
- (PatKind::Binding(_, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", key),
+ let (replacement_kind, annotation, bound_ident) = match (&key_pat.kind, &val_pat.kind) {
+ (key, PatKind::Binding(ann, _, value, _)) if pat_is_wild(cx, key, m_arg) => ("value", ann, value),
+ (PatKind::Binding(ann, _, key, _), value) if pat_is_wild(cx, value, m_arg) => ("key", ann, key),
_ => return,
};
if_chain! {
if let ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = body_expr.kind;
if let [local_ident] = path.segments;
- if local_ident.ident.as_str() == binded_ident.as_str();
+ if local_ident.ident.as_str() == bound_ident.as_str();
then {
span_lint_and_sugg(
applicability,
);
} else {
+ let ref_annotation = if annotation.0 == ByRef::Yes {
+ "ref "
+ } else {
+ ""
+ };
+ let mut_annotation = if annotation.1 == Mutability::Mut {
+ "mut "
+ } else {
+ ""
+ };
span_lint_and_sugg(
cx,
ITER_KV_MAP,
expr.span,
&format!("iterating on a map's {replacement_kind}s"),
"try",
- format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{binded_ident}| {})",
+ format!("{recv_snippet}.{into_prefix}{replacement_kind}s().map(|{ref_annotation}{mut_annotation}{bound_ident}| {})",
snippet_with_applicability(cx, body_expr.span, "/* body */", &mut applicability)),
applicability,
);
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, print::with_forced_trimmed_paths};
use rustc_span::sym;
use super::SUSPICIOUS_TO_OWNED;
cx,
SUSPICIOUS_TO_OWNED,
expr.span,
- &format!("this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"),
+ &with_forced_trimmed_paths!(format!(
+ "this `to_owned` call clones the {input_type} itself and does not cause the {input_type} contents to become owned"
+ )),
"consider using, depending on intent",
format!("{recv_snip}.clone()` or `{recv_snip}.into_owned()"),
app,
&& 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) {
let container_id = assoc_item.container_id(cx.tcx);
let trait_def_id = match assoc_item.container {
TraitContainer => Some(container_id),
- ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.def_id),
+ ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
};
if let Some(trait_def_id) = trait_def_id {
}
}
- for assoc in provided.values() {
- let source_map = cx.tcx.sess.source_map();
- let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
+ cx.tcx.with_stable_hashing_context(|hcx| {
+ for assoc in provided.values_sorted(&hcx, true) {
+ let source_map = cx.tcx.sess.source_map();
+ let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
- span_lint_and_help(
- cx,
- MISSING_TRAIT_METHODS,
- source_map.guess_head_span(item.span),
- &format!("missing trait method provided by default: `{}`", assoc.name),
- Some(definition_span),
- "implement the method",
- );
- }
+ span_lint_and_help(
+ cx,
+ MISSING_TRAIT_METHODS,
+ source_map.guess_head_span(item.span),
+ &format!("missing trait method provided by default: `{}`", assoc.name),
+ Some(definition_span),
+ "implement the method",
+ );
+ }
+ })
}
}
}
//! Checks for uses of mutex where an atomic value could be used
//!
-//! This lint is **warn** by default
+//! This lint is **allow** by default
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::is_type_diagnostic_item;
/// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
/// faster.
///
+ /// On the other hand, `Mutex`es are, in general, easier to
+ /// verify correctness. An atomic does not behave the same as
+ /// an equivalent mutex. See [this issue](https://github.com/rust-lang/rust-clippy/issues/4295)'s commentary for more details.
+ ///
/// ### Known problems
/// This lint cannot detect if the mutex is actually used
/// for waiting before a critical section.
/// ```
#[clippy::version = "pre 1.29.0"]
pub MUTEX_ATOMIC,
- nursery,
- "using a mutex where an atomic value could be used instead"
+ restriction,
+ "using a mutex where an atomic value could be used instead."
}
declare_clippy_lint! {
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! {
let sugg = |diag: &mut Diagnostic| {
if let ty::Adt(def, ..) = ty.kind() {
if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
- if can_type_implement_copy(
+ if type_allowed_to_implement_copy(
cx.tcx,
cx.param_env,
ty,
if send_trait == trait_id;
if hir_impl.polarity == ImplPolarity::Positive;
if let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
- if let self_ty = ty_trait_ref.self_ty();
+ if let self_ty = ty_trait_ref.subst_identity().self_ty();
if let ty::Adt(adt_def, impl_trait_substs) = self_ty.kind();
then {
let mut non_send_fields = Vec::new();
})) => {
#[allow(trivial_casts)]
if let Some(Node::Item(item)) = get_parent_node(cx.tcx, owner_id.into())
- && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
+ && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.owner_id).map(|t| t.subst_identity())
&& let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
{
(
use clippy_utils::{
consts::{constant, constant_simple},
diagnostics::span_lint,
- peel_hir_expr_refs,
+ peel_hir_expr_refs, peel_hir_expr_unary,
};
use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
}
/// If `expr` is not a literal integer like `1`, returns `None`.
+ ///
+ /// Returns the absolute value of the expression, if this is an integer literal.
fn literal_integer(expr: &hir::Expr<'_>) -> Option<u128> {
- if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node {
+ let actual = peel_hir_expr_unary(expr).0;
+ if let hir::ExprKind::Lit(ref lit) = actual.kind && let ast::LitKind::Int(n, _) = lit.node {
Some(n)
}
else {
if !matches!(
op.node,
hir::BinOpKind::Add
- | hir::BinOpKind::Sub
- | hir::BinOpKind::Mul
| hir::BinOpKind::Div
+ | hir::BinOpKind::Mul
| hir::BinOpKind::Rem
| hir::BinOpKind::Shl
| hir::BinOpKind::Shr
+ | hir::BinOpKind::Sub
) {
return;
};
// 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)))
{
declare_clippy_lint! {
/// ### What it does
/// Checks for range expressions `x..y` where both `x` and `y`
- /// are constant and `x` is greater or equal to `y`.
+ /// are constant and `x` is greater to `y`. Also triggers if `x` is equal to `y` when they are conditions to a `for` loop.
///
/// ### Why is this bad?
/// Empty ranges yield no values so iterating them is a no-op.
// `res = clone(arg)` can be turned into `res = move arg;`
// if `arg` is the only borrow of `cloned` at this point.
- if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg], cloned, loc) {
+ if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
continue;
}
// StorageDead(pred_arg);
// res = to_path_buf(cloned);
// ```
- if cannot_move_out || !possible_borrower.at_most_borrowers(cx, &[arg, cloned], local, loc) {
+ if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
continue;
}
("clippy::box_vec", "clippy::box_collection"),
("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
+ ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"),
("clippy::disallowed_method", "clippy::disallowed_methods"),
("clippy::disallowed_type", "clippy::disallowed_types"),
("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
// if desugar of `do yeet`, don't lint
if let Some(inner_expr) = inner
&& let ExprKind::Call(path_expr, _) = inner_expr.kind
- && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind {
- return;
+ && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, _, _)) = path_expr.kind
+ {
+ return;
}
- if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
- let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
- if !borrows {
- // check if expr return nothing
- let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
- extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
- } else {
- peeled_drop_expr.span
- };
-
- emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement);
- }
+ if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
+ return;
+ }
+ let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
+ if borrows {
+ return;
}
+ // check if expr return nothing
+ let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
+ extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
+ } else {
+ peeled_drop_expr.span
+ };
+
+ 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());
{
ControlFlow::Break(())
} else {
- ControlFlow::Continue(Descend::from(!expr.span.from_expansion()))
+ ControlFlow::Continue(Descend::from(!e.span.from_expansion()))
}
})
.is_some()
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!(
/// `Vec` or a `VecDeque` (formerly called `RingBuf`).
///
/// ### Why is this bad?
- /// Gankro says:
+ /// Gankra says:
///
/// > The TL;DR of `LinkedList` is that it's built on a massive amount of
/// pointers and indirection.
use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::visitors::is_local_used;
use if_chain::if_chain;
-use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
+use rustc_hir::{Body, Impl, ImplItem, ImplItemKind, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use std::ops::ControlFlow;
declare_clippy_lint! {
/// ### What it does
let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
let parent_item = cx.tcx.hir().expect_item(parent);
let assoc_item = cx.tcx.associated_item(impl_item.owner_id);
+ let contains_todo = |cx, body: &'_ Body<'_>| -> bool {
+ clippy_utils::visitors::for_each_expr(body.value, |e| {
+ if let Some(macro_call) = root_macro_call_first_node(cx, e) {
+ if cx.tcx.item_name(macro_call.def_id).as_str() == "todo" {
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ } else {
+ ControlFlow::Continue(())
+ }
+ })
+ .is_some()
+ };
if_chain! {
if let ItemKind::Impl(Impl { of_trait: None, .. }) = parent_item.kind;
if assoc_item.fn_has_self_parameter;
let body = cx.tcx.hir().body(*body_id);
if let [self_param, ..] = body.params;
if !is_local_used(cx, body, self_param.pat.hir_id);
+ if !contains_todo(cx, body);
then {
span_lint_and_help(
cx,
self_param.span,
"unused `self` argument",
None,
- "consider refactoring to a associated function",
+ "consider refactoring to an associated function",
);
}
}
then {
// `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
// `Self`.
- let self_ty = impl_trait_ref.self_ty();
+ let self_ty = impl_trait_ref.subst_identity().self_ty();
// `trait_method_sig` is the signature of the function, how it is declared in the
// trait, not in the impl of the trait.
fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
let map = cx.tcx.hir();
- match map.find_parent((hir_id)) {
+ match map.find_parent(hir_id) {
Some(hir::Node::Local(local)) => Some(local),
Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
_ => None,
extern crate rustc_ast_pretty;
extern crate rustc_attr;
extern crate rustc_data_structures;
+// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
+#[allow(unused_extern_crates)]
+extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_hir_typeck;
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
use crate::visitors::for_each_expr;
+use rustc_middle::hir::nested_filter;
+
#[macro_export]
macro_rules! extract_msrv_attr {
($context:ident) => {
}
}
-pub struct ContainsName {
+pub struct ContainsName<'a, 'tcx> {
+ pub cx: &'a LateContext<'tcx>,
pub name: Symbol,
pub result: bool,
}
-impl<'tcx> Visitor<'tcx> for ContainsName {
+impl<'a, 'tcx> Visitor<'tcx> for ContainsName<'a, 'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+
fn visit_name(&mut self, name: Symbol) {
if self.name == name {
self.result = true;
}
}
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
}
/// Checks if an `Expr` contains a certain name.
-pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
- let mut cn = ContainsName { name, result: false };
+pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
+ let mut cn = ContainsName {
+ name,
+ result: false,
+ cx,
+ };
cn.visit_expr(expr);
cn.result
}
}
}
+/// Gets the enclosing block, if any.
pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
let map = &cx.tcx.hir();
let enclosing_node = map
(e, count - remaining)
}
+/// Peels off all unary operators of an expression. Returns the underlying expression and the number
+/// of operators removed.
+pub fn peel_hir_expr_unary<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
+ let mut count: usize = 0;
+ let mut curr_expr = expr;
+ while let ExprKind::Unary(_, local_expr) = curr_expr.kind {
+ count = count.wrapping_add(1);
+ curr_expr = local_expr;
+ }
+ (curr_expr, count)
+}
+
/// Peels off all references on the expression. Returns the underlying expression and the number of
/// references removed.
pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
-use super::possible_origin::PossibleOriginVisitor;
+use super::{possible_origin::PossibleOriginVisitor, transitive_relation::TransitiveRelation};
use crate::ty::is_copy;
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_lint::LateContext;
-use rustc_middle::mir::{
- self, visit::Visitor as _, BasicBlock, Local, Location, Mutability, Statement, StatementKind, Terminator,
-};
-use rustc_middle::ty::{self, visit::TypeVisitor, TyCtxt};
-use rustc_mir_dataflow::{
- fmt::DebugWithContext, impls::MaybeStorageLive, lattice::JoinSemiLattice, Analysis, AnalysisDomain,
- CallReturnPlaces, ResultsCursor,
-};
+use rustc_middle::mir::{self, visit::Visitor as _, Mutability};
+use rustc_middle::ty::{self, visit::TypeVisitor};
+use rustc_mir_dataflow::{impls::MaybeStorageLive, Analysis, ResultsCursor};
use std::borrow::Cow;
use std::ops::ControlFlow;
/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
/// possible borrowers of `a`.
#[allow(clippy::module_name_repetitions)]
-struct PossibleBorrowerAnalysis<'b, 'tcx> {
- tcx: TyCtxt<'tcx>,
+struct PossibleBorrowerVisitor<'a, 'b, 'tcx> {
+ possible_borrower: TransitiveRelation,
body: &'b mir::Body<'tcx>,
+ cx: &'a LateContext<'tcx>,
possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
}
-#[derive(Clone, Debug, Eq, PartialEq)]
-struct PossibleBorrowerState {
- map: FxIndexMap<Local, BitSet<Local>>,
- domain_size: usize,
-}
-
-impl PossibleBorrowerState {
- fn new(domain_size: usize) -> Self {
+impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
+ fn new(
+ cx: &'a LateContext<'tcx>,
+ body: &'b mir::Body<'tcx>,
+ possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+ ) -> Self {
Self {
- map: FxIndexMap::default(),
- domain_size,
+ possible_borrower: TransitiveRelation::default(),
+ cx,
+ body,
+ possible_origin,
}
}
- #[allow(clippy::similar_names)]
- fn add(&mut self, borrowed: Local, borrower: Local) {
- self.map
- .entry(borrowed)
- .or_insert(BitSet::new_empty(self.domain_size))
- .insert(borrower);
- }
-}
-
-impl<C> DebugWithContext<C> for PossibleBorrowerState {
- fn fmt_with(&self, _ctxt: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- <_ as std::fmt::Debug>::fmt(self, f)
- }
- fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- unimplemented!()
- }
-}
+ fn into_map(
+ self,
+ cx: &'a LateContext<'tcx>,
+ maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>,
+ ) -> PossibleBorrowerMap<'b, 'tcx> {
+ let mut map = FxHashMap::default();
+ for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
+ if is_copy(cx, self.body.local_decls[row].ty) {
+ continue;
+ }
-impl JoinSemiLattice for PossibleBorrowerState {
- fn join(&mut self, other: &Self) -> bool {
- let mut changed = false;
- for (&borrowed, borrowers) in other.map.iter() {
+ let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
+ borrowers.remove(mir::Local::from_usize(0));
if !borrowers.is_empty() {
- changed |= self
- .map
- .entry(borrowed)
- .or_insert(BitSet::new_empty(self.domain_size))
- .union(borrowers);
+ map.insert(row, borrowers);
}
}
- changed
- }
-}
-
-impl<'b, 'tcx> AnalysisDomain<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> {
- type Domain = PossibleBorrowerState;
-
- const NAME: &'static str = "possible_borrower";
-
- fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
- PossibleBorrowerState::new(body.local_decls.len())
- }
-
- fn initialize_start_block(&self, _body: &mir::Body<'tcx>, _entry_set: &mut Self::Domain) {}
-}
-impl<'b, 'tcx> PossibleBorrowerAnalysis<'b, 'tcx> {
- fn new(
- tcx: TyCtxt<'tcx>,
- body: &'b mir::Body<'tcx>,
- possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
- ) -> Self {
- Self {
- tcx,
- body,
- possible_origin,
+ let bs = BitSet::new_empty(self.body.local_decls.len());
+ PossibleBorrowerMap {
+ map,
+ maybe_live,
+ bitset: (bs.clone(), bs),
}
}
}
-impl<'b, 'tcx> Analysis<'tcx> for PossibleBorrowerAnalysis<'b, 'tcx> {
- fn apply_call_return_effect(
- &self,
- _state: &mut Self::Domain,
- _block: BasicBlock,
- _return_places: CallReturnPlaces<'_, 'tcx>,
- ) {
- }
-
- fn apply_statement_effect(&self, state: &mut Self::Domain, statement: &Statement<'tcx>, _location: Location) {
- if let StatementKind::Assign(box (place, rvalue)) = &statement.kind {
- let lhs = place.local;
- match rvalue {
- mir::Rvalue::Ref(_, _, borrowed) => {
- state.add(borrowed.local, lhs);
- },
- other => {
- if ContainsRegion
- .visit_ty(place.ty(&self.body.local_decls, self.tcx).ty)
- .is_continue()
- {
- return;
+impl<'a, 'b, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'b, 'tcx> {
+ fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
+ let lhs = place.local;
+ match rvalue {
+ mir::Rvalue::Ref(_, _, borrowed) => {
+ self.possible_borrower.add(borrowed.local, lhs);
+ },
+ other => {
+ if ContainsRegion
+ .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
+ .is_continue()
+ {
+ return;
+ }
+ rvalue_locals(other, |rhs| {
+ if lhs != rhs {
+ self.possible_borrower.add(rhs, lhs);
}
- rvalue_locals(other, |rhs| {
- if lhs != rhs {
- state.add(rhs, lhs);
- }
- });
- },
- }
+ });
+ },
}
}
- fn apply_terminator_effect(&self, state: &mut Self::Domain, terminator: &Terminator<'tcx>, _location: Location) {
+ fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
if let mir::TerminatorKind::Call {
args,
destination: mir::Place { local: dest, .. },
for y in mutable_variables {
for x in &immutable_borrowers {
- state.add(*x, y);
+ self.possible_borrower.add(*x, y);
}
for x in &mutable_borrowers {
- state.add(*x, y);
+ self.possible_borrower.add(*x, y);
}
}
}
}
}
-/// Result of `PossibleBorrowerAnalysis`.
+/// Result of `PossibleBorrowerVisitor`.
#[allow(clippy::module_name_repetitions)]
pub struct PossibleBorrowerMap<'b, 'tcx> {
- body: &'b mir::Body<'tcx>,
- possible_borrower: ResultsCursor<'b, 'tcx, PossibleBorrowerAnalysis<'b, 'tcx>>,
- maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'b>>,
- pushed: BitSet<Local>,
- stack: Vec<Local>,
+ /// Mapping `Local -> its possible borrowers`
+ pub map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+ maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>,
+ // Caches to avoid allocation of `BitSet` on every query
+ pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
}
-impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
- pub fn new(cx: &LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self {
+impl<'a, 'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> {
+ pub fn new(cx: &'a LateContext<'tcx>, mir: &'b mir::Body<'tcx>) -> Self {
let possible_origin = {
let mut vis = PossibleOriginVisitor::new(mir);
vis.visit_body(mir);
vis.into_map(cx)
};
- let possible_borrower = PossibleBorrowerAnalysis::new(cx.tcx, mir, possible_origin)
+ let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len())))
.into_engine(cx.tcx, mir)
- .pass_name("possible_borrower")
+ .pass_name("redundant_clone")
.iterate_to_fixpoint()
.into_results_cursor(mir);
- let maybe_live = MaybeStorageLive::new(Cow::Owned(BitSet::new_empty(mir.local_decls.len())))
- .into_engine(cx.tcx, mir)
- .pass_name("possible_borrower")
- .iterate_to_fixpoint()
- .into_results_cursor(mir);
- PossibleBorrowerMap {
- body: mir,
- possible_borrower,
- maybe_live,
- pushed: BitSet::new_empty(mir.local_decls.len()),
- stack: Vec::with_capacity(mir.local_decls.len()),
- }
+ let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
+ vis.visit_body(mir);
+ vis.into_map(cx, maybe_storage_live_result)
}
- /// Returns true if the set of borrowers of `borrowed` living at `at` includes no more than
- /// `borrowers`.
- /// Notes:
- /// 1. It would be nice if `PossibleBorrowerMap` could store `cx` so that `at_most_borrowers`
- /// would not require it to be passed in. But a `PossibleBorrowerMap` is stored in `LintPass`
- /// `Dereferencing`, which outlives any `LateContext`.
- /// 2. In all current uses of `at_most_borrowers`, `borrowers` is a slice of at most two
- /// elements. Thus, `borrowers.contains(...)` is effectively a constant-time operation. If
- /// `at_most_borrowers`'s uses were to expand beyond this, its implementation might have to be
- /// adjusted.
- pub fn at_most_borrowers(
+ /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
+ pub fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
+ self.bounded_borrowers(borrowers, borrowers, borrowed, at)
+ }
+
+ /// Returns true if the set of borrowers of `borrowed` living at `at` includes at least `below`
+ /// but no more than `above`.
+ pub fn bounded_borrowers(
&mut self,
- cx: &LateContext<'tcx>,
- borrowers: &[mir::Local],
+ below: &[mir::Local],
+ above: &[mir::Local],
borrowed: mir::Local,
at: mir::Location,
) -> bool {
- if is_copy(cx, self.body.local_decls[borrowed].ty) {
- return true;
- }
-
- self.possible_borrower.seek_before_primary_effect(at);
- self.maybe_live.seek_before_primary_effect(at);
-
- let possible_borrower = &self.possible_borrower.get().map;
- let maybe_live = &self.maybe_live;
-
- self.pushed.clear();
- self.stack.clear();
+ self.maybe_live.seek_after_primary_effect(at);
- if let Some(borrowers) = possible_borrower.get(&borrowed) {
- for b in borrowers.iter() {
- if self.pushed.insert(b) {
- self.stack.push(b);
- }
+ self.bitset.0.clear();
+ let maybe_live = &mut self.maybe_live;
+ if let Some(bitset) = self.map.get(&borrowed) {
+ for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
+ self.bitset.0.insert(b);
}
} else {
- // Nothing borrows `borrowed` at `at`.
- return true;
+ return false;
}
- while let Some(borrower) = self.stack.pop() {
- if maybe_live.contains(borrower) && !borrowers.contains(&borrower) {
- return false;
- }
+ self.bitset.1.clear();
+ for b in below {
+ self.bitset.1.insert(*b);
+ }
- if let Some(borrowers) = possible_borrower.get(&borrower) {
- for b in borrowers.iter() {
- if self.pushed.insert(b) {
- self.stack.push(b);
- }
- }
- }
+ if !self.bitset.0.superset(&self.bitset.1) {
+ return false;
+ }
+
+ for b in above {
+ self.bitset.0.remove(*b);
}
- true
+ self.bitset.0.is_empty()
}
pub fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,65,0 { LET_ELSE }
- 1,62,0 { BOOL_THEN_SOME }
+ 1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
+ 1,55,0 { SEEK_REWIND }
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
- 1,55,0 { SEEK_REWIND }
}
fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
#[cfg(feature = "internal")]
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
-pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal")]
Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
},
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
- sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id))
+ ty::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, _, _) => {
ControlFlow::Continue(())
}
}
+
+pub fn contains_break_or_continue(expr: &Expr<'_>) -> bool {
+ for_each_expr(expr, |e| {
+ if matches!(e.kind, ExprKind::Break(..) | ExprKind::Continue(..)) {
+ ControlFlow::Break(())
+ } else {
+ ControlFlow::Continue(())
+ }
+ })
+ .is_some()
+}
[toolchain]
-channel = "nightly-2022-12-29"
+channel = "nightly-2023-01-12"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
LazyLock::force(&ICE_HOOK);
exit(rustc_driver::catch_with_exit_code(move || {
let mut orig_args: Vec<String> = env::args().collect();
+ let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some();
let sys_root_env = std::env::var("SYSROOT").ok();
let pass_sysroot_env_if_given = |args: &mut Vec<String>, sys_root_env| {
if let Some(sys_root) = sys_root_env {
- args.extend(vec!["--sysroot".into(), sys_root]);
+ if !has_sysroot_arg {
+ args.extend(vec!["--sysroot".into(), sys_root]);
+ }
};
};
+//! This test is meant to only be run in CI. To run it locally use:
+//!
+//! `env INTEGRATION=rust-lang/log cargo test --test integration --features=integration`
+//!
+//! You can use a different `INTEGRATION` value to test different repositories.
+//!
+//! This test will clone the specified repository and run Clippy on it. The test succeeds, if
+//! Clippy doesn't produce an ICE. Lint warnings are ignored by this test.
+
#![cfg(feature = "integration")]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![warn(rust_2018_idioms, unused_lifetimes)]
fn unary() {
// is explicitly on the list
let _ = -OutOfNames;
- // is specifically on the list
+ // is explicitly on the list
let _ = -Foo;
// not on the list
let _ = -Bar;
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:5:22
|
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::dbg-macro` implied by `-D warnings`
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | if let Some(n) = n.checked_sub(4) { n } else { n }
| ~~~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:9:8
|
LL | if dbg!(n <= 1) {
| ^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | if n <= 1 {
| ~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:10:9
|
LL | dbg!(1)
| ^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 1
|
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:12:9
|
LL | dbg!(n * factorial(n - 1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | n * factorial(n - 1)
|
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:17:5
|
LL | dbg!(42);
| ^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 42;
| ~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:18:5
|
LL | dbg!(dbg!(dbg!(42)));
| ^^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | dbg!(dbg!(42));
| ~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:19:14
|
LL | foo(3) + dbg!(factorial(4));
| ^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | foo(3) + factorial(4);
| ~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:20:5
|
LL | dbg!(1, 2, dbg!(3, 4));
| ^^^^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | (1, 2, dbg!(3, 4));
| ~~~~~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:21:5
|
LL | dbg!(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | (1, 2, 3, 4, 5);
| ~~~~~~~~~~~~~~~
clippy::assign_op_pattern,
clippy::erasing_op,
clippy::identity_op,
+ clippy::no_effect,
clippy::op_ref,
clippy::unnecessary_owned_empty_strings,
arithmetic_overflow,
use core::num::{Saturating, Wrapping};
+#[derive(Clone, Copy)]
pub struct Custom;
macro_rules! impl_arith {
- ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => {
+ ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => {
$(
- impl core::ops::$_trait<$ty> for Custom {
- type Output = Self;
- fn $method(self, _: $ty) -> Self::Output { Self }
+ impl core::ops::$_trait<$lhs> for $rhs {
+ type Output = Custom;
+ fn $method(self, _: $lhs) -> Self::Output { todo!() }
+ }
+ )*
+ }
+}
+
+macro_rules! impl_assign_arith {
+ ( $( $_trait:ident, $lhs:ty, $rhs:ty, $method:ident; )* ) => {
+ $(
+ impl core::ops::$_trait<$lhs> for $rhs {
+ fn $method(&mut self, _: $lhs) {}
}
)*
}
}
impl_arith!(
- Add, i32, add;
- Div, i32, div;
- Mul, i32, mul;
- Sub, i32, sub;
-
- Add, f64, add;
- Div, f64, div;
- Mul, f64, mul;
- Sub, f64, sub;
+ Add, Custom, Custom, add;
+ Div, Custom, Custom, div;
+ Mul, Custom, Custom, mul;
+ Rem, Custom, Custom, rem;
+ Sub, Custom, Custom, sub;
+
+ Add, Custom, &Custom, add;
+ Div, Custom, &Custom, div;
+ Mul, Custom, &Custom, mul;
+ Rem, Custom, &Custom, rem;
+ Sub, Custom, &Custom, sub;
+
+ Add, &Custom, Custom, add;
+ Div, &Custom, Custom, div;
+ Mul, &Custom, Custom, mul;
+ Rem, &Custom, Custom, rem;
+ Sub, &Custom, Custom, sub;
+
+ Add, &Custom, &Custom, add;
+ Div, &Custom, &Custom, div;
+ Mul, &Custom, &Custom, mul;
+ Rem, &Custom, &Custom, rem;
+ Sub, &Custom, &Custom, sub;
+);
+
+impl_assign_arith!(
+ AddAssign, Custom, Custom, add_assign;
+ DivAssign, Custom, Custom, div_assign;
+ MulAssign, Custom, Custom, mul_assign;
+ RemAssign, Custom, Custom, rem_assign;
+ SubAssign, Custom, Custom, sub_assign;
+
+ AddAssign, Custom, &Custom, add_assign;
+ DivAssign, Custom, &Custom, div_assign;
+ MulAssign, Custom, &Custom, mul_assign;
+ RemAssign, Custom, &Custom, rem_assign;
+ SubAssign, Custom, &Custom, sub_assign;
+
+ AddAssign, &Custom, Custom, add_assign;
+ DivAssign, &Custom, Custom, div_assign;
+ MulAssign, &Custom, Custom, mul_assign;
+ RemAssign, &Custom, Custom, rem_assign;
+ SubAssign, &Custom, Custom, sub_assign;
+
+ AddAssign, &Custom, &Custom, add_assign;
+ DivAssign, &Custom, &Custom, div_assign;
+ MulAssign, &Custom, &Custom, mul_assign;
+ RemAssign, &Custom, &Custom, rem_assign;
+ SubAssign, &Custom, &Custom, sub_assign;
);
+impl core::ops::Neg for Custom {
+ type Output = Custom;
+ fn neg(self) -> Self::Output {
+ todo!()
+ }
+}
+impl core::ops::Neg for &Custom {
+ type Output = Custom;
+ fn neg(self) -> Self::Output {
+ todo!()
+ }
+}
+
pub fn association_with_structures_should_not_trigger_the_lint() {
enum Foo {
Bar = -2,
_n *= &0;
_n *= 1;
_n *= &1;
+ _n += -0;
+ _n += &-0;
+ _n -= -0;
+ _n -= &-0;
+ _n /= -99;
+ _n /= &-99;
+ _n %= -99;
+ _n %= &-99;
+ _n *= -0;
+ _n *= &-0;
+ _n *= -1;
+ _n *= &-1;
// Binary
_n = _n + 0;
_n = -&i32::MIN;
}
-pub fn runtime_ops() {
+pub fn unknown_ops_or_runtime_ops_that_can_overflow() {
let mut _n = i32::MAX;
+ let mut _custom = Custom;
// Assign
_n += 1;
_n %= &0;
_n *= 2;
_n *= &2;
+ _n += -1;
+ _n += &-1;
+ _n -= -1;
+ _n -= &-1;
+ _n /= -0;
+ _n /= &-0;
+ _n %= -0;
+ _n %= &-0;
+ _n *= -2;
+ _n *= &-2;
+ _custom += Custom;
+ _custom += &Custom;
+ _custom -= Custom;
+ _custom -= &Custom;
+ _custom /= Custom;
+ _custom /= &Custom;
+ _custom %= Custom;
+ _custom %= &Custom;
+ _custom *= Custom;
+ _custom *= &Custom;
+ _custom += -Custom;
+ _custom += &-Custom;
+ _custom -= -Custom;
+ _custom -= &-Custom;
+ _custom /= -Custom;
+ _custom /= &-Custom;
+ _custom %= -Custom;
+ _custom %= &-Custom;
+ _custom *= -Custom;
+ _custom *= &-Custom;
// Binary
_n = _n + 1;
_n = 23 + &85;
_n = &23 + 85;
_n = &23 + &85;
-
- // Custom
- let _ = Custom + 0;
- let _ = Custom + 1;
- let _ = Custom + 2;
- let _ = Custom + 0.0;
- let _ = Custom + 1.0;
- let _ = Custom + 2.0;
- let _ = Custom - 0;
- let _ = Custom - 1;
- let _ = Custom - 2;
- let _ = Custom - 0.0;
- let _ = Custom - 1.0;
- let _ = Custom - 2.0;
- let _ = Custom / 0;
- let _ = Custom / 1;
- let _ = Custom / 2;
- let _ = Custom / 0.0;
- let _ = Custom / 1.0;
- let _ = Custom / 2.0;
- let _ = Custom * 0;
- let _ = Custom * 1;
- let _ = Custom * 2;
- let _ = Custom * 0.0;
- let _ = Custom * 1.0;
- let _ = Custom * 2.0;
+ _custom = _custom + _custom;
+ _custom = _custom + &_custom;
+ _custom = Custom + _custom;
+ _custom = &Custom + _custom;
+ _custom = _custom - Custom;
+ _custom = _custom - &Custom;
+ _custom = Custom - _custom;
+ _custom = &Custom - _custom;
+ _custom = _custom / Custom;
+ _custom = _custom / &Custom;
+ _custom = _custom % Custom;
+ _custom = _custom % &Custom;
+ _custom = _custom * Custom;
+ _custom = _custom * &Custom;
+ _custom = Custom * _custom;
+ _custom = &Custom * _custom;
+ _custom = Custom + &Custom;
+ _custom = &Custom + Custom;
+ _custom = &Custom + &Custom;
// Unary
_n = -_n;
_n = -&_n;
+ _custom = -_custom;
+ _custom = -&_custom;
+}
+
+// Copied and pasted from the `integer_arithmetic` lint for comparison.
+pub fn integer_arithmetic() {
+ let mut i = 1i32;
+ let mut var1 = 0i32;
+ let mut var2 = -1i32;
+
+ 1 + i;
+ i * 2;
+ 1 % i / 2;
+ i - 2 + 2 - i;
+ -i;
+ i >> 1;
+ i << 1;
+
+ -1;
+ -(-1);
+
+ i & 1;
+ i | 1;
+ i ^ 1;
+
+ i += 1;
+ i -= 1;
+ i *= 2;
+ i /= 2;
+ i /= 0;
+ i /= -1;
+ i /= var1;
+ i /= var2;
+ i %= 2;
+ i %= 0;
+ i %= -1;
+ i %= var1;
+ i %= var2;
+ i <<= 3;
+ i >>= 2;
+
+ i |= 1;
+ i &= 1;
+ i ^= i;
}
fn main() {}
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:165:5
+ --> $DIR/arithmetic_side_effects.rs:243:5
|
LL | _n += 1;
| ^^^^^^^
= note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:166:5
+ --> $DIR/arithmetic_side_effects.rs:244:5
|
LL | _n += &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:167:5
+ --> $DIR/arithmetic_side_effects.rs:245:5
|
LL | _n -= 1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:168:5
+ --> $DIR/arithmetic_side_effects.rs:246:5
|
LL | _n -= &1;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:169:5
+ --> $DIR/arithmetic_side_effects.rs:247:5
|
LL | _n /= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:170:5
+ --> $DIR/arithmetic_side_effects.rs:248:5
|
LL | _n /= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:171:5
+ --> $DIR/arithmetic_side_effects.rs:249:5
|
LL | _n %= 0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:172:5
+ --> $DIR/arithmetic_side_effects.rs:250:5
|
LL | _n %= &0;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:173:5
+ --> $DIR/arithmetic_side_effects.rs:251:5
|
LL | _n *= 2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:174:5
+ --> $DIR/arithmetic_side_effects.rs:252:5
|
LL | _n *= &2;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:177:10
+ --> $DIR/arithmetic_side_effects.rs:253:5
+ |
+LL | _n += -1;
+ | ^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:254:5
+ |
+LL | _n += &-1;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:255:5
+ |
+LL | _n -= -1;
+ | ^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:256:5
+ |
+LL | _n -= &-1;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:257:5
+ |
+LL | _n /= -0;
+ | ^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:258:5
+ |
+LL | _n /= &-0;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:259:5
+ |
+LL | _n %= -0;
+ | ^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:260:5
+ |
+LL | _n %= &-0;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:261:5
+ |
+LL | _n *= -2;
+ | ^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:262:5
+ |
+LL | _n *= &-2;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:263:5
+ |
+LL | _custom += Custom;
+ | ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:264:5
+ |
+LL | _custom += &Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:265:5
+ |
+LL | _custom -= Custom;
+ | ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:266:5
+ |
+LL | _custom -= &Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:267:5
+ |
+LL | _custom /= Custom;
+ | ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:268:5
+ |
+LL | _custom /= &Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:269:5
+ |
+LL | _custom %= Custom;
+ | ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:270:5
+ |
+LL | _custom %= &Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:271:5
+ |
+LL | _custom *= Custom;
+ | ^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:272:5
+ |
+LL | _custom *= &Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:273:5
+ |
+LL | _custom += -Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:274:5
+ |
+LL | _custom += &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:275:5
+ |
+LL | _custom -= -Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:276:5
+ |
+LL | _custom -= &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:277:5
+ |
+LL | _custom /= -Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:278:5
+ |
+LL | _custom /= &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:279:5
+ |
+LL | _custom %= -Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:280:5
+ |
+LL | _custom %= &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:281:5
+ |
+LL | _custom *= -Custom;
+ | ^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:282:5
+ |
+LL | _custom *= &-Custom;
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:285:10
|
LL | _n = _n + 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:178:10
+ --> $DIR/arithmetic_side_effects.rs:286:10
|
LL | _n = _n + &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:179:10
+ --> $DIR/arithmetic_side_effects.rs:287:10
|
LL | _n = 1 + _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:180:10
+ --> $DIR/arithmetic_side_effects.rs:288:10
|
LL | _n = &1 + _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:181:10
+ --> $DIR/arithmetic_side_effects.rs:289:10
|
LL | _n = _n - 1;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:182:10
+ --> $DIR/arithmetic_side_effects.rs:290:10
|
LL | _n = _n - &1;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:183:10
+ --> $DIR/arithmetic_side_effects.rs:291:10
|
LL | _n = 1 - _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:184:10
+ --> $DIR/arithmetic_side_effects.rs:292:10
|
LL | _n = &1 - _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:185:10
+ --> $DIR/arithmetic_side_effects.rs:293:10
|
LL | _n = _n / 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:186:10
+ --> $DIR/arithmetic_side_effects.rs:294:10
|
LL | _n = _n / &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:187:10
+ --> $DIR/arithmetic_side_effects.rs:295:10
|
LL | _n = _n % 0;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:188:10
+ --> $DIR/arithmetic_side_effects.rs:296:10
|
LL | _n = _n % &0;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:189:10
+ --> $DIR/arithmetic_side_effects.rs:297:10
|
LL | _n = _n * 2;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:190:10
+ --> $DIR/arithmetic_side_effects.rs:298:10
|
LL | _n = _n * &2;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:191:10
+ --> $DIR/arithmetic_side_effects.rs:299:10
|
LL | _n = 2 * _n;
| ^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:192:10
+ --> $DIR/arithmetic_side_effects.rs:300:10
|
LL | _n = &2 * _n;
| ^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:193:10
+ --> $DIR/arithmetic_side_effects.rs:301:10
|
LL | _n = 23 + &85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:194:10
+ --> $DIR/arithmetic_side_effects.rs:302:10
|
LL | _n = &23 + 85;
| ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:195:10
+ --> $DIR/arithmetic_side_effects.rs:303:10
|
LL | _n = &23 + &85;
| ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:198:13
+ --> $DIR/arithmetic_side_effects.rs:304:15
|
-LL | let _ = Custom + 0;
- | ^^^^^^^^^^
+LL | _custom = _custom + _custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:199:13
+ --> $DIR/arithmetic_side_effects.rs:305:15
|
-LL | let _ = Custom + 1;
- | ^^^^^^^^^^
+LL | _custom = _custom + &_custom;
+ | ^^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:200:13
+ --> $DIR/arithmetic_side_effects.rs:306:15
|
-LL | let _ = Custom + 2;
- | ^^^^^^^^^^
+LL | _custom = Custom + _custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:201:13
+ --> $DIR/arithmetic_side_effects.rs:307:15
|
-LL | let _ = Custom + 0.0;
- | ^^^^^^^^^^^^
+LL | _custom = &Custom + _custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:202:13
+ --> $DIR/arithmetic_side_effects.rs:308:15
|
-LL | let _ = Custom + 1.0;
- | ^^^^^^^^^^^^
+LL | _custom = _custom - Custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:203:13
+ --> $DIR/arithmetic_side_effects.rs:309:15
|
-LL | let _ = Custom + 2.0;
- | ^^^^^^^^^^^^
+LL | _custom = _custom - &Custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:204:13
+ --> $DIR/arithmetic_side_effects.rs:310:15
|
-LL | let _ = Custom - 0;
- | ^^^^^^^^^^
+LL | _custom = Custom - _custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:205:13
+ --> $DIR/arithmetic_side_effects.rs:311:15
|
-LL | let _ = Custom - 1;
- | ^^^^^^^^^^
+LL | _custom = &Custom - _custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:206:13
+ --> $DIR/arithmetic_side_effects.rs:312:15
|
-LL | let _ = Custom - 2;
- | ^^^^^^^^^^
+LL | _custom = _custom / Custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:207:13
+ --> $DIR/arithmetic_side_effects.rs:313:15
|
-LL | let _ = Custom - 0.0;
- | ^^^^^^^^^^^^
+LL | _custom = _custom / &Custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:208:13
+ --> $DIR/arithmetic_side_effects.rs:314:15
|
-LL | let _ = Custom - 1.0;
- | ^^^^^^^^^^^^
+LL | _custom = _custom % Custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:209:13
+ --> $DIR/arithmetic_side_effects.rs:315:15
|
-LL | let _ = Custom - 2.0;
- | ^^^^^^^^^^^^
+LL | _custom = _custom % &Custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:210:13
+ --> $DIR/arithmetic_side_effects.rs:316:15
|
-LL | let _ = Custom / 0;
- | ^^^^^^^^^^
+LL | _custom = _custom * Custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:211:13
+ --> $DIR/arithmetic_side_effects.rs:317:15
|
-LL | let _ = Custom / 1;
- | ^^^^^^^^^^
+LL | _custom = _custom * &Custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:212:13
+ --> $DIR/arithmetic_side_effects.rs:318:15
|
-LL | let _ = Custom / 2;
- | ^^^^^^^^^^
+LL | _custom = Custom * _custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:213:13
+ --> $DIR/arithmetic_side_effects.rs:319:15
|
-LL | let _ = Custom / 0.0;
- | ^^^^^^^^^^^^
+LL | _custom = &Custom * _custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:214:13
+ --> $DIR/arithmetic_side_effects.rs:320:15
|
-LL | let _ = Custom / 1.0;
- | ^^^^^^^^^^^^
+LL | _custom = Custom + &Custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:215:13
+ --> $DIR/arithmetic_side_effects.rs:321:15
|
-LL | let _ = Custom / 2.0;
- | ^^^^^^^^^^^^
+LL | _custom = &Custom + Custom;
+ | ^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:216:13
+ --> $DIR/arithmetic_side_effects.rs:322:15
|
-LL | let _ = Custom * 0;
- | ^^^^^^^^^^
+LL | _custom = &Custom + &Custom;
+ | ^^^^^^^^^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:217:13
+ --> $DIR/arithmetic_side_effects.rs:325:10
|
-LL | let _ = Custom * 1;
- | ^^^^^^^^^^
+LL | _n = -_n;
+ | ^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:218:13
+ --> $DIR/arithmetic_side_effects.rs:326:10
|
-LL | let _ = Custom * 2;
- | ^^^^^^^^^^
+LL | _n = -&_n;
+ | ^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:219:13
+ --> $DIR/arithmetic_side_effects.rs:327:15
|
-LL | let _ = Custom * 0.0;
- | ^^^^^^^^^^^^
+LL | _custom = -_custom;
+ | ^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:220:13
+ --> $DIR/arithmetic_side_effects.rs:328:15
|
-LL | let _ = Custom * 1.0;
- | ^^^^^^^^^^^^
+LL | _custom = -&_custom;
+ | ^^^^^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:221:13
+ --> $DIR/arithmetic_side_effects.rs:337:5
|
-LL | let _ = Custom * 2.0;
- | ^^^^^^^^^^^^
+LL | 1 + i;
+ | ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:224:10
+ --> $DIR/arithmetic_side_effects.rs:338:5
|
-LL | _n = -_n;
- | ^^^
+LL | i * 2;
+ | ^^^^^
error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:225:10
+ --> $DIR/arithmetic_side_effects.rs:340:5
|
-LL | _n = -&_n;
- | ^^^^
+LL | i - 2 + 2 - i;
+ | ^^^^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:341:5
+ |
+LL | -i;
+ | ^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:342:5
+ |
+LL | i >> 1;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:343:5
+ |
+LL | i << 1;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:352:5
+ |
+LL | i += 1;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:353:5
+ |
+LL | i -= 1;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:354:5
+ |
+LL | i *= 2;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:356:5
+ |
+LL | i /= 0;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:358:5
+ |
+LL | i /= var1;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:359:5
+ |
+LL | i /= var2;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:361:5
+ |
+LL | i %= 0;
+ | ^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:363:5
+ |
+LL | i %= var1;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:364:5
+ |
+LL | i %= var2;
+ | ^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:365:5
+ |
+LL | i <<= 3;
+ | ^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects.rs:366:5
+ |
+LL | i >>= 2;
+ | ^^^^^^^
-error: aborting due to 55 previous errors
+error: aborting due to 99 previous errors
fn main() {
let _string: Box<String> = Box::default();
let _byte = Box::<u8>::default();
- let _vec = Box::<std::vec::Vec<u8>>::default();
+ let _vec = Box::<Vec<u8>>::default();
let _impl = Box::<ImplementsDefault>::default();
let _impl2 = Box::<ImplementsDefault>::default();
let _impl3: Box<ImplementsDefault> = Box::default();
let _own = Box::new(OwnDefault::default()); // should not lint
- let _in_macro = outer!(Box::<std::string::String>::default());
- let _string_default = outer!(Box::<std::string::String>::default());
+ let _in_macro = outer!(Box::<String>::default());
+ let _string_default = outer!(Box::<String>::default());
let _vec2: Box<Vec<ImplementsDefault>> = Box::default();
let _vec3: Box<Vec<bool>> = Box::default();
- let _vec4: Box<_> = Box::<std::vec::Vec<bool>>::default();
+ let _vec4: Box<_> = Box::<Vec<bool>>::default();
let _more = ret_ty_fn();
call_ty_fn(Box::default());
}
fn issue_9621_dyn_trait() {
let _: Box<dyn Read> = Box::<ImplementsDefault>::default();
+ issue_10089();
+}
+
+fn issue_10089() {
+ let _closure = || {
+ #[derive(Default)]
+ struct WeirdPathed;
+
+ let _ = Box::<WeirdPathed>::default();
+ };
}
fn issue_9621_dyn_trait() {
let _: Box<dyn Read> = Box::new(ImplementsDefault::default());
+ issue_10089();
+}
+
+fn issue_10089() {
+ let _closure = || {
+ #[derive(Default)]
+ struct WeirdPathed;
+
+ let _ = Box::new(WeirdPathed::default());
+ };
}
--> $DIR/box_default.rs:24:16
|
LL | let _vec = Box::new(Vec::<u8>::new());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::vec::Vec<u8>>::default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Vec<u8>>::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:25:17
--> $DIR/box_default.rs:29:28
|
LL | let _in_macro = outer!(Box::new(String::new()));
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::string::String>::default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<String>::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:30:34
|
LL | let _string_default = outer!(Box::new(String::from("")));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::string::String>::default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<String>::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:31:46
--> $DIR/box_default.rs:33:25
|
LL | let _vec4: Box<_> = Box::new(Vec::from([false; 0]));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<std::vec::Vec<bool>>::default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<Vec<bool>>::default()`
error: `Box::new(_)` of default value
--> $DIR/box_default.rs:35:16
LL | let _: Box<dyn Read> = Box::new(ImplementsDefault::default());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<ImplementsDefault>::default()`
-error: aborting due to 14 previous errors
+error: `Box::new(_)` of default value
+ --> $DIR/box_default.rs:65:17
+ |
+LL | let _ = Box::new(WeirdPathed::default());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Box::<WeirdPathed>::default()`
+
+error: aborting due to 15 previous errors
--- /dev/null
+// run-rustfix
+#![warn(clippy::case_sensitive_file_extension_comparisons)]
+
+use std::string::String;
+
+struct TestStruct;
+
+impl TestStruct {
+ fn ends_with(self, _arg: &str) {}
+}
+
+#[allow(dead_code)]
+fn is_rust_file(filename: &str) -> bool {
+ std::path::Path::new(filename)
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
+}
+
+fn main() {
+ // std::string::String and &str should trigger the lint failure with .ext12
+ let _ = std::path::Path::new(&String::new())
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
+ let _ = std::path::Path::new("str")
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
+
+ // The fixup should preserve the indentation level
+ {
+ let _ = std::path::Path::new("str")
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
+ }
+
+ // The test struct should not trigger the lint failure with .ext12
+ TestStruct {}.ends_with(".ext12");
+
+ // std::string::String and &str should trigger the lint failure with .EXT12
+ let _ = std::path::Path::new(&String::new())
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12"));
+ let _ = std::path::Path::new("str")
+ .extension()
+ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12"));
+
+ // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase
+ let _ = String::new().to_lowercase().ends_with(".EXT12");
+ let _ = String::new().to_uppercase().ends_with(".EXT12");
+
+ // The test struct should not trigger the lint failure with .EXT12
+ TestStruct {}.ends_with(".EXT12");
+
+ // Should not trigger the lint failure with .eXT12
+ let _ = String::new().ends_with(".eXT12");
+ let _ = "str".ends_with(".eXT12");
+ TestStruct {}.ends_with(".eXT12");
+
+ // Should not trigger the lint failure with .EXT123 (too long)
+ let _ = String::new().ends_with(".EXT123");
+ let _ = "str".ends_with(".EXT123");
+ TestStruct {}.ends_with(".EXT123");
+
+ // Shouldn't fail if it doesn't start with a dot
+ let _ = String::new().ends_with("a.ext");
+ let _ = "str".ends_with("a.extA");
+ TestStruct {}.ends_with("a.ext");
+}
+// run-rustfix
#![warn(clippy::case_sensitive_file_extension_comparisons)]
use std::string::String;
struct TestStruct;
impl TestStruct {
- fn ends_with(self, arg: &str) {}
+ fn ends_with(self, _arg: &str) {}
}
+#[allow(dead_code)]
fn is_rust_file(filename: &str) -> bool {
filename.ends_with(".rs")
}
let _ = String::new().ends_with(".ext12");
let _ = "str".ends_with(".ext12");
+ // The fixup should preserve the indentation level
+ {
+ let _ = "str".ends_with(".ext12");
+ }
+
// The test struct should not trigger the lint failure with .ext12
TestStruct {}.ends_with(".ext12");
let _ = String::new().ends_with(".EXT12");
let _ = "str".ends_with(".EXT12");
+ // Should not trigger the lint failure because of the calls to to_lowercase and to_uppercase
+ let _ = String::new().to_lowercase().ends_with(".EXT12");
+ let _ = String::new().to_uppercase().ends_with(".EXT12");
+
// The test struct should not trigger the lint failure with .EXT12
TestStruct {}.ends_with(".EXT12");
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:14:5
|
LL | filename.ends_with(".rs")
- | ^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
= note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings`
+help: use std::path::Path
+ |
+LL ~ std::path::Path::new(filename)
+LL + .extension()
+LL + .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
+ |
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:19:13
|
LL | let _ = String::new().ends_with(".ext12");
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
+help: use std::path::Path
+ |
+LL ~ let _ = std::path::Path::new(&String::new())
+LL + .extension()
+LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
+ |
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:20:13
|
LL | let _ = "str".ends_with(".ext12");
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
+help: use std::path::Path
+ |
+LL ~ let _ = std::path::Path::new("str")
+LL + .extension()
+LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
+ |
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:24:17
+ |
+LL | let _ = "str".ends_with(".ext12");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using a case-insensitive comparison instead
+help: use std::path::Path
+ |
+LL ~ let _ = std::path::Path::new("str")
+LL + .extension()
+LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
+ |
+
+error: case-sensitive file extension comparison
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:31:13
|
LL | let _ = String::new().ends_with(".EXT12");
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
+help: use std::path::Path
+ |
+LL ~ let _ = std::path::Path::new(&String::new())
+LL + .extension()
+LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12"));
+ |
error: case-sensitive file extension comparison
- --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19
+ --> $DIR/case_sensitive_file_extension_comparisons.rs:32:13
|
LL | let _ = "str".ends_with(".EXT12");
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using a case-insensitive comparison instead
+help: use std::path::Path
+ |
+LL ~ let _ = std::path::Path::new("str")
+LL + .extension()
+LL ~ .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12"));
+ |
-error: aborting due to 5 previous errors
+error: aborting due to 6 previous errors
LL | vec.push(42.clone());
| ^^^^^^^^^^ help: try removing the `clone` call: `42`
-error: using `clone` on type `std::option::Option<i32>` which implements the `Copy` trait
+error: using `clone` on type `Option<i32>` which implements the `Copy` trait
--> $DIR/clone_on_copy.rs:77:17
|
LL | let value = opt.clone()?; // operator precedence needed (*opt)?
--> $DIR/ice-6252.rs:10:63
|
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
- | - ^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `, VAL`
+ | ^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | impl<N, M, VAL> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | +++++
error[E0046]: not all trait items implemented, missing: `VAL`
--> $DIR/ice-6252.rs:10:1
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:5:22
|
LL | if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::dbg-macro` implied by `-D warnings`
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | if let Some(n) = n.checked_sub(4) { n } else { n }
| ~~~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:9:8
|
LL | if dbg!(n <= 1) {
| ^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | if n <= 1 {
| ~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:10:9
|
LL | dbg!(1)
| ^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 1
|
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:12:9
|
LL | dbg!(n * factorial(n - 1))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | n * factorial(n - 1)
|
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:17:5
|
LL | dbg!(42);
| ^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 42;
| ~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:18:5
|
LL | dbg!(dbg!(dbg!(42)));
| ^^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | dbg!(dbg!(42));
| ~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:19:14
|
LL | foo(3) + dbg!(factorial(4));
| ^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | foo(3) + factorial(4);
| ~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:20:5
|
LL | dbg!(1, 2, dbg!(3, 4));
| ^^^^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | (1, 2, dbg!(3, 4));
| ~~~~~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:21:5
|
LL | dbg!(1, 2, 3, 4, 5);
| ^^^^^^^^^^^^^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | (1, 2, 3, 4, 5);
| ~~~~~~~~~~~~~~~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:41:9
|
LL | dbg!(2);
| ^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 2;
| ~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:47:5
|
LL | dbg!(1);
| ^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 1;
| ~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:52:5
|
LL | dbg!(1);
| ^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 1;
| ~
-error: `dbg!` macro is intended as a debugging tool
+error: the `dbg!` macro is intended as a debugging tool
--> $DIR/dbg_macro.rs:58:9
|
LL | dbg!(1);
| ^^^^^^^
|
-help: ensure to avoid having uses of it in version control
+help: remove the invocation before committing it to a version control system
|
LL | 1;
| ~
pub unsafe trait Freeze {}
#[lang = "start"]
-fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
0
}
use std::string;
fn main() {
- let s1: String = std::string::String::default();
+ let s1: String = String::default();
let s2 = String::default();
- let s3: String = std::string::String::default();
+ let s3: String = String::default();
- let s4: String = std::string::String::default();
+ let s4: String = String::default();
let s5 = string::String::default();
- let s6: String = std::string::String::default();
+ let s6: String = String::default();
let s7 = std::string::String::default();
-error: calling `std::string::String::default()` is more clear than this expression
+error: calling `String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:15:22
|
LL | let s1: String = Default::default();
- | ^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+ | ^^^^^^^^^^^^^^^^^^ help: try: `String::default()`
|
note: the lint level is defined here
--> $DIR/default_trait_access.rs:3:9
LL | #![deny(clippy::default_trait_access)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: calling `std::string::String::default()` is more clear than this expression
+error: calling `String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:19:22
|
LL | let s3: String = D2::default();
- | ^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+ | ^^^^^^^^^^^^^ help: try: `String::default()`
-error: calling `std::string::String::default()` is more clear than this expression
+error: calling `String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:21:22
|
LL | let s4: String = std::default::Default::default();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()`
-error: calling `std::string::String::default()` is more clear than this expression
+error: calling `String::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:25:22
|
LL | let s6: String = default::Default::default();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::string::String::default()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `String::default()`
error: calling `GenericDerivedDefault::default()` is more clear than this expression
--> $DIR/default_trait_access.rs:35:46
}
}
+#[derive(Default)]
+pub enum SimpleEnum {
+ Foo,
+ #[default]
+ Bar,
+}
+
+
+
+pub enum NonExhaustiveEnum {
+ Foo,
+ #[non_exhaustive]
+ Bar,
+}
+
+impl Default for NonExhaustiveEnum {
+ fn default() -> Self {
+ NonExhaustiveEnum::Bar
+ }
+}
+
fn main() {}
}
}
+pub enum SimpleEnum {
+ Foo,
+ Bar,
+}
+
+impl Default for SimpleEnum {
+ fn default() -> Self {
+ SimpleEnum::Bar
+ }
+}
+
+pub enum NonExhaustiveEnum {
+ Foo,
+ #[non_exhaustive]
+ Bar,
+}
+
+impl Default for NonExhaustiveEnum {
+ fn default() -> Self {
+ NonExhaustiveEnum::Bar
+ }
+}
+
fn main() {}
LL | #[derive(Default)]
|
-error: aborting due to 7 previous errors
+error: this `impl` can be derived
+ --> $DIR/derivable_impls.rs:252:1
+ |
+LL | / impl Default for SimpleEnum {
+LL | | fn default() -> Self {
+LL | | SimpleEnum::Bar
+LL | | }
+LL | | }
+ | |_^
+ |
+ = help: remove the manual implementation...
+help: ...and instead derive it...
+ |
+LL | #[derive(Default)]
+ |
+help: ...and mark the default variant
+ |
+LL ~ #[default]
+LL ~ Bar,
+ |
+
+error: aborting due to 8 previous errors
}
}
+// https://github.com/rust-lang/rust-clippy/issues/10188
+#[repr(packed)]
+#[derive(Copy)]
+struct Packed<T>(T);
+
+impl<T: Copy> Clone for Packed<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
fn main() {}
+++ /dev/null
-#![allow(clippy::derive_partial_eq_without_eq)]
-
-#[derive(PartialEq, Hash)]
-struct Foo;
-
-impl PartialEq<u64> for Foo {
- fn eq(&self, _: &u64) -> bool {
- true
- }
-}
-
-#[derive(Hash)]
-struct Bar;
-
-impl PartialEq for Bar {
- fn eq(&self, _: &Bar) -> bool {
- true
- }
-}
-
-#[derive(Hash)]
-struct Baz;
-
-impl PartialEq<Baz> for Baz {
- fn eq(&self, _: &Baz) -> bool {
- true
- }
-}
-
-#[derive(PartialEq)]
-struct Bah;
-
-impl std::hash::Hash for Bah {
- fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
-}
-
-#[derive(PartialEq)]
-struct Foo2;
-
-trait Hash {}
-
-// We don't want to lint on user-defined traits called `Hash`
-impl Hash for Foo2 {}
-
-mod use_hash {
- use std::hash::{Hash, Hasher};
-
- #[derive(PartialEq)]
- struct Foo3;
-
- impl Hash for Foo3 {
- fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
- }
-}
-
-fn main() {}
+++ /dev/null
-error: you are deriving `Hash` but have implemented `PartialEq` explicitly
- --> $DIR/derive_hash_xor_eq.rs:12:10
- |
-LL | #[derive(Hash)]
- | ^^^^
- |
-note: `PartialEq` implemented here
- --> $DIR/derive_hash_xor_eq.rs:15:1
- |
-LL | impl PartialEq for Bar {
- | ^^^^^^^^^^^^^^^^^^^^^^
- = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default
- = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: you are deriving `Hash` but have implemented `PartialEq` explicitly
- --> $DIR/derive_hash_xor_eq.rs:21:10
- |
-LL | #[derive(Hash)]
- | ^^^^
- |
-note: `PartialEq` implemented here
- --> $DIR/derive_hash_xor_eq.rs:24:1
- |
-LL | impl PartialEq<Baz> for Baz {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: you are implementing `Hash` explicitly but have derived `PartialEq`
- --> $DIR/derive_hash_xor_eq.rs:33:1
- |
-LL | / impl std::hash::Hash for Bah {
-LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
-LL | | }
- | |_^
- |
-note: `PartialEq` implemented here
- --> $DIR/derive_hash_xor_eq.rs:30:10
- |
-LL | #[derive(PartialEq)]
- | ^^^^^^^^^
- = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: you are implementing `Hash` explicitly but have derived `PartialEq`
- --> $DIR/derive_hash_xor_eq.rs:51:5
- |
-LL | / impl Hash for Foo3 {
-LL | | fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
-LL | | }
- | |_____^
- |
-note: `PartialEq` implemented here
- --> $DIR/derive_hash_xor_eq.rs:48:14
- |
-LL | #[derive(PartialEq)]
- | ^^^^^^^^^
- = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 4 previous errors
-
--- /dev/null
+#![allow(clippy::derive_partial_eq_without_eq)]
+
+#[derive(PartialEq, Hash)]
+struct Foo;
+
+impl PartialEq<u64> for Foo {
+ fn eq(&self, _: &u64) -> bool {
+ true
+ }
+}
+
+#[derive(Hash)]
+struct Bar;
+
+impl PartialEq for Bar {
+ fn eq(&self, _: &Bar) -> bool {
+ true
+ }
+}
+
+#[derive(Hash)]
+struct Baz;
+
+impl PartialEq<Baz> for Baz {
+ fn eq(&self, _: &Baz) -> bool {
+ true
+ }
+}
+
+// Implementing `Hash` with a derived `PartialEq` is fine. See #2627
+
+#[derive(PartialEq)]
+struct Bah;
+
+impl std::hash::Hash for Bah {
+ fn hash<H: std::hash::Hasher>(&self, _: &mut H) {}
+}
+
+fn main() {}
--- /dev/null
+error: you are deriving `Hash` but have implemented `PartialEq` explicitly
+ --> $DIR/derived_hash_with_manual_eq.rs:12:10
+ |
+LL | #[derive(Hash)]
+ | ^^^^
+ |
+note: `PartialEq` implemented here
+ --> $DIR/derived_hash_with_manual_eq.rs:15:1
+ |
+LL | impl PartialEq for Bar {
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::derived_hash_with_manual_eq)]` on by default
+ = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: you are deriving `Hash` but have implemented `PartialEq` explicitly
+ --> $DIR/derived_hash_with_manual_eq.rs:21:10
+ |
+LL | #[derive(Hash)]
+ | ^^^^
+ |
+note: `PartialEq` implemented here
+ --> $DIR/derived_hash_with_manual_eq.rs:24:1
+ |
+LL | impl PartialEq<Baz> for Baz {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
produce_half_owl_ok().map(drop)?;
Ok(1)
}
+
+#[allow(unused)]
+#[allow(clippy::unit_cmp)]
+fn issue10122(x: u8) {
+ // This is a function which returns a reference and has a side-effect, which means
+ // that calling drop() on the function is considered an idiomatic way of achieving the side-effect
+ // in a match arm.
+ fn println_and<T>(t: &T) -> &T {
+ println!("foo");
+ t
+ }
+
+ match x {
+ 0 => drop(println_and(&12)), // Don't lint (copy type), we only care about side-effects
+ 1 => drop(println_and(&String::new())), // Don't lint (no copy type), we only care about side-effects
+ 2 => {
+ drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block
+ },
+ 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm`
+ 4 => drop(&2), // Lint, not a fn/method call
+ _ => (),
+ }
+}
LL | std::mem::drop(&SomeStruct);
| ^^^^^^^^^^^
-error: aborting due to 9 previous errors
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:91:13
+ |
+LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&i32`
+ --> $DIR/drop_ref.rs:91:18
+ |
+LL | drop(println_and(&13)); // Lint, even if we only care about the side-effect, it's already in a block
+ | ^^^^^^^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:93:14
+ |
+LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm`
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: argument has type `&i32`
+ --> $DIR/drop_ref.rs:93:19
+ |
+LL | 3 if drop(println_and(&14)) == () => (), // Lint, idiomatic use is only in body of `Arm`
+ | ^^^^^^^^^^^^^^^^
+
+error: calls to `std::mem::drop` with a reference instead of an owned value. Dropping a reference does nothing
+ --> $DIR/drop_ref.rs:94:14
+ |
+LL | 4 => drop(&2), // Lint, not a fn/method call
+ | ^^^^^^^^
+ |
+note: argument has type `&i32`
+ --> $DIR/drop_ref.rs:94:19
+ |
+LL | 4 => drop(&2), // Lint, not a fn/method call
+ | ^^
+
+error: aborting due to 12 previous errors
}
}
}
+
+struct Collection {
+ items: Vec<i32>,
+ len: usize,
+}
+
+impl Default for Collection {
+ fn default() -> Self {
+ Self {
+ items: vec![1, 2, 3],
+ len: 0,
+ }
+ }
+}
+
+#[allow(clippy::redundant_closure_call)]
+fn issue10136() {
+ let mut c = Collection::default();
+ // don't lint, since c.items was used to calculate this value
+ c.len = (|| c.items.len())();
+}
// run-rustfix
#![warn(clippy::iter_kv_map)]
-#![allow(clippy::redundant_clone)]
-#![allow(clippy::suspicious_map)]
-#![allow(clippy::map_identity)]
+#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)]
use std::collections::{BTreeMap, HashMap};
fn main() {
let get_key = |(key, _val)| key;
+ fn ref_acceptor(v: &u32) -> u32 {
+ *v
+ }
let map: HashMap<u32, u32> = HashMap::new();
let _ = map.keys().map(|key| key * 9).count();
let _ = map.values().map(|value| value * 17).count();
+ // Preserve the ref in the fix.
+ let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count();
+
+ // Preserve the mut in the fix.
+ let _ = map
+ .clone().into_values().map(|mut val| {
+ val += 2;
+ val
+ })
+ .count();
+
+ // Don't let a mut interfere.
+ let _ = map.clone().into_values().count();
+
let map: BTreeMap<u32, u32> = BTreeMap::new();
let _ = map.keys().collect::<Vec<_>>();
// Lint
let _ = map.keys().map(|key| key * 9).count();
let _ = map.values().map(|value| value * 17).count();
+
+ // Preserve the ref in the fix.
+ let _ = map.clone().into_values().map(|ref val| ref_acceptor(val)).count();
+
+ // Preserve the mut in the fix.
+ let _ = map
+ .clone().into_values().map(|mut val| {
+ val += 2;
+ val
+ })
+ .count();
+
+ // Don't let a mut interfere.
+ let _ = map.clone().into_values().count();
}
// run-rustfix
#![warn(clippy::iter_kv_map)]
-#![allow(clippy::redundant_clone)]
-#![allow(clippy::suspicious_map)]
-#![allow(clippy::map_identity)]
+#![allow(unused_mut, clippy::redundant_clone, clippy::suspicious_map, clippy::map_identity)]
use std::collections::{BTreeMap, HashMap};
fn main() {
let get_key = |(key, _val)| key;
+ fn ref_acceptor(v: &u32) -> u32 {
+ *v
+ }
let map: HashMap<u32, u32> = HashMap::new();
let _ = map.iter().map(|(key, _value)| key * 9).count();
let _ = map.iter().map(|(_key, value)| value * 17).count();
+ // Preserve the ref in the fix.
+ let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
+
+ // Preserve the mut in the fix.
+ let _ = map
+ .clone()
+ .into_iter()
+ .map(|(_, mut val)| {
+ val += 2;
+ val
+ })
+ .count();
+
+ // Don't let a mut interfere.
+ let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
+
let map: BTreeMap<u32, u32> = BTreeMap::new();
let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
// Lint
let _ = map.iter().map(|(key, _value)| key * 9).count();
let _ = map.iter().map(|(_key, value)| value * 17).count();
+
+ // Preserve the ref in the fix.
+ let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
+
+ // Preserve the mut in the fix.
+ let _ = map
+ .clone()
+ .into_iter()
+ .map(|(_, mut val)| {
+ val += 2;
+ val
+ })
+ .count();
+
+ // Don't let a mut interfere.
+ let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
}
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:15:13
+ --> $DIR/iter_kv_map.rs:16:13
|
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
= note: `-D clippy::iter-kv-map` implied by `-D warnings`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:16:13
+ --> $DIR/iter_kv_map.rs:17:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:17:13
+ --> $DIR/iter_kv_map.rs:18:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:19:13
+ --> $DIR/iter_kv_map.rs:20:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:20:13
+ --> $DIR/iter_kv_map.rs:21:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:22:13
+ --> $DIR/iter_kv_map.rs:23:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:23:13
+ --> $DIR/iter_kv_map.rs:24:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:25:13
+ --> $DIR/iter_kv_map.rs:26:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:26:13
+ --> $DIR/iter_kv_map.rs:27:13
|
LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:36:13
+ --> $DIR/iter_kv_map.rs:37:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:37:13
+ --> $DIR/iter_kv_map.rs:38:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
-error: iterating on a map's keys
+error: iterating on a map's values
--> $DIR/iter_kv_map.rs:41:13
|
+LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
+
+error: iterating on a map's values
+ --> $DIR/iter_kv_map.rs:44:13
+ |
+LL | let _ = map
+ | _____________^
+LL | | .clone()
+LL | | .into_iter()
+LL | | .map(|(_, mut val)| {
+LL | | val += 2;
+LL | | val
+LL | | })
+ | |__________^
+ |
+help: try
+ |
+LL ~ let _ = map
+LL + .clone().into_values().map(|mut val| {
+LL + val += 2;
+LL + val
+LL + })
+ |
+
+error: iterating on a map's values
+ --> $DIR/iter_kv_map.rs:54:13
+ |
+LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
+
+error: iterating on a map's keys
+ --> $DIR/iter_kv_map.rs:58:13
+ |
LL | let _ = map.iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:42:13
+ --> $DIR/iter_kv_map.rs:59:13
|
LL | let _ = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values()`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:43:13
+ --> $DIR/iter_kv_map.rs:60:13
|
LL | let _ = map.iter().map(|(_, v)| v + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|v| v + 2)`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:45:13
+ --> $DIR/iter_kv_map.rs:62:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys()`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:46:13
+ --> $DIR/iter_kv_map.rs:63:13
|
LL | let _ = map.clone().into_iter().map(|(key, _)| key + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_keys().map(|key| key + 2)`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:48:13
+ --> $DIR/iter_kv_map.rs:65:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:49:13
+ --> $DIR/iter_kv_map.rs:66:13
|
LL | let _ = map.clone().into_iter().map(|(_, val)| val + 2).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|val| val + 2)`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:51:13
+ --> $DIR/iter_kv_map.rs:68:13
|
LL | let _ = map.clone().iter().map(|(_, val)| val).collect::<Vec<_>>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().values()`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:52:13
+ --> $DIR/iter_kv_map.rs:69:13
|
LL | let _ = map.iter().map(|(key, _)| key).filter(|x| *x % 2 == 0).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys()`
error: iterating on a map's keys
- --> $DIR/iter_kv_map.rs:62:13
+ --> $DIR/iter_kv_map.rs:79:13
|
LL | let _ = map.iter().map(|(key, _value)| key * 9).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.keys().map(|key| key * 9)`
error: iterating on a map's values
- --> $DIR/iter_kv_map.rs:63:13
+ --> $DIR/iter_kv_map.rs:80:13
|
LL | let _ = map.iter().map(|(_key, value)| value * 17).count();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.values().map(|value| value * 17)`
-error: aborting due to 22 previous errors
+error: iterating on a map's values
+ --> $DIR/iter_kv_map.rs:83:13
+ |
+LL | let _ = map.clone().into_iter().map(|(_, ref val)| ref_acceptor(val)).count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values().map(|ref val| ref_acceptor(val))`
+
+error: iterating on a map's values
+ --> $DIR/iter_kv_map.rs:86:13
+ |
+LL | let _ = map
+ | _____________^
+LL | | .clone()
+LL | | .into_iter()
+LL | | .map(|(_, mut val)| {
+LL | | val += 2;
+LL | | val
+LL | | })
+ | |__________^
+ |
+help: try
+ |
+LL ~ let _ = map
+LL + .clone().into_values().map(|mut val| {
+LL + val += 2;
+LL + val
+LL + })
+ |
+
+error: iterating on a map's values
+ --> $DIR/iter_kv_map.rs:96:13
+ |
+LL | let _ = map.clone().into_iter().map(|(_, mut val)| val).count();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map.clone().into_values()`
+
+error: aborting due to 28 previous errors
// run-rustfix
-#![feature(custom_inner_attributes, lint_reasons, rustc_private)]
+#![feature(lint_reasons)]
#![allow(
unused,
clippy::uninlined_format_args,
S.foo::<&[u8; 100]>(&a);
}
}
-
-extern crate rustc_lint;
-extern crate rustc_span;
-
-#[allow(dead_code)]
-mod span_lint {
- use rustc_lint::{LateContext, Lint, LintContext};
- fn foo(cx: &LateContext<'_>, lint: &'static Lint) {
- cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(String::new()));
- }
-}
// run-rustfix
-#![feature(custom_inner_attributes, lint_reasons, rustc_private)]
+#![feature(lint_reasons)]
#![allow(
unused,
clippy::uninlined_format_args,
S.foo::<&[u8; 100]>(&a);
}
}
-
-extern crate rustc_lint;
-extern crate rustc_span;
-
-#[allow(dead_code)]
-mod span_lint {
- use rustc_lint::{LateContext, Lint, LintContext};
- fn foo(cx: &LateContext<'_>, lint: &'static Lint) {
- cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new()));
- }
-}
LL | foo(&a);
| ^^ help: change this to: `a`
-error: the borrowed expression implements the required traits
- --> $DIR/needless_borrow.rs:502:85
- |
-LL | cx.struct_span_lint(lint, rustc_span::Span::default(), "", |diag| diag.note(&String::new()));
- | ^^^^^^^^^^^^^^ help: change this to: `String::new()`
-
-error: aborting due to 37 previous errors
+error: aborting due to 36 previous errors
do yeet "hello";
}
+// without anyhow, but triggers the same bug I believe
+#[expect(clippy::useless_format)]
+fn issue10051() -> Result<String, String> {
+ if true {
+ Ok(format!("ok!"))
+ } else {
+ Err(format!("err!"))
+ }
+}
+
fn main() {}
do yeet "hello";
}
+// without anyhow, but triggers the same bug I believe
+#[expect(clippy::useless_format)]
+fn issue10051() -> Result<String, String> {
+ if true {
+ return Ok(format!("ok!"));
+ } else {
+ return Err(format!("err!"));
+ }
+}
+
fn main() {}
|
= help: remove `return`
-error: aborting due to 46 previous errors
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:294:9
+ |
+LL | return Ok(format!("ok!"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: unneeded `return` statement
+ --> $DIR/needless_return.rs:296:9
+ |
+LL | return Err(format!("err!"));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: remove `return`
+
+error: aborting due to 48 previous errors
let _z = x.clone(); // pr 7346 can't lint on `x`
drop(y);
}
-
-#[allow(unused, clippy::manual_retain)]
-fn possible_borrower_improvements() {
- let mut s = String::from("foobar");
- s = s.chars().filter(|&c| c != 'o').collect();
-}
let _z = x.clone(); // pr 7346 can't lint on `x`
drop(y);
}
-
-#[allow(unused, clippy::manual_retain)]
-fn possible_borrower_improvements() {
- let mut s = String::from("foobar");
- s = s.chars().filter(|&c| c != 'o').to_owned().collect();
-}
LL | foo(&x.clone(), move || {
| ^
-error: redundant clone
- --> $DIR/redundant_clone.rs:246:40
- |
-LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect();
- | ^^^^^^^^^^^ help: remove this
- |
-note: this value is dropped without further use
- --> $DIR/redundant_clone.rs:246:9
- |
-LL | s = s.chars().filter(|&c| c != 'o').to_owned().collect();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 16 previous errors
+error: aborting due to 15 previous errors
#![allow(clippy::box_collection)]
#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::derived_hash_with_manual_eq)]
#![allow(clippy::disallowed_methods)]
#![allow(clippy::disallowed_types)]
#![allow(clippy::mixed_read_write_in_expression)]
#![warn(clippy::box_collection)]
#![warn(clippy::redundant_static_lifetimes)]
#![warn(clippy::cognitive_complexity)]
+#![warn(clippy::derived_hash_with_manual_eq)]
#![warn(clippy::disallowed_methods)]
#![warn(clippy::disallowed_types)]
#![warn(clippy::mixed_read_write_in_expression)]
#![allow(clippy::box_collection)]
#![allow(clippy::redundant_static_lifetimes)]
#![allow(clippy::cognitive_complexity)]
+#![allow(clippy::derived_hash_with_manual_eq)]
#![allow(clippy::disallowed_methods)]
#![allow(clippy::disallowed_types)]
#![allow(clippy::mixed_read_write_in_expression)]
#![warn(clippy::box_vec)]
#![warn(clippy::const_static_lifetime)]
#![warn(clippy::cyclomatic_complexity)]
+#![warn(clippy::derive_hash_xor_eq)]
#![warn(clippy::disallowed_method)]
#![warn(clippy::disallowed_type)]
#![warn(clippy::eval_order_dependence)]
error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
- --> $DIR/rename.rs:41:9
+ --> $DIR/rename.rs:42:9
|
LL | #![warn(clippy::almost_complete_letter_range)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
- --> $DIR/rename.rs:42:9
+ --> $DIR/rename.rs:43:9
|
LL | #![warn(clippy::blacklisted_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:43:9
+ --> $DIR/rename.rs:44:9
|
LL | #![warn(clippy::block_in_if_condition_expr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:44:9
+ --> $DIR/rename.rs:45:9
|
LL | #![warn(clippy::block_in_if_condition_stmt)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
- --> $DIR/rename.rs:45:9
+ --> $DIR/rename.rs:46:9
|
LL | #![warn(clippy::box_vec)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
- --> $DIR/rename.rs:46:9
+ --> $DIR/rename.rs:47:9
|
LL | #![warn(clippy::const_static_lifetime)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
- --> $DIR/rename.rs:47:9
+ --> $DIR/rename.rs:48:9
|
LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
+error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
+ --> $DIR/rename.rs:49:9
+ |
+LL | #![warn(clippy::derive_hash_xor_eq)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
+
error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
- --> $DIR/rename.rs:48:9
+ --> $DIR/rename.rs:50:9
|
LL | #![warn(clippy::disallowed_method)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
- --> $DIR/rename.rs:49:9
+ --> $DIR/rename.rs:51:9
|
LL | #![warn(clippy::disallowed_type)]
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
- --> $DIR/rename.rs:50:9
+ --> $DIR/rename.rs:52:9
|
LL | #![warn(clippy::eval_order_dependence)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
- --> $DIR/rename.rs:51:9
+ --> $DIR/rename.rs:53:9
|
LL | #![warn(clippy::identity_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
- --> $DIR/rename.rs:52:9
+ --> $DIR/rename.rs:54:9
|
LL | #![warn(clippy::if_let_some_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
- --> $DIR/rename.rs:53:9
+ --> $DIR/rename.rs:55:9
|
LL | #![warn(clippy::logic_bug)]
| ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
- --> $DIR/rename.rs:54:9
+ --> $DIR/rename.rs:56:9
|
LL | #![warn(clippy::new_without_default_derive)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
- --> $DIR/rename.rs:55:9
+ --> $DIR/rename.rs:57:9
|
LL | #![warn(clippy::option_and_then_some)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:56:9
+ --> $DIR/rename.rs:58:9
|
LL | #![warn(clippy::option_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:57:9
+ --> $DIR/rename.rs:59:9
|
LL | #![warn(clippy::option_map_unwrap_or)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:58:9
+ --> $DIR/rename.rs:60:9
|
LL | #![warn(clippy::option_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:59:9
+ --> $DIR/rename.rs:61:9
|
LL | #![warn(clippy::option_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
- --> $DIR/rename.rs:60:9
+ --> $DIR/rename.rs:62:9
|
LL | #![warn(clippy::ref_in_deref)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:61:9
+ --> $DIR/rename.rs:63:9
|
LL | #![warn(clippy::result_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:62:9
+ --> $DIR/rename.rs:64:9
|
LL | #![warn(clippy::result_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:63:9
+ --> $DIR/rename.rs:65:9
|
LL | #![warn(clippy::result_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
- --> $DIR/rename.rs:64:9
+ --> $DIR/rename.rs:66:9
|
LL | #![warn(clippy::single_char_push_str)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
- --> $DIR/rename.rs:65:9
+ --> $DIR/rename.rs:67:9
|
LL | #![warn(clippy::stutter)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
- --> $DIR/rename.rs:66:9
+ --> $DIR/rename.rs:68:9
|
LL | #![warn(clippy::to_string_in_display)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
- --> $DIR/rename.rs:67:9
+ --> $DIR/rename.rs:69:9
|
LL | #![warn(clippy::zero_width_space)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- --> $DIR/rename.rs:68:9
+ --> $DIR/rename.rs:70:9
|
LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:69:9
+ --> $DIR/rename.rs:71:9
|
LL | #![warn(clippy::for_loop_over_option)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:70:9
+ --> $DIR/rename.rs:72:9
|
LL | #![warn(clippy::for_loop_over_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:71:9
+ --> $DIR/rename.rs:73:9
|
LL | #![warn(clippy::for_loops_over_fallibles)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/rename.rs:72:9
+ --> $DIR/rename.rs:74:9
|
LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
- --> $DIR/rename.rs:73:9
+ --> $DIR/rename.rs:75:9
|
LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/rename.rs:74:9
+ --> $DIR/rename.rs:76:9
|
LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
- --> $DIR/rename.rs:75:9
+ --> $DIR/rename.rs:77:9
|
LL | #![warn(clippy::let_underscore_drop)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:76:9
+ --> $DIR/rename.rs:78:9
|
LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
- --> $DIR/rename.rs:77:9
+ --> $DIR/rename.rs:79:9
|
LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
- --> $DIR/rename.rs:78:9
+ --> $DIR/rename.rs:80:9
|
LL | #![warn(clippy::positional_named_format_parameters)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
- --> $DIR/rename.rs:79:9
+ --> $DIR/rename.rs:81:9
|
LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
- --> $DIR/rename.rs:80:9
+ --> $DIR/rename.rs:82:9
|
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::unused_label` has been renamed to `unused_labels`
- --> $DIR/rename.rs:81:9
+ --> $DIR/rename.rs:83:9
|
LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
-error: aborting due to 41 previous errors
+error: aborting due to 42 previous errors
let item = 0..5;
dbg!(item);
}
+
+ // should not lint (issue #10018)
+ for e in [42] {
+ if e > 0 {
+ continue;
+ }
+ }
+
+ // should not lint (issue #10018)
+ for e in [42] {
+ if e > 0 {
+ break;
+ }
+ }
+
+ // should lint (issue #10018)
+ {
+ let _ = 42;
+ let _f = |n: u32| {
+ for i in 0..n {
+ if i > 10 {
+ dbg!(i);
+ break;
+ }
+ }
+ };
+ }
}
for item in [0..5].into_iter() {
dbg!(item);
}
+
+ // should not lint (issue #10018)
+ for e in [42] {
+ if e > 0 {
+ continue;
+ }
+ }
+
+ // should not lint (issue #10018)
+ for e in [42] {
+ if e > 0 {
+ break;
+ }
+ }
+
+ // should lint (issue #10018)
+ for _ in [42] {
+ let _f = |n: u32| {
+ for i in 0..n {
+ if i > 10 {
+ dbg!(i);
+ break;
+ }
+ }
+ };
+ }
}
LL + }
|
-error: aborting due to 6 previous errors
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:46:5
+ |
+LL | / for _ in [42] {
+LL | | let _f = |n: u32| {
+LL | | for i in 0..n {
+LL | | if i > 10 {
+... |
+LL | | };
+LL | | }
+ | |_____^
+ |
+help: try
+ |
+LL ~ {
+LL + let _ = 42;
+LL + let _f = |n: u32| {
+LL + for i in 0..n {
+LL + if i > 10 {
+LL + dbg!(i);
+LL + break;
+LL + }
+LL + }
+LL + };
+LL + }
+ |
+
+error: aborting due to 7 previous errors
-error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned
+error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned
--> $DIR/suspicious_to_owned.rs:16:13
|
LL | let _ = cow.to_owned();
|
= note: `-D clippy::suspicious-to-owned` implied by `-D warnings`
-error: this `to_owned` call clones the std::borrow::Cow<'_, [char; 3]> itself and does not cause the std::borrow::Cow<'_, [char; 3]> contents to become owned
+error: this `to_owned` call clones the Cow<'_, [char; 3]> itself and does not cause the Cow<'_, [char; 3]> contents to become owned
--> $DIR/suspicious_to_owned.rs:26:13
|
LL | let _ = cow.to_owned();
| ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
-error: this `to_owned` call clones the std::borrow::Cow<'_, std::vec::Vec<char>> itself and does not cause the std::borrow::Cow<'_, std::vec::Vec<char>> contents to become owned
+error: this `to_owned` call clones the Cow<'_, Vec<char>> itself and does not cause the Cow<'_, Vec<char>> contents to become owned
--> $DIR/suspicious_to_owned.rs:36:13
|
LL | let _ = cow.to_owned();
| ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
-error: this `to_owned` call clones the std::borrow::Cow<'_, str> itself and does not cause the std::borrow::Cow<'_, str> contents to become owned
+error: this `to_owned` call clones the Cow<'_, str> itself and does not cause the Cow<'_, str> contents to become owned
--> $DIR/suspicious_to_owned.rs:46:13
|
LL | let _ = cow.to_owned();
|
= note: `-D clippy::clone-on-copy` implied by `-D warnings`
-error: using `clone` on type `std::option::Option<T>` which implements the `Copy` trait
+error: using `clone` on type `Option<T>` which implements the `Copy` trait
--> $DIR/unnecessary_clone.rs:42:5
|
LL | Some(t).clone();
| ^^^^^^^^^^^^^^^ help: try removing the `clone` call: `Some(t)`
-error: using `clone` on a double-reference; this will copy the reference of type `&std::vec::Vec<i32>` instead of cloning the inner type
+error: using `clone` on a double-reference; this will copy the reference of type `&Vec<i32>` instead of cloning the inner type
--> $DIR/unnecessary_clone.rs:48:22
|
LL | let z: &Vec<_> = y.clone();
| ~~~~~~~~~~~~~
help: or try being explicit if you are sure, that you want to clone a reference
|
-LL | let z: &Vec<_> = <&std::vec::Vec<i32>>::clone(y);
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | let z: &Vec<_> = <&Vec<i32>>::clone(y);
+ | ~~~~~~~~~~~~~~~~~~~~~
-error: using `clone` on type `many_derefs::E` which implements the `Copy` trait
+error: using `clone` on type `E` which implements the `Copy` trait
--> $DIR/unnecessary_clone.rs:84:20
|
LL | let _: E = a.clone();
// shouldn't trigger for public methods
pub fn unused_self_move(self) {}
}
+
+ pub struct E;
+
+ impl E {
+ // shouldn't trigger if body contains todo!()
+ pub fn unused_self_todo(self) {
+ let x = 42;
+ todo!()
+ }
+ }
}
pub use unused_self_allow::D;
LL | fn unused_self_move(self) {}
| ^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
= note: `-D clippy::unused-self` implied by `-D warnings`
error: unused `self` argument
LL | fn unused_self_ref(&self) {}
| ^^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:13:32
LL | fn unused_self_mut_ref(&mut self) {}
| ^^^^^^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:14:32
LL | fn unused_self_pin_ref(self: Pin<&Self>) {}
| ^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:15:36
LL | fn unused_self_pin_mut_ref(self: Pin<&mut Self>) {}
| ^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:16:35
LL | fn unused_self_pin_nested(self: Pin<Arc<Self>>) {}
| ^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:17:28
LL | fn unused_self_box(self: Box<Self>) {}
| ^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:18:40
LL | fn unused_with_other_used_args(&self, x: u8, y: u8) -> u8 {
| ^^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: unused `self` argument
--> $DIR/unused_self.rs:21:37
LL | fn unused_self_class_method(&self) {
| ^^^^^
|
- = help: consider refactoring to a associated function
+ = help: consider refactoring to an associated function
error: aborting due to 9 previous errors
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
}
}
}
// test `stat`
- assert_eq!(fs::metadata("foo.txt").unwrap_err().kind(), ErrorKind::PermissionDenied);
+ let err = fs::metadata("foo.txt").unwrap_err();
+ assert_eq!(err.kind(), ErrorKind::PermissionDenied);
// check that it is the right kind of `PermissionDenied`
- assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
+ assert_eq!(err.raw_os_error(), Some(libc::EACCES));
}
assert!(v[0].0 == 49);
}
+fn miri_issue_2759() {
+ let mut input = "1".to_string();
+ input.replace_range(0..0, "0");
+}
+
fn main() {
assert_eq!(vec_reallocate().len(), 5);
swap();
swap_remove();
reverse();
+ miri_issue_2759();
}
edition = "2021"
[dependencies]
-clap = "3.1.1"
+clap = "4.0.32"
env_logger = "0.7.1"
[dependencies.mdbook]
-version = "0.4.21"
+version = "0.4.25"
default-features = false
features = ["search"]
use mdbook::MDBook;
fn main() {
- let crate_version = format!("v{}", crate_version!());
+ let crate_version = concat!("v", crate_version!());
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
let d_arg = arg!(-d --"dest-dir" <DEST_DIR>
"The output directory for your book\n(Defaults to ./book when omitted)")
- .required(false);
- let dir_arg = arg!([dir]
-"A directory for your book\n(Defaults to Current Directory when omitted)");
+ .required(false)
+ .value_parser(clap::value_parser!(PathBuf));
+
+ let dir_arg = arg!([dir] "Root directory for the book\n\
+ (Defaults to the current directory when omitted)")
+ .value_parser(clap::value_parser!(PathBuf));
let matches = Command::new("rustbook")
.about("Build a book with mdBook")
.author("Steve Klabnik <steve@steveklabnik.com>")
- .version(&*crate_version)
+ .version(crate_version)
.subcommand_required(true)
.arg_required_else_help(true)
.subcommand(
// Set this to allow us to catch bugs in advance.
book.config.build.create_missing = false;
- if let Some(dest_dir) = args.value_of("dest-dir") {
- book.config.build.build_dir = PathBuf::from(dest_dir);
+ if let Some(dest_dir) = args.get_one::<PathBuf>("dest-dir") {
+ book.config.build.build_dir = dest_dir.into();
}
book.build()?;
}
fn get_book_dir(args: &ArgMatches) -> PathBuf {
- if let Some(dir) = args.value_of("dir") {
+ if let Some(p) = args.get_one::<PathBuf>("dir") {
// Check if path is relative from current dir, or absolute...
- let p = Path::new(dir);
- if p.is_relative() { env::current_dir().unwrap().join(dir) } else { p.to_path_buf() }
+ if p.is_relative() { env::current_dir().unwrap().join(p) } else { p.to_path_buf() }
} else {
env::current_dir().unwrap()
}
--- /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(
pub(crate) fn rewrite_closure(
binder: &ast::ClosureBinder,
+ constness: ast::Const,
capture: ast::CaptureBy,
is_async: &ast::Async,
movability: ast::Movability,
debug!("rewrite_closure {:?}", body);
let (prefix, extra_offset) = rewrite_closure_fn_decl(
- binder, capture, is_async, movability, fn_decl, body, span, context, shape,
+ binder, constness, capture, is_async, movability, fn_decl, body, span, context, shape,
)?;
// 1 = space between `|...|` and body.
let body_shape = shape.offset_left(extra_offset)?;
// Return type is (prefix, extra_offset)
fn rewrite_closure_fn_decl(
binder: &ast::ClosureBinder,
+ constness: ast::Const,
capture: ast::CaptureBy,
asyncness: &ast::Async,
movability: ast::Movability,
ast::ClosureBinder::NotPresent => "".to_owned(),
};
+ let const_ = if matches!(constness, ast::Const::Yes(_)) {
+ "const "
+ } else {
+ ""
+ };
+
let immovable = if movability == ast::Movability::Static {
"static "
} else {
// 4 = "|| {".len(), which is overconservative when the closure consists of
// a single expression.
let nested_shape = shape
- .shrink_left(binder.len() + immovable.len() + is_async.len() + mover.len())?
+ .shrink_left(binder.len() + const_.len() + immovable.len() + is_async.len() + mover.len())?
.sub_width(4)?;
// 1 = |
.tactic(tactic)
.preserve_newline(true);
let list_str = write_list(&item_vec, &fmt)?;
- let mut prefix = format!("{}{}{}{}|{}|", binder, immovable, is_async, mover, list_str);
+ let mut prefix = format!(
+ "{}{}{}{}{}|{}|",
+ binder, const_, immovable, is_async, mover, list_str
+ );
if !ret_str.is_empty() {
if prefix.contains('\n') {
if let ast::ExprKind::Closure(ref closure) = expr.kind {
let ast::Closure {
ref binder,
+ constness,
capture_clause,
ref asyncness,
movability,
};
let (prefix, extra_offset) = rewrite_closure_fn_decl(
binder,
+ constness,
capture_clause,
asyncness,
movability,
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::Closure(ref cl) => closures::rewrite_closure(
&cl.binder,
+ cl.constness,
cl.capture_clause,
&cl.asyncness,
cl.movability,
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 {
use crate::attr::*;
use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices};
use crate::config::Version;
-use crate::config::{BraceStyle, Config};
+use crate::config::{BraceStyle, Config, MacroSelector};
use crate::coverage::transform_missing_snippet;
use crate::items::{
format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate,
snippet_provider: &'a SnippetProvider,
report: FormatReport,
) -> FmtVisitor<'a> {
+ let mut skip_context = SkipContext::default();
+ let mut macro_names = Vec::new();
+ for macro_selector in config.skip_macro_invocations().0 {
+ match macro_selector {
+ MacroSelector::Name(name) => macro_names.push(name.to_string()),
+ MacroSelector::All => skip_context.macros.skip_all(),
+ }
+ }
+ skip_context.macros.extend(macro_names);
FmtVisitor {
parent_context: None,
parse_sess: parse_session,
is_macro_def: false,
macro_rewrite_failure: false,
report,
- skip_context: Default::default(),
+ skip_context,
}
}
use std::path::Path;
use std::process::Command;
+use rustfmt_config_proc_macro::rustfmt_only_ci_test;
+
/// Run the cargo-fmt executable and return its output.
fn cargo_fmt(args: &[&str]) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
};
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn version() {
assert_that!(&["--version"], starts_with("rustfmt "));
assert_that!(&["--", "--version"], starts_with("rustfmt "));
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn print_config() {
assert_that!(
);
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn rustfmt_help() {
assert_that!(&["--", "--help"], contains("Format Rust code"));
assert_that!(&["--", "--help=config"], contains("Configuration Options:"));
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn cargo_fmt_out_of_line_test_modules() {
// See also https://github.com/rust-lang/rustfmt/issues/5119
assert!(stdout.contains(&format!("Diff in {}", path.display())))
}
}
+
+#[rustfmt_only_ci_test]
+#[test]
+fn cargo_fmt_emits_error_on_line_overflow_true() {
+ // See also https://github.com/rust-lang/rustfmt/issues/3164
+ let args = [
+ "--check",
+ "--manifest-path",
+ "tests/cargo-fmt/source/issue_3164/Cargo.toml",
+ "--",
+ "--config",
+ "error_on_line_overflow=true",
+ ];
+
+ let (_stdout, stderr) = cargo_fmt(&args);
+ assert!(stderr.contains(
+ "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
+ ))
+}
--- /dev/null
+[package]
+name = "issue_3164"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
--- /dev/null
+#[allow(unused_macros)]
+macro_rules! foo {
+ ($id:ident) => {
+ macro_rules! bar {
+ ($id2:tt) => {
+ #[cfg(any(target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2, target_feature = $id2))]
+ fn $id() {}
+ };
+ }
+ };
+}
+
+fn main() {}
tab_spaces = 2
newline_style = "Unix"
brace_style = "SameLineWhere"
-fn_args_layout = "Tall"
+fn_params_layout = "Tall"
trailing_comma = "Vertical"
indent_style = "Block"
reorder_imports = false
* mod g;
Module resolution will fail if we look for './lib/c/d/e.rs' or './lib/c/d/e/mod.rs',
-so we should fall back to looking for './lib/c/e.rs', which correctly finds the modlue, that
+so we should fall back to looking for './lib/c/e.rs', which correctly finds the module, that
rustfmt should format.
'./lib/c/d/f.rs' and './lib/c/d/g/mod.rs' exist at the default submodule paths so we should be able
* mod c;
Module resolution will fail if we look for './lib/a.rs' or './lib/a/mod.rs',
-so we should fall back to looking for './a.rs', which correctly finds the modlue that
+so we should fall back to looking for './a.rs', which correctly finds the module that
rustfmt should format.
'./lib/b.rs' and './lib/c/mod.rs' exist at the default submodule paths so we should be able
use std::path::Path;
use std::process::Command;
+use rustfmt_config_proc_macro::rustfmt_only_ci_test;
+
/// Run the rustfmt executable and return its output.
fn rustfmt(args: &[&str]) -> (String, String) {
let mut bin_dir = env::current_exe().unwrap();
};
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn print_config() {
assert_that!(
remove_file("minimal-config").unwrap();
}
-#[ignore]
+#[rustfmt_only_ci_test]
#[test]
fn inline_config() {
// single invocation
// The path attribute points to a file that does not exist
assert!(stderr.contains("does_not_exist.rs does not exist"));
}
+
+#[test]
+fn rustfmt_emits_error_on_line_overflow_true() {
+ // See also https://github.com/rust-lang/rustfmt/issues/3164
+ let args = [
+ "--config",
+ "error_on_line_overflow=true",
+ "tests/cargo-fmt/source/issue_3164/src/main.rs",
+ ];
+
+ let (_stdout, stderr) = rustfmt(&args);
+ assert!(stderr.contains(
+ "line formatted, but exceeded maximum width (maximum: 100 (see `max_width` option)"
+ ))
+}
tbm,
/// POPCNT (Population Count)
popcnt,
- /// FXSR (Floating-point context fast save and restor)
+ /// FXSR (Floating-point context fast save and restore)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,
--- /dev/null
+impl Default for WhitespaceCharacters {
+ fn default() -> Self {
+ Self {
+ space: '·', // U+00B7
+ nbsp: '⍽', // U+237D
+ tab: '→', // U+2192
+ newline: '⏎', // U+23CE
+ }
+ }
+}
+
+const RAINBOWS: &[&str] = &[
+ "rаinЬοѡ", // hue: 0
+ "raіnЬοw", // hue: 2
+ "rаіɴЬow", // hue: 2
+ "raіɴЬoѡ", // hue: 8
+ "ʀainЬow", // hue: 8
+ "ʀaіɴboѡ", // hue: 8
+ "ʀаіnbοw", // hue: 11
+ "rainЬoѡ", // hue: 14
+ "raіɴbow", // hue: 14
+ "rаiɴЬow", // hue: 20
+ "raіnЬow", // hue: 26
+ "ʀaiɴbοw", // hue: 32
+ "raіɴboѡ", // hue: 35
+ "rаiɴbow", // hue: 35
+ "rаіnbοw", // hue: 38
+ "rаinЬow", // hue: 47
+ "ʀaіnboѡ", // hue: 47
+ "ʀaіnЬoѡ", // hue: 47
+ "ʀаіɴbοw", // hue: 53
+ "ʀaіnЬοѡ", // hue: 57
+ "raiɴЬoѡ", // hue: 68
+ "ʀainbοѡ", // hue: 68
+ "ʀаinboѡ", // hue: 68
+ "ʀаiɴbοw", // hue: 68
+ "ʀаіnbow", // hue: 68
+ "rаіnЬοѡ", // hue: 69
+ "ʀainЬοw", // hue: 71
+ "raiɴbow", // hue: 73
+ "raіnЬoѡ", // hue: 74
+ "rаіɴbοw", // hue: 77
+ "raіnЬοѡ", // hue: 81
+ "raiɴЬow", // hue: 83
+ "ʀainbοw", // hue: 83
+ "ʀаinbow", // hue: 83
+ "ʀаiɴbοѡ", // hue: 83
+ "ʀаіnboѡ", // hue: 83
+ "ʀаіɴЬοѡ", // hue: 84
+ "rainЬow", // hue: 85
+ "ʀаiɴЬοw", // hue: 86
+ "ʀаіnbοѡ", // hue: 89
+ "ʀаіnЬοw", // hue: 92
+ "rаiɴbοw", // hue: 95
+ "ʀаіɴbοѡ", // hue: 98
+ "ʀаiɴЬοѡ", // hue: 99
+ "raіnbοw", // hue: 101
+ "ʀаіɴЬοw", // hue: 101
+ "ʀaiɴboѡ", // hue: 104
+ "ʀаinbοѡ", // hue: 104
+ "rаiɴbοѡ", // hue: 107
+ "ʀаinЬοw", // hue: 107
+ "rаiɴЬοw", // hue: 110
+ "rаіnboѡ", // hue: 110
+ "rаіnbοѡ", // hue: 113
+ "ʀainЬοѡ", // hue: 114
+ "rаіnЬοw", // hue: 116
+ "ʀaіɴЬow", // hue: 116
+ "rаinbοw", // hue: 122
+ "ʀаіɴboѡ", // hue: 125
+ "rаinbοѡ", // hue: 131
+ "rainbow", // hue: 134
+ "rаinЬοw", // hue: 134
+ "ʀаiɴboѡ", // hue: 140
+ "rainЬοѡ", // hue: 141
+ "raіɴЬow", // hue: 143
+ "ʀainЬoѡ", // hue: 143
+ "ʀaіɴbow", // hue: 143
+ "ʀainbow", // hue: 148
+ "rаіɴboѡ", // hue: 149
+ "ʀainboѡ", // hue: 155
+ "ʀaіnbow", // hue: 155
+ "ʀaіnЬow", // hue: 155
+ "raiɴbοw", // hue: 158
+ "ʀаiɴЬoѡ", // hue: 158
+ "rainbοw", // hue: 160
+ "rаinbow", // hue: 160
+ "ʀaіɴbοѡ", // hue: 164
+ "ʀаiɴbow", // hue: 164
+ "ʀаіnЬoѡ", // hue: 164
+ "ʀaiɴЬοѡ", // hue: 165
+ "rаiɴboѡ", // hue: 167
+ "ʀaіɴЬοw", // hue: 167
+ "ʀaіɴЬοѡ", // hue: 171
+ "raіnboѡ", // hue: 173
+ "ʀаіɴЬoѡ", // hue: 173
+ "rаіɴbοѡ", // hue: 176
+ "ʀаinЬow", // hue: 176
+ "rаiɴЬοѡ", // hue: 177
+ "rаіɴЬοw", // hue: 179
+ "ʀаinЬoѡ", // hue: 179
+ "ʀаіɴbow", // hue: 179
+ "rаiɴЬoѡ", // hue: 182
+ "raіɴbοѡ", // hue: 188
+ "rаіnЬoѡ", // hue: 188
+ "raiɴЬοѡ", // hue: 189
+ "raіɴЬοw", // hue: 191
+ "ʀaіɴbοw", // hue: 191
+ "ʀаіnЬow", // hue: 191
+ "rainbοѡ", // hue: 194
+ "rаinboѡ", // hue: 194
+ "rаіnbow", // hue: 194
+ "rainЬοw", // hue: 197
+ "rаinЬoѡ", // hue: 206
+ "rаіɴbow", // hue: 206
+ "rаіɴЬοѡ", // hue: 210
+ "ʀaiɴЬow", // hue: 212
+ "raіɴbοw", // hue: 218
+ "rаіnЬow", // hue: 218
+ "ʀaiɴbοѡ", // hue: 221
+ "ʀaiɴЬοw", // hue: 224
+ "ʀaіnbοѡ", // hue: 227
+ "raiɴboѡ", // hue: 230
+ "ʀaіnbοw", // hue: 230
+ "ʀaіnЬοw", // hue: 230
+ "ʀаinЬοѡ", // hue: 231
+ "rainboѡ", // hue: 232
+ "raіnbow", // hue: 232
+ "ʀаіɴЬow", // hue: 233
+ "ʀaіɴЬoѡ", // hue: 239
+ "ʀаіnЬοѡ", // hue: 246
+ "raiɴbοѡ", // hue: 248
+ "ʀаiɴЬow", // hue: 248
+ "raіɴЬοѡ", // hue: 249
+ "raiɴЬοw", // hue: 251
+ "rаіɴЬoѡ", // hue: 251
+ "ʀaiɴbow", // hue: 251
+ "ʀаinbοw", // hue: 251
+ "raіnbοѡ", // hue: 254
+];
+++ /dev/null
-// rustfmt-fn_args_layout: Compressed
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Tall
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Vertical
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
- // body
- }
-}
--- /dev/null
+// rustfmt-fn_params_layout: Compressed
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Tall
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Vertical
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur, adipiscing: Adipiscing, elit: Elit) {
+ // body
+ }
+}
Normal(u32, String, ),
StructLike { x: i32, // Test comment
// Pre-comment
- #[Attr50] y: SomeType, // Aanother Comment
+ #[Attr50] y: SomeType, // Another Comment
}, SL { a: A }
}
// rustfmt-normalize_comments: true
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// rustfmt-brace_style: AlwaysNextLine
// Case with only one variable.
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
// Test some of the ways function signatures can be customised.
// Test compressed layout of args.
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// Empty list should stay on one line.
fn do_bar(
--- /dev/null
+// rustfmt-format_macro_bodies: true
+
+// with comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 + //comment 1
+ 42
+ //comment 2
+ ),
+ };
+ }};
+}
+
+// without comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 +
+ 42
+ ),
+ };
+ }};
+}
--- /dev/null
+// output doesn't get corrupted when using comments within generic type parameters of a trait
+
+pub trait Something<
+ A,
+ // some comment
+ B,
+ C
+> {
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
+
+pub trait SomethingElse<
+ A,
+ /* some comment */
+ B,
+ C
+> {
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
--- /dev/null
+// rustfmt-version: One
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write1 + fmt::Write2
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer1<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + Printer2<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+>,
+> {
+}
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+> + fmt::Write1
++ fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+)
+{
+}
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+) + fmt::Write
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+mut entries: entryyyyyyyy,
+) -> (
+impl Fn(
+AlphabeticalTraversal,
+Seconddddddddddddddddddddddddddddddddddd
+) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
++ Sendddddddddddddddddddddddddddddddddddddddddddd
+) {
+}
+
+pub trait SomeTrait:
+Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+for<'b> &'b Self: Send
++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
--- /dev/null
+// rustfmt-version: Two
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + fmt::Write1 + fmt::Write2
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+pub trait PrettyPrinter<'tcx>:
+fmt::Write + Printer1<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+> + Printer2<
+'tcx,
+Error = fmt::Error,
+Path = Self,
+Region = Self,
+Type = Self,
+DynExistential = Self,
+Const = Self,
+>
+{
+//
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+>,
+> {
+}
+fn f() -> Box<
+FnMut() -> Thing<
+WithType = LongItemName,
+Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+> + fmt::Write1
++ fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+)
+{
+}
+fn foo<F>(foo2: F)
+where
+F: Fn(
+// this comment is deleted
+) + fmt::Write
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+F: for<> FnMut(
+&mut ProbeContext<>,
+ty::PolyTraitRefffffffffffffffffffffffffffffffff<>,
+tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+mut entries: entryyyyyyyy,
+) -> (
+impl Fn(
+AlphabeticalTraversal,
+Seconddddddddddddddddddddddddddddddddddd
+) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
++ Sendddddddddddddddddddddddddddddddddddddddddddd
+) {
+}
+
+pub trait SomeTrait:
+Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+for<'b> &'b Self: Send
++ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
++ Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
--- /dev/null
+// rustfmt-max_width: 160
+// rustfmt-fn_call_width: 96
+// rustfmt-fn_args_layout: Compressed
+// rustfmt-trailing_comma: Always
+// rustfmt-wrap_comments: true
+
+fn foo() {
+ for elem in try!(gen_epub_book::ops::parse_descriptor_file(&mut try!(File::open(&opts.source_file.1).map_err(|_| {
+ gen_epub_book::Error::Io {
+ desc: "input file",
+ op: "open",
+ more: None,
+ }
+ })),
+ "input file")) {
+ println!("{}", elem);
+ }
+}
+
+fn write_content() {
+ io::copy(try!(File::open(in_f).map_err(|_| {
+ Error::Io {
+ desc: "Content",
+ op: "open",
+ more: None,
+ }
+ })),
+ w);
+}
--- /dev/null
+fn main() {
+ let x = 1;
+ ;let y = 3;
+}
--- /dev/null
+fn main() {;7
+}
+
+fn main() {
+ ;7
+}
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*","items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should also skip this invocation, as the wildcard covers it
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: []
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["unknown"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["foo","bar"]
+
+// Should skip this invocation
+foo!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+bar!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+baz!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should not skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["aaa","ccc"]
+
+// These tests demonstrate a realistic use case with use aliases.
+// The use statements should not impact functionality in any way.
+
+use crate::{aaa, bbb, ddd};
+
+// No use alias, invocation in list
+// Should skip this invocation
+aaa!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation in list
+// Should skip this invocation
+use crate::bbb as ccc;
+ccc!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation not in list
+// Should not skip this invocation
+use crate::ddd as eee;
+eee!(
+ const _: u8 = 0;
+);
+
+// No use alias, invocation not in list
+// Should not skip this invocation
+fff!(
+ const _: u8 = 0;
+);
-// Test tuple litterals
+// Test tuple literals
fn foo() {
let a = (a, a, a, a, a);
///
fn foo() {}
-/// A long commment for wrapping
+/// A long comment for wrapping
/// This is a long long long long long long long long long long long long long long long long long long long long sentence.
fn bar() {}
tbm,
/// POPCNT (Population Count)
popcnt,
- /// FXSR (Floating-point context fast save and restor)
+ /// FXSR (Floating-point context fast save and restore)
fxsr,
/// XSAVE (Save Processor Extended States)
xsave,
--- /dev/null
+impl Default for WhitespaceCharacters {
+ fn default() -> Self {
+ Self {
+ space: '·', // U+00B7
+ nbsp: '⍽', // U+237D
+ tab: '→', // U+2192
+ newline: '⏎', // U+23CE
+ }
+ }
+}
+
+const RAINBOWS: &[&str] = &[
+ "rаinЬοѡ", // hue: 0
+ "raіnЬοw", // hue: 2
+ "rаіɴЬow", // hue: 2
+ "raіɴЬoѡ", // hue: 8
+ "ʀainЬow", // hue: 8
+ "ʀaіɴboѡ", // hue: 8
+ "ʀаіnbοw", // hue: 11
+ "rainЬoѡ", // hue: 14
+ "raіɴbow", // hue: 14
+ "rаiɴЬow", // hue: 20
+ "raіnЬow", // hue: 26
+ "ʀaiɴbοw", // hue: 32
+ "raіɴboѡ", // hue: 35
+ "rаiɴbow", // hue: 35
+ "rаіnbοw", // hue: 38
+ "rаinЬow", // hue: 47
+ "ʀaіnboѡ", // hue: 47
+ "ʀaіnЬoѡ", // hue: 47
+ "ʀаіɴbοw", // hue: 53
+ "ʀaіnЬοѡ", // hue: 57
+ "raiɴЬoѡ", // hue: 68
+ "ʀainbοѡ", // hue: 68
+ "ʀаinboѡ", // hue: 68
+ "ʀаiɴbοw", // hue: 68
+ "ʀаіnbow", // hue: 68
+ "rаіnЬοѡ", // hue: 69
+ "ʀainЬοw", // hue: 71
+ "raiɴbow", // hue: 73
+ "raіnЬoѡ", // hue: 74
+ "rаіɴbοw", // hue: 77
+ "raіnЬοѡ", // hue: 81
+ "raiɴЬow", // hue: 83
+ "ʀainbοw", // hue: 83
+ "ʀаinbow", // hue: 83
+ "ʀаiɴbοѡ", // hue: 83
+ "ʀаіnboѡ", // hue: 83
+ "ʀаіɴЬοѡ", // hue: 84
+ "rainЬow", // hue: 85
+ "ʀаiɴЬοw", // hue: 86
+ "ʀаіnbοѡ", // hue: 89
+ "ʀаіnЬοw", // hue: 92
+ "rаiɴbοw", // hue: 95
+ "ʀаіɴbοѡ", // hue: 98
+ "ʀаiɴЬοѡ", // hue: 99
+ "raіnbοw", // hue: 101
+ "ʀаіɴЬοw", // hue: 101
+ "ʀaiɴboѡ", // hue: 104
+ "ʀаinbοѡ", // hue: 104
+ "rаiɴbοѡ", // hue: 107
+ "ʀаinЬοw", // hue: 107
+ "rаiɴЬοw", // hue: 110
+ "rаіnboѡ", // hue: 110
+ "rаіnbοѡ", // hue: 113
+ "ʀainЬοѡ", // hue: 114
+ "rаіnЬοw", // hue: 116
+ "ʀaіɴЬow", // hue: 116
+ "rаinbοw", // hue: 122
+ "ʀаіɴboѡ", // hue: 125
+ "rаinbοѡ", // hue: 131
+ "rainbow", // hue: 134
+ "rаinЬοw", // hue: 134
+ "ʀаiɴboѡ", // hue: 140
+ "rainЬοѡ", // hue: 141
+ "raіɴЬow", // hue: 143
+ "ʀainЬoѡ", // hue: 143
+ "ʀaіɴbow", // hue: 143
+ "ʀainbow", // hue: 148
+ "rаіɴboѡ", // hue: 149
+ "ʀainboѡ", // hue: 155
+ "ʀaіnbow", // hue: 155
+ "ʀaіnЬow", // hue: 155
+ "raiɴbοw", // hue: 158
+ "ʀаiɴЬoѡ", // hue: 158
+ "rainbοw", // hue: 160
+ "rаinbow", // hue: 160
+ "ʀaіɴbοѡ", // hue: 164
+ "ʀаiɴbow", // hue: 164
+ "ʀаіnЬoѡ", // hue: 164
+ "ʀaiɴЬοѡ", // hue: 165
+ "rаiɴboѡ", // hue: 167
+ "ʀaіɴЬοw", // hue: 167
+ "ʀaіɴЬοѡ", // hue: 171
+ "raіnboѡ", // hue: 173
+ "ʀаіɴЬoѡ", // hue: 173
+ "rаіɴbοѡ", // hue: 176
+ "ʀаinЬow", // hue: 176
+ "rаiɴЬοѡ", // hue: 177
+ "rаіɴЬοw", // hue: 179
+ "ʀаinЬoѡ", // hue: 179
+ "ʀаіɴbow", // hue: 179
+ "rаiɴЬoѡ", // hue: 182
+ "raіɴbοѡ", // hue: 188
+ "rаіnЬoѡ", // hue: 188
+ "raiɴЬοѡ", // hue: 189
+ "raіɴЬοw", // hue: 191
+ "ʀaіɴbοw", // hue: 191
+ "ʀаіnЬow", // hue: 191
+ "rainbοѡ", // hue: 194
+ "rаinboѡ", // hue: 194
+ "rаіnbow", // hue: 194
+ "rainЬοw", // hue: 197
+ "rаinЬoѡ", // hue: 206
+ "rаіɴbow", // hue: 206
+ "rаіɴЬοѡ", // hue: 210
+ "ʀaiɴЬow", // hue: 212
+ "raіɴbοw", // hue: 218
+ "rаіnЬow", // hue: 218
+ "ʀaiɴbοѡ", // hue: 221
+ "ʀaiɴЬοw", // hue: 224
+ "ʀaіnbοѡ", // hue: 227
+ "raiɴboѡ", // hue: 230
+ "ʀaіnbοw", // hue: 230
+ "ʀaіnЬοw", // hue: 230
+ "ʀаinЬοѡ", // hue: 231
+ "rainboѡ", // hue: 232
+ "raіnbow", // hue: 232
+ "ʀаіɴЬow", // hue: 233
+ "ʀaіɴЬoѡ", // hue: 239
+ "ʀаіnЬοѡ", // hue: 246
+ "raiɴbοѡ", // hue: 248
+ "ʀаiɴЬow", // hue: 248
+ "raіɴЬοѡ", // hue: 249
+ "raiɴЬοw", // hue: 251
+ "rаіɴЬoѡ", // hue: 251
+ "ʀaiɴbow", // hue: 251
+ "ʀаinbοw", // hue: 251
+ "raіnbοѡ", // hue: 254
+];
+++ /dev/null
-// rustfmt-fn_args_layout: Compressed
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(
- ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
- adipiscing: Adipiscing, elit: Elit,
- );
-
- fn lorem(
- ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
- adipiscing: Adipiscing, elit: Elit,
- ) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Tall
-// Function arguments density
-
-trait Lorem {
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
-
- fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
- // body
- }
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- );
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- ) {
- // body
- }
-}
+++ /dev/null
-// rustfmt-fn_args_layout: Vertical
-// Function arguments density
-
-trait Lorem {
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- );
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- ) {
- // body
- }
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- );
-
- fn lorem(
- ipsum: Ipsum,
- dolor: Dolor,
- sit: Sit,
- amet: Amet,
- consectetur: onsectetur,
- adipiscing: Adipiscing,
- elit: Elit,
- ) {
- // body
- }
-}
--- /dev/null
+// rustfmt-fn_params_layout: Compressed
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
+ adipiscing: Adipiscing, elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet, consectetur: onsectetur,
+ adipiscing: Adipiscing, elit: Elit,
+ ) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Tall
+// Function arguments density
+
+trait Lorem {
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet);
+
+ fn lorem(ipsum: Ipsum, dolor: Dolor, sit: Sit, amet: Amet) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ ) {
+ // body
+ }
+}
--- /dev/null
+// rustfmt-fn_params_layout: Vertical
+// Function arguments density
+
+trait Lorem {
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ ) {
+ // body
+ }
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ );
+
+ fn lorem(
+ ipsum: Ipsum,
+ dolor: Dolor,
+ sit: Sit,
+ amet: Amet,
+ consectetur: onsectetur,
+ adipiscing: Adipiscing,
+ elit: Elit,
+ ) {
+ // body
+ }
+}
x: i32, // Test comment
// Pre-comment
#[Attr50]
- y: SomeType, // Aanother Comment
+ y: SomeType, // Another Comment
},
SL {
a: A,
// rustfmt-normalize_comments: true
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// rustfmt-brace_style: AlwaysNextLine
// Case with only one variable.
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
// Test some of the ways function signatures can be customised.
// Test compressed layout of args.
-// rustfmt-fn_args_layout: Vertical
+// rustfmt-fn_params_layout: Vertical
// Empty list should stay on one line.
fn do_bar() -> u8 {
--- /dev/null
+// rustfmt-format_macro_matchers: false
+
+macro_rules! foo {
+ ($a:ident : $b:ty) => {};
+ ($a:ident $b:ident $c:ident) => {};
+}
--- /dev/null
+// rustfmt-format_macro_matchers: true
+
+macro_rules! foo {
+ ($a:ident : $b:ty) => {};
+ ($a:ident $b:ident $c:ident) => {};
+}
--- /dev/null
+// rustfmt-format_macro_bodies: false
+
+// with comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 + //comment 1
+ 42
+ //comment 2
+ ),
+ };
+ }};
+}
+
+// without comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 +
+ 42
+ ),
+ };
+ }};
+}
--- /dev/null
+// rustfmt-format_macro_bodies: true
+
+// with comments
+macro_rules! macros {
+ () => {{
+ Struct {
+ field: (
+ 42 + //comment 1
+ 42
+ //comment 2
+ ),
+ };
+ }};
+}
+
+// without comments
+macro_rules! macros {
+ () => {{
+ Struct { field: (42 + 42) };
+ }};
+}
--- /dev/null
+// output doesn't get corrupted when using comments within generic type parameters of a trait
+
+pub trait Something<
+ A,
+ // some comment
+ B,
+ C,
+>
+{
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
+
+pub trait SomethingElse<A, /* some comment */ B, C> {
+ fn a(&self, x: A) -> i32;
+ fn b(&self, x: B) -> i32;
+ fn c(&self, x: C) -> i32;
+}
--- /dev/null
+// rustfmt-version: One
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+>
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write1
+ + fmt::Write2
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer1<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + Printer2<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ >,
+> {
+}
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ > + fmt::Write1
+ + fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ),
+{
+}
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ) + fmt::Write,
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+ mut entries: entryyyyyyyy,
+) -> (impl Fn(
+ AlphabeticalTraversal,
+ Seconddddddddddddddddddddddddddddddddddd,
+) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ + Sendddddddddddddddddddddddddddddddddddddddddddd) {
+}
+
+pub trait SomeTrait:
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+ for<'b> &'b Self: Send
+ + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
--- /dev/null
+// rustfmt-version: Two
+
+// Based on the issue description
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + fmt::Write1
+ + fmt::Write2
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+pub trait PrettyPrinter<'tcx>:
+ fmt::Write
+ + Printer1<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ > + Printer2<
+ 'tcx,
+ Error = fmt::Error,
+ Path = Self,
+ Region = Self,
+ Type = Self,
+ DynExistential = Self,
+ Const = Self,
+ >
+{
+ //
+}
+
+// Some test cases to ensure other cases formatting were not changed
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ >,
+> {
+}
+fn f() -> Box<
+ FnMut() -> Thing<
+ WithType = LongItemName,
+ Error = LONGLONGLONGLONGLONGONGEvenLongerErrorNameLongerLonger,
+ > + fmt::Write1
+ + fmt::Write2,
+> {
+}
+
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ),
+{
+}
+fn foo<F>(foo2: F)
+where
+ F: Fn(
+ // this comment is deleted
+ ) + fmt::Write,
+{
+}
+
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ),
+{
+}
+fn elaborate_bounds<F>(mut mk_cand: F)
+where
+ F: FnMut(
+ &mut ProbeContext,
+ ty::PolyTraitRefffffffffffffffffffffffffffffffff,
+ tyyyyyyyyyyyyyyyyyyyyy::AssociatedItem,
+ ) + fmt::Write,
+{
+}
+
+fn build_sorted_static_get_entry_names(
+ mut entries: entryyyyyyyy,
+) -> (
+ impl Fn(
+ AlphabeticalTraversal,
+ Seconddddddddddddddddddddddddddddddddddd,
+ ) -> Parammmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
+ + Sendddddddddddddddddddddddddddddddddddddddddddd
+) {
+}
+
+pub trait SomeTrait:
+ Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Eqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq
+{
+}
+
+trait B = where
+ for<'b> &'b Self: Send
+ + Cloneeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
+ + Copyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy;
// rustfmt-brace_style: SameLineWhere
// rustfmt-comment_width: 100
// rustfmt-edition: 2018
-// rustfmt-fn_args_layout: Compressed
+// rustfmt-fn_params_layout: Compressed
// rustfmt-hard_tabs: false
// rustfmt-match_block_trailing_comma: true
// rustfmt-max_width: 100
--- /dev/null
+// Test /* comment */ inside trait generics does not get duplicated.
+trait Test</* comment */ T> {}
+
+trait TestTwo</* comment */ T, /* comment */ V> {}
--- /dev/null
+// rustfmt-max_width: 160
+// rustfmt-fn_call_width: 96
+// rustfmt-fn_args_layout: Compressed
+// rustfmt-trailing_comma: Always
+// rustfmt-wrap_comments: true
+
+fn foo() {
+ for elem in try!(gen_epub_book::ops::parse_descriptor_file(
+ &mut try!(File::open(&opts.source_file.1).map_err(|_| {
+ gen_epub_book::Error::Io {
+ desc: "input file",
+ op: "open",
+ more: None,
+ }
+ })),
+ "input file"
+ )) {
+ println!("{}", elem);
+ }
+}
+
+fn write_content() {
+ io::copy(
+ try!(File::open(in_f).map_err(|_| {
+ Error::Io {
+ desc: "Content",
+ op: "open",
+ more: None,
+ }
+ })),
+ w,
+ );
+}
--- /dev/null
+use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerBinding::
+ BluetoothRemoteGATTServerMethods;
--- /dev/null
+fn main() {
+ let x = 1;
+ let y = 3;
+}
--- /dev/null
+fn main() {
+ 7
+}
+
+fn main() {
+ 7
+}
--- /dev/null
+//rustfmt-format_macro_bodies: true
+
+macro_rules! mto_text_left {
+ ($buf:ident, $n:ident, $pos:ident, $state:ident) => {{
+ let cursor = loop {
+ state = match iter.next() {
+ None if $pos == DP::Start => break last_char_idx($buf),
+ None /*some comment */ => break 0,
+ };
+ };
+ Ok(saturate_cursor($buf, cursor))
+ }};
+}
--- /dev/null
+type Foo = impl Send;
+struct Struct<
+ const C: usize = {
+ let _: Foo = ();
+ //~^ ERROR: mismatched types
+ 0
+ },
+>;
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["*","items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should also skip this invocation, as the wildcard covers it
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: []
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should skip this invocation
+items!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+renamed_items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["unknown"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["foo","bar"]
+
+// Should skip this invocation
+foo!(
+ const _: u8 = 0;
+);
+
+// Should skip this invocation
+bar!(
+ const _: u8 = 0;
+);
+
+// Should not skip this invocation
+baz!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["items"]
+
+// Should not skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should skip this invocation
+self::items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["self::items"]
+
+// Should not skip this invocation
+items!(
+ const _: u8 = 0;
+);
--- /dev/null
+// rustfmt-skip_macro_invocations: ["aaa","ccc"]
+
+// These tests demonstrate a realistic use case with use aliases.
+// The use statements should not impact functionality in any way.
+
+use crate::{aaa, bbb, ddd};
+
+// No use alias, invocation in list
+// Should skip this invocation
+aaa!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation in list
+// Should skip this invocation
+use crate::bbb as ccc;
+ccc!(
+ const _: u8 = 0;
+);
+
+// Use alias, invocation not in list
+// Should not skip this invocation
+use crate::ddd as eee;
+eee!(
+ const _: u8 = 0;
+);
+
+// No use alias, invocation not in list
+// Should not skip this invocation
+fff!(
+ const _: u8 = 0;
+);
-// Test tuple litterals
+// Test tuple literals
fn foo() {
let a = (a, a, a, a, a);
/// ```
fn foo() {}
-/// A long commment for wrapping
+/// A long comment for wrapping
/// This is a long long long long long long long long long long long long long
/// long long long long long long long sentence.
fn bar() {}
[dependencies]
cargo_metadata = "0.14"
+cargo-platform = "0.1.2"
regex = "1"
miropt-test-tools = { path = "../miropt-test-tools" }
lazy_static = "1"
walkdir = "2"
ignore = "0.4.18"
+semver = "1.0"
termcolor = "1.1.3"
[[bin]]
//! Checks the licenses of third-party dependencies.
-use cargo_metadata::{Metadata, Package, PackageId, Resolve};
-use std::collections::{BTreeSet, HashSet};
+use cargo_metadata::{DepKindInfo, Metadata, Package, PackageId};
+use std::collections::HashSet;
use std::path::Path;
/// These are licenses that are allowed for all crates, including the runtime,
"autocfg",
"bitflags",
"block-buffer",
- "bumpalo", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
"cc",
"cfg-if",
"chalk-derive",
"chalk-engine",
"chalk-ir",
"chalk-solve",
- "chrono",
"convert_case", // dependency of derive_more
"compiler_builtins",
"cpufeatures",
"dlmalloc",
"either",
"ena",
- "env_logger",
"expect-test",
"fallible-iterator", // dependency of `thorin`
"fastrand",
- "filetime",
"fixedbitset",
"flate2",
"fluent-bundle",
"gsgdt",
"hashbrown",
"hermit-abi",
- "humantime",
"icu_list",
"icu_locid",
"icu_provider",
"icu_provider_adapters",
"icu_provider_macros",
- "if_chain",
"indexmap",
"instant",
"intl-memoizer",
"itertools",
"itoa",
"jobserver",
- "js-sys", // Included in Cargo's dep graph but only activated on wasm32-*-unknown.
"lazy_static",
"libc",
"libloading",
"memmap2",
"memoffset",
"miniz_oxide",
- "num-integer",
- "num-traits",
"num_cpus",
"object",
"odht",
"proc-macro2",
"psm",
"punycode",
- "quick-error",
"quote",
"rand",
"rand_chacha",
"serde",
"serde_derive",
"serde_json",
- "sha-1",
+ "sha1",
"sha2",
"sharded-slab",
"smallvec",
"thiserror-impl",
"thorin-dwp",
"thread_local",
- "time",
"tinystr",
"tinyvec",
"tinyvec_macros",
"valuable",
"version_check",
"wasi",
- // vvv Included in Cargo's dep graph but only activated on wasm32-*-unknown.
- "wasm-bindgen",
- "wasm-bindgen-backend",
- "wasm-bindgen-macro",
- "wasm-bindgen-macro-support",
- "wasm-bindgen-shared",
- // ^^^ Included in Cargo's dep graph but only activated on wasm32-*-unknown.
"winapi",
"winapi-i686-pc-windows-gnu",
"winapi-util",
restricted_dependency_crates: &[&'static str],
bad: &mut bool,
) {
+ let mut deps = HashSet::new();
+ for to_check in restricted_dependency_crates {
+ let to_check = pkg_from_name(metadata, to_check);
+ use cargo_platform::Cfg;
+ use std::str::FromStr;
+ // We don't expect the compiler to ever run on wasm32, so strip
+ // out those dependencies to avoid polluting the permitted list.
+ deps_of_filtered(metadata, &to_check.id, &mut deps, &|dep_kinds| {
+ dep_kinds.iter().any(|dep_kind| {
+ dep_kind
+ .target
+ .as_ref()
+ .map(|target| {
+ !target.matches(
+ "wasm32-unknown-unknown",
+ &[
+ Cfg::from_str("target_arch=\"wasm32\"").unwrap(),
+ Cfg::from_str("target_os=\"unknown\"").unwrap(),
+ ],
+ )
+ })
+ .unwrap_or(true)
+ })
+ });
+ }
+
// Check that the PERMITTED_DEPENDENCIES does not have unused entries.
- for name in permitted_dependencies {
- if !metadata.packages.iter().any(|p| p.name == *name) {
+ for permitted in permitted_dependencies {
+ if !deps.iter().any(|dep_id| &pkg_from_id(metadata, dep_id).name == permitted) {
tidy_error!(
bad,
- "could not find allowed package `{}`\n\
+ "could not find allowed package `{permitted}`\n\
Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
- name
);
}
}
- // Get the list in a convenient form.
- let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
-
- // Check dependencies.
- let mut visited = BTreeSet::new();
- let mut unapproved = BTreeSet::new();
- for &krate in restricted_dependency_crates.iter() {
- let pkg = pkg_from_name(metadata, krate);
- let mut bad =
- check_crate_dependencies(&permitted_dependencies, metadata, &mut visited, pkg);
- unapproved.append(&mut bad);
- }
-
- if !unapproved.is_empty() {
- tidy_error!(bad, "Dependencies for {} not explicitly permitted:", descr);
- for dep in unapproved {
- println!("* {dep}");
- }
- }
-}
-
-/// Checks the dependencies of the given crate from the given cargo metadata to see if they are on
-/// the list of permitted dependencies. Returns a list of disallowed dependencies.
-fn check_crate_dependencies<'a>(
- permitted_dependencies: &'a HashSet<&'static str>,
- metadata: &'a Metadata,
- visited: &mut BTreeSet<&'a PackageId>,
- krate: &'a Package,
-) -> BTreeSet<&'a PackageId> {
- // This will contain bad deps.
- let mut unapproved = BTreeSet::new();
-
- // Check if we have already visited this crate.
- if visited.contains(&krate.id) {
- return unapproved;
- }
- visited.insert(&krate.id);
+ // Get in a convenient form.
+ let permitted_dependencies: HashSet<_> = permitted_dependencies.iter().cloned().collect();
- // If this path is in-tree, we don't require it to be explicitly permitted.
- if krate.source.is_some() {
- // If this dependency is not on `PERMITTED_DEPENDENCIES`, add to bad set.
- if !permitted_dependencies.contains(krate.name.as_str()) {
- unapproved.insert(&krate.id);
+ for dep in deps {
+ let dep = pkg_from_id(metadata, dep);
+ // If this path is in-tree, we don't require it to be explicitly permitted.
+ if dep.source.is_some() {
+ if !permitted_dependencies.contains(dep.name.as_str()) {
+ tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id);
+ }
}
}
-
- // Do a DFS in the crate graph.
- let to_check = deps_of(metadata, &krate.id);
-
- for dep in to_check {
- let mut bad = check_crate_dependencies(permitted_dependencies, metadata, visited, dep);
- unapproved.append(&mut bad);
- }
-
- unapproved
}
/// Prevents multiple versions of some expensive crates.
}
}
-/// Returns a list of dependencies for the given package.
-fn deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
- let resolve = metadata.resolve.as_ref().unwrap();
- let node = resolve
- .nodes
- .iter()
- .find(|n| &n.id == pkg_id)
- .unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
- node.deps
- .iter()
- .map(|dep| {
- metadata.packages.iter().find(|pkg| pkg.id == dep.pkg).unwrap_or_else(|| {
- panic!("could not find dep `{}` for pkg `{}` in resolve", dep.pkg, pkg_id)
- })
- })
- .collect()
-}
-
/// Finds a package with the given name.
fn pkg_from_name<'a>(metadata: &'a Metadata, name: &'static str) -> &'a Package {
let mut i = metadata.packages.iter().filter(|p| p.name == name);
result
}
+fn pkg_from_id<'a>(metadata: &'a Metadata, id: &PackageId) -> &'a Package {
+ metadata.packages.iter().find(|p| &p.id == id).unwrap()
+}
+
/// Finds all the packages that are in the rust runtime.
fn compute_runtime_crates<'a>(metadata: &'a Metadata) -> HashSet<&'a PackageId> {
- let resolve = metadata.resolve.as_ref().unwrap();
let mut result = HashSet::new();
for name in RUNTIME_CRATES {
let id = &pkg_from_name(metadata, name).id;
- normal_deps_of_r(resolve, id, &mut result);
+ deps_of_filtered(metadata, id, &mut result, &|_| true);
}
result
}
-/// Recursively find all normal dependencies.
-fn normal_deps_of_r<'a>(
- resolve: &'a Resolve,
+/// Recursively find all dependencies.
+fn deps_of_filtered<'a>(
+ metadata: &'a Metadata,
pkg_id: &'a PackageId,
result: &mut HashSet<&'a PackageId>,
+ filter: &dyn Fn(&[DepKindInfo]) -> bool,
) {
if !result.insert(pkg_id) {
return;
}
- let node = resolve
+ let node = metadata
+ .resolve
+ .as_ref()
+ .unwrap()
.nodes
.iter()
.find(|n| &n.id == pkg_id)
.unwrap_or_else(|| panic!("could not find `{pkg_id}` in resolve"));
for dep in &node.deps {
- normal_deps_of_r(resolve, &dep.pkg, result);
+ if !filter(&dep.dep_kinds) {
+ continue;
+ }
+ deps_of_filtered(metadata, &dep.pkg, result, filter);
}
}
+fn direct_deps_of<'a>(metadata: &'a Metadata, pkg_id: &'a PackageId) -> Vec<&'a Package> {
+ let resolve = metadata.resolve.as_ref().unwrap();
+ let node = resolve.nodes.iter().find(|n| &n.id == pkg_id).unwrap();
+ node.deps.iter().map(|dep| pkg_from_id(metadata, &dep.pkg)).collect()
+}
+
fn check_rustfix(metadata: &Metadata, bad: &mut bool) {
let cargo = pkg_from_name(metadata, "cargo");
let compiletest = pkg_from_name(metadata, "compiletest");
- let cargo_deps = deps_of(metadata, &cargo.id);
- let compiletest_deps = deps_of(metadata, &compiletest.id);
+ let cargo_deps = direct_deps_of(metadata, &cargo.id);
+ let compiletest_deps = direct_deps_of(metadata, &compiletest.id);
let cargo_rustfix = cargo_deps.iter().find(|p| p.name == "rustfix").unwrap();
let compiletest_rustfix = compiletest_deps.iter().find(|p| p.name == "rustfix").unwrap();
if cargo_rustfix.version != compiletest_rustfix.version {
const ERROR_TESTS_PATH: &str = "tests/ui/error-codes/";
// Error codes that (for some reason) can't have a doctest in their explanation. Error codes are still expected to provide a code example, even if untested.
-const IGNORE_DOCTEST_CHECK: &[&str] =
- &["E0208", "E0464", "E0570", "E0601", "E0602", "E0640", "E0717"];
+const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E0640", "E0717"];
// Error codes that don't yet have a UI test. This list will eventually be removed.
-const IGNORE_UI_TEST_CHECK: &[&str] = &[
- "E0461", "E0465", "E0476", "E0490", "E0514", "E0523", "E0554", "E0640", "E0717", "E0729",
- "E0789",
-];
+const IGNORE_UI_TEST_CHECK: &[&str] =
+ &["E0461", "E0465", "E0476", "E0514", "E0523", "E0554", "E0640", "E0717", "E0729"];
macro_rules! verbose_print {
($verbose:expr, $($fmt:tt)*) => {
pub mod mir_opt_tests;
pub mod pal;
pub mod primitive_docs;
+pub mod rustdoc_gui_tests;
pub mod style;
pub mod target_specific_tests;
pub mod tests_placement;
pub mod unit_tests;
pub mod unstable_book;
pub mod walk;
+pub mod x_version;
let handle = s.spawn(|| {
let mut flag = false;
- $p::check($($args),* , &mut flag);
+ $p::check($($args, )* &mut flag);
if (flag) {
bad.store(true, Ordering::Relaxed);
}
check!(debug_artifacts, &tests_path);
check!(ui_tests, &tests_path);
check!(mir_opt_tests, &tests_path, bless);
+ check!(rustdoc_gui_tests, &tests_path);
// Checks that only make sense for the compiler.
check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose);
check!(alphabetical, &compiler_path);
check!(alphabetical, &library_path);
+ check!(x_version, &root_path, &cargo);
+
let collected = {
drain_handles(&mut handles);
--- /dev/null
+//! Tidy check to ensure that rustdoc GUI tests start with a small description.
+
+use std::path::Path;
+
+pub fn check(path: &Path, bad: &mut bool) {
+ crate::walk::walk(
+ &path.join("rustdoc-gui"),
+ &mut |p| {
+ // If there is no extension, it's very likely a folder and we want to go into it.
+ p.extension().map(|e| e != "goml").unwrap_or(false)
+ },
+ &mut |entry, content| {
+ for line in content.lines() {
+ if !line.starts_with("// ") {
+ tidy_error!(
+ bad,
+ "{}: rustdoc-gui tests must start with a small description",
+ entry.path().display(),
+ );
+ return;
+ } else if line.starts_with("// ") {
+ let parts = line[2..].trim();
+ // We ignore tidy comments.
+ if parts.starts_with("// tidy-") {
+ continue;
+ }
+ // All good!
+ return;
+ }
+ }
+ },
+ );
+}
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";
+const DOUBLE_SPACE_AFTER_DOT: &str = r"\
+Use a single space after dots in comments.";
+
const ANNOTATIONS_TO_IGNORE: &[&str] = &[
"// @!has",
"// @has",
if filename.contains("ignore-tidy") {
return;
}
+ // apfloat shouldn't be changed because of license problems
+ if is_in(file, "compiler", "rustc_apfloat") {
+ return;
+ }
let mut skip_cr = contains_ignore_directive(can_contain, &contents, "cr");
let mut skip_undocumented_unsafe =
contains_ignore_directive(can_contain, &contents, "undocumented-unsafe");
if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
err(LLVM_UNREACHABLE_INFO);
}
+
+ // For now only enforce in compiler
+ let is_compiler = || file.components().any(|c| c.as_os_str() == "compiler");
+ if is_compiler()
+ && line.contains("//")
+ && line
+ .chars()
+ .collect::<Vec<_>>()
+ .windows(4)
+ .any(|cs| matches!(cs, ['.', ' ', ' ', last] if last.is_alphabetic()))
+ {
+ err(DOUBLE_SPACE_AFTER_DOT)
+ }
}
if leading_new_lines {
let mut err = |_| {
--- /dev/null
+use semver::Version;
+use std::path::Path;
+use std::process::{Command, Stdio};
+
+pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
+ let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();
+
+ let child = match cargo_list {
+ Ok(child) => child,
+ Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e),
+ };
+
+ let cargo_list = child.wait_with_output().unwrap();
+
+ if cargo_list.status.success() {
+ let exe_list = String::from_utf8_lossy(&cargo_list.stdout);
+ let exe_list = exe_list.lines();
+
+ let mut installed: Option<Version> = None;
+
+ for line in exe_list {
+ let mut iter = line.split_whitespace();
+ if iter.next() == Some("x") {
+ if let Some(version) = iter.next() {
+ // Check this is the rust-lang/rust x tool installation since it should be
+ // installed at a path containing `src/tools/x`.
+ if let Some(path) = iter.next() {
+ if path.contains(&"src/tools/x") {
+ let version = version.strip_prefix("v").unwrap();
+ installed = Some(Version::parse(version).unwrap());
+ break;
+ }
+ };
+ }
+ } else {
+ continue;
+ }
+ }
+ // Unwrap the some if x is installed, otherwise return because it's fine if x isn't installed.
+ let installed = if let Some(i) = installed { i } else { return };
+
+ if let Some(expected) = get_x_wrapper_version(root, cargo) {
+ if installed < expected {
+ return println!(
+ "Current version of x is {installed}, but the latest version is {expected}\nConsider updating to the newer version of x by running `cargo install --path src/tools/x`"
+ );
+ }
+ } else {
+ return tidy_error!(
+ bad,
+ "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`"
+ );
+ }
+ } else {
+ return tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status);
+ }
+}
+
+// Parse latest version out of `x` Cargo.toml
+fn get_x_wrapper_version(root: &Path, cargo: &Path) -> Option<Version> {
+ let mut cmd = cargo_metadata::MetadataCommand::new();
+ cmd.cargo_path(cargo)
+ .manifest_path(root.join("src/tools/x/Cargo.toml"))
+ .no_deps()
+ .features(cargo_metadata::CargoOpt::AllFeatures);
+ let mut metadata = t!(cmd.exec());
+ metadata.packages.pop().map(|x| x.version)
+}
// assembly-output: emit-asm
-// min-llvm-version: 14.0
+// min-llvm-version: 15.0
// only-x86_64
// revisions: opt-speed opt-size
// [opt-speed] compile-flags: -Copt-level=1
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]
-#![feature(no_core, lang_items, abi_efiapi)]
+#![feature(no_core, lang_items)]
#![no_core]
#[lang="sized"]
// of the sysv64 abi.
//
// needs-llvm-components: x86
-// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu
+// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu -Copt-level=0
#![crate_type = "lib"]
#![no_core]
// of the x86-interrupt abi.
// needs-llvm-components: x86
-// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu
+// compile-flags: -C no-prepopulate-passes --target=x86_64-unknown-linux-gnu -Copt-level=0
#![crate_type = "lib"]
#![no_core]
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
// Hide the LLVM 15+ `allocalign` attribute in the declaration of __rust_alloc
// from the CHECK-NOT above. We don't check the attributes here because we can't rely
// on all of them being set until LLVM 15.
-// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+.*}})
+// CHECK: declare noalias{{.*}} @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+.*}} noundef)
// Hide the `allocalign` attribute in the declaration of __rust_alloc
// from the CHECK-NOT above, and also verify the attributes got set reasonably.
-// CHECK: declare noalias ptr @__rust_alloc(i{{[0-9]+}}, i{{[0-9]+}} allocalign) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
+// CHECK: declare noalias noundef ptr @__rust_alloc(i{{[0-9]+}} noundef, i{{[0-9]+}} allocalign noundef) unnamed_addr [[RUST_ALLOC_ATTRS:#[0-9]+]]
// CHECK-DAG: attributes [[RUST_ALLOC_ATTRS]] = { {{.*}} allockind("alloc,uninitialized,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} }
// ignore-wasm32-bare compiled with panic=abort by default
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
//
#![crate_type = "lib"]
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
// ignore-riscv64
pub struct Foo(u16);
// CHECK-LABEL: @check_lt
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_lt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ult i16 %[[A]], %[[B]]
}
// CHECK-LABEL: @check_le
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_le(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ule i16 %[[A]], %[[B]]
}
// CHECK-LABEL: @check_gt
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_gt(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp ugt i16 %[[A]], %[[B]]
}
// CHECK-LABEL: @check_ge
-// CHECK-SAME: (i16 %[[A:.+]], i16 %[[B:.+]])
+// CHECK-SAME: (i16 noundef %[[A:.+]], i16 noundef %[[B:.+]])
#[no_mangle]
pub fn check_ge(a: Foo, b: Foo) -> bool {
// CHECK: %[[R:.+]] = icmp uge i16 %[[A]], %[[B]]
-// This test is for *-windows-msvc only.
+ // This test is for *-windows-msvc only.
// only-windows
// ignore-gnu
// CHECK: @static_global1 = external local_unnamed_addr global i32
// CHECK: @static_global2 = external local_unnamed_addr global i32
-// CHECK: declare dllimport i32 @dylib_func1(i32)
-// CHECK: declare dllimport i32 @dylib_func2(i32)
-// CHECK: declare i32 @static_func1(i32)
-// CHECK: declare i32 @static_func2(i32)
+// CHECK: declare dllimport noundef i32 @dylib_func1(i32 noundef)
+// CHECK: declare dllimport noundef i32 @dylib_func2(i32 noundef)
+// CHECK: declare noundef i32 @static_func1(i32 noundef)
+// CHECK: declare noundef i32 @static_func2(i32 noundef)
#[link(name = "dummy", kind="dylib")]
extern "C" {
B,
}
-// CHECK: define i8 @match0{{.*}}
+// CHECK: define noundef i8 @match0{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: %1 = icmp eq i8 %0, 2
// CHECK-NEXT: %2 = and i8 %0, 1
C,
}
-// CHECK: define i8 @match1{{.*}}
+// CHECK: define noundef i8 @match1{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: [[DISCR:%.*]] = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
// CHECK-NEXT: switch i8 [[DISCR]], label {{.*}} [
E,
}
-// CHECK: define i8 @match2{{.*}}
+// CHECK: define noundef i8 @match2{{.*}}
// CHECK-NEXT: start:
// CHECK-NEXT: %1 = add i8 %0, 2
// CHECK-NEXT: %2 = zext i8 %1 to i64
trait Copy {}
pub mod tests {
- // CHECK: @f1(i32 inreg %_1, i32 inreg %_2, i32 %_3)
+ // CHECK: @f1(i32 inreg noundef %_1, i32 inreg noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f1(_: i32, _: i32, _: i32) {}
- // CHECK: @f2({{i32\*|ptr}} inreg %_1, {{i32\*|ptr}} inreg %_2, {{i32\*|ptr}} %_3)
+ // CHECK: @f2({{i32\*|ptr}} inreg noundef %_1, {{i32\*|ptr}} inreg noundef %_2, {{i32\*|ptr}} noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f2(_: *const i32, _: *const i32, _: *const i32) {}
- // CHECK: @f3(float %_1, i32 inreg %_2, i32 inreg %_3, i32 %_4)
+ // CHECK: @f3(float noundef %_1, i32 inreg noundef %_2, i32 inreg noundef %_3, i32 noundef %_4)
#[no_mangle]
pub extern "fastcall" fn f3(_: f32, _: i32, _: i32, _: i32) {}
- // CHECK: @f4(i32 inreg %_1, float %_2, i32 inreg %_3, i32 %_4)
+ // CHECK: @f4(i32 inreg noundef %_1, float noundef %_2, i32 inreg noundef %_3, i32 noundef %_4)
#[no_mangle]
pub extern "fastcall" fn f4(_: i32, _: f32, _: i32, _: i32) {}
- // CHECK: @f5(i64 %_1, i32 %_2)
+ // CHECK: @f5(i64 noundef %_1, i32 noundef %_2)
#[no_mangle]
pub extern "fastcall" fn f5(_: i64, _: i32) {}
- // CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg %_2, i32 %_3)
+ // CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg noundef %_2, i32 noundef %_3)
#[no_mangle]
pub extern "fastcall" fn f6(_: bool, _: i32, _: i32) {}
}
#[no_mangle]
pub fn sum(x: u32, y: u32) -> u32 {
-// YES-LABEL: define{{.*}}i32 @sum(i32 %0, i32 %1)
+// YES-LABEL: define{{.*}}i32 @sum(i32 noundef %0, i32 noundef %1)
// YES-NEXT: %3 = add i32 %1, %0
// YES-NEXT: ret i32 %3
-// NO-LABEL: define{{.*}}i32 @sum(i32 %x, i32 %y)
+// NO-LABEL: define{{.*}}i32 @sum(i32 noundef %x, i32 noundef %y)
// NO-NEXT: start:
// NO-NEXT: %z = add i32 %y, %x
// NO-NEXT: ret i32 %z
-// compile-flags: --crate-type=rlib
+// compile-flags: --crate-type=rlib -Copt-level=0
// revisions: aarch64-apple aarch64-linux force x64-apple x64-linux
// [aarch64-apple] needs-llvm-components: aarch64
// [aarch64-apple] compile-flags: --target=aarch64-apple-darwin
// compile-flags: -O -C no-prepopulate-passes
#![crate_type = "lib"]
-#![feature(rustc_attrs)]
use std::mem::MaybeUninit;
use std::num::NonZeroU64;
use std::marker::PhantomPinned;
+use std::ptr::NonNull;
pub struct S {
_field: [i32; 8],
x
}
-// CHECK: i64 @int(i64 %x)
+// CHECK: noundef i64 @int(i64 noundef %x)
#[no_mangle]
pub fn int(x: u64) -> u64 {
x
x
}
-// CHECK: i64 @option_nonzero_int(i64 %x)
+// CHECK: noundef i64 @option_nonzero_int(i64 noundef %x)
#[no_mangle]
pub fn option_nonzero_int(x: Option<NonZeroU64>) -> Option<NonZeroU64> {
x
pub fn borrowed_struct(_: &S) {
}
-// CHECK: @raw_struct({{%S\*|ptr}} %_1)
+// CHECK: @option_borrow({{i32\*|ptr}} noalias noundef readonly align 4 dereferenceable_or_null(4) %x)
+#[no_mangle]
+pub fn option_borrow(x: Option<&i32>) {
+}
+
+// CHECK: @option_borrow_mut({{i32\*|ptr}} noalias noundef align 4 dereferenceable_or_null(4) %x)
+#[no_mangle]
+pub fn option_borrow_mut(x: Option<&mut i32>) {
+}
+
+// CHECK: @raw_struct({{%S\*|ptr}} noundef %_1)
#[no_mangle]
pub fn raw_struct(_: *const S) {
}
+// CHECK: @raw_option_nonnull_struct({{i32\*|ptr}} noundef %_1)
+#[no_mangle]
+pub fn raw_option_nonnull_struct(_: Option<NonNull<S>>) {
+}
+
+
// `Box` can get deallocated during execution of the function, so it should
// not get `dereferenceable`.
// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @_box({{i32\*|ptr}} noalias noundef nonnull align 4 %x)
}
// Hack to get the correct size for the length part in slices
-// CHECK: @helper([[USIZE:i[0-9]+]] %_1)
+// CHECK: @helper([[USIZE:i[0-9]+]] noundef %_1)
#[no_mangle]
pub fn helper(_: usize) {
}
-// CHECK: @slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {
}
-// CHECK: @mutable_slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @mutable_slice({{\[0 x i8\]\*|ptr}} noalias noundef nonnull align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_slice(_: &mut [u8]) {
}
-// CHECK: @unsafe_slice({{\[0 x i16\]\*|ptr}} noundef nonnull align 2 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @unsafe_slice({{\[0 x i16\]\*|ptr}} noundef nonnull align 2 %_1.0, [[USIZE]] noundef %_1.1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {
}
-// CHECK: @raw_slice({{\[0 x i8\]\*|ptr}} %_1.0, [[USIZE]] %_1.1)
+// CHECK: @raw_slice({{\[0 x i8\]\*|ptr}} noundef %_1.0, [[USIZE]] noundef %_1.1)
#[no_mangle]
pub fn raw_slice(_: *const [u8]) {
}
-// CHECK: @str({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @str({{\[0 x i8\]\*|ptr}} noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] noundef %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {
// CHECK: @trait_borrow({{\{\}\*|ptr}} noundef nonnull align 1 %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
-pub fn trait_borrow(_: &Drop) {
+pub fn trait_borrow(_: &dyn Drop) {
+}
+
+// CHECK: @option_trait_borrow({{i8\*|ptr}} noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
+#[no_mangle]
+pub fn option_trait_borrow(x: Option<&dyn Drop>) {
+}
+
+// CHECK: @option_trait_borrow_mut({{i8\*|ptr}} noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
+#[no_mangle]
+pub fn option_trait_borrow_mut(x: Option<&mut dyn Drop>) {
}
-// CHECK: @trait_raw({{\{\}\*|ptr}} %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
+// CHECK: @trait_raw({{\{\}\*|ptr}} noundef %_1.0, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
#[no_mangle]
-pub fn trait_raw(_: *const Drop) {
+pub fn trait_raw(_: *const dyn Drop) {
}
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
-pub fn trait_box(_: Box<Drop>) {
+pub fn trait_box(_: Box<dyn Drop>) {
}
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle]
-pub fn trait_option(x: Option<Box<Drop>>) -> Option<Box<Drop>> {
+pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> {
x
}
-// CHECK: { {{\[0 x i16\]\*|ptr}}, [[USIZE]] } @return_slice({{\[0 x i16\]\*|ptr}} noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
+// CHECK: { {{\[0 x i16\]\*|ptr}}, [[USIZE]] } @return_slice({{\[0 x i16\]\*|ptr}} noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] noundef %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
#![feature(const_eval_select)]
+// compile-flags: -Copt-level=0
#![crate_type = "lib"]
#![feature(core_intrinsics)]
#[no_mangle]
pub fn mask_ptr(ptr: *const u16, mask: usize) -> *const u16 {
// CHECK: call
- // CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%0}}, [[WORD]] %mask)
+ // CHECK-SAME: @llvm.ptrmask.{{p0|p0i8}}.[[WORD]]({{ptr|i8\*}} {{%ptr|%1}}, [[WORD]] %mask)
core::intrinsics::ptr_mask(ptr, mask)
}
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
// prevent optimizing away bounds checks
// compile-flags: -O
+// ignore-debug: the debug assertions get in the way
#![crate_type="rlib"]
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
//
// only-x86_64
// ignore-windows
// in some situations, see https://github.com/rust-lang/rust/issues/96497#issuecomment-1112865218
// compile-flags: -O
-// min-llvm-version: 14.0
+// min-llvm-version: 15.0
#![crate_type="lib"]
#[no_mangle]
// CHECK-LABEL: @vec_extend_via_iter_repeat_n
pub fn vec_extend_via_iter_repeat_n() -> Vec<u8> {
- // CHECK: %[[ADDR:.+]] = tail call dereferenceable_or_null(1234) ptr @__rust_alloc(i64 1234, i64 1)
+ // CHECK: %[[ADDR:.+]] = tail call noundef dereferenceable_or_null(1234) ptr @__rust_alloc(i64 noundef 1234, i64 noundef 1)
// CHECK: tail call void @llvm.memset.p0.i64(ptr noundef nonnull align 1 dereferenceable(1234) %[[ADDR]], i8 42, i64 1234,
let n = 1234_usize;
// CHECK-LABEL: @load_raw_pointer
#[no_mangle]
pub fn load_raw_pointer<'a>(x: &*const i32) -> *const i32 {
- // loaded raw pointer should not have !nonnull, !align, or !noundef metadata
- // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %x, align [[PTR_ALIGNMENT]]{{$}}
+ // loaded raw pointer should not have !nonnull or !align metadata
+ // CHECK: load {{i32\*|ptr}}, {{i32\*\*|ptr}} %x, align [[PTR_ALIGNMENT]], !noundef ![[NOUNDEF:[0-9]+]]{{$}}
*x
}
// CHECK-LABEL: @load_int
#[no_mangle]
pub fn load_int(x: &u16) -> u16 {
- // CHECK: load i16, {{i16\*|ptr}} %x, align 2{{$}}
+ // CHECK: load i16, {{i16\*|ptr}} %x, align 2, !noundef ![[NOUNDEF]]{{$}}
*x
}
// CHECK-LABEL: @load_option_nonzero_int
#[no_mangle]
pub fn load_option_nonzero_int(x: &Option<NonZeroU16>) -> Option<NonZeroU16> {
- // CHECK: load i16, {{i16\*|ptr}} %x, align 2{{$}}
+ // CHECK: load i16, {{i16\*|ptr}} %x, align 2, !noundef ![[NOUNDEF]]{{$}}
*x
}
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
// needs-asm-support
// only-x86_64
-// compile-flags: -C relocation-model=pic
+// compile-flags: -C relocation-model=pic -Copt-level=0
#![crate_type = "rlib"]
-// compile-flags: -C relocation-model=pie
+// compile-flags: -C relocation-model=pie -Copt-level=0
// only-x86_64-unknown-linux-gnu
#![crate_type = "rlib"]
-// compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0
+// compile-flags: -C no-prepopulate-passes -Zmir-opt-level=0 -Copt-level=0
#![crate_type = "lib"]
#[repr(transparent)]
pub struct F32(f32);
-// CHECK: define{{.*}}float @test_F32(float %_1)
+// CHECK: define{{.*}}float @test_F32(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_F32(_: F32) -> F32 { loop {} }
#[repr(transparent)]
pub struct Ptr(*mut u8);
-// CHECK: define{{.*}}{{i8\*|ptr}} @test_Ptr({{i8\*|ptr}} %_1)
+// CHECK: define{{.*}}{{i8\*|ptr}} @test_Ptr({{i8\*|ptr}} noundef %_1)
#[no_mangle]
pub extern "C" fn test_Ptr(_: Ptr) -> Ptr { loop {} }
#[repr(transparent)]
pub struct WithZst(u64, Zst1);
-// CHECK: define{{.*}}i64 @test_WithZst(i64 %_1)
+// CHECK: define{{.*}}i64 @test_WithZst(i64 noundef %_1)
#[no_mangle]
pub extern "C" fn test_WithZst(_: WithZst) -> WithZst { loop {} }
pub struct WithZeroSizedArray(*const f32, [i8; 0]);
// Apparently we use i32* when newtype-unwrapping f32 pointers. Whatever.
-// CHECK: define{{.*}}{{i32\*|ptr}} @test_WithZeroSizedArray({{i32\*|ptr}} %_1)
+// CHECK: define{{.*}}{{i32\*|ptr}} @test_WithZeroSizedArray({{i32\*|ptr}} noundef %_1)
#[no_mangle]
pub extern "C" fn test_WithZeroSizedArray(_: WithZeroSizedArray) -> WithZeroSizedArray { loop {} }
#[repr(transparent)]
pub struct Generic<T>(T);
-// CHECK: define{{.*}}double @test_Generic(double %_1)
+// CHECK: define{{.*}}double @test_Generic(double noundef %_1)
#[no_mangle]
pub extern "C" fn test_Generic(_: Generic<f64>) -> Generic<f64> { loop {} }
#[repr(transparent)]
pub struct LifetimePhantom<'a, T: 'a>(*const T, PhantomData<&'a T>);
-// CHECK: define{{.*}}{{i16\*|ptr}} @test_LifetimePhantom({{i16\*|ptr}} %_1)
+// CHECK: define{{.*}}{{i16\*|ptr}} @test_LifetimePhantom({{i16\*|ptr}} noundef %_1)
#[no_mangle]
pub extern "C" fn test_LifetimePhantom(_: LifetimePhantom<i16>) -> LifetimePhantom<i16> { loop {} }
pub struct Px;
-// CHECK: define{{.*}}float @test_UnitPhantom(float %_1)
+// CHECK: define{{.*}}float @test_UnitPhantom(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_UnitPhantom(_: UnitPhantom<f32, Px>) -> UnitPhantom<f32, Px> { loop {} }
#[repr(transparent)]
pub struct TwoZsts(Zst1, i8, Zst2);
-// CHECK: define{{( dso_local)?}}{{( signext)?}} i8 @test_TwoZsts(i8{{( signext)?}} %_1)
+// CHECK: define{{( dso_local)?}} noundef{{( signext)?}} i8 @test_TwoZsts(i8 noundef{{( signext)?}} %_1)
#[no_mangle]
pub extern "C" fn test_TwoZsts(_: TwoZsts) -> TwoZsts { loop {} }
#[repr(transparent)]
pub struct Nested1(Zst2, Generic<f64>);
-// CHECK: define{{.*}}double @test_Nested1(double %_1)
+// CHECK: define{{.*}}double @test_Nested1(double noundef %_1)
#[no_mangle]
pub extern "C" fn test_Nested1(_: Nested1) -> Nested1 { loop {} }
#[repr(transparent)]
pub struct Nested2(Nested1, Zst1);
-// CHECK: define{{.*}}double @test_Nested2(double %_1)
+// CHECK: define{{.*}}double @test_Nested2(double noundef %_1)
#[no_mangle]
pub extern "C" fn test_Nested2(_: Nested2) -> Nested2 { loop {} }
#[repr(transparent)]
pub struct StructWithProjection(<f32 as Mirror>::It);
-// CHECK: define{{.*}}float @test_Projection(float %_1)
+// CHECK: define{{.*}}float @test_Projection(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_Projection(_: StructWithProjection) -> StructWithProjection { loop {} }
Variant(F32)
}
-// CHECK: define{{.*}}float @test_EnumF32(float %_1)
+// CHECK: define{{.*}}float @test_EnumF32(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_EnumF32(_: EnumF32) -> EnumF32 { loop {} }
Variant(Zst1, F32, Zst2)
}
-// CHECK: define{{.*}}float @test_EnumF32WithZsts(float %_1)
+// CHECK: define{{.*}}float @test_EnumF32WithZsts(float noundef %_1)
#[no_mangle]
pub extern "C" fn test_EnumF32WithZsts(_: EnumF32WithZsts) -> EnumF32WithZsts { loop {} }
field: F32,
}
-// CHECK: define{{.*}}float @test_UnionF32(float %_1)
+// CHECK: define{{.*}} float @test_UnionF32(float %_1)
#[no_mangle]
pub extern "C" fn test_UnionF32(_: UnionF32) -> UnionF32 { loop {} }
a
}
-// CHECK: define signext i8 @f_scalar_1(i8 signext %x)
+// CHECK: define noundef signext i8 @f_scalar_1(i8 noundef signext %x)
#[no_mangle]
pub extern "C" fn f_scalar_1(x: i8) -> i8 {
x
}
-// CHECK: define zeroext i8 @f_scalar_2(i8 zeroext %x)
+// CHECK: define noundef zeroext i8 @f_scalar_2(i8 noundef zeroext %x)
#[no_mangle]
pub extern "C" fn f_scalar_2(x: u8) -> u8 {
x
}
-// CHECK: define signext i32 @f_scalar_3(i32 signext %x)
+// CHECK: define noundef signext i32 @f_scalar_3(i32 noundef signext %x)
#[no_mangle]
pub extern "C" fn f_scalar_3(x: i32) -> u32 {
x as u32
}
-// CHECK: define i64 @f_scalar_4(i64 %x)
+// CHECK: define noundef i64 @f_scalar_4(i64 noundef %x)
#[no_mangle]
pub extern "C" fn f_scalar_4(x: i64) -> i64 {
x
pub extern "C" fn f_agg_large(mut x: Large) {
}
-// CHECK: define void @f_agg_large_ret({{%Large\*|ptr}} {{.*}}sret{{.*}}, i32 signext %i, i8 signext %j)
+// CHECK: define void @f_agg_large_ret({{%Large\*|ptr}} {{.*}}sret{{.*}}, i32 noundef signext %i, i8 noundef signext %j)
#[no_mangle]
pub extern "C" fn f_agg_large_ret(i: i32, j: i8) -> Large {
Large { a: 1, b: 2, c: 3, d: 4 }
}
-// CHECK: define void @f_scalar_stack_1(i64 %0, [2 x i64] %1, i128 %2, {{%Large\*|ptr}} {{.*}}%d, i8 zeroext %e, i8 signext %f, i8 %g, i8 %h)
+// CHECK: define void @f_scalar_stack_1(i64 %0, [2 x i64] %1, i128 %2, {{%Large\*|ptr}} {{.*}}%d, i8 noundef zeroext %e, i8 noundef signext %f, i8 noundef %g, i8 noundef %h)
#[no_mangle]
pub extern "C" fn f_scalar_stack_1(
a: Tiny,
) {
}
-// CHECK: define void @f_scalar_stack_2({{%Large\*|ptr}} {{.*}}sret{{.*}} %0, i64 %a, i128 %1, i128 %2, i64 %d, i8 zeroext %e, i8 %f, i8 %g)
+// CHECK: define void @f_scalar_stack_2({{%Large\*|ptr}} {{.*}}sret{{.*}} %0, i64 noundef %a, i128 %1, i128 %2, i64 noundef %d, i8 noundef zeroext %e, i8 noundef %f, i8 noundef %g)
#[no_mangle]
pub extern "C" fn f_scalar_stack_2(
a: u64,
#[no_mangle]
pub unsafe extern "C" fn f_va_caller() {
- // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 signext 1, i32 signext 2, i64 3, double {{.*}}, double {{.*}}, i64 {{.*}}, [2 x i64] {{.*}}, i128 {{.*}}, {{%Large\*|ptr}} {{.*}})
+ // CHECK: call noundef signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i64 noundef 3, double {{.*}}, double {{.*}}, i64 {{.*}}, [2 x i64] {{.*}}, i128 {{.*}}, {{%Large\*|ptr}} {{.*}})
f_va_callee(
1,
2i32,
SmallAligned { a: 11 },
Large { a: 12, b: 13, c: 14, d: 15 },
);
- // CHECK: call signext i32 (i32, ...) @f_va_callee(i32 signext 1, i32 signext 2, i32 signext 3, i32 signext 4, i128 {{.*}}, i32 signext 6, i32 signext 7, i32 8, i32 9)
+ // CHECK: call noundef signext i32 (i32, ...) @f_va_callee(i32 noundef signext 1, i32 noundef signext 2, i32 noundef signext 3, i32 noundef signext 4, i128 {{.*}}, i32 noundef signext 6, i32 noundef signext 7, i32 noundef 8, i32 noundef 9)
f_va_callee(1, 2i32, 3i32, 4i32, SmallAligned { a: 5 }, 6i32, 7i32, 8i32, 9i32);
}
// Verifies that pointer type membership tests for indirect calls are emitted.
//
// needs-sanitizer-cfi
-// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi
+// compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi -Copt-level=0
#![crate_type="lib"]
// [aarch64] needs-llvm-components: aarch64
// [x86_64] compile-flags: --target x86_64-unknown-none
// [x86_64] needs-llvm-components:
-// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi
+// compile-flags: -Cno-prepopulate-passes -Zsanitizer=kcfi -Copt-level=0
#![crate_type="lib"]
#![feature(no_core, lang_items)]
// revisions:ASAN ASAN-RECOVER MSAN MSAN-RECOVER MSAN-RECOVER-LTO
// no-prefer-dynamic
//
-//[ASAN] compile-flags: -Zsanitizer=address
-//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address
+//[ASAN] compile-flags: -Zsanitizer=address -Copt-level=0
+//[ASAN-RECOVER] compile-flags: -Zsanitizer=address -Zsanitizer-recover=address -Copt-level=0
//[MSAN] compile-flags: -Zsanitizer=memory
//[MSAN-RECOVER] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory
//[MSAN-RECOVER-LTO] compile-flags: -Zsanitizer=memory -Zsanitizer-recover=memory -C lto=fat
// ASAN-RECOVER-NOT: unreachable
// ASAN: }
//
-// MSAN-LABEL: define dso_local i32 @penguin(
+// MSAN-LABEL: define dso_local noundef i32 @penguin(
// MSAN: call void @__msan_warning{{(_with_origin_noreturn\(i32 0\)|_noreturn\(\))}}
// MSAN: unreachable
// MSAN: }
//
-// MSAN-RECOVER-LABEL: define dso_local i32 @penguin(
+// MSAN-RECOVER-LABEL: define dso_local noundef i32 @penguin(
// MSAN-RECOVER: call void @__msan_warning{{(_with_origin\(i32 0\)|\(\))}}
// MSAN-RECOVER-NOT: unreachable
// MSAN-RECOVER: }
//
-// MSAN-RECOVER-LTO-LABEL: define dso_local i32 @penguin(
+// MSAN-RECOVER-LTO-LABEL: define dso_local noundef i32 @penguin(
// MSAN-RECOVER-LTO: call void @__msan_warning{{(_with_origin\(i32 0\)|\(\))}}
// MSAN-RECOVER-LTO-NOT: unreachable
// MSAN-RECOVER-LTO: }
pair
}
-// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 %pair.1)
+// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 noundef %pair.1)
#[no_mangle]
pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) {
pair
}
-// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 %pair.0, i1 noundef zeroext %pair.1)
+// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 noundef %pair.0, i1 noundef zeroext %pair.1)
#[no_mangle]
pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) {
pair
-// compile-flags: -Cno-prepopulate-passes
+// compile-flags: -Cno-prepopulate-passes -Copt-level=0
// revisions:x86_64 i686 aarch64-apple aarch64-windows aarch64-linux arm riscv
// it to be marked `dso_local` as well, given the static relocation model.
//
// CHECK: @extern_static = external dso_local local_unnamed_addr global i8
-// CHECK: define dso_local i8 @access_extern() {{.*}}
-// CHECK: declare dso_local i8 @extern_fn() {{.*}}
+// CHECK: define dso_local noundef i8 @access_extern() {{.*}}
+// CHECK: declare dso_local noundef i8 @extern_fn() {{.*}}
#[no_mangle]
pub fn access_extern() -> u8 {
// FIXME(eddyb) all of these tests show memory stores and loads, even after a
// scalar `bitcast`, more special-casing is required to remove `alloca` usage.
-// CHECK-LABEL: define{{.*}}i32 @f32_to_bits(float %x)
+// CHECK-LABEL: define{{.*}}i32 @f32_to_bits(float noundef %x)
// CHECK: store i32 %{{.*}}, {{.*}} %0
// CHECK-NEXT: %[[RES:.*]] = load i32, {{.*}} %0
// CHECK: ret i32 %[[RES]]
unsafe { std::mem::transmute(b) }
}
-// CHECK-LABEL: define{{.*}}noundef zeroext i1 @byte_to_bool(i8 %byte)
+// CHECK-LABEL: define{{.*}}noundef zeroext i1 @byte_to_bool(i8 noundef %byte)
// CHECK: %1 = trunc i8 %byte to i1
// CHECK-NEXT: %2 = zext i1 %1 to i8
// CHECK-NEXT: store i8 %2, {{.*}} %0
std::mem::transmute(byte)
}
-// CHECK-LABEL: define{{.*}}{{i8\*|ptr}} @ptr_to_ptr({{i16\*|ptr}} %p)
+// CHECK-LABEL: define{{.*}}{{i8\*|ptr}} @ptr_to_ptr({{i16\*|ptr}} noundef %p)
// CHECK: store {{i8\*|ptr}} %{{.*}}, {{.*}} %0
// CHECK-NEXT: %[[RES:.*]] = load {{i8\*|ptr}}, {{.*}} %0
// CHECK: ret {{i8\*|ptr}} %[[RES]]
// Tests below show the non-special-cased behavior (with the possible
// future special-cased instructions in the "NOTE(eddyb)" comments).
-// CHECK: define{{.*}}[[USIZE:i[0-9]+]] @ptr_to_int({{i16\*|ptr}} %p)
+// CHECK: define{{.*}}[[USIZE:i[0-9]+]] @ptr_to_int({{i16\*|ptr}} noundef %p)
// NOTE(eddyb) see above, the following two CHECK lines should ideally be this:
// %2 = ptrtoint i16* %p to [[USIZE]]
unsafe { std::mem::transmute(p) }
}
-// CHECK: define{{.*}}{{i16\*|ptr}} @int_to_ptr([[USIZE]] %i)
+// CHECK: define{{.*}}{{i16\*|ptr}} @int_to_ptr([[USIZE]] noundef %i)
// NOTE(eddyb) see above, the following two CHECK lines should ideally be this:
// %2 = inttoptr [[USIZE]] %i to i16*
// ignore-emscripten
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
// Test that tuples get optimized layout, in particular with a ZST in the last field (#63244)
#![crate_type = "lib"]
-// CHECK-LABEL: define{{.*}}i32 @test(i32 %a, i32 %b)
+// CHECK-LABEL: define{{.*}}i32 @test(i32 noundef %a, i32 noundef %b)
#[no_mangle]
pub fn test(a: u32, b: u32) -> u32 {
let c = a + b;
vec![Some(false); n]
}
+// CHECK-LABEL: @vec_option_i32
+#[no_mangle]
+pub fn vec_option_i32(n: usize) -> Vec<Option<i32>> {
+ // CHECK-NOT: call {{.*}}alloc::vec::from_elem
+ // CHECK-NOT: call {{.*}}reserve
+ // CHECK-NOT: call {{.*}}__rust_alloc(
+
+ // CHECK: call {{.*}}__rust_alloc_zeroed(
+
+ // CHECK-NOT: call {{.*}}alloc::vec::from_elem
+ // CHECK-NOT: call {{.*}}reserve
+ // CHECK-NOT: call {{.*}}__rust_alloc(
+
+ // CHECK: ret void
+ vec![None; n]
+}
+
// Ensure that __rust_alloc_zeroed gets the right attributes for LLVM to optimize it away.
-// CHECK: declare noalias ptr @__rust_alloc_zeroed(i64, i64 allocalign) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]]
+// CHECK: declare noalias noundef ptr @__rust_alloc_zeroed(i64 noundef, i64 allocalign noundef) unnamed_addr [[RUST_ALLOC_ZEROED_ATTRS:#[0-9]+]]
// CHECK-DAG: attributes [[RUST_ALLOC_ZEROED_ATTRS]] = { {{.*}} allockind("alloc,zeroed,aligned") allocsize(0) uwtable "alloc-family"="__rust_alloc" {{.*}} }
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: -C no-prepopulate-passes -Copt-level=0
#![crate_type = "lib"]
#![feature(repr_simd)]
+++ /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: impl std::future::Future<Output = ()>,
+ _1: impl std::future::Future<Output = ()>,
+ },
+ 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
+}
})
}
-static S: i32 = 5;
-static mut T: i32 = 10;
+static S: i32 = 0x05050505;
+static mut T: i32 = 0x0a0a0a0a;
// EMIT_MIR consts.statics.built.after.mir
#[custom_mir(dialect = "built")]
fn statics() {
}
alloc2 (static: T, size: 4, align: 4) {
- 0a 00 00 00 │ ....
+ 0a 0a 0a 0a │ ....
}
alloc1 (static: S, size: 4, align: 4) {
- 05 00 00 00 │ ....
+ 05 05 05 05 │ ....
}
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
}
}
}
alloc1 (static: STATIC, size: 4, align: 4) {
- 2a 00 00 00 │ *...
+ 42 42 42 42 │ BBBB
}
// unit-test
// compile-flags: -O
-static mut STATIC: u32 = 42;
+static mut STATIC: u32 = 0x42424242;
// EMIT_MIR mutable_variable_no_prop.main.ConstProp.diff
fn main() {
let mut _6: (); // in scope 0 at $DIR/inline_into_box_place.rs:+1:42: +1:43
let mut _7: *const std::vec::Vec<u32>; // in scope 0 at $DIR/inline_into_box_place.rs:+1:29: +1:43
+ let mut _8: &mut std::vec::Vec<u32>; // in scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
++ let mut _9: std::vec::Vec<u32>; // in scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
scope 1 {
debug _x => _1; // in scope 1 at $DIR/inline_into_box_place.rs:+1:9: +1:11
}
scope 2 {
}
+ scope 3 (inlined Vec::<u32>::new) { // at $DIR/inline_into_box_place.rs:8:33: 8:43
-+ let mut _9: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ let mut _10: alloc::raw_vec::RawVec<u32>; // in scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ }
bb0: {
- (*_7) = Vec::<u32>::new() -> [return: bb2, unwind: bb5]; // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ StorageLive(_8); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ _8 = &mut (*_7); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
-+ StorageLive(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ _9 = const _; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageLive(_9); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
++ StorageLive(_10); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ _10 = const _; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
// mir::Constant
- // + span: $DIR/inline_into_box_place.rs:8:33: 8:41
- // + user_ty: UserType(1)
+ // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Unevaluated(alloc::raw_vec::RawVec::<T>::NEW, [u32], None) }
-+ Deinit((*_8)); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ ((*_8).0: alloc::raw_vec::RawVec<u32>) = move _9; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ ((*_8).1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ StorageDead(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ Deinit(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ (_9.0: alloc::raw_vec::RawVec<u32>) = move _10; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ (_9.1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ StorageDead(_10); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ (*_8) = move _9; // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
++ StorageDead(_9); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ StorageDead(_8); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
_1 = move _5; // scope 0 at $DIR/inline_into_box_place.rs:+1:29: +1:43
StorageDead(_5); // scope 0 at $DIR/inline_into_box_place.rs:+1:42: +1:43
--- /dev/null
+- // MIR for `outer` before Inline
++ // MIR for `outer` after Inline
+
+ fn outer() -> usize {
+ let mut _0: usize; // return place in scope 0 at $DIR/issue_106141.rs:+0:19: +0:24
++ scope 1 (inlined inner) { // at $DIR/issue_106141.rs:2:5: 2:12
++ let mut _1: bool; // in scope 1 at $DIR/issue_106141.rs:13:8: 13:21
++ let mut _2: bool; // in scope 1 at $DIR/issue_106141.rs:13:8: 13:21
++ let mut _3: &[bool; 1]; // in scope 1 at $DIR/issue_106141.rs:11:18: 11:25
++ scope 2 {
++ debug buffer => _3; // in scope 2 at $DIR/issue_106141.rs:11:9: 11:15
++ scope 3 {
++ debug index => _0; // in scope 3 at $DIR/issue_106141.rs:12:9: 12:14
++ }
++ }
++ }
+
+ bb0: {
+- _0 = inner() -> bb1; // scope 0 at $DIR/issue_106141.rs:+1:5: +1:12
++ StorageLive(_3); // scope 0 at $DIR/issue_106141.rs:+1:5: +1:12
++ _3 = const _; // scope 1 at $DIR/issue_106141.rs:11:18: 11:25
+ // mir::Constant
+- // + span: $DIR/issue_106141.rs:2:5: 2:10
+- // + literal: Const { ty: fn() -> usize {inner}, val: Value(<ZST>) }
++ // + span: $DIR/issue_106141.rs:11:18: 11:25
++ // + literal: Const { ty: &[bool; 1], val: Unevaluated(inner, [], Some(promoted[0])) }
++ _0 = index() -> bb1; // scope 2 at $DIR/issue_106141.rs:12:17: 12:24
++ // mir::Constant
++ // + span: $DIR/issue_106141.rs:12:17: 12:22
++ // + literal: Const { ty: fn() -> usize {index}, val: Value(<ZST>) }
+ }
+
+ bb1: {
++ StorageLive(_1); // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ _2 = Lt(_0, const 1_usize); // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ assert(move _2, "index out of bounds: the length is {} but the index is {}", const 1_usize, _0) -> bb2; // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ }
++
++ bb2: {
++ _1 = (*_3)[_0]; // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ switchInt(move _1) -> [0: bb3, otherwise: bb4]; // scope 3 at $DIR/issue_106141.rs:13:8: 13:21
++ }
++
++ bb3: {
++ _0 = const 0_usize; // scope 3 at $DIR/issue_106141.rs:16:9: 16:10
++ goto -> bb4; // scope 3 at $DIR/issue_106141.rs:13:5: 17:6
++ }
++
++ bb4: {
++ StorageDead(_1); // scope 3 at $DIR/issue_106141.rs:17:5: 17:6
++ StorageDead(_3); // scope 0 at $DIR/issue_106141.rs:+1:5: +1:12
+ return; // scope 0 at $DIR/issue_106141.rs:+2:2: +2:2
+ }
+ }
+
--- /dev/null
+pub fn outer() -> usize {
+ inner()
+}
+
+fn index() -> usize {
+ loop {}
+}
+
+#[inline]
+fn inner() -> usize {
+ let buffer = &[true];
+ let index = index();
+ if buffer[index] {
+ index
+ } else {
+ 0
+ }
+}
+
+fn main() {
+ outer();
+}
+
+// EMIT_MIR issue_106141.outer.Inline.diff
--- /dev/null
+- // MIR for `generic` before InstCombine
++ // MIR for `generic` after InstCombine
+
+ fn generic() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/intrinsic_asserts.rs:+0:21: +0:21
+ let _1: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:46
+ let _2: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:47
+ let _3: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:60
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:46
+ _1 = assert_inhabited::<T>() -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:46
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:25:5: 25:44
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_inhabited::<T>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:46: +1:47
+ StorageLive(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:47
+ _2 = assert_zero_valid::<T>() -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:47
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:26:5: 26:45
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_zero_valid::<T>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:47: +2:48
+ StorageLive(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:60
+ _3 = assert_mem_uninitialized_valid::<T>() -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:60
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:27:5: 27:58
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<T>}, val: Value(<ZST>) }
+ }
+
+ bb3: {
+ StorageDead(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:60: +3:61
+ nop; // scope 0 at $DIR/intrinsic_asserts.rs:+0:21: +4:2
+ return; // scope 0 at $DIR/intrinsic_asserts.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+- // MIR for `panics` before InstCombine
++ // MIR for `panics` after InstCombine
+
+ fn panics() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/intrinsic_asserts.rs:+0:17: +0:17
+ let _1: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
+ let _2: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
+ let _3: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
+- _1 = assert_inhabited::<Never>() -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
++ _1 = assert_inhabited::<Never>(); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:50
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:17:5: 17:48
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_inhabited::<Never>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:50: +1:51
+ StorageLive(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
+- _2 = assert_zero_valid::<&u8>() -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
++ _2 = assert_zero_valid::<&u8>(); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:49
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:18:5: 18:47
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_zero_valid::<&u8>}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:49: +2:50
+ StorageLive(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
+- _3 = assert_mem_uninitialized_valid::<&u8>() -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
++ _3 = assert_mem_uninitialized_valid::<&u8>(); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:62
+ // mir::Constant
+ // + span: $DIR/intrinsic_asserts.rs:19:5: 19:60
+ // + user_ty: UserType(1)
+ // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<&u8>}, val: Value(<ZST>) }
+ }
+
+ bb3: {
+ StorageDead(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:62: +3:63
+ nop; // scope 0 at $DIR/intrinsic_asserts.rs:+0:17: +4:2
+ return; // scope 0 at $DIR/intrinsic_asserts.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+- // MIR for `removable` before InstCombine
++ // MIR for `removable` after InstCombine
+
+ fn removable() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/intrinsic_asserts.rs:+0:20: +0:20
+ let _1: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+ let _2: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+ let _3: (); // in scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+- _1 = assert_inhabited::<()>() -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+- // mir::Constant
+- // + span: $DIR/intrinsic_asserts.rs:7:5: 7:45
+- // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_inhabited::<()>}, val: Value(<ZST>) }
++ goto -> bb1; // scope 0 at $DIR/intrinsic_asserts.rs:+1:5: +1:47
+ }
+
+ bb1: {
+ StorageDead(_1); // scope 0 at $DIR/intrinsic_asserts.rs:+1:47: +1:48
+ StorageLive(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+- _2 = assert_zero_valid::<u8>() -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+- // mir::Constant
+- // + span: $DIR/intrinsic_asserts.rs:8:5: 8:46
+- // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_zero_valid::<u8>}, val: Value(<ZST>) }
++ goto -> bb2; // scope 0 at $DIR/intrinsic_asserts.rs:+2:5: +2:48
+ }
+
+ bb2: {
+ StorageDead(_2); // scope 0 at $DIR/intrinsic_asserts.rs:+2:48: +2:49
+ StorageLive(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+- _3 = assert_mem_uninitialized_valid::<u8>() -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+- // mir::Constant
+- // + span: $DIR/intrinsic_asserts.rs:9:5: 9:59
+- // + literal: Const { ty: extern "rust-intrinsic" fn() {assert_mem_uninitialized_valid::<u8>}, val: Value(<ZST>) }
++ goto -> bb3; // scope 0 at $DIR/intrinsic_asserts.rs:+3:5: +3:61
+ }
+
+ bb3: {
+ StorageDead(_3); // scope 0 at $DIR/intrinsic_asserts.rs:+3:61: +3:62
+ nop; // scope 0 at $DIR/intrinsic_asserts.rs:+0:20: +4:2
+ return; // scope 0 at $DIR/intrinsic_asserts.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+// All these assertions pass, so all the intrinsic calls should be deleted.
+// EMIT_MIR intrinsic_asserts.removable.InstCombine.diff
+pub fn removable() {
+ core::intrinsics::assert_inhabited::<()>();
+ core::intrinsics::assert_zero_valid::<u8>();
+ core::intrinsics::assert_mem_uninitialized_valid::<u8>();
+}
+
+enum Never {}
+
+// These assertions all diverge, so their target blocks should become None.
+// EMIT_MIR intrinsic_asserts.panics.InstCombine.diff
+pub fn panics() {
+ core::intrinsics::assert_inhabited::<Never>();
+ core::intrinsics::assert_zero_valid::<&u8>();
+ core::intrinsics::assert_mem_uninitialized_valid::<&u8>();
+}
+
+// Whether or not these asserts pass isn't known, so they shouldn't be modified.
+// EMIT_MIR intrinsic_asserts.generic.InstCombine.diff
+pub fn generic<T>() {
+ core::intrinsics::assert_inhabited::<T>();
+ core::intrinsics::assert_zero_valid::<T>();
+ core::intrinsics::assert_mem_uninitialized_valid::<T>();
+}
+++ /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
_3 = _1; // scope 2 at $DIR/issue_75439.rs:+2:47: +2:52
_2 = transmute::<[u8; 16], [u32; 4]>(move _3) -> bb1; // scope 2 at $DIR/issue_75439.rs:+2:37: +2:53
// mir::Constant
- // + span: $DIR/issue_75439.rs:7:37: 7:46
+ // + span: $DIR/issue_75439.rs:8:37: 8:46
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn([u8; 16]) -> [u32; 4] {transmute::<[u8; 16], [u32; 4]>}, val: Value(<ZST>) }
}
_6 = _4; // scope 4 at $DIR/issue_75439.rs:+5:33: +5:35
_5 = transmute::<u32, [u8; 4]>(move _6) -> bb7; // scope 4 at $DIR/issue_75439.rs:+5:23: +5:36
// mir::Constant
- // + span: $DIR/issue_75439.rs:10:23: 10:32
+ // + span: $DIR/issue_75439.rs:11:23: 11:32
// + literal: Const { ty: unsafe extern "rust-intrinsic" fn(u32) -> [u8; 4] {transmute::<u32, [u8; 4]>}, val: Value(<ZST>) }
}
// EMIT_MIR issue_75439.foo.MatchBranchSimplification.diff
+// ignore-endian-big
use std::mem::transmute;
|
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
#![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();
}
auto trait Freeze {}
#[lang = "start"]
-fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
0
}
define-function: (
"check-colors",
- (theme, main_color, title_color, fqn_color, fqn_type_color, src_link_color, sidebar_link_color),
+ (theme, main_color, title_color, main_heading_color, main_heading_type_color, src_link_color, sidebar_link_color),
block {
goto: "file://" + |DOC_PATH| + "/staged_api/struct.Foo.html"
// This is needed to ensure that the text color is computed.
reload:
assert-css: ("#toggle-all-docs", {"color": |main_color|})
- assert-css: (".fqn a:nth-of-type(1)", {"color": |fqn_color|})
- assert-css: (".fqn a:nth-of-type(2)", {"color": |fqn_type_color|})
+ assert-css: (".main-heading h1 a:nth-of-type(1)", {"color": |main_heading_color|})
+ assert-css: (".main-heading a:nth-of-type(2)", {"color": |main_heading_type_color|})
assert-css: (
".rightside .srclink",
{"color": |src_link_color|, "text-decoration": "none solid " + |src_link_color|},
assert-css: ("#top-doc-prose-title", {"color": |title_color|})
assert-css: (".sidebar a", {"color": |sidebar_link_color|})
- assert-css: ("h1.fqn a", {"color": |title_color|})
+ assert-css: (".main-heading h1 a", {"color": |title_color|})
// We move the cursor over the "Implementations" title so the anchor is displayed.
move-cursor-to: "h2#implementations"
"theme": "ayu",
"main_color": "rgb(197, 197, 197)",
"title_color": "rgb(255, 255, 255)",
- "fqn_color": "rgb(255, 255, 255)",
- "fqn_type_color": "rgb(255, 160, 165)",
+ "main_heading_color": "rgb(255, 255, 255)",
+ "main_heading_type_color": "rgb(255, 160, 165)",
"src_link_color": "rgb(57, 175, 215)",
"sidebar_link_color": "rgb(83, 177, 219)",
},
"theme": "dark",
"main_color": "rgb(221, 221, 221)",
"title_color": "rgb(221, 221, 221)",
- "fqn_color": "rgb(221, 221, 221)",
- "fqn_type_color": "rgb(45, 191, 184)",
+ "main_heading_color": "rgb(221, 221, 221)",
+ "main_heading_type_color": "rgb(45, 191, 184)",
"src_link_color": "rgb(210, 153, 29)",
"sidebar_link_color": "rgb(253, 191, 53)",
},
"theme": "light",
"main_color": "rgb(0, 0, 0)",
"title_color": "rgb(0, 0, 0)",
- "fqn_color": "rgb(0, 0, 0)",
- "fqn_type_color": "rgb(173, 55, 138)",
+ "main_heading_color": "rgb(0, 0, 0)",
+ "main_heading_type_color": "rgb(173, 55, 138)",
"src_link_color": "rgb(56, 115, 173)",
"sidebar_link_color": "rgb(53, 109, 164)",
},
+// Small test to ensure the "src-line-numbers" element is only present once on
+// the page.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: ".srclink"
wait-for: ".src-line-numbers"
// Check that their content is inside <pre><code>
assert-count: (".example-wrap pre > code", 4)
// Check that function signature is inside <pre><code>
-assert: "pre.rust.fn > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
-assert: "pre.rust.struct > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/enum.AnEnum.html"
-assert: "pre.rust.enum > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/trait.AnotherOne.html"
-assert: "pre.rust.trait > code"
+assert: ".item-decl pre.rust > code"
goto: "file://" + |DOC_PATH| + "/test_docs/type.SomeType.html"
-assert: "pre.rust.typedef > code"
+assert: ".item-decl pre.rust > code"
// This is a complex selector, so here's how it works:
//
// * //*[@class='item-decl'] — selects element of any tag with classes docblock and item-decl
-// * /pre[@class='rust trait'] — selects immediate child with tag pre and classes rust and trait
+// * /pre[@class='rust'] — selects immediate child with tag pre and class rust
// * /code — selects immediate child with tag code
// * /a[@class='constant'] — selects immediate child with tag a and class constant
// * //text() — selects child that is text node
// This uses '/parent::*' as a proxy for the style of the text node.
// We can't just select the '<a>' because intermediate tags could be added.
assert-count: (
- "//*[@class='item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*",
+ "//*[@class='item-decl']/pre[@class='rust']/code/a[@class='constant']//text()/parent::*",
1,
)
assert-css: (
- "//*[@class='item-decl']/pre[@class='rust trait']/code/a[@class='constant']//text()/parent::*",
+ "//*[@class='item-decl']/pre[@class='rust']/code/a[@class='constant']//text()/parent::*",
{"font-weight": "400"},
)
// 14px 0.875rem
goto: "file://" + |DOC_PATH| + "/test_docs/struct.HeavilyDocumentedStruct.html"
-assert-css: ("h1.fqn", {"font-size": "24px"})
+assert-css: (".main-heading h1", {"font-size": "24px"})
assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
goto: "file://" + |DOC_PATH| + "/test_docs/enum.HeavilyDocumentedEnum.html"
-assert-css: ("h1.fqn", {"font-size": "24px"})
+assert-css: (".main-heading h1", {"font-size": "24px"})
assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
goto: "file://" + |DOC_PATH| + "/test_docs/union.HeavilyDocumentedUnion.html"
-assert-css: ("h1.fqn", {"font-size": "24px"})
+assert-css: (".main-heading h1", {"font-size": "24px"})
assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
goto: "file://" + |DOC_PATH| + "/test_docs/macro.heavily_documented_macro.html"
-assert-css: ("h1.fqn", {"font-size": "24px"})
+assert-css: (".main-heading h1", {"font-size": "24px"})
assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
// This test checks that code blocks in list are supported.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
goto: "./fn.check_list_code_block.html"
-assert: ("pre.rust.fn")
+assert: (".item-decl pre.rust")
// This test ensures that the margins on methods are coherent inside an impl block.
goto: "file://" + |DOC_PATH| + "/test_docs/trait_members/struct.HasTrait.html#impl-TraitMembers-for-HasTrait"
-assert-count: ("#trait-implementations-list > .rustdoc-toggle", 1)
+assert-count: ("#trait-implementations-list > .toggle", 1)
compare-elements-css: (
// compare margin on type with margin on method
- "#trait-implementations-list .impl-items > .rustdoc-toggle:nth-child(1) > summary",
- "#trait-implementations-list .impl-items > .rustdoc-toggle:nth-child(2) > summary",
+ "#trait-implementations-list .impl-items > .toggle:nth-child(1) > summary",
+ "#trait-implementations-list .impl-items > .toggle:nth-child(2) > summary",
["margin"]
)
compare-elements-css: (
// compare margin on type with margin on method
- "#trait-implementations-list .impl-items > .rustdoc-toggle:nth-child(1)",
- "#trait-implementations-list .impl-items > .rustdoc-toggle:nth-child(2)",
+ "#trait-implementations-list .impl-items > .toggle:nth-child(1)",
+ "#trait-implementations-list .impl-items > .toggle:nth-child(2)",
["margin"]
)
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 ensures that the scraped examples buttons are working as expecting
+// when 'Enter' key is pressed when they're focused.
goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
// The next/prev buttons vertically scroll the code viewport between examples
"help_hover_border": "rgb(0, 0, 0)",
"help_hover_color": "rgb(0, 0, 0)",
})
+
+// Now testing the top and bottom background in case there is only one scraped examples.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test.html"
+
+define-function: (
+ "check-background",
+ (theme, background_color_start, background_color_end),
+ block {
+ local-storage: { "rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false", }
+ reload:
+ assert-css: (".scraped-example:not(.expanded) .code-wrapper::before", {
+ "background-image": "linear-gradient(" + |background_color_start| + ", " +
+ |background_color_end| + ")",
+ })
+ assert-css: (".scraped-example:not(.expanded) .code-wrapper::after", {
+ "background-image": "linear-gradient(to top, " + |background_color_start| + ", " +
+ |background_color_end| + ")",
+ })
+ },
+)
+
+call-function: ("check-background", {
+ "theme": "ayu",
+ "background_color_start": "rgb(15, 20, 25)",
+ "background_color_end": "rgba(15, 20, 25, 0)",
+})
+call-function: ("check-background", {
+ "theme": "dark",
+ "background_color_start": "rgb(53, 53, 53)",
+ "background_color_end": "rgba(53, 53, 53, 0)",
+})
+call-function: ("check-background", {
+ "theme": "light",
+ "background_color_start": "rgb(255, 255, 255)",
+ "background_color_end": "rgba(255, 255, 255, 0)",
+})
// First, we check that the first page doesn't have the string we're looking for to ensure
// that the feature is changing page as expected.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
-assert-text-false: (".fqn", "Struct test_docs::Foo")
+assert-text-false: (".main-heading h1", "Struct test_docs::Foo")
// We now check that we land on the search result page if "go_to_first" isn't set.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo"
// Waiting for the search results to appear...
wait-for: "#search-tabs"
-assert-text-false: (".fqn", "Struct test_docs::Foo")
+assert-text-false: (".main-heading h1", "Struct test_docs::Foo")
// Ensure that the search results are displayed, not the "normal" content.
assert-css: ("#main-content", {"display": "none"})
// Now we can check that the feature is working as expected!
goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo&go_to_first=true"
// Waiting for the page to load...
-wait-for-text: (".fqn", "Struct test_docs::Foo")
+wait-for-text: (".main-heading h1", "Struct test_docs::Foo")
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 .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 .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"
// This test ensures that clicking on a method summary, but not on the "[-]",
// doesn't toggle the <details>.
goto: "file://" + |DOC_PATH| + "/lib2/struct.Foo.html"
-assert-attribute: (".impl-items .rustdoc-toggle", {"open": ""})
+assert-attribute: (".impl-items .toggle", {"open": ""})
click: "h4.code-header" // This is the position of "pub" in "pub fn a_method"
-assert-attribute: (".impl-items .rustdoc-toggle", {"open": ""})
+assert-attribute: (".impl-items .toggle", {"open": ""})
click-with-offset: (
- ".impl-items .rustdoc-toggle summary",
+ ".impl-items .toggle summary",
{"x": -24, "y": 8}, // This is the position of "[-]" next to that pub fn.
)
-assert-attribute-false: (".impl-items .rustdoc-toggle", {"open": ""})
+assert-attribute-false: (".impl-items .toggle", {"open": ""})
// Click the "Trait" part of "impl Trait" and verify it navigates.
click: "#impl-Trait-for-Foo h3 a:first-of-type"
-assert-text: (".fqn", "Trait lib2::Trait")
+assert-text: (".main-heading h1", "Trait lib2::Trait")
goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
// We first check that everything is visible.
assert-text: ("#toggle-all-docs", "[−]")
-assert-attribute: ("#implementations-list details.rustdoc-toggle", {"open": ""}, ALL)
-assert-attribute: ("#trait-implementations-list details.rustdoc-toggle", {"open": ""}, ALL)
+assert-attribute: ("#implementations-list details.toggle", {"open": ""}, ALL)
+assert-attribute: ("#trait-implementations-list details.toggle", {"open": ""}, ALL)
assert-attribute-false: (
- "#blanket-implementations-list > details.rustdoc-toggle",
+ "#blanket-implementations-list > details.toggle",
{"open": ""},
ALL,
)
click: "#toggle-all-docs"
wait-for-text: ("#toggle-all-docs", "[+]")
// We check that all <details> are collapsed (except for the impl block ones).
-assert-attribute-false: ("details.rustdoc-toggle:not(.implementors-toggle)", {"open": ""}, ALL)
+assert-attribute-false: ("details.toggle:not(.implementors-toggle)", {"open": ""}, ALL)
assert-attribute: ("#implementations-list > details.implementors-toggle", {"open": ""})
// We now check that the other impl blocks are collapsed.
assert-attribute-false: (
- "#blanket-implementations-list > details.rustdoc-toggle.implementors-toggle",
+ "#blanket-implementations-list > details.toggle.implementors-toggle",
{"open": ""},
ALL,
)
// We open them all again.
click: "#toggle-all-docs"
wait-for-text: ("#toggle-all-docs", "[−]")
-assert-attribute: ("details.rustdoc-toggle", {"open": ""}, ALL)
+assert-attribute: ("details.toggle", {"open": ""}, ALL)
// Checking the toggles style.
show-text: true
// We reload the page so the local storage settings are being used.
reload:
- assert-css: ("details.rustdoc-toggle > summary::before", {
+ assert-css: ("details.toggle > summary::before", {
"opacity": "0.5",
"filter": |filter|,
}, ALL)
- move-cursor-to: "details.rustdoc-toggle summary"
- assert-css: ("details.rustdoc-toggle > summary:hover::before", {
+ move-cursor-to: "details.toggle summary"
+ assert-css: ("details.toggle > summary:hover::before", {
"opacity": "1",
"filter": |filter|,
})
// has all the implementations toggled open by default, so users can
// find method names in those implementations with Ctrl-F.
goto: "file://" + |DOC_PATH| + "/test_docs/struct.Foo.html"
-assert-attribute: (".rustdoc-toggle.implementors-toggle", {"open": ""})
+assert-attribute: (".toggle.implementors-toggle", {"open": ""})
const EXPECTED = {
'others': [
{ 'path': 'std', 'name': 'print' },
- { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'println' },
+ { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'eprintln' },
],
};
const EXPECTED = {
'others': [
{ 'path': 'std', 'name': 'print' },
- { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'println' },
+ { 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'eprintln' },
{ 'path': 'std::pin', 'name': 'pin' },
{ 'path': 'std::future', 'name': 'join' },
const EXPECTED = {
'others': [
{ 'path': 'std::vec::Vec', 'name': 'new' },
- { 'path': 'std::vec::Vec', 'name': 'ne' },
- { 'path': 'alloc::vec::Vec', 'name': 'ne' },
+ { 'path': 'alloc::vec::Vec', 'name': 'new' },
+ { 'path': 'std::vec::Vec', 'name': 'new_in' },
+ { 'path': 'alloc::vec::Vec', 'name': 'new_in' },
],
};
'others': [
{ 'path': 'search_short_types', 'name': 'P' },
{ 'path': 'search_short_types::VeryLongTypeName', 'name': 'p' },
- { 'path': 'search_short_types', 'name': 'Ap' },
- { 'path': 'search_short_types::VeryLongTypeName', 'name': 'ap' },
+ { 'path': 'search_short_types', 'name': 'Pa' },
],
};
+// check-pass
// normalize-stderr-test: "`.*`" -> "`DEF_ID`"
// normalize-stdout-test: "`.*`" -> "`DEF_ID`"
// edition:2018
pub async fn f() -> impl std::fmt::Debug {
+ // rustdoc doesn't care that this is infinitely sized
#[derive(Debug)]
- enum E { //~ ERROR
+ enum E {
This(E),
Unit,
}
+++ /dev/null
-error[E0072]: recursive type `DEF_ID` has infinite size
- --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5
- |
-LL | enum E {
- | ^^^^^^
-LL | This(E),
- | - recursive without indirection
- |
-help: insert some indirection (e.g., a `DEF_ID`) to break the cycle
- |
-LL | This(Box<E>),
- | ++++ +
-
-error: aborting due to previous error
-
-For more information about this error, try `DEF_ID`.
+// check-pass
+
fn f() -> impl Sized {
- enum E { //~ ERROR
+ // rustdoc doesn't care that this is infinitely sized
+ enum E {
V(E),
}
unimplemented!()
+++ /dev/null
-error[E0072]: recursive type `f::E` has infinite size
- --> $DIR/infinite-recursive-type-impl-trait.rs:2:5
- |
-LL | enum E {
- | ^^^^^^
-LL | V(E),
- | - recursive without indirection
- |
-help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
- |
-LL | V(Box<E>),
- | ++++ +
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0072`.
| ^^^
|
= note: error from rustc: unknown start of token: `
- = note: error from rustc: unknown start of token: `
- = note: error from rustc: unknown start of token: `
warning: could not parse code block as Rust code
--> $DIR/invalid-syntax.rs:64:5
-Z llvm-plugins=val -- a list LLVM plugins to enable (space separated)
-Z llvm-time-trace=val -- generate JSON tracing data file from LLVM data (default: no)
-Z location-detail=val -- what location details should be tracked when using caller_location, either `none`, or a comma separated list of location details, for which valid options are `file`, `line`, and `column` (default: `file,line,column`)
+ -Z log-backtrace=val -- add a backtrace along with logging
-Z ls=val -- list the symbols defined by a library crate (default: no)
-Z macro-backtrace=val -- show macro backtraces (default: no)
-Z maximal-hir-to-mir-coverage=val -- save as much information as possible about the correspondence between MIR and HIR as source scopes (default: no)
pub struct MyBox<T: ?Sized>(*const T);
// @has 'foo/fn.alpha.html'
-// @snapshot link_slice_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn alpha() -> &'static [u32; 1] {
loop {}
}
// @has 'foo/fn.beta.html'
-// @snapshot link_slice_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn beta<T>() -> &'static [T; 1] {
loop {}
}
// @has 'foo/fn.gamma.html'
-// @snapshot link_box_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn gamma() -> MyBox<[u32; 1]> {
loop {}
}
// @has 'foo/fn.delta.html'
-// @snapshot link_box_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn delta<T>() -> MyBox<[T; 1]> {
loop {}
}
pub trait Foo {
- // @has assoc_consts/trait.Foo.html '//*[@class="rust trait"]' \
+ // @has assoc_consts/trait.Foo.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'const FOO: usize = 13usize;'
// @has - '//*[@id="associatedconstant.FOO"]' 'const FOO: usize'
const FOO: usize = 12 + 1;
}
// @has foo/type.AsExprOf.html
-// @has - '//pre[@class="rust typedef"]' 'type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;'
pub type AsExprOf<Item, Type> = <Item as AsExpression<Type>>::Expression;
}
// @has assoc_types/fn.use_output.html
-// @has - '//*[@class="rust fn"]' '-> &T::Output'
-// @has - '//*[@class="rust fn"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' '-> &T::Output'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]//a[@href="trait.Index.html#associatedtype.Output"]' 'Output'
pub fn use_output<T: Index<usize>>(obj: &T, index: usize) -> &T::Output {
obj.index(index)
}
}
// @has assoc_types/fn.use_input.html
-// @has - '//*[@class="rust fn"]' 'T::Input'
-// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'T::Input'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
pub fn use_input<T: Feed>(_feed: &T, _element: T::Input) { }
// @has assoc_types/fn.cmp_input.html
-// @has - '//*[@class="rust fn"]' 'where T::Input: PartialEq<U::Input>'
-// @has - '//*[@class="rust fn"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where T::Input: PartialEq<U::Input>'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]//a[@href="trait.Feed.html#associatedtype.Input"]' 'Input'
pub fn cmp_input<T: Feed, U: Feed>(a: &T::Input, b: &U::Input) -> bool
where T::Input: PartialEq<U::Input>
{
// edition:2018
-// @has async_fn/fn.foo.html '//pre[@class="rust fn"]' 'pub async fn foo() -> Option<Foo>'
+// @has async_fn/fn.foo.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn foo() -> Option<Foo>'
pub async fn foo() -> Option<Foo> {
None
}
-// @has async_fn/fn.bar.html '//pre[@class="rust fn"]' 'pub async fn bar(a: i32, b: i32) -> i32'
+// @has async_fn/fn.bar.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn bar(a: i32, b: i32) -> i32'
pub async fn bar(a: i32, b: i32) -> i32 {
0
}
-// @has async_fn/fn.baz.html '//pre[@class="rust fn"]' 'pub async fn baz<T>(a: T) -> T'
+// @has async_fn/fn.baz.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn baz<T>(a: T) -> T'
pub async fn baz<T>(a: T) -> T {
a
}
-// @has async_fn/fn.qux.html '//pre[@class="rust fn"]' 'pub async unsafe fn qux() -> char'
+// @has async_fn/fn.qux.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async unsafe fn qux() -> char'
pub async unsafe fn qux() -> char {
'⚠'
}
-// @has async_fn/fn.mut_args.html '//pre[@class="rust fn"]' 'pub async fn mut_args(a: usize)'
+// @has async_fn/fn.mut_args.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn mut_args(a: usize)'
pub async fn mut_args(mut a: usize) {}
-// @has async_fn/fn.mut_ref.html '//pre[@class="rust fn"]' 'pub async fn mut_ref(x: i32)'
+// @has async_fn/fn.mut_ref.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn mut_ref(x: i32)'
pub async fn mut_ref(ref mut x: i32) {}
trait Bar {}
impl Bar for () {}
-// @has async_fn/fn.quux.html '//pre[@class="rust fn"]' 'pub async fn quux() -> impl Bar'
+// @has async_fn/fn.quux.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn quux() -> impl Bar'
pub async fn quux() -> impl Bar {
()
}
pub trait Trait<const N: usize> {}
// @has async_fn/fn.const_generics.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn const_generics<const N: usize>(_: impl Trait<N>)'
pub async fn const_generics<const N: usize>(_: impl Trait<N>) {}
// test that elided lifetimes are properly elided and not displayed as `'_`
// regression test for #63037
// @has async_fn/fn.elided.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn elided(foo: &str) -> &str'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn elided(foo: &str) -> &str'
pub async fn elided(foo: &str) -> &str {}
// This should really be shown as written, but for implementation reasons it's difficult.
// See `impl Clean for TyKind::Ref`.
// @has async_fn/fn.user_elided.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn user_elided(foo: &str) -> &str'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn user_elided(foo: &str) -> &str'
pub async fn user_elided(foo: &'_ str) -> &str {}
// @has async_fn/fn.static_trait.html
-// @has - '//pre[@class="rust fn"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub async fn static_trait(foo: &str) -> Box<dyn Bar>'
pub async fn static_trait(foo: &str) -> Box<dyn Bar> {}
// @has async_fn/fn.lifetime_for_trait.html
-// @has - '//pre[@class="rust fn"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_>"
pub async fn lifetime_for_trait(foo: &str) -> Box<dyn Bar + '_> {}
// @has async_fn/fn.elided_in_input_trait.html
-// @has - '//pre[@class="rust fn"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn elided_in_input_trait(t: impl Pattern<'_>)"
pub async fn elided_in_input_trait(t: impl Pattern<'_>) {}
struct AsyncFdReadyGuard<'a, T> { x: &'a T }
// test named lifetimes, just in case
// @has async_fn/fn.named.html
-// @has - '//pre[@class="rust fn"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn named<'a, 'b>(foo: &'a str) -> &'b str"
pub async fn named<'a, 'b>(foo: &'a str) -> &'b str {}
// @has async_fn/fn.named_trait.html
-// @has - '//pre[@class="rust fn"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b>"
pub async fn named_trait<'a, 'b>(foo: impl Pattern<'a>) -> impl Pattern<'b> {}
#![crate_name = "foo"]
-// @has foo/fn.f.html '//*[@class="rust fn"]' '#[no_mangle]'
+// @has foo/fn.f.html '//div[@class="item-decl"]/pre[@class="rust"]' '#[no_mangle]'
#[no_mangle]
pub extern "C" fn f() {}
-// @has foo/fn.g.html '//*[@class="rust fn"]' '#[export_name = "bar"]'
+// @has foo/fn.g.html '//div[@class="item-decl"]/pre[@class="rust"]' '#[export_name = "bar"]'
#[export_name = "bar"]
pub extern "C" fn g() {}
-// @has foo/struct.Repr.html '//*[@class="item-decl"]' '#[repr(C, align(8))]'
+// @has foo/struct.Repr.html '//div[@class="item-decl"]' '#[repr(C, align(8))]'
#[repr(C, align(8))]
pub struct Repr;
// @has issue_85454/trait.FromResidual.html
-// @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
pub trait FromResidual<R = <Self as Try>::Residual> {
fn from_residual(residual: R) -> Self;
}
#![crate_name = "foo"]
// @has foo/fn.bar.html
-// @has - '//*[@class="rust fn"]' 'pub const fn bar() -> '
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub const fn bar() -> '
/// foo
pub const fn bar() -> usize {
2
use std::ops::Add;
-// @has foo/struct.Simd.html '//pre[@class="rust struct"]' 'pub struct Simd<T, const WIDTH: usize>'
+// @has foo/struct.Simd.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Simd<T, const WIDTH: usize>'
pub struct Simd<T, const WIDTH: usize> {
inner: T,
}
#![crate_name = "foo"]
-// @has foo/struct.Foo.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Foo.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>(_);'
pub struct Foo<const M: usize = 10, const N: usize = M, T = i32>(T);
#![crate_name = "foo"]
extern crate extern_crate;
-// @has foo/fn.extern_fn.html '//pre[@class="rust fn"]' \
+// @has foo/fn.extern_fn.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn extern_fn<const N: usize>() -> impl Iterator<Item = [u8; N]>'
pub use extern_crate::extern_fn;
-// @has foo/struct.ExternTy.html '//pre[@class="rust struct"]' \
+// @has foo/struct.ExternTy.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct ExternTy<const N: usize> {'
pub use extern_crate::ExternTy;
-// @has foo/type.TyAlias.html '//pre[@class="rust typedef"]' \
+// @has foo/type.TyAlias.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'type TyAlias<const N: usize> = ExternTy<N>;'
pub use extern_crate::TyAlias;
-// @has foo/trait.WTrait.html '//pre[@class="rust trait"]' \
+// @has foo/trait.WTrait.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub trait WTrait<const N: usize, const M: usize>'
-// @has - '//*[@class="rust trait"]' 'fn hey<const P: usize>() -> usize'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn hey<const P: usize>() -> usize'
pub use extern_crate::WTrait;
-// @has foo/trait.Trait.html '//pre[@class="rust trait"]' \
+// @has foo/trait.Trait.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub trait Trait<const N: usize>'
// @has - '//*[@id="impl-Trait%3C1%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<1> for u8'
// @has - '//*[@id="impl-Trait%3C2%3E-for-u8"]//h3[@class="code-header"]' 'impl Trait<2> for u8'
impl Trait<{1 + 2}> for u8 {}
impl<const N: usize> Trait<N> for [u8; N] {}
-// @has foo/struct.Foo.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Foo.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Foo<const N: usize>where u8: Trait<N>'
pub struct Foo<const N: usize> where u8: Trait<N>;
-// @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar<T, const N: usize>(_)'
+// @has foo/struct.Bar.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Bar<T, const N: usize>(_)'
pub struct Bar<T, const N: usize>([T; N]);
// @has foo/struct.Foo.html '//*[@id="impl-Foo%3CM%3E"]/h3[@class="code-header"]' 'impl<const M: usize> Foo<M>where u8: Trait<M>'
}
}
-// @has foo/fn.test.html '//pre[@class="rust fn"]' \
+// @has foo/fn.test.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn test<const N: usize>() -> impl Trait<N>where u8: Trait<N>'
pub fn test<const N: usize>() -> impl Trait<N> where u8: Trait<N> {
2u8
}
-// @has foo/fn.a_sink.html '//pre[@class="rust fn"]' \
+// @has foo/fn.a_sink.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub async fn a_sink<const N: usize>(v: [u8; N]) -> impl Trait<N>'
pub async fn a_sink<const N: usize>(v: [u8; N]) -> impl Trait<N> {
v
}
-// @has foo/fn.b_sink.html '//pre[@class="rust fn"]' \
+// @has foo/fn.b_sink.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub async fn b_sink<const N: usize>(_: impl Trait<N>)'
pub async fn b_sink<const N: usize>(_: impl Trait<N>) {}
-// @has foo/fn.concrete.html '//pre[@class="rust fn"]' \
+// @has foo/fn.concrete.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn concrete() -> [u8; 22]'
pub fn concrete() -> [u8; 3 + std::mem::size_of::<u64>() << 1] {
Default::default()
}
-// @has foo/type.Faz.html '//pre[@class="rust typedef"]' \
+// @has foo/type.Faz.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'type Faz<const N: usize> = [u8; N];'
pub type Faz<const N: usize> = [u8; N];
-// @has foo/type.Fiz.html '//pre[@class="rust typedef"]' \
+// @has foo/type.Fiz.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'type Fiz<const N: usize> = [[u8; N]; 48];'
pub type Fiz<const N: usize> = [[u8; N]; 3 << 4];
}
}
-// @has foo/struct.Foz.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Foz.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Foz<const N: usize>(_);'
define_me!(Foz<N>);
const ASSOC: usize = N;
}
-// @has foo/fn.q_user.html '//pre[@class="rust fn"]' \
+// @has foo/fn.q_user.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub fn q_user() -> [u8; 13]'
pub fn q_user() -> [u8; <[u8; 13] as Q>::ASSOC] {
[0; <[u8; 13] as Q>::ASSOC]
}
-// @has foo/union.Union.html '//pre[@class="rust union"]' \
+// @has foo/union.Union.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub union Union<const N: usize>'
pub union Union<const N: usize> {
// @has - //pre "pub arr: [u8; N]"
pub another_arr: [(); N],
}
-// @has foo/enum.Enum.html '//pre[@class="rust enum"]' \
+// @has foo/enum.Enum.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub enum Enum<const N: usize>'
pub enum Enum<const N: usize> {
// @has - //pre "Variant([u8; N])"
Unsorted,
}
-// @has foo/struct.VSet.html '//pre[@class="rust struct"]' 'pub struct VSet<T, const ORDER: Order>'
+// @has foo/struct.VSet.html '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct VSet<T, const ORDER: Order>'
// @has foo/struct.VSet.html '//*[@id="impl-Send-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>'
// @has foo/struct.VSet.html '//*[@id="impl-Sync-for-VSet%3CT%2C%20ORDER%3E"]/h3[@class="code-header"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>'
pub struct VSet<T, const ORDER: Order> {
#![feature(generic_const_exprs)]
#![allow(incomplete_features)]
// make sure that `ConstEvaluatable` predicates dont cause rustdoc to ICE #77647
-// @has foo/struct.Ice.html '//pre[@class="rust struct"]' \
+// @has foo/struct.Ice.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub struct Ice<const N: usize>;'
pub struct Ice<const N: usize> where [(); N + 1]:;
#![crate_name = "foo"]
-// @has foo/type.CellIndex.html '//pre[@class="rust typedef"]' 'type CellIndex<const D: usize> = [i64; D];'
+// @has foo/type.CellIndex.html '//div[@class="item-decl"]/pre[@class="rust"]' 'type CellIndex<const D: usize> = [i64; D];'
pub type CellIndex<const D: usize> = [i64; D];
--- /dev/null
+#![feature(intrinsics)]
+#![feature(staged_api)]
+
+#![crate_name = "foo"]
+#![stable(since="1.0.0", feature="rust1")]
+
+extern "rust-intrinsic" {
+ // @has 'foo/fn.transmute.html'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub const unsafe extern "rust-intrinsic" fn transmute<T, U>(_: T) -> U'
+ #[stable(since="1.0.0", feature="rust1")]
+ #[rustc_const_stable(feature = "const_transmute", since = "1.56.0")]
+ pub fn transmute<T, U>(_: T) -> U;
+
+ // @has 'foo/fn.unreachable.html'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
+ #[stable(since="1.0.0", feature="rust1")]
+ pub fn unreachable() -> !;
+}
+
+extern "C" {
+ // @has 'foo/fn.needs_drop.html'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "C" fn needs_drop() -> !'
+ #[stable(since="1.0.0", feature="rust1")]
+ pub fn needs_drop() -> !;
+}
-<script type="text/json" id="notable-traits-data">{"&'static [SomeStruct]":"<h3>Notable traits for <code>&amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait_slice::SomeTrait\">SomeTrait</a> for &amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"&'static [SomeStruct]":"<h3>Notable traits for <code>&amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait_slice::SomeTrait\">SomeTrait</a> for &amp;[<a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait_slice::SomeStruct\">SomeStruct</a>]</span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>","Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>","Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
#![crate_name = "foo"]
// @has foo/fn.f.html
-// @has - '//*[@class="rust fn"]' 'pub fn f(callback: fn(len: usize, foo: u32))'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f(callback: fn(len: usize, foo: u32))'
pub fn f(callback: fn(len: usize, foo: u32)) {}
// this test as long as one can ensure that private fields are not leaked!
//
// @has hide_complex_unevaluated_const_arguments/trait.Sub.html \
-// '//*[@class="rust trait"]' \
+// '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'pub trait Sub: Sup<{ _ }, { _ }> { }'
pub trait Sub: Sup<{ 90 * 20 * 4 }, { Struct { private: () } }> {}
+++ /dev/null
-// Regression test for #83026.
-// The goal of this test is to ensure that impl blocks inside
-// const expressions are documented as well.
-
-#![crate_name = "foo"]
-
-// @has 'foo/struct.A.html'
-// @has - '//*[@id="method.new"]/*[@class="code-header"]' 'pub fn new() -> A'
-// @has - '//*[@id="method.bar"]/*[@class="code-header"]' 'pub fn bar(&self)'
-// @has - '//*[@id="method.woo"]/*[@class="code-header"]' 'pub fn woo(&self)'
-// @has - '//*[@id="method.yoo"]/*[@class="code-header"]' 'pub fn yoo()'
-// @has - '//*[@id="method.yuu"]/*[@class="code-header"]' 'pub fn yuu()'
-pub struct A;
-
-const _: () = {
- impl A {
- const FOO: () = {
- impl A {
- pub fn woo(&self) {}
- }
- };
-
- pub fn new() -> A {
- A
- }
- }
-};
-pub const X: () = {
- impl A {
- pub fn bar(&self) {}
- }
-};
-
-fn foo() {
- impl A {
- pub fn yoo() {}
- }
- const _: () = {
- impl A {
- pub fn yuu() {}
- }
- };
-}
#![crate_name = "foo"]
// @has foo/../index.html
-// @has - '//h1[@class="fqn"]' 'List of all crates'
+// @has - '//h1' 'List of all crates'
// @has - '//ul[@class="all-items"]//a[@href="foo/index.html"]' 'foo'
// @has - '//ul[@class="all-items"]//a[@href="all_item_types/index.html"]' 'all_item_types'
pub struct Foo;
extern crate inline_default_methods;
// @has inline_default_methods/trait.Foo.html
-// @has - '//*[@class="rust trait"]' 'fn bar(&self);'
-// @has - '//*[@class="rust trait"]' 'fn foo(&mut self) { ... }'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn bar(&self);'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn foo(&mut self) { ... }'
pub use inline_default_methods::Foo;
pub struct SomeStruct;
pub fn some_fn() {}
+
+pub enum Shadowed {}
// @has cross_glob/struct.SomeStruct.html
// @has cross_glob/fn.some_fn.html
+// @!has cross_glob/enum.Shadowed.html
// @!has cross_glob/index.html '//code' 'pub use inner::*;'
#[doc(inline)]
pub use inner::*;
+
+// This type shadows the glob-imported enum `Shadowed`.
+// @has cross_glob/type.Shadowed.html
+pub type Shadowed = u8;
pub use dyn_trait::Ty3;
// @has user/fn.func0.html
-// @has - '//pre[@class="rust fn"]' "func0(_: &dyn Fn())"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func0(_: &dyn Fn())"
// FIXME(fmease): Show placeholder-lifetime bound, render "func0(_: &(dyn Fn() + '_))"
pub use dyn_trait::func0;
// @has user/fn.func1.html
-// @has - '//pre[@class="rust fn"]' "func1<'func>(_: &(dyn Fn() + 'func))"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func1<'func>(_: &(dyn Fn() + 'func))"
pub use dyn_trait::func1;
extern crate impl_trait_aux;
// @has impl_trait/fn.func.html
-// @has - '//pre[@class="rust fn"]' "pub fn func<'a>(_x: impl Clone + Into<Vec<u8, Global>> + 'a)"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn func<'a>(_x: impl Clone + Into<Vec<u8, Global>> + 'a)"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func;
// @has impl_trait/fn.func2.html
-// @has - '//pre[@class="rust fn"]' "func2<T>("
-// @has - '//pre[@class="rust fn"]' "_x: impl Deref<Target = Option<T>> + Iterator<Item = T>,"
-// @has - '//pre[@class="rust fn"]' "_y: impl Iterator<Item = u8>)"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func2<T>("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_x: impl Deref<Target = Option<T>> + Iterator<Item = T>,"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_y: impl Iterator<Item = u8>)"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func2;
// @has impl_trait/fn.func3.html
-// @has - '//pre[@class="rust fn"]' "func3("
-// @has - '//pre[@class="rust fn"]' "_x: impl Iterator<Item = impl Iterator<Item = u8>> + Clone)"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func3("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_x: impl Iterator<Item = impl Iterator<Item = u8>> + Clone)"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func3;
// @has impl_trait/fn.func4.html
-// @has - '//pre[@class="rust fn"]' "func4<T>("
-// @has - '//pre[@class="rust fn"]' "T: Iterator<Item = impl Clone>,"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func4<T>("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "T: Iterator<Item = impl Clone>,"
pub use impl_trait_aux::func4;
// @has impl_trait/fn.func5.html
-// @has - '//pre[@class="rust fn"]' "func5("
-// @has - '//pre[@class="rust fn"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
-// @has - '//pre[@class="rust fn"]' "_a: impl for<'alpha, 'beta> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>"
-// @!has - '//pre[@class="rust fn"]' 'where'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "func5("
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_f: impl for<'any> Fn(&'any str, &'any str) -> bool + for<'r> Other<T<'r> = ()>,"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_a: impl for<'alpha, 'beta> Auxiliary<'alpha, Item<'beta> = fn(_: &'beta ())>"
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where'
pub use impl_trait_aux::func5;
// @has impl_trait/fn.async_fn.html
-// @has - '//pre[@class="rust fn"]' "pub async fn async_fn()"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub async fn async_fn()"
pub use impl_trait_aux::async_fn;
// @has impl_trait/struct.Foo.html
}
// @has issue_20646/fn.fun.html \
-// '//*[@class="rust fn"]' 'where T: Trait<Output = i32>'
+// '//div[@class="item-decl"]/pre[@class="rust"]' 'where T: Trait<Output = i32>'
pub fn fun<T>(_: T) where T: Trait<Output=i32> {}
pub mod reexport {
// '//*[@id="associatedtype.Output"]' \
// 'type Output'
// @has issue_20646/reexport/fn.fun.html \
- // '//*[@class="rust fn"]' 'where T: Trait<Output = i32>'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'where T: Trait<Output = i32>'
pub use issue_20646::{Trait, fun};
}
// @has issue_20727_2/trait.Add.html
pub trait Add<RHS = Self> {
- // @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
- // @has - '//*[@class="rust trait"]' 'type Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Add<RHS = Self> {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output;'
type Output;
- // @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn add(self, rhs: RHS) -> Self::Output;'
fn add(self, rhs: RHS) -> Self::Output;
}
// @has issue_20727_2/reexport/trait.Add.html
pub mod reexport {
- // @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
- // @has - '//*[@class="rust trait"]' 'type Output;'
- // @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Add<RHS = Self> {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn add(self, rhs: RHS) -> Self::Output;'
pub use issue_20727::Add;
}
// @has issue_20727_3/trait.Deref2.html
pub trait Deref2 {
- // @has - '//*[@class="rust trait"]' 'trait Deref2 {'
- // @has - '//*[@class="rust trait"]' 'type Target: Bar;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref2 {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: Bar;'
type Target: Bar;
- // @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn deref(&self) -> Self::Target;'
fn deref(&self) -> Self::Target;
}
// @has issue_20727_3/reexport/trait.Deref2.html
pub mod reexport {
- // @has - '//*[@class="rust trait"]' 'trait Deref2 {'
- // @has - '//*[@class="rust trait"]' 'type Target: Bar;'
- // @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref2 {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: Bar;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn deref(&self) -> Self::Target;'
pub use issue_20727::Deref2;
}
// @has issue_20727_4/trait.Index.html
pub trait Index<Idx: ?Sized> {
- // @has - '//*[@class="rust trait"]' 'trait Index<Idx: ?Sized> {'
- // @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Index<Idx: ?Sized> {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output: ?Sized'
type Output: ?Sized;
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
fn index(&self, index: Idx) -> &Self::Output;
}
// @has issue_20727_4/trait.IndexMut.html
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'trait IndexMut<Idx: ?Sized>: Index<Idx> {'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}
pub mod reexport {
// @has issue_20727_4/reexport/trait.Index.html
- // @has - '//*[@class="rust trait"]' 'trait Index<Idx>where Idx: ?Sized,{'
- // @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Index<Idx>where Idx: ?Sized,{'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Output: ?Sized'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
pub use issue_20727::Index;
// @has issue_20727_4/reexport/trait.IndexMut.html
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'trait IndexMut<Idx>: Index<Idx>where Idx: ?Sized,{'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
pub use issue_20727::IndexMut;
}
// @has issue_20727/trait.Deref.html
pub trait Deref {
- // @has - '//*[@class="rust trait"]' 'trait Deref {'
- // @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: ?Sized;'
type Target: ?Sized;
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// "fn deref<'a>(&'a self) -> &'a Self::Target;"
fn deref<'a>(&'a self) -> &'a Self::Target;
}
// @has issue_20727/reexport/trait.Deref.html
pub mod reexport {
- // @has - '//*[@class="rust trait"]' 'trait Deref {'
- // @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
- // @has - '//*[@class="rust trait"]' \
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'trait Deref {'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'type Target: ?Sized;'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' \
// "fn deref<'a>(&'a self) -> &'a Self::Target;"
pub use issue_20727::Deref;
}
extern "C" {
// @has issue_22038/fn.foo1.html \
- // '//*[@class="rust fn"]' 'pub unsafe extern "C" fn foo1()'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "C" fn foo1()'
pub fn foo1();
}
extern "system" {
// @has issue_22038/fn.foo2.html \
- // '//*[@class="rust fn"]' 'pub unsafe extern "system" fn foo2()'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "system" fn foo2()'
pub fn foo2();
}
// @has issue_22038/fn.bar.html \
-// '//*[@class="rust fn"]' 'pub extern "C" fn bar()'
+// '//div[@class="item-decl"]/pre[@class="rust"]' 'pub extern "C" fn bar()'
pub extern "C" fn bar() {}
// @has issue_22038/fn.baz.html \
-// '//*[@class="rust fn"]' 'pub extern "system" fn baz()'
+// '//div[@class="item-decl"]/pre[@class="rust"]' 'pub extern "system" fn baz()'
pub extern "system" fn baz() {}
pub struct S;
// @has issue_33302/constant.CST.html \
- // '//pre[@class="rust const"]' 'pub const CST: i32'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub const CST: i32'
pub const CST: i32 = ($n * $n);
// @has issue_33302/static.ST.html \
- // '//pre[@class="rust static"]' 'pub static ST: i32'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'pub static ST: i32'
pub static ST: i32 = ($n * $n);
pub trait T<X> {
fn ignore(_: &X) {}
const C: X;
// @has issue_33302/trait.T.html \
- // '//*[@class="rust trait"]' 'const D: i32'
+ // '//div[@class="item-decl"]/pre[@class="rust"]' 'const D: i32'
// @has - '//*[@id="associatedconstant.D"]' 'const D: i32'
const D: i32 = ($n * $n);
}
// @!hasraw - '<span class="attr">#[outer]</span>'
// @hasraw - '#![inner]</span>'
// @!hasraw - '<span class="attr">#![inner]</span>'
-// @snapshot 'codeblock' - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]//pre/code'
+// @snapshot 'codeblock' - '//*[@class="toggle top-doc"]/*[@class="docblock"]//pre/code'
/// ```no_run
/// # # space
extern crate issue_85454;
// @has foo/trait.FromResidual.html
-// @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
pub trait FromResidual<R = <Self as Try>::Residual> {
fn from_residual(residual: R) -> Self;
}
pub mod reexport {
// @has foo/reexport/trait.FromResidual.html
- // @has - '//pre[@class="rust trait"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
pub use issue_85454::*;
}
extern crate issue_98697_reexport_with_anonymous_lifetime;
-// @has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'fn repro<F>()where F: Fn(&str)'
-// @!has issue_98697/fn.repro.html '//pre[@class="rust fn"]/code' 'for<'
+// @has issue_98697/fn.repro.html '//div[@class="item-decl"]/pre[@class="rust"]/code' 'fn repro<F>()where F: Fn(&str)'
+// @!has issue_98697/fn.repro.html '//div[@class="item-decl"]/pre[@class="rust"]/code' 'for<'
pub use issue_98697_reexport_with_anonymous_lifetime::repro;
// @has issue_98697/struct.Extra.html '//div[@id="trait-implementations-list"]//h3[@class="code-header"]' 'impl MyTrait<&Extra> for Extra'
// @has foo/index.html '//div[@class="sidebar-elems"]//li/a' 'Keywords'
// @has foo/index.html '//div[@class="sidebar-elems"]//li/a/@href' '#keywords'
// @has foo/keyword.match.html '//a[@class="keyword"]' 'match'
-// @has foo/keyword.match.html '//h1[@class="fqn"]' 'Keyword match'
+// @has foo/keyword.match.html '//h1' 'Keyword match'
// @has foo/keyword.match.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has foo/index.html '//a/@href' '../foo/index.html'
// @!has foo/foo/index.html
#![feature(rustc_attrs)]
// @has 'foo/fn.foo.html'
-// @has - '//*[@class="rust fn"]' 'fn foo(x: usize, const Y: usize, z: usize) -> [usize; 3]'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn foo(x: usize, const Y: usize, z: usize) -> [usize; 3]'
#[rustc_legacy_const_generics(1)]
pub fn foo<const Y: usize>(x: usize, z: usize) -> [usize; 3] {
[x, Y, z]
}
// @has 'foo/fn.bar.html'
-// @has - '//*[@class="rust fn"]' 'fn bar(x: usize, const Y: usize, const Z: usize) -> [usize; 3]'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'fn bar(x: usize, const Y: usize, const Z: usize) -> [usize; 3]'
#[rustc_legacy_const_generics(1, 2)]
pub fn bar<const Y: usize, const Z: usize>(x: usize) -> [usize; 3] {
[x, Y, z]
#![crate_name = "foo"]
// @has 'foo/type.Resolutions.html'
-// @has - '//*[@class="rust typedef"]' "pub type Resolutions<'tcx> = &'tcx u8;"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "pub type Resolutions<'tcx> = &'tcx u8;"
pub type Resolutions<'tcx> = &'tcx u8;
#![crate_name = "foo"]
// @has 'foo/fn.g.html'
-// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' \
+// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' \
// 'outer module inner module'
mod inner_mod {
#![crate_name = "foo"]
// @has 'foo/struct.S1.html'
-// @snapshot S1_top-doc - '//details[@class="rustdoc-toggle top-doc"]/div[@class="docblock"]'
+// @snapshot S1_top-doc - '//details[@class="toggle top-doc"]/div[@class="docblock"]'
#[doc = "Hello world!\n\n"]
/// Goodbye!
pub struct S1;
// @has 'foo/struct.S2.html'
-// @snapshot S2_top-doc - '//details[@class="rustdoc-toggle top-doc"]/div[@class="docblock"]'
+// @snapshot S2_top-doc - '//details[@class="toggle top-doc"]/div[@class="docblock"]'
/// Hello world!
///
pub struct S2;
// @has 'foo/struct.S3.html'
-// @snapshot S3_top-doc - '//details[@class="rustdoc-toggle top-doc"]/div[@class="docblock"]'
+// @snapshot S3_top-doc - '//details[@class="toggle top-doc"]/div[@class="docblock"]'
/** Par 1
*/ ///
/// Par 2
}
// @has 'foo/struct.Type.html'
-// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' 'foo 2 1'
+// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'foo 2 1'
/// foo
pub use b::Type;
// @has 'foo/struct.Whatever.html'
-// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' 'whatever 3 2 1'
+// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'whatever 3 2 1'
/// whatever
pub use c::Type as Whatever;
// @has 'foo/struct.Woof.html'
-// @has - '//*[@class="rustdoc-toggle top-doc"]/*[@class="docblock"]' 'a dog 4 2 1'
+// @has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'a dog 4 2 1'
/// a dog
pub use c::Woof;
pub fn bar(mut bar: ()) {}
}
-// @count foo/fn.baz.html '//*[@class="rust fn"]' 1
-// @!has - '//*[@class="rust fn"]' 'mut'
+// @count foo/fn.baz.html '//div[@class="item-decl"]/pre[@class="rust"]' 1
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]' 'mut'
pub fn baz(mut foo: Foo) {}
type X = <() as Trait>::X;
}
-// @has 'normalize_assoc_item/fn.f.html' '//pre[@class="rust fn"]' 'pub fn f() -> isize'
+// @has 'normalize_assoc_item/fn.f.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f() -> isize'
pub fn f() -> <usize as Trait>::X {
0
}
-// @has 'normalize_assoc_item/fn.f2.html' '//pre[@class="rust fn"]' 'pub fn f2() -> fn() -> i32'
+// @has 'normalize_assoc_item/fn.f2.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f2() -> fn() -> i32'
pub fn f2() -> <isize as Trait>::X {
todo!()
}
// These can't be normalized because they depend on a generic parameter.
// However the user can choose whether the text should be displayed as `Inner::X` or `<Inner as Trait>::X`.
-// @has 'normalize_assoc_item/struct.Unknown.html' '//pre[@class="rust struct"]' 'pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);'
+// @has 'normalize_assoc_item/struct.Unknown.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);'
pub struct Unknown<Inner: Trait>(pub <Inner as Trait>::X);
-// @has 'normalize_assoc_item/struct.Unknown2.html' '//pre[@class="rust struct"]' 'pub struct Unknown2<Inner: Trait>(pub Inner::X);'
+// @has 'normalize_assoc_item/struct.Unknown2.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub struct Unknown2<Inner: Trait>(pub Inner::X);'
pub struct Unknown2<Inner: Trait>(pub Inner::X);
trait Lifetimes<'a> {
type Y = &'a isize;
}
-// @has 'normalize_assoc_item/fn.g.html' '//pre[@class="rust fn"]' "pub fn g() -> &isize"
+// @has 'normalize_assoc_item/fn.g.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn g() -> &isize"
pub fn g() -> <usize as Lifetimes<'static>>::Y {
&0
}
-// @has 'normalize_assoc_item/constant.A.html' '//pre[@class="rust const"]' "pub const A: &isize"
+// @has 'normalize_assoc_item/constant.A.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub const A: &isize"
pub const A: <usize as Lifetimes<'static>>::Y = &0;
// test cross-crate re-exports
extern crate inner;
-// @has 'normalize_assoc_item/fn.foo.html' '//pre[@class="rust fn"]' "pub fn foo() -> i32"
+// @has 'normalize_assoc_item/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn foo() -> i32"
pub use inner::foo;
-// @has 'normalize_assoc_item/fn.h.html' '//pre[@class="rust fn"]' "pub fn h<T>() -> IntoIter<T, Global>"
+// @has 'normalize_assoc_item/fn.h.html' '//div[@class="item-decl"]/pre[@class="rust"]' "pub fn h<T>() -> IntoIter<T, Global>"
pub fn h<T>() -> <Vec<T> as IntoIterator>::IntoIter {
vec![].into_iter()
}
// @has - '//div[@class="sidebar-elems"]//li/a/@href' '#primitives'
// @has foo/primitive.reference.html
// @has - '//a[@class="primitive"]' 'reference'
-// @has - '//h1[@class="fqn"]' 'Primitive Type reference'
+// @has - '//h1' 'Primitive Type reference'
// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// There should be only one implementation listed.
#![feature(rustdoc_internals)]
// @has foo/primitive.slice.html '//a[@class="primitive"]' 'slice'
-// @has - '//h1[@class="fqn"]' 'Primitive Type slice'
+// @has - '//h1' 'Primitive Type slice'
// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations'
// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl<T> Send for [T]where T: Send'
#![feature(rustdoc_internals)]
// @has foo/primitive.tuple.html '//a[@class="primitive"]' 'tuple'
-// @has - '//h1[@class="fqn"]' 'Primitive Type tuple'
+// @has - '//h1' 'Primitive Type tuple'
// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations'
// @has - '//div[@id="synthetic-implementations-list"]//h3' 'Send'
#![feature(rustdoc_internals)]
// @has foo/primitive.unit.html '//a[@class="primitive"]' 'unit'
-// @has - '//h1[@class="fqn"]' 'Primitive Type unit'
+// @has - '//h1' 'Primitive Type unit'
// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has - '//h2[@id="synthetic-implementations"]' 'Auto Trait Implementations'
// @has - '//div[@id="synthetic-implementations-list"]//h3' 'impl Send for ()'
// @has foo/index.html '//div[@class="sidebar-elems"]//li/a' 'Primitive Types'
// @has foo/index.html '//div[@class="sidebar-elems"]//li/a/@href' '#primitives'
// @has foo/primitive.i32.html '//a[@class="primitive"]' 'i32'
-// @has foo/primitive.i32.html '//h1[@class="fqn"]' 'Primitive Type i32'
+// @has foo/primitive.i32.html '//h1' 'Primitive Type i32'
// @has foo/primitive.i32.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has foo/index.html '//a/@href' '../foo/index.html'
// @!has foo/index.html '//span' '🔒'
#![crate_name = "foo"]
// @has foo/fn.bar.html
-// @has - '//*[@class="rust fn"]' 'pub fn bar() -> '
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn bar() -> '
/// foo
pub fn bar() -> usize {
2
#![crate_name = "foo"]
// @has foo/fn.f.html
-// @has - '//*[@class="rust fn"]' 'pub fn f(_: u8)'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn f(_: u8)'
pub fn f(0u8..=255: u8) {}
--- /dev/null
+// Part of <https://github.com/rust-lang/rust/issues/59368>.
+// This test ensures that reexporting a `doc(hidden)` item will
+// still show the reexport.
+
+#![crate_name = "foo"]
+
+#[doc(hidden)]
+pub type Type = u32;
+
+// @has 'foo/index.html'
+// @has - '//*[@id="reexport.Type2"]/code' 'pub use crate::Type as Type2;'
+pub use crate::Type as Type2;
+
+// @count - '//*[@id="reexport.Type3"]' 0
+#[doc(hidden)]
+pub use crate::Type as Type3;
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! foo {
+ () => {};
+}
+
+// This is a bug: https://github.com/rust-lang/rust/issues/59368
+// @!has - '//*[@id="reexport.Macro"]/code' 'pub use crate::foo as Macro;'
+pub use crate::foo as Macro;
// @!has 'foo/enum.BarLocal.html'
use reexports::BarLocal;
-// @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+// @has 'foo/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
// @!has 'foo/fn.foo_crate.html'
pub(crate) use reexports::foo_crate;
// @!has 'foo/fn.foo_local.html'
use reexports::foo_local;
-// @has 'foo/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+// @has 'foo/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
// @!has 'foo/type.TypeCrate.html'
pub(crate) use reexports::TypeCrate;
// @!has 'foo/outer/inner/enum.BarLocal.html'
use reexports::BarLocal;
- // @has 'foo/outer/inner/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+ // @has 'foo/outer/inner/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
- // @has 'foo/outer/inner/fn.foo_crate.html' '//*[@class="rust fn"]' 'pub(crate) fn foo_crate()'
+ // @has 'foo/outer/inner/fn.foo_crate.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(crate) fn foo_crate()'
pub(crate) use reexports::foo_crate;
- // @has 'foo/outer/inner/fn.foo_super.html' '//*[@class="rust fn"]' 'pub(in outer) fn foo_super()'
+ // @has 'foo/outer/inner/fn.foo_super.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(in outer) fn foo_super()'
pub(super) use::reexports::foo_super;
// @!has 'foo/outer/inner/fn.foo_self.html'
pub(self) use reexports::foo_self;
// @!has 'foo/outer/inner/fn.foo_local.html'
use reexports::foo_local;
- // @has 'foo/outer/inner/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+ // @has 'foo/outer/inner/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
- // @has 'foo/outer/inner/type.TypeCrate.html' '//*[@class="rust typedef"]' 'pub(crate) type TypeCrate ='
+ // @has 'foo/outer/inner/type.TypeCrate.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(crate) type TypeCrate ='
pub(crate) use reexports::TypeCrate;
- // @has 'foo/outer/inner/type.TypeSuper.html' '//*[@class="rust typedef"]' 'pub(in outer) type TypeSuper ='
+ // @has 'foo/outer/inner/type.TypeSuper.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub(in outer) type TypeSuper ='
pub(super) use reexports::TypeSuper;
// @!has 'foo/outer/inner/type.TypeSelf.html'
pub(self) use reexports::TypeSelf;
// @!has 'foo/enum.BarLocal.html'
use reexports::BarLocal;
-// @has 'foo/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+// @has 'foo/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
// @!has 'foo/fn.foo_crate.html'
pub(crate) use reexports::foo_crate;
// @!has 'foo/fn.foo_local.html'
use reexports::foo_local;
-// @has 'foo/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+// @has 'foo/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
// @!has 'foo/type.TypeCrate.html'
pub(crate) use reexports::TypeCrate;
// @!has 'foo/outer/inner/enum.BarLocal.html'
use reexports::BarLocal;
- // @has 'foo/outer/inner/fn.foo.html' '//*[@class="rust fn"]' 'pub fn foo()'
+ // @has 'foo/outer/inner/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo()'
pub use reexports::foo;
// @!has 'foo/outer/inner/fn.foo_crate.html'
pub(crate) use reexports::foo_crate;
// @!has 'foo/outer/inner/fn.foo_local.html'
use reexports::foo_local;
- // @has 'foo/outer/inner/type.Type.html' '//*[@class="rust typedef"]' 'pub type Type ='
+ // @has 'foo/outer/inner/type.Type.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub type Type ='
pub use reexports::Type;
// @!has 'foo/outer/inner/type.TypeCrate.html'
pub(crate) use reexports::TypeCrate;
pub struct S<T>(T);
-// @!has foo/trait.Tr.html '//pre[@class="rust trait"]/code/a[@class="trait"]' '~const'
-// @has - '//pre[@class="rust trait"]/code/a[@class="trait"]' 'Clone'
-// @!has - '//pre[@class="rust trait"]/code/span[@class="where"]' '~const'
-// @has - '//pre[@class="rust trait"]/code/span[@class="where"]' ': Clone'
+// @!has foo/trait.Tr.html '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' 'Clone'
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where"]' ': Clone'
#[const_trait]
pub trait Tr<T> {
// @!has - '//section[@id="method.a"]/h4[@class="code-header"]' '~const'
}
}
-// @!has foo/fn.foo.html '//pre[@class="rust fn"]/code/a[@class="trait"]' '~const'
-// @has - '//pre[@class="rust fn"]/code/a[@class="trait"]' 'Clone'
-// @!has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' '~const'
-// @has - '//pre[@class="rust fn"]/code/span[@class="where fmt-newline"]' ': Clone'
+// @!has foo/fn.foo.html '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/a[@class="trait"]' 'Clone'
+// @!has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where fmt-newline"]' '~const'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]/code/span[@class="where fmt-newline"]' ': Clone'
pub const fn foo<F: ~const Clone + ~const Destruct>()
where
Option<F>: ~const Clone + ~const Destruct,
extern "rust-intrinsic" {
// @has 'foo/fn.abort.html'
- // @has - '//pre[@class="rust fn"]' 'pub extern "rust-intrinsic" fn abort() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub extern "rust-intrinsic" fn abort() -> !'
#[rustc_safe_intrinsic]
pub fn abort() -> !;
// @has 'foo/fn.unreachable.html'
- // @has - '//pre[@class="rust fn"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "rust-intrinsic" fn unreachable() -> !'
pub fn unreachable() -> !;
}
extern "C" {
// @has 'foo/fn.needs_drop.html'
- // @has - '//pre[@class="rust fn"]' 'pub unsafe extern "C" fn needs_drop() -> !'
+ // @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub unsafe extern "C" fn needs_drop() -> !'
pub fn needs_drop() -> !;
}
pub struct MyBox<T: ?Sized>(*const T);
// @has 'foo/fn.alpha.html'
-// @snapshot link_slice_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn alpha() -> &'static [u32] {
loop {}
}
// @has 'foo/fn.beta.html'
-// @snapshot link_slice_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_slice_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn beta<T>() -> &'static [T] {
loop {}
}
// @has 'foo/fn.gamma.html'
-// @snapshot link_box_u32 - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_u32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn gamma() -> MyBox<[u32]> {
loop {}
}
// @has 'foo/fn.delta.html'
-// @snapshot link_box_generic - '//pre[@class="rust fn"]/code'
+// @snapshot link_box_generic - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn delta<T>() -> MyBox<[T]> {
loop {}
}
-<script type="text/json" id="notable-traits-data">{"Odd":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></code></h3><pre class=\"content\"><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html\" title=\"trait core::iter::traits::iterator::Iterator\">Iterator</a> for <a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></span><span class=\"where fmt-newline\"> type <a href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html#associatedtype.Item\" class=\"associatedtype\">Item</a> = <a class=\"primitive\" href=\"{{channel}}/std/primitive.usize.html\">usize</a>;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"Odd":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html\" title=\"trait core::iter::traits::iterator::Iterator\">Iterator</a> for <a class=\"struct\" href=\"struct.Odd.html\" title=\"struct foo::Odd\">Odd</a></span><span class=\"where fmt-newline\"> type <a href=\"{{channel}}/core/iter/traits/iterator/trait.Iterator.html#associatedtype.Item\" class=\"associatedtype\">Item</a> = <a class=\"primitive\" href=\"{{channel}}/std/primitive.usize.html\">usize</a>;</span>"}</script>
\ No newline at end of file
// block doc comments can have their lines starting with a star.
// @has foo/fn.foo.html
-// @snapshot docblock - '//*[@class="rustdoc-toggle top-doc"]//*[@class="docblock"]'
+// @snapshot docblock - '//*[@class="toggle top-doc"]//*[@class="docblock"]'
/**
* a
*/
hir_id: usize,
}
-// @has 'foo/fn.body_owner.html' '//*[@class="rust fn"]' 'pub fn body_owner(_: BodyId)'
+// @has 'foo/fn.body_owner.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn body_owner(_: BodyId)'
pub fn body_owner(BodyId { hir_id }: BodyId) {
// ...
}
#![crate_name = "foo"]
// @has foo/fn.foo.html
-// @has - '//*[@class="rust fn"]' "_: &(dyn ToString + 'static)"
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' "_: &(dyn ToString + 'static)"
pub fn foo(_: &(ToString + 'static)) {}
#![allow(unused)]
// @has 'toggle_item_contents/struct.PubStruct.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+// @count - '//details[@class="toggle type-contents-toggle"]' 0
pub struct PubStruct {
pub a: usize,
pub b: usize,
}
// @has 'toggle_item_contents/struct.BigPubStruct.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 13 fields'
+// @count - '//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//details[@class="toggle type-contents-toggle"]' 'Show 13 fields'
pub struct BigPubStruct {
pub a: usize,
pub b: usize,
}
// @has 'toggle_item_contents/union.BigUnion.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 13 fields'
+// @count - '//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//details[@class="toggle type-contents-toggle"]' 'Show 13 fields'
pub union BigUnion {
pub a: usize,
pub b: usize,
}
// @has 'toggle_item_contents/union.Union.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+// @count - '//details[@class="toggle type-contents-toggle"]' 0
pub union Union {
pub a: usize,
pub b: usize,
}
// @has 'toggle_item_contents/struct.PrivStruct.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+// @count - '//details[@class="toggle type-contents-toggle"]' 0
// @has - '//div[@class="item-decl"]' '/* private fields */'
pub struct PrivStruct {
a: usize,
}
// @has 'toggle_item_contents/enum.Enum.html'
-// @!has - '//details[@class="rustdoc-toggle type-contents-toggle"]' ''
+// @!has - '//details[@class="toggle type-contents-toggle"]' ''
pub enum Enum {
A, B, C,
D {
}
// @has 'toggle_item_contents/enum.EnumStructVariant.html'
-// @!has - '//details[@class="rustdoc-toggle type-contents-toggle"]' ''
+// @!has - '//details[@class="toggle type-contents-toggle"]' ''
pub enum EnumStructVariant {
A, B, C,
D {
}
// @has 'toggle_item_contents/enum.LargeEnum.html'
-// @count - '//*[@class="rust enum"]//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//*[@class="rust enum"]//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 13 variants'
+// @count - '//div[@class="item-decl"]/pre//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//div[@class="item-decl"]/pre//details[@class="toggle type-contents-toggle"]' 'Show 13 variants'
pub enum LargeEnum {
A, B, C, D, E, F(u8), G, H, I, J, K, L, M
}
// @has 'toggle_item_contents/trait.Trait.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 0
+// @count - '//details[@class="toggle type-contents-toggle"]' 0
pub trait Trait {
type A;
#[must_use]
}
// @has 'toggle_item_contents/trait.GinormousTrait.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 16 associated items'
+// @count - '//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//details[@class="toggle type-contents-toggle"]' 'Show 16 associated items'
pub trait GinormousTrait {
type A;
type B;
}
// @has 'toggle_item_contents/trait.HugeTrait.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 12 associated constants and 2 methods'
+// @count - '//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//details[@class="toggle type-contents-toggle"]' 'Show 12 associated constants and 2 methods'
pub trait HugeTrait {
type A;
const M: usize = 1;
}
// @has 'toggle_item_contents/trait.GiganticTrait.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 1 associated constant and 1 method'
+// @count - '//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//details[@class="toggle type-contents-toggle"]' 'Show 1 associated constant and 1 method'
pub trait GiganticTrait {
type A;
type B;
}
// @has 'toggle_item_contents/trait.BigTrait.html'
-// @count - '//details[@class="rustdoc-toggle type-contents-toggle"]' 1
-// @has - '//details[@class="rustdoc-toggle type-contents-toggle"]' 'Show 14 methods'
+// @count - '//details[@class="toggle type-contents-toggle"]' 1
+// @has - '//details[@class="toggle type-contents-toggle"]' 'Show 14 methods'
pub trait BigTrait {
type A;
#[must_use]
// summary. Struct methods with no documentation should not be wrapped.
//
// @has foo/struct.Foo.html
-// @has - '//details[@class="rustdoc-toggle method-toggle"]//summary//h4[@class="code-header"]' 'is_documented()'
-// @has - '//details[@class="rustdoc-toggle method-toggle"]//*[@class="docblock"]' 'is_documented is documented'
-// @!has - '//details[@class="rustdoc-toggle method-toggle"]//summary//h4[@class="code-header"]' 'not_documented()'
+// @has - '//details[@class="toggle method-toggle"]//summary//h4[@class="code-header"]' 'is_documented()'
+// @has - '//details[@class="toggle method-toggle"]//*[@class="docblock"]' 'is_documented is documented'
+// @!has - '//details[@class="toggle method-toggle"]//summary//h4[@class="code-header"]' 'not_documented()'
pub struct Foo {
}
// summary. Trait methods with no documentation should not be wrapped.
//
// @has foo/trait.Foo.html
-// @has - '//details[@class="rustdoc-toggle"]//summary//h4[@class="code-header"]' 'type Item'
-// @!has - '//details[@class="rustdoc-toggle"]//summary//h4[@class="code-header"]' 'type Item2'
-// @has - '//details[@class="rustdoc-toggle method-toggle"]//summary//h4[@class="code-header"]' 'is_documented()'
-// @!has - '//details[@class="rustdoc-toggle method-toggle"]//summary//h4[@class="code-header"]' 'not_documented()'
-// @has - '//details[@class="rustdoc-toggle method-toggle"]//*[@class="docblock"]' 'is_documented is documented'
-// @has - '//details[@class="rustdoc-toggle method-toggle"]//summary//h4[@class="code-header"]' 'is_documented_optional()'
-// @!has - '//details[@class="rustdoc-toggle method-toggle"]//summary//h4[@class="code-header"]' 'not_documented_optional()'
-// @has - '//details[@class="rustdoc-toggle method-toggle"]//*[@class="docblock"]' 'is_documented_optional is documented'
+// @has - '//details[@class="toggle"]//summary//h4[@class="code-header"]' 'type Item'
+// @!has - '//details[@class="toggle"]//summary//h4[@class="code-header"]' 'type Item2'
+// @has - '//details[@class="toggle method-toggle"]//summary//h4[@class="code-header"]' 'is_documented()'
+// @!has - '//details[@class="toggle method-toggle"]//summary//h4[@class="code-header"]' 'not_documented()'
+// @has - '//details[@class="toggle method-toggle"]//*[@class="docblock"]' 'is_documented is documented'
+// @has - '//details[@class="toggle method-toggle"]//summary//h4[@class="code-header"]' 'is_documented_optional()'
+// @!has - '//details[@class="toggle method-toggle"]//summary//h4[@class="code-header"]' 'not_documented_optional()'
+// @has - '//details[@class="toggle method-toggle"]//*[@class="docblock"]' 'is_documented_optional is documented'
pub trait Foo {
/// is documented
type Item;
);
// @has foo/enum.Bar.html
-// @has - '//pre[@class="rust enum"]' 'BarVariant(String),'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'BarVariant(String),'
// @matches - '//*[@id="variant.BarVariant.fields"]/h4' '^Tuple Fields$'
// @has - '//*[@id="variant.BarVariant.field.0"]' '0: String'
// @has - '//*[@id="variant.BarVariant.fields"]//*[@class="docblock"]' 'Hello docs'
#![crate_name = "foo"]
// @has foo/fn.tuple0.html //pre 'pub fn tuple0(x: ())'
-// @snapshot link_unit - '//pre[@class="rust fn"]/code'
+// @snapshot link_unit - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple0(x: ()) -> () { x }
// @has foo/fn.tuple1.html //pre 'pub fn tuple1(x: (i32,)) -> (i32,)'
-// @snapshot link1_i32 - '//pre[@class="rust fn"]/code'
+// @snapshot link1_i32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple1(x: (i32,)) -> (i32,) { x }
// @has foo/fn.tuple2.html //pre 'pub fn tuple2(x: (i32, i32)) -> (i32, i32)'
-// @snapshot link2_i32 - '//pre[@class="rust fn"]/code'
+// @snapshot link2_i32 - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple2(x: (i32, i32)) -> (i32, i32) { x }
// @has foo/fn.tuple1_t.html //pre 'pub fn tuple1_t<T>(x: (T,)) -> (T,)'
-// @snapshot link1_t - '//pre[@class="rust fn"]/code'
+// @snapshot link1_t - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple1_t<T>(x: (T,)) -> (T,) { x }
// @has foo/fn.tuple2_t.html //pre 'pub fn tuple2_t<T>(x: (T, T)) -> (T, T)'
-// @snapshot link2_t - '//pre[@class="rust fn"]/code'
+// @snapshot link2_t - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple2_t<T>(x: (T, T)) -> (T, T) { x }
// @has foo/fn.tuple2_tu.html //pre 'pub fn tuple2_tu<T, U>(x: (T, U)) -> (T, U)'
-// @snapshot link2_tu - '//pre[@class="rust fn"]/code'
+// @snapshot link2_tu - '//div[@class="item-decl"]/pre[@class="rust"]/code'
pub fn tuple2_tu<T, U>(x: (T, U)) -> (T, U) { x }
extern crate unit_return;
-// @has 'foo/fn.f0.html' '//*[@class="rust fn"]' 'F: FnMut(u8) + Clone'
+// @has 'foo/fn.f0.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u8) + Clone'
pub fn f0<F: FnMut(u8) + Clone>(f: F) {}
-// @has 'foo/fn.f1.html' '//*[@class="rust fn"]' 'F: FnMut(u16) + Clone'
+// @has 'foo/fn.f1.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u16) + Clone'
pub fn f1<F: FnMut(u16) -> () + Clone>(f: F) {}
-// @has 'foo/fn.f2.html' '//*[@class="rust fn"]' 'F: FnMut(u32) + Clone'
+// @has 'foo/fn.f2.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u32) + Clone'
pub use unit_return::f2;
-// @has 'foo/fn.f3.html' '//*[@class="rust fn"]' 'F: FnMut(u64) + Clone'
+// @has 'foo/fn.f3.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'F: FnMut(u64) + Clone'
pub use unit_return::f3;
#![crate_name = "foo"]
// @has foo/fn.foo.html
-// @has - '//*[@class="rust fn"]' 'pub fn foo<X, Y: ?Sized>(_: &X)'
-// @has - '//*[@class="rust fn"]' 'where X: ?Sized,'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo<X, Y: ?Sized>(_: &X)'
+// @has - '//div[@class="item-decl"]/pre[@class="rust"]' 'where X: ?Sized,'
pub fn foo<X, Y: ?Sized>(_: &X) where X: ?Sized {}
-<div class="item-decl"><pre class="rust struct"><code>pub struct Simd<T>(_)<br /><span class="where">where<br />    T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre></div>
\ No newline at end of file
+<div class="item-decl"><pre class="rust"><code>pub struct Simd<T>(_)<br /><span class="where">where<br />    T: <a class="trait" href="trait.MyTrait.html" title="trait foo::MyTrait">MyTrait</a></span>;</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust trait"><code>pub trait TraitWhere {
+<div class="item-decl"><pre class="rust"><code>pub trait TraitWhere {
type <a href="#associatedtype.Item" class="associatedtype">Item</a><'a><br />    <span class="where">where<br />        Self: 'a</span>;
fn <a href="#method.func" class="fn">func</a>(self)<br />    <span class="where">where<br />        Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
// "impl<F> MyTrait for Foxtrot<F>where F: MyTrait"
impl<F> MyTrait for Foxtrot<F>where F: MyTrait {}
-// @has foo/type.Golf.html '//pre[@class="rust typedef"]' \
+// @has foo/type.Golf.html '//div[@class="item-decl"]/pre[@class="rust"]' \
// "type Golf<T>where T: Clone, = (T, T)"
pub type Golf<T> where T: Clone = (T, T);
-<div class="item-decl"><pre class="rust enum"><code>pub enum Cow<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
- Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B),
+<div class="item-decl"><pre class="rust"><code>pub enum Cow<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
+ Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>),
Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust enum"><code>pub enum Cow2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
- Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B),
+<div class="item-decl"><pre class="rust"><code>pub enum Cow2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+ Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>),
Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust struct"><code>pub struct Struct<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
- pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B,
+<div class="item-decl"><pre class="rust"><code>pub struct Struct<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
+ pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>,
pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust struct"><code>pub struct Struct2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
- pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a </a>B,
+<div class="item-decl"><pre class="rust"><code>pub struct Struct2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+ pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>,
pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust trait"><code>pub trait ToOwned<T><span class="where fmt-newline">where<br />    T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</span>{
+<div class="item-decl"><pre class="rust"><code>pub trait ToOwned<T><span class="where fmt-newline">where<br />    T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</span>{
type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&self) -> Self::<a class="associatedtype" href="trait.ToOwned.html#associatedtype.Owned" title="type foo::ToOwned::Owned">Owned</a>;
-<div class="item-decl"><pre class="rust trait"><code>pub trait ToOwned2<T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> {
+<div class="item-decl"><pre class="rust"><code>pub trait ToOwned2<T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> {
type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&self) -> Self::<a class="associatedtype" href="trait.ToOwned2.html#associatedtype.Owned" title="type foo::ToOwned2::Owned">Owned</a>;
-<div class="item-decl"><pre class="rust union"><code>pub union Union<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
+<div class="item-decl"><pre class="rust"><code>pub union Union<'a, B><span class="where fmt-newline">where<br />    B: <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + 'a,</span>{
/* private fields */
}</code></pre></div>
\ No newline at end of file
-<div class="item-decl"><pre class="rust union"><code>pub union Union2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+<div class="item-decl"><pre class="rust"><code>pub union Union2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
/* private fields */
}</code></pre></div>
\ No newline at end of file
use std::fmt::Debug;
-// @has 'wrapping/fn.foo.html' '//pre[@class="rust fn"]' 'pub fn foo() -> impl Debug'
-// @count - '//pre[@class="rust fn"]/br' 0
+// @has 'wrapping/fn.foo.html' '//div[@class="item-decl"]/pre[@class="rust"]' 'pub fn foo() -> impl Debug'
+// @count - '//div[@class="item-decl"]/pre[@class="rust"]/br' 0
pub fn foo() -> impl Debug {}
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 | }
// 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)]
g(ExprKind::Closure(Box::new(Closure {
binder: ClosureBinder::NotPresent,
capture_clause: CaptureBy::Value,
+ constness: Const::No,
asyncness: Async::No,
movability: Movability::Movable,
fn_decl: decl.clone(),
#[diag(compiletest_example)]
struct SubdiagnosticEagerCorrect {
#[subdiagnostic(eager)]
- //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
note: Note,
}
#[diag(compiletest_example)]
struct SubdiagnosticEagerSuggestion {
#[subdiagnostic(eager)]
- //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute
sub: SubdiagnosticWithSuggestion,
}
LL | #[subdiagnostic(bad)]
| ^^^^^^^^^^^^^^^^^^^^^
|
- = help: `subdiagnostic` does not support nested attributes
+ = help: `eager` is the only supported nested attribute for `subdiagnostic`
error: `#[subdiagnostic = ...]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:693:5
LL | #[subdiagnostic(bad, bad)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: `subdiagnostic` does not support nested attributes
+ = help: `eager` is the only supported nested attribute for `subdiagnostic`
error: `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:709:5
LL | #[subdiagnostic("bad")]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: `subdiagnostic` does not support nested attributes
+ = help: `eager` is the only supported nested attribute for `subdiagnostic`
error: `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:717:5
LL | #[subdiagnostic(eager)]
| ^^^^^^^^^^^^^^^^^^^^^^^
|
- = help: `subdiagnostic` does not support nested attributes
-
-error: `#[subdiagnostic(...)]` is not a valid attribute
- --> $DIR/diagnostic-derive.rs:725:5
- |
-LL | #[subdiagnostic(eager)]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: `subdiagnostic` does not support nested attributes
-
-error: `#[subdiagnostic(...)]` is not a valid attribute
- --> $DIR/diagnostic-derive.rs:746:5
- |
-LL | #[subdiagnostic(eager)]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: `subdiagnostic` does not support nested attributes
+ = help: eager subdiagnostics are not supported on lints
error: expected at least one string literal for `code(...)`
- --> $DIR/diagnostic-derive.rs:777:18
+ --> $DIR/diagnostic-derive.rs:775:18
|
LL | #[suggestion(code())]
| ^^^^^^
error: `code(...)` must contain only string literals
- --> $DIR/diagnostic-derive.rs:785:23
+ --> $DIR/diagnostic-derive.rs:783:23
|
LL | #[suggestion(code(foo))]
| ^^^
error: `code = "..."`/`code(...)` must contain only string literals
- --> $DIR/diagnostic-derive.rs:793:18
+ --> $DIR/diagnostic-derive.rs:791:18
|
LL | #[suggestion(code = 3)]
| ^^^^^^^^
--> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC
= note: this error originates in the derive macro `Diagnostic` which comes from the expansion of the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 85 previous errors
+error: aborting due to 83 previous errors
Some errors have detailed explanations: E0277, E0425.
For more information about an error, try `rustc --explain E0277`.
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 |
--- /dev/null
+// run-rustfix
+trait Trait<A> {}
+
+trait Assoc {
+ type Ty;
+}
+
+impl<A> Assoc for dyn Trait<A> {
+ type Ty = i32;
+}
+
+fn main() {
+ let _x: <dyn Trait<i32> as Assoc>::Ty; //~ ERROR ambiguous associated type
+}
--- /dev/null
+// run-rustfix
+trait Trait<A> {}
+
+trait Assoc {
+ type Ty;
+}
+
+impl<A> Assoc for dyn Trait<A> {
+ type Ty = i32;
+}
+
+fn main() {
+ let _x: <dyn Trait<i32>>::Ty; //~ ERROR ambiguous associated type
+}
--- /dev/null
+error[E0223]: ambiguous associated type
+ --> $DIR/ambiguous-associated-type-with-generics.rs:13:13
+ |
+LL | let _x: <dyn Trait<i32>>::Ty;
+ | ^^^^^^^^^^^^^^^^^^^^ help: use the fully-qualified path: `<dyn Trait<i32> as Assoc>::Ty`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0223`.
--> $DIR/associated-item-duplicate-names-3.rs:18:12
|
LL | let x: Baz::Bar = 5;
- | ^^^^^^^^ help: use fully-qualified syntax: `<Baz as Trait>::Bar`
+ | ^^^^^^^^ help: use the fully-qualified path: `<Baz as Foo>::Bar`
error: aborting due to 2 previous errors
--> $DIR/associated-types-in-ambiguous-context.rs:6:36
|
LL | fn get<T:Get,U:Get>(x: T, y: U) -> Get::Value {}
- | ^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Get>::Value`
+ | ^^^^^^^^^^
+ |
+help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path
+ |
+LL | fn get<T:Get,U:Get>(x: T, y: U) -> <Example as Get>::Value {}
+ | ~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/associated-types-in-ambiguous-context.rs:20:17
|
LL | trait Foo where Foo::Assoc: Bar {
- | ^^^^^^^^^^ help: use fully-qualified syntax: `<Self as Foo>::Assoc`
+ | ^^^^^^^^^^ help: use the fully-qualified path: `<Self as Foo>::Assoc`
error[E0223]: ambiguous associated type
--> $DIR/associated-types-in-ambiguous-context.rs:25:10
|
LL | type X = std::ops::Deref::Target;
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Deref>::Target`
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: if there were a type named `Example` that implemented `Deref`, you could use the fully-qualified path
+ |
+LL | type X = <Example as Deref>::Target;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/associated-types-in-ambiguous-context.rs:11:23
|
LL | fn grab(&self) -> Grab::Value;
- | ^^^^^^^^^^^ help: use fully-qualified syntax: `<Self as Grab>::Value`
+ | ^^^^^^^^^^^ help: use the fully-qualified path: `<Self as Grab>::Value`
error[E0223]: ambiguous associated type
--> $DIR/associated-types-in-ambiguous-context.rs:14:22
|
LL | fn get(&self) -> Get::Value;
- | ^^^^^^^^^^ help: use fully-qualified syntax: `<Type as Get>::Value`
+ | ^^^^^^^^^^
+ |
+help: if there were a type named `Example` that implemented `Get`, you could use the fully-qualified path
+ |
+LL | fn get(&self) -> <Example as Get>::Value;
+ | ~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 5 previous errors
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);
LL | type Assoc = T;
| ^ the trait `Copy` is not implemented for `T`
|
+note: required for `<T as Complete>::Assoc` to implement `Partial<T>`
+ --> $DIR/issue-43784-associated-type.rs:1:11
+ |
+LL | pub trait Partial<X: ?Sized>: Copy {
+ | ^^^^^^^
note: required by a bound in `Complete::Assoc`
--> $DIR/issue-43784-associated-type.rs:5:17
|
--- /dev/null
+// 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);
+}
--- /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
+
-// check-pass
// edition: 2021
+// known-bug: #105197
+// failure-status:101
+// dont-check-compiler-stderr
#![feature(async_fn_in_trait)]
#![feature(return_position_impl_trait_in_trait)]
--- /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();
+}
| ^^^^^^^^ 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
| ^^^^^^^^ 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
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`
--> $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
+// run-pass
+//
+// This test makes sure that log-backtrace option doesn't give a compilation error.
+//
+// dont-check-compiler-stdout
+// dont-check-compiler-stderr
+// rustc-env:RUSTC_LOG=info
+// compile-flags: -Zlog-backtrace=rustc_metadata::creader
+fn main() {}
}
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
...
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrow-tuple-fields.rs:12:13
|
+LL | let x: (Box<_>, _) = (Box::new(1), 2);
+ | - binding `x` declared here
LL | let r = &x.0;
| ---- borrow of `x.0` occurs here
LL | let y = x;
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrow-tuple-fields.rs:28:13
|
+LL | let x = Foo(Box::new(1), 2);
+ | - binding `x` declared here
LL | let r = &x.0;
| ---- borrow of `x.0` occurs here
LL | let y = x;
--> $DIR/borrowck-anon-fields-variant.rs:16:19
|
LL | Foo::Y(ref mut a, _) => a,
- | --------- borrow of `y.0` occurs here
+ | --------- `y.0` is borrowed here
...
LL | let b = match y {
| ^ use of borrowed `y.0`
--> $DIR/borrowck-anon-fields-variant.rs:34:19
|
LL | Foo::Y(ref mut a, _) => a,
- | --------- borrow of `y.0` occurs here
+ | --------- `y.0` is borrowed here
...
LL | let b = match y {
| ^ use of borrowed `y.0`
--> $DIR/borrowck-assign-comp.rs:10:5
|
LL | let q = &p;
- | -- borrow of `p.x` occurs here
+ | -- `p.x` is borrowed here
...
LL | p.x = 5;
- | ^^^^^^^ assignment to borrowed `p.x` occurs here
+ | ^^^^^^^ `p.x` is assigned to here but it was already borrowed
LL | q.x;
| --- borrow later used here
--> $DIR/borrowck-assign-comp.rs:20:5
|
LL | let q = &p.y;
- | ---- borrow of `p` occurs here
+ | ---- `p` is borrowed here
LL | p = Point {x: 5, y: 7};
- | ^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `p` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^ `p` is assigned to here but it was already borrowed
LL | p.x; // silence warning
LL | *q; // stretch loan
| -- borrow later used here
--> $DIR/borrowck-assign-comp.rs:31:5
|
LL | let q = &p.y;
- | ---- borrow of `p.y` occurs here
+ | ---- `p.y` is borrowed here
LL | p.y = 5;
- | ^^^^^^^ assignment to borrowed `p.y` occurs here
+ | ^^^^^^^ `p.y` is assigned to here but it was already borrowed
LL | *q;
| -- borrow later used here
--> $DIR/borrowck-assign-to-andmut-in-borrowed-loc.rs:18:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `y` occurs here
+ | ------ `y` is borrowed here
LL | *y.pointer += 1;
| ^^^^^^^^^^^^^^^ use of borrowed `y`
...
--> $DIR/borrowck-assign-to-andmut-in-borrowed-loc.rs:18:9
|
LL | let z = copy_borrowed_ptr(&mut y);
- | ------ borrow of `*y.pointer` occurs here
+ | ------ `*y.pointer` is borrowed here
LL | *y.pointer += 1;
- | ^^^^^^^^^^^^^^^ assignment to borrowed `*y.pointer` occurs here
+ | ^^^^^^^^^^^^^^^ `*y.pointer` is assigned to here but it was already borrowed
...
LL | *z.pointer += 1;
| --------------- borrow later used here
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/borrowck-bad-nested-calls-move.rs:25:9
|
+LL | let mut a: Box<_> = Box::new(1);
+ | ----- binding `a` declared here
+...
LL | add(
| --- borrow later used by call
LL | &*a,
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/borrowck-bad-nested-calls-move.rs:32:9
|
+LL | let mut a: Box<_> = Box::new(1);
+ | ----- binding `a` declared here
LL | add(
| --- borrow later used by call
LL | &*a,
LL | let c2 = || x * 5;
| -- - borrow occurs due to use in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | x = 5;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL |
LL | drop(c2);
| -- borrow later used here
LL | let c1 = || get(&x);
| -- - borrow occurs due to use in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | x = 5;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
LL |
LL | drop(c1);
| -- borrow later used here
LL | let c1 = || get(&*x);
| -- -- borrow occurs due to use in closure
| |
- | borrow of `*x` occurs here
+ | `*x` is borrowed here
LL | *x = 5;
- | ^^^^^^ assignment to borrowed `*x` occurs here
+ | ^^^^^^ `*x` is assigned to here but it was already borrowed
LL |
LL | drop(c1);
| -- borrow later used here
LL | let c1 = || get(&*x.f);
| -- ---- borrow occurs due to use in closure
| |
- | borrow of `*x.f` occurs here
+ | `*x.f` is borrowed here
LL | *x.f = 5;
- | ^^^^^^^^ assignment to borrowed `*x.f` occurs here
+ | ^^^^^^^^ `*x.f` is assigned to here but it was already borrowed
LL |
LL | drop(c1);
| -- borrow later used here
--> $DIR/borrowck-describe-lvalue.rs:37:9
|
LL | let x = f.x();
- | ----- borrow of `f` occurs here
+ | ----- `f` is borrowed here
LL | f.x;
| ^^^ use of borrowed `f`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:44:9
|
LL | let x = g.x();
- | ----- borrow of `g` occurs here
+ | ----- `g` is borrowed here
LL | g.0;
| ^^^ use of borrowed `g`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:51:9
|
LL | let x = &mut h.0;
- | -------- borrow of `h.0` occurs here
+ | -------- `h.0` is borrowed here
LL | h.0;
| ^^^ use of borrowed `h.0`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:59:20
|
LL | let x = e.x();
- | ----- borrow of `e` occurs here
+ | ----- `e` is borrowed here
LL | match e {
LL | Baz::X(value) => value
| ^^^^^ use of borrowed `e`
--> $DIR/borrowck-describe-lvalue.rs:67:9
|
LL | let x = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | u.a;
| ^^^ use of borrowed `u.a`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:74:9
|
LL | let x = f.x();
- | ----- borrow of `*f` occurs here
+ | ----- `*f` is borrowed here
LL | f.x;
| ^^^ use of borrowed `*f`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:81:9
|
LL | let x = g.x();
- | ----- borrow of `*g` occurs here
+ | ----- `*g` is borrowed here
LL | g.0;
| ^^^ use of borrowed `*g`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:88:9
|
LL | let x = &mut h.0;
- | -------- borrow of `h.0` occurs here
+ | -------- `h.0` is borrowed here
LL | h.0;
| ^^^ use of borrowed `h.0`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:96:20
|
LL | let x = e.x();
- | ----- borrow of `*e` occurs here
+ | ----- `*e` is borrowed here
LL | match *e {
LL | Baz::X(value) => value
| ^^^^^ use of borrowed `*e`
--> $DIR/borrowck-describe-lvalue.rs:105:9
|
LL | let x = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | u.a;
| ^^^ use of borrowed `u.a`
LL | drop(x);
--> $DIR/borrowck-describe-lvalue.rs:113:15
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | match v {
LL | &[x, _, .., _, _] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:118:18
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, x, .., _, _] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:123:25
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, _, .., x, _] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:128:28
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, _, .., _, x] => println!("{}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:139:15
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | match v {
LL | &[x @ ..] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:144:18
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, x @ ..] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:149:15
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[x @ .., _] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:154:18
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
...
LL | &[_, x @ .., _] => println!("{:?}", x),
| ^ use of borrowed `v`
--> $DIR/borrowck-describe-lvalue.rs:166:15
|
LL | let x = &mut e;
- | ------ borrow of `e` occurs here
+ | ------ `e` is borrowed here
LL | match e {
| ^ use of borrowed `e`
...
--> $DIR/borrowck-describe-lvalue.rs:232:9
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | v[0].y;
| ^^^^ use of borrowed `v`
...
--> $DIR/borrowck-describe-lvalue.rs:232:9
|
LL | let x = &mut v;
- | ------ borrow of `v` occurs here
+ | ------ `v` is borrowed here
LL | v[0].y;
| ^^^^^^ use of borrowed `v`
...
error[E0505]: cannot move out of `x.b` because it is borrowed
--> $DIR/borrowck-field-sensitivity.rs:34:10
|
+LL | let x = A { a: 1, b: Box::new(2) };
+ | - binding `x` declared here
LL | let p = &x.b;
| ---- borrow of `x.b` occurs here
LL | drop(x.b);
error[E0505]: cannot move out of `x.b` because it is borrowed
--> $DIR/borrowck-field-sensitivity.rs:41:14
|
+LL | let x = A { a: 1, b: Box::new(2) };
+ | - binding `x` declared here
LL | let p = &x.b;
| ---- borrow of `x.b` occurs here
LL | let _y = A { a: 3, .. x };
--> $DIR/borrowck-imm-ref-to-mut-rec-field-issue-3162-c.rs:6:9
|
LL | let b = &mut _a;
- | ------- borrow of `_a` occurs here
+ | ------- `_a` is borrowed here
...
LL | _a = 4;
- | ^^^^^^ assignment to borrowed `_a` occurs here
+ | ^^^^^^ `_a` is assigned to here but it was already borrowed
...
LL | drop(b);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:25:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:35:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:45:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:55:5
|
LL | let p = &y;
- | -- borrow of `**y` occurs here
+ | -- `**y` is borrowed here
LL | let q = &***p;
LL | **y = 2;
- | ^^^^^^^ assignment to borrowed `**y` occurs here
+ | ^^^^^^^ `**y` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:65:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:75:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:85:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-issue-14498.rs:95:5
|
LL | let p = &y.a;
- | ---- borrow of `**y.a` occurs here
+ | ---- `**y.a` is borrowed here
LL | let q = &***p;
LL | **y.a = 2;
- | ^^^^^^^^^ assignment to borrowed `**y.a` occurs here
+ | ^^^^^^^^^ `**y.a` is assigned to here but it was already borrowed
LL | drop(p);
| - borrow later used here
--> $DIR/borrowck-lend-flow-match.rs:12:13
|
LL | Some(ref r) => {
- | ----- borrow of `x` occurs here
+ | ----- `x` is borrowed here
LL | x = Some(1);
- | ^^^^^^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^^^^^^ `x` is assigned to here but it was already borrowed
LL | drop(r);
| - borrow later used here
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move-cc.rs:14:19
|
+LL | let v: Box<_> = Box::new(3);
+ | - binding `v` declared here
LL | let w = &v;
| -- borrow of `v` occurs here
LL | thread::spawn(move|| {
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move-cc.rs:24:19
|
+LL | let v: Box<_> = Box::new(3);
+ | - binding `v` declared here
LL | let w = &v;
| -- borrow of `v` occurs here
LL | thread::spawn(move|| {
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/borrowck-loan-blocks-move.rs:11:10
|
+LL | let v = Box::new(3);
+ | - binding `v` declared here
LL | let w = &v;
| -- borrow of `v` occurs here
LL | take(v);
--> $DIR/borrowck-loan-of-static-data-issue-27616.rs:16:5
|
LL | let alias: &'static mut String = s;
- | ------------------- - borrow of `*s` occurs here
+ | ------------------- - `*s` is borrowed here
| |
| type annotation requires that `*s` is borrowed for `'static`
...
LL | *s = String::new();
- | ^^ assignment to borrowed `*s` occurs here
+ | ^^ `*s` is assigned to here but it was already borrowed
error: aborting due to previous error
--> $DIR/borrowck-loan-rcvr-overloaded-op.rs:38:5
|
LL | let q = &mut p;
- | ------ borrow of `p` occurs here
+ | ------ `p` is borrowed here
LL |
LL | p + 3;
| ^ use of borrowed `p`
error[E0597]: `z.1` does not live long enough
--> $DIR/borrowck-local-borrow-with-panic-outlives-fn.rs:3:15
|
+LL | let mut z = (0, 0);
+ | ----- binding `z` declared here
LL | *x = Some(&mut z.1);
| ----------^^^^^^^^-
| | |
--> $DIR/borrowck-match-already-borrowed.rs:9:19
|
LL | let p = &mut foo;
- | -------- borrow of `foo` occurs here
+ | -------- `foo` is borrowed here
LL | let _ = match foo {
| ^^^ use of borrowed `foo`
...
--> $DIR/borrowck-match-already-borrowed.rs:12:16
|
LL | let p = &mut foo;
- | -------- borrow of `foo` occurs here
+ | -------- `foo` is borrowed here
...
LL | Foo::A(x) => x
| ^ use of borrowed `foo`
--> $DIR/borrowck-match-already-borrowed.rs:22:9
|
LL | let r = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | let _ = match x {
LL | x => x + 1,
| ^ use of borrowed `x`
--> $DIR/borrowck-match-already-borrowed.rs:23:9
|
LL | let r = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
...
LL | y => y + 2,
| ^ use of borrowed `x`
| | |
| | variable moved due to use in closure
| | move occurs because `bar` has type `Box<isize>`, which does not implement the `Copy` trait
- | move out of `bar` occurs here
+ | `bar` is moved here
error: aborting due to previous error
error[E0505]: cannot move out of `*a` because it is borrowed
--> $DIR/borrowck-move-from-subpath-of-borrowed-path.rs:12:13
|
+LL | let a: Box<Box<_>> = Box::new(Box::new(2));
+ | - binding `a` declared here
LL | let b = &a;
| -- borrow of `a` occurs here
LL |
error[E0505]: cannot move out of `t0` because it is borrowed
--> $DIR/borrowck-move-mut-base-ptr.rs:10:14
|
+LL | fn foo(t0: &mut isize) {
+ | -- binding `t0` declared here
LL | let p: &isize = &*t0; // Freezes `*t0`
| ---- borrow of `*t0` occurs here
LL | let t1 = t0;
error[E0505]: cannot move out of `a.x` because it is borrowed
--> $DIR/borrowck-move-subcomponent.rs:15:14
|
+LL | let a : S = S { x : Box::new(1) };
+ | - binding `a` declared here
LL | let pb = &a;
| -- borrow of `a` occurs here
LL | let S { x: ax } = a;
error[E0505]: cannot move out of `x1` because it is borrowed
--> $DIR/borrowck-multiple-captures.rs:12:19
|
+LL | let x1: Box<_> = Box::new(1);
+ | -- binding `x1` declared here
LL | let p1 = &x1;
| --- borrow of `x1` occurs here
...
error[E0505]: cannot move out of `x2` because it is borrowed
--> $DIR/borrowck-multiple-captures.rs:12:19
|
+LL | let x2: Box<_> = Box::new(2);
+ | -- binding `x2` declared here
LL | let p2 = &x2;
| --- borrow of `x2` occurs here
LL | thread::spawn(move|| {
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrowck-multiple-captures.rs:38:19
|
+LL | let x: Box<_> = Box::new(1);
+ | - binding `x` declared here
LL | let p = &x;
| -- borrow of `x` occurs here
LL | thread::spawn(move|| {
--> $DIR/borrowck-overloaded-index-and-overloaded-deref.rs:31:5
|
LL | let i = &v[0].f;
- | - borrow of `v` occurs here
+ | - `v` is borrowed here
LL | v = MyVec { x: MyPtr { x: Foo { f: 23 } } };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `v` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `v` is assigned to here but it was already borrowed
LL |
LL | read(*i);
| -- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:71:5
|
LL | let p = &f.foo[&s];
- | ----- borrow of `f.foo` occurs here
+ | ----- `f.foo` is borrowed here
LL | f.foo = g;
- | ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
+ | ^^^^^^^^^ `f.foo` is assigned to here but it was already borrowed
LL | p.use_ref();
| ----------- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:77:5
|
LL | let p = &f.foo[&s];
- | ----- borrow of `*f` occurs here
+ | ----- `*f` is borrowed here
LL | *f = g;
- | ^^^^^^ assignment to borrowed `*f` occurs here
+ | ^^^^^^ `*f` is assigned to here but it was already borrowed
LL | p.use_ref();
| ----------- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:83:5
|
LL | let p = &mut f.foo[&s];
- | ----- borrow of `f.foo` occurs here
+ | ----- `f.foo` is borrowed here
LL | f.foo = g;
- | ^^^^^^^^^ assignment to borrowed `f.foo` occurs here
+ | ^^^^^^^^^ `f.foo` is assigned to here but it was already borrowed
LL | p.use_mut();
| ----------- borrow later used here
--> $DIR/borrowck-overloaded-index-autoderef.rs:89:5
|
LL | let p = &mut f.foo[&s];
- | ----- borrow of `*f` occurs here
+ | ----- `*f` is borrowed here
LL | *f = g;
- | ^^^^^^ assignment to borrowed `*f` occurs here
+ | ^^^^^^ `*f` is assigned to here but it was already borrowed
LL | p.use_mut();
| ----------- borrow later used here
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/borrowck-overloaded-index-move-index.rs:50:22
|
+LL | let mut s = "hello".to_string();
+ | ----- binding `s` declared here
LL | let rs = &mut s;
| ------ borrow of `s` occurs here
LL |
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/borrowck-overloaded-index-move-index.rs:53:7
|
+LL | let mut s = "hello".to_string();
+ | ----- binding `s` declared here
LL | let rs = &mut s;
| ------ borrow of `s` occurs here
...
--> $DIR/borrowck-pat-reassign-binding.rs:10:11
|
LL | Some(ref i) => {
- | ----- borrow of `x` occurs here
+ | ----- `x` is borrowed here
LL | // But on this branch, `i` is an outstanding borrow
LL | x = Some(*i+1);
- | ^^^^^^^^^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^^^^^^^^^ `x` is assigned to here but it was already borrowed
LL | drop(i);
| - borrow later used here
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/borrowck-unary-move.rs:3:10
|
+LL | fn foo(x: Box<isize>) -> isize {
+ | - binding `x` declared here
LL | let y = &*x;
| --- borrow of `*x` occurs here
LL | free(x);
--> $DIR/borrowck-union-borrow-nested.rs:24:21
|
LL | let ra = &mut u.s.a;
- | ---------- borrow of `u.s.a` occurs here
+ | ---------- `u.s.a` is borrowed here
LL | let b = u.c;
| ^^^ use of borrowed `u.s.a`
LL | ra.use_mut();
--> $DIR/borrowck-union-borrow.rs:28:13
|
LL | let ra = &u.a;
- | ---- borrow of `u.a` occurs here
+ | ---- `u.a` is borrowed here
LL | u.a = 1;
- | ^^^^^^^ assignment to borrowed `u.a` occurs here
+ | ^^^^^^^ `u.a` is assigned to here but it was already borrowed
LL | drop(ra);
| -- borrow later used here
--> $DIR/borrowck-union-borrow.rs:49:13
|
LL | let ra = &u.a;
- | ---- borrow of `u.b` occurs here
+ | ---- `u.b` is borrowed here
LL | u.b = 1;
- | ^^^^^^^ assignment to borrowed `u.b` occurs here
+ | ^^^^^^^ `u.b` is assigned to here but it was already borrowed
LL | drop(ra);
| -- borrow later used here
--> $DIR/borrowck-union-borrow.rs:60:21
|
LL | let ra = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | let a = u.a;
| ^^^ use of borrowed `u.a`
LL | drop(ra);
--> $DIR/borrowck-union-borrow.rs:70:13
|
LL | let rma = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | u.a = 1;
- | ^^^^^^^ assignment to borrowed `u.a` occurs here
+ | ^^^^^^^ `u.a` is assigned to here but it was already borrowed
LL | drop(rma);
| --- borrow later used here
--> $DIR/borrowck-union-borrow.rs:81:21
|
LL | let ra = &mut u.a;
- | -------- borrow of `u.a` occurs here
+ | -------- `u.a` is borrowed here
LL | let b = u.b;
| ^^^ use of borrowed `u.a`
LL |
--> $DIR/borrowck-union-borrow.rs:92:13
|
LL | let rma = &mut u.a;
- | -------- borrow of `u.b` occurs here
+ | -------- `u.b` is borrowed here
LL | u.b = 1;
- | ^^^^^^^ assignment to borrowed `u.b` occurs here
+ | ^^^^^^^ `u.b` is assigned to here but it was already borrowed
LL | drop(rma);
| --- borrow later used here
--> $DIR/borrowck-use-mut-borrow.rs:11:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(x);
| ^ use of borrowed `x`
LL | *p = 2;
--> $DIR/borrowck-use-mut-borrow.rs:18:10
|
LL | let p = &mut x.a;
- | -------- borrow of `x.a` occurs here
+ | -------- `x.a` is borrowed here
LL | drop(x);
| ^ use of borrowed `x.a`
LL | *p = 3;
--> $DIR/borrowck-use-mut-borrow.rs:25:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(x.a);
| ^^^ use of borrowed `x`
LL | p.a = 3;
--> $DIR/borrowck-use-mut-borrow.rs:32:10
|
LL | let p = &mut x.a;
- | -------- borrow of `x.a` occurs here
+ | -------- `x.a` is borrowed here
LL | drop(x.a);
| ^^^ use of borrowed `x.a`
LL | *p = 3;
--> $DIR/borrowck-use-mut-borrow.rs:39:13
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | let y = A { b: 3, .. x };
| ^^^^^^^^^^^^^^^^ use of borrowed `x`
LL | drop(y);
--> $DIR/borrowck-use-mut-borrow.rs:47:13
|
LL | let p = &mut x.a;
- | -------- borrow of `x.a` occurs here
+ | -------- `x.a` is borrowed here
LL | let y = A { b: 3, .. x };
| ^^^^^^^^^^^^^^^^ use of borrowed `x.a`
LL | drop(y);
--> $DIR/borrowck-use-mut-borrow.rs:55:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(*x);
| ^^ use of borrowed `x`
LL | **p = 2;
--> $DIR/borrowck-use-mut-borrow.rs:62:10
|
LL | let p = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | drop(*x.b);
| ^^^^ use of borrowed `x`
LL | p.a = 3;
--> $DIR/borrowck-use-mut-borrow.rs:69:10
|
LL | let p = &mut x.b;
- | -------- borrow of `x.b` occurs here
+ | -------- `x.b` is borrowed here
LL | drop(*x.b);
| ^^^^ use of borrowed `x.b`
LL | **p = 3;
--> $DIR/borrowck-vec-pattern-move-tail.rs:8:5
|
LL | [1, 2, ref tail @ ..] => tail,
- | -------- borrow of `a[_]` occurs here
+ | -------- `a[_]` is borrowed here
...
LL | a[2] = 0;
- | ^^^^^^^^ assignment to borrowed `a[_]` occurs here
+ | ^^^^^^^^ `a[_]` is assigned to here but it was already borrowed
LL | println!("t[0]: {}", t[0]);
| ---- borrow later used here
let mut vec = [Box::new(1), Box::new(2), Box::new(3)];
match vec {
[box ref _a, _, _] => {
- //~^ NOTE borrow of `vec[_]` occurs here
+ //~^ NOTE `vec[_]` is borrowed here
vec[0] = Box::new(4); //~ ERROR cannot assign
- //~^ NOTE assignment to borrowed `vec[_]` occurs here
+ //~^ NOTE `vec[_]` is assigned to here
_a.use_ref();
//~^ NOTE borrow later used here
}
let vec: &mut [Box<isize>] = &mut vec;
match vec {
&mut [ref _b @ ..] => {
- //~^ borrow of `vec[_]` occurs here
+ //~^ `vec[_]` is borrowed here
vec[0] = Box::new(4); //~ ERROR cannot assign
- //~^ NOTE assignment to borrowed `vec[_]` occurs here
+ //~^ NOTE `vec[_]` is assigned to here
_b.use_ref();
//~^ NOTE borrow later used here
}
--> $DIR/borrowck-vec-pattern-nesting.rs:9:13
|
LL | [box ref _a, _, _] => {
- | ------ borrow of `vec[_]` occurs here
+ | ------ `vec[_]` is borrowed here
LL |
LL | vec[0] = Box::new(4);
- | ^^^^^^ assignment to borrowed `vec[_]` occurs here
+ | ^^^^^^ `vec[_]` is assigned to here but it was already borrowed
LL |
LL | _a.use_ref();
| ------------ borrow later used here
--> $DIR/borrowck-vec-pattern-nesting.rs:23:13
|
LL | &mut [ref _b @ ..] => {
- | ------ borrow of `vec[_]` occurs here
+ | ------ `vec[_]` is borrowed here
LL |
LL | vec[0] = Box::new(4);
- | ^^^^^^ assignment to borrowed `vec[_]` occurs here
+ | ^^^^^^ `vec[_]` is assigned to here but it was already borrowed
LL |
LL | _b.use_ref();
| ------------ borrow later used here
| ^^^^^^^^^^^ use of borrowed `*self`
...
LL | let r = &mut *self;
- | ---------- borrow of `*self` occurs here
+ | ---------- `*self` is borrowed here
LL | r.get_size(width!(self))
| -------- ------------ in this macro invocation
| |
--> $DIR/issue-52713-bug.rs:12:5
|
LL | let y = &x;
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
...
LL | x += 1;
- | ^^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^^ `x` is assigned to here but it was already borrowed
LL | println!("{}", y);
| - borrow later used here
LL | let res = (|| (|| &greeting)())();
| -- -------- borrow occurs due to use in closure
| |
- | borrow of `greeting` occurs here
+ | `greeting` is borrowed here
LL |
LL | greeting = "DEALLOCATED".to_string();
- | ^^^^^^^^ assignment to borrowed `greeting` occurs here
+ | ^^^^^^^^ `greeting` is assigned to here but it was already borrowed
...
LL | println!("thread result: {:?}", res);
| --- borrow later used here
--> $DIR/issue-81365-1.rs:21:9
|
LL | let first = &self.target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-10.rs:21:9
|
LL | let first = &self.deref().target_field;
- | ------------ borrow of `self.container_field` occurs here
+ | ------------ `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
--> $DIR/issue-81365-11.rs:27:9
|
LL | let first = &mut self.target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
--> $DIR/issue-81365-2.rs:25:9
|
LL | let first = &self.container.target_field;
- | -------------- borrow of `self.container.container_field` occurs here
+ | -------------- `self.container.container_field` is borrowed here
LL | self.container.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-3.rs:32:9
|
LL | let first = &self.target_field;
- | ---- borrow of `self.container.container_field` occurs here
+ | ---- `self.container.container_field` is borrowed here
LL | self.container.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-4.rs:33:9
|
LL | let first = &self.target_field;
- | ---- borrow of `self.outer_field` occurs here
+ | ---- `self.outer_field` is borrowed here
LL | self.outer_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.outer_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^ `self.outer_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-5.rs:28:9
|
LL | let first = self.get();
- | ---------- borrow of `self.container_field` occurs here
+ | ---------- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-6.rs:18:9
|
LL | let first = &self[0];
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-7.rs:20:5
|
LL | let first = &c.target_field;
- | - borrow of `c.container_field` occurs here
+ | - `c.container_field` is borrowed here
LL | c.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `c.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ `c.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-8.rs:21:9
|
LL | let first = &(*self).target_field;
- | ------- borrow of `self.container_field` occurs here
+ | ------- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
|
--> $DIR/issue-81365-9.rs:21:9
|
LL | let first = &Deref::deref(self).target_field;
- | ---- borrow of `self.container_field` occurs here
+ | ---- `self.container_field` is borrowed here
LL | self.container_field = true;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `self.container_field` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self.container_field` is assigned to here but it was already borrowed
LL | first;
| ----- borrow later used here
--- /dev/null
+#![feature(no_core)]
+#![feature(lang_items)]
+
+#![no_core]
+
+#[cfg(target_os = "linux")]
+#[link(name = "c")]
+extern {}
+
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ //~^ ERROR: incorrect number of parameters for the `start` lang item
+ 40+2
+}
+
+#[lang = "sized"]
+pub trait Sized {}
+#[lang = "copy"]
+pub trait Copy {}
+
+#[lang = "drop_in_place"]
+#[allow(unconditional_recursion)]
+pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ drop_in_place(to_drop)
+}
+
+#[lang = "add"]
+trait Add<RHS> {
+ type Output;
+ fn add(self, other: RHS) -> Self::Output;
+}
+
+impl Add<isize> for isize {
+ type Output = isize;
+ fn add(self, other: isize) -> isize {
+ self + other
+ }
+}
+
+fn main() {}
--- /dev/null
+error: incorrect number of parameters for the `start` lang item
+ --> $DIR/issue-92157.rs:11:1
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `start` lang item should have four parameters, but found 3
+ = note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
+
+error: aborting due to previous error
+
--> $DIR/two-phase-allow-access-during-reservation.rs:26:19
|
LL | /*1*/ let p = &mut i; // (reservation of `i` starts here)
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
LL |
LL | /*2*/ let j = i; // OK: `i` is only reserved here
| ^ use of borrowed `i`
--> $DIR/two-phase-allow-access-during-reservation.rs:31:19
|
LL | /*1*/ let p = &mut i; // (reservation of `i` starts here)
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
...
LL | /*4*/ let k = i;
| ^ use of borrowed `i`
--> $DIR/two-phase-surprise-no-conflict.rs:21:23
|
LL | let _mut_borrow = &mut *self;
- | ---------- borrow of `*self` occurs here
+ | ---------- `*self` is borrowed here
LL | let _access = self.cx;
| ^^^^^^^ use of borrowed `*self`
LL |
error[E0505]: cannot move out of `alloc` because it is borrowed
--> $DIR/leak-alloc.rs:26:10
|
+LL | let alloc = Alloc {};
+ | ----- binding `alloc` declared here
LL | let boxed = Box::new_in(10, alloc.by_ref());
| -------------- borrow of `alloc` occurs here
LL | let theref = Box::leak(boxed);
error[E0505]: cannot move out of `s` because it is borrowed
--> $DIR/btreemap_dropck.rs:15:10
|
+LL | let s = String::from("Hello World!");
+ | - binding `s` declared here
LL | let _map = BTreeMap::from_iter([((), PrintOnDrop(&s))]);
| -- borrow of `s` occurs here
LL | drop(s);
error[E0277]: `T` cannot be sent between threads safely
- --> $DIR/builtin-superkinds-double-superkind.rs:6:24
+ --> $DIR/builtin-superkinds-double-superkind.rs:6:32
|
LL | impl <T: Sync+'static> Foo for (T,) { }
- | ^^^ `T` cannot be sent between threads safely
+ | ^^^^ `T` cannot be sent between threads safely
|
= note: required because it appears within the type `(T,)`
note: required by a bound in `Foo`
| +++++++++++++++++++
error[E0277]: `T` cannot be shared between threads safely
- --> $DIR/builtin-superkinds-double-superkind.rs:9:16
+ --> $DIR/builtin-superkinds-double-superkind.rs:9:24
|
LL | impl <T: Send> Foo for (T,T) { }
- | ^^^ `T` cannot be shared between threads safely
+ | ^^^^^ `T` cannot be shared between threads safely
|
= note: required because it appears within the type `(T, T)`
note: required by a bound in `Foo`
error[E0277]: `T` cannot be sent between threads safely
- --> $DIR/builtin-superkinds-in-metadata.rs:13:23
+ --> $DIR/builtin-superkinds-in-metadata.rs:13:56
|
LL | impl <T:Sync+'static> RequiresRequiresShareAndSend for X<T> { }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `T` cannot be sent between threads safely
+ | ^^^^ `T` cannot be sent between threads safely
|
note: required because it appears within the type `X<T>`
--> $DIR/builtin-superkinds-in-metadata.rs:9:8
error[E0277]: `Rc<i8>` cannot be sent between threads safely
- --> $DIR/builtin-superkinds-simple.rs:6:6
+ --> $DIR/builtin-superkinds-simple.rs:6:14
|
LL | impl Foo for std::rc::Rc<i8> { }
- | ^^^ `Rc<i8>` cannot be sent between threads safely
+ | ^^^^^^^^^^^^^^^ `Rc<i8>` cannot be sent between threads safely
|
= help: the trait `Send` is not implemented for `Rc<i8>`
note: required by a bound in `Foo`
error[E0277]: `T` cannot be sent between threads safely
- --> $DIR/builtin-superkinds-typaram-not-send.rs:5:24
+ --> $DIR/builtin-superkinds-typaram-not-send.rs:5:32
|
LL | impl <T: Sync+'static> Foo for T { }
- | ^^^ `T` cannot be sent between threads safely
+ | ^ `T` cannot be sent between threads safely
|
note: required by a bound in `Foo`
--> $DIR/builtin-superkinds-typaram-not-send.rs:3:13
-#![feature(abi_efiapi)]
-
fn efiapi(f: extern "efiapi" fn(usize, ...)) {
//~^ ERROR: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
//~^^ ERROR: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
- --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:1:14
|
LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
- --> $DIR/feature-gate-extended_varargs_abi_support.rs:3:14
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:1:14
|
LL | fn efiapi(f: extern "efiapi" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
- --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:6:12
|
LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
- --> $DIR/feature-gate-extended_varargs_abi_support.rs:8:12
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:6:12
|
LL | fn sysv(f: extern "sysv64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
error[E0658]: using calling conventions other than `C` or `cdecl` for varargs functions is unstable
- --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11
|
LL | fn win(f: extern "win64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: add `#![feature(extended_varargs_abi_support)]` to the crate attributes to enable
error[E0045]: C-variadic function must have a compatible calling convention, like `C` or `cdecl`
- --> $DIR/feature-gate-extended_varargs_abi_support.rs:13:11
+ --> $DIR/feature-gate-extended_varargs_abi_support.rs:11:11
|
LL | fn win(f: extern "win64" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
// ignore-arm stdcall isn't supported
#![feature(extended_varargs_abi_support)]
-#![feature(abi_efiapi)]
fn baz(f: extern "stdcall" fn(usize, ...)) {
//~^ ERROR: C-variadic function must have a compatible calling convention,
error[E0045]: C-variadic function must have a compatible calling convention, like `C`, `cdecl`, `win64`, `sysv64` or `efiapi`
- --> $DIR/variadic-ffi-2.rs:5:11
+ --> $DIR/variadic-ffi-2.rs:4:11
|
LL | fn baz(f: extern "stdcall" fn(usize, ...)) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ C-variadic function must have a compatible calling convention
--> $DIR/variadic-ffi-4.rs:28:11
|
LL | pub unsafe extern "C" fn no_escape4(_: usize, mut ap0: &mut VaListImpl, mut ap1: ...) {
- | - let's call the lifetime of this reference `'3`
+ | - ------- binding `ap1` declared here
+ | |
+ | let's call the lifetime of this reference `'3`
LL | ap0 = &mut ap1;
| ------^^^^^^^^
| | |
let u = 5 as bool; //~ ERROR cannot cast as `bool`
//~| HELP compare with zero instead
//~| SUGGESTION 5 != 0
+
let t = (1 + 2) as bool; //~ ERROR cannot cast as `bool`
//~| HELP compare with zero instead
//~| SUGGESTION (1 + 2) != 0
- let v = "hello" as bool; //~ ERROR casting `&'static str` as `bool` is invalid
+
+ let v = "hello" as bool;
+ //~^ ERROR casting `&'static str` as `bool` is invalid
+ //~| HELP consider using the `is_empty` method on `&'static str` to determine if it contains anything
}
| ^^^^^^^^^ help: compare with zero instead: `5 != 0`
error[E0054]: cannot cast as `bool`
- --> $DIR/cast-as-bool.rs:5:13
+ --> $DIR/cast-as-bool.rs:6:13
|
LL | let t = (1 + 2) as bool;
| ^^^^^^^^^^^^^^^ help: compare with zero instead: `(1 + 2) != 0`
error[E0606]: casting `&'static str` as `bool` is invalid
- --> $DIR/cast-as-bool.rs:8:13
+ --> $DIR/cast-as-bool.rs:10:13
|
LL | let v = "hello" as bool;
| ^^^^^^^^^^^^^^^
+ |
+help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
+ |
+LL | let v = !"hello".is_empty();
+ | + ~~~~~~~~~~~
error: aborting due to 3 previous errors
--- /dev/null
+use std::ops::Deref;
+
+struct Foo;
+
+impl Deref for Foo {
+ type Target = [u8];
+
+ fn deref(&self) -> &Self::Target {
+ &[]
+ }
+}
+
+fn main() {
+ let _ = "foo" as bool;
+ //~^ ERROR casting `&'static str` as `bool` is invalid [E0606]
+
+ let _ = String::from("foo") as bool;
+ //~^ ERROR non-primitive cast: `String` as `bool` [E0605]
+
+ let _ = Foo as bool;
+ //~^ ERROR non-primitive cast: `Foo` as `bool` [E0605]
+}
+
+fn _slice(bar: &[i32]) -> bool {
+ bar as bool
+ //~^ ERROR casting `&[i32]` as `bool` is invalid [E0606]
+}
--- /dev/null
+error[E0606]: casting `&'static str` as `bool` is invalid
+ --> $DIR/issue-106883-is-empty.rs:14:13
+ |
+LL | let _ = "foo" as bool;
+ | ^^^^^^^^^^^^^
+ |
+help: consider using the `is_empty` method on `&'static str` to determine if it contains anything
+ |
+LL | let _ = !"foo".is_empty();
+ | + ~~~~~~~~~~~
+
+error[E0605]: non-primitive cast: `String` as `bool`
+ --> $DIR/issue-106883-is-empty.rs:17:13
+ |
+LL | let _ = String::from("foo") as bool;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+ |
+note: this expression `Deref`s to `str` which implements `is_empty`
+ --> $DIR/issue-106883-is-empty.rs:17:13
+ |
+LL | let _ = String::from("foo") as bool;
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider using the `is_empty` method on `String` to determine if it contains anything
+ |
+LL | let _ = !String::from("foo").is_empty();
+ | + ~~~~~~~~~~~
+
+error[E0605]: non-primitive cast: `Foo` as `bool`
+ --> $DIR/issue-106883-is-empty.rs:20:13
+ |
+LL | let _ = Foo as bool;
+ | ^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+ |
+note: this expression `Deref`s to `[u8]` which implements `is_empty`
+ --> $DIR/issue-106883-is-empty.rs:20:13
+ |
+LL | let _ = Foo as bool;
+ | ^^^
+help: consider using the `is_empty` method on `Foo` to determine if it contains anything
+ |
+LL | let _ = !Foo.is_empty();
+ | + ~~~~~~~~~~~
+
+error[E0606]: casting `&[i32]` as `bool` is invalid
+ --> $DIR/issue-106883-is-empty.rs:25:5
+ |
+LL | bar as bool
+ | ^^^^^^^^^^^
+ |
+help: consider using the `is_empty` method on `&[i32]` to determine if it contains anything
+ |
+LL | !bar.is_empty()
+ | + ~~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0605, E0606.
+For more information about an error, try `rustc --explain E0605`.
-// check-fail
-// known-bug
+// edition:2021
+// known-bug: unknown
// unset-rustc-env:RUST_BACKTRACE
-// compile-flags:-Z trait-solver=chalk --edition=2021
-// error-pattern:stack backtrace:
+// compile-flags:-Z trait-solver=chalk
+// error-pattern:internal compiler error
// failure-status:101
-// normalize-stderr-test "note: .*" -> ""
-// normalize-stderr-test "thread 'rustc' .*" -> ""
-// normalize-stderr-test " .*\n" -> ""
// normalize-stderr-test "DefId([^)]*)" -> "..."
+// normalize-stderr-test "\nerror: internal compiler error.*\n\n" -> ""
+// normalize-stderr-test "note:.*unexpectedly panicked.*\n\n" -> ""
+// normalize-stderr-test "note: we would appreciate a bug report.*\n\n" -> ""
+// normalize-stderr-test "note: compiler flags.*\n\n" -> ""
+// normalize-stderr-test "note: rustc.*running on.*\n\n" -> ""
+// normalize-stderr-test "thread.*panicked.*\n" -> ""
+// normalize-stderr-test "stack backtrace:\n" -> ""
+// normalize-stderr-test "\s\d{1,}: .*\n" -> ""
+// normalize-stderr-test "\s at .*\n" -> ""
+// normalize-stderr-test ".*note: Some details.*\n" -> ""
+// normalize-stderr-test "\n\n[ ]*\n" -> ""
+// normalize-stderr-test "compiler/.*: projection" -> "projection"
fn main() -> () {}
-error[E0277]: `[async fn body@$DIR/async.rs:14:29: 16:2]` is not a future
-LL |LL | |LL | | }
-
-
-error[E0277]: the size for values of type `<[async fn body@$DIR/async.rs:14:29: 16:2] as Future>::Output` cannot be known at compilation time
-LL |LL | |LL | | }
-
-
-error[E0277]: `[async fn body@$DIR/async.rs:14:29: 16:2]` is not a future
+error[E0277]: `[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future
+ --> $DIR/async.rs:23:29
+ |
+LL | async fn foo(x: u32) -> u32 {
+ | _____________________________-
+LL | | x
+LL | | }
+ | | ^
+ | | |
+ | |_`[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:23:29: 25:2]`
+ = note: [async fn body@$DIR/async.rs:23:29: 25:2] must be a future or must implement `IntoFuture` to be awaited
+note: required by a bound in `identity_future`
+ --> $SRC_DIR/core/src/future/mod.rs:LL:COL
+
+error[E0277]: the size for values of type `<[async fn body@$DIR/async.rs:23:29: 25:2] as Future>::Output` cannot be known at compilation time
+ --> $DIR/async.rs:23:29
+ |
+LL | async fn foo(x: u32) -> u32 {
+ | _____________________________^
+LL | | x
+LL | | }
+ | |_^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `<[async fn body@$DIR/async.rs:23:29: 25:2] as Future>::Output`
+note: required by a bound in `identity_future`
+ --> $SRC_DIR/core/src/future/mod.rs:LL:COL
+
+error[E0277]: `[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future
+ --> $DIR/async.rs:23:25
+ |
LL | async fn foo(x: u32) -> u32 {
-
-error: internal compiler error: compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs:1114:25: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:14:29: 16:2]], def_id: ...), _use_mk_alias_ty_instead: () }, Term::Ty(u32)), []), depth=0)`
+ | ^^^ `[async fn body@$DIR/async.rs:23:29: 25:2]` is not a future
+ |
+ = help: the trait `Future` is not implemented for `[async fn body@$DIR/async.rs:23:29: 25:2]`
+ = note: [async fn body@$DIR/async.rs:23:29: 25:2] must be a future or must implement `IntoFuture` to be awaited
+
+error: internal compiler error: projection clauses should be implied from elsewhere. obligation: `Obligation(predicate=Binder(ProjectionPredicate(AliasTy { substs: [[async fn body@$DIR/async.rs:23:29: 25:2]], def_id: ...) }, Term::Ty(u32)), []), depth=0)`
+ --> $DIR/async.rs:23:25
+ |
LL | async fn foo(x: u32) -> u32 {
-
-
-stack backtrace:
-
-
-
-
-
-
-
-
-
-query stack during panic:
+ | ^^^query stack during panic:
#0 [typeck] type-checking `foo`
#1 [thir_body] building THIR for `foo`
#2 [mir_built] building MIR for `foo`
error[E0277]: the size for values of type `str` cannot be known at compilation time
- --> $DIR/impl_wf.rs:11:6
+ --> $DIR/impl_wf.rs:11:14
|
LL | impl Foo for str { }
- | ^^^ doesn't have a size known at compile-time
+ | ^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `str`
note: required by a bound in `Foo`
| ^^^^^ required by this bound in `Foo`
error[E0277]: the trait bound `f32: Foo` is not satisfied
- --> $DIR/impl_wf.rs:22:6
+ --> $DIR/impl_wf.rs:22:19
|
LL | impl Baz<f32> for f32 { }
- | ^^^^^^^^ the trait `Foo` is not implemented for `f32`
+ | ^^^ the trait `Foo` is not implemented for `f32`
|
= help: the trait `Foo` is implemented for `i32`
note: required by a bound in `Baz`
| ^^^^^^^^^^^
|
= note: calls in statics are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` crate: https://crates.io/crates/once_cell
error[E0010]: allocations are not allowed in statics
--> $DIR/arrays.rs:14:5
|
LL | let mut c = || {
- | -- borrow of `arr` occurs here
+ | -- `arr` is borrowed here
LL | arr[0] += 10;
| --- borrow occurs due to use of `arr` in closure
...
--> $DIR/arrays.rs:14:5
|
LL | let mut c = || {
- | -- borrow of `arr` occurs here
+ | -- `arr` is borrowed here
LL | arr[0] += 10;
| --- borrow occurs due to use of `arr` in closure
...
--> $DIR/arrays.rs:29:5
|
LL | let c = || {
- | -- borrow of `arr[_]` occurs here
+ | -- `arr[_]` is borrowed here
LL | println!("{:#?}", &arr[3..4]);
| --- borrow occurs due to use in closure
...
LL | arr[1] += 10;
- | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+ | ^^^^^^^^^^^^ `arr[_]` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
--> $DIR/arrays.rs:43:5
|
LL | let c = || {
- | -- borrow of `arr[_]` occurs here
+ | -- `arr[_]` is borrowed here
LL | println!("{}", arr[3]);
| --- borrow occurs due to use in closure
...
LL | arr[1] += 10;
- | ^^^^^^^^^^^^ assignment to borrowed `arr[_]` occurs here
+ | ^^^^^^^^^^^^ `arr[_]` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
--> $DIR/arrays.rs:57:20
|
LL | let mut c = || {
- | -- borrow of `arr` occurs here
+ | -- `arr` is borrowed here
LL | arr[1] += 10;
| --- borrow occurs due to use of `arr` in closure
...
--> $DIR/box.rs:21:5
|
LL | let mut c = || {
- | -- borrow of `e.0.0.m.x` occurs here
+ | -- `e.0.0.m.x` is borrowed here
LL | e.0.0.m.x = format!("not-x");
| --------- borrow occurs due to use in closure
...
LL | e.0.0.m.x = format!("not-x");
- | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+ | ^^^^^^^^^ `e.0.0.m.x` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
--> $DIR/box.rs:55:5
|
LL | let c = || {
- | -- borrow of `e.0.0.m.x` occurs here
+ | -- `e.0.0.m.x` is borrowed here
LL | println!("{}", e.0.0.m.x);
| --------- borrow occurs due to use in closure
...
LL | e.0.0.m.x = format!("not-x");
- | ^^^^^^^^^ assignment to borrowed `e.0.0.m.x` occurs here
+ | ^^^^^^^^^ `e.0.0.m.x` is assigned to here but it was already borrowed
LL |
LL | c();
| - borrow later used here
fn main() {
let mut a = A { y: 1 };
let mut c = || {
- //~^ borrow of `a.y` occurs here
+ //~^ `a.y` is borrowed here
let _ = unsafe { &a.y };
let _ = &mut a;
//~^ borrow occurs due to use in closure
};
a.y = 1;
//~^ cannot assign to `a.y` because it is borrowed [E0506]
- //~| assignment to borrowed `a.y` occurs here
+ //~| `a.y` is assigned to here
c();
//~^ borrow later used here
}
--> $DIR/union.rs:20:5
|
LL | let mut c = || {
- | -- borrow of `a.y` occurs here
+ | -- `a.y` is borrowed here
...
LL | let _ = &mut a;
| - borrow occurs due to use in closure
...
LL | a.y = 1;
- | ^^^^^^^ assignment to borrowed `a.y` occurs here
+ | ^^^^^^^ `a.y` is assigned to here but it was already borrowed
...
LL | c();
| - borrow later used here
| |
| arguments to this struct are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-84128.rs:13:9
+ |
+LL | Foo(())
+ | ^^^^--^
+ | |
+ | this argument influences the type of `Foo`
note: tuple struct defined here
--> $DIR/issue-84128.rs:5:8
|
| |
| arguments to this enum variant are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-87461.rs:10:5
+ |
+LL | Ok(())
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
| |
| arguments to this enum variant are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-87461.rs:17:5
+ |
+LL | Ok(())
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
| |
| arguments to this enum variant are incorrect
|
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-87461.rs:26:9
+ |
+LL | Ok(())
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
--> $DIR/multiple-fn-bounds.rs:10:5
|
LL | foo(move |x| v);
- | ^^^ --------
- | | | |
- | | | help: do not borrow the argument: `char`
- | | found signature defined here
+ | ^^^ -------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `fn(char) -> _`
|
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
| ^^^^^^^^^^^^^^^^ required by this bound in `foo`
+help: do not borrow the argument
+ |
+LL | foo(move |char| v);
+ | ~~~~
error: aborting due to previous error
--> $DIR/coerce-overloaded-autoderef-fail.rs:17:5
|
LL | let y = borrow(x);
- | - borrow of `**x` occurs here
+ | - `**x` is borrowed here
LL | let z = borrow(x);
LL | **x += 1;
- | ^^^^^^^^ assignment to borrowed `**x` occurs here
+ | ^^^^^^^^ `**x` is assigned to here but it was already borrowed
LL |
LL | drop((y, z));
| - borrow later used here
error[E0283]: type annotations needed: cannot satisfy `u32: C`
- --> $DIR/coherence-overlap-trait-alias.rs:15:6
+ --> $DIR/coherence-overlap-trait-alias.rs:15:12
|
LL | impl C for u32 {}
- | ^
+ | ^^^
|
note: multiple `impl`s satisfying `u32: C` found
--> $DIR/coherence-overlap-trait-alias.rs:14:1
// 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
--> $DIR/array-size-in-generic-struct-param.rs:19:15
|
LL | arr: [u8; CFG.arr_size],
- | ^^^^^^^^^^^^ field access is not supported in generic constant
+ | ^^^^^^^^^^^^ field access is not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+pub struct Foo<const N: usize>;
+
+pub fn foo<const N: usize>() -> Foo<{ N + 1 }> {
+ Foo
+}
--- /dev/null
+#![feature(inline_const, generic_const_exprs)]
+//~^ WARN the feature `generic_const_exprs` is incomplete
+
+fn foo<T>() {
+ let _ = [0u8; const { std::mem::size_of::<T>() }];
+ //~^ ERROR: overly complex generic constant
+}
+
+fn main() {
+ foo::<i32>();
+}
--- /dev/null
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/const-block-is-poly.rs:1:26
+ |
+LL | #![feature(inline_const, generic_const_exprs)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: overly complex generic constant
+ --> $DIR/const-block-is-poly.rs:5:19
+ |
+LL | let _ = [0u8; const { std::mem::size_of::<T>() }];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ const blocks are not supported in generic constants
+ |
+ = help: consider moving this anonymous constant into a `const` function
+ = note: this operation may be supported in the future
+
+error: aborting due to previous error; 1 warning emitted
+
| arguments to this struct are incorrect
|
= note: expected array `[u32; X]`
- found array `[u32; _]`
+ found array `[u32; Self::SIZE]`
note: tuple struct defined here
--> $DIR/issue-62504.rs:14:8
|
--> $DIR/issue-79518-default_trait_method_normalization.rs:16:32
|
LL | Self::AssocInstance == [(); std::mem::size_of::<Self::Assoc>()];
- | ------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found array `[(); _]`
+ | ------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found array `[(); std::mem::size_of::<Self::Assoc>()]`
| |
| expected because this is `<Self as Foo>::Assoc`
|
= note: expected associated type `<Self as Foo>::Assoc`
- found array `[(); _]`
- = help: consider constraining the associated type `<Self as Foo>::Assoc` to `[(); _]` or calling a method that returns `<Self as Foo>::Assoc`
+ found array `[(); std::mem::size_of::<Self::Assoc>()]`
+ = help: consider constraining the associated type `<Self as Foo>::Assoc` to `[(); std::mem::size_of::<Self::Assoc>()]` or calling a method that returns `<Self as Foo>::Assoc`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
error: aborting due to previous error
--> $DIR/let-bindings.rs:6:68
|
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
- | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--> $DIR/let-bindings.rs:6:35
|
LL | fn test<const N: usize>() -> [u8; { let x = N; N + 1 }] where [u8; { let x = N; N + 1 }]: Default {
- | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+// aux-build:anon_const_non_local.rs
+
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+extern crate anon_const_non_local;
+
+fn bar<const M: usize>()
+where
+ [(); M + 1]:,
+{
+ let _: anon_const_non_local::Foo<2> = anon_const_non_local::foo::<M>();
+ //~^ ERROR: mismatched types
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/non_local_anon_const_diagnostics.rs:12:43
+ |
+LL | let _: anon_const_non_local::Foo<2> = anon_const_non_local::foo::<M>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `anon_const_non_local::::foo::{constant#0}`
+ |
+ = note: expected constant `2`
+ found constant `anon_const_non_local::::foo::{constant#0}`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--> $DIR/unused_expr.rs:4:34
|
LL | fn add<const N: usize>() -> [u8; { N + 1; 5 }] {
- | ^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--> $DIR/unused_expr.rs:9:34
|
LL | fn div<const N: usize>() -> [u8; { N / 1; 5 }] {
- | ^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--> $DIR/unused_expr.rs:16:38
|
LL | fn fn_call<const N: usize>() -> [u8; { foo(N); 5 }] {
- | ^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+// check-pass
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+#[derive(Clone)]
+struct Bar<const A: usize, const B: usize>
+where
+ [(); A as usize]:,
+ [(); B as usize]:,
+{}
+
+fn main() {}
|
= note: closures need an RFC before allowed to be called in constants
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
LL | fn foo<const N: usize, const A: [u8; N]>() {}
| ^ the type must not depend on the parameter `N`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; N]` is forbidden as the type of a const generic parameter
--> $DIR/issue-62878.rs:5:33
|
LL | fn foo<const N: usize, const A: [u8; N]>() {}
fn foo<const N: usize, const A: [u8; N]>() {}
//~^ ERROR the type of const parameters must not
-//[min]~| ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~| ERROR `[u8; N]` is forbidden as the type of a const generic parameter
fn main() {
foo::<_, { [1] }>();
LL | |
LL | | 0
LL | | }],
- | |_____^ blocks are not supported in generic constant
+ | |_____^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
LL | |
LL | | 0
LL | | }],
- | |_____^ blocks are not supported in generic constant
+ | |_____^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
LL | |
LL | | 0
LL | | }],
- | |_____^ blocks are not supported in generic constant
+ | |_____^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
LL | fn foo<const LEN: usize, const DATA: [u8; LEN]>() {}
| ^^^ the type must not depend on the parameter `LEN`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; LEN]` is forbidden as the type of a const generic parameter
--> $DIR/issue-71169.rs:5:38
|
LL | fn foo<const LEN: usize, const DATA: [u8; LEN]>() {}
fn foo<const LEN: usize, const DATA: [u8; LEN]>() {}
//~^ ERROR the type of const parameters must not
-//[min]~^^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^^ ERROR `[u8; LEN]` is forbidden as the type of a const generic parameter
fn main() {
const DATA: [u8; 4] = *b"ABCD";
foo::<4, DATA>();
-error: `[u32; _]` is forbidden as the type of a const generic parameter
+error: `[u32; LEN]` is forbidden as the type of a const generic parameter
--> $DIR/issue-73491.rs:8:19
|
LL | fn hoge<const IN: [u32; LEN]>() {}
const LEN: usize = 1024;
fn hoge<const IN: [u32; LEN]>() {}
-//[min]~^ ERROR `[u32; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u32; LEN]` is forbidden as the type of a const generic parameter
fn main() {}
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
--> $DIR/issue-74101.rs:6:18
|
LL | fn test<const N: [u8; 1 + 2]>() {}
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]`
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
--> $DIR/issue-74101.rs:9:21
|
LL | struct Foo<const N: [u8; 1 + 2]>;
#![cfg_attr(full, allow(incomplete_features))]
fn test<const N: [u8; 1 + 2]>() {}
-//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
struct Foo<const N: [u8; 1 + 2]>;
-//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u8; 1 + 2]` is forbidden as the type of a const generic parameter
fn main() {}
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; Bar::<u32>::value()]` is forbidden as the type of a const generic parameter
--> $DIR/issue-75047.rs:14:21
|
LL | struct Foo<const N: [u8; Bar::<u32>::value()]>;
}
struct Foo<const N: [u8; Bar::<u32>::value()]>;
-//[min]~^ ERROR `[u8; _]` is forbidden as the type of a const generic parameter
+//[min]~^ ERROR `[u8; Bar::<u32>::value()]` is forbidden as the type of a const generic parameter
fn main() {}
--> $DIR/issue-77357.rs:6:46
|
LL | fn bug<'a, T>() -> &'static dyn MyTrait<[(); { |x: &'a u32| { x }; 4 }]> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constant
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ blocks are not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
// check-pass
-// known-bug
+// known-bug: unknown
// This should not compile, as the compiler should not know
// `A - 0` is satisfied `?x - 0` if `?x` is inferred to `A`.
impl<'a> Ref<'a> {
pub fn foo<const A: usize>() -> [(); A - 0] {
- //~^ WARN function cannot
Self::foo()
}
}
|
LL | pub fn foo<const A: usize>() -> [(); A - 0] {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
-LL |
LL | Self::foo()
| ----------- recursive call site
|
fn consume<T: 'static>(_val: T)
where
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- //~^ ERROR: can't compare
+ //~^ overly complex generic constant
{
}
fn test<T: 'static>()
where
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- //~^ ERROR: can't compare
+ //~^ overly complex generic constant
{
}
-error[E0277]: can't compare `TypeId` with `_` in const contexts
- --> $DIR/issue-90318.rs:14:28
+error: overly complex generic constant
+ --> $DIR/issue-90318.rs:14:8
|
LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^ no implementation for `TypeId == _`
+ | ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | borrowing is not supported in generic constants
|
- = help: the trait `~const PartialEq<_>` is not implemented for `TypeId`
-note: the trait `PartialEq<_>` is implemented for `TypeId`, but that implementation is not `const`
- --> $DIR/issue-90318.rs:14:28
- |
-LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^
+ = help: consider moving this anonymous constant into a `const` function
+ = note: this operation may be supported in the future
-error[E0277]: can't compare `TypeId` with `_` in const contexts
- --> $DIR/issue-90318.rs:21:28
+error: overly complex generic constant
+ --> $DIR/issue-90318.rs:21:8
|
LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^ no implementation for `TypeId == _`
+ | ^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | borrowing is not supported in generic constants
|
- = help: the trait `~const PartialEq<_>` is not implemented for `TypeId`
-note: the trait `PartialEq<_>` is implemented for `TypeId`, but that implementation is not `const`
- --> $DIR/issue-90318.rs:21:28
- |
-LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
- | ^^
+ = help: consider moving this anonymous constant into a `const` function
+ = note: this operation may be supported in the future
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0277`.
-error: `[u8; _]` is forbidden as the type of a const generic parameter
+error: `[u8; {
+ struct Foo<const N: usize>;
+
+ impl<const N: usize> Foo<N> {
+ fn value() -> usize {
+ N
+ }
+ }
+
+ Foo::<17>::value()
+ }]` is forbidden as the type of a const generic parameter
--> $DIR/nested-type.rs:6:21
|
LL | struct Foo<const N: [u8; {
// run-rustfix
#![warn(unused_braces)]
+macro_rules! make_1 {
+ () => {
+ 1
+ }
+}
+
struct A<const N: usize>;
fn main() {
let _: A<7>; // ok
let _: A<7>; //~ WARN unnecessary braces
let _: A<{ 3 + 5 }>; // ok
+ let _: A<{make_1!()}>; // ok
}
// run-rustfix
#![warn(unused_braces)]
+macro_rules! make_1 {
+ () => {
+ 1
+ }
+}
+
struct A<const N: usize>;
fn main() {
let _: A<7>; // ok
let _: A<{ 7 }>; //~ WARN unnecessary braces
let _: A<{ 3 + 5 }>; // ok
+ let _: A<{make_1!()}>; // ok
}
warning: unnecessary braces around const expression
- --> $DIR/unused_braces.rs:9:14
+ --> $DIR/unused_braces.rs:15:14
|
LL | let _: A<{ 7 }>;
| ^^ ^^
--- /dev/null
+#![crate_type = "lib"]
+#![feature(const_closures, const_trait_impl)]
+#![allow(incomplete_features)]
+
+pub const fn test() {
+ let cl = const || {};
+ cl();
+}
--- /dev/null
+// aux-build:closure-in-foreign-crate.rs
+// build-pass
+
+extern crate closure_in_foreign_crate;
+
+const _: () = closure_in_foreign_crate::test();
+
+fn main() {}
note: impl defined here, but it is not `const`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error[E0658]: mutable references are not allowed in constant functions
--> $DIR/const-fn-error.rs:5:14
| ^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to 4 previous errors
note: impl defined here, but it is not `const`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error[E0015]: cannot call non-const fn `<std::ops::Range<i32> as Iterator>::next` in constants
--> $DIR/const-for.rs:5:14
| ^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to 2 previous errors
|
LL | A = { if let 0 = 0 { todo!() } 0 },
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | A = { let _0 = 0; 0 },
+ | +
error: aborting due to previous error
|
LL | let x: [i32; { if let 0 = 0 { todo!() } 0 }] = [];
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | let x: [i32; { let _0 = 0; 0 }] = [];
+ | +
error: aborting due to previous error
|
LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | const X: i32 = { let _0 = 0; 0 };
+ | +
error[E0005]: refutable pattern in local binding
--> $DIR/const-match-check.rs:8:23
|
LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | static Y: i32 = { let _0 = 0; 0 };
+ | +
error[E0005]: refutable pattern in local binding
--> $DIR/const-match-check.rs:13:26
|
LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | const X: i32 = { let _0 = 0; 0 };
+ | +
error[E0005]: refutable pattern in local binding
--> $DIR/const-match-check.rs:19:26
|
LL | const X: i32 = { if let 0 = 0 { todo!() } 0 };
| ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | const X: i32 = { let _0 = 0; 0 };
+ | +
error: aborting due to 4 previous errors
LL | bytes: [u8; std::mem::size_of::<Foo>()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
- = note: ...which requires computing layout of `[u8; _]`...
- = note: ...which requires normalizing `[u8; _]`...
+ = note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
+ = note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
= note: ...which again requires evaluating type-level constant, completing the cycle
note: cycle used when checking that `Foo` is well-formed
--> $DIR/const-size_of-cycle.rs:3:1
--- /dev/null
+// run-pass
+#![feature(const_type_id)]
+#![feature(const_trait_impl)]
+
+use std::any::TypeId;
+
+const fn main() {
+ assert!(TypeId::of::<u8>() == TypeId::of::<u8>());
+ assert!(TypeId::of::<()>() != TypeId::of::<u8>());
+ const _A: bool = TypeId::of::<u8>() < TypeId::of::<u16>();
+ // can't assert `_A` because it is not deterministic
+}
--- /dev/null
+struct Foo<T, const N: usize> {
+ array: [T; N],
+}
+
+trait Bar<const N: usize> {}
+
+impl<T, const N: usize> Foo<T, N> {
+ fn trigger(self) {
+ self.unsatisfied()
+ //~^ ERROR the trait bound `T: Bar<N>` is not satisfied
+ }
+
+ fn unsatisfied(self)
+ where
+ T: Bar<N>,
+ {
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `T: Bar<N>` is not satisfied
+ --> $DIR/ct-var-in-collect_all_mismatches.rs:9:14
+ |
+LL | self.unsatisfied()
+ | ^^^^^^^^^^^ the trait `Bar<N>` is not implemented for `T`
+ |
+note: required by a bound in `Foo::<T, N>::unsatisfied`
+ --> $DIR/ct-var-in-collect_all_mismatches.rs:15:12
+ |
+LL | fn unsatisfied(self)
+ | ----------- required by a bound in this
+LL | where
+LL | T: Bar<N>,
+ | ^^^^^^ required by this bound in `Foo::<T, N>::unsatisfied`
+help: consider restricting type parameter `T`
+ |
+LL | impl<T: Bar<N>, const N: usize> Foo<T, N> {
+ | ++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
|
= note: closures need an RFC before allowed to be called in constants
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
|
= note: closures need an RFC before allowed to be called in constants
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
- = note: ...which requires computing layout of `[u8; _]`...
- = note: ...which requires normalizing `[u8; _]`...
+ = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
+ = note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
= note: ...which again requires evaluating type-level constant, completing the cycle
note: cycle used when checking that `Foo` is well-formed
--> $DIR/issue-44415.rs:5:1
|
= note: closures need an RFC before allowed to be called in constant functions
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: function pointer calls are not allowed in constant functions
--> $DIR/issue-56164.rs:5:5
|
= note: closures need an RFC before allowed to be called in constants
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
#![feature(const_type_id)]
#![feature(const_type_name)]
+#![feature(const_trait_impl)]
use std::any::{self, TypeId};
}
const fn check_type_id<T: 'static>() -> bool {
- matches!(GetTypeId::<T>::VALUE, GetTypeId::<usize>::VALUE)
+ GetTypeId::<T>::VALUE == GetTypeId::<usize>::VALUE
}
pub struct GetTypeNameLen<T>(T);
*a == *b
//~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
+ //~| HELP: add `#![feature(const_trait_impl)]`
}
const fn g(a: &&&&i64, b: &&&&i64) -> bool {
****a == ****b
//~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
+ //~| HELP: add `#![feature(const_trait_impl)]`
}
const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
if *l == *r {
//~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
+ //~| HELP: add `#![feature(const_trait_impl)]`
a = at;
b = bt;
} else {
a == b
//~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
+ //~| HELP: add `#![feature(const_trait_impl)]`
}
const fn g(a: &&&&i64, b: &&&&i64) -> bool {
a == b
//~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
+ //~| HELP: add `#![feature(const_trait_impl)]`
}
const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
if l == r {
//~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
+ //~| HELP: add `#![feature(const_trait_impl)]`
a = at;
b = bt;
} else {
| ^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
help: consider dereferencing here
|
LL | *a == *b
| + +
error[E0015]: cannot call non-const operator in constant functions
- --> $DIR/issue-90870.rs:14:5
+ --> $DIR/issue-90870.rs:15:5
|
LL | a == b
| ^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
help: consider dereferencing here
|
LL | ****a == ****b
| ++++ ++++
error[E0015]: cannot call non-const operator in constant functions
- --> $DIR/issue-90870.rs:21:12
+ --> $DIR/issue-90870.rs:23:12
|
LL | if l == r {
| ^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
help: consider dereferencing here
|
LL | if *l == *r {
LL | let x: &'static u32 = {
| ------------ type annotation requires that `y` is borrowed for `'static`
LL | let y = 42;
+ | - binding `y` declared here
LL | &y
| ^^ borrowed value does not live long enough
LL | };
[5; Self::HOST_SIZE] == [6; 0]
//~^ ERROR constant expression depends on a generic parameter
//~| ERROR constant expression depends on a generic parameter
- //~| ERROR can't compare `[{integer}; _]` with `[{integer}; 0]`
+ //~| ERROR can't compare `[{integer}; Self::HOST_SIZE]` with `[{integer}; 0]`
}
}
|
= note: this may fail depending on what value the parameter takes
-error[E0277]: can't compare `[{integer}; _]` with `[{integer}; 0]`
+error[E0277]: can't compare `[{integer}; Self::HOST_SIZE]` with `[{integer}; 0]`
--> $DIR/too_generic_eval_ice.rs:7:30
|
LL | [5; Self::HOST_SIZE] == [6; 0]
- | ^^ no implementation for `[{integer}; _] == [{integer}; 0]`
+ | ^^ no implementation for `[{integer}; Self::HOST_SIZE] == [{integer}; 0]`
|
- = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; _]`
+ = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]`
= help: the following other types implement trait `PartialEq<Rhs>`:
<&[B] as PartialEq<[A; N]>>
<&[T] as PartialEq<Vec<U, A>>>
-error: values of the type `[u8; SIZE]` are too big for the current architecture
+error: values of the type `[u8; usize::MAX]` are too big for the current architecture
error: aborting due to previous error
-error: values of the type `[u8; SIZE]` are too big for the current architecture
+error: values of the type `[u8; usize::MAX]` are too big for the current architecture
error: aborting due to previous error
--- /dev/null
+// Test dump-dep-graph requires query-dep-graph enabled
+
+// incremental
+// compile-flags: -Z dump-dep-graph
+
+fn main() {}
--- /dev/null
+error: can't dump dependency graph without `-Z query-dep-graph`
+
| |
| doesn't satisfy `Value: Eq`
| doesn't satisfy `Value: Hash`
+ | doesn't satisfy `Value: PartialEq`
...
LL | hs.insert(Value(0));
| ^^^^^^
|
= note: the following trait bounds were not satisfied:
`Value: Eq`
+ `Value: PartialEq`
+ which is required by `Value: Eq`
`Value: Hash`
help: consider annotating `Value` with `#[derive(Eq, Hash, PartialEq)]`
|
--> $DIR/issue-91550.rs:26:9
|
LL | pub struct NoDerives;
- | -------------------- doesn't satisfy `NoDerives: Eq`
+ | --------------------
+ | |
+ | doesn't satisfy `NoDerives: Eq`
+ | doesn't satisfy `NoDerives: PartialEq`
LL |
LL | struct Object<T>(T);
| ---------------- method `use_eq` not found for this struct
| ^^ ---------
| |
| unsatisfied trait bound introduced here
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: Eq`
help: consider annotating `NoDerives` with `#[derive(Eq, PartialEq)]`
|
LL | #[derive(Eq, PartialEq)]
--> $DIR/issue-91550.rs:27:9
|
LL | pub struct NoDerives;
- | -------------------- doesn't satisfy `NoDerives: Ord`
+ | --------------------
+ | |
+ | doesn't satisfy `NoDerives: Eq`
+ | doesn't satisfy `NoDerives: Ord`
+ | doesn't satisfy `NoDerives: PartialEq`
+ | doesn't satisfy `NoDerives: PartialOrd`
LL |
LL | struct Object<T>(T);
| ---------------- method `use_ord` not found for this struct
| ^^^ ---------
| |
| unsatisfied trait bound introduced here
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: PartialOrd`
+ which is required by `NoDerives: Ord`
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: Ord`
+ `NoDerives: Eq`
+ which is required by `NoDerives: Ord`
help: consider annotating `NoDerives` with `#[derive(Eq, Ord, PartialEq, PartialOrd)]`
|
LL | #[derive(Eq, Ord, PartialEq, PartialOrd)]
LL | pub struct NoDerives;
| --------------------
| |
+ | doesn't satisfy `NoDerives: Eq`
| doesn't satisfy `NoDerives: Ord`
+ | doesn't satisfy `NoDerives: PartialEq`
| doesn't satisfy `NoDerives: PartialOrd`
LL |
LL | struct Object<T>(T);
| | |
| | unsatisfied trait bound introduced here
| unsatisfied trait bound introduced here
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: Ord`
+ `NoDerives: Eq`
+ which is required by `NoDerives: Ord`
+ `NoDerives: PartialEq`
+ which is required by `NoDerives: PartialOrd`
help: consider annotating `NoDerives` with `#[derive(Eq, Ord, PartialEq, PartialOrd)]`
|
LL | #[derive(Eq, Ord, PartialEq, PartialOrd)]
#[automatically_derived]
impl ::core::fmt::Debug for Fieldless {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
- match self {
- Fieldless::A => ::core::fmt::Formatter::write_str(f, "A"),
- Fieldless::B => ::core::fmt::Formatter::write_str(f, "B"),
- Fieldless::C => ::core::fmt::Formatter::write_str(f, "C"),
- }
+ ::core::fmt::Formatter::write_str(f,
+ match self {
+ Fieldless::A => "A",
+ Fieldless::B => "B",
+ Fieldless::C => "C",
+ })
}
}
#[automatically_derived]
--> $DIR/bad-assoc-ty.rs:1:10
|
LL | type A = [u8; 4]::AssocTy;
- | ^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<[u8; 4] as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `[u8; 4]`, you could use the fully-qualified path
+ |
+LL | type A = <[u8; 4] as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:5:10
|
LL | type B = [u8]::AssocTy;
- | ^^^^^^^^^^^^^ help: use fully-qualified syntax: `<[u8] as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `[u8]`, you could use the fully-qualified path
+ |
+LL | type B = <[u8] as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:9:10
|
LL | type C = (u8)::AssocTy;
- | ^^^^^^^^^^^^^ help: use fully-qualified syntax: `<u8 as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path
+ |
+LL | type C = <u8 as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:13:10
|
LL | type D = (u8, u8)::AssocTy;
- | ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(u8, u8) as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `(u8, u8)`, you could use the fully-qualified path
+ |
+LL | type D = <(u8, u8) as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0121]: the placeholder `_` is not allowed within types on item signatures for type aliases
--> $DIR/bad-assoc-ty.rs:17:10
--> $DIR/bad-assoc-ty.rs:21:19
|
LL | type F = &'static (u8)::AssocTy;
- | ^^^^^^^^^^^^^ help: use fully-qualified syntax: `<u8 as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path
+ |
+LL | type F = &'static <u8 as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:27:10
|
LL | type G = dyn 'static + (Send)::AssocTy;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Send + 'static) as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `(dyn Send + 'static)`, you could use the fully-qualified path
+ |
+LL | type G = <(dyn Send + 'static) as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
warning: trait objects without an explicit `dyn` are deprecated
--> $DIR/bad-assoc-ty.rs:33:10
--> $DIR/bad-assoc-ty.rs:33:10
|
LL | type H = Fn(u8) -> (u8)::Output;
- | ^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<(dyn Fn(u8) -> u8 + 'static) as Trait>::Output`
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: use the fully-qualified path: `<(dyn Fn(u8) -> u8 + 'static) as IntoFuture>::Output`
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:39:19
|
LL | ($ty: ty) => ($ty::AssocTy);
- | ^^^^^^^^^^^^ help: use fully-qualified syntax: `<u8 as Trait>::AssocTy`
+ | ^^^^^^^^^^^^
...
LL | type J = ty!(u8);
| ------- in this macro invocation
|
= note: this error originates in the macro `ty` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path
+ |
+LL | ($ty: ty) => (<u8 as Example>::AssocTy);
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/bad-assoc-ty.rs:46:10
|
LL | type I = ty!()::AssocTy;
- | ^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<u8 as Trait>::AssocTy`
+ | ^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `AssocTy` implemented for `u8`, you could use the fully-qualified path
+ |
+LL | type I = <u8 as Example>::AssocTy;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/bad-assoc-ty.rs:51:13
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;
| ++++++++
error[E0277]: the size for values of type `[usize]` cannot be known at compilation time
- --> $DIR/dst-sized-trait-param.rs:10:6
+ --> $DIR/dst-sized-trait-param.rs:10:21
|
LL | impl Foo<isize> for [usize] { }
- | ^^^^^^^^^^ doesn't have a size known at compile-time
+ | ^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[usize]`
note: required by a bound in `Foo`
LL | macro_two!();
| ^^^^^^^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
two_macros::macro_two
error: aborting due to previous error
--- /dev/null
+#![feature(rustc_attrs)]
+
+#[rustc_variance]
+struct Foo<'a, T> { //~ ERROR [-, o]
+ t: &'a mut T,
+}
+
+fn main() {}
--- /dev/null
+error: [-, o]
+ --> $DIR/E0208.rs:4:1
+ |
+LL | struct Foo<'a, T> {
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
trait MyTrait { type X; }
+struct MyStruct;
+impl MyTrait for MyStruct {
+ type X = ();
+}
fn main() {
let foo: MyTrait::X;
error[E0223]: ambiguous associated type
- --> $DIR/E0223.rs:4:14
+ --> $DIR/E0223.rs:8:14
|
LL | let foo: MyTrait::X;
- | ^^^^^^^^^^ help: use fully-qualified syntax: `<Type as MyTrait>::X`
+ | ^^^^^^^^^^ help: use the fully-qualified path: `<MyStruct as MyTrait>::X`
error: aborting due to previous error
error[E0308]: mismatched types
- --> $DIR/E0308-2.rs:9:6
+ --> $DIR/E0308-2.rs:9:13
|
LL | impl Eq for &dyn DynEq {}
- | ^^ lifetime mismatch
+ | ^^^^^^^^^^ lifetime mismatch
|
= note: expected trait `<&dyn DynEq as PartialEq>`
found trait `<&(dyn DynEq + 'static) as PartialEq>`
--> $DIR/E0503.rs:4:16
|
LL | let _borrow = &mut value;
- | ---------- borrow of `value` occurs here
+ | ---------- `value` is borrowed here
LL | let _sum = value + 1;
| ^^^^^ use of borrowed `value`
LL | _borrow.use_mut();
error[E0505]: cannot move out of `fancy_num` because it is borrowed
--> $DIR/E0504.rs:9:13
|
+LL | let fancy_num = FancyNum { num: 5 };
+ | --------- binding `fancy_num` declared here
LL | let fancy_ref = &fancy_num;
| ---------- borrow of `fancy_num` occurs here
LL |
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/E0505.rs:9:13
|
+LL | let x = Value{};
+ | - binding `x` declared here
+LL | {
LL | let _ref_to_val: &Value = &x;
| -- borrow of `x` occurs here
LL | eat(x);
--> $DIR/E0506.rs:8:5
|
LL | let fancy_ref = &fancy_num;
- | ---------- borrow of `fancy_num` occurs here
+ | ---------- `fancy_num` is borrowed here
LL | fancy_num = FancyNum { num: 6 };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ assignment to borrowed `fancy_num` occurs here
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fancy_num` is assigned to here but it was already borrowed
LL |
LL | println!("Num: {}, Ref: {}", fancy_num.num, fancy_ref.num);
| ------------- borrow later used here
error[E0597]: `y` does not live long enough
--> $DIR/E0597.rs:8:16
|
+LL | let y = 0;
+ | - binding `y` declared here
LL | x.x = Some(&y);
| ^^ borrowed value does not live long enough
LL |
fn main() {
- &0u8 as u8; //~ ERROR E0606
+ let x = &(&0u8 as u8); //~ ERROR E0606
+ x as u8; //~ casting `&u8` as `u8` is invalid [E0606]
}
error[E0606]: casting `&u8` as `u8` is invalid
- --> $DIR/E0606.rs:2:5
+ --> $DIR/E0606.rs:2:14
|
-LL | &0u8 as u8;
- | ----^^^^^^
- | |
- | cannot cast `&u8` as `u8`
- | help: dereference the expression: `*&0u8`
+LL | let x = &(&0u8 as u8);
+ | ^^^^^^^^^^^^
+ |
+help: remove the unneeded borrow
+ |
+LL - let x = &(&0u8 as u8);
+LL + let x = &(0u8 as u8);
+ |
+
+error[E0606]: casting `&u8` as `u8` is invalid
+ --> $DIR/E0606.rs:3:5
+ |
+LL | x as u8;
+ | ^^^^^^^
+ |
+help: dereference the expression
+ |
+LL | *x as u8;
+ | +
-error: aborting due to previous error
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0606`.
--- /dev/null
+// compile-flags: --crate-type lib
+
+#![feature(rustc_attrs)]
+#![feature(staged_api)]
+#![unstable(feature = "foo_module", reason = "...", issue = "123")]
+
+#[rustc_allowed_through_unstable_modules]
+// #[stable(feature = "foo", since = "1.0")]
+struct Foo;
+//~^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+//~^^ ERROR `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+// FIXME: we shouldn't have two errors here, only occurs when using `-Zdeduplicate-diagnostics=no`
--- /dev/null
+error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+ --> $DIR/E0789.rs:9:1
+ |
+LL | struct Foo;
+ | ^^^^^^^^^^^
+
+error[E0789]: `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+ --> $DIR/E0789.rs:9:1
+ |
+LL | struct Foo;
+ | ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0789`.
--> $DIR/error-festival.rs:37:18
|
LL | let y: u32 = x as u32;
- | -^^^^^^^
- | |
- | cannot cast `&u8` as `u32`
- | help: dereference the expression: `*x`
+ | ^^^^^^^^
+ |
+help: dereference the expression
+ |
+LL | let y: u32 = *x as u32;
+ | +
error[E0607]: cannot cast thin pointer `*const u8` to fat pointer `*const [u8]`
--> $DIR/error-festival.rs:41:5
--- /dev/null
+// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
+// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file.
+
+pub struct SomeStruct {} // This line should be show as part of the error.
--- /dev/null
+error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
+ --> $DIR/remap-path-prefix-reverse.rs:16:13
+ |
+LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
+ |
+ ::: remapped-aux/remapped_dep.rs:4:1
+ |
+LL | pub struct SomeStruct {} // This line should be show as part of the error.
+ | --------------------- `remapped_dep::SomeStruct` defined here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0423`.
--- /dev/null
+error[E0423]: expected value, found struct `remapped_dep::SomeStruct`
+ --> $DIR/remap-path-prefix-reverse.rs:16:13
+ |
+LL | let _ = remapped_dep::SomeStruct; // ~ERROR E0423
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use struct literal syntax instead: `remapped_dep::SomeStruct {}`
+ |
+ ::: remapped-aux/remapped_dep.rs:4:1
+ |
+LL | pub struct SomeStruct {} // This line should be show as part of the error.
+ | --------------------- `remapped_dep::SomeStruct` defined here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0423`.
--- /dev/null
+// aux-build:remapped_dep.rs
+// compile-flags: --remap-path-prefix={{src-base}}/errors/auxiliary=remapped-aux
+
+// revisions: local-self remapped-self
+// [local-self] no-remap-src-base: The hack should work regardless of remapping.
+// [remapped-self] remap-src-base
+
+// Verify that the expected source code is shown.
+// error-pattern: pub struct SomeStruct {} // This line should be show
+
+extern crate remapped_dep;
+
+fn main() {
+ // The actual error is irrelevant. The important part it that is should show
+ // a snippet of the dependency's source.
+ let _ = remapped_dep::SomeStruct; // ~ERROR E0423
+}
--- /dev/null
+// compile-flags: --remap-path-prefix={{src-base}}=remapped
+// no-remap-src-base: Manually remap, so the remapped path remains in .stderr file.
+
+// The remapped paths are not normalized by compiletest.
+// normalize-stderr-test: "\\(errors)" -> "/$1"
+
+// The remapped paths aren't recognized by compiletest, so we
+// cannot use line-specific patterns.
+// error-pattern: E0425
+
+fn main() {
+ // We cannot actually put an ERROR marker here because
+ // the file name in the error message is not what the
+ // test framework expects (since the filename gets remapped).
+ // We still test the expected error in the stderr file.
+ ferris
+}
--- /dev/null
+error[E0425]: cannot find value `ferris` in this scope
+ --> remapped/errors/remap-path-prefix.rs:16:5
+ |
+LL | ferris
+ | ^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
help: consider importing this module instead
|
LL | use std::env;
- | ~~~~~~~~~
+ | ~~~~~~~~
error: cannot determine resolution for the macro `env`
--> $DIR/issue-55897.rs:6:22
+++ /dev/null
-// needs-llvm-components: x86
-// compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib
-#![no_core]
-#![feature(no_core, lang_items)]
-#[lang="sized"]
-trait Sized { }
-
-// Functions
-extern "efiapi" fn f1() {} //~ ERROR efiapi ABI is experimental
-
-// Methods in trait defintion
-trait Tr {
- extern "efiapi" fn f2(); //~ ERROR efiapi ABI is experimental
- extern "efiapi" fn f3() {} //~ ERROR efiapi ABI is experimental
-}
-
-struct S;
-
-// Methods in trait impl
-impl Tr for S {
- extern "efiapi" fn f2() {} //~ ERROR efiapi ABI is experimental
-}
-
-// Methods in inherent impl
-impl S {
- extern "efiapi" fn f4() {} //~ ERROR efiapi ABI is experimental
-}
-
-// Function pointer types
-type A = extern "efiapi" fn(); //~ ERROR efiapi ABI is experimental
-
-// Foreign modules
-extern "efiapi" {} //~ ERROR efiapi ABI is experimental
+++ /dev/null
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:9:8
- |
-LL | extern "efiapi" fn f1() {}
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:13:12
- |
-LL | extern "efiapi" fn f2();
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:14:12
- |
-LL | extern "efiapi" fn f3() {}
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:21:12
- |
-LL | extern "efiapi" fn f2() {}
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:26:12
- |
-LL | extern "efiapi" fn f4() {}
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:30:17
- |
-LL | type A = extern "efiapi" fn();
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error[E0658]: efiapi ABI is experimental and subject to change
- --> $DIR/feature-gate-abi-efiapi.rs:33:8
- |
-LL | extern "efiapi" {}
- | ^^^^^^^^
- |
- = note: see issue #65815 <https://github.com/rust-lang/rust/issues/65815> for more information
- = help: add `#![feature(abi_efiapi)]` to the crate attributes to enable
-
-error: aborting due to 7 previous errors
-
-For more information about this error, try `rustc --explain E0658`.
--- /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
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) {}
| ^^ ----
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) {}
| ^^ ----
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) {}
| ^^ ----
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) {}
| ^^ ----
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`
+ = note: fn items are distinct from fn pointers
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
+
+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
+
+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
+
+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);
--> $DIR/fn-help-with-err-generic-is-not-function.rs:2:13
|
LL | impl Struct<T>
- | - ^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<T>`
+ | ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | impl<T> Struct<T>
+ | +++
error[E0412]: cannot find type `T` in this scope
--> $DIR/fn-help-with-err-generic-is-not-function.rs:7:5
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
...
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> {
//~| NOTE required for
//~| NOTE required by a bound introduced by this call
//~| NOTE captures the following types
+ //~| NOTE use `std::sync::RwLock` instead
}
fn main() {}
| ^^^^^^^^ 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:36:9
|
| ^^^^ required by this bound in `require_send`
error[E0277]: `RefCell<i32>` cannot be shared between threads safely
- --> $DIR/issue-68112.rs:63:18
+ --> $DIR/issue-68112.rs:64: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:48:5
+ --> $DIR/issue-68112.rs:49:5
|
LL | || {
| ^^
note: required because it appears within the type `impl Generator<Return = Arc<RefCell<i32>>>`
- --> $DIR/issue-68112.rs:45:30
+ --> $DIR/issue-68112.rs:46: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
+ --> $DIR/issue-68112.rs:54: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
+ --> $DIR/issue-68112.rs:60:20
|
LL | let send_gen = || {
| ^^
| |_____^ `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 for `&Cell<i32>` to implement `Send`
note: required because it's used within this generator
--> $DIR/not-send-sync.rs:16:17
| |_____^ 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: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
note: generator is not `Sync` as this value is used across a yield
--> $DIR/not-send-sync.rs:12:9
|
| ^^^^^^^^ 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:35:9
|
| 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:42:5
| |_____^ `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 for `&'_#4r Cell<i32>` to implement `Send`
note: required because it's used within this generator
--> $DIR/generator-print-verbose-2.rs:19:17
| |_____^ generator is not `Sync`
|
= help: within `[main::{closure#0} upvar_tys=() {Cell<i32>, ()}]`, 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: generator is not `Sync` as this value is used across a yield
--> $DIR/generator-print-verbose-2.rs:15:9
|
// check-fail
-// known-bug
+// known-bug: unknown
// This gives us problems because `for<'a> I::Item<'a>: Debug` should mean "for
// all 'a where I::Item<'a> is WF", but really means "for all 'a possible"
fn main() {
let slice = &mut ();
- //~^ temporary value dropped while borrowed
let windows = WindowsMut { slice };
print_items::<WindowsMut<'_>>(windows);
}
|
LL | let slice = &mut ();
| ^^ creates a temporary value which is freed while still in use
-...
+LL | let windows = WindowsMut { slice };
LL | print_items::<WindowsMut<'_>>(windows);
| -------------------------------------- argument requires that borrow lasts for `'static`
LL | }
// check-fail
-// known-bug
+// known-bug: unknown
// This gives us problems because `for<'a> I::Item<'a>: Debug` should mean "for
// all 'a where I::Item<'a> is WF", but really means "for all 'a possible"
{
let mut iter2 = Eat(iter, f);
let _next = iter2.next();
- //~^ borrowed data escapes
true
}
impl<I: LendingIterator> LendingIterator for &mut I {
// check-fail
-// known-bug
+// known-bug: unknown
// edition: 2021
// We really should accept this, but we need implied bounds between the regions
fn call<I: FutureIterator>() -> impl Send {
async { // a generator checked for autotrait impl `Send`
- //~^ lifetime bound not satisfied
let x = None::<I::Future<'_, '_>>; // a type referencing GAT
async {}.await; // a yield point
}
fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
async { // a generator checked for autotrait impl `Send`
- //~^ lifetime bound not satisfied
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
- //~^ lifetime may not live long enough
async {}.await; // a yield point
}
}
fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
async { // a generator checked for autotrait impl `Send`
- //~^ lifetime bound not satisfied
let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
async {}.await; // a yield point
}
--> $DIR/issue-100013.rs:15:5
|
LL | / async { // a generator checked for autotrait impl `Send`
-LL | |
LL | | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
LL | | async {}.await; // a yield point
LL | | }
| |_____^
|
note: the lifetime defined here...
- --> $DIR/issue-100013.rs:17:38
+ --> $DIR/issue-100013.rs:16:38
|
LL | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
| ^^
note: ...must outlive the lifetime defined here
- --> $DIR/issue-100013.rs:17:34
+ --> $DIR/issue-100013.rs:16:34
|
LL | let x = None::<I::Future<'_, '_>>; // a type referencing GAT
| ^^
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:23:5
+ --> $DIR/issue-100013.rs:22:5
|
LL | / async { // a generator checked for autotrait impl `Send`
-LL | |
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
-LL | |
LL | | async {}.await; // a yield point
LL | | }
| |_____^
|
note: the lifetime defined here...
- --> $DIR/issue-100013.rs:22:14
+ --> $DIR/issue-100013.rs:21:14
|
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
| ^^
note: ...must outlive the lifetime defined here
- --> $DIR/issue-100013.rs:22:10
+ --> $DIR/issue-100013.rs:21:10
|
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
| ^^
= note: this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)
error: lifetime may not live long enough
- --> $DIR/issue-100013.rs:25:17
+ --> $DIR/issue-100013.rs:23:17
|
LL | fn call2<'a, 'b, I: FutureIterator>() -> impl Send {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
-...
+LL | async { // a generator checked for autotrait impl `Send`
LL | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
| ^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'b`
|
= help: consider adding the following bound: `'a: 'b`
error: lifetime bound not satisfied
- --> $DIR/issue-100013.rs:32:5
+ --> $DIR/issue-100013.rs:29:5
|
LL | / async { // a generator checked for autotrait impl `Send`
-LL | |
LL | | let x = None::<I::Future<'a, 'b>>; // a type referencing GAT
LL | | async {}.await; // a yield point
LL | | }
| |_____^
|
note: the lifetime defined here...
- --> $DIR/issue-100013.rs:31:18
+ --> $DIR/issue-100013.rs:28:18
|
LL | fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
| ^^
note: ...must outlive the lifetime defined here
- --> $DIR/issue-100013.rs:31:10
+ --> $DIR/issue-100013.rs:28:10
|
LL | fn call3<'a: 'b, 'b, I: FutureIterator>() -> impl Send {
| ^^
// check-fail
-// known-bug
+// known-bug: unknown
// We almost certainly want this to pass, but
// it's particularly difficult currently, because we need a way of specifying
arg = self;
ret = <Self::Base as Functor>::fmap(arg);
- //~^ type annotations needed
}
}
LL | | let _x = x;
LL | | };
| |_____^
+ |
+note: due to current limitations in the borrow checker, this implies a `'static` lifetime
+ --> $DIR/collectivity-regression.rs:11:16
+ |
+LL | for<'a> T: Get<Value<'a> = ()>,
+ | ^^^^^^^^^^^^^^^^^^^
+help: consider restricting the type parameter to the `'static` lifetime
+ |
+LL | for<'a> T: Get<Value<'a> = ()> + 'static,
+ | +++++++++
error: aborting due to previous error
LL | fn bug<'a, T: ?Sized + Fun<F<'a> = [u8]>>(_ : Box<T>) -> &'static T::F<'a> {
| -- lifetime `'a` defined here
LL | let a = [0; 1];
+ | - binding `a` declared here
LL | let _x = T::identity(&a);
| ------------^^-
| | |
| ^^^^^^ the trait `Clone` is not implemented for `T`
|
= note: required for `Box<T>` to implement `Clone`
+ = note: required for `<Self as UnsafeCopy>::Copy<T>` to implement `Copy`
note: required by a bound in `UnsafeCopy::Copy`
--> $DIR/issue-74824.rs:6:19
|
--- /dev/null
+// run-rustfix
+
+trait GatTrait {
+ type Gat<'a> where Self: 'a;
+
+ fn test(&self) -> Self::Gat<'_>;
+}
+
+trait SuperTrait<T>
+where
+ Self: 'static,
+ for<'a> Self: GatTrait<Gat<'a> = &'a T>,
+{
+ fn copy(&self) -> Self::Gat<'_> where T: Copy {
+ self.test()
+ //~^ mismatched types
+ }
+}
+
+fn main() {}
+// run-rustfix
+
trait GatTrait {
type Gat<'a> where Self: 'a;
error[E0308]: mismatched types
- --> $DIR/issue-88360.rs:13:9
+ --> $DIR/issue-88360.rs:15:9
|
LL | trait SuperTrait<T>
| - this type parameter
LL | fn copy(&self) -> Self::Gat<'_> where T: Copy {
| ------------- expected `&T` because of return type
LL | *self.test()
- | ^^^^^^^^^^^^
- | |
- | expected `&T`, found type parameter `T`
- | help: consider borrowing here: `&*self.test()`
+ | ^^^^^^^^^^^^ expected `&T`, found type parameter `T`
|
= note: expected reference `&T`
found type parameter `T`
+help: consider removing deref here
+ |
+LL - *self.test()
+LL + self.test()
+ |
error: aborting due to previous error
|
= note: expected type parameter `B`
found associated type `<B as Add>::Output`
+help: the type constructed contains `<B as Add>::Output` due to the type of the argument passed
+ --> $DIR/missing-bounds.rs:11:9
+ |
+LL | A(self.0 + rhs.0)
+ | ^^--------------^
+ | |
+ | this argument influences the type of `A`
note: tuple struct defined here
--> $DIR/missing-bounds.rs:5:8
|
--- /dev/null
+trait Trait {}
+
+fn foo(_: impl &Trait) {}
+//~^ ERROR expected a trait, found type
+
+fn bar<T: &Trait>(_: T) {}
+//~^ ERROR expected a trait, found type
+
+fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
+//~^ ERROR expected a trait, found type
+
+fn foo_bad(_: impl &BadTrait) {}
+//~^ ERROR expected a trait, found type
+//~^^ ERROR cannot find trait `BadTrait` in this scope
+
+fn bar_bad<T: &BadTrait>(_: T) {}
+//~^ ERROR expected a trait, found type
+//~^^ ERROR cannot find trait `BadTrait` in this scope
+
+fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+//~^ ERROR expected a trait, found type
+//~^^ ERROR cannot find trait `BadTrait` in this scope
+
+fn main() {}
--- /dev/null
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:3:16
+ |
+LL | fn foo(_: impl &Trait) {}
+ | ^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn foo(_: impl &Trait) {}
+LL + fn foo(_: impl Trait) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:6:11
+ |
+LL | fn bar<T: &Trait>(_: T) {}
+ | ^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn bar<T: &Trait>(_: T) {}
+LL + fn bar<T: Trait>(_: T) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:9:35
+ |
+LL | fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
+ | ^^^^^^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn partially_correct_impl(_: impl &*const &Trait + Copy) {}
+LL + fn partially_correct_impl(_: impl Trait + Copy) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:12:20
+ |
+LL | fn foo_bad(_: impl &BadTrait) {}
+ | ^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn foo_bad(_: impl &BadTrait) {}
+LL + fn foo_bad(_: impl BadTrait) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:16:15
+ |
+LL | fn bar_bad<T: &BadTrait>(_: T) {}
+ | ^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn bar_bad<T: &BadTrait>(_: T) {}
+LL + fn bar_bad<T: BadTrait>(_: T) {}
+ |
+
+error: expected a trait, found type
+ --> $DIR/issue-106694.rs:20:39
+ |
+LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: consider removing the indirection
+ |
+LL - fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+LL + fn partially_correct_impl_bad(_: impl BadTrait + Copy) {}
+ |
+
+error[E0405]: cannot find trait `BadTrait` in this scope
+ --> $DIR/issue-106694.rs:12:21
+ |
+LL | fn foo_bad(_: impl &BadTrait) {}
+ | ^^^^^^^^ not found in this scope
+
+error[E0405]: cannot find trait `BadTrait` in this scope
+ --> $DIR/issue-106694.rs:16:16
+ |
+LL | fn bar_bad<T: &BadTrait>(_: T) {}
+ | ^^^^^^^^ not found in this scope
+
+error[E0405]: cannot find trait `BadTrait` in this scope
+ --> $DIR/issue-106694.rs:20:48
+ |
+LL | fn partially_correct_impl_bad(_: impl &*const &BadTrait + Copy) {}
+ | ^^^^^^^^ not found in this scope
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0405`.
--> $DIR/hrtb-identity-fn-borrows.rs:14:5
|
LL | let y = f.call(&x);
- | -- borrow of `x` occurs here
+ | -- `x` is borrowed here
LL | x = 5;
- | ^^^^^ assignment to borrowed `x` occurs here
+ | ^^^^^ `x` is assigned to here but it was already borrowed
...
LL | drop(y);
| - borrow later used here
--- /dev/null
+// check-pass
+
+fn lifetime<'a>()
+where
+ &'a (): 'a,
+{
+ /* do nothing */
+}
+
+fn doesnt_work()
+where
+ for<'a> &'a (): 'a,
+{
+ /* do nothing */
+}
+
+fn main() {
+ lifetime();
+ doesnt_work();
+}
LL | n!(f);
| ^ not found in this scope
|
- = note: consider importing this function:
+ = help: consider importing this function:
foo::f
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | f
| ^ not found in this scope
|
- = note: consider importing this function:
+ = help: consider importing this function:
foo::f
= note: this error originates in the macro `n` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | print!();
| ^^^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
std::print
error: aborting due to previous error
LL | let x = {
| - borrow later stored here
LL | let bar = 22;
+ | --- binding `bar` declared here
LL | Foo::new(&bar).into()
| ^^^^ borrowed value does not live long enough
LL |
LL | let x = {
| - borrow later stored here
LL | let y = ();
+ | - binding `y` declared here
LL | foo(&y)
| ^^ borrowed value does not live long enough
LL |
LL | let x = {
| - borrow later stored here
LL | let y = ();
+ | - binding `y` declared here
LL | foo(&y)
| ^^ borrowed value does not live long enough
LL |
fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
//~^ ERROR `impl Trait` is not allowed in path parameters
-//~^^ ERROR ambiguous associated type
+//~| ERROR `impl Trait` is not allowed in path parameters
x.next().unwrap()
}
LL | -> <dyn Iterator<Item = impl Debug> as Iterator>::Item
| ^^^^^^^^^^
-error[E0223]: ambiguous associated type
- --> $DIR/impl_trait_projections.rs:12:50
+error[E0667]: `impl Trait` is not allowed in path parameters
+ --> $DIR/impl_trait_projections.rs:12:51
|
LL | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
- | ^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<impl Iterator as Trait>::Item`
+ | ^^^^^^^^^^^^^
error: aborting due to 5 previous errors
-Some errors have detailed explanations: E0223, E0667.
-For more information about an error, try `rustc --explain E0223`.
+For more information about this error, try `rustc --explain E0667`.
--- /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`.
...
LL | / move || {
LL | | x;
+ | | - closure captures itself here
LL | | }
| |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:35:5: 35:12]`
...
LL | / move || {
LL | | &x;
+ | | - closure captures itself here
LL | | }
| |_____- returning here with type `[closure@$DIR/recursive-impl-trait-type-indirect.rs:43:5: 43:12]`
LL | / move || {
LL | | yield;
LL | | x;
+ | | - generator captures itself here
LL | | }
| |_____- returning here with type `[generator@$DIR/recursive-impl-trait-type-indirect.rs:61:5: 61:12]`
LL |
LL | / move || {
LL | | let x = generator_hold();
+ | | - generator captures itself here
LL | | yield;
LL | | x;
LL | | }
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);
| -----^^------
| | |
error: impl method assumes more implied bounds than the corresponding trait method
- --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
|
LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
|
= 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 #105572 <https://github.com/rust-lang/rust/issues/105572>
error: aborting due to previous error
+Future incompatibility report: Future breakage diagnostic:
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:31
+ |
+LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `()`
+ |
+ = 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 #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: impl method assumes more implied bounds than the corresponding trait method
- --> $DIR/impl-implied-bounds-compatibility.rs:14:5
+ --> $DIR/impl-implied-bounds-compatibility.rs:14:35
|
LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
error: aborting due to previous error
+Future incompatibility report: Future breakage diagnostic:
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility.rs:14:35
+ |
+LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this type to make the impl signature compatible: `&'b MessageListeners<'b>`
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
LL | use super::{super::C::D::AA, AA as _};
| ^^^^^^^^^^^^^^^ no `AA` in `C::D`
|
- = note: consider importing this type alias instead:
+ = help: consider importing this type alias instead:
crate::A::AA
error[E0432]: unresolved import `crate::C::AA`
LL | use crate::C::{self, AA};
| ^^ no `AA` in `C`
|
- = note: consider importing this type alias instead:
+ = help: consider importing this type alias instead:
crate::A::AA
error[E0432]: unresolved import `crate::C::BB`
LL | use crate::{A, C::BB};
| ^^^^^ no `BB` in `C`
|
- = note: consider importing this type alias instead:
+ = help: consider importing this type alias instead:
crate::A::BB
error: aborting due to 3 previous errors
help: consider importing this type alias instead
|
LL | use A::B as _;
- | ~~~~~~~~~~
+ | ~~~~~~~~~
error[E0432]: unresolved import `crate::D::B2`
--> $DIR/bad-import-with-rename.rs:10:9
help: consider importing this type alias instead
|
LL | use A::B2;
- | ~~~~~~
+ | ~~~~~
error: aborting due to 2 previous errors
help: consider importing one of these items instead
|
LL | use crate::m3::last_segment::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | use crate::m3::non_last_segment::non_last_segment::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | use issue_56125::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
LL | use issue_56125::last_segment::issue_56125;
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
and 1 other candidate
error[E0659]: `issue_56125` is ambiguous
help: consider importing this module instead
|
LL | use glob_ok::something;
- | ~~~~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+
+ mod p {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+ }
+
+ use ::nu;
+pub use self::p::{other_item as _};
+ //~^ ERROR unresolved import `self::p::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+
+ mod p {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+ }
+
+ pub use self::p::{nu, other_item as _};
+ //~^ ERROR unresolved import `self::p::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+error[E0432]: unresolved import `self::p::nu`
+ --> $DIR/issue-99695-b.rs:14:23
+ |
+LL | pub use self::p::{nu, other_item as _};
+ | ^^ no `nu` in `m::p`
+ |
+ = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
+help: a macro with this name exists at the root of the crate
+ |
+LL ~ use ::nu;
+LL ~ pub use self::p::{other_item as _};
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0432`.
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+
+ use ::nu;
+pub use self::{other_item as _};
+ //~^ ERROR unresolved import `self::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![allow(unused, nonstandard_style)]
+mod m {
+ #[macro_export]
+ macro_rules! nu {
+ {} => {};
+ }
+
+ pub struct other_item;
+
+ pub use self::{nu, other_item as _};
+ //~^ ERROR unresolved import `self::nu` [E0432]
+ //~| HELP a macro with this name exists at the root of the crate
+}
+
+fn main() {}
--- /dev/null
+error[E0432]: unresolved import `self::nu`
+ --> $DIR/issue-99695.rs:11:20
+ |
+LL | pub use self::{nu, other_item as _};
+ | ^^ no `nu` in `m`
+ |
+ = note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
+help: a macro with this name exists at the root of the crate
+ |
+LL ~ use ::nu;
+LL ~ pub use self::{other_item as _};
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0432`.
error[E0282]: type annotations needed
--> $DIR/cannot-infer-partial-try-return.rs:20:9
|
-LL | infallible()?;
- | ------------- type must be known at this point
LL | Ok(())
| ^^ cannot infer type of the type parameter `E` declared on the enum `Result`
|
--- /dev/null
+use std::marker::PhantomData;
+struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+where
+ Foo<'short, 'out, T>: Convert<'a, 'b>;
+ //~^ ERROR mismatched types
+ //~^^ ERROR mismatched types
+ //~^^^ ERROR use of undeclared lifetime name
+ //~| ERROR use of undeclared lifetime name `'out`
+
+trait Convert<'a, 'b>: Sized {
+ fn cast(&'a self) -> &'b Self;
+}
+impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ //~^ ERROR use of undeclared lifetime name
+ //~^^ ERROR use of undeclared lifetime name `'out`
+ //~| ERROR cannot infer an appropriate lifetime for lifetime parameter
+ fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ //~^ ERROR use of undeclared lifetime name
+ //~| ERROR cannot infer an appropriate lifetime for lifetime parameter
+ self
+ }
+}
+
+fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ //~^ ERROR use of undeclared lifetime name
+ //~^^ ERROR incompatible lifetime on type
+ //~| ERROR `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement
+ sadness.cast()
+}
+
+fn main() {}
--- /dev/null
+error[E0261]: use of undeclared lifetime name `'short`
+ --> $DIR/issue-107090.rs:4:9
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^ undeclared lifetime
+ |
+ = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
+help: consider making the bound lifetime-generic with a new `'short` lifetime
+ |
+LL | for<'short> Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | +++++++++++
+help: consider introducing lifetime `'short` here
+ |
+LL | struct Foo<'short, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | +++++++
+
+error[E0261]: use of undeclared lifetime name `'out`
+ --> $DIR/issue-107090.rs:4:17
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^ undeclared lifetime
+ |
+help: consider making the bound lifetime-generic with a new `'out` lifetime
+ |
+LL | for<'out> Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | +++++++++
+help: consider introducing lifetime `'out` here
+ |
+LL | struct Foo<'out, 'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | +++++
+
+error[E0261]: use of undeclared lifetime name `'b`
+ --> $DIR/issue-107090.rs:13:47
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | - ^^ undeclared lifetime
+ | |
+ | help: consider introducing lifetime `'b` here: `'b,`
+
+error[E0261]: use of undeclared lifetime name `'out`
+ --> $DIR/issue-107090.rs:13:67
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | - help: consider introducing lifetime `'out` here: `'out,` ^^^^ undeclared lifetime
+
+error[E0261]: use of undeclared lifetime name `'out`
+ --> $DIR/issue-107090.rs:17:49
+ |
+LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ^^^^ undeclared lifetime
+ |
+help: consider introducing lifetime `'out` here
+ |
+LL | fn cast<'out>(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ++++++
+help: consider introducing lifetime `'out` here
+ |
+LL | impl<'out, 'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | +++++
+
+error[E0261]: use of undeclared lifetime name `'short`
+ --> $DIR/issue-107090.rs:24:68
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | - ^^^^^^ undeclared lifetime
+ | |
+ | help: consider introducing lifetime `'short` here: `'short,`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-107090.rs:4:27
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^^^^^^^^^^ lifetime mismatch
+ |
+ = note: expected trait `Convert<'static, 'static>`
+ found trait `Convert<'a, 'b>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/issue-107090.rs:2:12
+ |
+LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error[E0308]: mismatched types
+ --> $DIR/issue-107090.rs:4:27
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^^^^^^^^^^ lifetime mismatch
+ |
+ = note: expected trait `Convert<'static, 'static>`
+ found trait `Convert<'a, 'b>`
+note: the lifetime `'b` as defined here...
+ --> $DIR/issue-107090.rs:2:16
+ |
+LL | struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements
+ --> $DIR/issue-107090.rs:13:55
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'short` as defined here...
+ --> $DIR/issue-107090.rs:13:21
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^
+ = note: ...but the lifetime must also be valid for the static lifetime...
+note: ...so that the types are compatible
+ --> $DIR/issue-107090.rs:13:55
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^^^^^^^^^^
+ = note: expected `Convert<'short, 'static>`
+ found `Convert<'_, 'static>`
+
+error: incompatible lifetime on type
+ --> $DIR/issue-107090.rs:24:29
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | ^^^^^^^^^^^^^^^^^^
+ |
+note: because this has an unmet lifetime requirement
+ --> $DIR/issue-107090.rs:4:27
+ |
+LL | Foo<'short, 'out, T>: Convert<'a, 'b>;
+ | ^^^^^^^^^^^^^^^ introduces a `'static` lifetime requirement
+note: the lifetime `'out` as defined here...
+ --> $DIR/issue-107090.rs:24:17
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | ^^^^
+note: ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
+ --> $DIR/issue-107090.rs:13:1
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0759]: `x` has lifetime `'in_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/issue-107090.rs:24:29
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ Foo<'short, 'out, T>) -> &'out T {
+ | ^^^^^^^^^^^^^^^^^^
+ | |
+ | this data with lifetime `'in_`...
+ | ...is used and required to live as long as `'static` here
+
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'long` due to conflicting requirements
+ --> $DIR/issue-107090.rs:17:13
+ |
+LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'short` as defined here...
+ --> $DIR/issue-107090.rs:13:21
+ |
+LL | impl<'long: 'short, 'short, T> Convert<'long, 'b> for Foo<'short, 'out, T> {
+ | ^^^^^^
+ = note: ...but the lifetime must also be valid for the static lifetime...
+note: ...so that the types are compatible
+ --> $DIR/issue-107090.rs:17:13
+ |
+LL | fn cast(&'long self) -> &'short Foo<'short, 'out, T> {
+ | ^^^^^^^^^^^
+ = note: expected `Convert<'short, 'static>`
+ found `Convert<'_, 'static>`
+
+error: aborting due to 12 previous errors
+
+Some errors have detailed explanations: E0261, E0308, E0495, E0759.
+For more information about an error, try `rustc --explain E0261`.
fn main() {
let _ = foo("foo");
- //~^ ERROR: type annotations needed for `[usize; _]`
+ //~^ ERROR: type annotations needed for `[usize; N]`
}
-error[E0282]: type annotations needed for `[usize; _]`
+error[E0282]: type annotations needed for `[usize; N]`
--> $DIR/issue-83606.rs:8:9
|
LL | let _ = foo("foo");
|
help: consider giving this pattern a type, where the the value of const parameter `N` is specified
|
-LL | let _: [usize; _] = foo("foo");
+LL | let _: [usize; N] = foo("foo");
| ++++++++++++
error: aborting due to previous error
error[E0282]: type annotations needed
- --> $DIR/question-mark-type-infer.rs:10:30
+ --> $DIR/question-mark-type-infer.rs:10:21
|
LL | l.iter().map(f).collect()?
- | ^ cannot infer type
+ | ^^^^^^^ cannot infer type of the type parameter `B` declared on the associated function `collect`
+ |
+help: consider specifying the generic argument
+ |
+LL | l.iter().map(f).collect::<Vec<_>>()?
+ | ++++++++++
error: aborting due to previous error
LL | fn foo<'a>() {
| -- lifetime `'a` defined here
LL | let y = ();
+ | - binding `y` declared here
LL | equate(InvariantRef::new(&y), const { InvariantRef::<'a>::NEW });
| ------------------^^-
| | |
-// 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-23073.rs:6:17
|
LL | type FooT = <<Self as Bar>::Foo>::T;
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<<Self as Bar>::Foo as Trait>::T`
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `T` implemented for `<Self as Bar>::Foo`, you could use the fully-qualified path
+ |
+LL | type FooT = <<Self as Bar>::Foo as Example>::T;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
--> $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
...
--> $DIR/issue-58712.rs:6:20
|
LL | impl<H> AddrVec<H, DeviceId> {
- | - ^^^^^^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `, DeviceId`
+ | ^^^^^^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | impl<H, DeviceId> AddrVec<H, DeviceId> {
+ | ++++++++++
error[E0412]: cannot find type `DeviceId` in this scope
--> $DIR/issue-58712.rs:8:29
error[E0308]: mismatched types
- --> $DIR/issue-65230.rs:8:6
+ --> $DIR/issue-65230.rs:8:13
|
LL | impl T1 for &dyn T2 {}
- | ^^ lifetime mismatch
+ | ^^^^^^^ lifetime mismatch
|
= note: expected trait `<&dyn T2 as T0>`
found trait `<&(dyn T2 + 'static) as T0>`
--- /dev/null
+error[E0034]: multiple applicable items in scope
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:24:13
+ |
+LL | r#fn {}.r#struct();
+ | ^^^^^^^^ multiple `r#struct` found
+ |
+note: candidate #1 is defined in an impl of the trait `async` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:7:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl of the trait `await` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:13:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+help: disambiguate the associated function for candidate #1
+ |
+LL | async::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: disambiguate the associated function for candidate #2
+ |
+LL | await::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0034`.
--- /dev/null
+error[E0034]: multiple applicable items in scope
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:24:13
+ |
+LL | r#fn {}.r#struct();
+ | ^^^^^^^^ multiple `r#struct` found
+ |
+note: candidate #1 is defined in an impl of the trait `r#async` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:7:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+note: candidate #2 is defined in an impl of the trait `r#await` for the type `r#fn`
+ --> $DIR/issue-65634-raw-ident-suggestion.rs:13:5
+ |
+LL | fn r#struct(&self) {
+ | ^^^^^^^^^^^^^^^^^^
+help: disambiguate the associated function for candidate #1
+ |
+LL | r#async::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: disambiguate the associated function for candidate #2
+ |
+LL | r#await::r#struct(&r#fn {});
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0034`.
+// revisions: edition2015 edition2018
+//[edition2018]edition:2018
+
#![allow(non_camel_case_types)]
trait r#async {
+++ /dev/null
-error[E0034]: multiple applicable items in scope
- --> $DIR/issue-65634-raw-ident-suggestion.rs:21:13
- |
-LL | r#fn {}.r#struct();
- | ^^^^^^^^ multiple `r#struct` found
- |
-note: candidate #1 is defined in an impl of the trait `async` for the type `fn`
- --> $DIR/issue-65634-raw-ident-suggestion.rs:4:5
- |
-LL | fn r#struct(&self) {
- | ^^^^^^^^^^^^^^^^^^
-note: candidate #2 is defined in an impl of the trait `await` for the type `fn`
- --> $DIR/issue-65634-raw-ident-suggestion.rs:10:5
- |
-LL | fn r#struct(&self) {
- | ^^^^^^^^^^^^^^^^^^
-help: disambiguate the associated function for candidate #1
- |
-LL | async::r#struct(&r#fn {});
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
-help: disambiguate the associated function for candidate #2
- |
-LL | await::r#struct(&r#fn {});
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0034`.
-error[E0282]: type annotations needed
- --> $DIR/issue-69455.rs:29:20
+error[E0284]: type annotations needed
+ --> $DIR/issue-69455.rs:29:41
|
LL | println!("{}", 23u64.test(xs.iter().sum()));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the associated function `new_display`
+ | ---- ^^^ cannot infer type of the type parameter `S` declared on the associated function `sum`
+ | |
+ | type must be known at this point
|
- = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: cannot satisfy `<u64 as Test<_>>::Output == _`
help: consider specifying the generic argument
|
-LL | println!("{}", 23u64.test(xs.iter().sum())::<T>);
- | +++++
+LL | println!("{}", 23u64.test(xs.iter().sum::<S>()));
+ | +++++
error[E0283]: type annotations needed
--> $DIR/issue-69455.rs:29:41
error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0282, E0283.
-For more information about an error, try `rustc --explain E0282`.
+Some errors have detailed explanations: E0283, E0284.
+For more information about an error, try `rustc --explain E0283`.
| ^^^^^^^^^^^^^^^^^^^ `RefCell<isize>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `RefCell<isize>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
= note: required for `Unique<RefCell<isize>>` to implement `Sync`
= note: required because it appears within the type `Box<RefCell<isize>>`
= note: shared static variables must have a type that implements `Sync`
--> $DIR/issue-77919.rs:11:63
|
LL | impl<N, M> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
- | - ^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `, VAL`
+ | ^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | impl<N, M, VAL> TypeVal<usize> for Multiply<N, M> where N: TypeVal<VAL> {}
+ | +++++
error[E0046]: not all trait items implemented, missing: `VAL`
--> $DIR/issue-77919.rs:11:1
--> $DIR/issue-78622.rs:5:5
|
LL | S::A::<f> {}
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A`
+ | ^^^^
+ |
+help: if there were a trait named `Example` with associated type `A` implemented for `S`, you could use the fully-qualified path
+ |
+LL | <S as Example>::A::<f> {}
+ | ~~~~~~~~~~~~~~~~~
error: aborting due to previous error
error[E0412]: cannot find type `dyn` in this scope
--> $DIR/issue-86756.rs:5:10
|
-LL | fn eq<A, B>() {
- | - help: you might be missing a type parameter: `, dyn`
LL | eq::<dyn, Foo>
| ^^^ not found in this scope
--- /dev/null
+// #106728
+
+fn main() {
+ for i in 0.2 {
+ //~^ ERROR `{float}` is not an iterator
+ //~| `{float}` is not an iterator
+ //~| NOTE in this expansion of desugaring of `for` loop
+ //~| NOTE in this expansion of desugaring of `for` loop
+ //~| NOTE in this expansion of desugaring of `for` loop
+ //~| NOTE in this expansion of desugaring of `for` loop
+ //~| 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`
+ //~| NOTE required for `{float}` to implement `IntoIterator`
+ println!();
+ }
+}
--- /dev/null
+error[E0277]: `{float}` is not an iterator
+ --> $DIR/float_iterator_hint.rs:4:14
+ |
+LL | for i in 0.2 {
+ | ^^^ `{float}` is not an iterator
+ |
+ = help: the trait `Iterator` is not implemented for `{float}`
+ = 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`
+ = note: required for `{float}` to implement `IntoIterator`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
| ^^^^ `{float}` is not an iterator
|
= help: the trait `Iterator` is not implemented for `{float}`
+ = 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`
= note: required for `{float}` to implement `IntoIterator`
error: aborting due to 12 previous errors
--- /dev/null
+fn main() {
+ let x = Some(()).iter().map(|()| 1).sum::<f32>();
+ //~^ ERROR a value of type `f32` cannot be made by summing an iterator over elements of type `{integer}`
+}
--- /dev/null
+error[E0277]: a value of type `f32` cannot be made by summing an iterator over elements of type `{integer}`
+ --> $DIR/invalid-iterator-chain-with-int-infer.rs:2:41
+ |
+LL | let x = Some(()).iter().map(|()| 1).sum::<f32>();
+ | ^^^ value of type `f32` cannot be made by summing a `std::iter::Iterator<Item={integer}>`
+ |
+ = help: the trait `Sum<{integer}>` is not implemented for `f32`
+ = help: the following other types implement trait `Sum<A>`:
+ <f32 as Sum<&'a f32>>
+ <f32 as Sum>
+note: the method call chain might not have had the expected associated types
+ --> $DIR/invalid-iterator-chain-with-int-infer.rs:2:29
+ |
+LL | let x = Some(()).iter().map(|()| 1).sum::<f32>();
+ | -------- ------ ^^^^^^^^^^^ `Iterator::Item` changed to `{integer}` here
+ | | |
+ | | `Iterator::Item` is `&()` here
+ | this expression has type `Option<()>`
+note: required by a bound in `std::iter::Iterator::sum`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+error: parameter 2 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:75:38
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: i8, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ^^ help: change the type from `i8` to `isize`
+
+error: aborting due to previous error
+
--- /dev/null
+error: parameter 3 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:89:52
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: u8, _sigpipe: u8) -> isize {
+ | ^^ help: change the type from `u8` to `*const *const u8`
+
+error: aborting due to previous error
+
--- /dev/null
+error: parameter 3 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:82:52
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const usize, _sigpipe: u8) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: change the type from `*const *const usize` to `*const *const u8`
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
--- /dev/null
+error: parameter 1 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:61:20
+ |
+LL | fn start<T>(_main: fn(i32) -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ^^^^^^^^^^^^
+ |
+help: change the type from `fn(i32) -> T` to `fn() -> T`
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ~~~~~~~~~
+
+error: aborting due to previous error
+
--- /dev/null
+error: parameter 1 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:68:20
+ |
+LL | fn start<T>(_main: fn() -> u16, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ^^^^^^^^^^^
+ |
+help: change the type from `fn() -> u16` to `fn() -> T`
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ~~~~~~~~~
+
+error: aborting due to previous error
+
--- /dev/null
+error: parameter 1 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:54:20
+ |
+LL | fn start<T>(_main: u64, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ | ^^^ help: change the type from `u64` to `fn() -> T`
+
+error: aborting due to previous error
+
--- /dev/null
+error: incorrect number of parameters for the `start` lang item
+ --> $DIR/start_lang_item_args.rs:15:1
+ |
+LL | fn start<T>() -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `start` lang item should have four parameters, but found 0
+ = note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
+
+error: aborting due to previous error
+
--- /dev/null
+error: the return type of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:29:84
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
+ | ^ help: change the type from `()` to `isize`
+
+error: aborting due to previous error
+
--- /dev/null
+error: incorrect number of parameters for the `start` lang item
+ --> $DIR/start_lang_item_args.rs:22:1
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the `start` lang item should have four parameters, but found 3
+ = note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
+
+error: aborting due to previous error
+
--- /dev/null
+// check-fail
+// revisions: missing_all_args missing_sigpipe_arg missing_ret start_ret too_many_args
+// revisions: main_ty main_args main_ret argc argv_inner_ptr argv sigpipe
+
+#![feature(lang_items, no_core)]
+#![no_core]
+
+#[lang = "copy"]
+pub trait Copy {}
+#[lang = "sized"]
+pub trait Sized {}
+
+#[cfg(missing_all_args)]
+#[lang = "start"]
+fn start<T>() -> isize {
+ //[missing_all_args]~^ ERROR incorrect number of parameters
+ 100
+}
+
+#[cfg(missing_sigpipe_arg)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
+ //[missing_sigpipe_arg]~^ ERROR incorrect number of parameters
+ 100
+}
+
+#[cfg(missing_ret)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
+//[missing_ret]~^ ERROR the return type of the `start` lang item is incorrect
+
+#[cfg(start_ret)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> u8 {
+ //[start_ret]~^ ERROR the return type of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(too_many_args)]
+#[lang = "start"]
+fn start<T>(
+ //[too_many_args]~^ ERROR incorrect number of parameters
+ _main: fn() -> T,
+ _argc: isize,
+ _argv: *const *const u8,
+ _sigpipe: u8,
+ _extra_arg: (),
+) -> isize {
+ 100
+}
+
+#[cfg(main_ty)]
+#[lang = "start"]
+fn start<T>(_main: u64, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ //[main_ty]~^ ERROR parameter 1 of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(main_args)]
+#[lang = "start"]
+fn start<T>(_main: fn(i32) -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ //[main_args]~^ ERROR parameter 1 of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(main_ret)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> u16, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ //[main_ret]~^ ERROR parameter 1 of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(argc)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: i8, _argv: *const *const u8, _sigpipe: u8) -> isize {
+ //[argc]~^ ERROR parameter 2 of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(argv_inner_ptr)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const usize, _sigpipe: u8) -> isize {
+ //[argv_inner_ptr]~^ ERROR parameter 3 of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(argv)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: u8, _sigpipe: u8) -> isize {
+ //[argv]~^ ERROR parameter 3 of the `start` lang item is incorrect
+ 100
+}
+
+#[cfg(sigpipe)]
+#[lang = "start"]
+fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: i64) -> isize {
+ //[sigpipe]~^ ERROR parameter 4 of the `start` lang item is incorrect
+ 100
+}
+
+fn main() {}
--- /dev/null
+error: parameter 4 of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:96:80
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: i64) -> isize {
+ | ^^^ help: change the type from `i64` to `u8`
+
+error: aborting due to previous error
+
--- /dev/null
+error: the return type of the `start` lang item is incorrect
+ --> $DIR/start_lang_item_args.rs:34:87
+ |
+LL | fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8, _sigpipe: u8) -> u8 {
+ | ^^ help: change the type from `u8` to `isize`
+
+error: aborting due to previous error
+
--- /dev/null
+error: incorrect number of parameters for the `start` lang item
+ --> $DIR/start_lang_item_args.rs:41:1
+ |
+LL | / fn start<T>(
+LL | |
+LL | | _main: fn() -> T,
+LL | | _argc: isize,
+... |
+LL | | _extra_arg: (),
+LL | | ) -> isize {
+ | |__________^
+ |
+ = note: the `start` lang item should have four parameters, but found 5
+ = note: the `start` lang item should have the signature `fn(fn() -> T, isize, *const *const u8, u8) -> isize`
+
+error: aborting due to previous error
+
--- /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
--> $DIR/bare-trait-objects-path.rs:23:12
|
LL | let _: Dyn::Ty;
- | ^^^^^^^ help: use fully-qualified syntax: `<dyn Dyn as Trait>::Ty`
+ | ^^^^^^^ help: use the fully-qualified path: `<dyn Dyn as Assoc>::Ty`
warning: trait objects without an explicit `dyn` are deprecated
--> $DIR/bare-trait-objects-path.rs:14:5
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`.
--- /dev/null
+// This is a regression test for issue https://github.com/rust-lang/rust/issues/106629.
+// It ensures that transparent types where all fields are PhantomData are marked as
+// FFI-safe.
+
+// check-pass
+
+#[repr(transparent)]
+#[derive(Copy, Clone)]
+struct MyPhantom(core::marker::PhantomData<u8>);
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct Bar {
+ pub x: i32,
+ _marker: MyPhantom,
+}
+
+extern "C" {
+ pub fn foo(bar: *mut Bar);
+}
+
+fn main() {}
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`.
warning: 3 warnings emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | foo!(first)
+ | ----------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo`
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:24:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(second);
+ | ------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:29:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(third);
+ | ----------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:32:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(fourth);
+ | ------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:37:13
+ |
+LL | #[allow(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | foo!(warn_in_block)
+ | ------------------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo`
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:4:9
+ |
+LL | #![warn(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = foo!(warn_in_expr);
+ | ------------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:4:9
+ |
+LL | #![warn(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/semicolon-in-expressions-from-macros.rs:9:13
+ |
+LL | true;
+ | ^
+...
+LL | let _ = #[allow(semicolon_in_expressions_from_macros)] foo!(allow_does_not_work);
+ | ------------------------- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/semicolon-in-expressions-from-macros.rs:4:9
+ |
+LL | #![warn(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
warning: 1 warning emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/warn-semicolon-in-expressions-from-macros.rs:6:13
+ |
+LL | true;
+ | ^
+...
+LL | _ => foo!()
+ | ------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
--- /dev/null
+#![warn(unused)]
+#![deny(warnings)]
+
+fn main() {
+ let _x: ([u32; 3]); //~ ERROR unnecessary parentheses around type
+ let _y: [u8; (3)]; //~ ERROR unnecessary parentheses around const expression
+ let _z: ([u8; (3)]);
+ //~^ ERROR unnecessary parentheses around const expression
+ //~| ERROR unnecessary parentheses around type
+
+}
--- /dev/null
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-array-lint.rs:5:13
+ |
+LL | let _x: ([u32; 3]);
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-105061-array-lint.rs:2:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(warnings)]`
+help: remove these parentheses
+ |
+LL - let _x: ([u32; 3]);
+LL + let _x: [u32; 3];
+ |
+
+error: unnecessary parentheses around const expression
+ --> $DIR/issue-105061-array-lint.rs:6:18
+ |
+LL | let _y: [u8; (3)];
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - let _y: [u8; (3)];
+LL + let _y: [u8; 3];
+ |
+
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-array-lint.rs:7:13
+ |
+LL | let _z: ([u8; (3)]);
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - let _z: ([u8; (3)]);
+LL + let _z: [u8; (3)];
+ |
+
+error: unnecessary parentheses around const expression
+ --> $DIR/issue-105061-array-lint.rs:7:19
+ |
+LL | let _z: ([u8; (3)]);
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - let _z: ([u8; (3)]);
+LL + let _z: ([u8; 3]);
+ |
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+#![warn(unused)]
+#![deny(warnings)]
+
+struct Inv<'a>(&'a mut &'a ());
+
+trait Trait<'a> {}
+impl<'b> Trait<'b> for for<'a> fn(Inv<'a>) {}
+
+fn with_bound()
+where
+ for<'b> (for<'a> fn(Inv<'a>)): Trait<'b>, //~ ERROR unnecessary parentheses around type
+{}
+
+trait Hello<T> {}
+fn with_dyn_bound<T>()
+where
+ (dyn Hello<(for<'b> fn(&'b ()))>): Hello<T> //~ ERROR unnecessary parentheses around type
+{}
+
+fn main() {
+ with_bound();
+ with_dyn_bound();
+}
--- /dev/null
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-should-lint.rs:11:13
+ |
+LL | for<'b> (for<'a> fn(Inv<'a>)): Trait<'b>,
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-105061-should-lint.rs:2:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(warnings)]`
+help: remove these parentheses
+ |
+LL - for<'b> (for<'a> fn(Inv<'a>)): Trait<'b>,
+LL + for<'b> for<'a> fn(Inv<'a>): Trait<'b>,
+ |
+
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061-should-lint.rs:17:16
+ |
+LL | (dyn Hello<(for<'b> fn(&'b ()))>): Hello<T>
+ | ^ ^
+ |
+help: remove these parentheses
+ |
+LL - (dyn Hello<(for<'b> fn(&'b ()))>): Hello<T>
+LL + (dyn Hello<for<'b> fn(&'b ())>): Hello<T>
+ |
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![warn(unused)]
+#![deny(warnings)]
+
+struct Inv<'a>(&'a mut &'a ());
+
+trait Trait {}
+impl Trait for (for<'a> fn(Inv<'a>),) {}
+
+
+fn with_bound()
+where
+ ((for<'a> fn(Inv<'a>)),): Trait, //~ ERROR unnecessary parentheses around type
+{}
+
+fn main() {
+ with_bound();
+}
--- /dev/null
+error: unnecessary parentheses around type
+ --> $DIR/issue-105061.rs:12:6
+ |
+LL | ((for<'a> fn(Inv<'a>)),): Trait,
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-105061.rs:2:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(warnings)]`
+help: remove these parentheses
+ |
+LL - ((for<'a> fn(Inv<'a>)),): Trait,
+LL + (for<'a> fn(Inv<'a>),): Trait,
+ |
+
+error: aborting due to previous error
+
if { return } {
}
+
+ // regression test for https://github.com/rust-lang/rust/issues/106899
+ return println!("!");
+ //~^ WARN unnecessary braces
}
if { return } {
}
+
+ // regression test for https://github.com/rust-lang/rust/issues/106899
+ return { println!("!") };
+ //~^ WARN unnecessary braces
}
LL + consume(7);
|
-warning: 5 warnings emitted
+warning: unnecessary braces around `return` value
+ --> $DIR/unused_braces.rs:55:12
+ |
+LL | return { println!("!") };
+ | ^^ ^^
+ |
+help: remove these braces
+ |
+LL - return { println!("!") };
+LL + return println!("!");
+ |
+
+warning: 6 warnings emitted
error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:41:27
|
+LL | let mutex = Mutex;
+ | ----- binding `mutex` declared here
LL | write!(Out, "{}", mutex.lock()) /* no semicolon */
| ^^^^^^^^^^^^
| |
error[E0597]: `mutex` does not live long enough
--> $DIR/format-args-temporaries-in-write.rs:47:29
|
+LL | let mutex = Mutex;
+ | ----- binding `mutex` declared here
LL | writeln!(Out, "{}", mutex.lock()) /* no semicolon */
| ^^^^^^^^^^^^
| |
error: aborting due to previous error
+Future incompatibility report: Future breakage diagnostic:
+error: trailing semicolon in macro used in expression position
+ --> $DIR/issue-84195-lint-anon-const.rs:8:14
+ |
+LL | () => { 0; };
+ | ^
+...
+LL | let val: [u8; len!()] = [];
+ | ------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+note: the lint level is defined here
+ --> $DIR/issue-84195-lint-anon-const.rs:5:9
+ |
+LL | #![deny(semicolon_in_expressions_from_macros)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `len` (in Nightly builds, run with -Z macro-backtrace for more info)
+
#[derive(Bla)]
//~^ ERROR cannot find derive macro `Bla`
-//~| NOTE consider importing this derive macro
+//~| HELP consider importing this derive macro
struct A;
#[derive(println)]
fn main() {
bla!();
//~^ ERROR cannot find macro `bla`
- //~| NOTE consider importing this macro
+ //~| HELP consider importing this macro
}
LL | bla!();
| ^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
crate::hey::bla
error: cannot find derive macro `println` in this scope
LL | #[derive(Bla)]
| ^^^
|
- = note: consider importing this derive macro:
+ = help: consider importing this derive macro:
crate::hey::Bla
error: aborting due to 3 previous errors
warning: 1 warning emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/lint-trailing-macro-call.rs:9:25
+ |
+LL | #[cfg(FALSE)] 25;
+ | ^
+...
+LL | expand_it!()
+ | ------------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `expand_it`
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `expand_it` (in Nightly builds, run with -Z macro-backtrace for more info)
+
Some errors have detailed explanations: E0412, E0425.
For more information about an error, try `rustc --explain E0412`.
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/macro-context.rs:3:15
+ |
+LL | () => ( i ; typeof );
+ | ^
+...
+LL | let i = m!();
+ | ---- in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
error: aborting due to previous error; 1 warning emitted
+Future incompatibility report: Future breakage diagnostic:
+warning: trailing semicolon in macro used in expression position
+ --> $DIR/macro-in-expression-context.rs:5:29
+ |
+LL | assert_eq!("A", "A");
+ | ^
+...
+LL | foo!()
+ | ------ in this macro invocation
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #79813 <https://github.com/rust-lang/rust/issues/79813>
+ = note: macro invocations at the end of a block are treated as expressions
+ = note: to ignore the value produced by the macro, add a semicolon after the invocation of `foo`
+ = note: `#[warn(semicolon_in_expressions_from_macros)]` on by default
+ = note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
LL | macro_rules! macro_one { () => ("one") }
| ---------------------- similarly named macro `macro_one` defined here
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
two_macros::macro_two
error: aborting due to previous error
error[E0283]: type annotations needed: cannot satisfy `&(): Marker`
- --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:6
+ --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:17
|
LL | impl Marker for &'_ () {}
- | ^^^^^^
+ | ^^^^^^
|
note: multiple `impl`s satisfying `&(): Marker` found
--> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:1
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0283]: type annotations needed: cannot satisfy `&(): Marker`
- --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:7:6
+ --> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:7:17
|
LL | impl Marker for &'_ () {}
- | ^^^^^^
+ | ^^^^^^
|
note: multiple `impl`s satisfying `&(): Marker` found
--> $DIR/overlap-marker-trait-with-underscore-lifetime.rs:6:1
error[E0283]: type annotations needed: cannot satisfy `(&'static (), &'a ()): A`
- --> $DIR/region-overlap.rs:5:10
+ --> $DIR/region-overlap.rs:5:16
|
LL | impl<'a> A for (&'static (), &'a ()) {}
- | ^
+ | ^^^^^^^^^^^^^^^^^^^^^
|
note: multiple `impl`s satisfying `(&'static (), &'a ()): A` found
--> $DIR/region-overlap.rs:5:1
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0283]: type annotations needed: cannot satisfy `(&'a (), &'static ()): A`
- --> $DIR/region-overlap.rs:6:10
+ --> $DIR/region-overlap.rs:6:16
|
LL | impl<'a> A for (&'a (), &'static ()) {}
- | ^
+ | ^^^^^^^^^^^^^^^^^^^^^
|
note: multiple `impl`s satisfying `(&'a (), &'static ()): A` found
--> $DIR/region-overlap.rs:5:1
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
|
= note: expected type parameter `bool` (type parameter `bool`)
found type `bool` (`bool`)
+help: the type constructed contains `bool` due to the type of the argument passed
+ --> $DIR/issue-35030.rs:9:9
+ |
+LL | Some(true)
+ | ^^^^^----^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
--> $DIR/issue-36053-2.rs:7:32
|
LL | once::<&str>("str").fuse().filter(|a: &str| true).count();
- | ^^^^^^ ---------
- | | | |
- | | | help: consider borrowing the argument: `&&str`
- | | found signature defined here
+ | ^^^^^^ --------- found signature defined here
+ | |
| expected due to this
|
= note: expected closure signature `for<'a> fn(&'a &str) -> _`
found closure signature `for<'a> fn(&'a str) -> _`
note: required by a bound in `filter`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: consider borrowing the argument
+ |
+LL | once::<&str>("str").fuse().filter(|a: &&str| true).count();
+ | +
error[E0599]: the method `count` exists for struct `Filter<Fuse<Once<&str>>, [closure@issue-36053-2.rs:7:39]>`, but its trait bounds were not satisfied
--> $DIR/issue-36053-2.rs:7:55
|
= note: the following trait bounds were not satisfied:
`T: Eq`
+ `T: PartialEq`
+ which is required by `T: Eq`
`T: Hash`
help: consider restricting the type parameters to satisfy the trait bounds
|
LL | macro_two!();
| ^^^^^^^^^
|
- = note: consider importing this macro:
+ = help: consider importing this macro:
two_macros::macro_two
error: aborting due to previous error
error[E0505]: cannot move out of `mut_foo` because it is borrowed
--> $DIR/move-fn-self-receiver.rs:50:5
|
+LL | let mut mut_foo = Foo;
+ | ----------- binding `mut_foo` declared here
LL | let ret = mut_foo.use_mut_self();
| ---------------------- borrow of `mut_foo` occurs here
LL | mut_foo;
--> $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`.
note: impl defined here, but it is not `const`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error[E0658]: mutable references are not allowed in constants
--> $DIR/issue-52443.rs:9:21
| ^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to 6 previous errors; 1 warning emitted
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);
| --------------^^---------------
| | |
--> $DIR/dyn-trait-compatibility.rs:5:15
|
LL | type A2 = dyn<dyn, dyn>;
- | - ^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<dyn>`
+ | ^^^ not found in this scope
error[E0412]: cannot find type `dyn` in this scope
--> $DIR/dyn-trait-compatibility.rs:5:20
|
LL | type A2 = dyn<dyn, dyn>;
- | - ^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<dyn>`
+ | ^^^ not found in this scope
error[E0412]: cannot find type `dyn` in this scope
--> $DIR/dyn-trait-compatibility.rs:9:11
--> $DIR/dyn-trait-compatibility.rs:9:16
|
LL | type A3 = dyn<<dyn as dyn>::dyn>;
- | - ^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<dyn>`
+ | ^^^ not found in this scope
error: aborting due to 8 previous errors
enum Foo { Bar }
fn foo(x: impl Iterator<Item = Foo>) {
for <Foo>::Bar in x {}
- //~^ ERROR expected one of `move`, `static`, `|`
+ //~^ ERROR expected one of `const`, `move`, `static`, `|`
//~^^ ERROR `for<...>` binders for closures are experimental
}
-error: expected one of `move`, `static`, `|`, or `||`, found `::`
+error: expected one of `const`, `move`, `static`, `|`, or `||`, found `::`
--> $DIR/recover-quantified-closure.rs:9:14
|
LL | for <Foo>::Bar in x {}
- | ^^ expected one of `move`, `static`, `|`, or `||`
+ | ^^ expected one of `const`, `move`, `static`, `|`, or `||`
error[E0658]: `for<...>` binders for closures are experimental
--> $DIR/recover-quantified-closure.rs:2:5
--- /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`.
let y = 0;
//~^ ERROR unknown start of token: \u{37e}
//~^^ HELP Unicode character ';' (Greek Question Mark) looks like ';' (Semicolon), but it is not
+ let x = 0;
+ //~^ ERROR unknown start of token: \u{a0}
+ //~^^ NOTE character appears 3 more times
+ //~^^^ HELP Unicode character ' ' (No-Break Space) looks like ' ' (Space), but it is not
+ let _ = 1 ⩵ 2;
+ //~^ ERROR unknown start of token
+ //~^^ HELP Unicode character '⩵' (Two Consecutive Equals Signs) looks like '==' (Double Equals Sign), but it is not
}
LL | let y = 0;
| ~
-error: aborting due to previous error
+error: unknown start of token: \u{a0}
+ --> $DIR/unicode-chars.rs:5:5
+ |
+LL | let x = 0;
+ | ^^^^
+ |
+ = note: character appears 3 more times
+help: Unicode character ' ' (No-Break Space) looks like ' ' (Space), but it is not
+ |
+LL | let x = 0;
+ | ++++
+
+error: unknown start of token: \u{2a75}
+ --> $DIR/unicode-chars.rs:9:15
+ |
+LL | let _ = 1 ⩵ 2;
+ | ^
+ |
+help: Unicode character '⩵' (Two Consecutive Equals Signs) looks like '==' (Double Equals Sign), but it is not
+ |
+LL | let _ = 1 == 2;
+ | ~~
+
+error: aborting due to 3 previous errors
--- /dev/null
+fn main() {
+ let 5 = 6;
+ //~^ error refutable pattern in local binding [E0005]
+
+ let x @ 5 = 6;
+ //~^ error refutable pattern in local binding [E0005]
+}
--- /dev/null
+error[E0005]: refutable pattern in local binding
+ --> $DIR/issue-106552.rs:2:9
+ |
+LL | let 5 = 6;
+ | ^ patterns `i32::MIN..=4_i32` and `6_i32..=i32::MAX` not covered
+ |
+ = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+ = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+ = note: the matched value is of type `i32`
+help: you might want to use `if let` to ignore the variants that aren't matched
+ |
+LL | if let 5 = 6 { todo!() }
+ | ++ ~~~~~~~~~~~
+help: alternatively, you could prepend the pattern with an underscore to define a new named variable; identifiers cannot begin with digits
+ |
+LL | let _5 = 6;
+ | +
+
+error[E0005]: refutable pattern in local binding
+ --> $DIR/issue-106552.rs:5:9
+ |
+LL | let x @ 5 = 6;
+ | ^^^^^ patterns `i32::MIN..=4_i32` and `6_i32..=i32::MAX` not covered
+ |
+ = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
+ = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
+ = note: the matched value is of type `i32`
+help: you might want to use `let else` to handle the variants that aren't matched
+ |
+LL | let x @ 5 = 6 else { todo!() };
+ | ++++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0005`.
pub fn key(e: ::E) -> &'static str {
match e {
A => "A",
-//~^ WARN pattern binding `A` is named the same as one of the variants of the type `E`
+//~^ ERROR pattern binding `A` is named the same as one of the variants of the type `E`
B => "B", //~ ERROR: unreachable pattern
-//~^ WARN pattern binding `B` is named the same as one of the variants of the type `E`
+//~^ ERROR pattern binding `B` is named the same as one of the variants of the type `E`
}
}
}
-warning[E0170]: pattern binding `A` is named the same as one of the variants of the type `E`
+error[E0170]: pattern binding `A` is named the same as one of the variants of the type `E`
--> $DIR/issue-14221.rs:13:13
|
LL | A => "A",
| ^ help: to match on the variant, qualify the path: `E::A`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `B` is named the same as one of the variants of the type `E`
+error[E0170]: pattern binding `B` is named the same as one of the variants of the type `E`
--> $DIR/issue-14221.rs:15:13
|
LL | B => "B",
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error; 2 warnings emitted
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0170`.
// Test for issue #67776: binding named the same as enum variant
-// should report a warning even when matching against a reference type
-
-// check-pass
+// should report an error even when matching against a reference type
#![allow(unused_variables)]
#![allow(non_snake_case)]
fn fn1(e: Foo) {
match e {
Bar => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
Baz => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
}
}
fn fn2(e: &Foo) {
match e {
Bar => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
Baz => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
}
}
fn fn3(e: &mut &&mut Foo) {
match e {
Bar => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
Baz => {},
- //~^ WARNING named the same as one of the variants of the type `Foo`
+ //~^ ERROR named the same as one of the variants of the type `Foo`
}
}
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:17:9
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:15:9
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
|
- = note: `#[warn(bindings_with_variant_name)]` on by default
+ = note: `#[deny(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:19:9
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:17:9
|
LL | Baz => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:26:9
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:24:9
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:28:9
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:26:9
|
LL | Baz => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:35:9
+error[E0170]: pattern binding `Bar` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:33:9
|
LL | Bar => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Bar`
-warning[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
- --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:37:9
+error[E0170]: pattern binding `Baz` is named the same as one of the variants of the type `Foo`
+ --> $DIR/issue-67776-match-same-name-enum-variant-refs.rs:35:9
|
LL | Baz => {},
| ^^^ help: to match on the variant, qualify the path: `Foo::Baz`
-warning: 6 warnings emitted
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0170`.
error[E0505]: cannot move out of `arr[..]` because it is borrowed
--> $DIR/borrowck-move-ref-pattern.rs:8:24
|
+LL | let mut arr = [U, U, U, U, U];
+ | ------- binding `arr` declared here
LL | let hold_all = &arr;
| ---- borrow of `arr` occurs here
LL | let [ref _x0_hold, _x1, ref xs_hold @ ..] = arr;
|
LL | pub struct Bar(u8);
| ^^ private field
+help: consider making the field publicly accessible
+ |
+LL | pub struct Bar(pub u8);
+ | +++
error: aborting due to previous error
mod foo {
pub(crate) struct Foo(u8);
- pub(crate) struct Bar(pub u8, u8, Foo);
+ pub(crate) struct Bar(pub u8, pub(in crate::foo) u8, Foo);
pub(crate) fn make_bar() -> Bar {
Bar(1, 12, Foo(10))
| ^ ^^^^^^ private field
| |
| private field
+help: consider making the fields publicly accessible
+ |
+LL | pub(crate) struct Bar(pub u8, pub u8, pub Foo);
+ | ~~~ ~~~ +++
error[E0532]: cannot match against a tuple struct which contains private fields
--> $DIR/issue-75907.rs:15:19
|
LL | let Bar(x, y, Foo(z)) = make_bar();
| ^ private field
+help: consider making the field publicly accessible
+ |
+LL | pub(crate) struct Foo(pub u8);
+ | +++
error: aborting due to 2 previous errors
|
LL | pub struct A(());
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub ());
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:52:16
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:53:16
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `A` is private
--> $DIR/privacy5.rs:56:12
|
LL | pub struct A(());
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub ());
+ | +++
error[E0603]: tuple struct constructor `A` is private
--> $DIR/privacy5.rs:57:12
|
LL | pub struct A(());
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub ());
+ | +++
error[E0603]: tuple struct constructor `A` is private
--> $DIR/privacy5.rs:58:18
|
LL | pub struct A(());
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub ());
+ | +++
error[E0603]: tuple struct constructor `A` is private
--> $DIR/privacy5.rs:59:18
|
LL | pub struct A(());
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub ());
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:61:12
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:62:12
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:63:18
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:64:18
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:65:18
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:65:32
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:68:12
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:69:12
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:70:12
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:71:12
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:72:18
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:73:18
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:74:18
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:75:18
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `A` is private
--> $DIR/privacy5.rs:83:17
|
LL | pub struct A(());
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub ());
+ | +++
error[E0603]: tuple struct constructor `B` is private
--> $DIR/privacy5.rs:84:17
|
LL | pub struct B(isize);
| ^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct B(pub isize);
+ | +++
error[E0603]: tuple struct constructor `C` is private
--> $DIR/privacy5.rs:85:17
|
LL | pub struct C(pub isize, isize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the fields publicly accessible
+ |
+LL | pub struct C(pub isize, pub isize);
+ | ~~~ +++
error[E0603]: tuple struct constructor `A` is private
--> $DIR/privacy5.rs:90:20
--- /dev/null
+// run-rustfix
+mod a {
+ pub struct A(pub String);
+}
+
+mod b {
+ use crate::a::A;
+ pub fn x() {
+ A("".into()); //~ ERROR cannot initialize a tuple struct which contains private fields
+ }
+}
+fn main() {
+ a::A("a".into()); //~ ERROR tuple struct constructor `A` is private
+ b::x();
+}
--- /dev/null
+// run-rustfix
+mod a {
+ pub struct A(pub(self)String);
+}
+
+mod b {
+ use crate::a::A;
+ pub fn x() {
+ A("".into()); //~ ERROR cannot initialize a tuple struct which contains private fields
+ }
+}
+fn main() {
+ a::A("a".into()); //~ ERROR tuple struct constructor `A` is private
+ b::x();
+}
--- /dev/null
+error[E0603]: tuple struct constructor `A` is private
+ --> $DIR/suggest-making-field-public.rs:13:8
+ |
+LL | pub struct A(pub(self)String);
+ | --------------- a constructor is private if any of the fields is private
+...
+LL | a::A("a".into());
+ | ^ private tuple struct constructor
+ |
+note: the tuple struct constructor `A` is defined here
+ --> $DIR/suggest-making-field-public.rs:3:5
+ |
+LL | pub struct A(pub(self)String);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub String);
+ | ~~~
+
+error[E0423]: cannot initialize a tuple struct which contains private fields
+ --> $DIR/suggest-making-field-public.rs:9:9
+ |
+LL | A("".into());
+ | ^
+ |
+note: constructor is not visible here due to private fields
+ --> $DIR/suggest-making-field-public.rs:3:18
+ |
+LL | pub struct A(pub(self)String);
+ | ^^^^^^^^^^^^^^^ private field
+help: consider making the field publicly accessible
+ |
+LL | pub struct A(pub String);
+ | ~~~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0423, E0603.
+For more information about an error, try `rustc --explain E0423`.
LL | #[derive(GenHelperUse)]
| ^^^^^^^^^^^^
|
- = note: consider importing this attribute macro:
+ = help: consider importing this attribute macro:
empty_helper
= note: this error originates in the derive macro `GenHelperUse` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | gen_helper_use!();
| ----------------- in this macro invocation
|
- = note: consider importing this attribute macro:
+ = help: consider importing this attribute macro:
crate::empty_helper
= note: this error originates in the macro `gen_helper_use` (in Nightly builds, run with -Z macro-backtrace for more info)
// aux-build:expand-expr.rs
+// no-remap-src-base: check_expand_expr_file!() fails when enabled.
+
#![feature(concat_bytes)]
extern crate expand_expr;
// Check builtin macros can be expanded.
-expand_expr_is!(11u32, line!());
+expand_expr_is!(13u32, line!());
expand_expr_is!(24u32, column!());
expand_expr_is!("Hello, World!", concat!("Hello, ", "World", "!"));
error: expected one of `.`, `?`, or an operator, found `;`
- --> $DIR/expand-expr.rs:106:27
+ --> $DIR/expand-expr.rs:108:27
|
LL | expand_expr_fail!("string"; hello);
| ^ expected one of `.`, `?`, or an operator
error: expected expression, found `$`
- --> $DIR/expand-expr.rs:109:19
+ --> $DIR/expand-expr.rs:111:19
|
LL | expand_expr_fail!($);
| ^ expected expression
error: expected expression, found `$`
- --> $DIR/expand-expr.rs:38:23
+ --> $DIR/expand-expr.rs:40:23
|
LL | ($($t:tt)*) => { $($t)* };
| ^^^^ expected expression
error: expected expression, found `$`
- --> $DIR/expand-expr.rs:111:28
+ --> $DIR/expand-expr.rs:113:28
|
LL | expand_expr_fail!(echo_pm!($));
| ^ expected expression
error: macro expansion ignores token `hello` and any following
- --> $DIR/expand-expr.rs:115:47
+ --> $DIR/expand-expr.rs:117:47
|
LL | expand_expr_is!("string", echo_tts!("string"; hello));
| --------------------^^^^^- caused by the macro expansion here
| +
error: macro expansion ignores token `;` and any following
- --> $DIR/expand-expr.rs:116:44
+ --> $DIR/expand-expr.rs:118:44
|
LL | expand_expr_is!("string", echo_pm!("string"; hello));
| -----------------^------- caused by the macro expansion here
| +
error: recursion limit reached while expanding `recursive_expand!`
- --> $DIR/expand-expr.rs:124:16
+ --> $DIR/expand-expr.rs:126:16
|
LL | const _: u32 = recursive_expand!();
| ^^^^^^^^^^^^^^^^^^^
#[derive(generate_mod::CheckDerive)] //~ ERROR cannot find type `FromOutside` in this scope
//~| ERROR cannot find type `OuterDerive` in this scope
+ //~| WARN this was previously accepted
+ //~| WARN this was previously accepted
struct Z;
fn inner_block() {
#[derive(generate_mod::CheckDerive)] //~ ERROR cannot find type `FromOutside` in this scope
//~| ERROR cannot find type `OuterDerive` in this scope
+ //~| WARN this was previously accepted
+ //~| WARN this was previously accepted
struct InnerZ;
}
-#[derive(generate_mod::CheckDeriveLint)] //~ ERROR cannot find type `OuterDeriveLint` in this scope
- //~| ERROR cannot find type `FromOutside` in this scope
+#[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
struct W;
fn main() {}
LL | generate_mod::check!();
| ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
FromOutside
= note: this error originates in the macro `generate_mod::check` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | generate_mod::check!();
| ^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
Outer
= note: this error originates in the macro `generate_mod::check` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | #[generate_mod::check_attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
FromOutside
= note: this error originates in the attribute macro `generate_mod::check_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
LL | #[generate_mod::check_attr]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
|
- = note: consider importing this struct:
+ = help: consider importing this struct:
OuterAttr
= note: this error originates in the attribute macro `generate_mod::check_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `FromOutside` in this scope
+error: cannot find type `FromOutside` in this scope
--> $DIR/generate-mod.rs:16:10
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- FromOutside
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `OuterDerive` in this scope
+error: cannot find type `OuterDerive` in this scope
--> $DIR/generate-mod.rs:16:10
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- OuterDerive
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `FromOutside` in this scope
- --> $DIR/generate-mod.rs:21:14
+error: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:23:14
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- FromOutside
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `OuterDerive` in this scope
- --> $DIR/generate-mod.rs:21:14
+error: cannot find type `OuterDerive` in this scope
+ --> $DIR/generate-mod.rs:23:14
|
LL | #[derive(generate_mod::CheckDerive)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- OuterDerive
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
= note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `FromOutside` in this scope
- --> $DIR/generate-mod.rs:26:10
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0412`.
+Future incompatibility report: Future breakage diagnostic:
+error: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:16:10
|
-LL | #[derive(generate_mod::CheckDeriveLint)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- FromOutside
- = note: this error originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+error: cannot find type `OuterDerive` in this scope
+ --> $DIR/generate-mod.rs:16:10
+ |
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0412]: cannot find type `OuterDeriveLint` in this scope
- --> $DIR/generate-mod.rs:26:10
+Future breakage diagnostic:
+error: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:23:14
|
-LL | #[derive(generate_mod::CheckDeriveLint)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
|
- = note: consider importing this struct:
- OuterDeriveLint
- = note: this error originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 10 previous errors
+Future breakage diagnostic:
+error: cannot find type `OuterDerive` in this scope
+ --> $DIR/generate-mod.rs:23:14
+ |
+LL | #[derive(generate_mod::CheckDerive)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+ = note: `#[deny(proc_macro_derive_resolution_fallback)]` on by default
+ = note: this error originates in the derive macro `generate_mod::CheckDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: cannot find type `FromOutside` in this scope
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+note: the lint level is defined here
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+Future breakage diagnostic:
+warning: cannot find type `OuterDeriveLint` in this scope
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ names from parent modules are not accessible without an explicit import
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #83583 <https://github.com/rust-lang/rust/issues/83583>
+note: the lint level is defined here
+ --> $DIR/generate-mod.rs:30:10
+ |
+LL | #[derive(generate_mod::CheckDeriveLint)] // OK, lint is suppressed
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this warning originates in the derive macro `generate_mod::CheckDeriveLint` (in Nightly builds, run with -Z macro-backtrace for more info)
-For more information about this error, try `rustc --explain E0412`.
--- /dev/null
+// error-pattern: building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic
+// compile-flags: --crate-type proc-macro -Cpanic=abort
+// force-host
+// check-pass
--- /dev/null
+warning: building proc macro crate with `panic=abort` may crash the compiler should the proc-macro panic
+
+warning: 1 warning emitted
+
--- /dev/null
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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: aborting due to 8 previous errors
+
+Future incompatibility report: Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
--- /dev/null
+PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
+PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input }
+PRINT-DERIVE INPUT (DEBUG): TokenStream [
+ Ident {
+ ident: "enum",
+ span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0),
+ },
+ Ident {
+ ident: "ProceduralMasqueradeDummyType",
+ 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: $DIR/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:4:36: 14:2 (#0),
+ },
+]
+PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
+PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input }
+PRINT-DERIVE INPUT (DEBUG): TokenStream [
+ Ident {
+ ident: "enum",
+ span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0),
+ },
+ Ident {
+ ident: "ProceduralMasqueradeDummyType",
+ 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: $DIR/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:4:36: 14:2 (#0),
+ },
+]
--- /dev/null
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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: aborting due to 8 previous errors
+
+Future incompatibility report: Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
+Future breakage diagnostic:
+error: using an old version of `rental`
+ --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
+ |
+LL | enum ProceduralMasqueradeDummyType {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
+ = 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
+ = note: `#[deny(proc_macro_back_compat)]` on by default
+
--- /dev/null
+PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
+PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input }
+PRINT-DERIVE INPUT (DEBUG): TokenStream [
+ Ident {
+ ident: "enum",
+ span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0),
+ },
+ Ident {
+ ident: "ProceduralMasqueradeDummyType",
+ 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: $DIR/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:4:36: 14:2 (#0),
+ },
+]
+PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
+PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input }
+PRINT-DERIVE INPUT (DEBUG): TokenStream [
+ Ident {
+ ident: "enum",
+ span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0),
+ },
+ Ident {
+ ident: "ProceduralMasqueradeDummyType",
+ 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: $DIR/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:4:36: 14:2 (#0),
+ },
+]
// aux-build:test-macros.rs
// compile-flags: -Z span-debug
+// revisions: local remapped
+// [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
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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: aborting due to 8 previous errors
-
-Future incompatibility report: Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
-Future breakage diagnostic:
-error: using an old version of `rental`
- --> $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:6
- |
-LL | enum ProceduralMasqueradeDummyType {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = 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 #83125 <https://github.com/rust-lang/rust/issues/83125>
- = 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
- = note: `#[deny(proc_macro_back_compat)]` on by default
-
+++ /dev/null
-PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
-PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input }
-PRINT-DERIVE INPUT (DEBUG): TokenStream [
- Ident {
- ident: "enum",
- span: $DIR/pretty-print-hack/allsorts-rental-0.5.6/src/lib.rs:4:1: 4:5 (#0),
- },
- Ident {
- ident: "ProceduralMasqueradeDummyType",
- 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: $DIR/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:4:36: 14:2 (#0),
- },
-]
-PRINT-DERIVE INPUT (DISPLAY): enum ProceduralMasqueradeDummyType { Input, }
-PRINT-DERIVE RE-COLLECTED (DISPLAY): enum ProceduralMasqueradeDummyType { Input }
-PRINT-DERIVE INPUT (DEBUG): TokenStream [
- Ident {
- ident: "enum",
- span: $DIR/pretty-print-hack/rental-0.5.5/src/lib.rs:4:1: 4:5 (#0),
- },
- Ident {
- ident: "ProceduralMasqueradeDummyType",
- 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: $DIR/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:4:36: 14:2 (#0),
- },
-]
--> $DIR/qualified-path-params-2.rs:18:10
|
LL | type A = <S as Tr>::A::f<u8>;
- | ^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<<S as Tr>::A as Trait>::f`
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `f` implemented for `<S as Tr>::A`, you could use the fully-qualified path
+ |
+LL | type A = <<S as Tr>::A as Example>::f;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
error[E0597]: `x` does not live long enough
--> $DIR/do-not-suggest-adding-bound-to-opaque-type.rs:9:7
|
+LL | let x = ();
+ | - binding `x` declared here
LL | S(&x)
| --^^-
| | |
--- /dev/null
+// FIXME: This test should pass as the first two fields add implied bounds that
+// `'a` is equal to `'b` while the last one should simply use that fact. With
+// the current implementation this errors. We have to be careful as implied bounds
+// are only sound if they're also correctly checked.
+
+struct Inv<T>(*mut T); // `T` is invariant.
+type A = for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>);
+type B = for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>);
+
+fn main() {
+ let x: A = |_, _, _| ();
+ let y: B = x; //~ ERROR mismatched types
+ let _: A = y; //~ ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-implied.rs:12:16
+ |
+LL | let y: B = x;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>)`
+ found fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>)`
+
+error[E0308]: mismatched types
+ --> $DIR/higher-ranked-implied.rs:13:16
+ |
+LL | let _: A = y;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'a ()>)`
+ found fn pointer `for<'a, 'b> fn(Inv<&'a &'b ()>, Inv<&'b &'a ()>, Inv<&'b ()>)`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error[E0597]: `a` does not live long enough
--> $DIR/regions-addr-of-arg.rs:5:30
|
+LL | fn foo(a: isize) {
+ | - binding `a` declared here
LL | let _p: &'static isize = &a;
| -------------- ^^ borrowed value does not live long enough
| |
LL | fn call1<'a>(x: &'a usize) {
| -- lifetime `'a` defined here
...
+LL | let y: usize = 3;
+ | - binding `y` declared here
LL | let z: &'a & usize = &(&y);
| ----------- ^^^^ borrowed value does not live long enough
| |
error[E0597]: `x` does not live long enough
--> $DIR/regions-infer-proc-static-upvar.rs:10:13
|
+LL | let x = 3;
+ | - binding `x` declared here
LL | let y = &x;
| ^^ borrowed value does not live long enough
LL | / foo(move|| {
error[E0597]: `y` does not live long enough
--> $DIR/regions-nested-fns.rs:5:18
|
+LL | let y = 3;
+ | - binding `y` declared here
LL | let mut ay = &y;
| ^^ borrowed value does not live long enough
...
error[E0597]: `line` does not live long enough
--> $DIR/regions-pattern-typing-issue-19552.rs:5:14
|
+LL | let line = String::new();
+ | ---- binding `line` declared here
LL | match [&*line] {
| ^^^^ borrowed value does not live long enough
LL | [ word ] => { assert_static(word); }
--> $DIR/regions-pattern-typing-issue-19997.rs:7:13
|
LL | match (&a1,) {
- | --- borrow of `a1` occurs here
+ | --- `a1` is borrowed here
LL | (&ref b0,) => {
LL | a1 = &f;
- | ^^^^^^^ assignment to borrowed `a1` occurs here
+ | ^^^^^^^ `a1` is assigned to here but it was already borrowed
LL | drop(b0);
| -- borrow later used here
|
= note: expected fn item `extern "rust-intrinsic" fn(_) -> _ {likely}`
found fn item `extern "rust-intrinsic" fn(_) -> _ {unlikely}`
- = note: different `fn` items always have unique types, even if their signatures are the same
- = help: change the expected type to be function pointer `extern "rust-intrinsic" fn(bool) -> bool`
- = help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `likely as extern "rust-intrinsic" fn(bool) -> bool`
+ = note: different fn items have unique types, even if their signatures are the same
error: aborting due to 3 previous errors
+++ /dev/null
-// compile-flags: --remap-path-prefix={{src-base}}=remapped
-
-fn main() {
- // We cannot actually put an ERROR marker here because
- // the file name in the error message is not what the
- // test framework expects (since the filename gets remapped).
- // We still test the expected error in the stderr file.
- ferris
-}
+++ /dev/null
-error[E0425]: cannot find value `ferris` in this scope
- --> remapped/remap-path-prefix.rs:8:5
- |
-LL | ferris
- | ^^^^^^ not found in this scope
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0425`.
--> $DIR/issue-103202.rs:4:17
|
LL | fn f(self: &S::x) {}
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::x`
+ | ^^^^
+ |
+help: if there were a trait named `Example` with associated type `x` implemented for `S`, you could use the fully-qualified path
+ |
+LL | fn f(self: &<S as Example>::x) {}
+ | ~~~~~~~~~~~~~~~~~
error: aborting due to previous error
| ^^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error[E0015]: cannot call non-const fn `<Dim3 as Dim>::dim` in constants
--> $DIR/issue-39559-2.rs:16:15
| ^^^^^^^^^^^
|
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to 2 previous errors
mod foo {
- pub struct Bx(());
+ pub struct Bx(pub(in crate::foo) ());
}
mod bar {
note: tuple struct `foo::Bx` exists but is inaccessible
--> $DIR/issue-42944.rs:2:5
|
-LL | pub struct Bx(());
- | ^^^^^^^^^^^^^^^^^^ not accessible
+LL | pub struct Bx(pub(in crate::foo) ());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible
error[E0423]: cannot initialize a tuple struct which contains private fields
--> $DIR/issue-42944.rs:9:9
note: constructor is not visible here due to private fields
--> $DIR/issue-42944.rs:2:19
|
-LL | pub struct Bx(());
- | ^^ private field
+LL | pub struct Bx(pub(in crate::foo) ());
+ | ^^^^^^^^^^^^^^^^^^^^^ private field
+help: consider making the field publicly accessible
+ |
+LL | pub struct Bx(pub ());
+ | ~~~
error: aborting due to 2 previous errors
|
LL | pub(in m) struct Z(pub(in m::n) u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub(in m) struct Z(pub u8);
+ | ~~~
error[E0603]: tuple struct constructor `S` is private
--> $DIR/privacy-struct-ctor.rs:29:8
|
LL | pub struct S(u8);
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct S(pub u8);
+ | +++
error[E0603]: tuple struct constructor `S` is private
--> $DIR/privacy-struct-ctor.rs:31:19
|
LL | pub struct S(u8);
| ^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub struct S(pub u8);
+ | +++
error[E0603]: tuple struct constructor `Z` is private
--> $DIR/privacy-struct-ctor.rs:35:11
|
LL | pub(in m) struct Z(pub(in m::n) u8);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider making the field publicly accessible
+ |
+LL | pub(in m) struct Z(pub u8);
+ | ~~~
error[E0603]: tuple struct constructor `S` is private
--> $DIR/privacy-struct-ctor.rs:41:16
--> $DIR/borrowck-non-exhaustive.rs:12:11
|
LL | let y = &mut x;
- | ------ borrow of `x` occurs here
+ | ------ `x` is borrowed here
LL | match x {
| ^ use of borrowed `x`
...
help: consider importing one of these items instead
|
LL | use core::alloc;
- | ~~~~~~~~~~~~
-LL | use std::alloc;
| ~~~~~~~~~~~
+LL | use std::alloc;
+ | ~~~~~~~~~~
error: aborting due to previous error
--- /dev/null
+#![feature(do_not_recommend)]
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+const CONST: () = ();
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+static Static: () = ();
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+type Type = ();
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+enum Enum {
+}
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+extern {
+}
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+fn fun() {
+}
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+struct Struct {
+}
+
+#[do_not_recommend]
+//~^ `#[do_not_recommend]` can only be placed
+trait Trait {
+}
+
+#[do_not_recommend]
+impl Trait for i32 {
+}
+
+fn main() {
+}
--- /dev/null
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:3:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:7:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:11:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:15:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:20:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:25:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:30:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: `#[do_not_recommend]` can only be placed on trait implementations
+ --> $DIR/incorrect-locations.rs:35:1
+ |
+LL | #[do_not_recommend]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
+trait Foo {
+}
+
#[do_not_recommend]
//~^ ERROR the `#[do_not_recommend]` attribute is an experimental feature
-trait Foo {
+impl Foo for i32 {
}
fn main() {
error[E0658]: the `#[do_not_recommend]` attribute is an experimental feature
- --> $DIR/unstable-feature.rs:1:1
+ --> $DIR/unstable-feature.rs:4:1
|
LL | #[do_not_recommend]
| ^^^^^^^^^^^^^^^^^^^
|
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
--- /dev/null
+// check-pass
+
+#![feature(const_closures, const_trait_impl)]
+#![allow(incomplete_features)]
+
+pub const _: () = {
+ assert!((const || true)());
+};
+
+fn main() {}
| ^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
= note: this error originates in the derive macro `Default` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
| ^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
--- /dev/null
+// gate-test-const_closures
+fn main() {
+ (const || {})();
+ //~^ ERROR: const closures are experimental
+}
--- /dev/null
+error[E0658]: const closures are experimental
+ --> $DIR/gate.rs:3:6
+ |
+LL | (const || {})();
+ | ^^^^^^^^^^^
+ |
+ = note: see issue #106003 <https://github.com/rust-lang/rust/issues/106003> for more information
+ = help: add `#![feature(const_closures)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+#![feature(const_closures, const_trait_impl)]
+#![allow(incomplete_features)]
+
+trait Foo {
+ fn foo(&self);
+}
+
+impl Foo for () {
+ fn foo(&self) {}
+}
+
+fn main() {
+ (const || { (()).foo() })();
+ //~^ ERROR: cannot call non-const fn
+}
--- /dev/null
+error[E0015]: cannot call non-const fn `<() as Foo>::foo` in constant functions
+ --> $DIR/non-const-op-const-closure-non-const-outer.rs:13:22
+ |
+LL | (const || { (()).foo() })();
+ | ^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
| ^^^^^^^^^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
| ^^^^^^^^^^^^^^^^^^
|
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable
error: aborting due to previous error
error[E0277]: the trait bound `S: ~const Foo` is not satisfied
- --> $DIR/super-traits-fail.rs:15:12
+ --> $DIR/super-traits-fail.rs:15:20
|
LL | impl const Bar for S {}
- | ^^^ the trait `~const Foo` is not implemented for `S`
+ | ^ the trait `~const Foo` is not implemented for `S`
|
note: the trait `Foo` is implemented for `S`, but that implementation is not `const`
- --> $DIR/super-traits-fail.rs:15:12
+ --> $DIR/super-traits-fail.rs:15:20
|
LL | impl const Bar for S {}
- | ^^^
+ | ^
note: required by a bound in `Bar`
--> $DIR/super-traits-fail.rs:8:12
|
| ---------------------------------- `#[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);
| ^^
| |
--> $DIR/self-impl.rs:23:16
|
LL | let _: <Self>::Baz = true;
- | ^^^^^^^^^^^ help: use fully-qualified syntax: `<Bar as Trait>::Baz`
+ | ^^^^^^^^^^^ help: use the fully-qualified path: `<Bar as Foo>::Baz`
error[E0223]: ambiguous associated type
--> $DIR/self-impl.rs:25:16
|
LL | let _: Self::Baz = true;
- | ^^^^^^^^^ help: use fully-qualified syntax: `<Bar as Trait>::Baz`
+ | ^^^^^^^^^ help: use the fully-qualified path: `<Bar as Foo>::Baz`
error: aborting due to 2 previous errors
help: consider importing this module instead
|
LL | use std::intrinsics;
- | ~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
--- /dev/null
+#![feature(decl_macro, rustc_attrs)]
+#![deny(single_use_lifetimes)]
+
+mod type_params {
+ macro m($T:ident) {
+ fn f<$T: Clone, T: PartialEq>(t1: $T, t2: T) -> ($T, bool) {
+ (t1.clone(), t2 == t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "semitransparent"]
+ macro n($T:ident) {
+ fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ fn h<T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ }
+
+ #[rustc_macro_transparency = "transparent"]
+ macro p($T:ident) {
+ fn j<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ fn k<T: Clone>(t1: $T, t2: T) -> (T, $T) {
+ (t1.clone(), t2.clone())
+ }
+ }
+
+ m!(T);
+ n!(T);
+ p!(T);
+}
+
+mod lifetime_params {
+ macro m($a:lifetime) {
+ fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "semitransparent"]
+ macro n($a:lifetime) {
+ fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ fn h<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "transparent"]
+ macro p($a:lifetime) {
+ fn j<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
+ (t1, t2)
+ }
+ }
+
+ m!('a); //~ ERROR lifetime parameter `'a` only used once
+ n!('a);
+ p!('a);
+}
+
+mod const_params {
+ macro m($C:ident) {
+ fn f<const $C: usize, const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); $C], [(); C]) {
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "semitransparent"]
+ macro n($C:ident) {
+ fn g<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ fn h<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ }
+
+ #[rustc_macro_transparency = "transparent"]
+ macro p($C:ident) {
+ fn j<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ fn k<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
+ (t1, t2)
+ }
+ }
+
+ m!(C);
+ n!(C);
+ p!(C);
+}
+
+fn main() {}
--- /dev/null
+error: lifetime parameter `'a` only used once
+ --> $DIR/issue-104440.rs:63:8
+ |
+LL | m!('a);
+ | ^^
+ | |
+ | this lifetime...
+ | ...is used only here
+ |
+note: the lint level is defined here
+ --> $DIR/issue-104440.rs:2:9
+ |
+LL | #![deny(single_use_lifetimes)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: lifetime parameter `'a` only used once
+ --> $DIR/issue-104440.rs:38:30
+ |
+LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) {
+ | ^^ this lifetime... -- ...is used only here
+...
+LL | m!('a);
+ | ------ in this macro invocation
+ |
+ = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
error[E0505]: cannot move out of `f` because it is borrowed
--> $DIR/borrowck-call-is-borrow-issue-12224.rs:55:16
|
+LL | let mut f = move |g: Box<dyn FnMut(isize)>, b: isize| {
+ | ----- binding `f` declared here
+...
LL | f(Box::new(|a| {
| - ^^^ move out of `f` occurs here
| |
{
let young = ['y']; // statement 3
+ //~^ NOTE binding `young` declared here
v2.push(&young[0]); // statement 4
//~^ ERROR `young[_]` does not live long enough
error[E0597]: `young[_]` does not live long enough
- --> $DIR/borrowck-let-suggestion-suffixes.rs:12:17
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:13:17
|
+LL | let young = ['y']; // statement 3
+ | ----- binding `young` declared here
+...
LL | v2.push(&young[0]); // statement 4
| ^^^^^^^^^ borrowed value does not live long enough
...
| -- borrow later used here
error[E0716]: temporary value dropped while borrowed
- --> $DIR/borrowck-let-suggestion-suffixes.rs:19:14
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:20:14
|
LL | v3.push(&id('x')); // statement 6
| ^^^^^^^ - temporary value is freed at the end of this statement
|
error[E0716]: temporary value dropped while borrowed
- --> $DIR/borrowck-let-suggestion-suffixes.rs:29:18
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:30:18
|
LL | v4.push(&id('y'));
| ^^^^^^^ - temporary value is freed at the end of this statement
= note: consider using a `let` binding to create a longer lived value
error[E0716]: temporary value dropped while borrowed
- --> $DIR/borrowck-let-suggestion-suffixes.rs:40:14
+ --> $DIR/borrowck-let-suggestion-suffixes.rs:41:14
|
LL | v5.push(&id('z'));
| ^^^^^^^ - temporary value is freed at the end of this statement
error[E0597]: `*a` does not live long enough
--> $DIR/destructor-restrictions.rs:8:10
|
+LL | let a = Box::new(RefCell::new(4));
+ | - binding `a` declared here
LL | *a.borrow() + 1
| ^^^^^^^^^^
| |
error[E0597]: `*m` does not live long enough
--> $DIR/dropck-object-cycle.rs:27:31
|
+LL | let m : Box<dyn Trait+'static> = make_val();
+ | - binding `m` declared here
LL | assert_eq!(object_invoke1(&*m), (4,5));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `b2` does not live long enough
--> $DIR/dropck_arr_cycle_checked.rs:93:24
|
+LL | let (b1, b2, b3);
+ | -- binding `b2` declared here
+...
LL | b1.a[0].v.set(Some(&b2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `b3` does not live long enough
--> $DIR/dropck_arr_cycle_checked.rs:95:24
|
+LL | let (b1, b2, b3);
+ | -- binding `b3` declared here
+...
LL | b1.a[1].v.set(Some(&b3));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `b1` does not live long enough
--> $DIR/dropck_arr_cycle_checked.rs:99:24
|
+LL | let (b1, b2, b3);
+ | -- binding `b1` declared here
+...
LL | b3.a[0].v.set(Some(&b1));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `d2` does not live long enough
--> $DIR/dropck_direct_cycle_with_drop.rs:36:19
|
+LL | let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2")));
+ | -- binding `d2` declared here
LL | d1.p.set(Some(&d2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `d1` does not live long enough
--> $DIR/dropck_direct_cycle_with_drop.rs:38:19
|
+LL | let (d1, d2) = (D::new(format!("d1")), D::new(format!("d2")));
+ | -- binding `d1` declared here
+...
LL | d2.p.set(Some(&d1));
| ^^^ borrowed value does not live long enough
LL |
error[E0597]: `bomb` does not live long enough
--> $DIR/dropck_misc_variants.rs:23:36
|
+LL | let (_w, bomb);
+ | ---- binding `bomb` declared here
+LL | bomb = vec![""];
LL | _w = Wrap::<&[&str]>(NoisyDrop(&bomb));
| ^^^^^ borrowed value does not live long enough
LL | }
error[E0597]: `v` does not live long enough
--> $DIR/dropck_misc_variants.rs:31:27
|
+LL | let (_w,v);
+ | - binding `v` declared here
+...
LL | let u = NoisyDrop(&v);
| ^^ borrowed value does not live long enough
...
error[E0597]: `c2` does not live long enough
--> $DIR/dropck_vec_cycle_checked.rs:98:24
|
+LL | let (mut c1, mut c2, mut c3);
+ | ------ binding `c2` declared here
+...
LL | c1.v[0].v.set(Some(&c2));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `c3` does not live long enough
--> $DIR/dropck_vec_cycle_checked.rs:100:24
|
+LL | let (mut c1, mut c2, mut c3);
+ | ------ binding `c3` declared here
+...
LL | c1.v[1].v.set(Some(&c3));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `c1` does not live long enough
--> $DIR/dropck_vec_cycle_checked.rs:104:24
|
+LL | let (mut c1, mut c2, mut c3);
+ | ------ binding `c1` declared here
+...
LL | c3.v[0].v.set(Some(&c1));
| ^^^ borrowed value does not live long enough
...
error[E0597]: `y` does not live long enough
--> $DIR/issue-23338-locals-die-before-temps-of-body.rs:10:5
|
+LL | let y = x;
+ | - binding `y` declared here
LL | y.borrow().clone()
| ^^^^^^^^^^
| |
error[E0597]: `y` does not live long enough
--> $DIR/issue-23338-locals-die-before-temps-of-body.rs:17:9
|
+LL | let y = x;
+ | - binding `y` declared here
LL | y.borrow().clone()
| ^^^^^^^^^^
| |
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-child-has-items-via-parent.rs:28:18
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+...
LL | _d = D_Child(&d1);
| ^^^ borrowed value does not live long enough
...
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-trait-has-items.rs:37:26
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+LL | d1 = D_HasSelfMethod(1);
LL | _d = D_HasSelfMethod(&d1);
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-trait-has-items.rs:43:33
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+LL | d1 = D_HasMethodWithSelfArg(1);
LL | _d = D_HasMethodWithSelfArg(&d1);
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24805-dropck-trait-has-items.rs:49:20
|
+LL | let (_d, d1);
+ | -- binding `d1` declared here
+LL | d1 = D_HasType(1);
LL | _d = D_HasType(&d1);
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `d1` does not live long enough
--> $DIR/issue-24895-copy-clone-dropck.rs:27:14
|
+LL | let (d2, d1);
+ | -- binding `d1` declared here
+LL | d1 = D(34, "d1");
LL | d2 = D(S(&d1, "inner"), "d2");
| ^^^ borrowed value does not live long enough
LL | }
error[E0597]: `container` does not live long enough
--> $DIR/issue-25199.rs:70:27
|
+LL | let container = Container::new();
+ | --------- binding `container` declared here
LL | let test = Test{test: &container};
| ^^^^^^^^^^ borrowed value does not live long enough
...
error[E0597]: `ticking` does not live long enough
--> $DIR/issue-26656.rs:40:35
|
+LL | let (mut zook, ticking);
+ | ------- binding `ticking` declared here
+...
LL | zook.button = B::BigRedButton(&ticking);
| ^^^^^^^^ borrowed value does not live long enough
LL | }
error[E0597]: `x` does not live long enough
--> $DIR/issue-29106.rs:16:26
|
+LL | let (y, x);
+ | - binding `x` declared here
+LL | x = "alive".to_string();
LL | y = Arc::new(Foo(&x));
| ^^ borrowed value does not live long enough
LL | }
error[E0597]: `x` does not live long enough
--> $DIR/issue-29106.rs:23:25
|
+LL | let (y, x);
+ | - binding `x` declared here
+LL | x = "alive".to_string();
LL | y = Rc::new(Foo(&x));
| ^^ borrowed value does not live long enough
LL | }
error[E0597]: `a` does not live long enough
--> $DIR/issue-36537.rs:5:13
|
+LL | let a = 42;
+ | - binding `a` declared here
LL | p = &a;
| ^^ borrowed value does not live long enough
...
--> $DIR/issue-40157.rs:2:53
|
LL | {println!("{:?}", match { let foo = vec![1, 2]; foo.get(1) } { x => x });}
- | ------------------------^^^^^^^^^^--
- | | | |
- | | | `foo` dropped here while still borrowed
- | | borrowed value does not live long enough
- | borrow later used here
+ | --- ^^^^^^^^^^ - `foo` dropped here while still borrowed
+ | | |
+ | | borrowed value does not live long enough
+ | binding `foo` declared here
error: aborting due to previous error
error[E0277]: `MyError` doesn't implement `std::fmt::Display`
- --> $DIR/issue-71363.rs:4:6
+ --> $DIR/issue-71363.rs:4:28
|
4 | impl std::error::Error for MyError {}
- | ^^^^^^^^^^^^^^^^^ `MyError` cannot be formatted with the default formatter
+ | ^^^^^^^ `MyError` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `MyError`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
--> $SRC_DIR/core/src/error.rs:LL:COL
error[E0277]: `MyError` doesn't implement `Debug`
- --> $DIR/issue-71363.rs:4:6
+ --> $DIR/issue-71363.rs:4:28
|
4 | impl std::error::Error for MyError {}
- | ^^^^^^^^^^^^^^^^^ `MyError` cannot be formatted using `{:?}`
+ | ^^^^^^^ `MyError` cannot be formatted using `{:?}`
|
= help: the trait `Debug` is not implemented for `MyError`
= note: add `#[derive(Debug)]` to `MyError` or manually `impl Debug for MyError`
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[E0277]: the trait bound `B: Clone` is not satisfied
- --> $DIR/issue-79224.rs:18:17
+ --> $DIR/issue-79224.rs:18:29
|
LL | impl<B: ?Sized> Display for Cow<'_, B> {
- | ^^^^^^^ the trait `Clone` is not implemented for `B`
+ | ^^^^^^^^^^ the trait `Clone` is not implemented for `B`
|
= note: required for `B` to implement `ToOwned`
help: consider further restricting this bound
--- /dev/null
+// #![feature(staged_api)] // note: `staged_api` not enabled
+
+#![stable(feature = "foo", since = "1.0.0")]
+//~^ ERROR stability attributes may not be used outside of the standard library
+
+#[unstable(feature = "foo", issue = "none")]
+//~^ ERROR stability attributes may not be used outside of the standard library
+fn foo_unstable() {}
+
+fn main() {}
--- /dev/null
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-106589.rs:6:1
+ |
+LL | #[unstable(feature = "foo", issue = "none")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0734]: stability attributes may not be used outside of the standard library
+ --> $DIR/issue-106589.rs:3:1
+ |
+LL | #![stable(feature = "foo", since = "1.0.0")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0734`.
error[E0597]: `x` does not live long enough
--> $DIR/static-lifetime-bound.rs:5:7
|
+LL | let x = 0;
+ | - binding `x` declared here
LL | f(&x);
| --^^-
| | |
--> $DIR/static-reference-to-fn-1.rs:17:15
|
LL | func: &foo,
- | ^^^^ expected fn pointer, found fn item
+ | ^^^^
+ | |
+ | expected fn pointer, found fn item
+ | help: consider casting to a fn pointer: `&(foo as fn() -> Option<isize>)`
|
= note: expected reference `&fn() -> Option<isize>`
found reference `&fn() -> Option<isize> {foo}`
+ = note: fn items are distinct from fn pointers
error: aborting due to previous error
| ^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `Cell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
note: required by a bound in `test`
--> $DIR/not-sync.rs:5:12
|
| ^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
|
= help: the trait `Sync` is not implemented for `RefCell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
note: required by a bound in `test`
--> $DIR/not-sync.rs:5:12
|
--> $DIR/struct-path-associated-type.rs:32:13
|
LL | let s = S::A {};
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A`
+ | ^^^^ help: use the fully-qualified path: `<S as Tr>::A`
error[E0223]: ambiguous associated type
--> $DIR/struct-path-associated-type.rs:33:13
|
LL | let z = S::A::<u8> {};
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A`
+ | ^^^^ help: use the fully-qualified path: `<S as Tr>::A`
error[E0223]: ambiguous associated type
--> $DIR/struct-path-associated-type.rs:35:9
|
LL | S::A {} => {}
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A`
+ | ^^^^ help: use the fully-qualified path: `<S as Tr>::A`
error: aborting due to 8 previous errors
| ^
= note: expected tuple `(i32, bool)`
found type `{integer}`
+help: the type constructed contains `{integer}` due to the type of the argument passed
+ --> $DIR/args-instead-of-tuple-errors.rs:6:34
+ |
+LL | let _: Option<(i32, bool)> = Some(1, 2);
+ | ^^^^^-^^^^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
help: remove the extra argument
|
= note: expected tuple `(i32,)`
found type `usize`
+help: the type constructed contains `usize` due to the type of the argument passed
+ --> $DIR/args-instead-of-tuple-errors.rs:14:29
+ |
+LL | let _: Option<(i32,)> = Some(5_usize);
+ | ^^^^^-------^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
|
= note: expected tuple `(i32,)`
found type `usize`
+help: the type constructed contains `usize` due to the type of the argument passed
+ --> $DIR/args-instead-of-tuple-errors.rs:17:29
+ |
+LL | let _: Option<(i32,)> = Some((5_usize));
+ | ^^^^^---------^
+ | |
+ | this argument influences the type of `Some`
note: tuple variant defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
error[E0505]: cannot move out of `a` because it is borrowed
--> $DIR/borrow-for-loop-head.rs:4:18
|
+LL | let a = vec![1, 2, 3];
+ | - binding `a` declared here
LL | for i in &a {
| -- borrow of `a` occurs here
LL | for j in a {
--- /dev/null
+trait Foo {}
+
+impl Foo for i32 {}
+
+fn needs_foo(_: impl Foo) {}
+
+fn test(x: &Box<dyn Fn() -> i32>) {
+ needs_foo(x);
+ //~^ ERROR the trait bound
+ //~| HELP use parentheses to call this trait object
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `&Box<dyn Fn() -> i32>: Foo` is not satisfied
+ --> $DIR/call-on-unimplemented-with-autoderef.rs:8:15
+ |
+LL | needs_foo(x);
+ | --------- ^ the trait `Foo` is not implemented for `&Box<dyn Fn() -> i32>`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `needs_foo`
+ --> $DIR/call-on-unimplemented-with-autoderef.rs:5:22
+ |
+LL | fn needs_foo(_: impl Foo) {}
+ | ^^^ required by this bound in `needs_foo`
+help: use parentheses to call this trait object
+ |
+LL | needs_foo(x());
+ | ++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--> $DIR/constrain-suggest-ice.rs:6:9
|
LL | F
- | ^
- |
-help: a local variable with a similar name exists
- |
-LL | x
- | ~
-help: you might be missing a type parameter
- |
-LL | struct Bug<S, F>{
- | +++
+ | ^ help: a local variable with a similar name exists: `x`
error: generic `Self` types are currently not permitted in anonymous constants
--> $DIR/constrain-suggest-ice.rs:3:21
--- /dev/null
+//run-rustfix
+#![allow(unused)]
+
+struct S;
+impl S {
+ fn foo(&mut self) {
+ let x = |this: &Self, v: i32| {
+ this.bar();
+ this.hel();
+ };
+ self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
+ x(self, 1);
+ x(self, 3);
+ }
+ fn bar(&self) {}
+ fn hel(&self) {}
+ fn qux(&mut self) {}
+
+ fn hello(&mut self) {
+ let y = |this: &Self| {
+ this.bar();
+ };
+ self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
+ y(self);
+ }
+}
+
+fn main() {}
--- /dev/null
+//run-rustfix
+#![allow(unused)]
+
+struct S;
+impl S {
+ fn foo(&mut self) {
+ let x = |v: i32| {
+ self.bar();
+ self.hel();
+ };
+ self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
+ x(1);
+ x(3);
+ }
+ fn bar(&self) {}
+ fn hel(&self) {}
+ fn qux(&mut self) {}
+
+ fn hello(&mut self) {
+ let y = || {
+ self.bar();
+ };
+ self.qux(); //~ ERROR cannot borrow `*self` as mutable because it is also borrowed as immutable
+ y();
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
+ --> $DIR/issue-105761-suggest-self-for-closure.rs:11:9
+ |
+LL | let x = |v: i32| {
+ | -------- immutable borrow occurs here
+LL | self.bar();
+ | ---- first borrow occurs due to use of `self` in closure
+...
+LL | self.qux();
+ | ^^^^^^^^^^ mutable borrow occurs here
+LL | x(1);
+ | - immutable borrow later used here
+ |
+help: try explicitly pass `&Self` into the Closure as an argument
+ |
+LL ~ let x = |this: &Self, v: i32| {
+LL ~ this.bar();
+LL ~ this.hel();
+LL | };
+LL | self.qux();
+LL ~ x(self, 1);
+LL ~ x(self, 3);
+ |
+
+error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
+ --> $DIR/issue-105761-suggest-self-for-closure.rs:23:9
+ |
+LL | let y = || {
+ | -- immutable borrow occurs here
+LL | self.bar();
+ | ---- first borrow occurs due to use of `self` in closure
+LL | };
+LL | self.qux();
+ | ^^^^^^^^^^ mutable borrow occurs here
+LL | y();
+ | - immutable borrow later used here
+ |
+help: try explicitly pass `&Self` into the Closure as an argument
+ |
+LL ~ let y = |this: &Self| {
+LL ~ this.bar();
+LL | };
+LL | self.qux();
+LL ~ y(self);
+ |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0502`.
#![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;
| ^
--> $DIR/let-binding-init-expr-as-ty.rs:2:14
|
LL | let foo: i32::from_be(num);
- | ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<i32 as Trait>::from_be`
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `from_be` implemented for `i32`, you could use the fully-qualified path
+ |
+LL | let foo: <i32 as Example>::from_be;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 3 previous errors
| -- 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);
| ---
|
= note: expected reference `&str`
found closure `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]`
+help: the return type of this call is `[closure@$DIR/sugg-else-for-closure.rs:6:26: 6:28]` due to the type of the argument passed
+ --> $DIR/sugg-else-for-closure.rs:6:14
+ |
+LL | let _s = y.unwrap_or(|| x.split('.').nth(1).unwrap());
+ | ^^^^^^^^^^^^-------------------------------^
+ | |
+ | this argument influences the return type of `unwrap_or`
note: associated function defined here
--> $SRC_DIR/core/src/option.rs:LL:COL
help: try calling `unwrap_or_else` instead
--- /dev/null
+// run-rustfix
+
+//issue #106496
+
+struct S;
+
+trait X {}
+impl X for S {}
+
+fn foo<T: X>(_: &T) {}
+fn test_foo() {
+ let hello = &S;
+ foo(hello);
+ //~^ ERROR mismatched types
+}
+
+fn bar(_: &String) {}
+fn test_bar() {
+ let v = String::from("hello");
+ let s = &v;
+ bar(s);
+ //~^ ERROR mismatched types
+}
+
+fn main() {
+ test_foo();
+ test_bar();
+}
--- /dev/null
+// run-rustfix
+
+//issue #106496
+
+struct S;
+
+trait X {}
+impl X for S {}
+
+fn foo<T: X>(_: &T) {}
+fn test_foo() {
+ let hello = &S;
+ foo(*hello);
+ //~^ ERROR mismatched types
+}
+
+fn bar(_: &String) {}
+fn test_bar() {
+ let v = String::from("hello");
+ let s = &v;
+ bar(*s);
+ //~^ ERROR mismatched types
+}
+
+fn main() {
+ test_foo();
+ test_bar();
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/suggest-remove-deref.rs:13:9
+ |
+LL | foo(*hello);
+ | --- ^^^^^^ expected reference, found struct `S`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected reference `&_`
+ found struct `S`
+note: function defined here
+ --> $DIR/suggest-remove-deref.rs:10:4
+ |
+LL | fn foo<T: X>(_: &T) {}
+ | ^^^ -----
+help: consider removing deref here
+ |
+LL - foo(*hello);
+LL + foo(hello);
+ |
+
+error[E0308]: mismatched types
+ --> $DIR/suggest-remove-deref.rs:21:9
+ |
+LL | bar(*s);
+ | --- ^^ expected `&String`, found struct `String`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/suggest-remove-deref.rs:17:4
+ |
+LL | fn bar(_: &String) {}
+ | ^^^ ----------
+help: consider removing deref here
+ |
+LL - bar(*s);
+LL + bar(s);
+ |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// Tests that a suggestion is issued for type mismatch errors when a
+// u8 is expected and a char literal which is ASCII is supplied.
+
+fn foo(_t: u8) {}
+
+fn main() {
+ let _x: u8 = 'X';
+ //~^ ERROR: mismatched types [E0308]
+ //~| HELP: if you meant to write a byte literal, prefix with `b`
+
+ foo('#');
+ //~^ ERROR: mismatched types [E0308]
+ //~| HELP: if you meant to write a byte literal, prefix with `b`
+
+ // Do not issue the suggestion if the char literal isn't ASCII
+ let _t: u8 = '€';
+ //~^ ERROR: mismatched types [E0308]
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch-byte-literal.rs:7:18
+ |
+LL | let _x: u8 = 'X';
+ | -- ^^^ expected `u8`, found `char`
+ | |
+ | expected due to this
+ |
+help: if you meant to write a byte literal, prefix with `b`
+ |
+LL | let _x: u8 = b'X';
+ | ~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch-byte-literal.rs:11:9
+ |
+LL | foo('#');
+ | --- ^^^ expected `u8`, found `char`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/type-mismatch-byte-literal.rs:4:4
+ |
+LL | fn foo(_t: u8) {}
+ | ^^^ ------
+help: if you meant to write a byte literal, prefix with `b`
+ |
+LL | foo(b'#');
+ | ~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch-byte-literal.rs:16:18
+ |
+LL | let _t: u8 = '€';
+ | -- ^^^ expected `u8`, found `char`
+ | |
+ | expected due to this
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error[E0412]: cannot find type `K` in this scope
--> $DIR/type-not-found-in-adt-field.rs:6:8
|
-LL | struct OtherStruct {
- | - help: you might be missing a type parameter: `<K>`
LL | m: K,
| ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | struct OtherStruct<K> {
+ | +++
error: aborting due to 2 previous errors
}
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]
--- /dev/null
+// MutexGuard<Cell<i32>> must not be Sync, that would be unsound.
+use std::sync::Mutex;
+use std::cell::Cell;
+
+fn test_sync<T: Sync>(_t: T) {}
+
+fn main()
+{
+ let m = Mutex::new(Cell::new(0i32));
+ let guard = m.lock().unwrap();
+ test_sync(guard);
+ //~^ ERROR `Cell<i32>` cannot be shared between threads safely [E0277]
+}
--- /dev/null
+error[E0277]: `Cell<i32>` cannot be shared between threads safely
+ --> $DIR/mutexguard-sync.rs:11:15
+ |
+LL | test_sync(guard);
+ | --------- ^^^^^ `Cell<i32>` cannot be shared between threads safely
+ | |
+ | required by a bound introduced by this call
+ |
+ = help: the trait `Sync` is not implemented for `Cell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
+ = note: required for `MutexGuard<'_, Cell<i32>>` to implement `Sync`
+note: required by a bound in `test_sync`
+ --> $DIR/mutexguard-sync.rs:5:17
+ |
+LL | fn test_sync<T: Sync>(_t: T) {}
+ | ^^^^ required by this bound in `test_sync`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+fn require_sync<T: Sync>() {}
+//~^ NOTE required by this bound in `require_sync`
+//~| NOTE required by this bound in `require_sync`
+//~| NOTE required by this bound in `require_sync`
+//~| NOTE required by this bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+
+fn main() {
+ require_sync::<std::cell::Cell<()>>();
+ //~^ ERROR `Cell<()>` cannot be shared between threads safely
+ //~| NOTE `Cell<()>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
+
+ require_sync::<std::cell::Cell<u8>>();
+ //~^ ERROR `Cell<u8>` cannot be shared between threads safely
+ //~| NOTE `Cell<u8>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead
+
+ require_sync::<std::cell::Cell<i32>>();
+ //~^ ERROR `Cell<i32>` cannot be shared between threads safely
+ //~| NOTE `Cell<i32>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
+
+ require_sync::<std::cell::Cell<bool>>();
+ //~^ ERROR `Cell<bool>` cannot be shared between threads safely
+ //~| NOTE `Cell<bool>` cannot be shared between threads safely
+ //~| NOTE if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead
+}
--- /dev/null
+error[E0277]: `Cell<()>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:12:20
+ |
+LL | require_sync::<std::cell::Cell<()>>();
+ | ^^^^^^^^^^^^^^^^^^^ `Cell<()>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<()>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error[E0277]: `Cell<u8>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:17:20
+ |
+LL | require_sync::<std::cell::Cell<u8>>();
+ | ^^^^^^^^^^^^^^^^^^^ `Cell<u8>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<u8>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicU8` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error[E0277]: `Cell<i32>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:22:20
+ |
+LL | require_sync::<std::cell::Cell<i32>>();
+ | ^^^^^^^^^^^^^^^^^^^^ `Cell<i32>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<i32>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicI32` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error[E0277]: `Cell<bool>` cannot be shared between threads safely
+ --> $DIR/suggest-cell.rs:27:20
+ |
+LL | require_sync::<std::cell::Cell<bool>>();
+ | ^^^^^^^^^^^^^^^^^^^^^ `Cell<bool>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `Cell<bool>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` or `std::sync::atomic::AtomicBool` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-cell.rs:1:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(once_cell)]
+
+fn require_sync<T: Sync>() {}
+//~^ NOTE required by this bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+
+fn main() {
+ require_sync::<std::cell::OnceCell<()>>();
+ //~^ ERROR `OnceCell<()>` cannot be shared between threads safely
+ //~| NOTE `OnceCell<()>` cannot be shared between threads safely
+ //~| NOTE use `std::sync::OnceLock` instead
+}
--- /dev/null
+error[E0277]: `OnceCell<()>` cannot be shared between threads safely
+ --> $DIR/suggest-once-cell.rs:8:20
+ |
+LL | require_sync::<std::cell::OnceCell<()>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ `OnceCell<()>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `OnceCell<()>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::OnceLock` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-once-cell.rs:3:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+#![feature(once_cell)]
+
+fn require_sync<T: Sync>() {}
+//~^ NOTE required by this bound in `require_sync`
+//~| NOTE required by a bound in `require_sync`
+
+fn main() {
+ require_sync::<std::cell::RefCell<()>>();
+ //~^ ERROR `RefCell<()>` cannot be shared between threads safely
+ //~| NOTE `RefCell<()>` cannot be shared between threads safely
+ //~| NOTE use `std::sync::RwLock` instead
+}
--- /dev/null
+error[E0277]: `RefCell<()>` cannot be shared between threads safely
+ --> $DIR/suggest-ref-cell.rs:8:20
+ |
+LL | require_sync::<std::cell::RefCell<()>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^ `RefCell<()>` cannot be shared between threads safely
+ |
+ = help: the trait `Sync` is not implemented for `RefCell<()>`
+ = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
+note: required by a bound in `require_sync`
+ --> $DIR/suggest-ref-cell.rs:3:20
+ |
+LL | fn require_sync<T: Sync>() {}
+ | ^^^^ required by this bound in `require_sync`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
help: consider importing this module instead
|
LL | use test::test as y;
- | ~~~~~~~~~~~~~~~~
+ | ~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
error[E0277]: the size for values of type `B` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:2:12
+ --> $DIR/unsized-bound.rs:2:30
|
LL | impl<A, B> Trait<(A, B)> for (A, B) where A: ?Sized, B: ?Sized, {}
- | - ^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^^^^^^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
|
error[E0277]: the size for values of type `C` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:5:31
+ --> $DIR/unsized-bound.rs:5:52
|
LL | impl<A, B: ?Sized, C: ?Sized> Trait<(A, B, C)> for (A, B, C) where A: ?Sized, {}
- | - ^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^^^^^^^^^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
|
error[E0277]: the size for values of type `B` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:10:28
+ --> $DIR/unsized-bound.rs:10:47
|
LL | impl<A: ?Sized, B: ?Sized> Trait2<(A, B)> for (A, B) {}
- | - ^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^^^^^^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
|
error[E0277]: the size for values of type `A` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:14:9
+ --> $DIR/unsized-bound.rs:14:23
|
LL | impl<A> Trait3<A> for A where A: ?Sized {}
- | - ^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
| ++++++++
error[E0277]: the size for values of type `A` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:17:17
+ --> $DIR/unsized-bound.rs:17:31
|
LL | impl<A: ?Sized> Trait4<A> for A {}
- | - ^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
| ++++++++
error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:20:12
+ --> $DIR/unsized-bound.rs:20:29
|
LL | impl<X, Y> Trait5<X, Y> for X where X: ?Sized {}
- | - ^^^^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
| ++++++++
error[E0277]: the size for values of type `X` cannot be known at compilation time
- --> $DIR/unsized-bound.rs:23:20
+ --> $DIR/unsized-bound.rs:23:37
|
LL | impl<X: ?Sized, Y> Trait6<X, Y> for X {}
- | - ^^^^^^^^^^^^ doesn't have a size known at compile-time
+ | - ^ doesn't have a size known at compile-time
| |
| this type parameter needs to be `std::marker::Sized`
|
error[E0597]: `s` does not live long enough
--> $DIR/check-trait-object-bounds-3.rs:15:34
|
+LL | let s = String::from("abcdef");
+ | - binding `s` declared here
LL | z = f::<dyn X<Y = &str>>(&s);
| ---------------------^^-
| | |
error[E0597]: `person` does not live long enough
--> $DIR/coercion-generic-regions.rs:17:24
|
+LL | let person = "Fred".to_string();
+ | ------ binding `person` declared here
LL | let person: &str = &person;
| ^^^^^^^
| |
LL | impl<T> Copy for Foo<T> {}
| ^^^^^^ the trait `TraitFoo` is not implemented for `T`
|
+note: required for `Foo<T>` to implement `Clone`
+ --> $DIR/copy-impl-cannot-normalize.rs:12:9
+ |
+LL | impl<T> Clone for Foo<T>
+ | ^^^^^ ^^^^^^
+LL | where
+LL | T: TraitFoo,
+ | -------- unsatisfied trait bound introduced here
+note: required by a bound in `Copy`
+ --> $SRC_DIR/core/src/marker.rs:LL:COL
help: consider restricting type parameter `T`
|
LL | impl<T: TraitFoo> Copy for Foo<T> {}
--- /dev/null
+error[E0204]: the trait `Copy` may not be implemented for this type
+ --> $DIR/copy-is-not-modulo-regions.rs:13:21
+ |
+LL | struct Bar<'lt>(Foo<'lt>);
+ | -------- this field does not implement `Copy`
+...
+LL | impl<'any> Copy for Bar<'any> {}
+ | ^^^^^^^^^
+ |
+note: the `Copy` impl for `Foo<'any>` requires that `'any: 'static`
+ --> $DIR/copy-is-not-modulo-regions.rs:10:17
+ |
+LL | struct Bar<'lt>(Foo<'lt>);
+ | ^^^^^^^^
+help: consider restricting type parameter `'any`
+ |
+LL | impl<'any: 'static> Copy for Bar<'any> {}
+ | +++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0204`.
--- /dev/null
+// revisions: not_static yes_static
+//[yes_static] check-pass
+
+#[derive(Clone)]
+struct Foo<'lt>(&'lt ());
+
+impl Copy for Foo<'static> {}
+
+#[derive(Clone)]
+struct Bar<'lt>(Foo<'lt>);
+
+#[cfg(not_static)]
+impl<'any> Copy for Bar<'any> {}
+//[not_static]~^ the trait `Copy` may not be implemented for this type
+
+#[cfg(yes_static)]
+impl<'any> Copy for Bar<'static> {}
+
+fn main() {}
--- /dev/null
+// check-pass
+
+#[derive(Clone)]
+struct A<'a, T>(&'a T);
+
+impl<'a, T: Copy + 'a> Copy for A<'a, T> {}
+
+#[derive(Clone)]
+struct B<'a, T>(A<'a, T>);
+
+// `T: '_` should be implied by `WF(B<'_, T>)`.
+impl<T: Copy> Copy for B<'_, T> {}
+
+fn main() {}
--- /dev/null
+// There are two different instances to check that even if
+// the trait is implemented for the output of a function,
+// it will still be displayed if the function itself implements a trait.
+trait Foo {}
+
+impl Foo for fn() -> bool {}
+impl Foo for bool {}
+
+fn example() -> bool {
+ true
+}
+
+trait NoOtherFoo {}
+
+impl NoOtherFoo for fn() -> bool {}
+
+fn do_on_foo(v: impl Foo) {}
+fn do_on_single_foo(v: impl NoOtherFoo) {}
+
+fn main() {
+ do_on_foo(example);
+ //~^ ERROR the trait bound
+
+ do_on_single_foo(example);
+ //~^ ERROR the trait bound
+}
--- /dev/null
+error[E0277]: the trait bound `fn() -> bool {example}: Foo` is not satisfied
+ --> $DIR/fn-trait-cast-diagnostic.rs:21:15
+ |
+LL | do_on_foo(example);
+ | --------- ^^^^^^^ the trait `Foo` is not implemented for fn item `fn() -> bool {example}`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `do_on_foo`
+ --> $DIR/fn-trait-cast-diagnostic.rs:17:22
+ |
+LL | fn do_on_foo(v: impl Foo) {}
+ | ^^^ required by this bound in `do_on_foo`
+help: use parentheses to call this function
+ |
+LL | do_on_foo(example());
+ | ++
+help: the trait `Foo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
+ |
+LL | do_on_foo(example as fn() -> bool);
+ | +++++++++++++++
+
+error[E0277]: the trait bound `fn() -> bool {example}: NoOtherFoo` is not satisfied
+ --> $DIR/fn-trait-cast-diagnostic.rs:24:22
+ |
+LL | do_on_single_foo(example);
+ | ---------------- ^^^^^^^ the trait `NoOtherFoo` is not implemented for fn item `fn() -> bool {example}`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `do_on_single_foo`
+ --> $DIR/fn-trait-cast-diagnostic.rs:18:29
+ |
+LL | fn do_on_single_foo(v: impl NoOtherFoo) {}
+ | ^^^^^^^^^^ required by this bound in `do_on_single_foo`
+help: the trait `NoOtherFoo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
+ |
+LL | do_on_single_foo(example as fn() -> bool);
+ | +++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--> $DIR/ignore-err-impls.rs:6:14
|
LL | impl Generic<Type> for S {}
- | - ^^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<Type>`
+ | ^^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | impl<Type> Generic<Type> for S {}
+ | ++++++
error: aborting due to previous error
error[E0277]: the trait bound `isize: Clone2` is not satisfied
- --> $DIR/impl-bounds-checking.rs:10:6
+ --> $DIR/impl-bounds-checking.rs:10:24
|
LL | impl Getter<isize> for isize {
- | ^^^^^^^^^^^^^ the trait `Clone2` is not implemented for `isize`
+ | ^^^^^ the trait `Clone2` is not implemented for `isize`
|
note: required by a bound in `Getter`
--> $DIR/impl-bounds-checking.rs:6:17
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'b` due to conflicting requirements
- --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:13
+ --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:28
|
LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> {
- | ^^^^^^^^^^
+ | ^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
--> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:6
LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> {
| ^^
note: ...so that the types are compatible
- --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:13
+ --> $DIR/impl-of-supertrait-has-wrong-lifetime-parameters.rs:24:28
|
LL | impl<'a,'b> T2<'a, 'b> for S<'a, 'b> {
- | ^^^^^^^^^^
+ | ^^^^^^^^^
= note: expected `T1<'a>`
found `T1<'_>`
--- /dev/null
+#[derive(Clone)] //~ trait objects must include the `dyn` keyword
+ //~| trait objects must include the `dyn` keyword
+struct Foo;
+trait Foo {} //~ the name `Foo` is defined multiple times
+fn main() {}
--- /dev/null
+error[E0428]: the name `Foo` is defined multiple times
+ --> $DIR/issue-106072.rs:4:1
+ |
+LL | struct Foo;
+ | ----------- previous definition of the type `Foo` here
+LL | trait Foo {}
+ | ^^^^^^^^^ `Foo` redefined here
+ |
+ = note: `Foo` must be defined only once in the type namespace of this module
+
+error[E0782]: trait objects must include the `dyn` keyword
+ --> $DIR/issue-106072.rs:1:10
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ |
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0782]: trait objects must include the `dyn` keyword
+ --> $DIR/issue-106072.rs:1:10
+ |
+LL | #[derive(Clone)]
+ | ^^^^^
+ |
+ = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0428, E0782.
+For more information about an error, try `rustc --explain E0428`.
error[E0277]: the trait bound `T: Copy` is not satisfied
- --> $DIR/issue-43784-supertrait.rs:8:9
+ --> $DIR/issue-43784-supertrait.rs:8:22
|
LL | impl<T> Complete for T {}
- | ^^^^^^^^ the trait `Copy` is not implemented for `T`
+ | ^ the trait `Copy` is not implemented for `T`
|
+note: required for `T` to implement `Partial`
+ --> $DIR/issue-43784-supertrait.rs:1:11
+ |
+LL | pub trait Partial: Copy {
+ | ^^^^^^^
note: required by a bound in `Complete`
--> $DIR/issue-43784-supertrait.rs:4:21
|
//~| ERROR cannot find type `NotDefined` in this scope
//~| ERROR cannot find type `N` in this scope
//~| ERROR cannot find type `N` in this scope
-//~| ERROR `i32` is not an iterator
#[derive(Clone, Copy)]
//~^ ERROR the trait `Copy` may not be implemented for this type
struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
//~^ ERROR cannot find type `NotDefined` in this scope
//~| ERROR cannot find type `N` in this scope
-//~| ERROR `i32` is not an iterator
fn main() {}
--> $DIR/issue-50480.rs:3:12
|
LL | struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | -^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<N>`
+ | ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | struct Foo<N>(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
+ | +++
error[E0412]: cannot find type `NotDefined` in this scope
--> $DIR/issue-50480.rs:3:15
--> $DIR/issue-50480.rs:3:12
|
LL | struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | -^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<N>`
+ | ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | struct Foo<N>(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
+ | +++
error[E0412]: cannot find type `NotDefined` in this scope
--> $DIR/issue-50480.rs:3:15
|
LL | struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | - ^^^^^^^^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<NotDefined>`
+ | ^^^^^^^^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | struct Foo<NotDefined>(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
+ | ++++++++++++
error[E0412]: cannot find type `N` in this scope
- --> $DIR/issue-50480.rs:12:18
+ --> $DIR/issue-50480.rs:11:18
|
LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| - ^
| +++
error[E0412]: cannot find type `NotDefined` in this scope
- --> $DIR/issue-50480.rs:12:21
+ --> $DIR/issue-50480.rs:11:21
|
LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
| ^^^^^^^^^^ not found in this scope
-error[E0277]: `i32` is not an iterator
- --> $DIR/issue-50480.rs:3:27
- |
-LL | struct Foo(N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator
- |
- = help: the trait `Iterator` is not implemented for `i32`
- = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end`
-
error[E0204]: the trait `Copy` may not be implemented for this type
--> $DIR/issue-50480.rs:1:17
|
|
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0277]: `i32` is not an iterator
- --> $DIR/issue-50480.rs:12:33
- |
-LL | struct Bar<T>(T, N, NotDefined, <i32 as Iterator>::Item, Vec<i32>, String);
- | ^^^^^^^^^^^^^^^^^^^^^^^ `i32` is not an iterator
- |
- = help: the trait `Iterator` is not implemented for `i32`
- = note: if you want to iterate between `start` until a value `end`, use the exclusive range syntax `start..end` or the inclusive range syntax `start..=end`
-
error[E0204]: the trait `Copy` may not be implemented for this type
- --> $DIR/issue-50480.rs:10:17
+ --> $DIR/issue-50480.rs:9:17
|
LL | #[derive(Clone, Copy)]
| ^^^^
|
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 10 previous errors
+error: aborting due to 8 previous errors
-Some errors have detailed explanations: E0204, E0277, E0412.
+Some errors have detailed explanations: E0204, E0412.
For more information about an error, try `rustc --explain E0204`.
|
= note: expected type parameter `F`
found struct `Class<P>`
+help: the return type of this call is `Class<P>` due to the type of the argument passed
+ --> $DIR/issue-52893.rs:53:9
+ |
+LL | builder.push(output);
+ | ^^^^^^^^^^^^^------^
+ | |
+ | this argument influences the return type of `push`
note: associated function defined here
--> $DIR/issue-52893.rs:11:8
|
--> $DIR/issue-75627.rs:3:26
|
LL | unsafe impl Send for Foo<T> {}
- | - ^ not found in this scope
- | |
- | help: you might be missing a type parameter: `<T>`
+ | ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | unsafe impl<T> Send for Foo<T> {}
+ | +++
error: aborting due to previous error
--> $DIR/issue-78372.rs:3:34
|
LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
- | - ^^^^ not found in this scope
- | |
- | help: you might be missing a type parameter: `, MISC`
+ | ^^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | impl<T, MISC> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | ++++++
error[E0658]: use of unstable library feature 'dispatch_from_dyn'
--> $DIR/issue-78372.rs:1:5
error[E0277]: the trait bound `Foo: HasComponent<()>` is not satisfied
- --> $DIR/issue-91594.rs:10:6
+ --> $DIR/issue-91594.rs:10:19
|
LL | impl HasComponent<<Foo as Component<Foo>>::Interface> for Foo {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasComponent<()>` is not implemented for `Foo`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HasComponent<()>` is not implemented for `Foo`
|
= help: the trait `HasComponent<<Foo as Component<Foo>>::Interface>` is implemented for `Foo`
note: required for `Foo` to implement `Component<Foo>`
| |
| required by a bound introduced by this call
|
- = help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`
note: required by a bound in `takes`
--> $DIR/issue-99875.rs:9:18
|
LL | fn takes(_: impl Trait) {}
| ^^^^^ required by this bound in `takes`
+help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, try casting using `as`
+ |
+LL | takes(function as fn(Argument) -> Return);
+ | +++++++++++++++++++++++++
error[E0277]: the trait bound `[closure@$DIR/issue-99875.rs:14:11: 14:34]: Trait` is not satisfied
--> $DIR/issue-99875.rs:14:11
--> $DIR/item-privacy.rs:115:12
|
LL | let _: S::A;
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::A`
+ | ^^^^
+ |
+help: if there were a trait named `Example` with associated type `A` implemented for `S`, you could use the fully-qualified path
+ |
+LL | let _: <S as Example>::A;
+ | ~~~~~~~~~~~~~~~~~
error[E0223]: ambiguous associated type
--> $DIR/item-privacy.rs:116:12
|
LL | let _: S::B;
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::B`
+ | ^^^^ help: use the fully-qualified path: `<S as assoc_ty::B>::B`
error[E0223]: ambiguous associated type
--> $DIR/item-privacy.rs:117:12
|
LL | let _: S::C;
- | ^^^^ help: use fully-qualified syntax: `<S as Trait>::C`
+ | ^^^^ help: use the fully-qualified path: `<S as assoc_ty::C>::C`
error[E0624]: associated type `A` is private
--> $DIR/item-privacy.rs:119:12
--- /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
+// compile-flags: -Ztrait-solver=next
+// check-pass
+#![feature(ptr_metadata)]
+
+use std::ptr::{DynMetadata, Pointee};
+
+trait Trait<U> {}
+struct MyDst<T: ?Sized>(T);
+
+fn works<T>() {
+ let _: <T as Pointee>::Metadata = ();
+ let _: <[T] as Pointee>::Metadata = 1_usize;
+ let _: <str as Pointee>::Metadata = 1_usize;
+ let _: <dyn Trait<T> as Pointee>::Metadata = give::<DynMetadata<dyn Trait<T>>>();
+ let _: <MyDst<T> as Pointee>::Metadata = ();
+ let _: <((((([u8],),),),),) as Pointee>::Metadata = 1_usize;
+}
+
+fn give<U>() -> U {
+ loop {}
+}
+
+fn main() {}
--- /dev/null
+#![feature(pointer_sized_trait)]
+
+use std::marker::PointerSized;
+
+fn require_pointer_sized(_: impl PointerSized) {}
+
+fn main() {
+ require_pointer_sized(1usize);
+ require_pointer_sized(1u16);
+ //~^ ERROR `u16` needs to be a pointer-sized type
+ require_pointer_sized(&1i16);
+}
--- /dev/null
+error[E0277]: `u16` needs to be a pointer-sized type
+ --> $DIR/pointer-sized.rs:9:27
+ |
+LL | require_pointer_sized(1u16);
+ | --------------------- ^^^^ the trait `PointerSized` is not implemented for `u16`
+ | |
+ | required by a bound introduced by this call
+ |
+ = note: the trait bound `u16: PointerSized` is not satisfied
+note: required by a bound in `require_pointer_sized`
+ --> $DIR/pointer-sized.rs:5:34
+ |
+LL | fn require_pointer_sized(_: impl PointerSized) {}
+ | ^^^^^^^^^^^^ required by this bound in `require_pointer_sized`
+help: consider borrowing here
+ |
+LL | require_pointer_sized(&1u16);
+ | +
+LL | require_pointer_sized(&mut 1u16);
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// These are simplifications of the tower traits by the same name:
+
+pub trait Service<Request> {
+ type Response;
+}
+
+pub trait Layer<C> {
+ type Service;
+}
+
+// Any type will do here:
+
+pub struct Req;
+pub struct Res;
+
+// This is encoding a trait alias.
+
+pub trait ParticularService:
+ Service<Req, Response = Res> {
+}
+
+impl<T> ParticularService for T
+where
+ T: Service<Req, Response = Res>,
+{
+}
+
+// This is also a trait alias.
+// The weird = <Self as ...> bound is there so that users of the trait do not
+// need to repeat the bounds. See https://github.com/rust-lang/rust/issues/20671
+// for context, and in particular the workaround in:
+// https://github.com/rust-lang/rust/issues/20671#issuecomment-529752828
+
+pub trait ParticularServiceLayer<C>:
+ Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
+{
+ type Service: ParticularService;
+}
+
+impl<T, C> ParticularServiceLayer<C> for T
+where
+ T: Layer<C>,
+ T::Service: ParticularService,
+{
+ type Service = T::Service;
+}
+
+// These are types that implement the traits that the trait aliases refer to.
+// They should also implement the alias traits due to the blanket impls.
+
+struct ALayer<C>(C);
+impl<C> Layer<C> for ALayer<C> {
+ type Service = AService;
+}
+
+struct AService;
+impl Service<Req> for AService {
+ // However, AService does _not_ meet the blanket implementation,
+ // since its Response type is bool, not Res as it should be.
+ type Response = bool;
+}
+
+// This is a wrapper type around ALayer that uses the trait alias
+// as a way to communicate the requirements of the provided types.
+struct Client<C>(C);
+
+// The method and the free-standing function below both have the same bounds.
+
+impl<C> Client<C>
+where
+ ALayer<C>: ParticularServiceLayer<C>,
+{
+ fn check(&self) {}
+}
+
+fn check<C>(_: C) where ALayer<C>: ParticularServiceLayer<C> {}
+
+// But, they give very different error messages.
+
+fn main() {
+ // This gives a very poor error message that does nothing to point the user
+ // at the underlying cause of why the types involved do not meet the bounds.
+ Client(()).check(); //~ ERROR E0599
+
+ // This gives a good(ish) error message that points the user at _why_ the
+ // bound isn't met, and thus how they might fix it.
+ check(()); //~ ERROR E0271
+}
--- /dev/null
+error[E0599]: the method `check` exists for struct `Client<()>`, but its trait bounds were not satisfied
+ --> $DIR/track-obligations.rs:83:16
+ |
+LL | struct ALayer<C>(C);
+ | ----------------
+ | |
+ | doesn't satisfy `<_ as Layer<()>>::Service = <ALayer<()> as ParticularServiceLayer<()>>::Service`
+ | doesn't satisfy `ALayer<()>: ParticularServiceLayer<()>`
+...
+LL | struct Client<C>(C);
+ | ---------------- method `check` not found for this struct
+...
+LL | Client(()).check();
+ | ^^^^^ method cannot be called on `Client<()>` due to unsatisfied trait bounds
+ |
+note: trait bound `<ALayer<()> as Layer<()>>::Service = <ALayer<()> as ParticularServiceLayer<()>>::Service` was not satisfied
+ --> $DIR/track-obligations.rs:35:14
+ |
+LL | pub trait ParticularServiceLayer<C>:
+ | ----------------------
+LL | Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
+note: trait bound `ALayer<()>: ParticularServiceLayer<()>` was not satisfied
+ --> $DIR/track-obligations.rs:71:16
+ |
+LL | impl<C> Client<C>
+ | ---------
+LL | where
+LL | ALayer<C>: ParticularServiceLayer<C>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound introduced here
+note: the trait `ParticularServiceLayer` must be implemented
+ --> $DIR/track-obligations.rs:34:1
+ |
+LL | / pub trait ParticularServiceLayer<C>:
+LL | | Layer<C, Service = <Self as ParticularServiceLayer<C>>::Service>
+ | |____________________________________________________________________^
+
+error[E0271]: type mismatch resolving `<AService as Service<Req>>::Response == Res`
+ --> $DIR/track-obligations.rs:87:11
+ |
+LL | check(());
+ | ----- ^^ type mismatch resolving `<AService as Service<Req>>::Response == Res`
+ | |
+ | required by a bound introduced by this call
+ |
+note: expected this to be `Res`
+ --> $DIR/track-obligations.rs:60:21
+ |
+LL | type Response = bool;
+ | ^^^^
+note: required for `AService` to implement `ParticularService`
+ --> $DIR/track-obligations.rs:22:9
+ |
+LL | impl<T> ParticularService for T
+ | ^^^^^^^^^^^^^^^^^ ^
+LL | where
+LL | T: Service<Req, Response = Res>,
+ | -------------- unsatisfied trait bound introduced here
+note: required for `ALayer<_>` to implement `ParticularServiceLayer<_>`
+ --> $DIR/track-obligations.rs:40:12
+ |
+LL | impl<T, C> ParticularServiceLayer<C> for T
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ ^
+...
+LL | T::Service: ParticularService,
+ | ----------------- unsatisfied trait bound introduced here
+note: required by a bound in `check`
+ --> $DIR/track-obligations.rs:76:36
+ |
+LL | fn check<C>(_: C) where ALayer<C>: ParticularServiceLayer<C> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `check`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0271, E0599.
+For more information about an error, try `rustc --explain E0271`.
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 |
error[E0412]: cannot find type `Dst` in this scope
--> $DIR/unknown_dst.rs:20:36
|
-LL | fn should_gracefully_handle_unknown_dst() {
- | - help: you might be missing a type parameter: `<Dst>`
-...
LL | assert::is_transmutable::<Src, Dst, Context>();
| ^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | fn should_gracefully_handle_unknown_dst<Dst>() {
+ | +++++
error: aborting due to previous error
error[E0412]: cannot find type `Src` in this scope
--> $DIR/unknown_src.rs:20:31
|
-LL | fn should_gracefully_handle_unknown_src() {
- | - help: you might be missing a type parameter: `<Src>`
-...
LL | assert::is_transmutable::<Src, Dst, Context>();
| ^^^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | fn should_gracefully_handle_unknown_src<Src>() {
+ | +++++
error: aborting due to previous error
LL | let result: Result<(), &str> = try {
| ------ borrow later stored here
LL | let my_string = String::from("");
+ | --------- binding `my_string` declared here
LL | let my_str: & str = & my_string;
| ^^^^^^^^^^^ borrowed value does not live long enough
...
--> $DIR/try-block-bad-lifetime.rs:29:13
|
LL | let k = &mut i;
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
...
LL | i = 10;
- | ^^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^^ `i` is assigned to here but it was already borrowed
LL | };
LL | ::std::mem::drop(k);
| - borrow later used here
--> $DIR/try-block-bad-lifetime.rs:32:9
|
LL | let k = &mut i;
- | ------ borrow of `i` occurs here
+ | ------ `i` is borrowed here
...
LL | i = 40;
- | ^^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^^ `i` is assigned to here but it was already borrowed
LL |
LL | let i_ptr = if let Err(i_ptr) = j { i_ptr } else { panic ! ("") };
| - borrow later used here
--> $DIR/try-block-maybe-bad-lifetime.rs:17:9
|
LL | &i
- | -- borrow of `i` occurs here
+ | -- `i` is borrowed here
LL | };
LL | i = 0;
- | ^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^ `i` is assigned to here but it was already borrowed
LL | let _ = i;
LL | do_something_with(x);
| - borrow later used here
--> $DIR/try-block-maybe-bad-lifetime.rs:40:9
|
LL | j = &i;
- | -- borrow of `i` occurs here
+ | -- `i` is borrowed here
LL | };
LL | i = 0;
- | ^^^^^ assignment to borrowed `i` occurs here
+ | ^^^^^ `i` is assigned to here but it was already borrowed
LL | let _ = i;
LL | do_something_with(j);
| - borrow later used here
fn foo_desugared<T: TraitWithAssoc>(_: T) -> Foo<T::Assoc> {
()
- //~^ ERROR non-defining opaque type use
+ //~^ ERROR expected generic type parameter, found `<T as TraitWithAssoc>::Assoc`
}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `<T as TraitWithAssoc>::Assoc`
--> $DIR/bound_reduction2.rs:16:5
|
+LL | type Foo<V> = impl Trait<V>;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | ()
| ^^
- |
-note: used non-generic type `<T as TraitWithAssoc>::Assoc` for generic parameter
- --> $DIR/bound_reduction2.rs:9:10
- |
-LL | type Foo<V> = impl Trait<V>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
type OneConst<const X: usize> = impl Debug;
-
// Not defining uses, because they doesn't define *all* possible generics.
fn concrete_ty() -> OneTy<u32> {
5u32
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic type parameter, found `u32`
}
fn concrete_lifetime() -> OneLifetime<'static> {
fn concrete_const() -> OneConst<{ 123 }> {
7u32
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic constant parameter, found `123`
}
-error: non-defining opaque type use in defining scope
- --> $DIR/generic_nondefining_use.rs:17:5
+error[E0792]: expected generic type parameter, found `u32`
+ --> $DIR/generic_nondefining_use.rs:16:5
|
+LL | type OneTy<T> = impl Debug;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | 5u32
| ^^^^
- |
-note: used non-generic type `u32` for generic parameter
- --> $DIR/generic_nondefining_use.rs:7:12
- |
-LL | type OneTy<T> = impl Debug;
- | ^
error: non-defining opaque type use in defining scope
- --> $DIR/generic_nondefining_use.rs:22:5
+ --> $DIR/generic_nondefining_use.rs:21:5
|
LL | type OneLifetime<'a> = impl Debug;
| -- cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
LL | 6u32
| ^^^^
-error: non-defining opaque type use in defining scope
- --> $DIR/generic_nondefining_use.rs:27:5
+error[E0792]: expected generic constant parameter, found `123`
+ --> $DIR/generic_nondefining_use.rs:26:5
|
+LL | type OneConst<const X: usize> = impl Debug;
+ | -------------- this generic parameter must be used with a generic constant parameter
+...
LL | 7u32
| ^^^^
- |
-note: used non-generic constant `123` for generic parameter
- --> $DIR/generic_nondefining_use.rs:11:15
- |
-LL | type OneConst<const X: usize> = impl Debug;
- | ^^^^^^^^^^^^^^
error: aborting due to 3 previous errors
+For more information about this error, try `rustc --explain E0792`.
let y = 42;
let x = wrong_generic(&y);
let z: i32 = x;
- //~^ ERROR non-defining opaque type use
+ //~^ ERROR expected generic type parameter, found `&'static i32
}
type WrongGeneric<T> = impl 'static;
LL | type WrongGeneric<T> = impl 'static;
| ^^^^^^^^^^^^
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `&'static i32`
--> $DIR/generic_type_does_not_live_long_enough.rs:6:18
|
LL | let z: i32 = x;
| ^
- |
-note: used non-generic type `&'static i32` for generic parameter
- --> $DIR/generic_type_does_not_live_long_enough.rs:10:19
- |
+...
LL | type WrongGeneric<T> = impl 'static;
- | ^
+ | - this generic parameter must be used with a generic type parameter
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/generic_type_does_not_live_long_enough.rs:14:5
error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0310`.
+Some errors have detailed explanations: E0310, E0792.
+For more information about an error, try `rustc --explain E0310`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+#![cfg_attr(specialized, feature(specialization))]
+#![allow(incomplete_features)]
+
+// revisions: stock specialized
+// [specialized]check-pass
+
+trait OpaqueTrait {}
+impl<T> OpaqueTrait for T {}
+type OpaqueType = impl OpaqueTrait;
+fn mk_opaque() -> OpaqueType {
+ || 0
+}
+trait AnotherTrait {}
+impl<T: Send> AnotherTrait for T {}
+impl AnotherTrait for OpaqueType {}
+//[stock]~^ conflicting implementations of trait `AnotherTrait` for type `OpaqueType`
+
+fn main() {}
--- /dev/null
+error[E0119]: conflicting implementations of trait `AnotherTrait` for type `OpaqueType`
+ --> $DIR/issue-104817.rs:16:1
+ |
+LL | impl<T: Send> AnotherTrait for T {}
+ | -------------------------------- first implementation here
+LL | impl AnotherTrait for OpaqueType {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `OpaqueType`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
type Bug<T, U> = impl Fn(T) -> U + Copy; //~ ERROR cycle detected
const CONST_BUG: Bug<u8, ()> = unsafe { std::mem::transmute(|_: u8| ()) };
-//~^ ERROR: cannot transmute
fn make_bug<T, U: From<T>>() -> Bug<T, U> {
|x| x.into() //~ ERROR the trait bound `U: From<T>` is not satisfied
LL | | }
| |_^
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
- --> $DIR/issue-53092-2.rs:6:41
- |
-LL | const CONST_BUG: Bug<u8, ()> = unsafe { std::mem::transmute(|_: u8| ()) };
- | ^^^^^^^^^^^^^^^^^^^
- |
- = note: source type: `[closure@$DIR/issue-53092-2.rs:6:61: 6:68]` (0 bits)
- = note: target type: `Bug<u8, ()>` (size can vary because of [type error])
-
error[E0277]: the trait bound `U: From<T>` is not satisfied
- --> $DIR/issue-53092-2.rs:10:5
+ --> $DIR/issue-53092-2.rs:9:5
|
LL | |x| x.into()
| ^^^^^^^^^^^^ the trait `From<T>` is not implemented for `U`
|
note: required by a bound in `make_bug`
- --> $DIR/issue-53092-2.rs:9:19
+ --> $DIR/issue-53092-2.rs:8:19
|
LL | fn make_bug<T, U: From<T>>() -> Bug<T, U> {
| ^^^^^^^ required by this bound in `make_bug`
LL | type Bug<T, U: std::convert::From<T>> = impl Fn(T) -> U + Copy;
| +++++++++++++++++++++++
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
-Some errors have detailed explanations: E0277, E0391, E0512.
+Some errors have detailed explanations: E0277, E0391.
For more information about an error, try `rustc --explain E0277`.
type BitsIter = IterBitsIter<T, E, u8>;
fn iter_bits(self, n: u8) -> Self::BitsIter {
(0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
- //~^ ERROR non-defining opaque type use in defining scope
+ //~^ ERROR expected generic type parameter, found `u8`
}
}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `u8`
--> $DIR/issue-60564.rs:20:9
|
+LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | (0u8..n).rev().map(move |shift| ((self >> T::from(shift)) & T::from(1)).try_into().unwrap())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: used non-generic type `u8` for generic parameter
- --> $DIR/issue-60564.rs:8:25
- |
-LL | type IterBitsIter<T, E, I> = impl std::iter::Iterator<Item = I>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
type Alias<'a, U> = impl Trait<U>;
fn f<'a>() -> Alias<'a, ()> {}
-//~^ ERROR non-defining opaque type use in defining scope
+//~^ ERROR expected generic type parameter, found `()`
fn main() {}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `()`
--> $DIR/issue-68368-non-defining-use-2.rs:9:29
|
+LL | type Alias<'a, U> = impl Trait<U>;
+ | - this generic parameter must be used with a generic type parameter
+LL |
LL | fn f<'a>() -> Alias<'a, ()> {}
| ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-68368-non-defining-use-2.rs:7:16
- |
-LL | type Alias<'a, U> = impl Trait<U>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
type Alias<'a, U> = impl Trait<U>;
fn f<'a>() -> Alias<'a, ()> {}
-//~^ ERROR non-defining opaque type use in defining scope
+//~^ ERROR expected generic type parameter, found `()`
fn main() {}
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `()`
--> $DIR/issue-68368-non-defining-use.rs:9:29
|
+LL | type Alias<'a, U> = impl Trait<U>;
+ | - this generic parameter must be used with a generic type parameter
+LL |
LL | fn f<'a>() -> Alias<'a, ()> {}
| ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-68368-non-defining-use.rs:7:16
- |
-LL | type Alias<'a, U> = impl Trait<U>;
- | ^
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0792`.
//~^ ERROR use of undeclared lifetime name `'a`
fn my_fun() -> Return<()> {}
-//~^ ERROR non-defining opaque type use in defining scope
+//~^ ERROR expected generic type parameter, found `()`
fn main() {}
LL | type Return<'a, A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
| +++
-error: non-defining opaque type use in defining scope
+error[E0792]: expected generic type parameter, found `()`
--> $DIR/issue-69136-inner-lifetime-resolve-error.rs:20:27
|
+LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
+ | - this generic parameter must be used with a generic type parameter
+...
LL | fn my_fun() -> Return<()> {}
| ^^
- |
-note: used non-generic type `()` for generic parameter
- --> $DIR/issue-69136-inner-lifetime-resolve-error.rs:17:13
- |
-LL | type Return<A> = impl WithAssoc<A, AssocType = impl SomeTrait + 'a>;
- | ^
error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0261`.
+Some errors have detailed explanations: E0261, E0792.
+For more information about an error, try `rustc --explain E0261`.
fn main() {
let _: foo::Foo = std::mem::transmute(0u8);
- //~^ ERROR cannot transmute between types of different sizes, or dependently-sized types
}
|
= note: `Foo` must be used in combination with a concrete type within the same module
-error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
- --> $DIR/no_inferrable_concrete_type.rs:17:23
- |
-LL | let _: foo::Foo = std::mem::transmute(0u8);
- | ^^^^^^^^^^^^^^^^^^^
- |
- = note: source type: `u8` (8 bits)
- = note: target type: `Foo` (size can vary because of [type error])
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-For more information about this error, try `rustc --explain E0512`.
--- /dev/null
+// Here we process outlive obligations involving
+// opaque types with bound vars in substs.
+// This was an ICE.
+//
+// check-pass
+#![feature(type_alias_impl_trait)]
+
+type Ty<'a> = impl Sized + 'a;
+fn define<'a>() -> Ty<'a> {}
+
+// Ty<'^0>: 'static
+fn test1(_: &'static fn(Ty<'_>)) {}
+
+fn test2() {
+ None::<&fn(Ty<'_>)>;
+}
+
+fn main() { }
--> $DIR/cannot_infer_local_or_vec_in_tuples.rs:2:9
|
LL | let (x, ) = (vec![], );
- | ^^^^^
+ | ^^^^^ ---------- type must be known at this point
|
help: consider giving this pattern a type, where the type for type parameter `T` is specified
|
--- /dev/null
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo4(x: Result<(), A>) -> Result<(), B> {
+ match true {
+ true => x, //~ ERROR mismatched types
+ false => x,
+ }
+}
+fn foo5(x: Result<(), A>) -> Result<(), B> {
+ match true {
+ true => return x, //~ ERROR mismatched types
+ false => return x,
+ }
+}
+fn main() {
+ let _ = foo4(Ok(()));
+ let _ = foo5(Ok(()));
+ let _: Result<(), B> = { //~ ERROR mismatched types
+ Err(A);
+ };
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value-2.rs:8:17
+ |
+LL | fn foo4(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | match true {
+LL | true => x,
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | true => Ok(x?),
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value-2.rs:14:24
+ |
+LL | fn foo5(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | match true {
+LL | true => return x,
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | true => return Ok(x?),
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value-2.rs:21:28
+ |
+LL | let _: Result<(), B> = {
+ | ____________________________^
+LL | | Err(A);
+LL | | };
+ | |_____^ expected enum `Result`, found `()`
+ |
+ = note: expected enum `Result<(), B>`
+ found unit type `()`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// run-rustfix
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+ Ok(x?) //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+ return Ok(x?); //~ ERROR mismatched types
+}
+fn foo3(x: Result<(), A>) -> Result<(), B> {
+ if true {
+ Ok(x?) //~ ERROR mismatched types
+ } else {
+ Ok(x?) //~ ERROR mismatched types
+ }
+}
+fn main() {
+ let _ = foo1(Ok(()));
+ let _ = foo2(Ok(()));
+ let _ = foo3(Ok(()));
+}
--- /dev/null
+// run-rustfix
+struct A;
+struct B;
+impl From<A> for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+ x //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+ return x; //~ ERROR mismatched types
+}
+fn foo3(x: Result<(), A>) -> Result<(), B> {
+ if true {
+ x //~ ERROR mismatched types
+ } else {
+ x //~ ERROR mismatched types
+ }
+}
+fn main() {
+ let _ = foo1(Ok(()));
+ let _ = foo2(Ok(()));
+ let _ = foo3(Ok(()));
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:8:5
+ |
+LL | fn foo1(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:11:12
+ |
+LL | fn foo2(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | return x;
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | return Ok(x?);
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:15:9
+ |
+LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | if true {
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:17:9
+ |
+LL | fn foo3(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+...
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: use `?` to coerce and return an appropriate `Err`, and wrap the resulting value in `Ok` so the expression remains of type `Result`
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+fn function<T>(x: T, y: bool) -> T {
+ x
+}
+
+struct S {}
+impl S {
+ fn method<T>(&self, x: T) -> T {
+ x
+ }
+}
+
+fn wrong_arg_type(x: u32) -> u32 {
+ x
+}
+
+fn main() {
+ // Should not trigger.
+ let x = wrong_arg_type(0u16); //~ ERROR mismatched types
+ let x: u16 = function(0, 0u8); //~ ERROR mismatched types
+
+ // Should trigger exactly once for the first argument.
+ let x: u16 = function(0u32, 0u8); //~ ERROR arguments to this function are incorrect
+
+ // Should trigger.
+ let x: u16 = function(0u32, true); //~ ERROR mismatched types
+ let x: u16 = (S {}).method(0u32); //~ ERROR mismatched types
+ function(0u32, 8u8) //~ ERROR arguments to this function are incorrect
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:18:28
+ |
+LL | let x = wrong_arg_type(0u16);
+ | -------------- ^^^^ expected `u32`, found `u16`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:12:4
+ |
+LL | fn wrong_arg_type(x: u32) -> u32 {
+ | ^^^^^^^^^^^^^^ ------
+help: change the type of the numeric literal from `u16` to `u32`
+ |
+LL | let x = wrong_arg_type(0u32);
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:19:30
+ |
+LL | let x: u16 = function(0, 0u8);
+ | -------- ^^^ expected `bool`, found `u8`
+ | |
+ | arguments to this function are incorrect
+ |
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ -------
+
+error[E0308]: arguments to this function are incorrect
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:22:18
+ |
+LL | let x: u16 = function(0u32, 0u8);
+ | ^^^^^^^^ ---- --- expected `bool`, found `u8`
+ | |
+ | expected `u16`, found `u32`
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:22:18
+ |
+LL | let x: u16 = function(0u32, 0u8);
+ | ^^^^^^^^^----^^^^^^
+ | |
+ | this argument influences the return type of `function`
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ ---- -------
+help: change the type of the numeric literal from `u32` to `u16`
+ |
+LL | let x: u16 = function(0u16, 0u8);
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:27
+ |
+LL | let x: u16 = function(0u32, true);
+ | -------- ^^^^ expected `u16`, found `u32`
+ | |
+ | arguments to this function are incorrect
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:25:18
+ |
+LL | let x: u16 = function(0u32, true);
+ | ^^^^^^^^^----^^^^^^^
+ | |
+ | this argument influences the return type of `function`
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ ----
+help: change the type of the numeric literal from `u32` to `u16`
+ |
+LL | let x: u16 = function(0u16, true);
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:32
+ |
+LL | let x: u16 = (S {}).method(0u32);
+ | ------ ^^^^ expected `u16`, found `u32`
+ | |
+ | arguments to this method are incorrect
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:26:18
+ |
+LL | let x: u16 = (S {}).method(0u32);
+ | ^^^^^^^^^^^^^^----^
+ | |
+ | this argument influences the return type of `method`
+note: associated function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:7:8
+ |
+LL | fn method<T>(&self, x: T) -> T {
+ | ^^^^^^ ----
+help: change the type of the numeric literal from `u32` to `u16`
+ |
+LL | let x: u16 = (S {}).method(0u16);
+ | ~~~
+
+error[E0308]: arguments to this function are incorrect
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5
+ |
+LL | function(0u32, 8u8)
+ | ^^^^^^^^ ---- --- expected `bool`, found `u8`
+ | |
+ | expected `()`, found `u32`
+ |
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:27:5
+ |
+LL | function(0u32, 8u8)
+ | ^^^^^^^^^----^^^^^^
+ | |
+ | this argument influences the return type of `function`
+note: function defined here
+ --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
+ |
+LL | fn function<T>(x: T, y: bool) -> T {
+ | ^^^^^^^^ ---- -------
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error[E0412]: cannot find type `T` in this scope
--> $DIR/autoderef-with-param-env-error.rs:3:5
|
-LL | fn foo()
- | - help: you might be missing a type parameter: `<T>`
-LL | where
LL | T: Send,
| ^ not found in this scope
+ |
+help: you might be missing a type parameter
+ |
+LL | fn foo<T>()
+ | +++
error: aborting due to previous error
error[E0405]: cannot find trait `Oops` in this scope
--> $DIR/issue-104513-ice.rs:3:19
|
-LL | fn f() {
- | - help: you might be missing a type parameter: `<Oops>`
LL | let _: S<impl Oops> = S;
| ^^^^ not found in this scope
|
= note: expected enum `Option<()>`
found unit type `()`
+help: the type constructed contains `()` due to the type of the argument passed
+ --> $DIR/issue-46112.rs:9:18
+ |
+LL | fn main() { test(Ok(())); }
+ | ^^^--^
+ | |
+ | this argument influences the type of `Ok`
note: tuple variant defined here
--> $SRC_DIR/core/src/result.rs:LL:COL
help: try wrapping the expression in `Some`
|
= note: expected tuple `(&mut u8,)`
found type `{integer}`
+help: the return type of this call is `{integer}` due to the type of the argument passed
+ --> $DIR/issue-84768.rs:7:5
+ |
+LL | <F as FnOnce(&mut u8)>::call_once(f, 1)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^
+ | |
+ | this argument influences the return type of `FnOnce`
note: associated function defined here
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
--> $DIR/ufcs-partially-resolved.rs:36:12
|
LL | let _: <u8 as Tr>::Y::NN;
- | ^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<<u8 as Tr>::Y as Trait>::NN`
+ | ^^^^^^^^^^^^^^^^^
+ |
+help: if there were a trait named `Example` with associated type `NN` implemented for `<u8 as Tr>::Y`, you could use the fully-qualified path
+ |
+LL | let _: <<u8 as Tr>::Y as Example>::NN;
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0599]: no associated item named `NN` found for type `u16` in the current scope
--> $DIR/ufcs-partially-resolved.rs:38:20
| |
| arguments to this function are incorrect
|
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/ufcs-qpath-self-mismatch.rs:7:5
+ |
+LL | <i32 as Add<i32>>::add(1u32, 2);
+ | ^^^^^^^^^^^^^^^^^^^^^^^----^^^^
+ | |
+ | this argument influences the return type of `Add`
note: associated function defined here
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
help: change the type of the numeric literal from `u32` to `i32`
| |
| arguments to this function are incorrect
|
+help: the return type of this call is `u32` due to the type of the argument passed
+ --> $DIR/ufcs-qpath-self-mismatch.rs:9:5
+ |
+LL | <i32 as Add<i32>>::add(1, 2u32);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^----^
+ | |
+ | this argument influences the return type of `Add`
note: associated function defined here
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
help: change the type of the numeric literal from `u32` to `i32`
LL | let f = || x += 1;
| -- - borrow occurs due to use of `x` in closure
| |
- | borrow of `x` occurs here
+ | `x` is borrowed here
LL | let _y = x;
| ^ use of borrowed `x`
LL | f;
--> $DIR/unboxed-closures-failed-recursive-fn-1.rs:20:5
|
LL | let f = |x: u32| -> u32 {
- | --------------- borrow of `factorial` occurs here
+ | --------------- `factorial` is borrowed here
LL | let g = factorial.as_ref().unwrap();
| --------- borrow occurs due to use in closure
...
LL | factorial = Some(Box::new(f));
| ^^^^^^^^^
| |
- | assignment to borrowed `factorial` occurs here
+ | `factorial` is assigned to here but it was already borrowed
| borrow later used here
error[E0597]: `factorial` does not live long enough
| ----------------------------------------- type annotation requires that `factorial` is borrowed for `'static`
LL |
LL | let f = |x: u32| -> u32 {
- | --------------- borrow of `factorial` occurs here
+ | --------------- `factorial` is borrowed here
LL | let g = factorial.as_ref().unwrap();
| --------- borrow occurs due to use in closure
...
LL | factorial = Some(Box::new(f));
- | ^^^^^^^^^ assignment to borrowed `factorial` occurs here
+ | ^^^^^^^^^ `factorial` is assigned to here but it was already borrowed
error: aborting due to 4 previous errors
error[E0505]: cannot move out of `x` because it is borrowed
--> $DIR/unop-move-semantics.rs:15:6
|
+LL | fn move_borrowed<T: Not<Output=T>>(x: T, mut y: T) {
+ | - binding `x` declared here
LL | let m = &x;
| -- borrow of `x` occurs here
...
error[E0505]: cannot move out of `y` because it is borrowed
--> $DIR/unop-move-semantics.rs:17:6
|
+LL | fn move_borrowed<T: Not<Output=T>>(x: T, mut y: T) {
+ | ----- binding `y` declared here
+LL | let m = &x;
LL | let n = &mut y;
| ------ borrow of `y` occurs here
...
help: consider importing this trait instead
|
LL | use a::Trait;
- | ~~~~~~~~~
+ | ~~~~~~~~
error[E0405]: cannot find trait `Trait` in this scope
--> $DIR/unresolved-candidates.rs:10:10
-error[E0208]: [o]
+error: [o]
--> $DIR/variance-associated-consts.rs:13:1
|
LL | struct Foo<T: Trait> {
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0208`.
-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",
"src/tools/x",
"configure",
"Cargo.toml",
- "Cargo.lock",
"config.toml.example",
"src/stage0.json"
]
[autolabel."WG-trait-system-refactor"]
trigger_files = [
- "compiler/rustc_trait_selection/solve"
+ "compiler/rustc_trait_selection/src/solve"
]
[notify-zulip."I-prioritize"]
"@lcnr",
"@nagisa",
"@wesleywiser",
+ "@michaelwoerister",
]
compiler-team-contributors = [
"@compiler-errors",
"@jackh726",
"@TaKO8Ki",
"@Nilstrieb",
+ "@WaffleLapkin",
]
compiler = [
"compiler-team",
]
bootstrap = [
"@Mark-Simulacrum",
+ "@albertlarsan68",
+ "@ozkanonur",
]
infra-ci = [
"@Mark-Simulacrum",