Compare commits

..

31 commits
main ... beta

Author SHA1 Message Date
bors
2e10913d41 Auto merge of #152187 - cuviper:beta-next, r=cuviper
[beta] backports, plus stable versions in stdarch

- Replace `stdarch` version placeholders with 1.94
- Parse ident with allowing recovery when trying to diagnose rust-lang/rust#151249
- Revert enabling `outline-atomics` on various platforms rust-lang/rust#151896
- Revert doc attribute parsing errors to future warnings rust-lang/rust#151952
- Remove the 4 failing tests from rustdoc-gui rust-lang/rust#152194
- Remove rustdoc GUI flaky test rust-lang/rust#152116
- Align `ArrayWindows` trait impls with `Windows` rust-lang/rust#151613
- Fix suppression of `unused_assignment` in binding of `unused_variable` rust-lang/rust#151556
- layout: handle rigid aliases without params rust-lang/rust#151814
- Fix missing unused_variables lint when using a match guard rust-lang/rust#151990
- Partially revert "resolve: Update `NameBindingData::vis` in place" rust-lang/rust#152498
- [BETA]: parse array lengths without stripping const blocks rust-lang/rust#152237

r? cuviper
2026-02-12 23:54:59 +00:00
Boxy
9cc698e8d6 parse array lengths without mgca shenanigans
(cherry picked from commit 01703e30c0)
2026-02-12 09:07:19 -08:00
Vadim Petrochenkov
a096799b4a Partially revert "resolve: Update NameBindingData::vis in place"
(cherry picked from commit 324267ccaa)
2026-02-12 09:07:10 -08:00
Vadim Petrochenkov
bcfdf1bf97 Add tests for issues 152004, 151124 and 152347
(cherry picked from commit 0cd0840243)
2026-02-12 09:07:10 -08:00
Alan Egerton
1087b41a83 Fix missing unused_variables lint when using a match guard
Within a binding pattern match guard, only real reads of a bound local
impact its liveness analysis - not the fake read that is injected.

(cherry picked from commit 5aba6b1635)
2026-02-12 09:06:20 -08:00
lcnr
8ac1f9d27d fix rustdoc test
(cherry picked from commit 337abba988)
2026-02-12 09:06:14 -08:00
lcnr
25c0e0569c prevent incorrect layout error
aliases may be rigid even if they don't reference params. If the alias isn't well-formed, trying to normalize it as part of the input should have already failed

(cherry picked from commit 39a532445a)
2026-02-12 09:06:14 -08:00
lcnr
4dbbfa3087 add regression test
(cherry picked from commit d4454e59d3)
2026-02-12 09:06:14 -08:00
Alan Egerton
01268919a5 Fix suppression of unused_assignment in binding of unused_variable
Unused assignments to an unused variable should trigger only the
`unused_variables` lint and not also the `unused_assignments` lint.
This was previously implemented by checking whether the span of the
assignee was within the span of the binding pattern, however that failed
to capture situations was imported from elsewhere (eg from the input
tokenstream of a proc-macro that generates the binding pattern).

By comparing the span of the assignee to those of the variable
introductions instead, a reported stable-to-stable regression is
resolved.

This fix also impacted some other preexisting tests, which had
(undesirably) been triggering both the `unused_variables` and
`unused_assignments` lints on the same initializing assignment; those
tests have therefore now been updated to expect only the former lint.

(cherry picked from commit 22b3f59882)
2026-02-12 09:06:11 -08:00
Josh Stone
e1dd1bb6cb impl TrustedRandomAccess for ArrayWindows
(cherry picked from commit 129d552d3f)
2026-02-09 16:30:51 -08:00
Josh Stone
8011d42521 impl TrustedLen for ArrayWindows
(cherry picked from commit 8f7d556c75)
2026-02-09 16:30:51 -08:00
Josh Stone
622cdb3575 impl FusedIterator for ArrayWindows
(cherry picked from commit 08cd2ac33d)
2026-02-09 16:30:51 -08:00
Josh Stone
90e99bdd32 Manually impl Clone for ArrayWindows
This implementation doesn't need the derived `T: Clone`.

(cherry picked from commit 90521553e6)
2026-02-09 16:30:51 -08:00
Josh Stone
f5ccb92483 Remove derive(Copy) on ArrayWindows
The derived `T: Copy` constraint is not appropriate for an iterator by
reference, but we generally do not want `Copy` on iterators anyway.

(cherry picked from commit 2bae85ec52)
2026-02-09 16:30:51 -08:00
Guillaume Gomez
dbfbf2df54 Remove rustdoc GUI flaky test
(cherry picked from commit 607ac4bb84)
2026-02-06 08:38:33 -08:00
Jonathan Brouwer
322be0cdee Remove the 4 failing tests from rustdoc-gui
(cherry picked from commit bce8c00e2f)
2026-02-06 08:34:18 -08:00
Guillaume Gomez
34f6f4827d Move remaining doc attribute parsing errors to warnings
(cherry picked from commit c910511cab)
2026-02-05 11:44:34 -08:00
Guillaume Gomez
d8ac42edd1 Make more doc attribute parsing error into future warnings
(cherry picked from commit 6d713489d0)
2026-02-05 11:38:02 -08:00
Guillaume Gomez
9126c7a0b7 Emit a future error warning for duplicate doc attribute
(cherry picked from commit 7dbbab63e2)
2026-02-05 11:36:45 -08:00
Trevor Gross
dcea3c7ea3 Revert "Enable outline-atomics by default on AArch64 Windows platforms"
This reverts commit 1ed1b6e267.

(cherry picked from commit 9fb68d60ef)
2026-02-05 11:36:35 -08:00
Trevor Gross
4afad17828 Revert "Enable outline-atomics by default on AArch64 Android"
This reverts commit c455903978.

(cherry picked from commit 1173034b7b)
2026-02-05 11:36:35 -08:00
Trevor Gross
b5e2de23c2 Revert "Enable outline-atomics by default on AArch64 Fuchsia"
This reverts commit 21525f862d.

(cherry picked from commit a9da102e20)
2026-02-05 11:36:35 -08:00
Trevor Gross
610c87797c Revert "Enable outline-atomics by default on AArch64 OpenBSD"
This reverts commit 66c150c1fa.

(cherry picked from commit 63e16520a2)
2026-02-05 11:36:35 -08:00
Trevor Gross
e9a79f3f76 Revert "Enable outline-atomics by default on AArch64 FreeBSD"
This reverts commit 383053e016.

(cherry picked from commit 54f88be3cd)
2026-02-05 11:36:35 -08:00
mu001999
c226970869 Parse ident with allowing recovery when trying to recover in diagnosing
(cherry picked from commit 3713512217)
2026-02-05 11:36:26 -08:00
Josh Stone
b90149755a Replace stdarch version placeholders with 1.94 2026-02-05 11:30:41 -08:00
bors
23a44d3c70 Auto merge of #151605 - Mark-Simulacrum:beta-next, r=Mark-Simulacrum
[beta] Switch to released 1.93 artifacts

Last part of 1.93 release.

https://forge.rust-lang.org/release/process.html#beta-stage0-update-friday

r? @Mark-Simulacrum
2026-01-25 06:38:02 +00:00
Mark Rousskov
8065524b58 Switch to released 1.93 artifacts 2026-01-24 13:47:14 -05:00
bors
9b1f8ff42d Auto merge of #151370 - Mark-Simulacrum:beta-next, r=Mark-Simulacrum
[beta] prepare 1.94 beta

https://forge.rust-lang.org/release/process.html#beta-pr

r? @Mark-Simulacrum
2026-01-19 19:45:12 +00:00
Mark Rousskov
9118d50436 Set channel to beta 2026-01-19 09:27:26 -05:00
Mark Rousskov
5d1cb2526b Replace version placeholders with 1.94 2026-01-19 09:26:42 -05:00
4318 changed files with 79059 additions and 110755 deletions

View file

@ -2,7 +2,6 @@
name: Internal Compiler Error
about: Create a report for an internal compiler error in rustc.
labels: C-bug, I-ICE, T-compiler
title: "[ICE]: "
---
<!--
Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide

View file

@ -165,6 +165,9 @@ jobs:
- name: install sccache
run: src/ci/scripts/install-sccache.sh
- name: select Xcode
run: src/ci/scripts/select-xcode.sh
- name: install clang
run: src/ci/scripts/install-clang.sh

View file

@ -62,9 +62,19 @@ jobs:
rustup toolchain install --no-self-update --profile minimal $TOOLCHAIN
rustup default $TOOLCHAIN
- name: cargo update
run: ./src/tools/update-lockfile.sh
- name: cargo update compiler & tools
# Remove first line that always just says "Updating crates.io index"
run: |
echo -e "\ncompiler & tools dependencies:" >> cargo_update.log
cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: cargo update library
run: |
echo -e "\nlibrary dependencies:" >> cargo_update.log
cargo update --manifest-path library/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: cargo update rustbook
run: |
echo -e "\nrustbook dependencies:" >> cargo_update.log
cargo update --manifest-path src/tools/rustbook/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: upload Cargo.lock artifact for use in PR
uses: actions/upload-artifact@v4
with:

2
.gitmodules vendored
View file

@ -25,7 +25,7 @@
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
branch = rustc/22.1-2026-01-27
branch = rustc/21.1-2025-08-01
shallow = true
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book

View file

@ -10,7 +10,7 @@ the Zulip stream is the best place to *ask* for help.
Documentation for contributing to the compiler or tooling is located in the [Guide to Rustc
Development][rustc-dev-guide], commonly known as the [rustc-dev-guide]. Documentation for the
standard library is in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
standard library in the [Standard library developers Guide][std-dev-guide], commonly known as the [std-dev-guide].
## Making changes to subtrees and submodules

View file

@ -184,9 +184,9 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "askama"
version = "0.15.4"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57"
checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427"
dependencies = [
"askama_macros",
"itoa",
@ -197,9 +197,9 @@ dependencies = [
[[package]]
name = "askama_derive"
version = "0.15.4"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37"
checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994"
dependencies = [
"askama_parser",
"basic-toml",
@ -214,18 +214,18 @@ dependencies = [
[[package]]
name = "askama_macros"
version = "0.15.4"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b"
checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46"
dependencies = [
"askama_derive",
]
[[package]]
name = "askama_parser"
version = "0.15.4"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c"
checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e"
dependencies = [
"rustc-hash 2.1.1",
"serde",
@ -580,9 +580,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.54"
version = "4.5.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5"
dependencies = [
"clap_builder",
"clap_derive",
@ -600,9 +600,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.54"
version = "4.5.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a"
dependencies = [
"anstream",
"anstyle",
@ -630,7 +630,7 @@ checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
[[package]]
name = "clippy"
version = "0.1.95"
version = "0.1.94"
dependencies = [
"anstream",
"askama",
@ -657,7 +657,7 @@ dependencies = [
[[package]]
name = "clippy_config"
version = "0.1.95"
version = "0.1.94"
dependencies = [
"clippy_utils",
"itertools",
@ -681,7 +681,7 @@ dependencies = [
[[package]]
name = "clippy_lints"
version = "0.1.95"
version = "0.1.94"
dependencies = [
"arrayvec",
"cargo_metadata 0.18.1",
@ -713,7 +713,7 @@ dependencies = [
[[package]]
name = "clippy_utils"
version = "0.1.95"
version = "0.1.94"
dependencies = [
"arrayvec",
"itertools",
@ -857,7 +857,7 @@ dependencies = [
"tracing-subscriber",
"unified-diff",
"walkdir",
"windows 0.61.3",
"windows",
]
[[package]]
@ -1117,7 +1117,7 @@ dependencies = [
[[package]]
name = "declare_clippy_lint"
version = "0.1.95"
version = "0.1.94"
[[package]]
name = "derive-where"
@ -1298,9 +1298,9 @@ dependencies = [
[[package]]
name = "ena"
version = "0.14.4"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabffdaee24bd1bf95c5ef7cec31260444317e72ea56c4c91750e8b7ee58d5f1"
checksum = "3d248bdd43ce613d87415282f69b9bb99d947d290b10962dd6c56233312c2ad5"
dependencies = [
"log",
]
@ -1670,12 +1670,9 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.16.1"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"foldhash 0.2.0",
]
checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
[[package]]
name = "heck"
@ -1953,12 +1950,12 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
[[package]]
name = "indexmap"
version = "2.13.0"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"hashbrown 0.16.0",
"serde",
"serde_core",
]
@ -2038,7 +2035,7 @@ dependencies = [
"serde",
"tempfile",
"uuid",
"windows 0.61.3",
"windows",
]
[[package]]
@ -2652,9 +2649,9 @@ dependencies = [
[[package]]
name = "objc2-core-foundation"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536"
dependencies = [
"bitflags",
]
@ -2667,9 +2664,9 @@ checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-io-kit"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71c1c64d6120e51cd86033f67176b1cb66780c2efe34dec55176f77befd93c0a"
checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15"
dependencies = [
"libc",
"objc2-core-foundation",
@ -3337,7 +3334,6 @@ dependencies = [
"rustdoc-json-types",
"serde_json",
"similar",
"tempfile",
"wasmparser 0.236.1",
]
@ -3356,9 +3352,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.27"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustc-hash"
@ -3490,6 +3486,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
@ -3514,6 +3511,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_macros",
"rustc_session",
"rustc_span",
@ -3539,9 +3537,9 @@ dependencies = [
"rustc_abi",
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_lexer",
"rustc_macros",
@ -3572,6 +3570,7 @@ dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_graphviz",
"rustc_hir",
"rustc_index",
@ -3599,6 +3598,7 @@ dependencies = [
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_lexer",
@ -3632,6 +3632,7 @@ dependencies = [
"rustc_codegen_ssa",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_fs_util",
"rustc_hashes",
"rustc_hir",
@ -3640,6 +3641,7 @@ dependencies = [
"rustc_macros",
"rustc_metadata",
"rustc_middle",
"rustc_query_system",
"rustc_sanitizers",
"rustc_session",
"rustc_span",
@ -3668,6 +3670,7 @@ dependencies = [
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_fs_util",
"rustc_hashes",
"rustc_hir",
@ -3677,6 +3680,7 @@ dependencies = [
"rustc_macros",
"rustc_metadata",
"rustc_middle",
"rustc_query_system",
"rustc_serialize",
"rustc_session",
"rustc_span",
@ -3686,10 +3690,11 @@ dependencies = [
"serde_json",
"smallvec",
"tempfile",
"thin-vec",
"thorin-dwp",
"tracing",
"wasm-encoder 0.219.2",
"windows 0.61.3",
"windows",
]
[[package]]
@ -3702,6 +3707,7 @@ dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_infer",
@ -3724,7 +3730,7 @@ dependencies = [
"either",
"elsa",
"ena",
"hashbrown 0.16.1",
"hashbrown 0.15.5",
"indexmap",
"jobserver",
"libc",
@ -3746,7 +3752,7 @@ dependencies = [
"tempfile",
"thin-vec",
"tracing",
"windows 0.61.3",
"windows",
]
[[package]]
@ -3761,41 +3767,56 @@ dependencies = [
name = "rustc_driver_impl"
version = "0.0.0"
dependencies = [
"anstyle",
"ctrlc",
"jiff",
"libc",
"rustc_abi",
"rustc_ast",
"rustc_ast_lowering",
"rustc_ast_passes",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_borrowck",
"rustc_builtin_macros",
"rustc_codegen_ssa",
"rustc_const_eval",
"rustc_data_structures",
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir_analysis",
"rustc_hir_pretty",
"rustc_hir_typeck",
"rustc_incremental",
"rustc_index",
"rustc_infer",
"rustc_interface",
"rustc_lexer",
"rustc_lint",
"rustc_log",
"rustc_macros",
"rustc_metadata",
"rustc_middle",
"rustc_mir_build",
"rustc_mir_dataflow",
"rustc_mir_transform",
"rustc_monomorphize",
"rustc_parse",
"rustc_passes",
"rustc_pattern_analysis",
"rustc_privacy",
"rustc_public",
"rustc_query_system",
"rustc_resolve",
"rustc_session",
"rustc_span",
"rustc_target",
"rustc_trait_selection",
"rustc_ty_utils",
"serde_json",
"shlex",
"tracing",
"windows 0.61.3",
"windows",
]
[[package]]
@ -3835,8 +3856,10 @@ dependencies = [
"rustc_data_structures",
"rustc_error_codes",
"rustc_error_messages",
"rustc_fluent_macro",
"rustc_hashes",
"rustc_index",
"rustc_lexer",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
@ -3845,7 +3868,7 @@ dependencies = [
"serde_json",
"termize",
"tracing",
"windows 0.61.3",
"windows",
]
[[package]]
@ -3859,6 +3882,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_lexer",
"rustc_lint_defs",
@ -3886,6 +3910,19 @@ dependencies = [
"serde_json",
]
[[package]]
name = "rustc_fluent_macro"
version = "0.0.0"
dependencies = [
"annotate-snippets 0.11.5",
"fluent-bundle",
"fluent-syntax",
"proc-macro2",
"quote",
"syn 2.0.110",
"unic-langid",
]
[[package]]
name = "rustc_fs_util"
version = "0.0.0"
@ -3940,6 +3977,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_infer",
@ -3986,6 +4024,7 @@ dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_hir_analysis",
"rustc_hir_pretty",
@ -4007,8 +4046,10 @@ name = "rustc_incremental"
version = "0.0.0"
dependencies = [
"rand 0.9.2",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_fs_util",
"rustc_graphviz",
"rustc_hashes",
@ -4018,6 +4059,7 @@ dependencies = [
"rustc_serialize",
"rustc_session",
"rustc_span",
"thin-vec",
"tracing",
]
@ -4046,6 +4088,7 @@ version = "0.0.0"
dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
@ -4076,6 +4119,7 @@ dependencies = [
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_fs_util",
"rustc_hir",
"rustc_hir_analysis",
@ -4092,6 +4136,7 @@ dependencies = [
"rustc_passes",
"rustc_privacy",
"rustc_query_impl",
"rustc_query_system",
"rustc_resolve",
"rustc_session",
"rustc_span",
@ -4120,13 +4165,13 @@ version = "0.0.0"
dependencies = [
"bitflags",
"rustc_abi",
"rustc_apfloat",
"rustc_ast",
"rustc_ast_pretty",
"rustc_attr_parsing",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_infer",
@ -4178,8 +4223,6 @@ dependencies = [
name = "rustc_macros"
version = "0.0.0"
dependencies = [
"fluent-bundle",
"fluent-syntax",
"proc-macro2",
"quote",
"syn 2.0.110",
@ -4201,6 +4244,7 @@ dependencies = [
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_fs_util",
"rustc_hir",
"rustc_hir_pretty",
@ -4224,7 +4268,6 @@ dependencies = [
"bitflags",
"either",
"gsgdt",
"parking_lot",
"polonius-engine",
"rustc_abi",
"rustc_apfloat",
@ -4235,6 +4278,7 @@ dependencies = [
"rustc_error_messages",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_graphviz",
"rustc_hashes",
"rustc_hir",
@ -4242,6 +4286,7 @@ dependencies = [
"rustc_index",
"rustc_lint_defs",
"rustc_macros",
"rustc_query_system",
"rustc_serialize",
"rustc_session",
"rustc_span",
@ -4264,6 +4309,7 @@ dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_infer",
@ -4284,10 +4330,11 @@ dependencies = [
"polonius-engine",
"regex",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_graphviz",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
@ -4301,6 +4348,7 @@ name = "rustc_mir_transform"
version = "0.0.0"
dependencies = [
"either",
"hashbrown 0.15.5",
"itertools",
"rustc_abi",
"rustc_arena",
@ -4308,6 +4356,7 @@ dependencies = [
"rustc_const_eval",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_infer",
@ -4329,6 +4378,7 @@ dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
@ -4365,6 +4415,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_index",
"rustc_lexer",
"rustc_macros",
@ -4397,6 +4448,7 @@ dependencies = [
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
@ -4419,6 +4471,7 @@ dependencies = [
"rustc_arena",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
@ -4438,6 +4491,7 @@ dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_macros",
"rustc_middle",
@ -4490,18 +4544,38 @@ name = "rustc_query_impl"
version = "0.0.0"
dependencies = [
"measureme",
"rustc_data_structures",
"rustc_hashes",
"rustc_hir",
"rustc_index",
"rustc_middle",
"rustc_query_system",
"rustc_serialize",
"rustc_span",
"tracing",
]
[[package]]
name = "rustc_query_system"
version = "0.0.0"
dependencies = [
"hashbrown 0.15.5",
"parking_lot",
"rustc_abi",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hashes",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_thread_pool",
"smallvec",
"tracing",
]
@ -4520,11 +4594,13 @@ dependencies = [
"rustc_errors",
"rustc_expand",
"rustc_feature",
"rustc_fluent_macro",
"rustc_hir",
"rustc_index",
"rustc_macros",
"rustc_metadata",
"rustc_middle",
"rustc_query_system",
"rustc_session",
"rustc_span",
"smallvec",
@ -4572,6 +4648,7 @@ dependencies = [
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
"rustc_fluent_macro",
"rustc_fs_util",
"rustc_hashes",
"rustc_hir",
@ -4582,7 +4659,7 @@ dependencies = [
"rustc_target",
"termize",
"tracing",
"windows 0.61.3",
"windows",
]
[[package]]
@ -4678,6 +4755,7 @@ dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hir",
"rustc_infer",
"rustc_macros",
@ -4726,6 +4804,7 @@ dependencies = [
"rustc_abi",
"rustc_data_structures",
"rustc_errors",
"rustc_fluent_macro",
"rustc_hashes",
"rustc_hir",
"rustc_index",
@ -5354,14 +5433,14 @@ dependencies = [
[[package]]
name = "sysinfo"
version = "0.38.2"
version = "0.37.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efc19935b4b66baa6f654ac7924c192f55b175c00a7ab72410fc24284dacda8"
checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f"
dependencies = [
"libc",
"objc2-core-foundation",
"objc2-io-kit",
"windows 0.62.2",
"windows",
]
[[package]]
@ -5538,7 +5617,7 @@ version = "0.1.0"
dependencies = [
"build_helper",
"cargo_metadata 0.21.0",
"clap",
"fluent-syntax",
"globset",
"ignore",
"miropt-test-tools",
@ -6314,34 +6393,22 @@ version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
"windows-collections 0.2.0",
"windows-collections",
"windows-core 0.61.2",
"windows-future 0.2.1",
"windows-future",
"windows-link 0.1.3",
"windows-numerics 0.2.0",
]
[[package]]
name = "windows"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580"
dependencies = [
"windows-collections 0.3.2",
"windows-core 0.62.2",
"windows-future 0.3.2",
"windows-numerics 0.3.1",
"windows-numerics",
]
[[package]]
name = "windows-bindgen"
version = "0.66.0"
version = "0.61.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81b7ec123a4eadd44d1f44f76804316b477b2537abed9a2ab950b3c54afa1fcf"
checksum = "9b4e97b01190d32f268a2dfbd3f006f77840633746707fbe40bcee588108a231"
dependencies = [
"serde",
"serde_json",
"windows-threading 0.2.1",
"windows-threading",
]
[[package]]
@ -6353,15 +6420,6 @@ dependencies = [
"windows-core 0.61.2",
]
[[package]]
name = "windows-collections"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610"
dependencies = [
"windows-core 0.62.2",
]
[[package]]
name = "windows-core"
version = "0.61.2"
@ -6396,18 +6454,7 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
"windows-core 0.61.2",
"windows-link 0.1.3",
"windows-threading 0.1.0",
]
[[package]]
name = "windows-future"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb"
dependencies = [
"windows-core 0.62.2",
"windows-link 0.2.1",
"windows-threading 0.2.1",
"windows-threading",
]
[[package]]
@ -6454,16 +6501,6 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-numerics"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26"
dependencies = [
"windows-core 0.62.2",
"windows-link 0.2.1",
]
[[package]]
name = "windows-result"
version = "0.3.4"
@ -6569,15 +6606,6 @@ dependencies = [
"windows-link 0.1.3",
]
[[package]]
name = "windows-threading"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37"
dependencies = [
"windows-link 0.2.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"

View file

@ -233,7 +233,7 @@ itself back on after some time).
### MSVC
MSVC builds of Rust additionally require an installation of:
MSVC builds of Rust additionally requires an installation of:
- Visual Studio 2022 (or later) build tools so `rustc` can use its linker. Older
Visual Studio versions such as 2019 *may* work but aren't actively tested.

View file

@ -1,123 +1,3 @@
Version 1.93.1 (2026-02-12)
===========================
<a id="1.93.1"></a>
- [Don't try to recover keyword as non-keyword identifier](https://github.com/rust-lang/rust/pull/150590), fixing an ICE that especially [affected rustfmt](https://github.com/rust-lang/rustfmt/issues/6739).
- [Fix `clippy::panicking_unwrap` false-positive on field access with implicit deref](https://github.com/rust-lang/rust-clippy/pull/16196).
- [Revert "Update wasm-related dependencies in CI"](https://github.com/rust-lang/rust/pull/152259), fixing file descriptor leaks on the `wasm32-wasip2` target.
Version 1.93.0 (2026-01-22)
==========================
<a id="1.93.0-Language"></a>
Language
--------
- [Stabilize several s390x `vector`-related target features and the `is_s390x_feature_detected!` macro](https://github.com/rust-lang/rust/pull/145656)
- [Stabilize declaration of C-style variadic functions for the `system` ABI](https://github.com/rust-lang/rust/pull/145954)
- [Emit error when using some keyword as a `cfg` predicate](https://github.com/rust-lang/rust/pull/146978)
- [Stabilize `asm_cfg`](https://github.com/rust-lang/rust/pull/147736)
- [During const-evaluation, support copying pointers byte-by-byte](https://github.com/rust-lang/rust/pull/148259)
- [LUB coercions now correctly handle function item types, and functions with differing safeties](https://github.com/rust-lang/rust/pull/148602)
- [Allow `const` items that contain mutable references to `static` (which is *very* unsafe, but not *always* UB)](https://github.com/rust-lang/rust/pull/148746)
- [Add warn-by-default `const_item_interior_mutations` lint to warn against calls which mutate interior mutable `const` items](https://github.com/rust-lang/rust/pull/148407)
- [Add warn-by-default `function_casts_as_integer` lint](https://github.com/rust-lang/rust/pull/141470)
<a id="1.93.0-Compiler"></a>
Compiler
--------
- [Stabilize `-Cjump-tables=bool`](https://github.com/rust-lang/rust/pull/145974). The flag was previously called `-Zno-jump-tables`.
<a id="1.93.0-Platform-Support"></a>
Platform Support
----------------
- [Promote `riscv64a23-unknown-linux-gnu` to Tier 2 (without host tools)](https://github.com/rust-lang/rust/pull/148435)
Refer to Rust's [platform support page][platform-support-doc]
for more information on Rust's tiered platform support.
[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html
<a id="1.93.0-Libraries"></a>
Libraries
---------
- [Stop internally using `specialization` on the `Copy` trait as it is unsound in the presence of lifetime dependent `Copy` implementations. This may result in some performance regressions as some standard library APIs may now call `Clone::clone` instead of performing bitwise copies](https://github.com/rust-lang/rust/pull/135634)
- [Allow the global allocator to use thread-local storage and `std::thread::current()`](https://github.com/rust-lang/rust/pull/144465)
- [Make `BTree::append` not update existing keys when appending an entry which already exists](https://github.com/rust-lang/rust/pull/145628)
- [Don't require `T: RefUnwindSafe` for `vec::IntoIter<T>: UnwindSafe`](https://github.com/rust-lang/rust/pull/145665)
<a id="1.93.0-Stabilized-APIs"></a>
Stabilized APIs
---------------
- [`<[MaybeUninit<T>]>::assume_init_drop`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_drop)
- [`<[MaybeUninit<T>]>::assume_init_ref`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_ref)
- [`<[MaybeUninit<T>]>::assume_init_mut`](https://doc.rust-lang.org/stable/core/primitive.slice.html#method.assume_init_mut)
- [`<[MaybeUninit<T>]>::write_copy_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_copy_of_slice)
- [`<[MaybeUninit<T>]>::write_clone_of_slice`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.write_clone_of_slice)
- [`String::into_raw_parts`](https://doc.rust-lang.org/stable/std/string/struct.String.html#method.into_raw_parts)
- [`Vec::into_raw_parts`](https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.into_raw_parts)
- [`<iN>::unchecked_neg`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_neg)
- [`<iN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shl)
- [`<iN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.isize.html#method.unchecked_shr)
- [`<uN>::unchecked_shl`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shl)
- [`<uN>::unchecked_shr`](https://doc.rust-lang.org/stable/std/primitive.usize.html#method.unchecked_shr)
- [`<[T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array)
- [`<[T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_mut_array)
- [`<*const [T]>::as_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_array)
- [`<*mut [T]>::as_mut_array`](https://doc.rust-lang.org/stable/std/primitive.pointer.html#method.as_mut_array)
- [`VecDeque::pop_front_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_front_if)
- [`VecDeque::pop_back_if`](https://doc.rust-lang.org/stable/std/collections/struct.VecDeque.html#method.pop_back_if)
- [`Duration::from_nanos_u128`](https://doc.rust-lang.org/stable/std/time/struct.Duration.html#method.from_nanos_u128)
- [`char::MAX_LEN_UTF8`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF8)
- [`char::MAX_LEN_UTF16`](https://doc.rust-lang.org/stable/std/primitive.char.html#associatedconstant.MAX_LEN_UTF16)
- [`std::fmt::from_fn`](https://doc.rust-lang.org/stable/std/fmt/fn.from_fn.html)
- [`std::fmt::FromFn`](https://doc.rust-lang.org/stable/std/fmt/struct.FromFn.html)
<a id="1.93.0-Cargo"></a>
Cargo
-----
- [Enable CARGO_CFG_DEBUG_ASSERTIONS in build scripts based on profile](https://github.com/rust-lang/cargo/pull/16160/)
- [In `cargo tree`, support long forms for `--format` variables](https://github.com/rust-lang/cargo/pull/16204/)
- [Add `--workspace` to `cargo clean`](https://github.com/rust-lang/cargo/pull/16263/)
<a id="1.93.0-Rustdoc"></a>
Rustdoc
-----
- [Remove `#![doc(document_private_items)]`](https://github.com/rust-lang/rust/pull/146495)
- [Include attribute and derive macros in search filters for "macros"](https://github.com/rust-lang/rust/pull/148176)
- [Include extern crates in search filters for `import`](https://github.com/rust-lang/rust/pull/148301)
- [Validate usage of crate-level doc attributes](https://github.com/rust-lang/rust/pull/149197). This means if any of `html_favicon_url`, `html_logo_url`, `html_playground_url`, `issue_tracker_base_url`, or `html_no_source` either has a missing value, an unexpected value, or a value of the wrong type, rustdoc will emit the deny-by-default lint `rustdoc::invalid_doc_attributes`.
<a id="1.93.0-Compatibility-Notes"></a>
Compatibility Notes
-------------------
- [Introduce `pin_v2` into the builtin attributes namespace](https://github.com/rust-lang/rust/pull/139751)
- [Update bundled musl to 1.2.5](https://github.com/rust-lang/rust/pull/142682)
- [On Emscripten, the unwinding ABI used when compiling with `panic=unwind` was changed from the JS exception handling ABI to the wasm exception handling ABI.](https://github.com/rust-lang/rust/pull/147224) If linking C/C++ object files with Rust objects, `-fwasm-exceptions` must be passed to the linker now. On nightly Rust, it is possible to get the old behavior with `-Zwasm-emscripten-eh=false -Zbuild-std`, but it will be removed in a future release.
- The `#[test]` attribute, used to define tests, was previously ignored in various places where it had no meaning (e.g on trait methods or types). Putting the `#[test]` attribute in these places is no longer ignored, and will now result in an error; this may also result in errors when generating rustdoc. [Error when `test` attribute is applied to structs](https://github.com/rust-lang/rust/pull/147841)
- Cargo now sets the `CARGO_CFG_DEBUG_ASSERTIONS` environment variable in more situations. This will cause crates depending on `static-init` versions 1.0.1 to 1.0.3 to fail compilation with "failed to resolve: use of unresolved module or unlinked crate `parking_lot`". See [the linked issue](https://github.com/rust-lang/rust/issues/150646#issuecomment-3718964342) for details.
- [User written types in the `offset_of!` macro are now checked to be well formed.](https://github.com/rust-lang/rust/issues/150465/)
- `cargo publish` no longer emits `.crate` files as a final artifact for user access when the `build.build-dir` config is unset
- [Upgrade the `deref_nullptr` lint from warn-by-default to deny-by-default](https://github.com/rust-lang/rust/pull/148122)
- [Add future-incompatibility warning for `...` function parameters without a pattern outside of `extern` blocks](https://github.com/rust-lang/rust/pull/143619)
- [Introduce future-compatibility warning for `repr(C)` enums whose discriminant values do not fit into a `c_int` or `c_uint`](https://github.com/rust-lang/rust/pull/147017)
- [Introduce future-compatibility warning against ignoring `repr(C)` types as part of `repr(transparent)`](https://github.com/rust-lang/rust/pull/147185)
Version 1.92.0 (2025-12-11)
==========================
@ -1555,7 +1435,7 @@ Compatibility Notes
- [Check well-formedness of the source type's signature in fn pointer casts.](https://github.com/rust-lang/rust/pull/129021) This partly closes a soundness hole that comes when casting a function item to function pointer
- [Use equality instead of subtyping when resolving type dependent paths.](https://github.com/rust-lang/rust/pull/129073)
- Linking on macOS now correctly includes Rust's default deployment target. Due to a linker bug, you might have to pass `MACOSX_DEPLOYMENT_TARGET` or fix your `#[link]` attributes to point to the correct frameworks. See <https://github.com/rust-lang/rust/pull/129369>.
- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previously did not have any effect.](https://github.com/rust-lang/rust/pull/129422)
- [Rust will now correctly raise an error for `repr(Rust)` written on non-`struct`/`enum`/`union` items, since it previous did not have any effect.](https://github.com/rust-lang/rust/pull/129422)
- The future incompatibility lint `deprecated_cfg_attr_crate_type_name` [has been made into a hard error](https://github.com/rust-lang/rust/pull/129670). It was used to deny usage of `#![crate_type]` and `#![crate_name]` attributes in `#![cfg_attr]`, which required a hack in the compiler to be able to change the used crate type and crate name after cfg expansion.
Users can use `--crate-type` instead of `#![cfg_attr(..., crate_type = "...")]` and `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]` when running `rustc`/`cargo rustc` on the command line.
Use of those two attributes outside of `#![cfg_attr]` continue to be fully supported.
@ -1731,7 +1611,7 @@ Cargo
Compatibility Notes
-------------------
- We now [disallow setting some built-in cfgs via the command-line](https://github.com/rust-lang/rust/pull/126158) with the newly added [`explicit_builtin_cfgs_in_flags`](https://doc.rust-lang.org/rustc/lints/listing/deny-by-default.html#explicit-builtin-cfgs-in-flags) lint in order to prevent incoherent state, eg. `windows` cfg active but target is Linux based. The appropriate [`rustc` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html) should be used instead.
- The standard library has a new implementation of `binary_search` which significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
- The standard library has a new implementation of `binary_search` which is significantly improves performance ([#128254](https://github.com/rust-lang/rust/pull/128254)). However when a sorted slice has multiple values which compare equal, the new implementation may select a different value among the equal ones than the old implementation.
- [illumos/Solaris now sets `MSG_NOSIGNAL` when writing to sockets](https://github.com/rust-lang/rust/pull/128259). This avoids killing the process with SIGPIPE when writing to a closed socket, which matches the existing behavior on other UNIX targets.
- [Removes a problematic hack that always passed the --whole-archive linker flag for tests, which may cause linker errors for code accidentally relying on it.](https://github.com/rust-lang/rust/pull/128400)
- The WebAssembly target features `multivalue` and `reference-types` are now
@ -1881,7 +1761,7 @@ 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.
- [Add a Rust-for-Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
- [Add a Rust-for Linux `auto` CI job to check kernel builds.](https://github.com/rust-lang/rust/pull/125209/)
Version 1.80.1 (2024-08-08)
===========================
@ -4519,7 +4399,7 @@ Compatibility Notes
saturating to `0` instead][89926]. In the real world the panic happened mostly
on platforms with buggy monotonic clock implementations rather than catching
programming errors like reversing the start and end times. Such programming
errors will now result in `0` rather than a panic.
errors will now results in `0` rather than a panic.
- In a future release we're planning to increase the baseline requirements for
the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love
your feedback in [PR #95026][95026].

View file

@ -53,7 +53,7 @@ path = [
]
precedence = "override"
SPDX-FileCopyrightText = "The Rust Project Developers (see https://thanks.rust-lang.org)"
SPDX-License-Identifier = "MIT OR Apache-2.0"
SPDX-License-Identifier = "MIT or Apache-2.0"
[[annotations]]
path = "compiler/rustc_llvm/llvm-wrapper/SymbolWrapper.cpp"

View file

@ -3,8 +3,6 @@
// Several crates are depended upon but unused so that they are present in the sysroot
#![expect(unused_crate_dependencies)]
use std::process::ExitCode;
// A note about jemalloc: rustc uses jemalloc when built for CI and
// distribution. The obvious way to do this is with the `#[global_allocator]`
// mechanism. However, for complicated reasons (see
@ -40,6 +38,6 @@ use std::process::ExitCode;
#[cfg(feature = "jemalloc")]
use tikv_jemalloc_sys as _;
fn main() -> ExitCode {
fn main() {
rustc_driver::main()
}

View file

@ -35,7 +35,6 @@ impl Reg {
reg_ctor!(f32, Float, 32);
reg_ctor!(f64, Float, 64);
reg_ctor!(f128, Float, 128);
}
impl Reg {

View file

@ -27,7 +27,6 @@ pub enum CanonAbi {
C,
Rust,
RustCold,
RustPreserveNone,
/// An ABI that rustc does not know how to call or define.
Custom,
@ -55,7 +54,7 @@ pub enum CanonAbi {
impl CanonAbi {
pub fn is_rustic_abi(self) -> bool {
match self {
CanonAbi::Rust | CanonAbi::RustCold | CanonAbi::RustPreserveNone => true,
CanonAbi::Rust | CanonAbi::RustCold => true,
CanonAbi::C
| CanonAbi::Custom
| CanonAbi::Arm(_)
@ -75,7 +74,6 @@ impl fmt::Display for CanonAbi {
CanonAbi::C => ExternAbi::C { unwind: false },
CanonAbi::Rust => ExternAbi::Rust,
CanonAbi::RustCold => ExternAbi::RustCold,
CanonAbi::RustPreserveNone => ExternAbi::RustPreserveNone,
CanonAbi::Custom => ExternAbi::Custom,
CanonAbi::Arm(arm_call) => match arm_call {
ArmCall::Aapcs => ExternAbi::Aapcs { unwind: false },

View file

@ -42,13 +42,6 @@ pub enum ExternAbi {
/// in a platform-agnostic way.
RustInvalid,
/// Preserves no registers.
///
/// Note, that this ABI is not stable in the registers it uses, is intended as an optimization
/// and may fall-back to a more conservative calling convention if the backend does not support
/// forcing callers to save all registers.
RustPreserveNone,
/// Unstable impl detail that directly uses Rust types to describe the ABI to LLVM.
/// Even normally-compatible Rust types can become ABI-incompatible with this ABI!
Unadjusted,
@ -170,7 +163,6 @@ abi_impls! {
RustCall =><= "rust-call",
RustCold =><= "rust-cold",
RustInvalid =><= "rust-invalid",
RustPreserveNone =><= "rust-preserve-none",
Stdcall { unwind: false } =><= "stdcall",
Stdcall { unwind: true } =><= "stdcall-unwind",
System { unwind: false } =><= "system",
@ -251,7 +243,7 @@ impl ExternAbi {
/// - are subject to change between compiler versions
pub fn is_rustic_abi(self) -> bool {
use ExternAbi::*;
matches!(self, Rust | RustCall | RustCold | RustPreserveNone)
matches!(self, Rust | RustCall | RustCold)
}
/// Returns whether the ABI supports C variadics. This only controls whether we allow *imports*
@ -323,8 +315,7 @@ impl ExternAbi {
| Self::Thiscall { .. }
| Self::Vectorcall { .. }
| Self::SysV64 { .. }
| Self::Win64 { .. }
| Self::RustPreserveNone => true,
| Self::Win64 { .. } => true,
}
}
}

View file

@ -1,7 +1,6 @@
use std::assert_matches::assert_matches;
use std::str::FromStr;
use rustc_data_structures::assert_matches;
use super::*;
#[allow(non_snake_case)]

View file

@ -290,19 +290,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
/// function call isn't allowed (a.k.a. `va_list`).
///
/// This function handles transparent types automatically.
pub fn pass_indirectly_in_non_rustic_abis<C>(self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
let base = self.peel_transparent_wrappers(cx);
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(base)
}
/// Recursively peel away transparent wrappers, returning the inner value.
///
/// The return value is not `repr(transparent)` and/or does
/// not have a non-1zst field.
pub fn peel_transparent_wrappers<C>(mut self, cx: &C) -> Self
pub fn pass_indirectly_in_non_rustic_abis<C>(mut self, cx: &C) -> bool
where
Ty: TyAbiInterface<'a, C> + Copy,
{
@ -312,7 +300,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
self = field;
}
self
Ty::is_pass_indirectly_in_non_rustic_abis_flag_set(self)
}
/// Finds the one field that is not a 1-ZST.

View file

@ -1,6 +1,6 @@
// tidy-alphabetical-start
#![cfg_attr(all(feature = "nightly", bootstrap, test), feature(assert_matches))]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#![cfg_attr(feature = "nightly", feature(assert_matches))]
#![cfg_attr(feature = "nightly", feature(rustc_attrs))]
#![cfg_attr(feature = "nightly", feature(step_trait))]
// tidy-alphabetical-end

View file

@ -13,6 +13,7 @@
#![cfg_attr(test, feature(test))]
#![deny(unsafe_op_in_unsafe_fn)]
#![doc(test(no_crate_inject, attr(deny(warnings), allow(internal_features))))]
#![feature(core_intrinsics)]
#![feature(decl_macro)]
#![feature(dropck_eyepatch)]
#![feature(never_type)]
@ -25,7 +26,7 @@ use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem::{self, MaybeUninit};
use std::ptr::{self, NonNull};
use std::{cmp, hint, slice};
use std::{cmp, intrinsics, slice};
use smallvec::SmallVec;
@ -171,22 +172,8 @@ impl<T> TypedArena<T> {
available_bytes >= additional_bytes
}
/// Allocates storage for `len >= 1` values in this arena, and returns a
/// raw pointer to the first value's storage.
///
/// # Safety
///
/// Caller must initialize each of the `len` slots to a droppable value
/// before the arena is dropped.
///
/// In practice, this typically means that the caller must be able to
/// raw-copy `len` already-initialized values into the slice without any
/// possibility of panicking.
///
/// FIXME(Zalathar): This is *very* fragile; perhaps we need a different
/// approach to arena-allocating slices of droppable values.
#[inline]
unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
fn alloc_raw_slice(&self, len: usize) -> *mut T {
assert!(size_of::<T>() != 0);
assert!(len != 0);
@ -221,7 +208,7 @@ impl<T> TypedArena<T> {
&self,
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<&mut [T], E> {
// Despite the similarity with `DroplessArena`, we cannot reuse their fast case. The reason
// Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
// is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
// reference to `self` and adding elements to the arena during iteration.
//
@ -242,15 +229,9 @@ impl<T> TypedArena<T> {
}
// Move the content to the arena by copying and then forgetting it.
let len = vec.len();
// SAFETY: After allocating raw storage for exactly `len` values, we
// must fully initialize the storage without panicking, and we must
// also prevent the stale values in the vec from being dropped.
let start_ptr = self.alloc_raw_slice(len);
Ok(unsafe {
let start_ptr = self.alloc_raw_slice(len);
// Initialize the newly-allocated storage without panicking.
vec.as_ptr().copy_to_nonoverlapping(start_ptr, len);
// Prevent the stale values in the vec from being dropped.
vec.set_len(0);
slice::from_raw_parts_mut(start_ptr, len)
})
@ -451,7 +432,7 @@ impl DroplessArena {
let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
// Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT.
unsafe { hint::assert_unchecked(end == align_down(end, DROPLESS_ALIGNMENT)) };
unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) };
if let Some(sub) = end.checked_sub(bytes) {
let new_end = align_down(sub, layout.align());
@ -509,6 +490,19 @@ impl DroplessArena {
}
}
/// Used by `Lift` to check whether this slice is allocated
/// in this arena.
#[inline]
pub fn contains_slice<T>(&self, slice: &[T]) -> bool {
for chunk in self.chunks.borrow_mut().iter_mut() {
let ptr = slice.as_ptr().cast::<u8>().cast_mut();
if chunk.start() <= ptr && chunk.end() >= ptr {
return true;
}
}
false
}
/// Allocates a string slice that is copied into the `DroplessArena`, returning a
/// reference to it. Will panic if passed an empty string.
///
@ -590,7 +584,7 @@ impl DroplessArena {
&self,
iter: impl IntoIterator<Item = Result<T, E>>,
) -> Result<&mut [T], E> {
// Despite the similarity with `alloc_from_iter`, we cannot reuse their fast case, as we
// Despite the similarlty with `alloc_from_iter`, we cannot reuse their fast case, as we
// cannot know the minimum length of the iterator in this case.
assert!(size_of::<T>() != 0);

View file

@ -656,7 +656,11 @@ impl Pat {
// A tuple pattern `(P0, .., Pn)` can be reparsed as `(T0, .., Tn)`
// assuming `T0` to `Tn` are all syntactically valid as types.
PatKind::Tuple(pats) => {
let tys = pats.iter().map(|pat| pat.to_ty()).collect::<Option<ThinVec<_>>>()?;
let mut tys = ThinVec::with_capacity(pats.len());
// FIXME(#48994) - could just be collected into an Option<Vec>
for pat in pats {
tys.push(pat.to_ty()?);
}
TyKind::Tup(tys)
}
_ => return None,
@ -3131,16 +3135,8 @@ pub enum Const {
/// For details see the [RFC #2532](https://github.com/rust-lang/rfcs/pull/2532).
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, Walkable)]
pub enum Defaultness {
/// Item is unmarked. Implicitly determined based off of position.
/// For impls, this is `final`; for traits, this is `default`.
///
/// If you're expanding an item in a built-in macro or parsing an item
/// by hand, you probably want to use this.
Implicit,
/// `default`
Default(Span),
/// `final`; per RFC 3678, only trait items may be *explicitly* marked final.
Final(Span),
Final,
}
#[derive(Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic, Walkable)]
@ -3630,7 +3626,6 @@ impl Item {
pub fn opt_generics(&self) -> Option<&Generics> {
match &self.kind {
ItemKind::ExternCrate(..)
| ItemKind::ConstBlock(_)
| ItemKind::Use(_)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(_)
@ -3877,55 +3872,27 @@ pub struct ConstItem {
pub ident: Ident,
pub generics: Generics,
pub ty: Box<Ty>,
pub rhs_kind: ConstItemRhsKind,
pub rhs: Option<ConstItemRhs>,
pub define_opaque: Option<ThinVec<(NodeId, Path)>>,
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub enum ConstItemRhsKind {
Body { rhs: Option<Box<Expr>> },
TypeConst { rhs: Option<AnonConst> },
pub enum ConstItemRhs {
TypeConst(AnonConst),
Body(Box<Expr>),
}
impl ConstItemRhsKind {
pub fn new_body(rhs: Box<Expr>) -> Self {
Self::Body { rhs: Some(rhs) }
impl ConstItemRhs {
pub fn span(&self) -> Span {
self.expr().span
}
pub fn span(&self) -> Option<Span> {
Some(self.expr()?.span)
}
pub fn expr(&self) -> Option<&Expr> {
pub fn expr(&self) -> &Expr {
match self {
Self::Body { rhs: Some(body) } => Some(&body),
Self::TypeConst { rhs: Some(anon) } => Some(&anon.value),
_ => None,
ConstItemRhs::TypeConst(anon_const) => &anon_const.value,
ConstItemRhs::Body(expr) => expr,
}
}
pub fn has_expr(&self) -> bool {
match self {
Self::Body { rhs: Some(_) } => true,
Self::TypeConst { rhs: Some(_) } => true,
_ => false,
}
}
pub fn is_type_const(&self) -> bool {
matches!(self, &Self::TypeConst { .. })
}
}
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
pub struct ConstBlockItem {
pub id: NodeId,
pub span: Span,
pub block: Box<Block>,
}
impl ConstBlockItem {
pub const IDENT: Ident = Ident { name: kw::Underscore, span: DUMMY_SP };
}
// Adding a new variant? Please update `test_item` in `tests/ui/macros/stringify.rs`.
@ -3947,11 +3914,6 @@ pub enum ItemKind {
///
/// E.g., `const FOO: i32 = 42;`.
Const(Box<ConstItem>),
/// A module-level const block.
/// Equivalent to `const _: () = const { ... };`.
///
/// E.g., `const { assert!(true) }`.
ConstBlock(ConstBlockItem),
/// A function declaration (`fn`).
///
/// E.g., `fn foo(bar: usize) -> usize { .. }`.
@ -4028,8 +3990,6 @@ impl ItemKind {
| ItemKind::MacroDef(ident, _)
| ItemKind::Delegation(box Delegation { ident, .. }) => Some(ident),
ItemKind::ConstBlock(_) => Some(ConstBlockItem::IDENT),
ItemKind::Use(_)
| ItemKind::ForeignMod(_)
| ItemKind::GlobalAsm(_)
@ -4043,9 +4003,9 @@ impl ItemKind {
pub fn article(&self) -> &'static str {
use ItemKind::*;
match self {
Use(..) | Static(..) | Const(..) | ConstBlock(..) | Fn(..) | Mod(..)
| GlobalAsm(..) | TyAlias(..) | Struct(..) | Union(..) | Trait(..) | TraitAlias(..)
| MacroDef(..) | Delegation(..) | DelegationMac(..) => "a",
Use(..) | Static(..) | Const(..) | Fn(..) | Mod(..) | GlobalAsm(..) | TyAlias(..)
| Struct(..) | Union(..) | Trait(..) | TraitAlias(..) | MacroDef(..)
| Delegation(..) | DelegationMac(..) => "a",
ExternCrate(..) | ForeignMod(..) | MacCall(..) | Enum(..) | Impl { .. } => "an",
}
}
@ -4056,7 +4016,6 @@ impl ItemKind {
ItemKind::Use(..) => "`use` import",
ItemKind::Static(..) => "static item",
ItemKind::Const(..) => "constant item",
ItemKind::ConstBlock(..) => "const block",
ItemKind::Fn(..) => "function",
ItemKind::Mod(..) => "module",
ItemKind::ForeignMod(..) => "extern block",
@ -4086,18 +4045,7 @@ impl ItemKind {
| Self::Trait(box Trait { generics, .. })
| Self::TraitAlias(box TraitAlias { generics, .. })
| Self::Impl(Impl { generics, .. }) => Some(generics),
Self::ExternCrate(..)
| Self::Use(..)
| Self::Static(..)
| Self::ConstBlock(..)
| Self::Mod(..)
| Self::ForeignMod(..)
| Self::GlobalAsm(..)
| Self::MacCall(..)
| Self::MacroDef(..)
| Self::Delegation(..)
| Self::DelegationMac(..) => None,
_ => None,
}
}
}
@ -4148,7 +4096,7 @@ impl AssocItemKind {
| Self::Fn(box Fn { defaultness, .. })
| Self::Type(box TyAlias { defaultness, .. }) => defaultness,
Self::MacCall(..) | Self::Delegation(..) | Self::DelegationMac(..) => {
Defaultness::Implicit
Defaultness::Final
}
}
}

View file

@ -5,6 +5,7 @@
//! This API is completely unstable and subject to change.
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(array_windows))]
#![doc(test(attr(deny(warnings), allow(internal_features))))]
#![feature(associated_type_defaults)]
#![feature(box_patterns)]

View file

@ -625,12 +625,12 @@ impl TokenKind {
}
impl Token {
pub const fn new(kind: TokenKind, span: Span) -> Self {
pub fn new(kind: TokenKind, span: Span) -> Self {
Token { kind, span }
}
/// Some token that will be thrown away later.
pub const fn dummy() -> Self {
pub fn dummy() -> Self {
Token::new(TokenKind::Question, DUMMY_SP)
}

View file

@ -354,13 +354,7 @@ fn make_attr_token_stream(
FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] },
));
} else if let Some(delim) = kind.close_delim() {
// If there's no matching opening delimiter, the token stream is malformed,
// likely due to a improper delimiter positions in the source code.
// It's not delimiter mismatch, and lexer can not detect it, so we just ignore it here.
let Some(frame) = stack_rest.pop() else {
return AttrTokenStream::new(stack_top.inner);
};
let frame_data = mem::replace(&mut stack_top, frame);
let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap());
let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap();
assert!(
open_delim.eq_ignoring_invisible_origin(&delim),

View file

@ -425,9 +425,8 @@ macro_rules! common_visitor_and_walkers {
ByRef,
Closure,
Const,
ConstBlockItem,
ConstItem,
ConstItemRhsKind,
ConstItemRhs,
Defaultness,
Delegation,
DelegationMac,
@ -826,8 +825,6 @@ macro_rules! common_visitor_and_walkers {
visit_visitable!($($mut)? vis, use_tree),
ItemKind::Static(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::ConstBlock(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::Const(item) =>
visit_visitable!($($mut)? vis, item),
ItemKind::Mod(safety, ident, mod_kind) =>

View file

@ -15,6 +15,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }

View file

@ -0,0 +1,191 @@
ast_lowering_abi_specified_multiple_times =
`{$prev_name}` ABI specified multiple times
.label = previously specified here
.note = these ABIs are equivalent on the current target
ast_lowering_arbitrary_expression_in_pattern =
arbitrary expressions aren't allowed in patterns
.pattern_from_macro_note = the `expr` fragment specifier forces the metavariable's content to be an expression
.const_block_in_pattern_help = use a named `const`-item or an `if`-guard (`x if x == const {"{ ... }"}`) instead
ast_lowering_argument = argument
ast_lowering_assoc_ty_binding_in_dyn =
associated type bounds are not allowed in `dyn` types
.suggestion = use `impl Trait` to introduce a type instead
ast_lowering_assoc_ty_parentheses =
parenthesized generic arguments cannot be used in associated type constraints
ast_lowering_async_bound_not_on_trait =
`async` bound modifier only allowed on trait, not `{$descr}`
ast_lowering_async_bound_only_for_fn_traits =
`async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits
ast_lowering_async_coroutines_not_supported =
`async` coroutines are not yet supported
ast_lowering_att_syntax_only_x86 =
the `att_syntax` option is only supported on x86
ast_lowering_await_only_in_async_fn_and_blocks =
`await` is only allowed inside `async` functions and blocks
.label = only allowed inside `async` functions and blocks
ast_lowering_bad_return_type_notation_inputs =
argument types not allowed with return type notation
.suggestion = remove the input types
ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..`
.suggestion = use the correct syntax by adding `..` to the arguments
ast_lowering_bad_return_type_notation_output =
return type not allowed with return type notation
ast_lowering_bad_return_type_notation_output_suggestion = use the right argument notation and remove the return type
ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
ast_lowering_clobber_abi_not_supported =
`clobber_abi` is not supported on this target
ast_lowering_closure_cannot_be_static = closures cannot be static
ast_lowering_coroutine_too_many_parameters =
too many parameters for a coroutine (expected 0 or 1 parameters)
ast_lowering_default_field_in_tuple = default fields are not supported in tuple structs
.label = default fields are only supported on structs
ast_lowering_delegation_cycle_in_signature_resolution = encountered a cycle during delegation signature resolution
ast_lowering_delegation_unresolved_callee = failed to resolve delegation callee
ast_lowering_does_not_support_modifiers =
the `{$class_name}` register class does not support template modifiers
ast_lowering_extra_double_dot =
`..` can only be used once per {$ctx} pattern
.label = can only be used once per {$ctx} pattern
ast_lowering_functional_record_update_destructuring_assignment =
functional record updates are not allowed in destructuring assignments
.suggestion = consider removing the trailing pattern
ast_lowering_generic_param_default_in_binder =
defaults for generic parameters are not allowed in `for<...>` binders
ast_lowering_generic_type_with_parentheses =
parenthesized type parameters may only be used with a `Fn` trait
.label = only `Fn` traits may use parentheses
ast_lowering_inclusive_range_with_no_end = inclusive range with no end
ast_lowering_inline_asm_unsupported_target =
inline assembly is unsupported on this target
ast_lowering_invalid_abi =
invalid ABI: found `{$abi}`
.label = invalid ABI
.note = invoke `{$command}` for a full list of supported calling conventions
ast_lowering_invalid_abi_clobber_abi =
invalid ABI for `clobber_abi`
.note = the following ABIs are supported on this target: {$supported_abis}
ast_lowering_invalid_abi_suggestion = there's a similarly named valid ABI `{$suggestion}`
ast_lowering_invalid_asm_template_modifier_const =
asm template modifiers are not allowed for `const` arguments
ast_lowering_invalid_asm_template_modifier_label =
asm template modifiers are not allowed for `label` arguments
ast_lowering_invalid_asm_template_modifier_reg_class =
invalid asm template modifier for this register class
ast_lowering_invalid_asm_template_modifier_sym =
asm template modifiers are not allowed for `sym` arguments
ast_lowering_invalid_legacy_const_generic_arg =
invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items
ast_lowering_invalid_legacy_const_generic_arg_suggestion =
try using a const generic argument instead
ast_lowering_invalid_register =
invalid register `{$reg}`: {$error}
ast_lowering_invalid_register_class =
invalid register class `{$reg_class}`: unknown register class
.note = the following register classes are supported on this target: {$supported_register_classes}
ast_lowering_match_arm_with_no_body =
`match` arm with no body
.suggestion = add a body after the pattern
ast_lowering_misplaced_double_dot =
`..` patterns are not allowed here
.note = only allowed in tuple, tuple struct, and slice patterns
ast_lowering_misplaced_impl_trait =
`impl Trait` is not allowed in {$position}
.note = `impl Trait` is only allowed in arguments and return types of functions and methods
ast_lowering_never_pattern_with_body =
a never pattern is always unreachable
.label = this will never be executed
.suggestion = remove this expression
ast_lowering_never_pattern_with_guard =
a guard on a never pattern will never be run
.suggestion = remove this guard
ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
ast_lowering_previously_used_here = previously used here
ast_lowering_register1 = register `{$reg1_name}`
ast_lowering_register2 = register `{$reg2_name}`
ast_lowering_register_class_only_clobber =
register class `{$reg_class_name}` can only be used as a clobber, not as an input or output
ast_lowering_register_class_only_clobber_stable =
register class `{$reg_class_name}` can only be used as a clobber in stable
ast_lowering_register_conflict =
register `{$reg1_name}` conflicts with register `{$reg2_name}`
.help = use `lateout` instead of `out` to avoid conflict
ast_lowering_remove_parentheses = remove these parentheses
ast_lowering_sub_tuple_binding =
`{$ident_name} @` is not allowed in a {$ctx}
.label = this is only allowed in slice patterns
.help = remove this and bind each tuple field independently
ast_lowering_sub_tuple_binding_suggestion = if you don't need to use the contents of {$ident}, discard the tuple's remaining fields
ast_lowering_support_modifiers =
the `{$class_name}` register class supports the following template modifiers: {$modifiers}
ast_lowering_template_modifier = template modifier
ast_lowering_this_not_async = this is not `async`
ast_lowering_underscore_expr_lhs_assign =
in expressions, `_` can only be used on the left-hand side of an assignment
.label = `_` not allowed here
ast_lowering_union_default_field_values = unions cannot have default field values
ast_lowering_unstable_inline_assembly = inline assembly is not stable yet on this architecture
ast_lowering_unstable_inline_assembly_label_operand_with_outputs =
using both label and output operands for inline assembly is unstable
ast_lowering_unstable_may_unwind = the `may_unwind` option is unstable
ast_lowering_use_angle_brackets = use angle brackets instead
ast_lowering_yield = yield syntax is experimental
ast_lowering_yield_in_closure =
`yield` can only be used in `#[coroutine]` closures, or `gen` blocks
.suggestion = use `#[coroutine]` to make this closure a coroutine

View file

@ -3,7 +3,6 @@ use std::fmt::Write;
use rustc_ast::*;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::msg;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_session::parse::feature_err;
@ -20,7 +19,8 @@ use super::errors::{
RegisterConflict,
};
use crate::{
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt,
AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, ParamMode,
ResolverAstLoweringExt, fluent_generated as fluent,
};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
@ -51,8 +51,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| asm::InlineAsmArch::LoongArch32
| asm::InlineAsmArch::LoongArch64
| asm::InlineAsmArch::S390x
| asm::InlineAsmArch::PowerPC
| asm::InlineAsmArch::PowerPC64
);
if !is_stable
&& !self.tcx.features().asm_experimental_arch()
@ -67,7 +65,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&self.tcx.sess,
sym::asm_experimental_arch,
sp,
msg!("inline assembly is not stable yet on this architecture"),
fluent::ast_lowering_unstable_inline_assembly,
)
.emit();
}
@ -84,7 +82,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&self.tcx.sess,
sym::asm_unwind,
sp,
msg!("the `may_unwind` option is unstable"),
fluent::ast_lowering_unstable_may_unwind,
)
.emit();
}
@ -499,7 +497,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
sess,
sym::asm_goto_with_outputs,
*op_sp,
msg!("using both label and output operands for inline assembly is unstable"),
fluent::ast_lowering_unstable_inline_assembly_label_operand_with_outputs,
)
.emit();
}

View file

@ -152,21 +152,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> DelegationResults<'hir> {
let span = self.lower_span(delegation.path.segments.last().unwrap().ident.span);
// Delegation can be unresolved in illegal places such as function bodies in extern blocks (see #151356)
let ids = if let Some(delegation_info) =
self.resolver.delegation_infos.get(&self.local_def_id(item_id))
{
self.get_delegation_ids(delegation_info.resolution_node, span)
} else {
return self.generate_delegation_error(
self.dcx().span_delayed_bug(
span,
format!("LoweringContext: the delegation {:?} is unresolved", item_id),
),
span,
delegation,
);
};
let ids = self.get_delegation_ids(
self.resolver.delegation_infos[&self.local_def_id(item_id)].resolution_node,
span,
);
match ids {
Ok(ids) => {

View file

@ -4,17 +4,17 @@ use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Ident, Span, Symbol};
#[derive(Diagnostic)]
#[diag("parenthesized type parameters may only be used with a `Fn` trait", code = E0214)]
#[diag(ast_lowering_generic_type_with_parentheses, code = E0214)]
pub(crate) struct GenericTypeWithParentheses {
#[primary_span]
#[label("only `Fn` traits may use parentheses")]
#[label]
pub span: Span,
#[subdiagnostic]
pub sub: Option<UseAngleBrackets>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("use angle brackets instead", applicability = "maybe-incorrect")]
#[multipart_suggestion(ast_lowering_use_angle_brackets, applicability = "maybe-incorrect")]
pub(crate) struct UseAngleBrackets {
#[suggestion_part(code = "<")]
pub open_param: Span,
@ -23,11 +23,11 @@ pub(crate) struct UseAngleBrackets {
}
#[derive(Diagnostic)]
#[diag("invalid ABI: found `{$abi}`", code = E0703)]
#[note("invoke `{$command}` for a full list of supported calling conventions")]
#[diag(ast_lowering_invalid_abi, code = E0703)]
#[note]
pub(crate) struct InvalidAbi {
#[primary_span]
#[label("invalid ABI")]
#[label]
pub span: Span,
pub abi: Symbol,
pub command: String,
@ -36,16 +36,16 @@ pub(crate) struct InvalidAbi {
}
#[derive(Diagnostic)]
#[diag("default fields are not supported in tuple structs")]
#[diag(ast_lowering_default_field_in_tuple)]
pub(crate) struct TupleStructWithDefault {
#[primary_span]
#[label("default fields are only supported on structs")]
#[label]
pub span: Span,
}
#[derive(Subdiagnostic)]
#[suggestion(
"there's a similarly named valid ABI `{$suggestion}`",
ast_lowering_invalid_abi_suggestion,
code = "\"{suggestion}\"",
applicability = "maybe-incorrect",
style = "verbose"
@ -57,7 +57,7 @@ pub(crate) struct InvalidAbiSuggestion {
}
#[derive(Diagnostic)]
#[diag("parenthesized generic arguments cannot be used in associated type constraints")]
#[diag(ast_lowering_assoc_ty_parentheses)]
pub(crate) struct AssocTyParentheses {
#[primary_span]
pub span: Span,
@ -67,12 +67,12 @@ pub(crate) struct AssocTyParentheses {
#[derive(Subdiagnostic)]
pub(crate) enum AssocTyParenthesesSub {
#[multipart_suggestion("remove these parentheses")]
#[multipart_suggestion(ast_lowering_remove_parentheses)]
Empty {
#[suggestion_part(code = "")]
parentheses_span: Span,
},
#[multipart_suggestion("use angle brackets instead")]
#[multipart_suggestion(ast_lowering_use_angle_brackets)]
NotEmpty {
#[suggestion_part(code = "<")]
open_param: Span,
@ -82,8 +82,8 @@ pub(crate) enum AssocTyParenthesesSub {
}
#[derive(Diagnostic)]
#[diag("`impl Trait` is not allowed in {$position}", code = E0562)]
#[note("`impl Trait` is only allowed in arguments and return types of functions and methods")]
#[diag(ast_lowering_misplaced_impl_trait, code = E0562)]
#[note]
pub(crate) struct MisplacedImplTrait<'a> {
#[primary_span]
pub span: Span,
@ -91,106 +91,97 @@ pub(crate) struct MisplacedImplTrait<'a> {
}
#[derive(Diagnostic)]
#[diag("associated type bounds are not allowed in `dyn` types")]
#[diag(ast_lowering_assoc_ty_binding_in_dyn)]
pub(crate) struct MisplacedAssocTyBinding {
#[primary_span]
pub span: Span,
#[suggestion(
"use `impl Trait` to introduce a type instead",
code = " = impl",
applicability = "maybe-incorrect",
style = "verbose"
)]
#[suggestion(code = " = impl", applicability = "maybe-incorrect", style = "verbose")]
pub suggestion: Option<Span>,
}
#[derive(Diagnostic)]
#[diag("in expressions, `_` can only be used on the left-hand side of an assignment")]
#[diag(ast_lowering_underscore_expr_lhs_assign)]
pub(crate) struct UnderscoreExprLhsAssign {
#[primary_span]
#[label("`_` not allowed here")]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`await` is only allowed inside `async` functions and blocks", code = E0728)]
#[diag(ast_lowering_await_only_in_async_fn_and_blocks, code = E0728)]
pub(crate) struct AwaitOnlyInAsyncFnAndBlocks {
#[primary_span]
#[label("only allowed inside `async` functions and blocks")]
#[label]
pub await_kw_span: Span,
#[label("this is not `async`")]
#[label(ast_lowering_this_not_async)]
pub item_span: Option<Span>,
}
#[derive(Diagnostic)]
#[diag("too many parameters for a coroutine (expected 0 or 1 parameters)", code = E0628)]
#[diag(ast_lowering_coroutine_too_many_parameters, code = E0628)]
pub(crate) struct CoroutineTooManyParameters {
#[primary_span]
pub fn_decl_span: Span,
}
#[derive(Diagnostic)]
#[diag("closures cannot be static", code = E0697)]
#[diag(ast_lowering_closure_cannot_be_static, code = E0697)]
pub(crate) struct ClosureCannotBeStatic {
#[primary_span]
pub fn_decl_span: Span,
}
#[derive(Diagnostic)]
#[diag("functional record updates are not allowed in destructuring assignments")]
#[diag(ast_lowering_functional_record_update_destructuring_assignment)]
pub(crate) struct FunctionalRecordUpdateDestructuringAssignment {
#[primary_span]
#[suggestion(
"consider removing the trailing pattern",
code = "",
applicability = "machine-applicable"
)]
#[suggestion(code = "", applicability = "machine-applicable")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`async` coroutines are not yet supported", code = E0727)]
#[diag(ast_lowering_async_coroutines_not_supported, code = E0727)]
pub(crate) struct AsyncCoroutinesNotSupported {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("inline assembly is unsupported on this target", code = E0472)]
#[diag(ast_lowering_inline_asm_unsupported_target, code = E0472)]
pub(crate) struct InlineAsmUnsupportedTarget {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("the `att_syntax` option is only supported on x86")]
#[diag(ast_lowering_att_syntax_only_x86)]
pub(crate) struct AttSyntaxOnlyX86 {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`{$prev_name}` ABI specified multiple times")]
#[diag(ast_lowering_abi_specified_multiple_times)]
pub(crate) struct AbiSpecifiedMultipleTimes {
#[primary_span]
pub abi_span: Span,
pub prev_name: Symbol,
#[label("previously specified here")]
#[label]
pub prev_span: Span,
#[note("these ABIs are equivalent on the current target")]
#[note]
pub equivalent: bool,
}
#[derive(Diagnostic)]
#[diag("`clobber_abi` is not supported on this target")]
#[diag(ast_lowering_clobber_abi_not_supported)]
pub(crate) struct ClobberAbiNotSupported {
#[primary_span]
pub abi_span: Span,
}
#[derive(Diagnostic)]
#[note("the following ABIs are supported on this target: {$supported_abis}")]
#[diag("invalid ABI for `clobber_abi`")]
#[note]
#[diag(ast_lowering_invalid_abi_clobber_abi)]
pub(crate) struct InvalidAbiClobberAbi {
#[primary_span]
pub abi_span: Span,
@ -198,7 +189,7 @@ pub(crate) struct InvalidAbiClobberAbi {
}
#[derive(Diagnostic)]
#[diag("invalid register `{$reg}`: {$error}")]
#[diag(ast_lowering_invalid_register)]
pub(crate) struct InvalidRegister<'a> {
#[primary_span]
pub op_span: Span,
@ -207,10 +198,8 @@ pub(crate) struct InvalidRegister<'a> {
}
#[derive(Diagnostic)]
#[note(
"the following register classes are supported on this target: {$supported_register_classes}"
)]
#[diag("invalid register class `{$reg_class}`: unknown register class")]
#[note]
#[diag(ast_lowering_invalid_register_class)]
pub(crate) struct InvalidRegisterClass {
#[primary_span]
pub op_span: Span,
@ -219,12 +208,12 @@ pub(crate) struct InvalidRegisterClass {
}
#[derive(Diagnostic)]
#[diag("invalid asm template modifier for this register class")]
#[diag(ast_lowering_invalid_asm_template_modifier_reg_class)]
pub(crate) struct InvalidAsmTemplateModifierRegClass {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
#[subdiagnostic]
pub sub: InvalidAsmTemplateModifierRegClassSub,
@ -232,48 +221,44 @@ pub(crate) struct InvalidAsmTemplateModifierRegClass {
#[derive(Subdiagnostic)]
pub(crate) enum InvalidAsmTemplateModifierRegClassSub {
#[note(
"the `{$class_name}` register class supports the following template modifiers: {$modifiers}"
)]
#[note(ast_lowering_support_modifiers)]
SupportModifier { class_name: Symbol, modifiers: String },
#[note("the `{$class_name}` register class does not support template modifiers")]
#[note(ast_lowering_does_not_support_modifiers)]
DoesNotSupportModifier { class_name: Symbol },
}
#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `const` arguments")]
#[diag(ast_lowering_invalid_asm_template_modifier_const)]
pub(crate) struct InvalidAsmTemplateModifierConst {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `sym` arguments")]
#[diag(ast_lowering_invalid_asm_template_modifier_sym)]
pub(crate) struct InvalidAsmTemplateModifierSym {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic)]
#[diag("asm template modifiers are not allowed for `label` arguments")]
#[diag(ast_lowering_invalid_asm_template_modifier_label)]
pub(crate) struct InvalidAsmTemplateModifierLabel {
#[primary_span]
#[label("template modifier")]
#[label(ast_lowering_template_modifier)]
pub placeholder_span: Span,
#[label("argument")]
#[label(ast_lowering_argument)]
pub op_span: Span,
}
#[derive(Diagnostic)]
#[diag(
"register class `{$reg_class_name}` can only be used as a clobber, not as an input or output"
)]
#[diag(ast_lowering_register_class_only_clobber)]
pub(crate) struct RegisterClassOnlyClobber {
#[primary_span]
pub op_span: Span,
@ -281,7 +266,7 @@ pub(crate) struct RegisterClassOnlyClobber {
}
#[derive(Diagnostic)]
#[diag("register class `{$reg_class_name}` can only be used as a clobber in stable")]
#[diag(ast_lowering_register_class_only_clobber_stable)]
pub(crate) struct RegisterClassOnlyClobberStable {
#[primary_span]
pub op_span: Span,
@ -289,27 +274,27 @@ pub(crate) struct RegisterClassOnlyClobberStable {
}
#[derive(Diagnostic)]
#[diag("register `{$reg1_name}` conflicts with register `{$reg2_name}`")]
#[diag(ast_lowering_register_conflict)]
pub(crate) struct RegisterConflict<'a> {
#[primary_span]
#[label("register `{$reg1_name}`")]
#[label(ast_lowering_register1)]
pub op_span1: Span,
#[label("register `{$reg2_name}`")]
#[label(ast_lowering_register2)]
pub op_span2: Span,
pub reg1_name: &'a str,
pub reg2_name: &'a str,
#[help("use `lateout` instead of `out` to avoid conflict")]
#[help]
pub in_out: Option<Span>,
}
#[derive(Diagnostic)]
#[help("remove this and bind each tuple field independently")]
#[diag("`{$ident_name} @` is not allowed in a {$ctx}")]
#[help]
#[diag(ast_lowering_sub_tuple_binding)]
pub(crate) struct SubTupleBinding<'a> {
#[primary_span]
#[label("this is only allowed in slice patterns")]
#[label]
#[suggestion(
"if you don't need to use the contents of {$ident}, discard the tuple's remaining fields",
ast_lowering_sub_tuple_binding_suggestion,
style = "verbose",
code = "..",
applicability = "maybe-incorrect"
@ -321,67 +306,63 @@ pub(crate) struct SubTupleBinding<'a> {
}
#[derive(Diagnostic)]
#[diag("`..` can only be used once per {$ctx} pattern")]
#[diag(ast_lowering_extra_double_dot)]
pub(crate) struct ExtraDoubleDot<'a> {
#[primary_span]
#[label("can only be used once per {$ctx} pattern")]
#[label]
pub span: Span,
#[label("previously used here")]
#[label(ast_lowering_previously_used_here)]
pub prev_span: Span,
pub ctx: &'a str,
}
#[derive(Diagnostic)]
#[note("only allowed in tuple, tuple struct, and slice patterns")]
#[diag("`..` patterns are not allowed here")]
#[note]
#[diag(ast_lowering_misplaced_double_dot)]
pub(crate) struct MisplacedDoubleDot {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`match` arm with no body")]
#[diag(ast_lowering_match_arm_with_no_body)]
pub(crate) struct MatchArmWithNoBody {
#[primary_span]
pub span: Span,
#[suggestion(
"add a body after the pattern",
code = " => todo!(),",
applicability = "has-placeholders"
)]
#[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
pub suggestion: Span,
}
#[derive(Diagnostic)]
#[diag("a never pattern is always unreachable")]
#[diag(ast_lowering_never_pattern_with_body)]
pub(crate) struct NeverPatternWithBody {
#[primary_span]
#[label("this will never be executed")]
#[suggestion("remove this expression", code = "", applicability = "maybe-incorrect")]
#[label]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("a guard on a never pattern will never be run")]
#[diag(ast_lowering_never_pattern_with_guard)]
pub(crate) struct NeverPatternWithGuard {
#[primary_span]
#[suggestion("remove this guard", code = "", applicability = "maybe-incorrect")]
#[suggestion(code = "", applicability = "maybe-incorrect")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("arbitrary expressions aren't allowed in patterns")]
#[diag(ast_lowering_arbitrary_expression_in_pattern)]
pub(crate) struct ArbitraryExpressionInPattern {
#[primary_span]
pub span: Span,
#[note("the `expr` fragment specifier forces the metavariable's content to be an expression")]
#[note(ast_lowering_pattern_from_macro_note)]
pub pattern_from_macro_note: bool,
#[help("use a named `const`-item or an `if`-guard (`x if x == const {\"{ ... }\"}`) instead")]
#[help(ast_lowering_const_block_in_pattern_help)]
pub const_block_in_pattern_help: bool,
}
#[derive(Diagnostic)]
#[diag("inclusive range with no end")]
#[diag(ast_lowering_inclusive_range_with_no_end)]
pub(crate) struct InclusiveRangeWithNoEnd {
#[primary_span]
pub span: Span,
@ -389,7 +370,7 @@ pub(crate) struct InclusiveRangeWithNoEnd {
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"use the right argument notation and remove the return type",
ast_lowering_bad_return_type_notation_output_suggestion,
applicability = "machine-applicable",
style = "verbose"
)]
@ -403,36 +384,26 @@ pub(crate) struct RTNSuggestion {
#[derive(Diagnostic)]
pub(crate) enum BadReturnTypeNotation {
#[diag("argument types not allowed with return type notation")]
#[diag(ast_lowering_bad_return_type_notation_inputs)]
Inputs {
#[primary_span]
#[suggestion(
"remove the input types",
code = "(..)",
applicability = "machine-applicable",
style = "verbose"
)]
#[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
span: Span,
},
#[diag("return type not allowed with return type notation")]
#[diag(ast_lowering_bad_return_type_notation_output)]
Output {
#[primary_span]
span: Span,
#[subdiagnostic]
suggestion: RTNSuggestion,
},
#[diag("return type notation arguments must be elided with `..`")]
#[diag(ast_lowering_bad_return_type_notation_needs_dots)]
NeedsDots {
#[primary_span]
#[suggestion(
"use the correct syntax by adding `..` to the arguments",
code = "(..)",
applicability = "machine-applicable",
style = "verbose"
)]
#[suggestion(code = "(..)", applicability = "machine-applicable", style = "verbose")]
span: Span,
},
#[diag("return type notation not allowed in this position yet")]
#[diag(ast_lowering_bad_return_type_notation_position)]
Position {
#[primary_span]
span: Span,
@ -440,14 +411,14 @@ pub(crate) enum BadReturnTypeNotation {
}
#[derive(Diagnostic)]
#[diag("defaults for generic parameters are not allowed in `for<...>` binders")]
#[diag(ast_lowering_generic_param_default_in_binder)]
pub(crate) struct GenericParamDefaultInBinder {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`async` bound modifier only allowed on trait, not `{$descr}`")]
#[diag(ast_lowering_async_bound_not_on_trait)]
pub(crate) struct AsyncBoundNotOnTrait {
#[primary_span]
pub span: Span,
@ -455,37 +426,30 @@ pub(crate) struct AsyncBoundNotOnTrait {
}
#[derive(Diagnostic)]
#[diag("`async` bound modifier only allowed on `Fn`/`FnMut`/`FnOnce` traits")]
#[diag(ast_lowering_async_bound_only_for_fn_traits)]
pub(crate) struct AsyncBoundOnlyForFnTraits {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`use<...>` precise capturing syntax not allowed in argument-position `impl Trait`")]
#[diag(ast_lowering_no_precise_captures_on_apit)]
pub(crate) struct NoPreciseCapturesOnApit {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`yield` can only be used in `#[coroutine]` closures, or `gen` blocks")]
#[diag(ast_lowering_yield_in_closure)]
pub(crate) struct YieldInClosure {
#[primary_span]
pub span: Span,
#[suggestion(
"use `#[coroutine]` to make this closure a coroutine",
code = "#[coroutine] ",
applicability = "maybe-incorrect",
style = "verbose"
)]
#[suggestion(code = "#[coroutine] ", applicability = "maybe-incorrect", style = "verbose")]
pub suggestion: Option<Span>,
}
#[derive(Diagnostic)]
#[diag(
"invalid argument to a legacy const generic: cannot have const blocks, closures, async blocks or items"
)]
#[diag(ast_lowering_invalid_legacy_const_generic_arg)]
pub(crate) struct InvalidLegacyConstGenericArg {
#[primary_span]
pub span: Span,
@ -495,7 +459,7 @@ pub(crate) struct InvalidLegacyConstGenericArg {
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"try using a const generic argument instead",
ast_lowering_invalid_legacy_const_generic_arg_suggestion,
applicability = "maybe-incorrect"
)]
pub(crate) struct UseConstGenericArg {
@ -508,21 +472,21 @@ pub(crate) struct UseConstGenericArg {
}
#[derive(Diagnostic)]
#[diag("unions cannot have default field values")]
#[diag(ast_lowering_union_default_field_values)]
pub(crate) struct UnionWithDefault {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("failed to resolve delegation callee")]
#[diag(ast_lowering_delegation_unresolved_callee)]
pub(crate) struct UnresolvedDelegationCallee {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("encountered a cycle during delegation signature resolution")]
#[diag(ast_lowering_delegation_cycle_in_signature_resolution)]
pub(crate) struct CycleInDelegationSignatureResolution {
#[primary_span]
pub span: Span,

View file

@ -5,7 +5,6 @@ use std::sync::Arc;
use rustc_ast::*;
use rustc_ast_pretty::pprust::expr_to_string;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::msg;
use rustc_hir as hir;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
@ -29,7 +28,9 @@ use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
};
use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure};
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope};
use crate::{
AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope, fluent_generated,
};
struct WillCreateDefIdsVisitor {}
@ -966,14 +967,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
this.arena.alloc(this.expr(gen_future_span, expr_break))
});
self.arm(ready_pat, break_x, span)
self.arm(ready_pat, break_x)
};
// `::std::task::Poll::Pending => {}`
let pending_arm = {
let pending_pat = self.pat_lang_item_variant(span, hir::LangItem::PollPending, &[]);
let empty_block = self.expr_block_empty(span);
self.arm(pending_pat, empty_block, span)
self.arm(pending_pat, empty_block)
};
let inner_match_stmt = {
@ -1027,7 +1028,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
});
// mut __awaitee => loop { ... }
let awaitee_arm = self.arm(awaitee_pat, loop_expr, span);
let awaitee_arm = self.arm(awaitee_pat, loop_expr);
// `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
let into_future_expr = match await_kind {
@ -1702,7 +1703,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
&self.tcx.sess,
sym::yield_expr,
span,
msg!("yield syntax is experimental"),
fluent_generated::ast_lowering_yield,
)
.emit();
}
@ -1817,7 +1818,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let break_expr =
self.with_loop_scope(loop_hir_id, |this| this.expr_break_alloc(for_span));
let pat = self.pat_none(for_span);
self.arm(pat, break_expr, for_span)
self.arm(pat, break_expr)
};
// Some(<pat>) => <body>,
@ -1826,7 +1827,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let body_block =
self.with_loop_scope(loop_hir_id, |this| this.lower_block(body, false));
let body_expr = self.arena.alloc(self.expr_block(body_block));
self.arm(some_pat, body_expr, for_span)
self.arm(some_pat, body_expr)
};
// `mut iter`
@ -1885,7 +1886,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let loop_expr = self.arena.alloc(hir::Expr { hir_id: loop_hir_id, kind, span: for_span });
// `mut iter => { ... }`
let iter_arm = self.arm(iter_pat, loop_expr, for_span);
let iter_arm = self.arm(iter_pat, loop_expr);
let match_expr = match loop_kind {
ForLoopKind::For => {
@ -1930,7 +1931,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::LangItem::IntoAsyncIterIntoIter,
arena_vec![self; head],
);
let iter_arm = self.arm(async_iter_pat, inner_match_expr, for_span);
let iter_arm = self.arm(async_iter_pat, inner_match_expr);
self.arena.alloc(self.expr_match(
for_span,
iter,
@ -1997,7 +1998,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let val_expr = self.expr_ident(span, val_ident, val_pat_nid);
self.lower_attrs(val_expr.hir_id, &attrs, span, Target::Expression);
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
self.arm(continue_pat, val_expr, try_span)
self.arm(continue_pat, val_expr)
};
// `ControlFlow::Break(residual) =>
@ -2040,7 +2041,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_attrs(ret_expr.hir_id, &attrs, span, Target::Expression);
let break_pat = self.pat_cf_break(try_span, residual_local);
self.arm(break_pat, ret_expr, try_span)
self.arm(break_pat, ret_expr)
};
hir::ExprKind::Match(
@ -2368,13 +2369,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
&mut self,
pat: &'hir hir::Pat<'hir>,
expr: &'hir hir::Expr<'hir>,
span: Span,
) -> hir::Arm<'hir> {
hir::Arm {
hir_id: self.next_id(),
pat,
guard: None,
span: self.lower_span(span),
span: self.lower_span(expr.span),
body: expr,
}
}

View file

@ -205,7 +205,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
| ItemKind::Use(..)
| ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::ConstBlock(..)
| ItemKind::Mod(..)
| ItemKind::ForeignMod(..)
| ItemKind::GlobalAsm(..)
@ -283,13 +282,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.lower_define_opaque(hir_id, define_opaque);
hir::ItemKind::Static(*m, ident, ty, body_id)
}
ItemKind::Const(box ConstItem {
defaultness: _,
ident,
generics,
ty,
rhs_kind,
define_opaque,
ItemKind::Const(box ast::ConstItem {
ident, generics, ty, rhs, define_opaque, ..
}) => {
let ident = self.lower_ident(*ident);
let (generics, (ty, rhs)) = self.lower_generics(
@ -301,26 +295,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
let rhs = this.lower_const_item_rhs(rhs_kind, span);
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), span);
(ty, rhs)
},
);
self.lower_define_opaque(hir_id, &define_opaque);
hir::ItemKind::Const(ident, generics, ty, rhs)
}
ItemKind::ConstBlock(ConstBlockItem { span, id, block }) => hir::ItemKind::Const(
self.lower_ident(ConstBlockItem::IDENT),
hir::Generics::empty(),
self.arena.alloc(self.ty_tup(DUMMY_SP, &[])),
hir::ConstItemRhs::Body({
let body = hir::Expr {
hir_id: self.lower_node_id(*id),
kind: hir::ExprKind::Block(self.lower_block(block, false), None),
span: self.lower_span(*span),
};
self.record_body(&[], body)
}),
),
ItemKind::Fn(box Fn {
sig: FnSig { decl, header, span: fn_sig_span },
ident,
@ -827,10 +808,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir_id,
def_id: self.local_def_id(v.id),
data: self.lower_variant_data(hir_id, item_kind, &v.data),
disr_expr: v
.disr_expr
.as_ref()
.map(|e| self.lower_anon_const_to_anon_const(e, e.value.span)),
disr_expr: v.disr_expr.as_ref().map(|e| self.lower_anon_const_to_anon_const(e)),
ident: self.lower_ident(v.ident),
span: self.lower_span(v.span),
}
@ -920,10 +898,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
},
vis_span: self.lower_span(f.vis.span),
default: f
.default
.as_ref()
.map(|v| self.lower_anon_const_to_anon_const(v, v.value.span)),
default: f.default.as_ref().map(|v| self.lower_anon_const_to_anon_const(v)),
ty,
safety: self.lower_safety(f.safety, hir::Safety::Safe),
}
@ -939,14 +914,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let trait_item_def_id = hir_id.expect_owner();
let (ident, generics, kind, has_value) = match &i.kind {
let (ident, generics, kind, has_default) = match &i.kind {
AssocItemKind::Const(box ConstItem {
ident,
generics,
ty,
rhs_kind,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => {
let (generics, kind) = self.lower_generics(
generics,
@ -957,18 +927,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
// Trait associated consts don't need an expression/body.
let rhs = if rhs_kind.has_expr() {
Some(this.lower_const_item_rhs(rhs_kind, i.span))
} else {
None
};
hir::TraitItemKind::Const(ty, rhs, rhs_kind.is_type_const().into())
let rhs = rhs
.as_ref()
.map(|rhs| this.lower_const_item_rhs(attrs, Some(rhs), i.span));
hir::TraitItemKind::Const(ty, rhs)
},
);
if define_opaque.is_some() {
if rhs_kind.has_expr() {
if rhs.is_some() {
self.lower_define_opaque(hir_id, &define_opaque);
} else {
self.dcx().span_err(
@ -978,7 +945,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}
(*ident, generics, kind, rhs_kind.has_expr())
(*ident, generics, kind, rhs.is_some())
}
AssocItemKind::Fn(box Fn {
sig, ident, generics, body: None, define_opaque, ..
@ -1088,17 +1055,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
};
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value, || {
hir::Defaultness::Default { has_value }
});
let item = hir::TraitItem {
owner_id: trait_item_def_id,
ident: self.lower_ident(ident),
generics,
kind,
span: self.lower_span(i.span),
defaultness,
defaultness: hir::Defaultness::Default { has_value: has_default },
has_delayed_lints: !self.delayed_lints.is_empty(),
};
self.arena.alloc(item)
@ -1126,8 +1089,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
// to not cause an assertion failure inside the `lower_defaultness` function.
let has_val = true;
let (defaultness, defaultness_span) =
self.lower_defaultness(defaultness, has_val, || hir::Defaultness::Final);
let (defaultness, defaultness_span) = self.lower_defaultness(defaultness, has_val);
let modifiers = TraitBoundModifiers {
constness: BoundConstness::Never,
asyncness: BoundAsyncness::Normal,
@ -1156,8 +1118,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
) -> &'hir hir::ImplItem<'hir> {
// Since `default impl` is not yet implemented, this is always true in impls.
let has_value = true;
let (defaultness, _) =
self.lower_defaultness(i.kind.defaultness(), has_value, || hir::Defaultness::Final);
let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
let hir_id = hir::HirId::make_owner(self.current_hir_id_owner.def_id);
let attrs = self.lower_attrs(
hir_id,
@ -1168,12 +1129,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
let (ident, (generics, kind)) = match &i.kind {
AssocItemKind::Const(box ConstItem {
ident,
generics,
ty,
rhs_kind,
define_opaque,
..
ident, generics, ty, rhs, define_opaque, ..
}) => (
*ident,
self.lower_generics(
@ -1186,7 +1142,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
);
this.lower_define_opaque(hir_id, &define_opaque);
let rhs = this.lower_const_item_rhs(rhs_kind, i.span);
let rhs = this.lower_const_item_rhs(attrs, rhs.as_ref(), i.span);
hir::ImplItemKind::Const(ty, rhs)
},
),
@ -1310,14 +1266,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
&self,
d: Defaultness,
has_value: bool,
implicit: impl FnOnce() -> hir::Defaultness,
) -> (hir::Defaultness, Option<Span>) {
match d {
Defaultness::Implicit => (implicit(), None),
Defaultness::Default(sp) => {
(hir::Defaultness::Default { has_value }, Some(self.lower_span(sp)))
}
Defaultness::Final(sp) => (hir::Defaultness::Final, Some(self.lower_span(sp))),
Defaultness::Final => {
assert!(has_value);
(hir::Defaultness::Final, None)
}
}
}
@ -1415,7 +1372,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
// create a fake body so that the entire rest of the compiler doesn't have to deal with
// this as a special case.
return self.lower_fn_body(decl, contract, |this| {
if find_attr!(attrs, AttributeKind::RustcIntrinsic)
if attrs.iter().any(|a| a.has_name(sym::rustc_intrinsic))
|| this.tcx.is_sdylib_interface_build()
{
let span = this.lower_span(span);

View file

@ -88,6 +88,8 @@ mod pat;
mod path;
pub mod stability;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
struct LoweringContext<'a, 'hir> {
tcx: TyCtxt<'hir>,
resolver: &'a mut ResolverAstLowering,
@ -878,7 +880,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
(hir::ParamName::Fresh, hir::LifetimeParamKind::Elided(kind))
}
LifetimeRes::Static { .. } | LifetimeRes::Error(..) => return None,
LifetimeRes::Static { .. } | LifetimeRes::Error => return None,
res => panic!(
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
res, ident, ident.span
@ -1931,29 +1933,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
source: LifetimeSource,
syntax: LifetimeSyntax,
) -> &'hir hir::Lifetime {
let res = if let Some(res) = self.resolver.get_lifetime_res(id) {
match res {
LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param),
LifetimeRes::Fresh { param, .. } => {
assert_eq!(ident.name, kw::UnderscoreLifetime);
let param = self.local_def_id(param);
hir::LifetimeKind::Param(param)
}
LifetimeRes::Infer => {
assert_eq!(ident.name, kw::UnderscoreLifetime);
hir::LifetimeKind::Infer
}
LifetimeRes::Static { .. } => {
assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
hir::LifetimeKind::Static
}
LifetimeRes::Error(guar) => hir::LifetimeKind::Error(guar),
LifetimeRes::ElidedAnchor { .. } => {
panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span);
}
let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
let res = match res {
LifetimeRes::Param { param, .. } => hir::LifetimeKind::Param(param),
LifetimeRes::Fresh { param, .. } => {
assert_eq!(ident.name, kw::UnderscoreLifetime);
let param = self.local_def_id(param);
hir::LifetimeKind::Param(param)
}
LifetimeRes::Infer => {
assert_eq!(ident.name, kw::UnderscoreLifetime);
hir::LifetimeKind::Infer
}
LifetimeRes::Static { .. } => {
assert!(matches!(ident.name, kw::StaticLifetime | kw::UnderscoreLifetime));
hir::LifetimeKind::Static
}
LifetimeRes::Error => hir::LifetimeKind::Error,
LifetimeRes::ElidedAnchor { .. } => {
panic!("Unexpected `ElidedAnchar` {:?} at {:?}", ident, ident.span);
}
} else {
hir::LifetimeKind::Error(self.dcx().span_delayed_bug(ident.span, "unresolved lifetime"))
};
debug!(?res);
@ -2017,13 +2016,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// AST resolution emitted an error on those parameters, so we lower them using
// `ParamName::Error`.
let ident = self.lower_ident(param.ident);
let param_name = if let Some(LifetimeRes::Error(..)) =
self.resolver.get_lifetime_res(param.id)
{
ParamName::Error(ident)
} else {
ParamName::Plain(ident)
};
let param_name =
if let Some(LifetimeRes::Error) = self.resolver.get_lifetime_res(param.id) {
ParamName::Error(ident)
} else {
ParamName::Plain(ident)
};
let kind =
hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit };
@ -2378,20 +2376,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn lower_const_item_rhs(
&mut self,
rhs_kind: &ConstItemRhsKind,
attrs: &[hir::Attribute],
rhs: Option<&ConstItemRhs>,
span: Span,
) -> hir::ConstItemRhs<'hir> {
match rhs_kind {
ConstItemRhsKind::Body { rhs: Some(body) } => {
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
}
ConstItemRhsKind::Body { rhs: None } => {
hir::ConstItemRhs::Body(self.lower_const_body(span, None))
}
ConstItemRhsKind::TypeConst { rhs: Some(anon) } => {
match rhs {
Some(ConstItemRhs::TypeConst(anon)) => {
hir::ConstItemRhs::TypeConst(self.lower_anon_const_to_const_arg_and_alloc(anon))
}
ConstItemRhsKind::TypeConst { rhs: None } => {
None if attr::contains_name(attrs, sym::type_const) => {
let const_arg = ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Error(
@ -2401,6 +2394,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
hir::ConstItemRhs::TypeConst(self.arena.alloc(const_arg))
}
Some(ConstItemRhs::Body(body)) => {
hir::ConstItemRhs::Body(self.lower_const_body(span, Some(body)))
}
None => hir::ConstItemRhs::Body(self.lower_const_body(span, None)),
}
}
@ -2430,7 +2427,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
);
let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| {
let const_arg = self.lower_expr_to_const_arg_direct(arg);
let const_arg = if let ExprKind::ConstBlock(anon_const) = &arg.kind {
let def_id = self.local_def_id(anon_const.id);
let def_kind = self.tcx.def_kind(def_id);
assert_eq!(DefKind::AnonConst, def_kind);
self.lower_anon_const_to_const_arg(anon_const)
} else {
self.lower_expr_to_const_arg_direct(arg)
};
&*self.arena.alloc(const_arg)
}));
@ -2442,7 +2447,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
ExprKind::Tup(exprs) => {
let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| {
let expr = self.lower_expr_to_const_arg_direct(&expr);
let expr = if let ExprKind::ConstBlock(anon_const) = &expr.kind {
let def_id = self.local_def_id(anon_const.id);
let def_kind = self.tcx.def_kind(def_id);
assert_eq!(DefKind::AnonConst, def_kind);
self.lower_anon_const_to_const_arg(anon_const)
} else {
self.lower_expr_to_const_arg_direct(&expr)
};
&*self.arena.alloc(expr)
}));
@ -2482,7 +2496,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// then go unused as the `Target::ExprField` is not actually
// corresponding to `Node::ExprField`.
self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField);
let expr = self.lower_expr_to_const_arg_direct(&f.expr);
let expr = if let ExprKind::ConstBlock(anon_const) = &f.expr.kind {
let def_id = self.local_def_id(anon_const.id);
let def_kind = self.tcx.def_kind(def_id);
assert_eq!(DefKind::AnonConst, def_kind);
self.lower_anon_const_to_const_arg(anon_const)
} else {
self.lower_expr_to_const_arg_direct(&f.expr)
};
&*self.arena.alloc(hir::ConstArgExprField {
hir_id,
@ -2500,7 +2523,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
ExprKind::Array(elements) => {
let lowered_elems = self.arena.alloc_from_iter(elements.iter().map(|element| {
let const_arg = self.lower_expr_to_const_arg_direct(element);
let const_arg = if let ExprKind::ConstBlock(anon_const) = &element.kind {
let def_id = self.local_def_id(anon_const.id);
assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id));
self.lower_anon_const_to_const_arg(anon_const)
} else {
self.lower_expr_to_const_arg_direct(element)
};
&*self.arena.alloc(const_arg)
}));
let array_expr = self.arena.alloc(hir::ConstArgArrayExpr {
@ -2530,7 +2559,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
| ExprKind::Call(..)
| ExprKind::Tup(..)
| ExprKind::Array(..)
| ExprKind::ConstBlock(..)
)
{
return self.lower_expr_to_const_arg_direct(expr);
@ -2544,27 +2572,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
ConstArg {
hir_id: self.lower_node_id(expr.id),
kind: hir::ConstArgKind::Literal { lit: literal.node, negated: false },
kind: hir::ConstArgKind::Literal(literal.node),
span,
}
}
ExprKind::Unary(UnOp::Neg, inner_expr)
if let ExprKind::Lit(literal) = &inner_expr.kind =>
{
let span = expr.span;
let literal = self.lower_lit(literal, span);
ConstArg {
hir_id: self.lower_node_id(expr.id),
kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true },
span,
}
}
ExprKind::ConstBlock(anon_const) => {
let def_id = self.local_def_id(anon_const.id);
assert_eq!(DefKind::AnonConst, self.tcx.def_kind(def_id));
self.lower_anon_const_to_const_arg(anon_const, span)
}
_ => overly_complex_const(self),
}
}
@ -2575,15 +2586,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&mut self,
anon: &AnonConst,
) -> &'hir hir::ConstArg<'hir> {
self.arena.alloc(self.lower_anon_const_to_const_arg(anon, anon.value.span))
self.arena.alloc(self.lower_anon_const_to_const_arg(anon))
}
#[instrument(level = "debug", skip(self))]
fn lower_anon_const_to_const_arg(
&mut self,
anon: &AnonConst,
span: Span,
) -> hir::ConstArg<'hir> {
fn lower_anon_const_to_const_arg(&mut self, anon: &AnonConst) -> hir::ConstArg<'hir> {
let tcx = self.tcx;
// We cannot change parsing depending on feature gates available,
@ -2594,7 +2601,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
if tcx.features().min_generic_const_args() {
return match anon.mgca_disambiguation {
MgcaDisambiguation::AnonConst => {
let lowered_anon = self.lower_anon_const_to_anon_const(anon, span);
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(lowered_anon),
@ -2640,7 +2647,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
};
}
let lowered_anon = self.lower_anon_const_to_anon_const(anon, anon.value.span);
let lowered_anon = self.lower_anon_const_to_anon_const(anon);
ConstArg {
hir_id: self.next_id(),
kind: hir::ConstArgKind::Anon(lowered_anon),
@ -2650,11 +2657,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// See [`hir::ConstArg`] for when to use this function vs
/// [`Self::lower_anon_const_to_const_arg`].
fn lower_anon_const_to_anon_const(
&mut self,
c: &AnonConst,
span: Span,
) -> &'hir hir::AnonConst {
fn lower_anon_const_to_anon_const(&mut self, c: &AnonConst) -> &'hir hir::AnonConst {
self.arena.alloc(self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
let hir_id = this.lower_node_id(c.id);
@ -2662,7 +2665,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
def_id,
hir_id,
body: this.lower_const_body(c.value.span, Some(&c.value)),
span: this.lower_span(span),
span: this.lower_span(c.value.span),
}
}))
}

View file

@ -29,6 +29,7 @@ pub(crate) fn extern_abi_enabled(
})
}
#[allow(rustc::untranslatable_diagnostic)]
pub(crate) fn gate_unstable_abi(sess: &Session, features: &Features, span: Span, abi: ExternAbi) {
match extern_abi_enabled(features, span, abi) {
Ok(_) => (),
@ -95,11 +96,6 @@ pub fn extern_abi_stability(abi: ExternAbi) -> Result<(), UnstableAbi> {
ExternAbi::RustCold => {
Err(UnstableAbi { abi, feature: sym::rust_cold_cc, explain: GateReason::Experimental })
}
ExternAbi::RustPreserveNone => Err(UnstableAbi {
abi,
feature: sym::rust_preserve_none_cc,
explain: GateReason::Experimental,
}),
ExternAbi::RustInvalid => {
Err(UnstableAbi { abi, feature: sym::rustc_attrs, explain: GateReason::ImplDetail })
}

View file

@ -13,6 +13,7 @@ rustc_attr_parsing = { path = "../rustc_attr_parsing" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_macros = { path = "../rustc_macros" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }

View file

@ -0,0 +1,342 @@
ast_passes_abi_cannot_be_coroutine =
functions with the {$abi} ABI cannot be `{$coroutine_kind_str}`
.suggestion = remove the `{$coroutine_kind_str}` keyword from this definition
ast_passes_abi_custom_safe_foreign_function =
foreign functions with the "custom" ABI cannot be safe
.suggestion = remove the `safe` keyword from this definition
ast_passes_abi_custom_safe_function =
functions with the "custom" ABI must be unsafe
.suggestion = add the `unsafe` keyword to this definition
ast_passes_abi_must_not_have_parameters_or_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the {$abi} ABI cannot have any parameters or return type
.suggestion = remove the parameters and return type
ast_passes_abi_must_not_have_return_type=
invalid signature for `extern {$abi}` function
.note = functions with the {$abi} ABI cannot have a return type
.help = remove the return type
ast_passes_abi_x86_interrupt =
invalid signature for `extern "x86-interrupt"` function
.note = functions with the "x86-interrupt" ABI must be have either 1 or 2 parameters (but found {$param_count})
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant
ast_passes_assoc_fn_without_body =
associated function in `impl` without body
.suggestion = provide a definition for the function
ast_passes_assoc_type_without_body =
associated type in `impl` without body
.suggestion = provide a definition for the type
ast_passes_async_fn_in_const_trait_or_trait_impl =
async functions are not allowed in `const` {$context ->
[trait_impl] trait impls
[impl] impls
*[trait] traits
}
.label = associated functions of `const` cannot be declared `async`
ast_passes_at_least_one_trait = at least one trait must be specified
ast_passes_auto_generic = auto traits cannot have generic parameters
.label = auto trait cannot have generic parameters
.suggestion = remove the parameters
ast_passes_auto_items = auto traits cannot have associated items
.label = {ast_passes_auto_items}
.suggestion = remove the associated items
ast_passes_auto_super_lifetime = auto traits cannot have super traits or lifetime bounds
.label = {ast_passes_auto_super_lifetime}
.suggestion = remove the super traits or lifetime bounds
ast_passes_body_in_extern = incorrect `{$kind}` inside `extern` block
.cannot_have = cannot have a body
.invalid = the invalid body
.existing = `extern` blocks define existing foreign {$kind}s and {$kind}s inside of them cannot have a body
ast_passes_bound_in_context = bounds on `type`s in {$ctx} have no effect
ast_passes_c_variadic_bad_extern = `...` is not supported for `extern "{$abi}"` functions
.label = `extern "{$abi}"` because of this
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
ast_passes_c_variadic_bad_naked_extern = `...` is not supported for `extern "{$abi}"` naked functions
.label = `extern "{$abi}"` because of this
.help = C-variadic function must have a compatible calling convention
ast_passes_c_variadic_must_be_unsafe =
functions with a C variable argument list must be unsafe
.suggestion = add the `unsafe` keyword to this definition
ast_passes_c_variadic_no_extern = `...` is not supported for non-extern functions
.help = only `extern "C"` and `extern "C-unwind"` functions may have a C variable argument list
ast_passes_c_variadic_not_supported = the `{$target}` target does not support c-variadic functions
ast_passes_const_and_c_variadic = functions cannot be both `const` and C-variadic
.const = `const` because of this
.variadic = C-variadic because of this
ast_passes_const_and_coroutine = functions cannot be both `const` and `{$coroutine_kind}`
.const = `const` because of this
.coroutine = `{$coroutine_kind}` because of this
.label = {""}
ast_passes_const_auto_trait = auto traits cannot be const
.help = remove the `const` keyword
ast_passes_const_bound_trait_object = const trait bounds are not allowed in trait object types
ast_passes_const_without_body =
free constant item without body
.suggestion = provide a definition for the constant
ast_passes_constraint_on_negative_bound =
associated type constraints not allowed on negative bounds
ast_passes_coroutine_and_c_variadic = functions cannot be both `{$coroutine_kind}` and C-variadic
.const = `{$coroutine_kind}` because of this
.variadic = C-variadic because of this
ast_passes_equality_in_where = equality constraints are not yet supported in `where` clauses
.label = not supported
.suggestion = if `{$ident}` is an associated type you're trying to set, use the associated type binding syntax
.suggestion_path = if `{$trait_segment}::{$potential_assoc}` is an associated type you're trying to set, use the associated type binding syntax
.note = see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
ast_passes_extern_block_suggestion = if you meant to declare an externally defined function, use an `extern` block
ast_passes_extern_fn_qualifiers = functions in `extern` blocks cannot have `{$kw}` qualifier
.label = in this `extern` block
.suggestion = remove the `{$kw}` qualifier
ast_passes_extern_invalid_safety = items in `extern` blocks without an `unsafe` qualifier cannot have safety qualifiers
.suggestion = add `unsafe` to this `extern` block
ast_passes_extern_item_ascii = items in `extern` blocks cannot use non-ascii identifiers
.label = in this `extern` block
.note = this limitation may be lifted in the future; see issue #83942 <https://github.com/rust-lang/rust/issues/83942> for more information
ast_passes_extern_keyword_link = for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
ast_passes_extern_types_cannot = `type`s inside `extern` blocks cannot have {$descr}
.suggestion = remove the {$remove_descr}
.label = `extern` block begins here
ast_passes_extern_without_abi = `extern` declarations without an explicit ABI are disallowed
.suggestion = specify an ABI
.help = prior to Rust 2024, a default ABI was inferred
ast_passes_extern_without_abi_sugg = `extern` declarations without an explicit ABI are deprecated
.label = ABI should be specified here
.suggestion = explicitly specify the {$default_abi} ABI
ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
.suggestion = remove the attribute
.stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
ast_passes_fieldless_union = unions cannot have zero fields
ast_passes_fn_body_extern = incorrect function inside `extern` block
.cannot_have = cannot have a body
.suggestion = remove the invalid body
.help = you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
.label = `extern` blocks define existing foreign functions and functions inside of them cannot have a body
ast_passes_fn_param_c_var_args_not_last =
`...` must be the last argument of a C-variadic function
ast_passes_fn_param_doc_comment =
documentation comments cannot be applied to function parameters
.label = doc comments are not allowed here
ast_passes_fn_param_forbidden_attr =
allow, cfg, cfg_attr, deny, expect, forbid, and warn are the only allowed built-in attributes in function parameters
ast_passes_fn_param_forbidden_self =
`self` parameter is only allowed in associated functions
.label = not semantically valid as function parameter
.note = associated functions are those in `impl` or `trait` definitions
ast_passes_fn_param_too_many =
function can not have more than {$max_num_args} arguments
ast_passes_fn_ptr_invalid_safety = function pointers cannot be declared with `safe` safety qualifier
.suggestion = remove safe from this item
ast_passes_fn_without_body =
free function without a body
.suggestion = provide a definition for the function
ast_passes_forbidden_bound =
bounds cannot be used in this context
ast_passes_forbidden_const_param =
late-bound const parameters cannot be used currently
ast_passes_forbidden_default =
`default` is only allowed on items in trait impls
.label = `default` because of this
ast_passes_forbidden_non_lifetime_param =
only lifetime parameters can be used in this context
ast_passes_generic_before_constraints = generic arguments must come before the first constraint
.constraints = {$constraint_len ->
[one] constraint
*[other] constraints
}
.args = generic {$args_len ->
[one] argument
*[other] arguments
}
.empty_string = {""},
.suggestion = move the {$constraint_len ->
[one] constraint
*[other] constraints
} after the generic {$args_len ->
[one] argument
*[other] arguments
}
ast_passes_generic_default_trailing = generic parameters with a default must be trailing
ast_passes_impl_fn_const =
redundant `const` fn marker in const impl
.parent_constness = this declares all associated functions implicitly const
.label = remove the `const`
ast_passes_incompatible_features = `{$f1}` and `{$f2}` are incompatible, using them at the same time is not allowed
.help = remove one of these features
ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
.suggestion = remove safe from this item
ast_passes_item_underscore = `{$kind}` items in this context need a name
.label = `_` is not a valid name for this `{$kind}` item
ast_passes_match_arm_with_no_body =
`match` arm with no body
.suggestion = add a body after the pattern
ast_passes_missing_unsafe_on_extern = extern blocks must be unsafe
.suggestion = needs `unsafe` before the extern keyword
ast_passes_missing_unsafe_on_extern_lint = extern blocks should be unsafe
.suggestion = needs `unsafe` before the extern keyword
ast_passes_module_nonascii = trying to load file for module `{$name}` with non-ascii identifier name
.help = consider using the `#[path]` attribute to specify filesystem path
ast_passes_negative_bound_not_supported =
negative bounds are not supported
ast_passes_negative_bound_with_parenthetical_notation =
parenthetical notation may not be used for negative bounds
ast_passes_nested_impl_trait = nested `impl Trait` is not allowed
.outer = outer `impl Trait`
.inner = nested `impl Trait` here
ast_passes_nested_lifetimes = nested quantification of lifetimes
ast_passes_nomangle_ascii = `#[no_mangle]` requires ASCII identifier
ast_passes_obsolete_auto = `impl Trait for .. {"{}"}` is an obsolete syntax
.help = use `auto trait Trait {"{}"}` instead
ast_passes_out_of_order_params = {$param_ord} parameters must be declared prior to {$max_param} parameters
.suggestion = reorder the parameters: lifetimes, then consts and types
ast_passes_pattern_in_bodiless = patterns aren't allowed in functions without bodies
.label = pattern not allowed in function without body
ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer types
ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations
.label = pattern not allowed in foreign function
ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing syntax
.label = second `use<...>` here
ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
ast_passes_scalable_vector_not_tuple_struct = scalable vectors must be tuple structs
ast_passes_static_without_body =
free static item without body
.suggestion = provide a definition for the static
ast_passes_tilde_const_disallowed = `[const]` is not allowed here
.closure = closures cannot have `[const]` trait bounds
.function = this function is not `const`, so it cannot have `[const]` trait bounds
.trait = this trait is not `const`, so it cannot have `[const]` trait bounds
.trait_impl = this impl is not `const`, so it cannot have `[const]` trait bounds
.impl = inherent impls cannot have `[const]` trait bounds
.trait_assoc_ty = associated types in non-`const` traits cannot have `[const]` trait bounds
.trait_impl_assoc_ty = associated types in non-const impls cannot have `[const]` trait bounds
.inherent_assoc_ty = inherent associated types cannot have `[const]` trait bounds
.struct = structs cannot have `[const]` trait bounds
.enum = enums cannot have `[const]` trait bounds
.union = unions cannot have `[const]` trait bounds
.anon_const = anonymous constants cannot have `[const]` trait bounds
.object = trait objects cannot have `[const]` trait bounds
.item = this item cannot have `[const]` trait bounds
ast_passes_trait_fn_const =
functions in {$in_impl ->
[true] trait impls
*[false] traits
} cannot be declared const
.label = functions in {$in_impl ->
[true] trait impls
*[false] traits
} cannot be const
.const_context_label = this declares all associated functions implicitly const
.remove_const_sugg = remove the `const`{$requires_multiple_changes ->
[true] {" ..."}
*[false] {""}
}
.make_impl_const_sugg = ... and declare the impl to be const instead
.make_trait_const_sugg = ... and declare the trait to be const instead
ast_passes_trait_object_single_bound = only a single explicit lifetime bound is permitted
ast_passes_ty_alias_without_body =
free type alias without body
.suggestion = provide a definition for the type
ast_passes_unsafe_item = {$kind} cannot be declared unsafe
ast_passes_unsafe_negative_impl = negative impls cannot be unsafe
.negative = negative because of this
.unsafe = unsafe because of this
ast_passes_unsafe_static =
static items cannot be declared with `unsafe` safety qualifier outside of `extern` block
ast_passes_visibility_not_permitted =
visibility qualifiers are not permitted here
.enum_variant = enum variants and their fields always share the visibility of the enum they are in
.trait_impl = trait items always share the visibility of their trait
.individual_impl_items = place qualifiers on individual impl items instead
.individual_foreign_items = place qualifiers on individual foreign items instead
.remove_qualifier_sugg = remove the qualifier
ast_passes_where_clause_after_type_alias = where clauses are not allowed after the type for type aliases
.note = see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
.help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable
ast_passes_where_clause_before_type_alias = where clauses are not allowed before the type for type aliases
.note = see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information
.remove_suggestion = remove this `where`
.move_suggestion = move it to the end of the type declaration

View file

@ -65,28 +65,6 @@ impl TraitOrImpl {
}
}
enum AllowDefault {
Yes,
No,
}
impl AllowDefault {
fn when(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}
enum AllowFinal {
Yes,
No,
}
impl AllowFinal {
fn when(b: bool) -> Self {
if b { Self::Yes } else { Self::No }
}
}
struct AstValidator<'a> {
sess: &'a Session,
features: &'a Features,
@ -422,7 +400,6 @@ impl<'a> AstValidator<'a> {
CanonAbi::C
| CanonAbi::Rust
| CanonAbi::RustCold
| CanonAbi::RustPreserveNone
| CanonAbi::Arm(_)
| CanonAbi::X86(_) => { /* nothing to check */ }
@ -585,32 +562,10 @@ impl<'a> AstValidator<'a> {
}
}
fn check_defaultness(
&self,
span: Span,
defaultness: Defaultness,
allow_default: AllowDefault,
allow_final: AllowFinal,
) {
match defaultness {
Defaultness::Default(def_span) if matches!(allow_default, AllowDefault::No) => {
let span = self.sess.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenDefault { span, def_span });
}
Defaultness::Final(def_span) if matches!(allow_final, AllowFinal::No) => {
let span = self.sess.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenFinal { span, def_span });
}
_ => (),
}
}
fn check_final_has_body(&self, item: &Item<AssocItemKind>, defaultness: Defaultness) {
if let AssocItemKind::Fn(box Fn { body: None, .. }) = &item.kind
&& let Defaultness::Final(def_span) = defaultness
{
let span = self.sess.source_map().guess_head_span(item.span);
self.dcx().emit_err(errors::ForbiddenFinalWithoutBody { span, def_span });
fn check_defaultness(&self, span: Span, defaultness: Defaultness) {
if let Defaultness::Default(def_span) = defaultness {
let span = self.sess.source_map().guess_head_span(span);
self.dcx().emit_err(errors::ForbiddenDefault { span, def_span });
}
}
@ -742,11 +697,13 @@ impl<'a> AstValidator<'a> {
unreachable!("C variable argument list cannot be used in closures")
};
if let Const::Yes(_) = sig.header.constness
&& !self.features.enabled(sym::const_c_variadic)
{
let msg = format!("c-variadic const function definitions are unstable");
feature_err(&self.sess, sym::const_c_variadic, sig.span, msg).emit();
// C-variadics are not yet implemented in const evaluation.
if let Const::Yes(const_span) = sig.header.constness {
self.dcx().emit_err(errors::ConstAndCVariadic {
spans: vec![const_span, variadic_param.span],
const_span,
variadic_span: variadic_param.span,
});
}
if let Some(coroutine_kind) = sig.header.coroutine_kind {
@ -1234,7 +1191,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
},
) => {
self.visit_attrs_vis_ident(&item.attrs, &item.vis, ident);
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_defaultness(item.span, *defaultness);
for EiiImpl { eii_macro_path, .. } in eii_impls {
self.visit_path(eii_macro_path);
@ -1403,9 +1360,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
});
}
ItemKind::Const(box ConstItem { defaultness, ident, rhs_kind, .. }) => {
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
if !rhs_kind.has_expr() {
ItemKind::Const(box ConstItem { defaultness, ident, rhs, .. }) => {
self.check_defaultness(item.span, *defaultness);
if rhs.is_none() {
self.dcx().emit_err(errors::ConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
@ -1442,7 +1399,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
ItemKind::TyAlias(
ty_alias @ box TyAlias { defaultness, bounds, after_where_clause, ty, .. },
) => {
self.check_defaultness(item.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_defaultness(item.span, *defaultness);
if ty.is_none() {
self.dcx().emit_err(errors::TyAliasWithoutBody {
span: item.span,
@ -1472,7 +1429,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_foreign_item(&mut self, fi: &'a ForeignItem) {
match &fi.kind {
ForeignItemKind::Fn(box Fn { defaultness, ident, sig, body, .. }) => {
self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_fn_bodyless(*ident, body.as_deref());
self.check_foreign_fn_headerless(sig.header);
self.check_foreign_item_ascii_only(*ident);
@ -1492,7 +1449,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
ty,
..
}) => {
self.check_defaultness(fi.span, *defaultness, AllowDefault::No, AllowFinal::No);
self.check_defaultness(fi.span, *defaultness);
self.check_foreign_kind_bodyless(*ident, "type", ty.as_ref().map(|b| b.span));
self.check_type_no_bounds(bounds, "`extern` blocks");
self.check_foreign_ty_genericless(generics, after_where_clause);
@ -1751,29 +1708,17 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_nomangle_item_asciionly(ident, item.span);
}
let defaultness = item.kind.defaultness();
self.check_defaultness(
item.span,
defaultness,
// `default` is allowed on all associated items in impls.
AllowDefault::when(matches!(ctxt, AssocCtxt::Impl { .. })),
// `final` is allowed on all associated *functions* in traits.
AllowFinal::when(
ctxt == AssocCtxt::Trait && matches!(item.kind, AssocItemKind::Fn(..)),
),
);
self.check_final_has_body(item, defaultness);
if ctxt == AssocCtxt::Trait || self.outer_trait_or_trait_impl.is_none() {
self.check_defaultness(item.span, item.kind.defaultness());
}
if let AssocCtxt::Impl { .. } = ctxt {
match &item.kind {
AssocItemKind::Const(box ConstItem { rhs_kind, .. }) => {
if !rhs_kind.has_expr() {
self.dcx().emit_err(errors::AssocConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
}
AssocItemKind::Const(box ConstItem { rhs: None, .. }) => {
self.dcx().emit_err(errors::AssocConstWithoutBody {
span: item.span,
replace_span: self.ending_semi_or_hi(item.span),
});
}
AssocItemKind::Fn(box Fn { body, .. }) => {
if body.is_none() && !self.is_sdylib_interface {

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,5 @@
use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
use rustc_ast::{self as ast, AttrVec, NodeId, PatKind, attr, token};
use rustc_errors::msg;
use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features};
use rustc_session::Session;
use rustc_session::parse::{feature_err, feature_warn};
@ -14,11 +13,15 @@ use crate::errors;
macro_rules! gate {
($visitor:expr, $feature:ident, $span:expr, $explain:expr) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
feature_err(&$visitor.sess, sym::$feature, $span, $explain).emit();
}
}};
($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
if !$visitor.features.$feature() && !$span.allows_unstable(sym::$feature) {
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit();
}
}};
@ -28,11 +31,13 @@ macro_rules! gate {
macro_rules! gate_alt {
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr) => {{
if !$has_feature && !$span.allows_unstable($name) {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
feature_err(&$visitor.sess, $name, $span, $explain).emit();
}
}};
($visitor:expr, $has_feature:expr, $name:expr, $span:expr, $explain:expr, $notes: expr) => {{
if !$has_feature && !$span.allows_unstable($name) {
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
let mut diag = feature_err(&$visitor.sess, $name, $span, $explain);
for note in $notes {
diag.note(*note);
@ -125,7 +130,7 @@ impl<'a> PostExpansionVisitor<'a> {
&self,
non_lifetime_binders,
non_lt_param_spans,
msg!("only lifetime parameters can be used in this context")
crate::fluent_generated::ast_passes_forbidden_non_lifetime_param
);
// FIXME(non_lifetime_binders): Const bound params are pretty broken.
@ -249,14 +254,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
ast::ItemKind::TyAlias(box ast::TyAlias { ty: Some(ty), .. }) => {
self.check_impl_trait(ty, false)
}
ast::ItemKind::Const(box ast::ConstItem {
rhs_kind: ast::ConstItemRhsKind::TypeConst { .. },
..
}) => {
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(min_generic_const_args)]
gate!(&self, min_generic_const_args, i.span, "top-level `type const` are unstable");
}
_ => {}
}
@ -338,11 +335,15 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.kind {
ast::ExprKind::TryBlock(_, None) => {
// `try { ... }` is old and is only gated post-expansion here.
gate!(&self, try_blocks, e.span, "`try` expression is experimental");
}
ast::ExprKind::TryBlock(_, Some(_)) => {
// `try_blocks_heterogeneous` is new, and gated pre-expansion instead.
gate!(
&self,
try_blocks_heterogeneous,
e.span,
"`try bikeshed` expression is experimental"
);
}
ast::ExprKind::Lit(token::Lit {
kind: token::LitKind::Float | token::LitKind::Integer,
@ -426,20 +427,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
}
false
}
ast::AssocItemKind::Const(box ast::ConstItem {
rhs_kind: ast::ConstItemRhsKind::TypeConst { .. },
..
}) => {
// Make sure this is only allowed if the feature gate is enabled.
// #![feature(min_generic_const_args)]
gate!(
&self,
min_generic_const_args,
i.span,
"associated `type const` are unstable"
);
false
}
_ => false,
};
if let ast::Defaultness::Default(_) = i.kind.defaultness() {
@ -459,7 +446,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
maybe_stage_features(sess, features, krate);
check_incompatible_features(sess, features);
check_dependent_features(sess, features);
check_new_solver_banned_features(sess, features);
let mut visitor = PostExpansionVisitor { sess, features };
@ -505,6 +491,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
&& (!visitor.features.gen_blocks() && !span.allows_unstable(sym::gen_blocks))
&& (!visitor.features.yield_expr() && !span.allows_unstable(sym::yield_expr))
{
#[allow(rustc::untranslatable_diagnostic)]
// Emit yield_expr as the error, since that will be sufficient. You can think of it
// as coroutines and gen_blocks imply yield_expr.
feature_err(&visitor.sess, sym::yield_expr, *span, "yield syntax is experimental")
@ -518,7 +505,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
half_open_range_patterns_in_slices,
"half-open range patterns in slices are unstable"
);
gate_all!(try_blocks_heterogeneous, "`try bikeshed` expression is experimental");
gate_all!(yeet_expr, "`do yeet` expression is experimental");
gate_all!(const_closures, "const closures are experimental");
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
@ -537,6 +523,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
if !visitor.features.min_generic_const_args()
&& !span.allows_unstable(sym::min_generic_const_args)
{
#[allow(rustc::untranslatable_diagnostic)]
feature_err(
&visitor.sess,
sym::min_generic_const_args,
@ -547,27 +534,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
}
}
}
// `mgca_type_const_syntax` is part of `min_generic_const_args` so either
// or both are enabled we don't need to emit a feature error.
if let Some(spans) = spans.get(&sym::mgca_type_const_syntax) {
for span in spans {
if visitor.features.min_generic_const_args()
|| visitor.features.mgca_type_const_syntax()
|| span.allows_unstable(sym::min_generic_const_args)
|| span.allows_unstable(sym::mgca_type_const_syntax)
{
continue;
}
feature_err(
&visitor.sess,
sym::min_generic_const_args,
*span,
"`type const` syntax is experimental",
)
.emit();
}
}
gate_all!(global_registration, "global registration is experimental");
gate_all!(return_type_notation, "return type notation is experimental");
gate_all!(pin_ergonomics, "pinned reference syntax is experimental");
@ -579,8 +545,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(super_let, "`super let` is experimental");
gate_all!(frontmatter, "frontmatters are experimental");
gate_all!(coroutines, "coroutine syntax is experimental");
gate_all!(const_block_items, "const block items are experimental");
gate_all!(final_associated_functions, "`final` on trait functions is experimental");
if !visitor.features.never_patterns() {
if let Some(spans) = spans.get(&sym::never_patterns) {
@ -595,6 +559,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
if let Ok(snippet) = sm.span_to_snippet(span)
&& snippet == "!"
{
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
feature_err(sess, sym::never_patterns, span, "`!` patterns are experimental")
.emit();
} else {
@ -691,27 +656,6 @@ fn check_incompatible_features(sess: &Session, features: &Features) {
}
}
fn check_dependent_features(sess: &Session, features: &Features) {
for &(parent, children) in
rustc_feature::DEPENDENT_FEATURES.iter().filter(|(parent, _)| features.enabled(*parent))
{
if children.iter().any(|f| !features.enabled(*f)) {
let parent_span = features
.enabled_features_iter_stable_order()
.find_map(|(name, span)| (name == parent).then_some(span))
.unwrap();
// FIXME: should probably format this in fluent instead of here
let missing = children
.iter()
.filter(|f| !features.enabled(**f))
.map(|s| format!("`{}`", s.as_str()))
.intersperse(String::from(", "))
.collect();
sess.dcx().emit_err(errors::MissingDependentFeatures { parent_span, parent, missing });
}
}
}
fn check_new_solver_banned_features(sess: &Session, features: &Features) {
if !sess.opts.unstable_opts.next_solver.globally {
return;

View file

@ -5,10 +5,11 @@
// tidy-alphabetical-start
#![feature(box_patterns)]
#![feature(if_let_guard)]
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
// tidy-alphabetical-end
pub mod ast_validation;
mod errors;
pub mod feature_gate;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -1961,8 +1961,7 @@ impl<'a> State<'a> {
}
fn print_lifetime(&mut self, lifetime: ast::Lifetime) {
self.word(lifetime.ident.name.to_string());
self.ann_post(lifetime.ident)
self.print_name(lifetime.ident.name)
}
fn print_lifetime_bounds(&mut self, bounds: &ast::GenericBounds) {

View file

@ -51,7 +51,7 @@ impl<'a> State<'a> {
expr.as_deref(),
vis,
*safety,
ast::Defaultness::Implicit,
ast::Defaultness::Final,
define_opaque.as_deref(),
),
ast::ForeignItemKind::TyAlias(box ast::TyAlias {
@ -201,27 +201,16 @@ impl<'a> State<'a> {
body.as_deref(),
&item.vis,
ast::Safety::Default,
ast::Defaultness::Implicit,
ast::Defaultness::Final,
define_opaque.as_deref(),
);
}
ast::ItemKind::ConstBlock(ast::ConstBlockItem { id: _, span: _, block }) => {
let ib = self.ibox(INDENT_UNIT);
self.word("const");
self.nbsp();
{
let cb = self.cbox(0);
let ib = self.ibox(0);
self.print_block_with_attrs(block, &[], cb, ib);
}
self.end(ib);
}
ast::ItemKind::Const(box ast::ConstItem {
defaultness,
ident,
generics,
ty,
rhs_kind,
rhs,
define_opaque,
}) => {
self.print_item_const(
@ -229,7 +218,7 @@ impl<'a> State<'a> {
None,
generics,
ty,
rhs_kind.expr(),
rhs.as_ref().map(|ct| ct.expr()),
&item.vis,
ast::Safety::Default,
*defaultness,
@ -573,7 +562,7 @@ impl<'a> State<'a> {
ident,
generics,
ty,
rhs_kind,
rhs,
define_opaque,
}) => {
self.print_item_const(
@ -581,7 +570,7 @@ impl<'a> State<'a> {
None,
generics,
ty,
rhs_kind.expr(),
rhs.as_ref().map(|ct| ct.expr()),
vis,
ast::Safety::Default,
*defaultness,

View file

@ -8,9 +8,9 @@ edition = "2024"
rustc_abi = { path = "../rustc_abi" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_feature = { path = "../rustc_feature" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_hir = { path = "../rustc_hir" }
rustc_lexer = { path = "../rustc_lexer" }
rustc_macros = { path = "../rustc_macros" }

View file

@ -0,0 +1,246 @@
attr_parsing_as_needed_compatibility =
linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds
attr_parsing_bundle_needs_static =
linking modifier `bundle` is only compatible with `static` linking kind
attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters
attr_parsing_deprecated_item_suggestion =
suggestions on deprecated items are unstable
.help = add `#![feature(deprecated_suggestion)]` to the crate root
.note = see #94785 for more details
attr_parsing_doc_alias_bad_char =
{$char_} character isn't allowed in {$attr_str}
attr_parsing_doc_alias_empty =
{$attr_str} attribute cannot have empty value
attr_parsing_doc_alias_malformed =
doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]`
attr_parsing_doc_alias_start_end =
{$attr_str} cannot start or end with ' '
attr_parsing_doc_attr_not_crate_level =
`#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute
attr_parsing_doc_attribute_not_attribute =
nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]`
.help = only existing builtin attributes are allowed in core/std
attr_parsing_doc_keyword_not_keyword =
nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]`
.help = only existing keywords are allowed in core/std
attr_parsing_empty_confusables =
expected at least one confusable name
attr_parsing_empty_link_name =
link name must not be empty
.label = empty link name
attr_parsing_expected_single_version_literal =
expected single version literal
attr_parsing_expected_version_literal =
expected a version literal
attr_parsing_expects_feature_list =
`{$name}` expects a list of feature names
attr_parsing_expects_features =
`{$name}` expects feature names
attr_parsing_import_name_type_raw =
import name type can only be used with link kind `raw-dylib`
attr_parsing_import_name_type_x86 =
import name type is only supported on x86
attr_parsing_incompatible_wasm_link =
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes
attr_parsing_incorrect_repr_format_align_one_arg =
incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
attr_parsing_incorrect_repr_format_expect_literal_integer =
incorrect `repr(align)` attribute format: `align` expects a literal integer as argument
attr_parsing_incorrect_repr_format_generic =
incorrect `repr({$repr_arg})` attribute format
.suggestion = use parentheses instead
attr_parsing_incorrect_repr_format_packed_expect_integer =
incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument
attr_parsing_incorrect_repr_format_packed_one_or_zero_arg =
incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
attr_parsing_invalid_alignment_value =
invalid alignment value: {$error_part}
attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute
.label = this is not an unsafe attribute
.suggestion = remove the `unsafe(...)`
.note = extraneous unsafe is not allowed in attributes
attr_parsing_invalid_issue_string =
`issue` must be a non-zero numeric string or "none"
.must_not_be_zero = `issue` must not be "0", use "none" instead
.empty = cannot parse integer from empty string
.invalid_digit = invalid digit found in string
.pos_overflow = number too large to fit in target type
.neg_overflow = number too small to fit in target type
attr_parsing_invalid_link_modifier =
invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed
attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr}
.remove_neg_sugg = negative numbers are not literals, try removing the `-` sign
.quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal
.label = {$descr}s are not allowed here
attr_parsing_invalid_predicate =
invalid predicate `{$predicate}`
attr_parsing_invalid_repr_align_need_arg =
invalid `repr(align)` attribute: `align` needs an argument
.suggestion = supply an argument here
attr_parsing_invalid_repr_generic =
invalid `repr({$repr_arg})` attribute: {$error_part}
attr_parsing_invalid_repr_hint_no_paren =
invalid representation hint: `{$name}` does not take a parenthesized argument list
attr_parsing_invalid_repr_hint_no_value =
invalid representation hint: `{$name}` does not take a value
attr_parsing_invalid_since =
'since' must be a Rust version number, such as "1.31.0"
attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target}
.help = `#[{$name}]` can {$only}be applied to {$applied}
.suggestion = remove the attribute
attr_parsing_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}
attr_parsing_link_arg_unstable =
link kind `link-arg` is unstable
attr_parsing_link_cfg_unstable =
link cfg is unstable
attr_parsing_link_framework_apple =
link kind `framework` is only supported on Apple targets
attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}`
.note = the value may not exceed `u16::MAX`
attr_parsing_link_requires_name =
`#[link]` attribute requires a `name = "string"` argument
.label = missing `name` argument
attr_parsing_meta_bad_delim = wrong meta list delimiters
attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)`
attr_parsing_missing_feature =
missing 'feature'
attr_parsing_missing_issue =
missing 'issue'
attr_parsing_missing_note =
missing 'note'
attr_parsing_missing_since =
missing 'since'
attr_parsing_multiple_modifiers =
multiple `{$modifier}` modifiers in a single `modifiers` argument
attr_parsing_multiple_stability_levels =
multiple stability levels
attr_parsing_naked_functions_incompatible_attribute =
attribute incompatible with `#[unsafe(naked)]`
.label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`
.naked_attribute = function marked with `#[unsafe(naked)]` here
attr_parsing_non_ident_feature =
'feature' is not an identifier
attr_parsing_null_on_export = `export_name` may not contain null characters
attr_parsing_null_on_link_section = `link_section` may not contain null characters
attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters
attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters
attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal
attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal
attr_parsing_raw_dylib_elf_unstable =
link kind `raw-dylib` is unstable on ELF platforms
attr_parsing_raw_dylib_no_nul =
link name must not contain NUL characters if link kind is `raw-dylib`
attr_parsing_raw_dylib_only_windows =
link kind `raw-dylib` is only supported on Windows targets
attr_parsing_repr_ident =
meta item in `repr` must be an identifier
attr_parsing_rustc_allowed_unstable_pairing =
`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
attr_parsing_rustc_promotable_pairing =
`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
attr_parsing_rustc_scalable_vector_count_out_of_range = element count in `rustc_scalable_vector` is too large: `{$n}`
.note = the value may not exceed `u16::MAX`
attr_parsing_soft_no_args =
`soft` should not have any arguments
attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library
attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes
.help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)
attr_parsing_unknown_version_literal =
unknown version literal format, assuming it refers to a future version
attr_parsing_unrecognized_repr_hint =
unrecognized representation hint
.help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`
.note = for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>
attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe
.label = usage of unsafe attribute
attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)`
attr_parsing_unstable_cfg_target_compact =
compact `cfg(target(..))` is experimental and subject to change
attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable
.help = If this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`
attr_parsing_unsupported_instruction_set = target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`
attr_parsing_unsupported_literal_suggestion =
consider removing the prefix
attr_parsing_unused_multiple =
multiple `{$name}` attributes
.suggestion = remove this attribute
.note = attribute also specified here
attr_parsing_whole_archive_needs_static =
linking modifier `whole-archive` is only compatible with `static` linking kind

View file

@ -29,7 +29,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowInternalUnstableParser {
pub(crate) struct UnstableFeatureBoundParser;
impl<S: Stage> CombineAttributeParser<S> for UnstableFeatureBoundParser {
const PATH: &[rustc_span::Symbol] = &[sym::unstable_feature_bound];
const PATH: &'static [rustc_span::Symbol] = &[sym::unstable_feature_bound];
type Item = (Symbol, Span);
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::UnstableFeatureBound(items);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@ -57,7 +57,7 @@ impl<S: Stage> CombineAttributeParser<S> for AllowConstFnUnstableParser {
const PATH: &[Symbol] = &[sym::rustc_allow_const_fn_unstable];
type Item = Symbol;
const CONVERT: ConvertFn<Self::Item> =
|items, first_span| AttributeKind::RustcAllowConstFnUnstable(items, first_span);
|items, first_span| AttributeKind::AllowConstFnUnstable(items, first_span);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),

View file

@ -3,14 +3,14 @@ use std::convert::identity;
use rustc_ast::token::Delimiter;
use rustc_ast::tokenstream::DelimSpan;
use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token};
use rustc_errors::{Applicability, PResult, msg};
use rustc_errors::{Applicability, PResult};
use rustc_feature::{
AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template,
};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{AttrPath, RustcVersion, Target};
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_parse::parser::{ForceCollect, Parser};
use rustc_parse::{exp, parse_in};
use rustc_session::Session;
use rustc_session::config::ExpectedValues;
@ -25,7 +25,7 @@ use crate::session_diagnostics::{
AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg,
ParsedDescription,
};
use crate::{AttributeParser, parse_version, session_diagnostics};
use crate::{AttributeParser, fluent_generated, parse_version, session_diagnostics};
pub const CFG_TEMPLATE: AttributeTemplate = template!(
List: &["predicate"],
@ -141,7 +141,7 @@ fn parse_cfg_entry_target<S: Stage>(
cx.sess(),
sym::cfg_target_compact,
meta_span,
msg!("compact `cfg(target(..))` is experimental and subject to change"),
fluent_generated::attr_parsing_unstable_cfg_target_compact,
)
.emit();
}
@ -360,10 +360,7 @@ fn parse_cfg_attr_internal<'a>(
) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> {
// Parse cfg predicate
let pred_start = parser.token.span;
let meta = MetaItemOrLitParser::parse_single(
parser,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
)?;
let meta = MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints)?;
let pred_span = pred_start.with_hi(parser.token.span.hi());
let cfg_predicate = AttributeParser::parse_single_args(
@ -378,7 +375,7 @@ fn parse_cfg_attr_internal<'a>(
CRATE_NODE_ID,
Target::Crate,
features,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&CFG_ATTR_TEMPLATE,
@ -415,6 +412,7 @@ fn try_gate_cfg(name: Symbol, span: Span, sess: &Session, features: Option<&Feat
}
}
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn gate_cfg(gated_cfg: &GatedCfg, cfg_span: Span, sess: &Session, features: &Features) {
let (cfg, feature, has_feature) = gated_cfg;
if !has_feature(features) && !cfg_span.allows_unstable(*feature) {

View file

@ -1,35 +1,22 @@
use rustc_ast::token::Token;
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{AttrStyle, NodeId, token};
use rustc_data_structures::fx::FxHashMap;
use rustc_feature::{AttributeTemplate, Features};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, Target};
use rustc_parse::exp;
use rustc_parse::parser::{Parser, Recovery};
use rustc_parse::parser::Parser;
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::lint::builtin::UNREACHABLE_CFG_SELECT_PREDICATES;
use rustc_span::{ErrorGuaranteed, Span, Symbol, sym};
use rustc_span::{ErrorGuaranteed, Span, sym};
use crate::parser::MetaItemOrLitParser;
use crate::{AttributeParser, ParsedDescription, ShouldEmit, parse_cfg_entry};
#[derive(Clone)]
pub enum CfgSelectPredicate {
Cfg(CfgEntry),
Wildcard(Token),
}
impl CfgSelectPredicate {
fn span(&self) -> Span {
match self {
CfgSelectPredicate::Cfg(cfg_entry) => cfg_entry.span(),
CfgSelectPredicate::Wildcard(token) => token.span,
}
}
}
#[derive(Default)]
pub struct CfgSelectBranches {
/// All the conditional branches.
@ -91,11 +78,8 @@ pub fn parse_cfg_select(
}
}
} else {
let meta = MetaItemOrLitParser::parse_single(
p,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
)
.map_err(|diag| diag.emit())?;
let meta = MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints)
.map_err(|diag| diag.emit())?;
let cfg_span = meta.span();
let cfg = AttributeParser::parse_single_args(
sess,
@ -110,7 +94,7 @@ pub fn parse_cfg_select(
// Doesn't matter what the target actually is here.
Target::Crate,
features,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed },
ShouldEmit::ErrorsAndLints,
&meta,
parse_cfg_entry,
&AttributeTemplate::default(),
@ -128,102 +112,5 @@ pub fn parse_cfg_select(
}
}
if let Some(features) = features
&& features.enabled(sym::cfg_select)
{
let it = branches
.reachable
.iter()
.map(|(entry, _, _)| CfgSelectPredicate::Cfg(entry.clone()))
.chain(branches.wildcard.as_ref().map(|(t, _, _)| CfgSelectPredicate::Wildcard(*t)))
.chain(
branches.unreachable.iter().map(|(entry, _, _)| CfgSelectPredicate::clone(entry)),
);
lint_unreachable(p, it, lint_node_id);
}
Ok(branches)
}
fn lint_unreachable(
p: &mut Parser<'_>,
predicates: impl Iterator<Item = CfgSelectPredicate>,
lint_node_id: NodeId,
) {
// Symbols that have a known value.
let mut known = FxHashMap::<Symbol, bool>::default();
let mut wildcard_span = None;
let mut it = predicates;
let branch_is_unreachable = |predicate: CfgSelectPredicate, wildcard_span| {
let span = predicate.span();
p.psess.buffer_lint(
UNREACHABLE_CFG_SELECT_PREDICATES,
span,
lint_node_id,
BuiltinLintDiag::UnreachableCfg { span, wildcard_span },
);
};
for predicate in &mut it {
let CfgSelectPredicate::Cfg(ref cfg_entry) = predicate else {
wildcard_span = Some(predicate.span());
break;
};
match cfg_entry {
CfgEntry::Bool(true, _) => {
wildcard_span = Some(predicate.span());
break;
}
CfgEntry::Bool(false, _) => continue,
CfgEntry::NameValue { name, value, .. } => match value {
None => {
// `name` will be false in all subsequent branches.
let current = known.insert(*name, false);
match current {
None => continue,
Some(false) => {
branch_is_unreachable(predicate, None);
break;
}
Some(true) => {
// this branch will be taken, so all subsequent branches are unreachable.
break;
}
}
}
Some(_) => { /* for now we don't bother solving these */ }
},
CfgEntry::Not(inner, _) => match &**inner {
CfgEntry::NameValue { name, value: None, .. } => {
// `name` will be true in all subsequent branches.
let current = known.insert(*name, true);
match current {
None => continue,
Some(true) => {
branch_is_unreachable(predicate, None);
break;
}
Some(false) => {
// this branch will be taken, so all subsequent branches are unreachable.
break;
}
}
}
_ => { /* for now we don't bother solving these */ }
},
CfgEntry::All(_, _) | CfgEntry::Any(_, _) => {
/* for now we don't bother solving these */
}
CfgEntry::Version(..) => { /* don't bother solving these */ }
}
}
for predicate in it {
branch_is_unreachable(predicate, wildcard_span)
}
}

View file

@ -181,7 +181,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcClassParser {
cx.emit_err(NullOnObjcClass { span: nv.value_span });
return None;
}
Some(AttributeKind::RustcObjcClass { classname, span: cx.attr_span })
Some(AttributeKind::ObjcClass { classname, span: cx.attr_span })
}
}
@ -213,7 +213,7 @@ impl<S: Stage> SingleAttributeParser<S> for ObjcSelectorParser {
cx.emit_err(NullOnObjcSelector { span: nv.value_span });
return None;
}
Some(AttributeKind::RustcObjcSelector { methname, span: cx.attr_span })
Some(AttributeKind::ObjcSelector { methname, span: cx.attr_span })
}
}
@ -717,100 +717,3 @@ impl<S: Stage> NoArgsAttributeParser<S> for EiiForeignItemParser {
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::EiiForeignItem;
}
pub(crate) struct PatchableFunctionEntryParser;
impl<S: Stage> SingleAttributeParser<S> for PatchableFunctionEntryParser {
const PATH: &[Symbol] = &[sym::patchable_function_entry];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const TEMPLATE: AttributeTemplate = template!(List: &["prefix_nops = m, entry_nops = n"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(meta_item_list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};
let mut prefix = None;
let mut entry = None;
if meta_item_list.len() == 0 {
cx.expected_list(meta_item_list.span, args);
return None;
}
let mut errored = false;
for item in meta_item_list.mixed() {
let Some(meta_item) = item.meta_item() else {
errored = true;
cx.expected_name_value(item.span(), None);
continue;
};
let Some(name_value_lit) = meta_item.args().name_value() else {
errored = true;
cx.expected_name_value(item.span(), None);
continue;
};
let attrib_to_write = match meta_item.ident().map(|ident| ident.name) {
Some(sym::prefix_nops) => {
// Duplicate prefixes are not allowed
if prefix.is_some() {
errored = true;
cx.duplicate_key(meta_item.path().span(), sym::prefix_nops);
continue;
}
&mut prefix
}
Some(sym::entry_nops) => {
// Duplicate entries are not allowed
if entry.is_some() {
errored = true;
cx.duplicate_key(meta_item.path().span(), sym::entry_nops);
continue;
}
&mut entry
}
_ => {
errored = true;
cx.expected_specific_argument(
meta_item.path().span(),
&[sym::prefix_nops, sym::entry_nops],
);
continue;
}
};
let rustc_ast::LitKind::Int(val, _) = name_value_lit.value_as_lit().kind else {
errored = true;
cx.expected_integer_literal(name_value_lit.value_span);
continue;
};
let Ok(val) = val.get().try_into() else {
errored = true;
cx.expected_integer_literal_in_range(
name_value_lit.value_span,
u8::MIN as isize,
u8::MAX as isize,
);
continue;
};
*attrib_to_write = Some(val);
}
if errored {
None
} else {
Some(AttributeKind::PatchableFunctionEntry {
prefix: prefix.unwrap_or(0),
entry: entry.unwrap_or(0),
})
}
}
}

View file

@ -43,7 +43,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
return None;
}
Some(AttributeKind::RustcConfusables {
Some(AttributeKind::Confusables {
symbols: self.confusables,
first_span: self.first_span.unwrap(),
})

View file

@ -1,8 +1,4 @@
use rustc_hir::attrs::{CrateType, WindowsSubsystemKind};
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::UNKNOWN_CRATE_TYPES;
use rustc_span::Symbol;
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_hir::attrs::WindowsSubsystemKind;
use super::prelude::*;
@ -30,56 +26,6 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
}
}
pub(crate) struct CrateTypeParser;
impl<S: Stage> CombineAttributeParser<S> for CrateTypeParser {
const PATH: &[Symbol] = &[sym::crate_type];
type Item = CrateType;
const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::CrateType(items);
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate =
template!(NameValueStr: "crate type", "https://doc.rust-lang.org/reference/linkage.html");
fn extend(
cx: &mut AcceptContext<'_, '_, S>,
args: &ArgParser,
) -> impl IntoIterator<Item = Self::Item> {
let ArgParser::NameValue(n) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
let Some(crate_type) = n.value_as_str() else {
cx.expected_string_literal(n.value_span, Some(n.value_as_lit()));
return None;
};
let Ok(crate_type) = crate_type.try_into() else {
// We don't error on invalid `#![crate_type]` when not applied to a crate
if cx.shared.target == Target::Crate {
let candidate = find_best_match_for_name(
&CrateType::all_stable().iter().map(|(name, _)| *name).collect::<Vec<_>>(),
crate_type,
None,
);
cx.emit_lint(
UNKNOWN_CRATE_TYPES,
AttributeLintKind::CrateTypeUnknown {
span: n.value_span,
suggested: candidate,
},
n.value_span,
);
}
return None;
};
Some(crate_type)
}
}
pub(crate) struct RecursionLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
@ -190,15 +136,6 @@ impl<S: Stage> NoArgsAttributeParser<S> for NoStdParser {
const CREATE: fn(Span) -> AttributeKind = AttributeKind::NoStd;
}
pub(crate) struct NoMainParser;
impl<S: Stage> NoArgsAttributeParser<S> for NoMainParser {
const PATH: &[Symbol] = &[sym::no_main];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoMain;
}
pub(crate) struct RustcCoherenceIsCoreParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcCoherenceIsCoreParser {
@ -238,66 +175,3 @@ impl<S: Stage> SingleAttributeParser<S> for WindowsSubsystemParser {
Some(AttributeKind::WindowsSubsystem(kind, cx.attr_span))
}
}
pub(crate) struct PanicRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for PanicRuntimeParser {
const PATH: &[Symbol] = &[sym::panic_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::PanicRuntime;
}
pub(crate) struct NeedsPanicRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for NeedsPanicRuntimeParser {
const PATH: &[Symbol] = &[sym::needs_panic_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsPanicRuntime;
}
pub(crate) struct ProfilerRuntimeParser;
impl<S: Stage> NoArgsAttributeParser<S> for ProfilerRuntimeParser {
const PATH: &[Symbol] = &[sym::profiler_runtime];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ProfilerRuntime;
}
pub(crate) struct NoBuiltinsParser;
impl<S: Stage> NoArgsAttributeParser<S> for NoBuiltinsParser {
const PATH: &[Symbol] = &[sym::no_builtins];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins;
}
pub(crate) struct RustcPreserveUbChecksParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcPreserveUbChecksParser {
const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks;
}
pub(crate) struct RustcNoImplicitBoundsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcNoImplicitBoundsParser {
const PATH: &[Symbol] = &[sym::rustc_no_implicit_bounds];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcNoImplicitBounds;
}
pub(crate) struct DefaultLibAllocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for DefaultLibAllocatorParser {
const PATH: &[Symbol] = &[sym::default_lib_allocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::DefaultLibAllocator;
}

View file

@ -1,5 +1,4 @@
use rustc_hir::attrs::{DeprecatedSince, Deprecation};
use rustc_hir::{RustcVersion, VERSION_PLACEHOLDER};
use super::prelude::*;
use super::util::parse_version;
@ -144,8 +143,6 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
DeprecatedSince::Future
} else if !is_rustc {
DeprecatedSince::NonStandard(since)
} else if since.as_str() == VERSION_PLACEHOLDER {
DeprecatedSince::RustcVersion(RustcVersion::CURRENT)
} else if let Some(version) = parse_version(since) {
DeprecatedSince::RustcVersion(version)
} else {

View file

@ -16,6 +16,6 @@ impl<S: Stage> SingleAttributeParser<S> for DummyParser {
const TEMPLATE: AttributeTemplate = template!(Word); // Anything, really
fn convert(_: &mut AcceptContext<'_, '_, S>, _: &ArgParser) -> Option<AttributeKind> {
Some(AttributeKind::RustcDummy)
Some(AttributeKind::Dummy)
}
}

View file

@ -10,7 +10,7 @@ use super::prelude::*;
pub(crate) struct InlineParser;
impl<S: Stage> SingleAttributeParser<S> for InlineParser {
const PATH: &[Symbol] = &[sym::inline];
const PATH: &'static [Symbol] = &[sym::inline];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@ -67,7 +67,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
pub(crate) struct RustcForceInlineParser;
impl<S: Stage> SingleAttributeParser<S> for RustcForceInlineParser {
const PATH: &[Symbol] = &[sym::rustc_force_inline];
const PATH: &'static [Symbol] = &[sym::rustc_force_inline];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[

View file

@ -1,4 +1,3 @@
use rustc_errors::msg;
use rustc_feature::Features;
use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
use rustc_hir::attrs::*;
@ -11,11 +10,12 @@ use rustc_target::spec::{Arch, BinaryFormat};
use super::prelude::*;
use super::util::parse_single_integer;
use crate::attributes::cfg::parse_cfg_entry;
use crate::fluent_generated;
use crate::session_diagnostics::{
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic,
ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier,
LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers,
NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic,
AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86,
IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange,
LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows,
WholeArchiveNeedsStatic,
};
pub(crate) struct LinkNameParser;
@ -141,6 +141,8 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
macro report_unstable_modifier($feature: ident) {
if !features.$feature() {
// FIXME: make this translatable
#[expect(rustc::untranslatable_diagnostic)]
feature_err(
sess,
sym::$feature,
@ -165,14 +167,6 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
cx.emit_err(BundleNeedsStatic { span });
}
(sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => {
assign_modifier(export_symbols)
}
(sym::export_symbols, _) => {
cx.emit_err(ExportSymbolsNeedsStatic { span });
}
(sym::verbatim, _) => assign_modifier(&mut verbatim),
(
@ -198,7 +192,6 @@ impl<S: Stage> CombineAttributeParser<S> for LinkParser {
span,
&[
sym::bundle,
sym::export_symbols,
sym::verbatim,
sym::whole_dash_archive,
sym::as_dash_needed,
@ -294,9 +287,7 @@ impl LinkParser {
};
let link_kind = match link_kind {
kw::Static => {
NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }
}
kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None },
sym::dylib => NativeLibKind::Dylib { as_needed: None },
sym::framework => {
if !sess.target.is_like_darwin {
@ -316,7 +307,7 @@ impl LinkParser {
sess,
sym::raw_dylib_elf,
nv.value_span,
msg!("link kind `raw-dylib` is unstable on ELF platforms"),
fluent_generated::attr_parsing_raw_dylib_elf_unstable,
)
.emit();
} else {
@ -331,7 +322,7 @@ impl LinkParser {
sess,
sym::link_arg_attribute,
nv.value_span,
msg!("link kind `link-arg` is unstable"),
fluent_generated::attr_parsing_link_arg_unstable,
)
.emit();
}
@ -396,7 +387,13 @@ impl LinkParser {
return true;
};
if !features.link_cfg() {
feature_err(sess, sym::link_cfg, item.span(), msg!("link cfg is unstable")).emit();
feature_err(
sess,
sym::link_cfg,
item.span(),
fluent_generated::attr_parsing_link_cfg_unstable,
)
.emit();
}
*cfg = parse_cfg_entry(cx, link_cfg).ok();
true
@ -534,7 +531,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for StdInternalSymbolParser {
Allow(Target::Static),
Allow(Target::ForeignStatic),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcStdInternalSymbol;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::StdInternalSymbol;
}
pub(crate) struct LinkOrdinalParser;
@ -661,21 +658,3 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
Some(AttributeKind::Linkage(linkage, cx.attr_span))
}
}
pub(crate) struct NeedsAllocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for NeedsAllocatorParser {
const PATH: &[Symbol] = &[sym::needs_allocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NeedsAllocator;
}
pub(crate) struct CompilerBuiltinsParser;
impl<S: Stage> NoArgsAttributeParser<S> for CompilerBuiltinsParser {
const PATH: &[Symbol] = &[sym::compiler_builtins];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::CompilerBuiltins;
}

View file

@ -11,7 +11,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for AsPtrParser {
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAsPtr;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AsPtr;
}
pub(crate) struct PubTransparentParser;
@ -23,7 +23,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for PubTransparentParser {
Allow(Target::Enum),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPubTransparent;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PubTransparent;
}
pub(crate) struct PassByValueParser;
@ -35,7 +35,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for PassByValueParser {
Allow(Target::Enum),
Allow(Target::TyAlias),
]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcPassByValue;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue;
}
pub(crate) struct RustcShouldNotBeCalledOnConstItems;

View file

@ -206,12 +206,3 @@ impl<S: Stage> SingleAttributeParser<S> for CollapseDebugInfoParser {
Some(AttributeKind::CollapseDebugInfo(info))
}
}
pub(crate) struct RustcProcMacroDeclsParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcProcMacroDeclsParser {
const PATH: &[Symbol] = &[sym::rustc_proc_macro_decls];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Static)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcProcMacroDecls;
}

View file

@ -58,7 +58,6 @@ pub(crate) mod pin_v2;
pub(crate) mod proc_macro_attrs;
pub(crate) mod prototype;
pub(crate) mod repr;
pub(crate) mod rustc_allocator;
pub(crate) mod rustc_dump;
pub(crate) mod rustc_internal;
pub(crate) mod semantics;

View file

@ -1,6 +1,3 @@
use rustc_hir::lints::AttributeLintKind;
use rustc_session::lint::builtin::AMBIGUOUS_DERIVE_HELPERS;
use super::prelude::*;
const PROC_MACRO_ALLOWED_TARGETS: AllowedTargets =
@ -129,13 +126,6 @@ fn parse_derive_like<S: Stage>(
cx.expected_identifier(ident.span);
return None;
}
if rustc_feature::is_builtin_attr_name(ident.name) {
cx.emit_lint(
AMBIGUOUS_DERIVE_HELPERS,
AttributeLintKind::AmbiguousDeriveHelpers,
ident.span,
);
}
attributes.push(ident.name);
}
}

View file

@ -272,7 +272,7 @@ fn parse_alignment(node: &LitKind) -> Result<Align, &'static str> {
pub(crate) struct AlignParser(Option<(Align, Span)>);
impl AlignParser {
const PATH: &[Symbol] = &[sym::rustc_align];
const PATH: &'static [Symbol] = &[sym::rustc_align];
const TEMPLATE: AttributeTemplate = template!(List: &["<alignment in bytes>"]);
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {
@ -329,7 +329,7 @@ impl<S: Stage> AttributeParser<S> for AlignParser {
pub(crate) struct AlignStaticParser(AlignParser);
impl AlignStaticParser {
const PATH: &[Symbol] = &[sym::rustc_align_static];
const PATH: &'static [Symbol] = &[sym::rustc_align_static];
const TEMPLATE: AttributeTemplate = AlignParser::TEMPLATE;
fn parse<S: Stage>(&mut self, cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) {

View file

@ -1,60 +0,0 @@
use super::prelude::*;
pub(crate) struct RustcAllocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcAllocatorParser {
const PATH: &[Symbol] = &[sym::rustc_allocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAllocator;
}
pub(crate) struct RustcAllocatorZeroedParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcAllocatorZeroedParser {
const PATH: &[Symbol] = &[sym::rustc_allocator_zeroed];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcAllocatorZeroed;
}
pub(crate) struct RustcAllocatorZeroedVariantParser;
impl<S: Stage> SingleAttributeParser<S> for RustcAllocatorZeroedVariantParser {
const PATH: &[Symbol] = &[sym::rustc_allocator_zeroed_variant];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "function");
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(name) = args.name_value().and_then(NameValueParser::value_as_str) else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
Some(AttributeKind::RustcAllocatorZeroedVariant { name })
}
}
pub(crate) struct RustcDeallocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDeallocatorParser {
const PATH: &[Symbol] = &[sym::rustc_deallocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDeallocator;
}
pub(crate) struct RustcReallocatorParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcReallocatorParser {
const PATH: &[Symbol] = &[sym::rustc_reallocator];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Fn), Allow(Target::ForeignFn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcReallocator;
}

View file

@ -7,36 +7,36 @@ use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
use crate::context::Stage;
use crate::target_checking::AllowedTargets;
pub(crate) struct RustcDumpUserArgsParser;
pub(crate) struct RustcDumpUserArgs;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpUserArgsParser {
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpUserArgs {
const PATH: &[Symbol] = &[sym::rustc_dump_user_args];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs;
}
pub(crate) struct RustcDumpDefParentsParser;
pub(crate) struct RustcDumpDefParents;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpDefParentsParser {
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpDefParents {
const PATH: &[Symbol] = &[sym::rustc_dump_def_parents];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents;
}
pub(crate) struct RustcDumpItemBoundsParser;
pub(crate) struct RustcDumpItemBounds;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpItemBoundsParser {
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpItemBounds {
const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds;
}
pub(crate) struct RustcDumpPredicatesParser;
pub(crate) struct RustcDumpPredicates;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpPredicatesParser {
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpPredicates {
const PATH: &[Symbol] = &[sym::rustc_dump_predicates];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@ -49,9 +49,9 @@ impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpPredicatesParser {
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates;
}
pub(crate) struct RustcDumpVtableParser;
pub(crate) struct RustcDumpVtable;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpVtableParser {
impl<S: Stage> NoArgsAttributeParser<S> for RustcDumpVtable {
const PATH: &[Symbol] = &[sym::rustc_dump_vtable];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[

File diff suppressed because it is too large Load diff

View file

@ -173,7 +173,7 @@ impl<S: Stage> AttributeParser<S> for BodyStabilityParser {
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
let (stability, span) = self.stability?;
Some(AttributeKind::RustcBodyStability { stability, span })
Some(AttributeKind::BodyStability { stability, span })
}
}
@ -185,7 +185,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for ConstStabilityIndirectParser {
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcConstStabilityIndirect;
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::ConstStabilityIndirect;
}
#[derive(Default)]
@ -244,20 +244,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
this.promotable = true;
}),
];
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Impl { of_trait: false }),
Allow(Target::Impl { of_trait: true }),
Allow(Target::Use), // FIXME I don't think this does anything?
Allow(Target::Const),
Allow(Target::AssocConst),
Allow(Target::Trait),
Allow(Target::Static),
Allow(Target::Crate),
]);
const ALLOWED_TARGETS: AllowedTargets = ALLOWED_TARGETS;
fn finalize(mut self, cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
if self.promotable {
@ -271,7 +258,7 @@ impl<S: Stage> AttributeParser<S> for ConstStabilityParser {
let (stability, span) = self.stability?;
Some(AttributeKind::RustcConstStability { stability, span })
Some(AttributeKind::ConstStability { stability, span })
}
}

View file

@ -1,4 +1,3 @@
use rustc_hir::attrs::RustcAbiAttrKind;
use rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
use super::prelude::*;
@ -92,201 +91,3 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
})
}
}
pub(crate) struct RustcVarianceParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceParser {
const PATH: &[Symbol] = &[sym::rustc_variance];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVariance;
}
pub(crate) struct RustcVarianceOfOpaquesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcVarianceOfOpaquesParser {
const PATH: &[Symbol] = &[sym::rustc_variance_of_opaques];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcVarianceOfOpaques;
}
pub(crate) struct ReexportTestHarnessMainParser;
impl<S: Stage> SingleAttributeParser<S> for ReexportTestHarnessMainParser {
const PATH: &[Symbol] = &[sym::reexport_test_harness_main];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(nv) = args.name_value() else {
cx.expected_name_value(
args.span().unwrap_or(cx.inner_span),
Some(sym::reexport_test_harness_main),
);
return None;
};
let Some(name) = nv.value_as_str() else {
cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
Some(AttributeKind::ReexportTestHarnessMain(name))
}
}
pub(crate) struct RustcAbiParser;
impl<S: Stage> SingleAttributeParser<S> for RustcAbiParser {
const PATH: &[Symbol] = &[sym::rustc_abi];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const TEMPLATE: AttributeTemplate = template!(OneOf: &[sym::debug, sym::assert_eq]);
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::TyAlias),
Allow(Target::Fn),
Allow(Target::ForeignFn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::Trait { body: false })),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(args) = args.list() else {
cx.expected_specific_argument_and_list(cx.attr_span, &[sym::assert_eq, sym::debug]);
return None;
};
let Some(arg) = args.single() else {
cx.expected_single_argument(cx.attr_span);
return None;
};
let fail_incorrect_argument =
|span| cx.expected_specific_argument(span, &[sym::assert_eq, sym::debug]);
let Some(arg) = arg.meta_item() else {
fail_incorrect_argument(args.span);
return None;
};
let kind: RustcAbiAttrKind = match arg.path().word_sym() {
Some(sym::assert_eq) => RustcAbiAttrKind::AssertEq,
Some(sym::debug) => RustcAbiAttrKind::Debug,
None | Some(_) => {
fail_incorrect_argument(arg.span());
return None;
}
};
Some(AttributeKind::RustcAbi { attr_span: cx.attr_span, kind })
}
}
pub(crate) struct RustcDelayedBugFromInsideQueryParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcDelayedBugFromInsideQueryParser {
const PATH: &[Symbol] = &[sym::rustc_delayed_bug_from_inside_query];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDelayedBugFromInsideQuery;
}
pub(crate) struct RustcEvaluateWhereClausesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcEvaluateWhereClausesParser {
const PATH: &[Symbol] = &[sym::rustc_evaluate_where_clauses];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Fn),
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::Trait { body: true })),
Allow(Target::Method(MethodKind::TraitImpl)),
Allow(Target::Method(MethodKind::Trait { body: false })),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcEvaluateWhereClauses;
}
pub(crate) struct RustcOutlivesParser;
impl<S: Stage> NoArgsAttributeParser<S> for RustcOutlivesParser {
const PATH: &[Symbol] = &[sym::rustc_outlives];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Struct),
Allow(Target::Enum),
Allow(Target::Union),
Allow(Target::TyAlias),
]);
const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcOutlives;
}
pub(crate) struct TestRunnerParser;
impl<S: Stage> SingleAttributeParser<S> for TestRunnerParser {
const PATH: &[Symbol] = &[sym::test_runner];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
const TEMPLATE: AttributeTemplate = template!(List: &["path"]);
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(list) = args.list() else {
cx.expected_list(cx.attr_span, args);
return None;
};
let Some(single) = list.single() else {
cx.expected_single_argument(list.span);
return None;
};
let Some(meta) = single.meta_item() else {
cx.unexpected_literal(single.span());
return None;
};
Some(AttributeKind::TestRunner(meta.path().0.clone()))
}
}
pub(crate) struct RustcTestMarkerParser;
impl<S: Stage> SingleAttributeParser<S> for RustcTestMarkerParser {
const PATH: &[Symbol] = &[sym::rustc_test_marker];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Const),
Allow(Target::Fn),
Allow(Target::Static),
]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "test_path");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser) -> Option<AttributeKind> {
let Some(name_value) = args.name_value() else {
cx.expected_name_value(cx.attr_span, Some(sym::rustc_test_marker));
return None;
};
let Some(value_str) = name_value.value_as_str() else {
cx.expected_string_literal(name_value.value_span, None);
return None;
};
if value_str.as_str().trim().is_empty() {
cx.expected_non_empty_string_literal(name_value.value_span);
return None;
}
Some(AttributeKind::RustcTestMarker(value_str))
}
}

View file

@ -50,11 +50,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
cx.duplicate_key(arg.span(), key);
}
}
Some(AttributeKind::RustcSkipDuringMethodDispatch {
array,
boxed_slice,
span: cx.attr_span,
})
Some(AttributeKind::SkipDuringMethodDispatch { array, boxed_slice, span: cx.attr_span })
}
}
@ -63,7 +59,16 @@ impl<S: Stage> NoArgsAttributeParser<S> for ParenSugarParser {
const PATH: &[Symbol] = &[sym::rustc_paren_sugar];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcParenSugar;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::ParenSugar;
}
pub(crate) struct TypeConstParser;
impl<S: Stage> NoArgsAttributeParser<S> for TypeConstParser {
const PATH: &[Symbol] = &[sym::type_const];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Const), Allow(Target::AssocConst)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::TypeConst;
}
// Markers
@ -86,15 +91,15 @@ impl<S: Stage> NoArgsAttributeParser<S> for DenyExplicitImplParser {
const PATH: &[Symbol] = &[sym::rustc_deny_explicit_impl];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDenyExplicitImpl;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DenyExplicitImpl;
}
pub(crate) struct DynIncompatibleTraitParser;
impl<S: Stage> NoArgsAttributeParser<S> for DynIncompatibleTraitParser {
const PATH: &[Symbol] = &[sym::rustc_dyn_incompatible_trait];
pub(crate) struct DoNotImplementViaObjectParser;
impl<S: Stage> NoArgsAttributeParser<S> for DoNotImplementViaObjectParser {
const PATH: &[Symbol] = &[sym::rustc_do_not_implement_via_object];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcDynIncompatibleTrait;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::DoNotImplementViaObject;
}
// Specialization
@ -104,7 +109,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for SpecializationTraitParser {
const PATH: &[Symbol] = &[sym::rustc_specialization_trait];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcSpecializationTrait;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::SpecializationTrait;
}
pub(crate) struct UnsafeSpecializationMarkerParser;
@ -112,7 +117,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for UnsafeSpecializationMarkerParser {
const PATH: &[Symbol] = &[sym::rustc_unsafe_specialization_marker];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcUnsafeSpecializationMarker;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::UnsafeSpecializationMarker;
}
// Coherence
@ -122,7 +127,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for CoinductiveParser {
const PATH: &[Symbol] = &[sym::rustc_coinductive];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Trait)]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcCoinductive;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::Coinductive;
}
pub(crate) struct AllowIncoherentImplParser;
@ -131,7 +136,7 @@ impl<S: Stage> NoArgsAttributeParser<S> for AllowIncoherentImplParser {
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets =
AllowedTargets::AllowList(&[Allow(Target::Method(MethodKind::Inherent))]);
const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcAllowIncoherentImpl;
const CREATE: fn(Span) -> AttributeKind = AttributeKind::AllowIncoherentImpl;
}
pub(crate) struct FundamentalParser;

View file

@ -4,6 +4,9 @@ use super::prelude::*;
pub(crate) struct TransparencyParser;
// FIXME(jdonszelmann): make these proper diagnostics
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
const PATH: &[Symbol] = &[sym::rustc_macro_transparency];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
@ -32,6 +35,6 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
}
None => None,
}
.map(AttributeKind::RustcMacroTransparency)
.map(AttributeKind::MacroTransparency)
}
}

View file

@ -1,6 +1,5 @@
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::ops::{Deref, DerefMut};
use std::sync::LazyLock;
@ -11,48 +10,85 @@ use rustc_feature::{AttrSuggestionStyle, AttributeTemplate};
use rustc_hir::attrs::AttributeKind;
use rustc_hir::lints::AttributeLintKind;
use rustc_hir::{AttrPath, HirId};
use rustc_parse::parser::Recovery;
use rustc_session::Session;
use rustc_session::lint::{Lint, LintId};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use crate::AttributeParser;
// Glob imports to avoid big, bitrotty import lists
use crate::attributes::allow_unstable::*;
use crate::attributes::body::*;
use crate::attributes::cfi_encoding::*;
use crate::attributes::codegen_attrs::*;
use crate::attributes::confusables::*;
use crate::attributes::crate_level::*;
use crate::attributes::debugger::*;
use crate::attributes::deprecation::*;
use crate::attributes::do_not_recommend::*;
use crate::attributes::doc::*;
use crate::attributes::dummy::*;
use crate::attributes::inline::*;
use crate::attributes::instruction_set::*;
use crate::attributes::link_attrs::*;
use crate::attributes::lint_helpers::*;
use crate::attributes::loop_match::*;
use crate::attributes::macro_attrs::*;
use crate::attributes::must_not_suspend::*;
use crate::attributes::must_use::*;
use crate::attributes::no_implicit_prelude::*;
use crate::attributes::no_link::*;
use crate::attributes::non_exhaustive::*;
use crate::attributes::allow_unstable::{
AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
};
use crate::attributes::body::CoroutineParser;
use crate::attributes::cfi_encoding::CfiEncodingParser;
use crate::attributes::codegen_attrs::{
ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser,
NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser,
RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, TargetFeatureParser,
ThreadLocalParser, TrackCallerParser, UsedParser,
};
use crate::attributes::confusables::ConfusablesParser;
use crate::attributes::crate_level::{
CrateNameParser, MoveSizeLimitParser, NoCoreParser, NoStdParser, PatternComplexityLimitParser,
RecursionLimitParser, RustcCoherenceIsCoreParser, TypeLengthLimitParser,
WindowsSubsystemParser,
};
use crate::attributes::debugger::DebuggerViualizerParser;
use crate::attributes::deprecation::DeprecationParser;
use crate::attributes::do_not_recommend::DoNotRecommendParser;
use crate::attributes::doc::DocParser;
use crate::attributes::dummy::DummyParser;
use crate::attributes::inline::{InlineParser, RustcForceInlineParser};
use crate::attributes::instruction_set::InstructionSetParser;
use crate::attributes::link_attrs::{
ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, LinkOrdinalParser,
LinkParser, LinkSectionParser, LinkageParser, StdInternalSymbolParser,
};
use crate::attributes::lint_helpers::{
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
RustcShouldNotBeCalledOnConstItems,
};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::macro_attrs::{
AllowInternalUnsafeParser, CollapseDebugInfoParser, MacroEscapeParser, MacroExportParser,
MacroUseParser,
};
use crate::attributes::must_not_suspend::MustNotSuspendParser;
use crate::attributes::must_use::MustUseParser;
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
use crate::attributes::no_link::NoLinkParser;
use crate::attributes::non_exhaustive::NonExhaustiveParser;
use crate::attributes::path::PathParser as PathAttributeParser;
use crate::attributes::pin_v2::*;
use crate::attributes::proc_macro_attrs::*;
use crate::attributes::prototype::*;
use crate::attributes::repr::*;
use crate::attributes::rustc_allocator::*;
use crate::attributes::rustc_dump::*;
use crate::attributes::rustc_internal::*;
use crate::attributes::semantics::*;
use crate::attributes::stability::*;
use crate::attributes::test_attrs::*;
use crate::attributes::traits::*;
use crate::attributes::transparency::*;
use crate::attributes::pin_v2::PinV2Parser;
use crate::attributes::proc_macro_attrs::{
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
};
use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_dump::{
RustcDumpDefParents, RustcDumpItemBounds, RustcDumpPredicates, RustcDumpUserArgs,
RustcDumpVtable,
};
use crate::attributes::rustc_internal::{
RustcHasIncoherentInherentImplsParser, RustcLayoutScalarValidRangeEndParser,
RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser,
RustcLintDiagnosticsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser,
RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser,
RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser,
RustcNoImplicitAutorefsParser, RustcObjectLifetimeDefaultParser, RustcScalableVectorParser,
RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser,
};
use crate::attributes::test_attrs::{IgnoreParser, ShouldPanicParser};
use crate::attributes::traits::{
AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser,
DoNotImplementViaObjectParser, FundamentalParser, MarkerParser, ParenSugarParser,
PointeeParser, SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser,
UnsafeSpecializationMarkerParser,
};
use crate::attributes::transparency::TransparencyParser;
use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
use crate::parser::{ArgParser, RefPathParser};
use crate::session_diagnostics::{
@ -62,19 +98,19 @@ use crate::target_checking::AllowedTargets;
type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], GroupTypeInnerAccept<S>>,
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
pub(super) finalizers: Vec<FinalizeFn<S>>,
}
pub(super) struct GroupTypeInnerAccept<S: Stage> {
pub(super) template: AttributeTemplate,
pub(super) accept_fn: AcceptFn<S>,
pub(super) allowed_targets: AllowedTargets,
pub(super) finalizer: FinalizeFn<S>,
}
pub(crate) type AcceptFn<S> =
type AcceptFn<S> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
pub(crate) type FinalizeFn<S> =
type FinalizeFn<S> =
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
macro_rules! attribute_parsers {
@ -102,7 +138,8 @@ macro_rules! attribute_parsers {
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
let mut accepters = BTreeMap::<_, GroupTypeInnerAccept<$stage>>::new();
let mut accepts = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
let mut finalizes = Vec::<FinalizeFn<$stage>>::new();
$(
{
thread_local! {
@ -110,29 +147,25 @@ macro_rules! attribute_parsers {
};
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
match accepters.entry(*path) {
Entry::Vacant(e) => {
e.insert(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
accept_fn(s, cx, args)
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
finalizer: Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
})
});
}
Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"),
}
accepts.entry(*path).or_default().push(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
accept_fn(s, cx, args)
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
});
}
finalizes.push(Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}));
}
)*
GroupTypeInner { accepters }
GroupTypeInner { accepters:accepts, finalizers:finalizes }
});
};
}
@ -147,7 +180,6 @@ attribute_parsers!(
DocParser,
MacroUseParser,
NakedParser,
RustcCguTestAttributeParser,
StabilityParser,
UsedParser,
// tidy-alphabetical-end
@ -155,15 +187,10 @@ attribute_parsers!(
// tidy-alphabetical-start
Combine<AllowConstFnUnstableParser>,
Combine<AllowInternalUnstableParser>,
Combine<CrateTypeParser>,
Combine<DebuggerViualizerParser>,
Combine<ForceTargetFeatureParser>,
Combine<LinkParser>,
Combine<ReprParser>,
Combine<RustcCleanParser>,
Combine<RustcLayoutParser>,
Combine<RustcMirParser>,
Combine<RustcThenThisWouldNeedParser>,
Combine<TargetFeatureParser>,
Combine<UnstableFeatureBoundParser>,
// tidy-alphabetical-end
@ -181,7 +208,6 @@ attribute_parsers!(
Single<IgnoreParser>,
Single<InlineParser>,
Single<InstructionSetParser>,
Single<LangParser>,
Single<LinkNameParser>,
Single<LinkOrdinalParser>,
Single<LinkSectionParser>,
@ -193,35 +219,23 @@ attribute_parsers!(
Single<ObjcClassParser>,
Single<ObjcSelectorParser>,
Single<OptimizeParser>,
Single<PatchableFunctionEntryParser>,
Single<PathAttributeParser>,
Single<PatternComplexityLimitParser>,
Single<ProcMacroDeriveParser>,
Single<RecursionLimitParser>,
Single<ReexportTestHarnessMainParser>,
Single<RustcAbiParser>,
Single<RustcAllocatorZeroedVariantParser>,
Single<RustcBuiltinMacroParser>,
Single<RustcDefPath>,
Single<RustcDeprecatedSafe2024Parser>,
Single<RustcDiagnosticItemParser>,
Single<RustcForceInlineParser>,
Single<RustcIfThisChangedParser>,
Single<RustcLayoutScalarValidRangeEndParser>,
Single<RustcLayoutScalarValidRangeStartParser>,
Single<RustcLegacyConstGenericsParser>,
Single<RustcLintOptDenyFieldAccessParser>,
Single<RustcMustImplementOneOfParser>,
Single<RustcNeverTypeOptionsParser>,
Single<RustcReservationImplParser>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcScalableVectorParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
Single<RustcSymbolName>,
Single<RustcTestMarkerParser>,
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,
Single<TestRunnerParser>,
Single<TransparencyParser>,
Single<TypeLengthLimitParser>,
Single<WindowsSubsystemParser>,
@ -231,13 +245,11 @@ attribute_parsers!(
Single<WithoutArgs<AutomaticallyDerivedParser>>,
Single<WithoutArgs<CoinductiveParser>>,
Single<WithoutArgs<ColdParser>>,
Single<WithoutArgs<CompilerBuiltinsParser>>,
Single<WithoutArgs<ConstContinueParser>>,
Single<WithoutArgs<ConstStabilityIndirectParser>>,
Single<WithoutArgs<CoroutineParser>>,
Single<WithoutArgs<DefaultLibAllocatorParser>>,
Single<WithoutArgs<DenyExplicitImplParser>>,
Single<WithoutArgs<DynIncompatibleTraitParser>>,
Single<WithoutArgs<DoNotImplementViaObjectParser>>,
Single<WithoutArgs<EiiForeignItemParser>>,
Single<WithoutArgs<ExportStableParser>>,
Single<WithoutArgs<FfiConstParser>>,
@ -247,75 +259,40 @@ attribute_parsers!(
Single<WithoutArgs<MacroEscapeParser>>,
Single<WithoutArgs<MarkerParser>>,
Single<WithoutArgs<MayDangleParser>>,
Single<WithoutArgs<NeedsAllocatorParser>>,
Single<WithoutArgs<NeedsPanicRuntimeParser>>,
Single<WithoutArgs<NoBuiltinsParser>>,
Single<WithoutArgs<NoCoreParser>>,
Single<WithoutArgs<NoImplicitPreludeParser>>,
Single<WithoutArgs<NoLinkParser>>,
Single<WithoutArgs<NoMainParser>>,
Single<WithoutArgs<NoMangleParser>>,
Single<WithoutArgs<NoStdParser>>,
Single<WithoutArgs<NonExhaustiveParser>>,
Single<WithoutArgs<PanicHandlerParser>>,
Single<WithoutArgs<PanicRuntimeParser>>,
Single<WithoutArgs<ParenSugarParser>>,
Single<WithoutArgs<PassByValueParser>>,
Single<WithoutArgs<PinV2Parser>>,
Single<WithoutArgs<PointeeParser>>,
Single<WithoutArgs<PreludeImportParser>>,
Single<WithoutArgs<ProcMacroAttributeParser>>,
Single<WithoutArgs<ProcMacroParser>>,
Single<WithoutArgs<ProfilerRuntimeParser>>,
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcAllocatorParser>>,
Single<WithoutArgs<RustcAllocatorZeroedParser>>,
Single<WithoutArgs<RustcCaptureAnalysisParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcConversionSuggestionParser>>,
Single<WithoutArgs<RustcDeallocatorParser>>,
Single<WithoutArgs<RustcDelayedBugFromInsideQueryParser>>,
Single<WithoutArgs<RustcDoNotConstCheckParser>>,
Single<WithoutArgs<RustcDumpDefParentsParser>>,
Single<WithoutArgs<RustcDumpItemBoundsParser>>,
Single<WithoutArgs<RustcDumpPredicatesParser>>,
Single<WithoutArgs<RustcDumpUserArgsParser>>,
Single<WithoutArgs<RustcDumpVtableParser>>,
Single<WithoutArgs<RustcEffectiveVisibilityParser>>,
Single<WithoutArgs<RustcEvaluateWhereClausesParser>>,
Single<WithoutArgs<RustcDumpDefParents>>,
Single<WithoutArgs<RustcDumpItemBounds>>,
Single<WithoutArgs<RustcDumpPredicates>>,
Single<WithoutArgs<RustcDumpUserArgs>>,
Single<WithoutArgs<RustcDumpVtable>>,
Single<WithoutArgs<RustcHasIncoherentInherentImplsParser>>,
Single<WithoutArgs<RustcHiddenTypeOfOpaquesParser>>,
Single<WithoutArgs<RustcInsignificantDtorParser>>,
Single<WithoutArgs<RustcIntrinsicConstStableIndirectParser>>,
Single<WithoutArgs<RustcIntrinsicParser>>,
Single<WithoutArgs<RustcLintDiagnosticsParser>>,
Single<WithoutArgs<RustcLintOptTyParser>>,
Single<WithoutArgs<RustcLintQueryInstabilityParser>>,
Single<WithoutArgs<RustcLintUntrackedQueryInformationParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<RustcNeverReturnsNullPointerParser>>,
Single<WithoutArgs<RustcNoImplicitAutorefsParser>>,
Single<WithoutArgs<RustcNoImplicitBoundsParser>>,
Single<WithoutArgs<RustcNoMirInlineParser>>,
Single<WithoutArgs<RustcNonConstTraitMethodParser>>,
Single<WithoutArgs<RustcNonnullOptimizationGuaranteedParser>>,
Single<WithoutArgs<RustcNounwindParser>>,
Single<WithoutArgs<RustcObjectLifetimeDefaultParser>>,
Single<WithoutArgs<RustcOffloadKernelParser>>,
Single<WithoutArgs<RustcOutlivesParser>>,
Single<WithoutArgs<RustcPassIndirectlyInNonRusticAbisParser>>,
Single<WithoutArgs<RustcPreserveUbChecksParser>>,
Single<WithoutArgs<RustcProcMacroDeclsParser>>,
Single<WithoutArgs<RustcReallocatorParser>>,
Single<WithoutArgs<RustcRegionsParser>>,
Single<WithoutArgs<RustcShouldNotBeCalledOnConstItems>>,
Single<WithoutArgs<RustcStrictCoherenceParser>>,
Single<WithoutArgs<RustcTrivialFieldReadsParser>>,
Single<WithoutArgs<RustcVarianceOfOpaquesParser>>,
Single<WithoutArgs<RustcVarianceParser>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<ThreadLocalParser>>,
Single<WithoutArgs<TrackCallerParser>>,
Single<WithoutArgs<TypeConstParser>>,
Single<WithoutArgs<UnsafeSpecializationMarkerParser>>,
// tidy-alphabetical-end
];
@ -381,7 +358,7 @@ impl Stage for Late {
}
fn should_emit(&self) -> ShouldEmit {
ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }
ShouldEmit::ErrorsAndLints
}
}
@ -438,7 +415,7 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
pub(crate) fn emit_lint(&mut self, lint: &'static Lint, kind: AttributeLintKind, span: Span) {
if !matches!(
self.stage.should_emit(),
ShouldEmit::ErrorsAndLints { .. } | ShouldEmit::EarlyFatal { also_emit_lints: true }
ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
) {
return;
}
@ -510,27 +487,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
)
}
/// Error that a filename string literal was expected.
pub(crate) fn expected_filename_literal(&self, span: Span) {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral);
}
pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed {
self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral)
}
pub(crate) fn expected_integer_literal_in_range(
&self,
span: Span,
lower_bound: isize,
upper_bound: isize,
) -> ErrorGuaranteed {
self.emit_parse_error(
span,
AttributeParseErrorReason::ExpectedIntegerLiteralInRange { lower_bound, upper_bound },
)
}
pub(crate) fn expected_list(&self, span: Span, args: &ArgParser) -> ErrorGuaranteed {
let span = match args {
ArgParser::NoArgs => span,
@ -770,18 +730,9 @@ pub enum ShouldEmit {
EarlyFatal { also_emit_lints: bool },
/// The operation will emit errors and lints.
/// This is usually what you need.
ErrorsAndLints {
/// Whether [`ArgParser`] will attempt to recover from errors.
///
/// Whether it is allowed to recover from bad input (like an invalid literal). Setting
/// this to `Forbidden` will instead return early, and not raise errors except at the top
/// level (in [`ArgParser::from_attr_args`]).
recovery: Recovery,
},
/// The operation will *not* emit errors and lints.
///
/// The parser can still call `delay_bug`, so you *must* ensure that this operation will also be
/// called with `ShouldEmit::ErrorsAndLints`.
ErrorsAndLints,
/// The operation will emit *not* errors and lints.
/// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::ErrorsAndLints`.
Nothing,
}
@ -790,7 +741,7 @@ impl ShouldEmit {
match self {
ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(),
ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(),
ShouldEmit::ErrorsAndLints { .. } => diag.emit(),
ShouldEmit::ErrorsAndLints => diag.emit(),
ShouldEmit::Nothing => diag.delay_as_bug(),
}
}

View file

@ -12,7 +12,7 @@ use rustc_session::Session;
use rustc_session::lint::{BuiltinLintDiag, LintId};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage};
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
use crate::parser::{ArgParser, PathParser, RefPathParser};
use crate::session_diagnostics::ParsedDescription;
@ -270,8 +270,6 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
let mut early_parsed_state = EarlyParsedState::default();
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
for attr in attrs {
// If we're only looking for a single attribute, skip all the ones we don't care about.
if let Some(expected) = self.parse_only {
@ -327,7 +325,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let parts =
n.item.path.segments.iter().map(|seg| seg.ident.name).collect::<Vec<_>>();
if let Some(accept) = S::parsers().accepters.get(parts.as_slice()) {
if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
let Some(args) = ArgParser::from_attr_args(
args,
&parts,
@ -368,26 +366,26 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
continue;
}
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
cx: self,
target_span,
target,
emit_lint: &mut emit_lint,
},
attr_span,
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
template: &accept.template,
attr_path: attr_path.clone(),
};
for accept in accepts {
let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
shared: SharedContext {
cx: self,
target_span,
target,
emit_lint: &mut emit_lint,
},
attr_span,
inner_span: lower_span(n.item.span()),
attr_style: attr.style,
parsed_description: ParsedDescription::Attribute,
template: &accept.template,
attr_path: attr_path.clone(),
};
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
(accept.accept_fn)(&mut cx, &args);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
}
} else {
// If we're here, we must be compiling a tool attribute... Or someone
@ -419,7 +417,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
}
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
for f in &finalizers {
for f in &S::parsers().finalizers {
if let Some(attr) = f(&mut FinalizeContext {
shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
all_attrs: &attr_paths,

View file

@ -113,3 +113,5 @@ pub use attributes::util::{is_builtin_attr, parse_version};
pub use context::{Early, Late, OmitDoc, ShouldEmit};
pub use interface::AttributeParser;
pub use session_diagnostics::ParsedDescription;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }

View file

@ -15,8 +15,8 @@ use rustc_ast_pretty::pprust;
use rustc_errors::{Diag, PResult};
use rustc_hir::{self as hir, AttrPath};
use rustc_parse::exp;
use rustc_parse::parser::{ForceCollect, Parser, PathStyle, Recovery, token_descr};
use rustc_session::errors::create_lit_error;
use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr};
use rustc_session::errors::{create_lit_error, report_lit_error};
use rustc_session::parse::ParseSess;
use rustc_span::{Ident, Span, Symbol, sym};
use thin_vec::ThinVec;
@ -113,29 +113,16 @@ impl ArgParser {
Some(match value {
AttrArgs::Empty => Self::NoArgs,
AttrArgs::Delimited(args) => {
// Diagnostic attributes can't error if they encounter non meta item syntax.
// However, the current syntax for diagnostic attributes is meta item syntax.
// Therefore we can substitute with a dummy value on invalid syntax.
if matches!(parts, [sym::rustc_dummy] | [sym::diagnostic, ..]) {
match MetaItemListParser::new(
&args.tokens,
args.dspan.entire(),
psess,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden },
) {
Ok(p) => return Some(ArgParser::List(p)),
Err(e) => {
// We can just dispose of the diagnostic and not bother with a lint,
// because this will look like `#[diagnostic::attr()]` was used. This
// is invalid for all diagnostic attrs, so a lint explaining the proper
// form will be issued later.
e.cancel();
return Some(ArgParser::List(MetaItemListParser {
sub_parsers: ThinVec::new(),
span: args.dspan.entire(),
}));
}
}
// The arguments of rustc_dummy and diagnostic::do_not_recommend are not validated
// if the arguments are delimited.
// See https://doc.rust-lang.org/reference/attributes/diagnostics.html#r-attributes.diagnostic.namespace.unknown-invalid-syntax
if parts == &[sym::rustc_dummy]
|| parts == &[sym::diagnostic, sym::do_not_recommend]
{
return Some(ArgParser::List(MetaItemListParser {
sub_parsers: ThinVec::new(),
span: args.dspan.entire(),
}));
}
if args.delim != Delimiter::Parenthesis {
@ -154,9 +141,7 @@ impl ArgParser {
}
AttrArgs::Eq { eq_span, expr } => Self::NameValue(NameValueParser {
eq_span: *eq_span,
value: expr_to_lit(psess, &expr, expr.span, should_emit)
.map_err(|e| should_emit.emit_err(e))
.ok()??,
value: expr_to_lit(psess, &expr, expr.span, should_emit)?,
value_span: expr.span,
}),
})
@ -351,56 +336,58 @@ impl NameValueParser {
}
}
fn expr_to_lit<'sess>(
psess: &'sess ParseSess,
fn expr_to_lit(
psess: &ParseSess,
expr: &Expr,
span: Span,
should_emit: ShouldEmit,
) -> PResult<'sess, Option<MetaItemLit>> {
) -> Option<MetaItemLit> {
if let ExprKind::Lit(token_lit) = expr.kind {
let res = MetaItemLit::from_token_lit(token_lit, expr.span);
match res {
Ok(lit) => {
if token_lit.suffix.is_some() {
Err(psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }))
should_emit.emit_err(
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
);
None
} else {
if lit.kind.is_unsuffixed() {
Ok(Some(lit))
} else {
Err(psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }))
if !lit.kind.is_unsuffixed() {
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
should_emit.emit_err(
psess.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
);
}
Some(lit)
}
}
Err(err) => {
let err = create_lit_error(psess, err, token_lit, expr.span);
if matches!(
should_emit,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }
) {
Err(err)
} else {
let lit = MetaItemLit {
symbol: token_lit.symbol,
suffix: token_lit.suffix,
kind: LitKind::Err(err.emit()),
span: expr.span,
};
Ok(Some(lit))
}
let guar = report_lit_error(psess, err, token_lit, expr.span);
let lit = MetaItemLit {
symbol: token_lit.symbol,
suffix: token_lit.suffix,
kind: LitKind::Err(guar),
span: expr.span,
};
Some(lit)
}
}
} else {
if matches!(should_emit, ShouldEmit::Nothing) {
return Ok(None);
return None;
}
// Example cases:
// - `#[foo = 1+1]`: results in `ast::ExprKind::BinOp`.
// - `#[foo = include_str!("nonexistent-file.rs")]`:
// results in `ast::ExprKind::Err`.
// results in `ast::ExprKind::Err`. In that case we delay
// the error because an earlier error will have already
// been reported.
let msg = "attribute value must be a literal";
let err = psess.dcx().struct_span_err(span, msg);
Err(err)
should_emit.emit_err(err);
None
}
}
@ -433,15 +420,9 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
if !lit.kind.is_unsuffixed() {
// Emit error and continue, we can still parse the attribute as if the suffix isn't there
let err = self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span });
if matches!(
self.should_emit,
ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }
) {
return Err(err);
} else {
self.should_emit.emit_err(err)
};
self.should_emit.emit_err(
self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }),
);
}
Ok(lit)
@ -575,10 +556,6 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> {
should_emit: ShouldEmit,
) -> PResult<'sess, MetaItemListParser> {
let mut parser = Parser::new(psess, tokens, None);
if let ShouldEmit::ErrorsAndLints { recovery } = should_emit {
parser = parser.recovery(recovery);
}
let mut this = MetaItemListParserContext { parser: &mut parser, should_emit };
// Presumably, the majority of the time there will only be one attr.

View file

@ -11,8 +11,10 @@ use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{Span, Symbol};
use rustc_target::spec::TargetTuple;
use crate::fluent_generated as fluent;
#[derive(Diagnostic)]
#[diag("invalid predicate `{$predicate}`", code = E0537)]
#[diag(attr_parsing_invalid_predicate, code = E0537)]
pub(crate) struct InvalidPredicate {
#[primary_span]
pub span: Span,
@ -21,7 +23,7 @@ pub(crate) struct InvalidPredicate {
}
#[derive(Diagnostic)]
#[diag("{$attr_str} attribute cannot have empty value")]
#[diag(attr_parsing_doc_alias_empty)]
pub(crate) struct DocAliasEmpty<'a> {
#[primary_span]
pub span: Span,
@ -29,7 +31,7 @@ pub(crate) struct DocAliasEmpty<'a> {
}
#[derive(Diagnostic)]
#[diag("{$char_} character isn't allowed in {$attr_str}")]
#[diag(attr_parsing_doc_alias_bad_char)]
pub(crate) struct DocAliasBadChar<'a> {
#[primary_span]
pub span: Span,
@ -38,7 +40,7 @@ pub(crate) struct DocAliasBadChar<'a> {
}
#[derive(Diagnostic)]
#[diag("{$attr_str} cannot start or end with ' '")]
#[diag(attr_parsing_doc_alias_start_end)]
pub(crate) struct DocAliasStartEnd<'a> {
#[primary_span]
pub span: Span,
@ -46,16 +48,7 @@ pub(crate) struct DocAliasStartEnd<'a> {
}
#[derive(Diagnostic)]
#[diag("`#[{$name})]` is missing a `{$field}` argument")]
pub(crate) struct CguFieldsMissing<'a> {
#[primary_span]
pub span: Span,
pub name: &'a AttrPath,
pub field: Symbol,
}
#[derive(Diagnostic)]
#[diag("`#![doc({$attr_name} = \"...\")]` isn't allowed as a crate-level attribute")]
#[diag(attr_parsing_doc_attr_not_crate_level)]
pub(crate) struct DocAttrNotCrateLevel {
#[primary_span]
pub span: Span,
@ -63,8 +56,8 @@ pub(crate) struct DocAttrNotCrateLevel {
}
#[derive(Diagnostic)]
#[diag("nonexistent keyword `{$keyword}` used in `#[doc(keyword = \"...\")]`")]
#[help("only existing keywords are allowed in core/std")]
#[diag(attr_parsing_doc_keyword_not_keyword)]
#[help]
pub(crate) struct DocKeywordNotKeyword {
#[primary_span]
pub span: Span,
@ -72,8 +65,8 @@ pub(crate) struct DocKeywordNotKeyword {
}
#[derive(Diagnostic)]
#[diag("nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = \"...\")]`")]
#[help("only existing builtin attributes are allowed in core/std")]
#[diag(attr_parsing_doc_attribute_not_attribute)]
#[help]
pub(crate) struct DocAttributeNotAttribute {
#[primary_span]
pub span: Span,
@ -81,28 +74,28 @@ pub(crate) struct DocAttributeNotAttribute {
}
#[derive(Diagnostic)]
#[diag("missing 'since'", code = E0542)]
#[diag(attr_parsing_missing_since, code = E0542)]
pub(crate) struct MissingSince {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("missing 'note'", code = E0543)]
#[diag(attr_parsing_missing_note, code = E0543)]
pub(crate) struct MissingNote {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("multiple stability levels", code = E0544)]
#[diag(attr_parsing_multiple_stability_levels, code = E0544)]
pub(crate) struct MultipleStabilityLevels {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`issue` must be a non-zero numeric string or \"none\"", code = E0545)]
#[diag(attr_parsing_invalid_issue_string, code = E0545)]
pub(crate) struct InvalidIssueString {
#[primary_span]
pub span: Span,
@ -115,31 +108,31 @@ pub(crate) struct InvalidIssueString {
// translatable.
#[derive(Subdiagnostic)]
pub(crate) enum InvalidIssueStringCause {
#[label("`issue` must not be \"0\", use \"none\" instead")]
#[label(attr_parsing_must_not_be_zero)]
MustNotBeZero {
#[primary_span]
span: Span,
},
#[label("cannot parse integer from empty string")]
#[label(attr_parsing_empty)]
Empty {
#[primary_span]
span: Span,
},
#[label("invalid digit found in string")]
#[label(attr_parsing_invalid_digit)]
InvalidDigit {
#[primary_span]
span: Span,
},
#[label("number too large to fit in target type")]
#[label(attr_parsing_pos_overflow)]
PosOverflow {
#[primary_span]
span: Span,
},
#[label("number too small to fit in target type")]
#[label(attr_parsing_neg_overflow)]
NegOverflow {
#[primary_span]
span: Span,
@ -160,21 +153,21 @@ impl InvalidIssueStringCause {
}
#[derive(Diagnostic)]
#[diag("missing 'feature'", code = E0546)]
#[diag(attr_parsing_missing_feature, code = E0546)]
pub(crate) struct MissingFeature {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("'feature' is not an identifier", code = E0546)]
#[diag(attr_parsing_non_ident_feature, code = E0546)]
pub(crate) struct NonIdentFeature {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("missing 'issue'", code = E0547)]
#[diag(attr_parsing_missing_issue, code = E0547)]
pub(crate) struct MissingIssue {
#[primary_span]
pub span: Span,
@ -183,20 +176,20 @@ pub(crate) struct MissingIssue {
// FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`?
// It is more similar to `IncorrectReprFormatGeneric`.
#[derive(Diagnostic)]
#[diag("incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all", code = E0552)]
#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)]
pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument", code = E0552)]
#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)]
pub(crate) struct IncorrectReprFormatPackedExpectInteger {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("invalid representation hint: `{$name}` does not take a parenthesized argument list", code = E0552)]
#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)]
pub(crate) struct InvalidReprHintNoParen {
#[primary_span]
pub span: Span,
@ -205,7 +198,7 @@ pub(crate) struct InvalidReprHintNoParen {
}
#[derive(Diagnostic)]
#[diag("invalid representation hint: `{$name}` does not take a value", code = E0552)]
#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)]
pub(crate) struct InvalidReprHintNoValue {
#[primary_span]
pub span: Span,
@ -214,19 +207,15 @@ pub(crate) struct InvalidReprHintNoValue {
}
#[derive(Diagnostic)]
#[diag("invalid `repr(align)` attribute: `align` needs an argument", code = E0589)]
#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)]
pub(crate) struct InvalidReprAlignNeedArg {
#[primary_span]
#[suggestion(
"supply an argument here",
code = "align(...)",
applicability = "has-placeholders"
)]
#[suggestion(code = "align(...)", applicability = "has-placeholders")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("invalid `repr({$repr_arg})` attribute: {$error_part}", code = E0589)]
#[diag(attr_parsing_invalid_repr_generic, code = E0589)]
pub(crate) struct InvalidReprGeneric<'a> {
#[primary_span]
pub span: Span,
@ -236,21 +225,21 @@ pub(crate) struct InvalidReprGeneric<'a> {
}
#[derive(Diagnostic)]
#[diag("incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses", code = E0693)]
#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)]
pub(crate) struct IncorrectReprFormatAlignOneArg {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("incorrect `repr(align)` attribute format: `align` expects a literal integer as argument", code = E0693)]
#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)]
pub(crate) struct IncorrectReprFormatExpectInteger {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("incorrect `repr({$repr_arg})` attribute format", code = E0693)]
#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)]
pub(crate) struct IncorrectReprFormatGeneric {
#[primary_span]
pub span: Span,
@ -264,7 +253,7 @@ pub(crate) struct IncorrectReprFormatGeneric {
#[derive(Subdiagnostic)]
pub(crate) enum IncorrectReprFormatGenericCause {
#[suggestion(
"use parentheses instead",
attr_parsing_suggestion,
code = "{name}({value})",
applicability = "machine-applicable"
)]
@ -280,7 +269,7 @@ pub(crate) enum IncorrectReprFormatGenericCause {
},
#[suggestion(
"use parentheses instead",
attr_parsing_suggestion,
code = "{name}({value})",
applicability = "machine-applicable"
)]
@ -309,48 +298,48 @@ impl IncorrectReprFormatGenericCause {
}
#[derive(Diagnostic)]
#[diag("`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute", code = E0717)]
#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)]
pub(crate) struct RustcPromotablePairing {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute", code = E0789)]
#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)]
pub(crate) struct RustcAllowedUnstablePairing {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("suggestions on deprecated items are unstable")]
#[diag(attr_parsing_deprecated_item_suggestion)]
pub(crate) struct DeprecatedItemSuggestion {
#[primary_span]
pub span: Span,
#[help("add `#![feature(deprecated_suggestion)]` to the crate root")]
#[help]
pub is_nightly: bool,
#[note("see #94785 for more details")]
#[note]
pub details: (),
}
#[derive(Diagnostic)]
#[diag("expected single version literal")]
#[diag(attr_parsing_expected_single_version_literal)]
pub(crate) struct ExpectedSingleVersionLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("expected a version literal")]
#[diag(attr_parsing_expected_version_literal)]
pub(crate) struct ExpectedVersionLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`{$name}` expects a list of feature names")]
#[diag(attr_parsing_expects_feature_list)]
pub(crate) struct ExpectsFeatureList {
#[primary_span]
pub span: Span,
@ -359,7 +348,7 @@ pub(crate) struct ExpectsFeatureList {
}
#[derive(Diagnostic)]
#[diag("`{$name}` expects feature names")]
#[diag(attr_parsing_expects_features)]
pub(crate) struct ExpectsFeatures {
#[primary_span]
pub span: Span,
@ -368,21 +357,21 @@ pub(crate) struct ExpectsFeatures {
}
#[derive(Diagnostic)]
#[diag("'since' must be a Rust version number, such as \"1.31.0\"")]
#[diag(attr_parsing_invalid_since)]
pub(crate) struct InvalidSince {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`soft` should not have any arguments")]
#[diag(attr_parsing_soft_no_args)]
pub(crate) struct SoftNoArgs {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("unknown version literal format, assuming it refers to a future version")]
#[diag(attr_parsing_unknown_version_literal)]
pub(crate) struct UnknownVersionLiteral {
#[primary_span]
pub span: Span,
@ -390,83 +379,78 @@ pub(crate) struct UnknownVersionLiteral {
// FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated.
#[derive(Diagnostic)]
#[diag("multiple `{$name}` attributes")]
#[diag(attr_parsing_unused_multiple)]
pub(crate) struct UnusedMultiple {
#[primary_span]
#[suggestion("remove this attribute", code = "", applicability = "machine-applicable")]
#[suggestion(code = "", applicability = "machine-applicable")]
pub this: Span,
#[note("attribute also specified here")]
#[note]
pub other: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag("`export_name` may not contain null characters", code = E0648)]
#[diag(attr_parsing_null_on_export, code = E0648)]
pub(crate) struct NullOnExport {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`link_section` may not contain null characters", code = E0648)]
#[diag(attr_parsing_null_on_link_section, code = E0648)]
pub(crate) struct NullOnLinkSection {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`objc::class!` may not contain null characters")]
#[diag(attr_parsing_null_on_objc_class)]
pub(crate) struct NullOnObjcClass {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`objc::selector!` may not contain null characters")]
#[diag(attr_parsing_null_on_objc_selector)]
pub(crate) struct NullOnObjcSelector {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`objc::class!` expected a string literal")]
#[diag(attr_parsing_objc_class_expected_string_literal)]
pub(crate) struct ObjcClassExpectedStringLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`objc::selector!` expected a string literal")]
#[diag(attr_parsing_objc_selector_expected_string_literal)]
pub(crate) struct ObjcSelectorExpectedStringLiteral {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("stability attributes may not be used outside of the standard library", code = E0734)]
#[diag(attr_parsing_stability_outside_std, code = E0734)]
pub(crate) struct StabilityOutsideStd {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("expected at least one confusable name")]
#[diag(attr_parsing_empty_confusables)]
pub(crate) struct EmptyConfusables {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[help("`#[{$name}]` can {$only}be applied to {$applied}")]
#[diag("`#[{$name}]` attribute cannot be used on {$target}")]
#[help]
#[diag(attr_parsing_invalid_target)]
pub(crate) struct InvalidTarget {
#[primary_span]
#[suggestion(
"remove the attribute",
code = "",
applicability = "machine-applicable",
style = "tool-only"
)]
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub span: Span,
pub name: AttrPath,
pub target: &'static str,
@ -475,7 +459,7 @@ pub(crate) struct InvalidTarget {
}
#[derive(Diagnostic)]
#[diag("invalid alignment value: {$error_part}", code = E0589)]
#[diag(attr_parsing_invalid_alignment_value, code = E0589)]
pub(crate) struct InvalidAlignmentValue {
#[primary_span]
pub span: Span,
@ -483,49 +467,43 @@ pub(crate) struct InvalidAlignmentValue {
}
#[derive(Diagnostic)]
#[diag("meta item in `repr` must be an identifier", code = E0565)]
#[diag(attr_parsing_repr_ident, code = E0565)]
pub(crate) struct ReprIdent {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("unrecognized representation hint", code = E0552)]
#[help(
"valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`"
)]
#[note(
"for more information, visit <https://doc.rust-lang.org/reference/type-layout.html?highlight=repr#representations>"
)]
#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)]
#[help]
#[note]
pub(crate) struct UnrecognizedReprHint {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("item annotated with `#[unstable_feature_bound]` should not be stable")]
#[help(
"if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`"
)]
#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)]
#[help]
pub(crate) struct UnstableFeatureBoundIncompatibleStability {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("attribute incompatible with `#[unsafe(naked)]`", code = E0736)]
#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)]
pub(crate) struct NakedFunctionIncompatibleAttribute {
#[primary_span]
#[label("the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`")]
#[label]
pub span: Span,
#[label("function marked with `#[unsafe(naked)]` here")]
#[label(attr_parsing_naked_attribute)]
pub naked_span: Span,
pub attr: String,
}
#[derive(Diagnostic)]
#[diag("ordinal value in `link_ordinal` is too large: `{$ordinal}`")]
#[note("the value may not exceed `u16::MAX`")]
#[diag(attr_parsing_link_ordinal_out_of_range)]
#[note]
pub(crate) struct LinkOrdinalOutOfRange {
#[primary_span]
pub span: Span,
@ -533,33 +511,20 @@ pub(crate) struct LinkOrdinalOutOfRange {
}
#[derive(Diagnostic)]
#[diag("element count in `rustc_scalable_vector` is too large: `{$n}`")]
#[note("the value may not exceed `u16::MAX`")]
#[diag(attr_parsing_rustc_scalable_vector_count_out_of_range)]
#[note]
pub(crate) struct RustcScalableVectorCountOutOfRange {
#[primary_span]
pub span: Span,
pub n: u128,
}
#[derive(Diagnostic)]
#[diag("attribute requires {$opt} to be enabled")]
pub(crate) struct AttributeRequiresOpt {
#[primary_span]
pub span: Span,
pub opt: &'static str,
}
pub(crate) enum AttributeParseErrorReason<'a> {
ExpectedNoArgs,
ExpectedStringLiteral {
byte_string: Option<Span>,
},
ExpectedFilenameLiteral,
ExpectedIntegerLiteral,
ExpectedIntegerLiteralInRange {
lower_bound: isize,
upper_bound: isize,
},
ExpectedAtLeastOneArgument,
ExpectedSingleArgument,
ExpectedList,
@ -617,7 +582,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
if let Some(start_point_span) = byte_string {
diag.span_suggestion(
start_point_span,
"consider removing the prefix",
fluent::attr_parsing_unsupported_literal_suggestion,
"",
Applicability::MaybeIncorrect,
);
@ -628,23 +593,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
diag.span_label(self.span, "expected a string literal here");
}
}
AttributeParseErrorReason::ExpectedFilenameLiteral => {
diag.span_label(self.span, "expected a filename string literal here");
}
AttributeParseErrorReason::ExpectedIntegerLiteral => {
diag.span_label(self.span, "expected an integer literal here");
}
AttributeParseErrorReason::ExpectedIntegerLiteralInRange {
lower_bound,
upper_bound,
} => {
diag.span_label(
self.span,
format!(
"expected an integer literal in the range of {lower_bound}..={upper_bound}"
),
);
}
AttributeParseErrorReason::ExpectedSingleArgument => {
diag.span_label(self.span, "expected a single argument here");
diag.code(E0805);
@ -785,27 +736,30 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
}
#[derive(Diagnostic)]
#[diag("`{$name}` is not an unsafe attribute")]
#[note("extraneous unsafe is not allowed in attributes")]
#[diag(attr_parsing_invalid_attr_unsafe)]
#[note]
pub(crate) struct InvalidAttrUnsafe {
#[primary_span]
#[label("this is not an unsafe attribute")]
#[label]
pub span: Span,
pub name: AttrPath,
}
#[derive(Diagnostic)]
#[diag("unsafe attribute used without unsafe")]
#[diag(attr_parsing_unsafe_attr_outside_unsafe)]
pub(crate) struct UnsafeAttrOutsideUnsafe {
#[primary_span]
#[label("usage of unsafe attribute")]
#[label]
pub span: Span,
#[subdiagnostic]
pub suggestion: Option<UnsafeAttrOutsideUnsafeSuggestion>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")]
#[multipart_suggestion(
attr_parsing_unsafe_attr_outside_unsafe_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
#[suggestion_part(code = "unsafe(")]
pub left: Span,
@ -814,7 +768,7 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
}
#[derive(Diagnostic)]
#[diag("wrong meta list delimiters")]
#[diag(attr_parsing_meta_bad_delim)]
pub(crate) struct MetaBadDelim {
#[primary_span]
pub span: Span,
@ -824,7 +778,7 @@ pub(crate) struct MetaBadDelim {
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"the delimiters should be `(` and `)`",
attr_parsing_meta_bad_delim_suggestion,
applicability = "machine-applicable"
)]
pub(crate) struct MetaBadDelimSugg {
@ -835,7 +789,7 @@ pub(crate) struct MetaBadDelimSugg {
}
#[derive(Diagnostic)]
#[diag("expected a literal (`1u8`, `1.0f32`, `\"string\"`, etc.) here, found {$descr}")]
#[diag(attr_parsing_invalid_meta_item)]
pub(crate) struct InvalidMetaItem {
#[primary_span]
pub span: Span,
@ -844,15 +798,12 @@ pub(crate) struct InvalidMetaItem {
pub quote_ident_sugg: Option<InvalidMetaItemQuoteIdentSugg>,
#[subdiagnostic]
pub remove_neg_sugg: Option<InvalidMetaItemRemoveNegSugg>,
#[label("{$descr}s are not allowed here")]
#[label]
pub label: Option<Span>,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"surround the identifier with quotation marks to make it into a string literal",
applicability = "machine-applicable"
)]
#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")]
pub(crate) struct InvalidMetaItemQuoteIdentSugg {
#[suggestion_part(code = "\"")]
pub before: Span,
@ -861,80 +812,73 @@ pub(crate) struct InvalidMetaItemQuoteIdentSugg {
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(
"negative numbers are not literals, try removing the `-` sign",
applicability = "machine-applicable"
)]
#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")]
pub(crate) struct InvalidMetaItemRemoveNegSugg {
#[suggestion_part(code = "")]
pub negative_sign: Span,
}
#[derive(Diagnostic)]
#[diag("suffixed literals are not allowed in attributes")]
#[help(
"instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)"
)]
#[diag(attr_parsing_suffixed_literal_in_attribute)]
#[help]
pub(crate) struct SuffixedLiteralInAttribute {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("link name must not be empty", code = E0454)]
#[diag(attr_parsing_empty_link_name, code = E0454)]
pub(crate) struct EmptyLinkName {
#[primary_span]
#[label("empty link name")]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("link kind `framework` is only supported on Apple targets", code = E0455)]
#[diag(attr_parsing_link_framework_apple, code = E0455)]
pub(crate) struct LinkFrameworkApple {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`wasm_import_module` is incompatible with other arguments in `#[link]` attributes")]
#[diag(attr_parsing_incompatible_wasm_link)]
pub(crate) struct IncompatibleWasmLink {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`#[link]` attribute requires a `name = \"string\"` argument", code = E0459)]
#[diag(attr_parsing_link_requires_name, code = E0459)]
pub(crate) struct LinkRequiresName {
#[primary_span]
#[label("missing `name` argument")]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")]
#[diag(attr_parsing_raw_dylib_no_nul)]
pub(crate) struct RawDylibNoNul {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)]
#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)]
pub(crate) struct RawDylibOnlyWindows {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(
"invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols"
)]
#[diag(attr_parsing_invalid_link_modifier)]
pub(crate) struct InvalidLinkModifier {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("multiple `{$modifier}` modifiers in a single `modifiers` argument")]
#[diag(attr_parsing_multiple_modifiers)]
pub(crate) struct MultipleModifiers {
#[primary_span]
pub span: Span,
@ -942,61 +886,52 @@ pub(crate) struct MultipleModifiers {
}
#[derive(Diagnostic)]
#[diag("import name type is only supported on x86")]
#[diag(attr_parsing_import_name_type_x86)]
pub(crate) struct ImportNameTypeX86 {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("linking modifier `bundle` is only compatible with `static` linking kind")]
#[diag(attr_parsing_bundle_needs_static)]
pub(crate) struct BundleNeedsStatic {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("linking modifier `export-symbols` is only compatible with `static` linking kind")]
pub(crate) struct ExportSymbolsNeedsStatic {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("linking modifier `whole-archive` is only compatible with `static` linking kind")]
#[diag(attr_parsing_whole_archive_needs_static)]
pub(crate) struct WholeArchiveNeedsStatic {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(
"linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds"
)]
#[diag(attr_parsing_as_needed_compatibility)]
pub(crate) struct AsNeededCompatibility {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("import name type can only be used with link kind `raw-dylib`")]
#[diag(attr_parsing_import_name_type_raw)]
pub(crate) struct ImportNameTypeRaw {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`limit` must be a non-negative integer")]
#[diag(attr_parsing_limit_invalid)]
pub(crate) struct LimitInvalid<'a> {
#[primary_span]
pub span: Span,
#[label("{$error_str}")]
#[label]
pub value_span: Span,
pub error_str: &'a str,
}
#[derive(Diagnostic)]
#[diag("wrong `cfg_attr` delimiters")]
#[diag(attr_parsing_cfg_attr_bad_delim)]
pub(crate) struct CfgAttrBadDelim {
#[primary_span]
pub span: Span,
@ -1005,25 +940,14 @@ pub(crate) struct CfgAttrBadDelim {
}
#[derive(Diagnostic)]
#[diag(
"doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of strings `#[doc(alias(\"a\", \"b\"))]`"
)]
#[diag(attr_parsing_doc_alias_malformed)]
pub(crate) struct DocAliasMalformed {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("definition of an unknown lang item: `{$name}`", code = E0522)]
pub(crate) struct UnknownLangItem {
#[primary_span]
#[label("definition of unknown lang item `{$name}`")]
pub span: Span,
pub name: Symbol,
}
#[derive(Diagnostic)]
#[diag("target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`")]
#[diag(attr_parsing_unsupported_instruction_set)]
pub(crate) struct UnsupportedInstructionSet<'a> {
#[primary_span]
pub span: Span,

View file

@ -205,7 +205,7 @@ pub(crate) fn allowed_targets_applied(
];
const IMPL_LIKE: &[Target] =
&[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum, Target::Union];
const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
let mut added_fake_targets = Vec::new();
filter_targets(

View file

@ -11,6 +11,7 @@ polonius-engine = "0.13.0"
rustc_abi = { path = "../rustc_abi" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }

View file

@ -0,0 +1,296 @@
borrowck_assign_due_to_use_closure =
assignment occurs due to use in closure
borrowck_assign_due_to_use_coroutine =
assign occurs due to use in coroutine
borrowck_assign_part_due_to_use_closure =
assignment to part occurs due to use in closure
borrowck_assign_part_due_to_use_coroutine =
assign to part occurs due to use in coroutine
borrowck_borrow_due_to_use_closure =
borrow occurs due to use in closure
borrowck_borrow_due_to_use_coroutine =
borrow occurs due to use in coroutine
borrowck_calling_operator_moves =
calling this operator moves the value
borrowck_calling_operator_moves_lhs =
calling this operator moves the left-hand side
borrowck_cannot_move_when_borrowed =
cannot move out of {$place ->
[value] value
*[other] {$place}
} because it is borrowed
.label = borrow of {$borrow_place ->
[value] value
*[other] {$borrow_place}
} occurs here
.move_label = move out of {$value_place ->
[value] value
*[other] {$value_place}
} occurs here
borrowck_capture_immute =
capture is immutable because of use here
borrowck_capture_move =
capture is moved because of use here
borrowck_capture_mut =
capture is mutable because of use here
borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
borrowck_closure_invoked_twice =
closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment
borrowck_closure_moved_twice =
closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment
borrowck_consider_borrow_type_contents =
help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
borrowck_could_not_normalize =
could not normalize `{$value}`
borrowck_could_not_prove =
could not prove `{$predicate}`
borrowck_dereference_suggestion =
dereference the return value
borrowck_func_take_self_moved_place =
`{$func}` takes ownership of the receiver `self`, which moves {$place_name}
borrowck_generic_does_not_live_long_enough =
`{$kind}` does not live long enough
borrowck_higher_ranked_lifetime_error =
higher-ranked lifetime error
borrowck_higher_ranked_subtype_error =
higher-ranked subtype error
borrowck_implicit_static =
this has an implicit `'static` lifetime requirement
borrowck_implicit_static_introduced =
calling this method introduces the `impl`'s `'static` requirement
borrowck_implicit_static_relax =
consider relaxing the implicit `'static` requirement
borrowck_lifetime_constraints_error =
lifetime may not live long enough
borrowck_limitations_implies_static =
due to a current limitation of the type system, this implies a `'static` lifetime
borrowck_move_closure_suggestion =
consider adding 'move' keyword before the nested closure
borrowck_move_out_place_here =
{$place} is moved here
borrowck_move_unsized =
cannot move a value of type `{$ty}`
.label = the size of `{$ty}` cannot be statically determined
borrowck_moved_a_fn_once_in_call =
this value implements `FnOnce`, which causes it to be moved when called
borrowck_moved_a_fn_once_in_call_call =
`FnOnce` closures can only be called once
borrowck_moved_a_fn_once_in_call_def =
`{$ty}` is made to be an `FnOnce` closure here
borrowck_moved_due_to_await =
{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this {$is_loop_message ->
[true] await, in previous iteration of loop
*[false] await
}
borrowck_moved_due_to_call =
{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this {$is_loop_message ->
[true] call, in previous iteration of loop
*[false] call
}
borrowck_moved_due_to_implicit_into_iter_call =
{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this implicit call to {$is_loop_message ->
[true] `.into_iter()`, in previous iteration of loop
*[false] `.into_iter()`
}
borrowck_moved_due_to_method_call =
{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this method {$is_loop_message ->
[true] call, in previous iteration of loop
*[false] call
}
borrowck_moved_due_to_usage_in_operator =
{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to usage in {$is_loop_message ->
[true] operator, in previous iteration of loop
*[false] operator
}
borrowck_opaque_type_lifetime_mismatch =
opaque type used twice with different lifetimes
.label = lifetime `{$arg}` used here
.prev_lifetime_label = lifetime `{$prev}` previously used here
.note = if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types
borrowck_partial_var_move_by_use_in_closure =
variable {$is_partial ->
[true] partially moved
*[false] moved
} due to use in closure
borrowck_partial_var_move_by_use_in_coroutine =
variable {$is_partial ->
[true] partially moved
*[false] moved
} due to use in coroutine
borrowck_restrict_to_static =
consider restricting the type parameter to the `'static` lifetime
borrowck_returned_async_block_escaped =
returns an `async` block that contains a reference to a captured variable, which then escapes the closure body
borrowck_returned_closure_escaped =
returns a closure that contains a reference to a captured variable, which then escapes the closure body
borrowck_returned_lifetime_short =
{$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`
borrowck_returned_lifetime_wrong =
{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`
borrowck_returned_ref_escaped =
returns a reference to a captured variable which escapes the closure body
borrowck_simd_intrinsic_arg_const =
{$arg ->
[1] 1st
[2] 2nd
[3] 3rd
*[other] {$arg}th
} argument of `{$intrinsic}` is required to be a `const` item
borrowck_suggest_create_fresh_reborrow =
consider reborrowing the `Pin` instead of moving it
borrowck_suggest_iterate_over_slice =
consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop
borrowck_tail_expr_drop_order = relative drop order changing in Rust 2024
.label = this temporary value will be dropped at the end of the block
.note = consider using a `let` binding to ensure the value will live long enough
borrowck_ty_no_impl_copy =
{$is_partial_move ->
[true] partial move
*[false] move
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait
borrowck_use_due_to_use_closure =
use occurs due to use in closure
borrowck_use_due_to_use_coroutine =
use occurs due to use in coroutine
borrowck_used_impl_require_static =
the used `impl` has a `'static` requirement
borrowck_value_capture_here =
value captured {$is_within ->
[true] here by coroutine
*[false] here
}
borrowck_value_moved_here =
value {$is_partial ->
[true] partially moved
*[false] moved
} {$is_move_msg ->
[true] into closure here
*[false] here
}{$is_loop_message ->
[true] , in previous iteration of loop
*[false] {""}
}
borrowck_var_borrow_by_use_in_closure =
borrow occurs due to use in closure
borrowck_var_borrow_by_use_in_coroutine =
borrow occurs due to use in coroutine
borrowck_var_borrow_by_use_place_in_closure =
{$is_single_var ->
*[true] borrow occurs
[false] borrows occur
} due to use of {$place} in closure
borrowck_var_borrow_by_use_place_in_coroutine =
{$is_single_var ->
*[true] borrow occurs
[false] borrows occur
} due to use of {$place} in coroutine
borrowck_var_cannot_escape_closure =
captured variable cannot escape `FnMut` closure body
.note = `FnMut` closures only have access to their captured variables while they are executing...
.cannot_escape = ...therefore, they cannot allow references to captured variables to escape
borrowck_var_does_not_need_mut =
variable does not need to be mutable
.suggestion = remove this `mut`
borrowck_var_first_borrow_by_use_place_in_closure =
first borrow occurs due to use of {$place} in closure
borrowck_var_first_borrow_by_use_place_in_coroutine =
first borrow occurs due to use of {$place} in coroutine
borrowck_var_here_captured = variable captured here
borrowck_var_here_defined = variable defined here
borrowck_var_move_by_use_in_closure =
move occurs due to use in closure
borrowck_var_move_by_use_in_coroutine =
move occurs due to use in coroutine
borrowck_var_mutable_borrow_by_use_place_in_closure =
mutable borrow occurs due to use of {$place} in closure
borrowck_var_second_borrow_by_use_place_in_closure =
second borrow occurs due to use of {$place} in closure
borrowck_var_second_borrow_by_use_place_in_coroutine =
second borrow occurs due to use of {$place} in coroutine

View file

@ -253,52 +253,19 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
}
let region = region.as_var();
let borrow = |activation_location| BorrowData {
let borrow = BorrowData {
kind,
region,
reserve_location: location,
activation_location,
activation_location: TwoPhaseActivation::NotTwoPhase,
borrowed_place,
assigned_place: *assigned_place,
};
let (idx, _) = self.location_map.insert_full(location, borrow);
let idx = BorrowIndex::from(idx);
let idx = if !kind.is_two_phase_borrow() {
debug!(" -> {:?}", location);
let (idx, _) = self
.location_map
.insert_full(location, borrow(TwoPhaseActivation::NotTwoPhase));
BorrowIndex::from(idx)
} else {
// When we encounter a 2-phase borrow statement, it will always
// be assigning into a temporary TEMP:
//
// TEMP = &foo
//
// so extract `temp`.
let Some(temp) = assigned_place.as_local() else {
span_bug!(
self.body.source_info(location).span,
"expected 2-phase borrow to assign to a local, not `{:?}`",
assigned_place,
);
};
// Consider the borrow not activated to start. When we find an activation, we'll update
// this field.
let (idx, _) = self
.location_map
.insert_full(location, borrow(TwoPhaseActivation::NotActivated));
let idx = BorrowIndex::from(idx);
// Insert `temp` into the list of pending activations. From
// now on, we'll be on the lookout for a use of it. Note that
// we are guaranteed that this use will come after the
// assignment.
let prev = self.pending_activations.insert(temp, idx);
assert_eq!(prev, None, "temporary associated with multiple two phase borrows");
idx
};
self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx);
self.local_map.entry(borrowed_place.local).or_default().insert(idx);
}
@ -367,3 +334,62 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
self.super_rvalue(rvalue, location)
}
}
impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
/// If this is a two-phase borrow, then we will record it
/// as "pending" until we find the activating use.
fn insert_as_pending_if_two_phase(
&mut self,
start_location: Location,
assigned_place: &mir::Place<'tcx>,
kind: mir::BorrowKind,
borrow_index: BorrowIndex,
) {
debug!(
"Borrows::insert_as_pending_if_two_phase({:?}, {:?}, {:?})",
start_location, assigned_place, borrow_index,
);
if !kind.allows_two_phase_borrow() {
debug!(" -> {:?}", start_location);
return;
}
// When we encounter a 2-phase borrow statement, it will always
// be assigning into a temporary TEMP:
//
// TEMP = &foo
//
// so extract `temp`.
let Some(temp) = assigned_place.as_local() else {
span_bug!(
self.body.source_info(start_location).span,
"expected 2-phase borrow to assign to a local, not `{:?}`",
assigned_place,
);
};
// Consider the borrow not activated to start. When we find an activation, we'll update
// this field.
{
let borrow_data = &mut self.location_map[borrow_index.as_usize()];
borrow_data.activation_location = TwoPhaseActivation::NotActivated;
}
// Insert `temp` into the list of pending activations. From
// now on, we'll be on the lookout for a use of it. Note that
// we are guaranteed that this use will come after the
// assignment.
let old_value = self.pending_activations.insert(temp, borrow_index);
if let Some(old_index) = old_value {
span_bug!(
self.body.source_info(start_location).span,
"found already pending activation for temp: {:?} \
at borrow_index: {:?} with associated data {:?}",
temp,
old_index,
self.location_map[old_index.as_usize()]
);
}
}
}

View file

@ -1,3 +1,6 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use rustc_errors::codes::*;
use rustc_errors::{Applicability, Diag, DiagCtxtHandle, struct_span_code_err};
use rustc_hir as hir;

View file

@ -24,6 +24,7 @@ use rustc_traits::{type_op_ascribe_user_type_with_span, type_op_prove_predicate_
use tracing::{debug, instrument};
use crate::MirBorrowckCtxt;
use crate::region_infer::values::RegionElement;
use crate::session_diagnostics::{
HigherRankedErrorCause, HigherRankedLifetimeError, HigherRankedSubtypeError,
};
@ -48,12 +49,11 @@ impl<'tcx> UniverseInfo<'tcx> {
UniverseInfo::RelateTys { expected, found }
}
/// Report an error where an element erroneously made its way into `placeholder`.
pub(crate) fn report_erroneous_element(
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion<'tcx>,
error_element: Option<ty::PlaceholderRegion<'tcx>>,
error_element: RegionElement<'tcx>,
cause: ObligationCause<'tcx>,
) {
match *self {
@ -146,14 +146,14 @@ pub(crate) trait TypeOpInfo<'tcx> {
) -> Option<Diag<'infcx>>;
/// Constraints require that `error_element` appear in the
/// values of `placeholder`, but this cannot be proven to
/// values of `placeholder`, but this cannot be proven to
/// hold. Report an error.
#[instrument(level = "debug", skip(self, mbcx))]
fn report_erroneous_element(
&self,
mbcx: &mut MirBorrowckCtxt<'_, '_, 'tcx>,
placeholder: ty::PlaceholderRegion<'tcx>,
error_element: Option<ty::PlaceholderRegion<'tcx>>,
error_element: RegionElement<'tcx>,
cause: ObligationCause<'tcx>,
) {
let tcx = mbcx.infcx.tcx;
@ -169,20 +169,22 @@ pub(crate) trait TypeOpInfo<'tcx> {
let placeholder_region = ty::Region::new_placeholder(
tcx,
ty::PlaceholderRegion::new(adjusted_universe.into(), placeholder.bound),
ty::Placeholder::new(adjusted_universe.into(), placeholder.bound),
);
// FIXME: one day this should just be error_element,
// and this method shouldn't do anything.
let error_region = error_element.and_then(|e| {
let adjusted_universe = e.universe.as_u32().checked_sub(base_universe.as_u32());
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::PlaceholderRegion::new(adjusted.into(), e.bound),
)
})
});
let error_region =
if let RegionElement::PlaceholderRegion(error_placeholder) = error_element {
let adjusted_universe =
error_placeholder.universe.as_u32().checked_sub(base_universe.as_u32());
adjusted_universe.map(|adjusted| {
ty::Region::new_placeholder(
tcx,
ty::Placeholder::new(adjusted.into(), error_placeholder.bound),
)
})
} else {
None
};
debug!(?placeholder_region);

View file

@ -1,5 +1,8 @@
// ignore-tidy-filelength
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use std::iter;
use std::ops::ControlFlow;
@ -1256,7 +1259,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.suggest_cloning_inner(err, ty, expr);
}
} else if let ty::Adt(def, args) = ty.kind()
&& let Some(local_did) = def.did().as_local()
&& def.did().as_local().is_some()
&& def.variants().iter().all(|variant| {
variant
.fields
@ -1266,50 +1269,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
{
let ty_span = self.infcx.tcx.def_span(def.did());
let mut span: MultiSpan = ty_span.into();
let mut derive_clone = false;
self.infcx.tcx.for_each_relevant_impl(
self.infcx.tcx.lang_items().clone_trait().unwrap(),
ty,
|def_id| {
if self.infcx.tcx.is_automatically_derived(def_id) {
derive_clone = true;
span.push_span_label(
self.infcx.tcx.def_span(def_id),
"derived `Clone` adds implicit bounds on type parameters",
);
if let Some(generics) = self.infcx.tcx.hir_get_generics(local_did) {
for param in generics.params {
if let hir::GenericParamKind::Type { .. } = param.kind {
span.push_span_label(
param.span,
format!(
"introduces an implicit `{}: Clone` bound",
param.name.ident()
),
);
}
}
}
}
},
);
let msg = if !derive_clone {
span.push_span_label(
ty_span,
format!(
"consider {}implementing `Clone` for this type",
if derive_clone { "manually " } else { "" }
),
);
format!("if `{ty}` implemented `Clone`, you could clone the value")
} else {
format!("if all bounds were met, you could clone the value")
};
span.push_span_label(ty_span, "consider implementing `Clone` for this type");
span.push_span_label(expr.span, "you could clone this value");
err.span_note(span, msg);
if derive_clone {
err.help("consider manually implementing `Clone` to avoid undesired bounds");
}
err.span_note(
span,
format!("if `{ty}` implemented `Clone`, you could clone the value"),
);
} else if let ty::Param(param) = ty.kind()
&& let Some(_clone_trait_def) = self.infcx.tcx.lang_items().clone_trait()
&& let generics = self.infcx.tcx.generics_of(self.mir_def_id())
@ -2347,12 +2312,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
tcx: TyCtxt<'hir>,
issue_span: Span,
expr_span: Span,
body_expr: Option<&'hir hir::Expr<'hir>> = None,
loop_bind: Option<&'hir Ident> = None,
loop_span: Option<Span> = None,
head_span: Option<Span> = None,
pat_span: Option<Span> = None,
head: Option<&'hir hir::Expr<'hir>> = None,
body_expr: Option<&'hir hir::Expr<'hir>>,
loop_bind: Option<&'hir Ident>,
loop_span: Option<Span>,
head_span: Option<Span>,
pat_span: Option<Span>,
head: Option<&'hir hir::Expr<'hir>>,
}
impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
@ -2418,7 +2383,17 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
hir::intravisit::walk_expr(self, ex);
}
}
let mut finder = ExprFinder { tcx, expr_span: span, issue_span, .. };
let mut finder = ExprFinder {
tcx,
expr_span: span,
issue_span,
loop_bind: None,
body_expr: None,
head_span: None,
loop_span: None,
pat_span: None,
head: None,
};
finder.visit_expr(tcx.hir_body(body_id).value);
if let Some(body_expr) = finder.body_expr
@ -2653,13 +2628,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
struct ExpressionFinder<'tcx> {
capture_span: Span,
closure_change_spans: Vec<Span> = vec![],
closure_arg_span: Option<Span> = None,
in_closure: bool = false,
suggest_arg: String = String::new(),
closure_change_spans: Vec<Span>,
closure_arg_span: Option<Span>,
in_closure: bool,
suggest_arg: String,
tcx: TyCtxt<'tcx>,
closure_local_id: Option<hir::HirId> = None,
closure_call_changes: Vec<(Span, String)> = vec![],
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>) {
@ -2740,8 +2715,16 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}) = self.infcx.tcx.hir_node(self.mir_hir_id())
&& let hir::Node::Expr(expr) = self.infcx.tcx.hir_node(body_id.hir_id)
{
let mut finder =
ExpressionFinder { capture_span: *capture_kind_span, tcx: self.infcx.tcx, .. };
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![],
tcx: self.infcx.tcx,
};
finder.visit_expr(expr);
if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {

View file

@ -1,6 +1,10 @@
//! Print diagnostics to explain why values are borrowed.
use rustc_data_structures::assert_matches;
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use std::assert_matches::assert_matches;
use rustc_errors::{Applicability, Diag, EmissionGuarantee};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
@ -649,8 +653,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
// We want to focus on relevant live locals in diagnostics, so when polonius is enabled, we
// ensure that we don't emit live boring locals as explanations.
let is_local_boring = |local| {
if let Some(polonius_context) = self.polonius_context {
polonius_context.boring_nll_locals.contains(&local)
if let Some(polonius_diagnostics) = self.polonius_diagnostics {
polonius_diagnostics.boring_nll_locals.contains(&local)
} else {
assert!(!tcx.sess.opts.unstable_opts.polonius.is_next_enabled());

View file

@ -4,7 +4,7 @@ use std::collections::BTreeMap;
use rustc_abi::{FieldIdx, VariantIdx};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::{Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, listify, msg};
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, listify};
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::{
self as hir, CoroutineKind, GenericBound, LangItem, WhereBoundPredicate, WherePredicateKind,
@ -35,6 +35,7 @@ use tracing::debug;
use super::MirBorrowckCtxt;
use super::borrow_set::BorrowData;
use crate::constraints::OutlivesConstraint;
use crate::fluent_generated as fluent;
use crate::nll::ConstraintDescription;
use crate::session_diagnostics::{
CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
@ -161,6 +162,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) {
if count > 10 {
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
diag.note(format!("...and {} other attempted mutable borrows", count - 10));
}
self.buffer_error(diag);
@ -233,6 +236,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// LL | for (key, value) in dict {
/// | ^^^^
/// ```
#[allow(rustc::diagnostic_outside_of_impl)] // FIXME
pub(super) fn add_moved_or_invoked_closure_note(
&self,
location: Location,
@ -699,7 +703,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
diag.span_note(pred.span, LIMITATION_NOTE);
diag.span_note(pred.span, fluent::borrowck_limitations_implies_static);
return;
}
for bound in bounds.iter() {
@ -710,7 +714,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
.rfind(|bgp| tcx.local_def_id_to_hir_id(bgp.def_id) == gat_hir_id)
.is_some()
{
diag.span_note(bound.span, LIMITATION_NOTE);
diag.span_note(bound.span, fluent::borrowck_limitations_implies_static);
return;
}
}
@ -816,6 +820,7 @@ impl UseSpans<'_> {
}
/// Add a span label to the arguments of the closure, if it exists.
#[allow(rustc::diagnostic_outside_of_impl)]
pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) {
if let UseSpans::ClosureUse { args_span, .. } = self {
err.subdiagnostic(f(args_span));
@ -824,6 +829,7 @@ impl UseSpans<'_> {
/// Add a span label to the use of the captured variable, if it exists.
/// only adds label to the `path_span`
#[allow(rustc::diagnostic_outside_of_impl)]
pub(super) fn var_path_only_subdiag(
self,
err: &mut Diag<'_>,
@ -855,6 +861,7 @@ impl UseSpans<'_> {
}
/// Add a subdiagnostic to the use of the captured variable, if it exists.
#[allow(rustc::diagnostic_outside_of_impl)]
pub(super) fn var_subdiag(
self,
err: &mut Diag<'_>,
@ -1218,6 +1225,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.borrow_spans(span, borrow.reserve_location)
}
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn explain_captures(
&mut self,
err: &mut Diag<'infcx>,
@ -1311,7 +1320,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let mut span: MultiSpan = spans.clone().into();
err.arg("ty", param_ty.to_string());
let msg = err.dcx.eagerly_translate_to_string(
msg!("`{$ty}` is made to be an `FnOnce` closure here"),
fluent::borrowck_moved_a_fn_once_in_call_def,
err.args.iter(),
);
err.remove_arg("ty");
@ -1320,9 +1329,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
span.push_span_label(
fn_call_span,
msg!("this value implements `FnOnce`, which causes it to be moved when called"),
fluent::borrowck_moved_a_fn_once_in_call,
);
err.span_note(span, msg!("`FnOnce` closures can only be called once"));
err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
} else {
err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
}
@ -1567,6 +1576,3 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.local_name(index).is_none_or(|name| name.as_str().starts_with('_'))
}
}
const LIMITATION_NOTE: DiagMessage =
msg!("due to a current limitation of the type system, this implies a `'static` lifetime");

View file

@ -1,4 +1,6 @@
use rustc_abi::FieldIdx;
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::Visitor;
@ -8,7 +10,7 @@ use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
use rustc_span::def_id::DefId;
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
use rustc_span::{BytePos, DUMMY_SP, ExpnKind, MacroKind, Span};
use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::debug;
@ -473,30 +475,49 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if def_id.as_local() == Some(self.mir_def_id())
&& let Some(upvar_field) = upvar_field =>
{
self.report_closure_move_error(
span,
move_place,
*def_id,
closure_args.as_closure().kind_ty(),
upvar_field,
ty::Asyncness::No,
)
}
ty::CoroutineClosure(def_id, closure_args)
if def_id.as_local() == Some(self.mir_def_id())
&& let Some(upvar_field) = upvar_field
&& self
.get_closure_bound_clause_span(*def_id, ty::Asyncness::Yes)
.is_some() =>
{
self.report_closure_move_error(
span,
move_place,
*def_id,
closure_args.as_coroutine_closure().kind_ty(),
upvar_field,
ty::Asyncness::Yes,
)
let closure_kind_ty = closure_args.as_closure().kind_ty();
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
Some(ty::ClosureKind::FnOnce) => {
bug!("closure kind does not match first argument type")
}
None => bug!("closure kind not inferred by borrowck"),
};
let capture_description =
format!("captured variable in an `{closure_kind}` closure");
let upvar = &self.upvars[upvar_field.index()];
let upvar_hir_id = upvar.get_root_variable();
let upvar_name = upvar.to_string(tcx);
let upvar_span = tcx.hir_span(upvar_hir_id);
let place_name = self.describe_any_place(move_place.as_ref());
let place_description =
if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
format!("{place_name}, a {capture_description}")
} else {
format!("{place_name}, as `{upvar_name}` is a {capture_description}")
};
debug!(
"report: closure_kind_ty={:?} closure_kind={:?} place_description={:?}",
closure_kind_ty, closure_kind, place_description,
);
let closure_span = tcx.def_span(def_id);
self.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{closure_kind}` closure"),
)
.with_span_help(
self.get_closure_bound_clause_span(*def_id),
"`Fn` and `FnMut` closures require captured values to be able to be \
consumed multiple times, but `FnOnce` closures may consume them only once",
)
}
_ => {
let source = self.borrowed_content_source(deref_base);
@ -545,134 +566,45 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
err
}
fn report_closure_move_error(
&self,
span: Span,
move_place: Place<'tcx>,
def_id: DefId,
closure_kind_ty: Ty<'tcx>,
upvar_field: FieldIdx,
asyncness: ty::Asyncness,
) -> Diag<'infcx> {
let tcx = self.infcx.tcx;
let closure_kind = match closure_kind_ty.to_opt_closure_kind() {
Some(kind @ (ty::ClosureKind::Fn | ty::ClosureKind::FnMut)) => kind,
Some(ty::ClosureKind::FnOnce) => {
bug!("closure kind does not match first argument type")
}
None => bug!("closure kind not inferred by borrowck"),
};
let async_prefix = if asyncness.is_async() { "Async" } else { "" };
let capture_description =
format!("captured variable in an `{async_prefix}{closure_kind}` closure");
let upvar = &self.upvars[upvar_field.index()];
let upvar_hir_id = upvar.get_root_variable();
let upvar_name = upvar.to_string(tcx);
let upvar_span = tcx.hir_span(upvar_hir_id);
let place_name = self.describe_any_place(move_place.as_ref());
let place_description = if self.is_upvar_field_projection(move_place.as_ref()).is_some() {
format!("{place_name}, a {capture_description}")
} else {
format!("{place_name}, as `{upvar_name}` is a {capture_description}")
};
debug!(?closure_kind_ty, ?closure_kind, ?place_description);
let closure_span = tcx.def_span(def_id);
let help_msg = format!(
"`{async_prefix}Fn` and `{async_prefix}FnMut` closures require captured values to \
be able to be consumed multiple times, but `{async_prefix}FnOnce` closures may \
consume them only once"
);
let mut err = self
.cannot_move_out_of(span, &place_description)
.with_span_label(upvar_span, "captured outer variable")
.with_span_label(
closure_span,
format!("captured by this `{async_prefix}{closure_kind}` closure"),
);
if let Some(bound_span) = self.get_closure_bound_clause_span(def_id, asyncness) {
err.span_help(bound_span, help_msg);
} else if !asyncness.is_async() {
// For sync closures, always emit the help message even without a span.
// For async closures, we only enter this branch if we found a valid span
// (due to the match guard), so no fallback is needed.
err.help(help_msg);
}
err
}
fn get_closure_bound_clause_span(
&self,
def_id: DefId,
asyncness: ty::Asyncness,
) -> Option<Span> {
fn get_closure_bound_clause_span(&self, def_id: DefId) -> Span {
let tcx = self.infcx.tcx;
let typeck_result = tcx.typeck(self.mir_def_id());
// Check whether the closure is an argument to a call, if so,
// get the instantiated where-bounds of that call.
let closure_hir_id = tcx.local_def_id_to_hir_id(def_id.expect_local());
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return None };
let hir::Node::Expr(parent) = tcx.parent_hir_node(closure_hir_id) else { return DUMMY_SP };
let predicates = match parent.kind {
hir::ExprKind::Call(callee, _) => {
let ty = typeck_result.node_type_opt(callee.hir_id)?;
let ty::FnDef(fn_def_id, args) = ty.kind() else { return None };
let Some(ty) = typeck_result.node_type_opt(callee.hir_id) else { return DUMMY_SP };
let ty::FnDef(fn_def_id, args) = ty.kind() else { return DUMMY_SP };
tcx.predicates_of(fn_def_id).instantiate(tcx, args)
}
hir::ExprKind::MethodCall(..) => {
let (_, method) = typeck_result.type_dependent_def(parent.hir_id)?;
let Some((_, method)) = typeck_result.type_dependent_def(parent.hir_id) else {
return DUMMY_SP;
};
let args = typeck_result.node_args(parent.hir_id);
tcx.predicates_of(method).instantiate(tcx, args)
}
_ => return None,
_ => return DUMMY_SP,
};
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`
// or `AsyncFn[Mut]`.
// Check whether one of the where-bounds requires the closure to impl `Fn[Mut]`.
for (pred, span) in predicates.predicates.iter().zip(predicates.spans.iter()) {
let dominated_by_fn_trait = self
.closure_clause_kind(*pred, def_id, asyncness)
.is_some_and(|kind| matches!(kind, ty::ClosureKind::Fn | ty::ClosureKind::FnMut));
if dominated_by_fn_trait {
// Found `<TyOfCapturingClosure as FnMut>` or
// `<TyOfCapturingClosure as AsyncFnMut>`.
// We point at the bound that coerced the closure, which could be changed
// to `FnOnce()` or `AsyncFnOnce()` to avoid the move error.
return Some(*span);
if let Some(clause) = pred.as_trait_clause()
&& let ty::Closure(clause_closure_def_id, _) = clause.self_ty().skip_binder().kind()
&& *clause_closure_def_id == def_id
&& (tcx.lang_items().fn_mut_trait() == Some(clause.def_id())
|| tcx.lang_items().fn_trait() == Some(clause.def_id()))
{
// Found `<TyOfCapturingClosure as FnMut>`
// We point at the `Fn()` or `FnMut()` bound that coerced the closure, which
// could be changed to `FnOnce()` to avoid the move error.
return *span;
}
}
None
}
/// If `pred` is a trait clause binding the closure `def_id` to `Fn`/`FnMut`/`FnOnce`
/// (or their async equivalents based on `asyncness`), returns the corresponding
/// `ClosureKind`. Otherwise returns `None`.
fn closure_clause_kind(
&self,
pred: ty::Clause<'tcx>,
def_id: DefId,
asyncness: ty::Asyncness,
) -> Option<ty::ClosureKind> {
let tcx = self.infcx.tcx;
let clause = pred.as_trait_clause()?;
let kind = match asyncness {
ty::Asyncness::Yes => tcx.async_fn_trait_kind_from_def_id(clause.def_id()),
ty::Asyncness::No => tcx.fn_trait_kind_from_def_id(clause.def_id()),
}?;
match clause.self_ty().skip_binder().kind() {
ty::Closure(id, _) | ty::CoroutineClosure(id, _) if *id == def_id => Some(kind),
_ => None,
}
DUMMY_SP
}
fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diag<'_>, span: Span) {

View file

@ -1,3 +1,6 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use core::ops::ControlFlow;
use either::Either;
@ -354,71 +357,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
{
if snippet.starts_with("&mut ") {
// In calls, `&mut &mut T` may be deref-coerced to `&mut T`, and
// removing the extra `&mut` is the most direct suggestion. But for
// pattern-matching expressions (`match`, `if let`, `while let`), that
// can easily turn into a move, so prefer suggesting an explicit
// reborrow via `&mut *x` instead.
let mut in_pat_scrutinee = false;
let mut is_deref_coerced = false;
if let Some(expr) = self.find_expr(source_info.span) {
let tcx = self.infcx.tcx;
let span = expr.span.source_callsite();
for (_, node) in tcx.hir_parent_iter(expr.hir_id) {
if let Node::Expr(parent_expr) = node {
match parent_expr.kind {
ExprKind::Match(scrutinee, ..)
if scrutinee
.span
.source_callsite()
.contains(span) =>
{
in_pat_scrutinee = true;
break;
}
ExprKind::Let(let_expr)
if let_expr
.init
.span
.source_callsite()
.contains(span) =>
{
in_pat_scrutinee = true;
break;
}
_ => {}
}
}
}
let typeck = tcx.typeck(expr.hir_id.owner.def_id);
is_deref_coerced =
typeck.expr_adjustments(expr).iter().any(|adj| {
matches!(adj.kind, ty::adjustment::Adjust::Deref(_))
});
}
if in_pat_scrutinee {
// Best-effort structured suggestion: insert `*` after `&mut `.
err.span_suggestion_verbose(
source_info
.span
.with_lo(source_info.span.lo() + BytePos(5))
.shrink_to_lo(),
"to reborrow the mutable reference, add `*`",
"*",
Applicability::MaybeIncorrect,
);
} else if is_deref_coerced {
// We don't have access to the HIR to get accurate spans, but we
// can give a best effort structured suggestion.
err.span_suggestion_verbose(
source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
"if there is only one mutable reborrow, remove the `&mut`",
"",
Applicability::MaybeIncorrect,
);
}
// We don't have access to the HIR to get accurate spans, but we can
// give a best effort structured suggestion.
err.span_suggestion_verbose(
source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
"if there is only one mutable reborrow, remove the `&mut`",
"",
Applicability::MaybeIncorrect,
);
} else {
// This can occur with things like `(&mut self).foo()`.
err.span_help(source_info.span, "try removing `&mut` here");
@ -1752,7 +1698,7 @@ fn suggest_ampmut<'tcx>(
&& let Either::Left(rhs_stmt_new) = body.stmt_at(*assign)
&& let StatementKind::Assign(box (_, rvalue_new)) = &rhs_stmt_new.kind
&& let rhs_span_new = rhs_stmt_new.source_info.span
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span_new)
&& let Ok(rhs_str_new) = tcx.sess.source_map().span_to_snippet(rhs_span)
{
(rvalue, rhs_span, rhs_str) = (rvalue_new, rhs_span_new, rhs_str_new);
}

View file

@ -1,3 +1,6 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use std::ops::ControlFlow;
use either::Either;

View file

@ -1,6 +1,9 @@
//! Contains utilities for generating suggestions for borrowck errors related to unsatisfied
//! outlives constraints.
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use std::collections::BTreeMap;
use rustc_data_structures::fx::FxIndexSet;

View file

@ -1,7 +1,7 @@
//! Error reporting machinery for lifetime errors.
use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, msg};
use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::GenericBound::Trait;
use rustc_hir::QPath::Resolved;
@ -27,15 +27,16 @@ use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::{Obligation, ObligationCtxt};
use tracing::{debug, instrument, trace};
use super::{LIMITATION_NOTE, OutlivesSuggestionBuilder, RegionName, RegionNameSource};
use super::{OutlivesSuggestionBuilder, RegionName, RegionNameSource};
use crate::nll::ConstraintDescription;
use crate::region_infer::values::RegionElement;
use crate::region_infer::{BlameConstraint, TypeTest};
use crate::session_diagnostics::{
FnMutError, FnMutReturnTypeErr, GenericDoesNotLiveLongEnough, LifetimeOutliveErr,
LifetimeReturnCategoryErr, RequireStaticErr, VarHereDenote,
};
use crate::universal_regions::DefiningTy;
use crate::{MirBorrowckCtxt, borrowck_errors};
use crate::{MirBorrowckCtxt, borrowck_errors, fluent_generated as fluent};
impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
fn description(&self) -> &'static str {
@ -103,9 +104,15 @@ pub(crate) enum RegionErrorKind<'tcx> {
/// A generic bound failure for a type test (`T: 'a`).
TypeTestError { type_test: TypeTest<'tcx> },
/// 'p outlives 'r, which does not hold. 'p is always a placeholder
/// and 'r is some other region.
PlaceholderOutlivesIllegalRegion { longer_fr: RegionVid, illegally_outlived_r: RegionVid },
/// Higher-ranked subtyping error.
BoundUniversalRegionError {
/// The placeholder free region.
longer_fr: RegionVid,
/// The region element that erroneously must be outlived by `longer_fr`.
error_element: RegionElement<'tcx>,
/// The placeholder region.
placeholder: ty::PlaceholderRegion<'tcx>,
},
/// Any other lifetime error.
RegionError {
@ -189,6 +196,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
// 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
#[allow(rustc::diagnostic_outside_of_impl)]
fn suggest_static_lifetime_for_gat_from_hrtb(
&self,
diag: &mut Diag<'_>,
@ -258,7 +266,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }) = bound else {
return;
};
diag.span_note(*trait_span, LIMITATION_NOTE);
diag.span_note(*trait_span, fluent::borrowck_limitations_implies_static);
let Some(generics_fn) = tcx.hir_get_generics(self.body.source.def_id().expect_local())
else {
return;
@ -291,7 +299,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if suggestions.len() > 0 {
suggestions.dedup();
diag.multipart_suggestion_verbose(
msg!("consider restricting the type parameter to the `'static` lifetime"),
fluent::borrowck_restrict_to_static,
suggestions,
Applicability::MaybeIncorrect,
);
@ -353,11 +361,28 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}
RegionErrorKind::PlaceholderOutlivesIllegalRegion {
RegionErrorKind::BoundUniversalRegionError {
longer_fr,
illegally_outlived_r,
placeholder,
error_element,
} => {
self.report_erroneous_rvid_reaches_placeholder(longer_fr, illegally_outlived_r)
let error_vid = self.regioncx.region_from_element(longer_fr, &error_element);
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
let cause = self
.regioncx
.best_blame_constraint(
longer_fr,
NllRegionVariableOrigin::Placeholder(placeholder),
error_vid,
)
.0
.cause;
let universe = placeholder.universe;
let universe_info = self.regioncx.universe_info(universe);
universe_info.report_erroneous_element(self, placeholder, error_element, cause);
}
RegionErrorKind::RegionError { fr_origin, longer_fr, shorter_fr, is_reported } => {
@ -388,43 +413,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
outlives_suggestion.add_suggestion(self);
}
/// Report that `longer_fr: error_vid`, which doesn't hold,
/// where `longer_fr` is a placeholder.
fn report_erroneous_rvid_reaches_placeholder(
&mut self,
longer_fr: RegionVid,
error_vid: RegionVid,
) {
use NllRegionVariableOrigin::*;
let origin_longer = self.regioncx.definitions[longer_fr].origin;
let Placeholder(placeholder) = origin_longer else {
bug!("Expected {longer_fr:?} to come from placeholder!");
};
// FIXME: Is throwing away the existential region really the best here?
let error_region = match self.regioncx.definitions[error_vid].origin {
FreeRegion | Existential { .. } => None,
Placeholder(other_placeholder) => Some(other_placeholder),
};
// Find the code to blame for the fact that `longer_fr` outlives `error_fr`.
let cause =
self.regioncx.best_blame_constraint(longer_fr, origin_longer, error_vid).0.cause;
// FIXME these methods should have better names, and also probably not be this generic.
// FIXME note that we *throw away* the error element here! We probably want to
// thread it through the computation further down and use it, but there currently isn't
// anything there to receive it.
self.regioncx.universe_info(placeholder.universe).report_erroneous_element(
self,
placeholder,
error_region,
cause,
);
}
/// Report an error because the universal region `fr` was required to outlive
/// `outlived_fr` but it is not known to do so. For example:
///
@ -433,6 +421,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// ```
///
/// Here we would be invoked with `fr = 'a` and `outlived_fr = 'b`.
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
pub(crate) fn report_region_error(
&mut self,
fr: RegionVid,
@ -586,6 +577,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// executing...
/// = note: ...therefore, returned references to captured variables will escape the closure
/// ```
#[allow(rustc::diagnostic_outside_of_impl)] // FIXME
fn report_fnmut_error(
&self,
errci: &ErrorConstraintInfo<'tcx>,
@ -694,12 +686,18 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from);
if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
diag.span_label(
outlived_fr_span,
format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",),
);
}
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
diag.span_label(
fr_span,
@ -734,6 +732,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
let outlived_fr_region_name = self.give_region_a_name(errci.outlived_fr).unwrap();
outlived_fr_region_name.highlight_region_name(&mut diag);
// FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
diag.span_label(
*span,
format!(
@ -765,6 +766,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// | ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
/// | is returning data with lifetime `'b`
/// ```
#[allow(rustc::diagnostic_outside_of_impl)] // FIXME
fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> {
let ErrorConstraintInfo { fr, outlived_fr, span, category, .. } = errci;
@ -822,6 +824,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
/// LL | fn iter_values_anon(&self) -> impl Iterator<Item=u32> + 'a {
/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// ```
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable
fn add_static_impl_trait_suggestion(
&self,
diag: &mut Diag<'_>,
@ -885,7 +889,6 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
for alias_ty in alias_tys {
if alias_ty.span.desugaring_kind().is_some() {
// Skip `async` desugaring `impl Future`.
continue;
}
if let TyKind::TraitObject(_, lt) = alias_ty.kind {
if lt.kind == hir::LifetimeKind::ImplicitObjectLifetimeDefault {
@ -963,6 +966,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty);
}
#[allow(rustc::diagnostic_outside_of_impl)]
#[instrument(skip(self, err), level = "debug")]
fn suggest_constrain_dyn_trait_in_impl(
&self,
@ -980,18 +984,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
debug!("trait spans found: {:?}", traits);
for span in &traits {
let mut multi_span: MultiSpan = vec![*span].into();
multi_span.push_span_label(
*span,
msg!("this has an implicit `'static` lifetime requirement"),
);
multi_span.push_span_label(
ident.span,
msg!("calling this method introduces the `impl`'s `'static` requirement"),
);
multi_span.push_span_label(*span, fluent::borrowck_implicit_static);
multi_span.push_span_label(ident.span, fluent::borrowck_implicit_static_introduced);
err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
err.span_suggestion_verbose(
span.shrink_to_hi(),
msg!("consider relaxing the implicit `'static` requirement"),
fluent::borrowck_implicit_static_relax,
" + '_",
Applicability::MaybeIncorrect,
);
@ -1034,6 +1032,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
);
}
#[allow(rustc::diagnostic_outside_of_impl)]
/// When encountering a lifetime error caused by the return type of a closure, check the
/// corresponding trait bound and see if dereferencing the closure return value would satisfy
/// them. If so, we produce a structured suggestion.
@ -1154,13 +1153,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if ocx.evaluate_obligations_error_on_ambiguity().is_empty() && count > 0 {
diag.span_suggestion_verbose(
tcx.hir_body(*body).value.peel_blocks().span.shrink_to_lo(),
msg!("dereference the return value"),
fluent::borrowck_dereference_suggestion,
"*".repeat(count),
Applicability::MachineApplicable,
);
}
}
#[allow(rustc::diagnostic_outside_of_impl)]
fn suggest_move_on_borrowing_closure(&self, diag: &mut Diag<'_>) {
let body = self.infcx.tcx.hir_body_owned_by(self.mir_def_id());
let expr = &body.value.peel_blocks();
@ -1198,7 +1198,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
if let Some(closure_span) = closure_span {
diag.span_suggestion_verbose(
closure_span,
msg!("consider adding 'move' keyword before the nested closure"),
fluent::borrowck_move_closure_suggestion,
"move ",
Applicability::MaybeIncorrect,
);

View file

@ -1,3 +1,6 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use std::fmt::{self, Display};
use std::iter;

View file

@ -62,23 +62,57 @@ impl scc::Annotations<RegionVid> for SccAnnotations<'_, '_, RegionTracker> {
}
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
struct PlaceholderReachability {
/// The largest-universed placeholder we can reach
max_universe: (UniverseIndex, RegionVid),
enum PlaceholderReachability {
/// This SCC reaches no placeholders.
NoPlaceholders,
/// This SCC reaches at least one placeholder.
Placeholders {
/// The largest-universed placeholder we can reach
max_universe: (UniverseIndex, RegionVid),
/// The placeholder with the smallest ID
min_placeholder: RegionVid,
/// The placeholder with the smallest ID
min_placeholder: RegionVid,
/// The placeholder with the largest ID
max_placeholder: RegionVid,
/// The placeholder with the largest ID
max_placeholder: RegionVid,
},
}
impl PlaceholderReachability {
/// Merge the reachable placeholders of two graph components.
fn merge(&mut self, other: &Self) {
self.max_universe = self.max_universe.max(other.max_universe);
self.min_placeholder = self.min_placeholder.min(other.min_placeholder);
self.max_placeholder = self.max_placeholder.max(other.max_placeholder);
fn merge(self, other: PlaceholderReachability) -> PlaceholderReachability {
use PlaceholderReachability::*;
match (self, other) {
(NoPlaceholders, NoPlaceholders) => NoPlaceholders,
(NoPlaceholders, p @ Placeholders { .. })
| (p @ Placeholders { .. }, NoPlaceholders) => p,
(
Placeholders {
min_placeholder: min_pl,
max_placeholder: max_pl,
max_universe: max_u,
},
Placeholders { min_placeholder, max_placeholder, max_universe },
) => Placeholders {
min_placeholder: min_pl.min(min_placeholder),
max_placeholder: max_pl.max(max_placeholder),
max_universe: max_u.max(max_universe),
},
}
}
fn max_universe(&self) -> Option<(UniverseIndex, RegionVid)> {
match self {
Self::NoPlaceholders => None,
Self::Placeholders { max_universe, .. } => Some(*max_universe),
}
}
/// If we have reached placeholders, determine if they can
/// be named from this universe.
fn can_be_named_by(&self, from: UniverseIndex) -> bool {
self.max_universe()
.is_none_or(|(max_placeholder_universe, _)| from.can_name(max_placeholder_universe))
}
}
@ -86,7 +120,7 @@ impl PlaceholderReachability {
/// the values of its elements. This annotates a single SCC.
#[derive(Copy, Debug, Clone)]
pub(crate) struct RegionTracker {
reachable_placeholders: Option<PlaceholderReachability>,
reachable_placeholders: PlaceholderReachability,
/// The largest universe nameable from this SCC.
/// It is the smallest nameable universes of all
@ -101,13 +135,13 @@ impl RegionTracker {
pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
let reachable_placeholders =
if matches!(definition.origin, NllRegionVariableOrigin::Placeholder(_)) {
Some(PlaceholderReachability {
PlaceholderReachability::Placeholders {
max_universe: (definition.universe, rvid),
min_placeholder: rvid,
max_placeholder: rvid,
})
}
} else {
None
PlaceholderReachability::NoPlaceholders
};
Self {
@ -125,46 +159,43 @@ impl RegionTracker {
}
pub(crate) fn max_placeholder_universe_reached(self) -> UniverseIndex {
self.reachable_placeholders.map(|pls| pls.max_universe.0).unwrap_or(UniverseIndex::ROOT)
}
/// Can all reachable placeholders be named from `from`?
/// True vacuously in case no placeholders were reached.
fn placeholders_can_be_named_by(&self, from: UniverseIndex) -> bool {
self.reachable_placeholders.is_none_or(|pls| from.can_name(pls.max_universe.0))
if let Some((universe, _)) = self.reachable_placeholders.max_universe() {
universe
} else {
UniverseIndex::ROOT
}
}
/// Determine if we can name all the placeholders in `other`.
pub(crate) fn can_name_all_placeholders(&self, other: Self) -> bool {
// HACK: We first check whether we can name the highest existential universe
// of `other`. This only exists to avoid errors in case that scc already
// depends on a placeholder it cannot name itself.
self.max_nameable_universe().can_name(other.max_nameable_universe())
|| other.placeholders_can_be_named_by(self.max_nameable_universe.0)
other.reachable_placeholders.can_be_named_by(self.max_nameable_universe.0)
}
/// If this SCC reaches a placeholder it can't name, return it.
fn unnameable_placeholder(&self) -> Option<(UniverseIndex, RegionVid)> {
self.reachable_placeholders
.filter(|pls| !self.max_nameable_universe().can_name(pls.max_universe.0))
.map(|pls| pls.max_universe)
self.reachable_placeholders.max_universe().filter(|&(placeholder_universe, _)| {
!self.max_nameable_universe().can_name(placeholder_universe)
})
}
}
impl scc::Annotation for RegionTracker {
fn update_scc(&mut self, other: &Self) {
fn merge_scc(self, other: Self) -> Self {
trace!("{:?} << {:?}", self.representative, other.representative);
self.representative = self.representative.min(other.representative);
self.update_reachable(other);
Self {
representative: self.representative.min(other.representative),
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
}
}
fn update_reachable(&mut self, other: &Self) {
self.max_nameable_universe = self.max_nameable_universe.min(other.max_nameable_universe);
match (self.reachable_placeholders.as_mut(), other.reachable_placeholders.as_ref()) {
(None, None) | (Some(_), None) => (),
(None, Some(theirs)) => self.reachable_placeholders = Some(*theirs),
(Some(ours), Some(theirs)) => ours.merge(theirs),
};
fn merge_reached(self, other: Self) -> Self {
Self {
max_nameable_universe: self.max_nameable_universe.min(other.max_nameable_universe),
reachable_placeholders: self.reachable_placeholders.merge(other.reachable_placeholders),
representative: self.representative,
}
}
}

View file

@ -2,9 +2,8 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(assert_matches))]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(default_field_values)]
#![feature(file_buffered)]
#![feature(if_let_guard)]
#![feature(negative_impls)]
@ -63,10 +62,10 @@ use crate::diagnostics::{
use crate::path_utils::*;
use crate::place_ext::PlaceExt;
use crate::places_conflict::{PlaceConflictBias, places_conflict};
use crate::polonius::PoloniusContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
use crate::prefixes::PrefixSet;
use crate::region_infer::RegionInferenceContext;
use crate::region_infer::opaque_types::DeferredOpaqueTypeError;
@ -99,6 +98,8 @@ mod used_muts;
/// A public API provided for the Rust compiler consumers.
pub mod consumers;
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
/// Associate some local constants with the `'tcx` lifetime
struct TyCtxtConsts<'tcx>(PhantomData<&'tcx ()>);
@ -121,11 +122,6 @@ fn mir_borrowck(
let (input_body, _) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));
// We should eagerly check stalled coroutine obligations from HIR typeck.
// Not doing so leads to silent normalization failures later, which will
// fail to register opaque types in the next solver.
tcx.check_coroutine_obligations(def)?;
let input_body: &Body<'_> = &input_body.borrow();
if let Some(guar) = input_body.tainted_by_errors {
debug!("Skipping borrowck because of tainted body");
@ -424,7 +420,7 @@ fn borrowck_check_region_constraints<'tcx>(
polonius_output,
opt_closure_req,
nll_errors,
polonius_context,
polonius_diagnostics,
} = nll::compute_regions(
root_cx,
&infcx,
@ -448,7 +444,7 @@ fn borrowck_check_region_constraints<'tcx>(
&regioncx,
&opt_closure_req,
&borrow_set,
polonius_context.as_ref(),
polonius_diagnostics.as_ref(),
);
// We also have a `#[rustc_regions]` annotation that causes us to dump
@ -490,7 +486,7 @@ fn borrowck_check_region_constraints<'tcx>(
polonius_output: None,
move_errors: Vec::new(),
diags_buffer,
polonius_context: polonius_context.as_ref(),
polonius_diagnostics: polonius_diagnostics.as_ref(),
};
struct MoveVisitor<'a, 'b, 'infcx, 'tcx> {
ctxt: &'a mut MirBorrowckCtxt<'b, 'infcx, 'tcx>,
@ -529,7 +525,7 @@ fn borrowck_check_region_constraints<'tcx>(
move_errors: Vec::new(),
diags_buffer,
polonius_output: polonius_output.as_deref(),
polonius_context: polonius_context.as_ref(),
polonius_diagnostics: polonius_diagnostics.as_ref(),
};
// Compute and report region errors, if any.
@ -779,7 +775,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
/// Results of Polonius analysis.
polonius_output: Option<&'a PoloniusOutput>,
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics.
polonius_context: Option<&'a PoloniusContext>,
polonius_diagnostics: Option<&'a PoloniusDiagnosticsContext>,
}
// Check that:
@ -1210,17 +1206,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
"access_place: suppressing error place_span=`{:?}` kind=`{:?}`",
place_span, kind
);
// If the place is being mutated, then mark it as such anyway in order to suppress the
// `unused_mut` lint, which is likely incorrect once the access place error has been
// resolved.
if rw == ReadOrWrite::Write(WriteKind::Mutate)
&& let Ok(root_place) =
self.is_mutable(place_span.0.as_ref(), is_local_mutation_allowed)
{
self.add_used_mut(root_place, state);
}
return;
}
@ -1316,7 +1301,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
(Read(kind), BorrowKind::Mut { .. }) => {
// Reading from mere reservations of mutable-borrows is OK.
if !is_active(this.dominators(), borrow, location) {
assert!(borrow.kind.is_two_phase_borrow());
assert!(borrow.kind.allows_two_phase_borrow());
return ControlFlow::Continue(());
}
@ -1479,7 +1464,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
}
BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
if bk.is_two_phase_borrow() {
if bk.allows_two_phase_borrow() {
(Deep, Reservation(wk))
} else {
(Deep, Write(wk))
@ -1544,7 +1529,8 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
Rvalue::Use(operand)
| Rvalue::Repeat(operand, _)
| Rvalue::UnaryOp(_ /*un_op*/, operand)
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) => {
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
| Rvalue::ShallowInitBox(operand, _ /*ty*/) => {
self.consume_operand(location, (operand, span), state)
}

View file

@ -7,8 +7,6 @@ use std::str::FromStr;
use polonius_engine::{Algorithm, AllFacts, Output};
use rustc_data_structures::frozen::Frozen;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
use rustc_index::IndexSlice;
use rustc_middle::mir::pretty::PrettyPrintMirOptions;
use rustc_middle::mir::{Body, MirDumper, PassWhere, Promoted};
@ -17,16 +15,17 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::points::DenseLocationMap;
use rustc_session::config::MirIncludeSpans;
use rustc_span::sym;
use tracing::{debug, instrument};
use crate::borrow_set::BorrowSet;
use crate::consumers::RustcFacts;
use crate::diagnostics::RegionErrors;
use crate::handle_placeholders::compute_sccs_applying_placeholder_outlives_constraints;
use crate::polonius::PoloniusContext;
use crate::polonius::legacy::{
PoloniusFacts, PoloniusFactsExt, PoloniusLocationTable, PoloniusOutput,
};
use crate::polonius::{PoloniusContext, PoloniusDiagnosticsContext};
use crate::region_infer::RegionInferenceContext;
use crate::type_check::MirTypeckRegionConstraints;
use crate::type_check::free_region_relations::UniversalRegionRelations;
@ -47,7 +46,7 @@ pub(crate) struct NllOutput<'tcx> {
/// When using `-Zpolonius=next`: the data used to compute errors and diagnostics, e.g.
/// localized typeck and liveness constraints.
pub polonius_context: Option<PoloniusContext>,
pub polonius_diagnostics: Option<PoloniusDiagnosticsContext>,
}
/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal
@ -122,7 +121,7 @@ pub(crate) fn compute_regions<'tcx>(
universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
constraints: MirTypeckRegionConstraints<'tcx>,
mut polonius_facts: Option<AllFacts<RustcFacts>>,
mut polonius_context: Option<PoloniusContext>,
polonius_context: Option<PoloniusContext>,
) -> NllOutput<'tcx> {
let polonius_output = root_cx.consumer.as_ref().map_or(false, |c| c.polonius_output())
|| infcx.tcx.sess.opts.unstable_opts.polonius.is_legacy_enabled();
@ -154,9 +153,9 @@ pub(crate) fn compute_regions<'tcx>(
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives constraints
// and use them to compute loan liveness.
if let Some(polonius_context) = polonius_context.as_mut() {
polonius_context.compute_loan_liveness(&mut regioncx, body, borrow_set)
}
let polonius_diagnostics = polonius_context.map(|polonius_context| {
polonius_context.compute_loan_liveness(infcx.tcx, &mut regioncx, body, borrow_set)
});
// If requested: dump NLL facts, and run legacy polonius analysis.
let polonius_output = polonius_facts.as_ref().and_then(|polonius_facts| {
@ -189,7 +188,7 @@ pub(crate) fn compute_regions<'tcx>(
polonius_output,
opt_closure_req: closure_region_requirements,
nll_errors,
polonius_context,
polonius_diagnostics,
}
}
@ -231,13 +230,13 @@ pub(super) fn dump_nll_mir<'tcx>(
dumper.dump_mir(body);
// Also dump the region constraint graph as a graphviz file.
let _ = try {
let _: io::Result<()> = try {
let mut file = dumper.create_dump_file("regioncx.all.dot", body)?;
regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?;
};
// Also dump the region constraint SCC graph as a graphviz file.
let _ = try {
let _: io::Result<()> = try {
let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?;
regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?;
};
@ -288,6 +287,8 @@ pub(crate) fn emit_nll_mir<'tcx>(
Ok(())
}
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
pub(super) fn dump_annotation<'tcx, 'infcx>(
infcx: &'infcx BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
@ -296,7 +297,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
) {
let tcx = infcx.tcx;
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
if !find_attr!(tcx.get_all_attrs(base_def_id), AttributeKind::RustcRegions) {
if !tcx.has_attr(base_def_id, sym::rustc_regions) {
return;
}

View file

@ -1,19 +1,6 @@
use std::collections::BTreeMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_index::interval::SparseIntervalMatrix;
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::RegionVid;
use rustc_mir_dataflow::points::PointIndex;
use crate::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::dataflow::BorrowIndex;
use crate::polonius::ConstraintDirection;
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::universal_regions::UniversalRegions;
/// A localized outlives constraint reifies the CFG location where the outlives constraint holds,
/// within the origins themselves as if they were different from point to point: from `a: b`
/// outlives constraints to `a@p: b@p`, where `p` is the point in the CFG.
@ -25,300 +12,32 @@ use crate::universal_regions::UniversalRegions;
/// of `q`. These depend on the liveness of the regions at these points, as well as their
/// variance.
///
/// The `source` origin at `from` flows into the `target` origin at `to`.
///
/// This dual of NLL's [crate::constraints::OutlivesConstraint] therefore encodes the
/// position-dependent outlives constraints used by Polonius, to model the flow-sensitive loan
/// propagation via reachability within a graph of localized constraints.
///
/// That `LocalizedConstraintGraph` can create these edges on-demand during traversal, and we
/// therefore model them as a pair of `LocalizedNode` vertices.
///
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub(super) struct LocalizedNode {
pub region: RegionVid,
pub point: PointIndex,
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub(crate) struct LocalizedOutlivesConstraint {
pub source: RegionVid,
pub from: PointIndex,
pub target: RegionVid,
pub to: PointIndex,
}
/// The localized constraint graph indexes the physical and logical edges to lazily compute a given
/// node's successors during traversal.
pub(super) struct LocalizedConstraintGraph {
/// The actual, physical, edges we have recorded for a given node. We localize them on-demand
/// when traversing from the node to the successor region.
edges: FxHashMap<LocalizedNode, FxIndexSet<RegionVid>>,
/// The logical edges representing the outlives constraints that hold at all points in the CFG,
/// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
/// can be big, and we don't need to create such a physical edge for every point in the CFG.
logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
/// A container of [LocalizedOutlivesConstraint]s that can be turned into a traversable
/// `rustc_data_structures` graph.
#[derive(Clone, Default, Debug)]
pub(crate) struct LocalizedOutlivesConstraintSet {
pub outlives: Vec<LocalizedOutlivesConstraint>,
}
/// The visitor interface when traversing a `LocalizedConstraintGraph`.
pub(super) trait LocalizedConstraintGraphVisitor {
/// Callback called when traversing a given `loan` encounters a localized `node` it hasn't
/// visited before.
fn on_node_traversed(&mut self, _loan: BorrowIndex, _node: LocalizedNode) {}
/// Callback called when discovering a new `successor` node for the `current_node`.
fn on_successor_discovered(&mut self, _current_node: LocalizedNode, _successor: LocalizedNode) {
}
}
impl LocalizedConstraintGraph {
/// Traverses the constraints and returns the indexed graph of edges per node.
pub(super) fn new<'tcx>(
liveness: &LivenessValues,
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
) -> Self {
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
for outlives_constraint in outlives_constraints {
match outlives_constraint.locations {
Locations::All(_) => {
logical_edges
.entry(outlives_constraint.sup)
.or_default()
.insert(outlives_constraint.sub);
}
Locations::Single(location) => {
let node = LocalizedNode {
region: outlives_constraint.sup,
point: liveness.point_from_location(location),
};
edges.entry(node).or_default().insert(outlives_constraint.sub);
}
}
}
LocalizedConstraintGraph { edges, logical_edges }
}
/// Traverses the localized constraint graph per-loan, and notifies the `visitor` of discovered
/// nodes and successors.
pub(super) fn traverse<'tcx>(
&self,
body: &Body<'tcx>,
liveness: &LivenessValues,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &UniversalRegions<'tcx>,
borrow_set: &BorrowSet<'tcx>,
visitor: &mut impl LocalizedConstraintGraphVisitor,
) {
let live_regions = liveness.points();
let mut visited = FxHashSet::default();
let mut stack = Vec::new();
// Compute reachability per loan by traversing each loan's subgraph starting from where it
// is introduced.
for (loan_idx, loan) in borrow_set.iter_enumerated() {
visited.clear();
stack.clear();
let start_node = LocalizedNode {
region: loan.region,
point: liveness.point_from_location(loan.reserve_location),
};
stack.push(start_node);
while let Some(node) = stack.pop() {
if !visited.insert(node) {
continue;
}
// We've reached a node we haven't visited before.
let location = liveness.location_from_point(node.point);
visitor.on_node_traversed(loan_idx, node);
// When we find a _new_ successor, we'd like to
// - visit it eventually,
// - and let the generic visitor know about it.
let mut successor_found = |succ| {
if !visited.contains(&succ) {
stack.push(succ);
visitor.on_successor_discovered(node, succ);
}
};
// Then, we propagate the loan along the localized constraint graph. The outgoing
// edges are computed lazily, from:
// - the various physical edges present at this node,
// - the materialized logical edges that exist virtually at all points for this
// node's region, localized at this point.
// Universal regions propagate loans along the CFG, i.e. forwards only.
let is_universal_region = universal_regions.is_universal_region(node.region);
// The physical edges present at this node are:
//
// 1. the typeck edges that flow from region to region *at this point*.
for &succ in self.edges.get(&node).into_iter().flatten() {
let succ = LocalizedNode { region: succ, point: node.point };
successor_found(succ);
}
// 2a. the liveness edges that flow *forward*, from this node's point to its
// successors in the CFG.
if body[location.block].statements.get(location.statement_index).is_some() {
// Intra-block edges, straight line constraints from each point to its successor
// within the same block.
let next_point = node.point + 1;
if let Some(succ) = compute_forward_successor(
node.region,
next_point,
live_regions,
live_region_variances,
is_universal_region,
) {
successor_found(succ);
}
} else {
// Inter-block edges, from the block's terminator to each successor block's
// entry point.
for successor_block in body[location.block].terminator().successors() {
let next_location = Location { block: successor_block, statement_index: 0 };
let next_point = liveness.point_from_location(next_location);
if let Some(succ) = compute_forward_successor(
node.region,
next_point,
live_regions,
live_region_variances,
is_universal_region,
) {
successor_found(succ);
}
}
}
// 2b. the liveness edges that flow *backward*, from this node's point to its
// predecessors in the CFG.
if !is_universal_region {
if location.statement_index > 0 {
// Backward edges to the predecessor point in the same block.
let previous_point = PointIndex::from(node.point.as_usize() - 1);
if let Some(succ) = compute_backward_successor(
node.region,
node.point,
previous_point,
live_regions,
live_region_variances,
) {
successor_found(succ);
}
} else {
// Backward edges from the block entry point to the terminator of the
// predecessor blocks.
let predecessors = body.basic_blocks.predecessors();
for &pred_block in &predecessors[location.block] {
let previous_location = Location {
block: pred_block,
statement_index: body[pred_block].statements.len(),
};
let previous_point = liveness.point_from_location(previous_location);
if let Some(succ) = compute_backward_successor(
node.region,
node.point,
previous_point,
live_regions,
live_region_variances,
) {
successor_found(succ);
}
}
}
}
// And finally, we have the logical edges, materialized at this point.
for &logical_succ in self.logical_edges.get(&node.region).into_iter().flatten() {
let succ = LocalizedNode { region: logical_succ, point: node.point };
successor_found(succ);
}
}
}
}
}
/// Returns the successor for the current region/point node when propagating a loan through forward
/// edges, if applicable, according to liveness and variance.
fn compute_forward_successor(
region: RegionVid,
next_point: PointIndex,
live_regions: &SparseIntervalMatrix<RegionVid, PointIndex>,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
is_universal_region: bool,
) -> Option<LocalizedNode> {
// 1. Universal regions are semantically live at all points.
if is_universal_region {
let succ = LocalizedNode { region, point: next_point };
return Some(succ);
}
// 2. Otherwise, gather the edges due to explicit region liveness, when applicable.
if !live_regions.contains(region, next_point) {
return None;
}
// Here, `region` could be live at the current point, and is live at the next point: add a
// constraint between them, according to variance.
// Note: there currently are cases related to promoted and const generics, where we don't yet
// have variance information (possibly about temporary regions created when typeck sanitizes the
// promoteds). Until that is done, we conservatively fallback to maximizing reachability by
// adding a bidirectional edge here. This will not limit traversal whatsoever, and thus
// propagate liveness when needed.
//
// FIXME: add the missing variance information and remove this fallback bidirectional edge.
let direction =
live_region_variances.get(&region).unwrap_or(&ConstraintDirection::Bidirectional);
match direction {
ConstraintDirection::Backward => {
// Contravariant cases: loans flow in the inverse direction, but we're only interested
// in forward successors and there are none here.
None
}
ConstraintDirection::Forward | ConstraintDirection::Bidirectional => {
// 1. For covariant cases: loans flow in the regular direction, from the current point
// to the next point.
// 2. For invariant cases, loans can flow in both directions, but here as well, we only
// want the forward path of the bidirectional edge.
Some(LocalizedNode { region, point: next_point })
}
}
}
/// Returns the successor for the current region/point node when propagating a loan through backward
/// edges, if applicable, according to liveness and variance.
fn compute_backward_successor(
region: RegionVid,
current_point: PointIndex,
previous_point: PointIndex,
live_regions: &SparseIntervalMatrix<RegionVid, PointIndex>,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
) -> Option<LocalizedNode> {
// Liveness flows into the regions live at the next point. So, in a backwards view, we'll link
// the region from the current point, if it's live there, to the previous point.
if !live_regions.contains(region, current_point) {
return None;
}
// FIXME: add the missing variance information and remove this fallback bidirectional edge. See
// the same comment in `compute_forward_successor`.
let direction =
live_region_variances.get(&region).unwrap_or(&ConstraintDirection::Bidirectional);
match direction {
ConstraintDirection::Forward => {
// Covariant cases: loans flow in the regular direction, but we're only interested in
// backward successors and there are none here.
None
}
ConstraintDirection::Backward | ConstraintDirection::Bidirectional => {
// 1. For contravariant cases: loans flow in the inverse direction, from the current
// point to the previous point.
// 2. For invariant cases, loans can flow in both directions, but here as well, we only
// want the backward path of the bidirectional edge.
Some(LocalizedNode { region, point: previous_point })
}
impl LocalizedOutlivesConstraintSet {
pub(crate) fn push(&mut self, constraint: LocalizedOutlivesConstraint) {
if constraint.source == constraint.target && constraint.from == constraint.to {
// 'a@p: 'a@p is pretty uninteresting
return;
}
self.outlives.push(constraint);
}
}

View file

@ -10,7 +10,9 @@ use rustc_session::config::MirIncludeSpans;
use crate::borrow_set::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::polonius::{LocalizedConstraintGraphVisitor, LocalizedNode, PoloniusContext};
use crate::polonius::{
LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet, PoloniusDiagnosticsContext,
};
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::{BorrowckInferCtxt, ClosureRegionRequirements, RegionInferenceContext};
@ -22,7 +24,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
polonius_context: Option<&PoloniusContext>,
polonius_diagnostics: Option<&PoloniusDiagnosticsContext>,
) {
let tcx = infcx.tcx;
if !tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
@ -31,22 +33,8 @@ pub(crate) fn dump_polonius_mir<'tcx>(
let Some(dumper) = MirDumper::new(tcx, "polonius", body) else { return };
let polonius_context =
polonius_context.expect("missing polonius context with `-Zpolonius=next`");
// If we have a polonius graph to dump along the rest of the MIR and NLL info, we extract its
// constraints here.
let mut collector = LocalizedOutlivesConstraintCollector { constraints: Vec::new() };
if let Some(graph) = &polonius_context.graph {
graph.traverse(
body,
regioncx.liveness_constraints(),
&polonius_context.live_region_variances,
regioncx.universal_regions(),
borrow_set,
&mut collector,
);
}
let polonius_diagnostics =
polonius_diagnostics.expect("missing diagnostics context with `-Zpolonius=next`");
let extra_data = &|pass_where, out: &mut dyn io::Write| {
emit_polonius_mir(
@ -54,7 +42,7 @@ pub(crate) fn dump_polonius_mir<'tcx>(
regioncx,
closure_region_requirements,
borrow_set,
&collector.constraints,
&polonius_diagnostics.localized_outlives_constraints,
pass_where,
out,
)
@ -70,36 +58,19 @@ pub(crate) fn dump_polonius_mir<'tcx>(
let dumper = dumper.set_extra_data(extra_data).set_options(options);
let _ = try {
let _: io::Result<()> = try {
let mut file = dumper.create_dump_file("html", body)?;
emit_polonius_dump(&dumper, body, regioncx, borrow_set, &collector.constraints, &mut file)?;
emit_polonius_dump(
&dumper,
body,
regioncx,
borrow_set,
&polonius_diagnostics.localized_outlives_constraints,
&mut file,
)?;
};
}
/// The constraints we'll dump as text or a mermaid graph.
struct LocalizedOutlivesConstraint {
source: RegionVid,
from: PointIndex,
target: RegionVid,
to: PointIndex,
}
/// Visitor to record constraints encountered when traversing the localized constraint graph.
struct LocalizedOutlivesConstraintCollector {
constraints: Vec<LocalizedOutlivesConstraint>,
}
impl LocalizedConstraintGraphVisitor for LocalizedOutlivesConstraintCollector {
fn on_successor_discovered(&mut self, current_node: LocalizedNode, successor: LocalizedNode) {
self.constraints.push(LocalizedOutlivesConstraint {
source: current_node.region,
from: current_node.point,
target: successor.region,
to: successor.point,
});
}
}
/// The polonius dump consists of:
/// - the NLL MIR
/// - the list of polonius localized constraints
@ -111,7 +82,7 @@ fn emit_polonius_dump<'tcx>(
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &[LocalizedOutlivesConstraint],
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
out: &mut dyn io::Write,
) -> io::Result<()> {
// Prepare the HTML dump file prologue.
@ -222,7 +193,7 @@ fn emit_polonius_mir<'tcx>(
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &[LocalizedOutlivesConstraint],
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
pass_where: PassWhere,
out: &mut dyn io::Write,
) -> io::Result<()> {
@ -241,10 +212,10 @@ fn emit_polonius_mir<'tcx>(
// Add localized outlives constraints
match pass_where {
PassWhere::BeforeCFG => {
if localized_outlives_constraints.len() > 0 {
if localized_outlives_constraints.outlives.len() > 0 {
writeln!(out, "| Localized constraints")?;
for constraint in localized_outlives_constraints {
for constraint in &localized_outlives_constraints.outlives {
let LocalizedOutlivesConstraint { source, from, target, to } = constraint;
let from = liveness.location_from_point(*from);
let to = liveness.location_from_point(*to);
@ -428,7 +399,7 @@ fn emit_mermaid_nll_sccs<'tcx>(
fn emit_mermaid_constraint_graph<'tcx>(
borrow_set: &BorrowSet<'tcx>,
liveness: &LivenessValues,
localized_outlives_constraints: &[LocalizedOutlivesConstraint],
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
out: &mut dyn io::Write,
) -> io::Result<usize> {
let location_name = |location: Location| {
@ -467,7 +438,7 @@ fn emit_mermaid_constraint_graph<'tcx>(
// The regions subgraphs containing the region/point nodes.
let mut points_per_region: FxIndexMap<RegionVid, FxIndexSet<PointIndex>> =
FxIndexMap::default();
for constraint in localized_outlives_constraints {
for constraint in &localized_outlives_constraints.outlives {
points_per_region.entry(constraint.source).or_default().insert(constraint.from);
points_per_region.entry(constraint.target).or_default().insert(constraint.to);
}
@ -480,7 +451,7 @@ fn emit_mermaid_constraint_graph<'tcx>(
}
// The constraint graph edges.
for constraint in localized_outlives_constraints {
for constraint in &localized_outlives_constraints.outlives {
// FIXME: add killed loans and constraint kind as edge labels.
writeln!(
out,
@ -492,6 +463,6 @@ fn emit_mermaid_constraint_graph<'tcx>(
// Return the number of edges: this is the biggest graph in the dump and its edge count will be
// mermaid's max edge count to support.
let edge_count = borrow_set.len() + localized_outlives_constraints.len();
let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len();
Ok(edge_count)
}

View file

@ -264,7 +264,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
}
BorrowKind::Mut { .. } => {
let wk = WriteKind::MutableBorrow(bk);
if bk.is_two_phase_borrow() {
if bk.allows_two_phase_borrow() {
(Deep, Reservation(wk))
} else {
(Deep, Write(wk))
@ -297,9 +297,8 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
Rvalue::Use(operand)
| Rvalue::Repeat(operand, _)
| Rvalue::UnaryOp(_ /*un_op*/, operand)
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/) => {
self.consume_operand(location, operand)
}
| Rvalue::Cast(_ /*cast_kind*/, operand, _ /*ty*/)
| Rvalue::ShallowInitBox(operand, _ /*ty*/) => self.consume_operand(location, operand),
&Rvalue::Discriminant(place) => {
self.access_place(
@ -385,7 +384,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
// Reading from mere reservations of mutable-borrows is OK.
if !is_active(this.dominators, borrow, location) {
// If the borrow isn't active yet, reads don't invalidate it
assert!(borrow.kind.is_two_phase_borrow());
assert!(borrow.kind.allows_two_phase_borrow());
return ControlFlow::Continue(());
}

View file

@ -1,15 +1,22 @@
use std::collections::BTreeMap;
use rustc_hir::def_id::DefId;
use rustc_index::bit_set::SparseBitMatrix;
use rustc_middle::mir::{Body, Location};
use rustc_middle::ty::relate::{
self, Relate, RelateResult, TypeRelation, relate_args_with_variances,
};
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
use rustc_mir_dataflow::points::PointIndex;
use super::{ConstraintDirection, PoloniusContext};
use super::{
ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
PoloniusLivenessContext,
};
use crate::region_infer::values::LivenessValues;
use crate::universal_regions::UniversalRegions;
impl PoloniusContext {
impl PoloniusLivenessContext {
/// Record the variance of each region contained within the given value.
pub(crate) fn record_live_region_variance<'tcx>(
&mut self,
@ -27,6 +34,165 @@ impl PoloniusContext {
}
}
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
/// constraints for loans that are propagated to the next statements.
pub(super) fn create_liveness_constraints<'tcx>(
body: &Body<'tcx>,
liveness: &LivenessValues,
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &UniversalRegions<'tcx>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
for (block, bb) in body.basic_blocks.iter_enumerated() {
let statement_count = bb.statements.len();
for statement_index in 0..=statement_count {
let current_location = Location { block, statement_index };
let current_point = liveness.point_from_location(current_location);
if statement_index < statement_count {
// Intra-block edges, straight line constraints from each point to its successor
// within the same block.
let next_location = Location { block, statement_index: statement_index + 1 };
let next_point = liveness.point_from_location(next_location);
propagate_loans_between_points(
current_point,
next_point,
live_regions,
live_region_variances,
universal_regions,
localized_outlives_constraints,
);
} else {
// Inter-block edges, from the block's terminator to each successor block's entry
// point.
for successor_block in bb.terminator().successors() {
let next_location = Location { block: successor_block, statement_index: 0 };
let next_point = liveness.point_from_location(next_location);
propagate_loans_between_points(
current_point,
next_point,
live_regions,
live_region_variances,
universal_regions,
localized_outlives_constraints,
);
}
}
}
}
}
/// Propagate loans within a region between two points in the CFG, if that region is live at both
/// the source and target points.
fn propagate_loans_between_points(
current_point: PointIndex,
next_point: PointIndex,
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
universal_regions: &UniversalRegions<'_>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
// Universal regions are semantically live at all points.
// Note: we always have universal regions but they're not always (or often) involved in the
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
// will be disconnected from the rest of the graph and thus, unnecessary.
//
// FIXME: only emit the edges of universal regions that existential regions can reach.
for region in universal_regions.universal_regions_iter() {
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
}
let Some(next_live_regions) = live_regions.row(next_point) else {
// There are no constraints to add: there are no live regions at the next point.
return;
};
for region in next_live_regions.iter() {
// `region` could be live at the current point, and is live at the next point: add a
// constraint between them, according to variance.
if let Some(&direction) = live_region_variances.get(&region) {
add_liveness_constraint(
region,
current_point,
next_point,
direction,
localized_outlives_constraints,
);
} else {
// Note: there currently are cases related to promoted and const generics, where we
// don't yet have variance information (possibly about temporary regions created when
// typeck sanitizes the promoteds). Until that is done, we conservatively fallback to
// maximizing reachability by adding a bidirectional edge here. This will not limit
// traversal whatsoever, and thus propagate liveness when needed.
//
// FIXME: add the missing variance information and remove this fallback bidirectional
// edge.
let fallback = ConstraintDirection::Bidirectional;
add_liveness_constraint(
region,
current_point,
next_point,
fallback,
localized_outlives_constraints,
);
}
}
}
/// Adds `LocalizedOutlivesConstraint`s between two connected points, according to the given edge
/// direction.
fn add_liveness_constraint(
region: RegionVid,
current_point: PointIndex,
next_point: PointIndex,
direction: ConstraintDirection,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
match direction {
ConstraintDirection::Forward => {
// Covariant cases: loans flow in the regular direction, from the current point to the
// next point.
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
}
ConstraintDirection::Backward => {
// Contravariant cases: loans flow in the inverse direction, from the next point to the
// current point.
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: next_point,
target: region,
to: current_point,
});
}
ConstraintDirection::Bidirectional => {
// For invariant cases, loans can flow in both directions: we add both edges.
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: current_point,
target: region,
to: next_point,
});
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
source: region,
from: next_point,
target: region,
to: current_point,
});
}
}
}
/// Extracts variances for regions contained within types. Follows the same structure as
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
/// variances of regions.

View file

@ -0,0 +1,160 @@
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
use rustc_middle::ty::RegionVid;
use rustc_mir_dataflow::points::PointIndex;
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
use crate::BorrowSet;
use crate::constraints::OutlivesConstraint;
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
/// Compute loan reachability to approximately trace loan liveness throughout the CFG, by
/// traversing the full graph of constraints that combines:
/// - the localized constraints (the physical edges),
/// - with the constraints that hold at all points (the logical edges).
pub(super) fn compute_loan_liveness<'tcx>(
liveness: &LivenessValues,
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
) -> LiveLoans {
let mut live_loans = LiveLoans::new(borrow_set.len());
// Create the full graph with the physical edges we've localized earlier, and the logical edges
// of constraints that hold at all points.
let logical_constraints =
outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_)));
let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints);
let mut visited = FxHashSet::default();
let mut stack = Vec::new();
// Compute reachability per loan by traversing each loan's subgraph starting from where it is
// introduced.
for (loan_idx, loan) in borrow_set.iter_enumerated() {
visited.clear();
stack.clear();
let start_node = LocalizedNode {
region: loan.region,
point: liveness.point_from_location(loan.reserve_location),
};
stack.push(start_node);
while let Some(node) = stack.pop() {
if !visited.insert(node) {
continue;
}
// Record the loan as being live on entry to this point if it reaches a live region
// there.
//
// This is an approximation of liveness (which is the thing we want), in that we're
// using a single notion of reachability to represent what used to be _two_ different
// transitive closures. It didn't seem impactful when coming up with the single-graph
// and reachability through space (regions) + time (CFG) concepts, but in practice the
// combination of time-traveling with kills is more impactful than initially
// anticipated.
//
// Kills should prevent a loan from reaching its successor points in the CFG, but not
// while time-traveling: we're not actually at that CFG point, but looking for
// predecessor regions that contain the loan. One of the two TCs we had pushed the
// transitive subset edges to each point instead of having backward edges, and the
// problem didn't exist before. In the abstract, naive reachability is not enough to
// model this, we'd need a slightly different solution. For example, maybe with a
// two-step traversal:
// - at each point we first traverse the subgraph (and possibly time-travel) looking for
// exit nodes while ignoring kills,
// - and then when we're back at the current point, we continue normally.
//
// Another (less annoying) subtlety is that kills and the loan use-map are
// flow-insensitive. Kills can actually appear in places before a loan is introduced, or
// at a location that is actually unreachable in the CFG from the introduction point,
// and these can also be encountered during time-traveling.
//
// The simplest change that made sense to "fix" the issues above is taking into
// account kills that are:
// - reachable from the introduction point
// - encountered during forward traversal. Note that this is not transitive like the
// two-step traversal described above: only kills encountered on exit via a backward
// edge are ignored.
//
// This version of the analysis, however, is enough in practice to pass the tests that
// we care about and NLLs reject, without regressions on crater, and is an actionable
// subset of the full analysis. It also naturally points to areas of improvement that we
// wish to explore later, namely handling kills appropriately during traversal, instead
// of continuing traversal to all the reachable nodes.
//
// FIXME: analyze potential unsoundness, possibly in concert with a borrowck
// implementation in a-mir-formality, fuzzing, or manually crafting counter-examples.
if liveness.is_live_at(node.region, liveness.location_from_point(node.point)) {
live_loans.insert(node.point, loan_idx);
}
for succ in graph.outgoing_edges(node) {
stack.push(succ);
}
}
}
live_loans
}
/// The localized constraint graph indexes the physical and logical edges to compute a given node's
/// successors during traversal.
struct LocalizedConstraintGraph {
/// The actual, physical, edges we have recorded for a given node.
edges: FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>,
/// The logical edges representing the outlives constraints that hold at all points in the CFG,
/// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
/// can be big, and we don't need to create such a physical edge for every point in the CFG.
logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
}
/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
struct LocalizedNode {
region: RegionVid,
point: PointIndex,
}
impl LocalizedConstraintGraph {
/// Traverses the constraints and returns the indexed graph of edges per node.
fn new<'tcx>(
constraints: &LocalizedOutlivesConstraintSet,
logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
) -> Self {
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
for constraint in &constraints.outlives {
let source = LocalizedNode { region: constraint.source, point: constraint.from };
let target = LocalizedNode { region: constraint.target, point: constraint.to };
edges.entry(source).or_default().insert(target);
}
let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
for constraint in logical_constraints {
logical_edges.entry(constraint.sup).or_default().insert(constraint.sub);
}
LocalizedConstraintGraph { edges, logical_edges }
}
/// Returns the outgoing edges of a given node, not its transitive closure.
fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> {
// The outgoing edges are:
// - the physical edges present at this node,
// - the materialized logical edges that exist virtually at all points for this node's
// region, localized at this point.
let physical_edges =
self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied());
let materialized_edges =
self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| {
targets
.iter()
.copied()
.map(move |target| LocalizedNode { point: node.point, region: target })
});
physical_edges.chain(materialized_edges)
}
}

View file

@ -32,37 +32,47 @@
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/22/polonius-part-1/>
//! - <https://smallcultfollowing.com/babysteps/blog/2023/09/29/polonius-part-2/>
//!
//!
//! Data flows like this:
//! 1) during MIR typeck, record liveness data needed later: live region variances, as well as the
//! usual NLL liveness data (just computed on more locals). That's the [PoloniusLivenessContext].
//! 2) once that is done, variance data is transferred, and the NLL region liveness is converted to
//! the polonius shape. That's the main [PoloniusContext].
//! 3) during region inference, that data and the NLL outlives constraints are used to create the
//! localized outlives constraints, as described above. That's the [PoloniusDiagnosticsContext].
//! 4) transfer this back to the main borrowck procedure: it handles computing errors and
//! diagnostics, debugging and MIR dumping concerns.
mod constraints;
mod dump;
pub(crate) mod legacy;
mod liveness_constraints;
mod loan_liveness;
mod typeck_constraints;
use std::collections::BTreeMap;
use rustc_data_structures::fx::FxHashSet;
use rustc_index::bit_set::SparseBitMatrix;
use rustc_index::interval::SparseIntervalMatrix;
use rustc_middle::mir::{Body, Local};
use rustc_middle::ty::RegionVid;
use rustc_middle::ty::{RegionVid, TyCtxt};
use rustc_mir_dataflow::points::PointIndex;
pub(self) use self::constraints::*;
pub(crate) use self::constraints::*;
pub(crate) use self::dump::dump_polonius_mir;
use self::liveness_constraints::create_liveness_constraints;
use self::loan_liveness::compute_loan_liveness;
use self::typeck_constraints::convert_typeck_constraints;
use crate::dataflow::BorrowIndex;
use crate::region_infer::values::LivenessValues;
use crate::{BorrowSet, RegionInferenceContext};
pub(crate) type LiveLoans = SparseBitMatrix<PointIndex, BorrowIndex>;
/// This struct holds the necessary
/// - liveness data, created during MIR typeck, and which will be used to lazily compute the
/// polonius localized constraints, during NLL region inference as well as MIR dumping,
/// - data needed by the borrowck error computation and diagnostics.
/// This struct holds the liveness data created during MIR typeck, and which will be used later in
/// the process, to compute the polonius localized constraints.
#[derive(Default)]
pub(crate) struct PoloniusContext {
/// The graph from which we extract the localized outlives constraints.
graph: Option<LocalizedConstraintGraph>,
pub(crate) struct PoloniusLivenessContext {
/// The expected edge direction per live region: the kind of directed edge we'll create as
/// liveness constraints depends on the variance of types with respect to each contained region.
live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
@ -74,6 +84,27 @@ pub(crate) struct PoloniusContext {
pub(crate) boring_nll_locals: FxHashSet<Local>,
}
/// This struct holds the data needed to create the Polonius localized constraints. Its data is
/// transferred and converted from the [PoloniusLivenessContext] at the end of MIR typeck.
pub(crate) struct PoloniusContext {
/// The liveness data we recorded during MIR typeck.
liveness_context: PoloniusLivenessContext,
/// The set of regions that are live at a given point in the CFG, used to create localized
/// outlives constraints between regions that are live at connected points in the CFG.
live_regions: SparseBitMatrix<PointIndex, RegionVid>,
}
/// This struct holds the data needed by the borrowck error computation and diagnostics. Its data is
/// computed from the [PoloniusContext] when computing NLL regions.
pub(crate) struct PoloniusDiagnosticsContext {
/// The localized outlives constraints that were computed in the main analysis.
localized_outlives_constraints: LocalizedOutlivesConstraintSet,
/// The liveness data computed during MIR typeck: [PoloniusLivenessContext::boring_nll_locals].
pub(crate) boring_nll_locals: FxHashSet<Local>,
}
/// The direction a constraint can flow into. Used to create liveness constraints according to
/// variance.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@ -89,6 +120,26 @@ enum ConstraintDirection {
}
impl PoloniusContext {
/// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
/// need to transpose the "points where each region is live" matrix to a "live regions per point"
/// matrix.
// FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
// borrowck.
pub(crate) fn create_from_liveness(
liveness_context: PoloniusLivenessContext,
num_regions: usize,
points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
) -> PoloniusContext {
let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
for region in points_per_live_region.rows() {
for point in points_per_live_region.row(region).unwrap().iter() {
live_regions_per_point.insert(point, region);
}
}
PoloniusContext { live_regions: live_regions_per_point, liveness_context }
}
/// Computes live loans using the set of loans model for `-Zpolonius=next`.
///
/// First, creates a constraint graph combining regions and CFG points, by:
@ -100,90 +151,44 @@ impl PoloniusContext {
///
/// The constraint data will be used to compute errors and diagnostics.
pub(crate) fn compute_loan_liveness<'tcx>(
&mut self,
self,
tcx: TyCtxt<'tcx>,
regioncx: &mut RegionInferenceContext<'tcx>,
body: &Body<'tcx>,
borrow_set: &BorrowSet<'tcx>,
) {
let liveness = regioncx.liveness_constraints();
) -> PoloniusDiagnosticsContext {
let PoloniusLivenessContext { live_region_variances, boring_nll_locals } =
self.liveness_context;
// We don't need to prepare the graph (index NLL constraints, etc.) if we have no loans to
// trace throughout localized constraints.
if borrow_set.len() > 0 {
// From the outlives constraints, liveness, and variances, we can compute reachability
// on the lazy localized constraint graph to trace the liveness of loans, for the next
// step in the chain (the NLL loan scope and active loans computations).
let graph = LocalizedConstraintGraph::new(liveness, regioncx.outlives_constraints());
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
convert_typeck_constraints(
tcx,
body,
regioncx.liveness_constraints(),
regioncx.outlives_constraints(),
regioncx.universal_regions(),
&mut localized_outlives_constraints,
);
let mut live_loans = LiveLoans::new(borrow_set.len());
let mut visitor = LoanLivenessVisitor { liveness, live_loans: &mut live_loans };
graph.traverse(
body,
liveness,
&self.live_region_variances,
regioncx.universal_regions(),
borrow_set,
&mut visitor,
);
regioncx.record_live_loans(live_loans);
create_liveness_constraints(
body,
regioncx.liveness_constraints(),
&self.live_regions,
&live_region_variances,
regioncx.universal_regions(),
&mut localized_outlives_constraints,
);
// The graph can be traversed again during MIR dumping, so we store it here.
self.graph = Some(graph);
}
}
}
/// Visitor to record loan liveness when traversing the localized constraint graph.
struct LoanLivenessVisitor<'a> {
liveness: &'a LivenessValues,
live_loans: &'a mut LiveLoans,
}
impl LocalizedConstraintGraphVisitor for LoanLivenessVisitor<'_> {
fn on_node_traversed(&mut self, loan: BorrowIndex, node: LocalizedNode) {
// Record the loan as being live on entry to this point if it reaches a live region
// there.
//
// This is an approximation of liveness (which is the thing we want), in that we're
// using a single notion of reachability to represent what used to be _two_ different
// transitive closures. It didn't seem impactful when coming up with the single-graph
// and reachability through space (regions) + time (CFG) concepts, but in practice the
// combination of time-traveling with kills is more impactful than initially
// anticipated.
//
// Kills should prevent a loan from reaching its successor points in the CFG, but not
// while time-traveling: we're not actually at that CFG point, but looking for
// predecessor regions that contain the loan. One of the two TCs we had pushed the
// transitive subset edges to each point instead of having backward edges, and the
// problem didn't exist before. In the abstract, naive reachability is not enough to
// model this, we'd need a slightly different solution. For example, maybe with a
// two-step traversal:
// - at each point we first traverse the subgraph (and possibly time-travel) looking for
// exit nodes while ignoring kills,
// - and then when we're back at the current point, we continue normally.
//
// Another (less annoying) subtlety is that kills and the loan use-map are
// flow-insensitive. Kills can actually appear in places before a loan is introduced, or
// at a location that is actually unreachable in the CFG from the introduction point,
// and these can also be encountered during time-traveling.
//
// The simplest change that made sense to "fix" the issues above is taking into account
// kills that are:
// - reachable from the introduction point
// - encountered during forward traversal. Note that this is not transitive like the
// two-step traversal described above: only kills encountered on exit via a backward
// edge are ignored.
//
// This version of the analysis, however, is enough in practice to pass the tests that
// we care about and NLLs reject, without regressions on crater, and is an actionable
// subset of the full analysis. It also naturally points to areas of improvement that we
// wish to explore later, namely handling kills appropriately during traversal, instead
// of continuing traversal to all the reachable nodes.
//
// FIXME: analyze potential unsoundness, possibly in concert with a borrowck
// implementation in a-mir-formality, fuzzing, or manually crafting counter-examples.
if self.liveness.is_live_at_point(node.region, node.point) {
self.live_loans.insert(node.point, loan);
}
// Now that we have a complete graph, we can compute reachability to trace the liveness of
// loans for the next step in the chain, the NLL loan scope and active loans computations.
let live_loans = compute_loan_liveness(
regioncx.liveness_constraints(),
regioncx.outlives_constraints(),
borrow_set,
&localized_outlives_constraints,
);
regioncx.record_live_loans(live_loans);
PoloniusDiagnosticsContext { localized_outlives_constraints, boring_nll_locals }
}
}

View file

@ -0,0 +1,218 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir::{Body, Location, Statement, StatementKind, Terminator, TerminatorKind};
use rustc_middle::ty::{TyCtxt, TypeVisitable};
use rustc_mir_dataflow::points::PointIndex;
use super::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet};
use crate::constraints::OutlivesConstraint;
use crate::region_infer::values::LivenessValues;
use crate::type_check::Locations;
use crate::universal_regions::UniversalRegions;
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
/// location where effects start to be visible).
pub(super) fn convert_typeck_constraints<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
liveness: &LivenessValues,
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
universal_regions: &UniversalRegions<'tcx>,
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
) {
for outlives_constraint in outlives_constraints {
match outlives_constraint.locations {
Locations::All(_) => {
// We don't turn constraints holding at all points into physical edges at every
// point in the graph. They are encoded into *traversal* instead: a given node's
// successors will combine these logical edges with the regular, physical, localized
// edges.
continue;
}
Locations::Single(location) => {
// This constraint is marked as holding at one location, we localize it to that
// location or its successor, depending on the corresponding MIR
// statement/terminator. Unfortunately, they all show up from typeck as coming "on
// entry", so for now we modify them to take effects that should apply "on exit"
// into account.
//
// FIXME: this approach is subtle, complicated, and hard to test, so we should track
// this information better in MIR typeck instead, for example with a new `Locations`
// variant that contains which node is crossing over between entry and exit.
let point = liveness.point_from_location(location);
let localized_constraint = if let Some(stmt) =
body[location.block].statements.get(location.statement_index)
{
localize_statement_constraint(
tcx,
body,
stmt,
&outlives_constraint,
point,
universal_regions,
)
} else {
assert_eq!(location.statement_index, body[location.block].statements.len());
let terminator = body[location.block].terminator();
localize_terminator_constraint(
tcx,
body,
terminator,
liveness,
&outlives_constraint,
point,
universal_regions,
)
};
localized_outlives_constraints.push(localized_constraint);
}
}
}
}
/// For a given outlives constraint arising from a MIR statement, localize the constraint with the
/// needed CFG `from`-`to` intra-block nodes.
fn localize_statement_constraint<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
stmt: &Statement<'tcx>,
outlives_constraint: &OutlivesConstraint<'tcx>,
current_point: PointIndex,
universal_regions: &UniversalRegions<'tcx>,
) -> LocalizedOutlivesConstraint {
match &stmt.kind {
StatementKind::Assign(box (lhs, rhs)) => {
// To create localized outlives constraints without midpoints, we rely on the property
// that no input regions from the RHS of the assignment will flow into themselves: they
// should not appear in the output regions in the LHS. We believe this to be true by
// construction of the MIR, via temporaries, and assert it here.
//
// We think we don't need midpoints because:
// - every LHS Place has a unique set of regions that don't appear elsewhere
// - this implies that for them to be part of the RHS, the same Place must be read and
// written
// - and that should be impossible in MIR
//
// When we have a more complete implementation in the future, tested with crater, etc,
// we can remove this assertion. It's a debug assert because it can be expensive.
debug_assert!(
{
let mut lhs_regions = FxHashSet::default();
tcx.for_each_free_region(lhs, |region| {
let region = universal_regions.to_region_vid(region);
lhs_regions.insert(region);
});
let mut rhs_regions = FxHashSet::default();
tcx.for_each_free_region(rhs, |region| {
let region = universal_regions.to_region_vid(region);
rhs_regions.insert(region);
});
// The intersection between LHS and RHS regions should be empty.
lhs_regions.is_disjoint(&rhs_regions)
},
"there should be no common regions between the LHS and RHS of an assignment"
);
let lhs_ty = body.local_decls[lhs.local].ty;
let successor_point = current_point;
compute_constraint_direction(
tcx,
outlives_constraint,
&lhs_ty,
current_point,
successor_point,
universal_regions,
)
}
_ => {
// For the other cases, we localize an outlives constraint to where it arises.
LocalizedOutlivesConstraint {
source: outlives_constraint.sup,
from: current_point,
target: outlives_constraint.sub,
to: current_point,
}
}
}
}
/// For a given outlives constraint arising from a MIR terminator, localize the constraint with the
/// needed CFG `from`-`to` inter-block nodes.
fn localize_terminator_constraint<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
terminator: &Terminator<'tcx>,
liveness: &LivenessValues,
outlives_constraint: &OutlivesConstraint<'tcx>,
current_point: PointIndex,
universal_regions: &UniversalRegions<'tcx>,
) -> LocalizedOutlivesConstraint {
// FIXME: check if other terminators need the same handling as `Call`s, in particular
// Assert/Yield/Drop. A handful of tests are failing with Drop related issues, as well as some
// coroutine tests, and that may be why.
match &terminator.kind {
// FIXME: also handle diverging calls.
TerminatorKind::Call { destination, target: Some(target), .. } => {
// Calls are similar to assignments, and thus follow the same pattern. If there is a
// target for the call we also relate what flows into the destination here to entry to
// that successor.
let destination_ty = destination.ty(&body.local_decls, tcx);
let successor_location = Location { block: *target, statement_index: 0 };
let successor_point = liveness.point_from_location(successor_location);
compute_constraint_direction(
tcx,
outlives_constraint,
&destination_ty,
current_point,
successor_point,
universal_regions,
)
}
_ => {
// Typeck constraints guide loans between regions at the current point, so we do that in
// the general case, and liveness will take care of making them flow to the terminator's
// successors.
LocalizedOutlivesConstraint {
source: outlives_constraint.sup,
from: current_point,
target: outlives_constraint.sub,
to: current_point,
}
}
}
}
/// For a given outlives constraint and CFG edge, returns the localized constraint with the
/// appropriate `from`-`to` direction. This is computed according to whether the constraint flows to
/// or from a free region in the given `value`, some kind of result for an effectful operation, like
/// the LHS of an assignment.
fn compute_constraint_direction<'tcx>(
tcx: TyCtxt<'tcx>,
outlives_constraint: &OutlivesConstraint<'tcx>,
value: &impl TypeVisitable<TyCtxt<'tcx>>,
current_point: PointIndex,
successor_point: PointIndex,
universal_regions: &UniversalRegions<'tcx>,
) -> LocalizedOutlivesConstraint {
let mut to = current_point;
let mut from = current_point;
tcx.for_each_free_region(value, |region| {
let region = universal_regions.to_region_vid(region);
if region == outlives_constraint.sub {
// This constraint flows into the result, its effects start becoming visible on exit.
to = successor_point;
} else if region == outlives_constraint.sup {
// This constraint flows from the result, its effects start becoming visible on exit.
from = successor_point;
}
});
LocalizedOutlivesConstraint {
source: outlives_constraint.sup,
from,
target: outlives_constraint.sub,
to,
}
}

View file

@ -52,7 +52,7 @@ fn render_region_vid<'tcx>(
format!(" (for<{}>)", tcx.item_name(def_id))
}
ty::BoundRegionKind::ClosureEnv | ty::BoundRegionKind::Anon => " (for<'_>)".to_string(),
ty::BoundRegionKind::NamedForPrinting(_) => {
ty::BoundRegionKind::NamedAnon(_) => {
bug!("only used for pretty printing")
}
},

View file

@ -1379,11 +1379,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
.elements_contained_in(longer_fr_scc)
.find(|e| *e != RegionElement::PlaceholderRegion(placeholder))
{
let illegally_outlived_r = self.region_from_element(longer_fr, &error_element);
// Stop after the first error, it gets too noisy otherwise, and does not provide more information.
errors_buffer.push(RegionErrorKind::PlaceholderOutlivesIllegalRegion {
errors_buffer.push(RegionErrorKind::BoundUniversalRegionError {
longer_fr,
illegally_outlived_r,
error_element,
placeholder,
});
} else {
debug!("check_bound_universal_region: all bounds satisfied");
@ -1572,7 +1572,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
/// Get the region outlived by `longer_fr` and live at `element`.
fn region_from_element(
pub(crate) fn region_from_element(
&self,
longer_fr: RegionVid,
element: &RegionElement<'tcx>,

View file

@ -24,13 +24,13 @@ use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use tracing::{debug, instrument};
use super::reverse_sccs::ReverseSccGraph;
use crate::BorrowckInferCtxt;
use crate::consumers::RegionInferenceContext;
use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
use crate::type_check::canonical::fully_perform_op_raw;
use crate::type_check::free_region_relations::UniversalRegionRelations;
use crate::type_check::{Locations, MirTypeckRegionConstraints};
use crate::universal_regions::{RegionClassification, UniversalRegions};
use crate::{BorrowckInferCtxt, CollectRegionConstraintsResult};
mod member_constraints;
mod region_ctxt;
@ -126,31 +126,6 @@ fn nll_var_to_universal_region<'tcx>(
}
}
/// Record info needed to report the same name error later.
#[derive(Copy, Clone, Debug)]
pub(crate) struct UnexpectedHiddenRegion<'tcx> {
// The def_id of the body where this error occurs.
// Needed to handle region vars with their corresponding `infcx`.
def_id: LocalDefId,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_type: ProvisionalHiddenType<'tcx>,
member_region: Region<'tcx>,
}
impl<'tcx> UnexpectedHiddenRegion<'tcx> {
pub(crate) fn to_error(self) -> (LocalDefId, DeferredOpaqueTypeError<'tcx>) {
let UnexpectedHiddenRegion { def_id, opaque_type_key, hidden_type, member_region } = self;
(
def_id,
DeferredOpaqueTypeError::UnexpectedHiddenRegion {
opaque_type_key,
hidden_type,
member_region,
},
)
}
}
/// Collect all defining uses of opaque types inside of this typeck root. This
/// expects the hidden type to be mapped to the definition parameters of the opaque
/// and errors if we end up with distinct hidden types.
@ -201,13 +176,11 @@ struct DefiningUse<'tcx> {
/// It also means that this whole function is not really soundness critical as we
/// recheck all uses of the opaques regardless.
pub(crate) fn compute_definition_site_hidden_types<'tcx>(
def_id: LocalDefId,
infcx: &BorrowckInferCtxt<'tcx>,
universal_region_relations: &Frozen<UniversalRegionRelations<'tcx>>,
constraints: &MirTypeckRegionConstraints<'tcx>,
location_map: Rc<DenseLocationMap>,
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
unconstrained_hidden_type_errors: &mut Vec<UnexpectedHiddenRegion<'tcx>>,
opaque_types: &[(OpaqueTypeKey<'tcx>, ProvisionalHiddenType<'tcx>)],
) -> Vec<DeferredOpaqueTypeError<'tcx>> {
let mut errors = Vec::new();
@ -231,10 +204,8 @@ pub(crate) fn compute_definition_site_hidden_types<'tcx>(
// up equal to one of their choice regions and compute the actual hidden type of
// the opaque type definition. This is stored in the `root_cx`.
compute_definition_site_hidden_types_from_defining_uses(
def_id,
&rcx,
hidden_types,
unconstrained_hidden_type_errors,
&defining_uses,
&mut errors,
);
@ -303,10 +274,8 @@ fn collect_defining_uses<'tcx>(
#[instrument(level = "debug", skip(rcx, hidden_types, defining_uses, errors))]
fn compute_definition_site_hidden_types_from_defining_uses<'tcx>(
def_id: LocalDefId,
rcx: &RegionCtxt<'_, 'tcx>,
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
unconstrained_hidden_type_errors: &mut Vec<UnexpectedHiddenRegion<'tcx>>,
defining_uses: &[DefiningUse<'tcx>],
errors: &mut Vec<DeferredOpaqueTypeError<'tcx>>,
) {
@ -324,29 +293,16 @@ fn compute_definition_site_hidden_types_from_defining_uses<'tcx>(
Ok(hidden_type) => hidden_type,
Err(r) => {
debug!("UnexpectedHiddenRegion: {:?}", r);
// If we're using the next solver, the unconstrained region may be resolved by a
// fully defining use from another body.
// So we don't generate error eagerly here.
if rcx.infcx.tcx.use_typing_mode_borrowck() {
unconstrained_hidden_type_errors.push(UnexpectedHiddenRegion {
def_id,
hidden_type,
opaque_type_key,
member_region: ty::Region::new_var(tcx, r),
});
continue;
} else {
errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion {
hidden_type,
opaque_type_key,
member_region: ty::Region::new_var(tcx, r),
});
let guar = tcx.dcx().span_delayed_bug(
hidden_type.span,
"opaque type with non-universal region args",
);
ty::ProvisionalHiddenType::new_error(tcx, guar)
}
errors.push(DeferredOpaqueTypeError::UnexpectedHiddenRegion {
hidden_type,
opaque_type_key,
member_region: ty::Region::new_var(tcx, r),
});
let guar = tcx.dcx().span_delayed_bug(
hidden_type.span,
"opaque type with non-universal region args",
);
ty::ProvisionalHiddenType::new_error(tcx, guar)
}
};
@ -614,40 +570,6 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>(
errors
}
/// We handle `UnexpectedHiddenRegion` error lazily in the next solver as
/// there may be a fully defining use in another body.
///
/// In case such a defining use does not exist, we register an error here.
pub(crate) fn handle_unconstrained_hidden_type_errors<'tcx>(
tcx: TyCtxt<'tcx>,
hidden_types: &mut FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
unconstrained_hidden_type_errors: &mut Vec<UnexpectedHiddenRegion<'tcx>>,
collect_region_constraints_results: &mut FxIndexMap<
LocalDefId,
CollectRegionConstraintsResult<'tcx>,
>,
) {
let mut unconstrained_hidden_type_errors = std::mem::take(unconstrained_hidden_type_errors);
unconstrained_hidden_type_errors
.retain(|unconstrained| !hidden_types.contains_key(&unconstrained.opaque_type_key.def_id));
unconstrained_hidden_type_errors.iter().for_each(|t| {
tcx.dcx()
.span_delayed_bug(t.hidden_type.span, "opaque type with non-universal region args");
});
// `UnexpectedHiddenRegion` error contains region var which only makes sense in the
// corresponding `infcx`.
// So we need to insert the error to the body where it originates from.
for error in unconstrained_hidden_type_errors {
let (def_id, error) = error.to_error();
let Some(result) = collect_region_constraints_results.get_mut(&def_id) else {
unreachable!("the body should depend on opaques type if it has opaque use");
};
result.deferred_opaque_type_errors.push(error);
}
}
/// In theory `apply_definition_site_hidden_types` could introduce new uses of opaque types.
/// We do not check these new uses so this could be unsound.
///

View file

@ -10,8 +10,8 @@ use rustc_middle::ty::{self, RegionVid};
use rustc_mir_dataflow::points::{DenseLocationMap, PointIndex};
use tracing::debug;
use crate::BorrowIndex;
use crate::polonius::LiveLoans;
use crate::{BorrowIndex, TyCtxt};
rustc_index::newtype_index! {
/// A single integer representing a `ty::Placeholder`.
@ -131,17 +131,9 @@ impl LivenessValues {
}
}
/// Returns whether `region` is marked live at the given
/// [`location`][rustc_middle::mir::Location].
/// Returns whether `region` is marked live at the given `location`.
pub(crate) fn is_live_at(&self, region: RegionVid, location: Location) -> bool {
let point = self.location_map.point_from_location(location);
self.is_live_at_point(region, point)
}
/// Returns whether `region` is marked live at the given
/// [`point`][rustc_mir_dataflow::points::PointIndex].
#[inline]
pub(crate) fn is_live_at_point(&self, region: RegionVid, point: PointIndex) -> bool {
if let Some(points) = &self.points {
points.row(region).is_some_and(|r| r.contains(point))
} else {
@ -428,18 +420,18 @@ impl ToElementIndex<'_> for RegionVid {
impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> {
fn add_to_row<N: Idx>(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool
where
Self: Into<ty::PlaceholderRegion<'tcx>>,
Self: Into<ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion>>,
{
let placeholder: ty::PlaceholderRegion<'tcx> = self.into();
let placeholder: ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion> = self.into();
let index = values.placeholder_indices.lookup_index(placeholder);
values.placeholders.insert(row, index)
}
fn contained_in_row<N: Idx>(self, values: &RegionValues<'tcx, N>, row: N) -> bool
where
Self: Into<ty::PlaceholderRegion<'tcx>>,
Self: Into<ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion>>,
{
let placeholder: ty::PlaceholderRegion<'tcx> = self.into();
let placeholder: ty::Placeholder<TyCtxt<'tcx>, ty::BoundRegion> = self.into();
let index = values.placeholder_indices.lookup_index(placeholder);
values.placeholders.contains(row, index)
}

View file

@ -12,9 +12,8 @@ use smallvec::SmallVec;
use crate::consumers::BorrowckConsumer;
use crate::nll::compute_closure_requirements_modulo_opaques;
use crate::region_infer::opaque_types::{
UnexpectedHiddenRegion, apply_definition_site_hidden_types, clone_and_resolve_opaque_types,
apply_definition_site_hidden_types, clone_and_resolve_opaque_types,
compute_definition_site_hidden_types, detect_opaque_types_added_while_handling_opaque_types,
handle_unconstrained_hidden_type_errors,
};
use crate::type_check::{Locations, constraint_conversion};
use crate::{
@ -27,12 +26,7 @@ use crate::{
pub(super) struct BorrowCheckRootCtxt<'tcx> {
pub tcx: TyCtxt<'tcx>,
root_def_id: LocalDefId,
/// This contains fully resolved hidden types or `ty::Error`.
hidden_types: FxIndexMap<LocalDefId, ty::DefinitionSiteHiddenType<'tcx>>,
/// This contains unconstrained regions in hidden types.
/// Only used for deferred error reporting. See
/// [`crate::region_infer::opaque_types::handle_unconstrained_hidden_type_errors`]
unconstrained_hidden_type_errors: Vec<UnexpectedHiddenRegion<'tcx>>,
/// The region constraints computed by [borrowck_collect_region_constraints]. This uses
/// an [FxIndexMap] to guarantee that iterating over it visits nested bodies before
/// their parents.
@ -55,7 +49,6 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
tcx,
root_def_id,
hidden_types: Default::default(),
unconstrained_hidden_type_errors: Default::default(),
collect_region_constraints_results: Default::default(),
propagated_borrowck_results: Default::default(),
tainted_by_errors: None,
@ -91,32 +84,23 @@ impl<'tcx> BorrowCheckRootCtxt<'tcx> {
fn handle_opaque_type_uses(&mut self) {
let mut per_body_info = Vec::new();
for (def_id, input) in &mut self.collect_region_constraints_results {
for input in self.collect_region_constraints_results.values_mut() {
let (num_entries, opaque_types) = clone_and_resolve_opaque_types(
&input.infcx,
&input.universal_region_relations,
&mut input.constraints,
);
input.deferred_opaque_type_errors = compute_definition_site_hidden_types(
*def_id,
&input.infcx,
&input.universal_region_relations,
&input.constraints,
Rc::clone(&input.location_map),
&mut self.hidden_types,
&mut self.unconstrained_hidden_type_errors,
&opaque_types,
);
per_body_info.push((num_entries, opaque_types));
}
handle_unconstrained_hidden_type_errors(
self.tcx,
&mut self.hidden_types,
&mut self.unconstrained_hidden_type_errors,
&mut self.collect_region_constraints_results,
);
for (input, (opaque_types_storage_num_entries, opaque_types)) in
self.collect_region_constraints_results.values_mut().zip(per_body_info)
{

View file

@ -7,16 +7,16 @@ use rustc_span::Span;
use crate::diagnostics::RegionName;
#[derive(Diagnostic)]
#[diag("cannot move a value of type `{$ty}`", code = E0161)]
#[diag(borrowck_move_unsized, code = E0161)]
pub(crate) struct MoveUnsized<'tcx> {
pub ty: Ty<'tcx>,
#[primary_span]
#[label("the size of `{$ty}` cannot be statically determined")]
#[label]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("higher-ranked lifetime error")]
#[diag(borrowck_higher_ranked_lifetime_error)]
pub(crate) struct HigherRankedLifetimeError {
#[subdiagnostic]
pub cause: Option<HigherRankedErrorCause>,
@ -26,21 +26,21 @@ pub(crate) struct HigherRankedLifetimeError {
#[derive(Subdiagnostic)]
pub(crate) enum HigherRankedErrorCause {
#[note("could not prove `{$predicate}`")]
#[note(borrowck_could_not_prove)]
CouldNotProve { predicate: String },
#[note("could not normalize `{$value}`")]
#[note(borrowck_could_not_normalize)]
CouldNotNormalize { value: String },
}
#[derive(Diagnostic)]
#[diag("higher-ranked subtype error")]
#[diag(borrowck_higher_ranked_subtype_error)]
pub(crate) struct HigherRankedSubtypeError {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("`{$kind}` does not live long enough")]
#[diag(borrowck_generic_does_not_live_long_enough)]
pub(crate) struct GenericDoesNotLiveLongEnough {
pub kind: String,
#[primary_span]
@ -48,20 +48,15 @@ pub(crate) struct GenericDoesNotLiveLongEnough {
}
#[derive(LintDiagnostic)]
#[diag("variable does not need to be mutable")]
#[diag(borrowck_var_does_not_need_mut)]
pub(crate) struct VarNeedNotMut {
#[suggestion(
"remove this `mut`",
style = "short",
applicability = "machine-applicable",
code = ""
)]
#[suggestion(style = "short", applicability = "machine-applicable", code = "")]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("captured variable cannot escape `FnMut` closure body")]
#[note("`FnMut` closures only have access to their captured variables while they are executing...")]
#[note("...therefore, they cannot allow references to captured variables to escape")]
#[diag(borrowck_var_cannot_escape_closure)]
#[note]
#[note(borrowck_cannot_escape)]
pub(crate) struct FnMutError {
#[primary_span]
pub span: Span,
@ -71,17 +66,17 @@ pub(crate) struct FnMutError {
#[derive(Subdiagnostic)]
pub(crate) enum VarHereDenote {
#[label("variable captured here")]
#[label(borrowck_var_here_captured)]
Captured {
#[primary_span]
span: Span,
},
#[label("variable defined here")]
#[label(borrowck_var_here_defined)]
Defined {
#[primary_span]
span: Span,
},
#[label("inferred to be a `FnMut` closure")]
#[label(borrowck_closure_inferred_mut)]
FnMutInferred {
#[primary_span]
span: Span,
@ -90,21 +85,17 @@ pub(crate) enum VarHereDenote {
#[derive(Subdiagnostic)]
pub(crate) enum FnMutReturnTypeErr {
#[label(
"returns a closure that contains a reference to a captured variable, which then escapes the closure body"
)]
#[label(borrowck_returned_closure_escaped)]
ReturnClosure {
#[primary_span]
span: Span,
},
#[label(
"returns an `async` block that contains a reference to a captured variable, which then escapes the closure body"
)]
#[label(borrowck_returned_async_block_escaped)]
ReturnAsyncBlock {
#[primary_span]
span: Span,
},
#[label("returns a reference to a captured variable which escapes the closure body")]
#[label(borrowck_returned_ref_escaped)]
ReturnRef {
#[primary_span]
span: Span,
@ -112,7 +103,7 @@ pub(crate) enum FnMutReturnTypeErr {
}
#[derive(Diagnostic)]
#[diag("lifetime may not live long enough")]
#[diag(borrowck_lifetime_constraints_error)]
pub(crate) struct LifetimeOutliveErr {
#[primary_span]
pub span: Span,
@ -120,9 +111,7 @@ pub(crate) struct LifetimeOutliveErr {
#[derive(Subdiagnostic)]
pub(crate) enum LifetimeReturnCategoryErr<'a> {
#[label(
"{$mir_def_name} was supposed to return data with lifetime `{$outlived_fr_name}` but it is returning data with lifetime `{$fr_name}`"
)]
#[label(borrowck_returned_lifetime_wrong)]
WrongReturn {
#[primary_span]
span: Span,
@ -130,9 +119,7 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> {
outlived_fr_name: RegionName,
fr_name: &'a RegionName,
},
#[label(
"{$category_desc}requires that `{$free_region_name}` must outlive `{$outlived_fr_name}`"
)]
#[label(borrowck_returned_lifetime_short)]
ShortReturn {
#[primary_span]
span: Span,
@ -144,7 +131,7 @@ pub(crate) enum LifetimeReturnCategoryErr<'a> {
#[derive(Subdiagnostic)]
pub(crate) enum RequireStaticErr {
#[note("the used `impl` has a `'static` requirement")]
#[note(borrowck_used_impl_require_static)]
UsedImpl {
#[primary_span]
multi_span: MultiSpan,
@ -153,42 +140,42 @@ pub(crate) enum RequireStaticErr {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureVarPathUseCause {
#[label("borrow occurs due to use in coroutine")]
#[label(borrowck_borrow_due_to_use_coroutine)]
BorrowInCoroutine {
#[primary_span]
path_span: Span,
},
#[label("use occurs due to use in coroutine")]
#[label(borrowck_use_due_to_use_coroutine)]
UseInCoroutine {
#[primary_span]
path_span: Span,
},
#[label("assign occurs due to use in coroutine")]
#[label(borrowck_assign_due_to_use_coroutine)]
AssignInCoroutine {
#[primary_span]
path_span: Span,
},
#[label("assign to part occurs due to use in coroutine")]
#[label(borrowck_assign_part_due_to_use_coroutine)]
AssignPartInCoroutine {
#[primary_span]
path_span: Span,
},
#[label("borrow occurs due to use in closure")]
#[label(borrowck_borrow_due_to_use_closure)]
BorrowInClosure {
#[primary_span]
path_span: Span,
},
#[label("use occurs due to use in closure")]
#[label(borrowck_use_due_to_use_closure)]
UseInClosure {
#[primary_span]
path_span: Span,
},
#[label("assignment occurs due to use in closure")]
#[label(borrowck_assign_due_to_use_closure)]
AssignInClosure {
#[primary_span]
path_span: Span,
},
#[label("assignment to part occurs due to use in closure")]
#[label(borrowck_assign_part_due_to_use_closure)]
AssignPartInClosure {
#[primary_span]
path_span: Span,
@ -197,17 +184,17 @@ pub(crate) enum CaptureVarPathUseCause {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureVarKind {
#[label("capture is immutable because of use here")]
#[label(borrowck_capture_immute)]
Immut {
#[primary_span]
kind_span: Span,
},
#[label("capture is mutable because of use here")]
#[label(borrowck_capture_mut)]
Mut {
#[primary_span]
kind_span: Span,
},
#[label("capture is moved because of use here")]
#[label(borrowck_capture_move)]
Move {
#[primary_span]
kind_span: Span,
@ -216,97 +203,77 @@ pub(crate) enum CaptureVarKind {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureVarCause {
#[label(
"{$is_single_var ->
*[true] borrow occurs
[false] borrows occur
} due to use of {$place} in coroutine"
)]
#[label(borrowck_var_borrow_by_use_place_in_coroutine)]
BorrowUsePlaceCoroutine {
is_single_var: bool,
place: String,
#[primary_span]
var_span: Span,
},
#[label(
"{$is_single_var ->
*[true] borrow occurs
[false] borrows occur
} due to use of {$place} in closure"
)]
#[label(borrowck_var_borrow_by_use_place_in_closure)]
BorrowUsePlaceClosure {
is_single_var: bool,
place: String,
#[primary_span]
var_span: Span,
},
#[label("borrow occurs due to use in coroutine")]
#[label(borrowck_var_borrow_by_use_in_coroutine)]
BorrowUseInCoroutine {
#[primary_span]
var_span: Span,
},
#[label("borrow occurs due to use in closure")]
#[label(borrowck_var_borrow_by_use_in_closure)]
BorrowUseInClosure {
#[primary_span]
var_span: Span,
},
#[label("move occurs due to use in coroutine")]
#[label(borrowck_var_move_by_use_in_coroutine)]
MoveUseInCoroutine {
#[primary_span]
var_span: Span,
},
#[label("move occurs due to use in closure")]
#[label(borrowck_var_move_by_use_in_closure)]
MoveUseInClosure {
#[primary_span]
var_span: Span,
},
#[label("first borrow occurs due to use of {$place} in coroutine")]
#[label(borrowck_var_first_borrow_by_use_place_in_coroutine)]
FirstBorrowUsePlaceCoroutine {
place: String,
#[primary_span]
var_span: Span,
},
#[label("first borrow occurs due to use of {$place} in closure")]
#[label(borrowck_var_first_borrow_by_use_place_in_closure)]
FirstBorrowUsePlaceClosure {
place: String,
#[primary_span]
var_span: Span,
},
#[label("second borrow occurs due to use of {$place} in coroutine")]
#[label(borrowck_var_second_borrow_by_use_place_in_coroutine)]
SecondBorrowUsePlaceCoroutine {
place: String,
#[primary_span]
var_span: Span,
},
#[label("second borrow occurs due to use of {$place} in closure")]
#[label(borrowck_var_second_borrow_by_use_place_in_closure)]
SecondBorrowUsePlaceClosure {
place: String,
#[primary_span]
var_span: Span,
},
#[label("mutable borrow occurs due to use of {$place} in closure")]
#[label(borrowck_var_mutable_borrow_by_use_place_in_closure)]
MutableBorrowUsePlaceClosure {
place: String,
#[primary_span]
var_span: Span,
},
#[label(
"variable {$is_partial ->
[true] partially moved
*[false] moved
} due to use in coroutine"
)]
#[label(borrowck_partial_var_move_by_use_in_coroutine)]
PartialMoveUseInCoroutine {
#[primary_span]
var_span: Span,
is_partial: bool,
},
#[label(
"variable {$is_partial ->
[true] partially moved
*[false] moved
} due to use in closure"
)]
#[label(borrowck_partial_var_move_by_use_in_closure)]
PartialMoveUseInClosure {
#[primary_span]
var_span: Span,
@ -315,57 +282,34 @@ pub(crate) enum CaptureVarCause {
}
#[derive(Diagnostic)]
#[diag("cannot move out of {$place ->
[value] value
*[other] {$place}
} because it is borrowed", code = E0505)]
#[diag(borrowck_cannot_move_when_borrowed, code = E0505)]
pub(crate) struct MoveBorrow<'a> {
pub place: &'a str,
pub borrow_place: &'a str,
pub value_place: &'a str,
#[primary_span]
#[label(
"move out of {$value_place ->
[value] value
*[other] {$value_place}
} occurs here"
)]
#[label(borrowck_move_label)]
pub span: Span,
#[label(
"borrow of {$borrow_place ->
[value] value
*[other] {$borrow_place}
} occurs here"
)]
#[label]
pub borrow_span: Span,
}
#[derive(Diagnostic)]
#[diag("opaque type used twice with different lifetimes")]
#[diag(borrowck_opaque_type_lifetime_mismatch)]
pub(crate) struct LifetimeMismatchOpaqueParam<'tcx> {
pub arg: GenericArg<'tcx>,
pub prev: GenericArg<'tcx>,
#[primary_span]
#[label("lifetime `{$arg}` used here")]
#[note(
"if all non-lifetime generic parameters are the same, but the lifetime parameters differ, it is not possible to differentiate the opaque types"
)]
#[label]
#[note]
pub span: Span,
#[label("lifetime `{$prev}` previously used here")]
#[label(borrowck_prev_lifetime_label)]
pub prev_span: Span,
}
#[derive(Subdiagnostic)]
pub(crate) enum CaptureReasonLabel<'a> {
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this {$is_loop_message ->
[true] call, in previous iteration of loop
*[false] call
}"
)]
#[label(borrowck_moved_due_to_call)]
Call {
#[primary_span]
fn_call_span: Span,
@ -373,15 +317,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to usage in {$is_loop_message ->
[true] operator, in previous iteration of loop
*[false] operator
}"
)]
#[label(borrowck_moved_due_to_usage_in_operator)]
OperatorUse {
#[primary_span]
fn_call_span: Span,
@ -389,15 +325,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this implicit call to {$is_loop_message ->
[true] `.into_iter()`, in previous iteration of loop
*[false] `.into_iter()`
}"
)]
#[label(borrowck_moved_due_to_implicit_into_iter_call)]
ImplicitCall {
#[primary_span]
fn_call_span: Span,
@ -405,15 +333,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this method {$is_loop_message ->
[true] call, in previous iteration of loop
*[false] call
}"
)]
#[label(borrowck_moved_due_to_method_call)]
MethodCall {
#[primary_span]
fn_call_span: Span,
@ -421,15 +341,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"{$place_name} {$is_partial ->
[true] partially moved
*[false] moved
} due to this {$is_loop_message ->
[true] await, in previous iteration of loop
*[false] await
}"
)]
#[label(borrowck_moved_due_to_await)]
Await {
#[primary_span]
fn_call_span: Span,
@ -437,18 +349,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_partial: bool,
is_loop_message: bool,
},
#[label(
"value {$is_partial ->
[true] partially moved
*[false] moved
} {$is_move_msg ->
[true] into closure here
*[false] here
}{$is_loop_message ->
[true] , in previous iteration of loop
*[false] {\"\"}
}"
)]
#[label(borrowck_value_moved_here)]
MovedHere {
#[primary_span]
move_span: Span,
@ -456,7 +357,7 @@ pub(crate) enum CaptureReasonLabel<'a> {
is_move_msg: bool,
is_loop_message: bool,
},
#[label("help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents")]
#[label(borrowck_consider_borrow_type_contents)]
BorrowContent {
#[primary_span]
var_span: Span,
@ -465,22 +366,22 @@ pub(crate) enum CaptureReasonLabel<'a> {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureReasonNote {
#[note("this value implements `FnOnce`, which causes it to be moved when called")]
#[note(borrowck_moved_a_fn_once_in_call)]
FnOnceMoveInCall {
#[primary_span]
var_span: Span,
},
#[note("calling this operator moves the value")]
#[note(borrowck_calling_operator_moves)]
UnOpMoveByOperator {
#[primary_span]
span: Span,
},
#[note("calling this operator moves the left-hand side")]
#[note(borrowck_calling_operator_moves_lhs)]
LhsMoveByOperator {
#[primary_span]
span: Span,
},
#[note("`{$func}` takes ownership of the receiver `self`, which moves {$place_name}")]
#[note(borrowck_func_take_self_moved_place)]
FuncTakeSelf {
func: String,
place_name: String,
@ -492,7 +393,7 @@ pub(crate) enum CaptureReasonNote {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureReasonSuggest<'tcx> {
#[suggestion(
"consider iterating over a slice of the `{$ty}`'s content to avoid moving into the `for` loop",
borrowck_suggest_iterate_over_slice,
applicability = "maybe-incorrect",
code = "&",
style = "verbose"
@ -503,7 +404,7 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
span: Span,
},
#[suggestion(
"consider reborrowing the `Pin` instead of moving it",
borrowck_suggest_create_fresh_reborrow,
applicability = "maybe-incorrect",
code = ".as_mut()",
style = "verbose"
@ -516,18 +417,13 @@ pub(crate) enum CaptureReasonSuggest<'tcx> {
#[derive(Subdiagnostic)]
pub(crate) enum CaptureArgLabel {
#[label(
"value captured {$is_within ->
[true] here by coroutine
*[false] here
}"
)]
#[label(borrowck_value_capture_here)]
Capture {
is_within: bool,
#[primary_span]
args_span: Span,
},
#[label("{$place} is moved here")]
#[label(borrowck_move_out_place_here)]
MoveOutPlace {
place: String,
#[primary_span]
@ -537,17 +433,13 @@ pub(crate) enum CaptureArgLabel {
#[derive(Subdiagnostic)]
pub(crate) enum OnClosureNote<'a> {
#[note(
"closure cannot be invoked more than once because it moves the variable `{$place_name}` out of its environment"
)]
#[note(borrowck_closure_invoked_twice)]
InvokedTwice {
place_name: &'a str,
#[primary_span]
span: Span,
},
#[note(
"closure cannot be moved more than once as it is not `Copy` due to moving the variable `{$place_name}` out of its environment"
)]
#[note(borrowck_closure_moved_twice)]
MovedTwice {
place_name: &'a str,
#[primary_span]
@ -557,12 +449,7 @@ pub(crate) enum OnClosureNote<'a> {
#[derive(Subdiagnostic)]
pub(crate) enum TypeNoCopy<'a, 'tcx> {
#[label(
"{$is_partial_move ->
[true] partial move
*[false] move
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait"
)]
#[label(borrowck_ty_no_impl_copy)]
Label {
is_partial_move: bool,
ty: Ty<'tcx>,
@ -570,24 +457,12 @@ pub(crate) enum TypeNoCopy<'a, 'tcx> {
#[primary_span]
span: Span,
},
#[note(
"{$is_partial_move ->
[true] partial move
*[false] move
} occurs because {$place} has type `{$ty}`, which does not implement the `Copy` trait"
)]
#[note(borrowck_ty_no_impl_copy)]
Note { is_partial_move: bool, ty: Ty<'tcx>, place: &'a str },
}
#[derive(Diagnostic)]
#[diag(
"{$arg ->
[1] 1st
[2] 2nd
[3] 3rd
*[other] {$arg}th
} argument of `{$intrinsic}` is required to be a `const` item"
)]
#[diag(borrowck_simd_intrinsic_arg_const)]
pub(crate) struct SimdIntrinsicArgConst {
#[primary_span]
pub span: Span,
@ -596,8 +471,8 @@ pub(crate) struct SimdIntrinsicArgConst {
}
#[derive(LintDiagnostic)]
#[diag("relative drop order changing in Rust 2024")]
#[diag(borrowck_tail_expr_drop_order)]
pub(crate) struct TailExprDropOrder {
#[label("this temporary value will be dropped at the end of the block")]
#[label]
pub borrowed: Span,
}

View file

@ -343,15 +343,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
return;
}
// This is a hack. `body.local_decls` are not necessarily normalized in the old
// solver due to not deeply normalizing in writeback. So we must re-normalize here.
//
// I am not sure of a test case where this actually matters. There is a similar
// hack in `equate_inputs_and_outputs` which does have associated test cases.
let mir_ty = match self.infcx.next_trait_solver() {
true => mir_ty,
false => self.normalize(mir_ty, Locations::All(span)),
};
// FIXME: Ideally MIR types are normalized, but this is not always true.
let mir_ty = self.normalize(mir_ty, Locations::All(span));
let cause = ObligationCause::dummy_with_span(span);
let param_env = self.infcx.param_env;
@ -360,10 +353,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
ConstraintCategory::Boring,
type_op::custom::CustomTypeOp::new(
|ocx| {
// The `AscribeUserType` query would normally emit a wf
// obligation for the unnormalized user_ty here. This is
// where the "incorrectly skips the WF checks we normally do"
// happens
let user_ty = ocx.normalize(&cause, param_env, user_ty);
ocx.eq(&cause, param_env, user_ty, mir_ty)?;
Ok(())

Some files were not shown because too many files have changed in this diff Show more