TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
CACHE_DOMAIN: ci-caches.rust-lang.org
if: "github.event_name == 'pull_request'"
+ continue-on-error: "${{ matrix.tidy }}"
strategy:
matrix:
include:
- name: mingw-check
+ tidy: false
+ os: ubuntu-20.04-xl
+ env: {}
+ - name: mingw-check-tidy
+ tidy: true
os: ubuntu-20.04-xl
env: {}
- name: x86_64-gnu-llvm-13
+ tidy: false
os: ubuntu-20.04-xl
env: {}
- name: x86_64-gnu-tools
+ tidy: false
env:
CI_ONLY_WHEN_SUBMODULES_CHANGED: 1
os: ubuntu-20.04-xl
- name: dist-x86_64-apple
env:
SCRIPT: "./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin"
- RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
+ RUST_CONFIGURE_ARGS: "--enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin"
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.7
SELECT_XCODE: /Applications/Xcode_13.4.1.app
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
dependencies = [
- "getrandom 0.2.0",
+ "getrandom 0.2.8",
"once_cell",
"version_check",
]
"yansi-term",
]
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "ansi_term"
version = "0.12.1"
dependencies = [
"addr2line",
"cc",
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"miniz_oxide",
"object",
[[package]]
name = "cargo"
-version = "0.68.0"
+version = "0.69.0"
dependencies = [
"anyhow",
"bytesize",
"directories",
"rustc-build-sysroot",
"rustc-workspace-hack",
- "rustc_tools_util",
+ "rustc_tools_util 0.2.1",
"rustc_version",
"serde",
"serde_json",
"jobserver",
]
-[[package]]
-name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
[[package]]
name = "cfg-if"
version = "1.0.0"
[[package]]
name = "clippy"
-version = "0.1.67"
+version = "0.1.68"
dependencies = [
"clippy_lints",
"clippy_utils",
"regex",
"rustc-semver",
"rustc-workspace-hack",
- "rustc_tools_util",
+ "rustc_tools_util 0.3.0",
"semver",
"serde",
"syn",
[[package]]
name = "clippy_lints"
-version = "0.1.67"
+version = "0.1.68"
dependencies = [
"cargo_metadata 0.14.0",
"clippy_utils",
"declare_clippy_lint",
"if_chain",
"itertools",
- "pulldown-cmark",
+ "pulldown-cmark 0.9.2",
"quine-mc_cluskey",
"regex-syntax",
"rustc-semver",
[[package]]
name = "clippy_utils"
-version = "0.1.67"
+version = "0.1.68"
dependencies = [
"arrayvec",
"if_chain",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
-version = "0.8.1"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
-version = "0.9.6"
+version = "0.9.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762"
+checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a"
dependencies = [
- "cfg-if 1.0.0",
+ "autocfg",
+ "cfg-if",
"crossbeam-utils",
- "lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
-version = "0.8.8"
+version = "0.8.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
+checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f"
dependencies = [
- "cfg-if 1.0.0",
- "lazy_static",
+ "cfg-if",
]
[[package]]
"quote",
]
+[[package]]
+name = "ctor"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
+dependencies = [
+ "quote",
+ "syn",
+]
+
[[package]]
name = "curl"
version = "0.4.44"
[[package]]
name = "declare_clippy_lint"
-version = "0.1.67"
+version = "0.1.68"
dependencies = [
"itertools",
"quote",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
+[[package]]
+name = "difference"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
+
[[package]]
name = "digest"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"dirs-sys-next",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"redox_syscall",
"winapi",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crc32fast",
"libc",
"libz-sys",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
-version = "0.2.0"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
+checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if",
"libc",
- "wasi 0.9.0+wasi-snapshot-preview1",
+ "wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
"serde",
]
+[[package]]
+name = "handlebars"
+version = "3.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4498fc115fa7d34de968184e473529abb40eeb6be8bc5f7faba3d08c316cb3e3"
+dependencies = [
+ "log",
+ "pest",
+ "pest_derive",
+ "quick-error 2.0.1",
+ "serde",
+ "serde_json",
+ "walkdir",
+]
+
[[package]]
name = "handlebars"
version = "4.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
dependencies = [
- "quick-error",
+ "quick-error 1.2.3",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
[[package]]
name = "io-lifetimes"
-version = "1.0.2"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e394faa0efb47f9f227f1cd89978f854542b318a6f64fa695489c9c993056656"
+checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
dependencies = [
"libc",
"windows-sys",
[[package]]
name = "is-terminal"
-version = "0.4.0"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aae5bc6e2eb41c9def29a3e0f1306382807764b9b53112030eff57435667352d"
+checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
dependencies = [
"hermit-abi 0.2.6",
"io-lifetimes",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"winapi",
]
[[package]]
name = "linux-raw-sys"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
+checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4"
[[package]]
name = "litemap"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
"clap_complete",
"elasticlunr-rs",
"env_logger 0.9.0",
- "handlebars",
+ "handlebars 4.3.3",
"lazy_static",
"log",
"memchr",
"opener",
- "pulldown-cmark",
+ "pulldown-cmark 0.9.2",
"regex",
"serde",
"serde_json",
"topological-sort",
]
+[[package]]
+name = "mdman"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "handlebars 3.5.5",
+ "pretty_assertions",
+ "pulldown-cmark 0.7.2",
+ "same-file",
+ "serde_json",
+ "url",
+]
+
[[package]]
name = "measureme"
version = "10.1.0"
[[package]]
name = "memoffset"
-version = "0.6.5"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
dependencies = [
"colored",
"env_logger 0.9.0",
- "getrandom 0.2.0",
+ "getrandom 0.2.8",
"lazy_static",
"libc",
"libffi",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a518809ac14b25b569624d0268eba1e88498f71615893dca57982bed7621abb"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
]
[[package]]
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
dependencies = [
"bitflags",
- "cfg-if 1.0.0",
+ "cfg-if",
"foreign-types",
"libc",
"once_cell",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
+[[package]]
+name = "output_vt100"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "owo-colors"
version = "3.4.0"
[[package]]
name = "packed_simd_2"
-version = "0.3.4"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3278e0492f961fd4ae70909f56b2723a7e8d01a228427294e19cdfdebda89a17"
+checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if",
"libm",
]
version = "0.0.0"
dependencies = [
"alloc",
- "cfg-if 1.0.0",
+ "cfg-if",
"compiler_builtins",
"core",
"libc",
version = "0.0.0"
dependencies = [
"alloc",
- "cfg-if 1.0.0",
+ "cfg-if",
"compiler_builtins",
"core",
"libc",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"instant",
"libc",
"redox_syscall",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"redox_syscall",
"smallvec",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
+[[package]]
+name = "pretty_assertions"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
+dependencies = [
+ "ansi_term 0.11.0",
+ "ctor",
+ "difference",
+ "output_vt100",
+]
+
[[package]]
name = "pretty_env_logger"
version = "0.4.0"
"cc",
]
+[[package]]
+name = "pulldown-cmark"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca36dea94d187597e104a5c8e4b07576a8a45aa5db48a65e12940d3eb7461f55"
+dependencies = [
+ "bitflags",
+ "memchr",
+ "unicase",
+]
+
[[package]]
name = "pulldown-cmark"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
+[[package]]
+name = "quick-error"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
+
[[package]]
name = "quine-mc_cluskey"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
- "getrandom 0.2.0",
+ "getrandom 0.2.8",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
dependencies = [
- "getrandom 0.2.0",
+ "getrandom 0.2.8",
"redox_syscall",
]
dependencies = [
"arrayvec",
"bitflags",
- "cfg-if 0.1.10",
+ "cfg-if",
"ena",
"indexmap",
"jobserver",
"rustc_ast",
"rustc_attr",
"rustc_data_structures",
+ "rustc_error_messages",
"rustc_errors",
"rustc_feature",
"rustc_graphviz",
"rustc_hir",
"rustc_index",
"rustc_infer",
+ "rustc_macros",
"rustc_middle",
"rustc_serialize",
"rustc_session",
name = "rustc_span"
version = "0.0.0"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if",
"md-5",
"rustc_arena",
"rustc_data_structures",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "598f48ce2a421542b3e64828aa742b687cc1b91d2f96591cfdb7ac5988cd6366"
+[[package]]
+name = "rustc_tools_util"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ba09476327c4b70ccefb6180f046ef588c26a24cf5d269a9feba316eb4f029f"
+
[[package]]
name = "rustc_trait_selection"
version = "0.0.0"
"itertools",
"minifier",
"once_cell",
- "pulldown-cmark",
+ "pulldown-cmark 0.9.2",
"rayon",
"regex",
"rustdoc-json-types",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"cpufeatures",
"digest",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"cpufeatures",
"digest",
]
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
- "cfg-if 1.0.0",
+ "cfg-if",
"libc",
"psm",
"winapi",
dependencies = [
"addr2line",
"alloc",
- "cfg-if 1.0.0",
+ "cfg-if",
"compiler_builtins",
"core",
"dlmalloc",
name = "std_detect"
version = "0.1.5"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"compiler_builtins",
"libc",
"rustc-std-workspace-alloc",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"fastrand",
"libc",
"redox_syscall",
name = "test"
version = "0.0.0"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"core",
"getopts",
"libc",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0639d10d8f4615f223a57275cf40f9bdb7cfbb806bcb7f7cc56e3beb55a576eb"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"getopts",
"libc",
"num_cpus",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3"
dependencies = [
- "ansi_term",
+ "ansi_term 0.12.1",
"lazy_static",
"matchers",
"parking_lot 0.11.2",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ce989c9962c7f61fe084dd4a230eec784649dfc2392467c790007c3a6e134e7"
dependencies = [
- "ansi_term",
+ "ansi_term 0.12.1",
"atty",
"tracing-core",
"tracing-log",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"rand 0.8.5",
"static_assertions",
]
version = "0.0.0"
dependencies = [
"cc",
- "cfg-if 1.0.0",
+ "cfg-if",
"compiler_builtins",
"core",
"libc",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
dependencies = [
- "getrandom 0.2.0",
+ "getrandom 0.2.8",
]
[[package]]
"src/tools/cargo/crates/credential/cargo-credential-1password",
"src/tools/cargo/crates/credential/cargo-credential-macos-keychain",
"src/tools/cargo/crates/credential/cargo-credential-wincred",
+ "src/tools/cargo/crates/mdman",
+ # "src/tools/cargo/crates/resolver-tests",
"src/tools/rustdoc",
"src/tools/rls",
"src/tools/rustfmt",
}
}
+ /// Returns the largest signed value that can be represented by this Integer.
+ #[inline]
+ pub fn signed_max(self) -> i128 {
+ match self {
+ I8 => i8::MAX as i128,
+ I16 => i16::MAX as i128,
+ I32 => i32::MAX as i128,
+ I64 => i64::MAX as i128,
+ I128 => i128::MAX,
+ }
+ }
+
/// Finds the smallest Integer type which can represent the signed value.
#[inline]
pub fn fit_signed(x: i128) -> Integer {
}
rustc_index::newtype_index! {
- pub struct AttrId {
- ENCODABLE = custom
- DEBUG_FORMAT = "AttrId({})"
- }
+ #[custom_encodable]
+ #[debug_format = "AttrId({})]"]
+ pub struct AttrId {}
}
impl<S: Encoder> Encodable<S> for AttrId {
/// This is later turned into [`DefId`] and `HirId` for the HIR.
///
/// [`DefId`]: rustc_span::def_id::DefId
- pub struct NodeId {
- DEBUG_FORMAT = "NodeId({})"
- }
+ #[debug_format = "NodeId({})"]
+ pub struct NodeId {}
}
rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeMapEntry, NodeId);
if i != 0 || j != lines.len() { Some((i, j)) } else { None }
}
- fn get_horizontal_trim<'a>(lines: &'a [&str], kind: CommentKind) -> Option<String> {
+ fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option<String> {
let mut i = usize::MAX;
let mut first = true;
use rustc_hir::definitions::DefPathData;
use rustc_session::errors::report_lit_error;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
-use rustc_span::symbol::{kw, sym, Ident};
+use rustc_span::symbol::{sym, Ident};
use rustc_span::DUMMY_SP;
use thin_vec::thin_vec;
) -> hir::ExprKind<'hir> {
let output = ret_ty.unwrap_or_else(|| hir::FnRetTy::DefaultReturn(self.lower_span(span)));
- // Resume argument type, which should be `&mut Context<'_>`.
- // NOTE: Using the `'static` lifetime here is technically cheating.
- // The `Future::poll` argument really is `&'a mut Context<'b>`, but we cannot
- // express the fact that we are not storing it across yield-points yet,
- // and we would thus run into lifetime errors.
- // See <https://github.com/rust-lang/rust/issues/68923>.
- // Our lowering makes sure we are not mis-using the `_task_context` input type
- // in the sense that we are indeed not using it across yield points. We
- // get a fresh `&mut Context` for each resume / call of `Future::poll`.
- // This "cheating" was previously done with a `ResumeTy` that contained a raw
- // pointer, and a `get_context` accessor that pulled the `Context` lifetimes
- // out of thin air.
- let context_lifetime_ident = Ident::with_dummy_span(kw::StaticLifetime);
- let context_lifetime = self.arena.alloc(hir::Lifetime {
- hir_id: self.next_id(),
- ident: context_lifetime_ident,
- res: hir::LifetimeName::Static,
- });
- let context_path =
- hir::QPath::LangItem(hir::LangItem::Context, self.lower_span(span), None);
- let context_ty = hir::MutTy {
- ty: self.arena.alloc(hir::Ty {
- hir_id: self.next_id(),
- kind: hir::TyKind::Path(context_path),
- span: self.lower_span(span),
- }),
- mutbl: hir::Mutability::Mut,
- };
+ // Resume argument type: `ResumeTy`
+ let unstable_span =
+ self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
+ let resume_ty = hir::QPath::LangItem(hir::LangItem::ResumeTy, unstable_span, None);
let input_ty = hir::Ty {
hir_id: self.next_id(),
- kind: hir::TyKind::Rptr(context_lifetime, context_ty),
- span: self.lower_span(span),
+ kind: hir::TyKind::Path(resume_ty),
+ span: unstable_span,
};
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
hir::ExprKind::Closure(c)
};
- let track_caller = self
- .attrs
- .get(&outer_hir_id.local_id)
- .map_or(false, |attrs| attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)));
-
let hir_id = self.lower_node_id(closure_node_id);
let unstable_span =
self.mark_span_with_reason(DesugaringKind::Async, span, self.allow_gen_future.clone());
- if track_caller {
+
+ if self.tcx.features().closure_track_caller
+ && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id)
+ && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller))
+ {
self.lower_attrs(
hir_id,
&[Attribute {
/// mut __awaitee => loop {
/// match unsafe { ::std::future::Future::poll(
/// <::std::pin::Pin>::new_unchecked(&mut __awaitee),
- /// task_context,
+ /// ::std::future::get_context(task_context),
/// ) } {
/// ::std::task::Poll::Ready(result) => break result,
/// ::std::task::Poll::Pending => {}
// unsafe {
// ::std::future::Future::poll(
// ::std::pin::Pin::new_unchecked(&mut __awaitee),
- // task_context,
+ // ::std::future::get_context(task_context),
// )
// }
let poll_expr = {
arena_vec![self; ref_mut_awaitee],
Some(expr_hir_id),
);
+ let get_context = self.expr_call_lang_item_fn_mut(
+ gen_future_span,
+ hir::LangItem::GetContext,
+ arena_vec![self; task_context],
+ Some(expr_hir_id),
+ );
let call = self.expr_call_lang_item_fn(
span,
hir::LangItem::FuturePoll,
- arena_vec![self; new_unchecked, task_context],
+ arena_vec![self; new_unchecked, get_context],
Some(expr_hir_id),
);
self.arena.alloc(self.expr_unsafe(call))
use super::errors::{InvalidAbi, InvalidAbiSuggestion, MisplacedRelaxTraitBound};
use super::ResolverAstLoweringExt;
-use super::{Arena, AstOwner, ImplTraitContext, ImplTraitPosition};
+use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
use super::{FnDeclKind, LoweringContext, ParamMode};
use rustc_ast::ptr::P;
pub(super) struct ItemLowerer<'a, 'hir> {
pub(super) tcx: TyCtxt<'hir>,
pub(super) resolver: &'a mut ResolverAstLowering,
- pub(super) ast_arena: &'a Arena<'static>,
pub(super) ast_index: &'a IndexVec<LocalDefId, AstOwner<'a>>,
pub(super) owners: &'a mut IndexVec<LocalDefId, hir::MaybeOwner<&'hir hir::OwnerInfo<'hir>>>,
}
tcx: self.tcx,
resolver: self.resolver,
arena: self.tcx.hir_arena,
- ast_arena: self.ast_arena,
// HirId handling.
bodies: Vec::new(),
use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait, TraitFnAsync};
-use rustc_arena::declare_arena;
use rustc_ast::ptr::P;
use rustc_ast::visit;
use rustc_ast::{self as ast, *};
/// Used to allocate HIR nodes.
arena: &'hir hir::Arena<'hir>,
- /// Used to allocate temporary AST nodes for use during lowering.
- /// This allows us to create "fake" AST -- these nodes can sometimes
- /// be allocated on the stack, but other times we need them to live longer
- /// than the current stack frame, so they can be collected into vectors
- /// and things like that.
- ast_arena: &'a Arena<'static>,
-
/// Bodies inside the owner being lowered.
bodies: Vec<(hir::ItemLocalId, &'hir hir::Body<'hir>)>,
/// Attributes inside the owner being lowered.
generics_def_id_map: Vec<FxHashMap<LocalDefId, LocalDefId>>,
}
-declare_arena!([
- [] tys: rustc_ast::Ty,
- [] aba: rustc_ast::AngleBracketedArgs,
- [] ptr: rustc_ast::PolyTraitRef,
- // This _marker field is needed because `declare_arena` creates `Arena<'tcx>` and we need to
- // use `'tcx`. If we don't have this we get a compile error.
- [] _marker: std::marker::PhantomData<&'tcx ()>,
-]);
-
trait ResolverAstLoweringExt {
fn legacy_const_generic_args(&self, expr: &Expr) -> Option<Vec<usize>>;
fn get_partial_res(&self, id: NodeId) -> Option<PartialRes>;
})
}
-pub fn lower_to_hir<'hir>(tcx: TyCtxt<'hir>, (): ()) -> hir::Crate<'hir> {
+pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> {
let sess = tcx.sess;
let krate = tcx.untracked_crate.steal();
let mut resolver = tcx.resolver_for_lowering(()).steal();
tcx.definitions_untracked().def_index_count(),
);
- let ast_arena = Arena::default();
-
for def_id in ast_index.indices() {
item::ItemLowerer {
tcx,
resolver: &mut resolver,
- ast_arena: &ast_arena,
ast_index: &ast_index,
owners: &mut owners,
}
self.impl_trait_defs = current_impl_trait_defs;
self.impl_trait_bounds = current_impl_trait_bounds;
- debug_assert!(self.children.iter().find(|(id, _)| id == &def_id).is_none());
+ debug_assert!(!self.children.iter().any(|(id, _)| id == &def_id));
self.children.push((def_id, hir::MaybeOwner::Owner(info)));
}
}
GenericArgs::Parenthesized(data) => {
self.emit_bad_parenthesized_trait_in_assoc_ty(data);
- let aba = self.ast_arena.aba.alloc(data.as_angle_bracketed_args());
- self.lower_angle_bracketed_parameter_data(aba, ParamMode::Explicit, itctx).0
+ self.lower_angle_bracketed_parameter_data(
+ &data.as_angle_bracketed_args(),
+ ParamMode::Explicit,
+ itctx,
+ )
+ .0
}
};
gen_args_ctor.into_generic_args(self)
self.with_dyn_type_scope(false, |this| {
let node_id = this.next_node_id();
- let ty = this.ast_arena.tys.alloc(Ty {
- id: node_id,
- kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
- span: this.lower_span(constraint.span),
- tokens: None,
- });
- let ty = this.lower_ty(ty, itctx);
+ let ty = this.lower_ty(
+ &Ty {
+ id: node_id,
+ kind: TyKind::ImplTrait(impl_trait_node_id, bounds.clone()),
+ span: this.lower_span(constraint.span),
+ tokens: None,
+ },
+ itctx,
+ );
hir::TypeBindingKind::Equality { term: ty.into() }
})
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) = partial_res.full_res()
{
let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
- let poly_trait_ref = this.ast_arena.ptr.alloc(PolyTraitRef {
- bound_generic_params: vec![],
- trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
- span: t.span
- });
let bound = this.lower_poly_trait_ref(
- poly_trait_ref,
+ &PolyTraitRef {
+ bound_generic_params: vec![],
+ trait_ref: TraitRef { path: path.clone(), ref_id: t.id },
+ span: t.span
+ },
itctx,
);
let bounds = this.arena.alloc_from_iter([bound]);
/// What is the context that prevents using `~const`?
enum DisallowTildeConstContext<'a> {
TraitObject,
- ImplTrait,
Fn(FnKind<'a>),
}
fn with_impl_trait(&mut self, outer: Option<Span>, f: impl FnOnce(&mut Self)) {
let old = mem::replace(&mut self.outer_impl_trait, outer);
- if outer.is_some() {
- self.with_banned_tilde_const(DisallowTildeConstContext::ImplTrait, f);
- } else {
- f(self);
- }
+ f(self);
self.outer_impl_trait = old;
}
let mut err = self.err_handler().struct_span_err(bound.span(), "`~const` is not allowed here");
match reason {
DisallowTildeConstContext::TraitObject => err.note("trait objects cannot have `~const` trait bounds"),
- DisallowTildeConstContext::ImplTrait => err.note("`impl Trait`s cannot have `~const` trait bounds"),
DisallowTildeConstContext::Fn(FnKind::Closure(..)) => err.note("closures cannot have `~const` trait bounds"),
DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => err.span_note(ident.span, "this function is not `const`, so it cannot have `~const` trait bounds"),
};
if let Some(p) = self.pointer {
self.pointer = self.graph.next_constraints[p];
- Some(self.constraints[p].clone())
+ Some(self.constraints[p])
} else if let Some(next_static_idx) = self.next_static_idx {
self.next_static_idx = if next_static_idx == (self.graph.first_constraints.len() - 1) {
None
}
rustc_index::newtype_index! {
- pub struct OutlivesConstraintIndex {
- DEBUG_FORMAT = "OutlivesConstraintIndex({})"
- }
+ #[debug_format = "OutlivesConstraintIndex({})"]
+ pub struct OutlivesConstraintIndex {}
}
rustc_index::newtype_index! {
- pub struct ConstraintSccIndex {
- DEBUG_FORMAT = "ConstraintSccIndex({})"
- }
+ #[debug_format = "ConstraintSccIndex({})"]
+ pub struct ConstraintSccIndex {}
}
/// that shows how to do this at `src/test/run-make/obtain-borrowck/`.
///
/// * Polonius is highly unstable, so expect regular changes in its signature or other details.
-pub fn get_body_with_borrowck_facts<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub fn get_body_with_borrowck_facts(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> BodyWithBorrowckFacts<'tcx> {
+) -> BodyWithBorrowckFacts<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
let infcx = tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(def.did)).build();
let input_body: &Body<'_> = &input_body.borrow();
}
rustc_index::newtype_index! {
- pub struct BorrowIndex {
- DEBUG_FORMAT = "bw{}"
- }
+ #[debug_format = "bw{}"]
+ pub struct BorrowIndex {}
}
/// `Borrows` stores the data used in the analyses that track the flow
if !assign_value.is_empty() {
err.span_suggestion_verbose(
sugg_span.shrink_to_hi(),
- format!("consider assigning a value"),
+ "consider assigning a value",
format!(" = {}", assign_value),
Applicability::MaybeIncorrect,
);
for extra in extra_info {
match extra {
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
- err.span_note(*span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+ err.span_note(*span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
}
}
}
/// Find all uses of (including assignments to) a [`Local`].
///
/// Uses `BTreeSet` so output is deterministic.
-pub(super) fn find<'tcx>(body: &Body<'tcx>, local: Local) -> BTreeSet<Location> {
+pub(super) fn find(body: &Body<'_>, local: Local) -> BTreeSet<Location> {
let mut visitor = AllLocalUsesVisitor { for_local: local, uses: BTreeSet::default() };
visitor.visit_body(body);
visitor.uses
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::Region;
use rustc_middle::ty::TypeVisitor;
use rustc_middle::ty::{self, RegionVid, Ty};
+use rustc_middle::ty::{Region, TyCtxt};
use rustc_span::symbol::{kw, Ident};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
use crate::borrowck_errors;
use crate::session_diagnostics::{
///
/// Usually we expect this to either be empty or contain a small number of items, so we can avoid
/// allocation most of the time.
-pub(crate) type RegionErrors<'tcx> = Vec<RegionErrorKind<'tcx>>;
+pub(crate) struct RegionErrors<'tcx>(Vec<RegionErrorKind<'tcx>>, TyCtxt<'tcx>);
+
+impl<'tcx> RegionErrors<'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+ Self(vec![], tcx)
+ }
+ #[track_caller]
+ pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) {
+ let val = val.into();
+ self.1.sess.delay_span_bug(DUMMY_SP, "{val:?}");
+ self.0.push(val);
+ }
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+ pub fn into_iter(self) -> impl Iterator<Item = RegionErrorKind<'tcx>> {
+ self.0.into_iter()
+ }
+}
#[derive(Clone, Debug)]
pub(crate) enum RegionErrorKind<'tcx> {
for extra in extra_info {
match extra {
ExtraConstraintInfo::PlaceholderFromPredicate(span) => {
- diag.span_note(span, format!("due to current limitations in the borrow checker, this implies a `'static` lifetime"));
+ diag.span_note(span, "due to current limitations in the borrow checker, this implies a `'static` lifetime");
}
}
}
};
}
-fn mir_borrowck<'tcx>(
- tcx: TyCtxt<'tcx>,
- def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx BorrowCheckResult<'tcx> {
+fn mir_borrowck(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &BorrowCheckResult<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));
) -> Option<InitIndex> {
let mpi = self.move_data.rev_lookup.find_local(local);
let ii = &self.move_data.init_path_map[mpi];
- for &index in ii {
- if flow_state.ever_inits.contains(index) {
- return Some(index);
- }
- }
- None
+ ii.into_iter().find(|&&index| flow_state.ever_inits.contains(index)).copied()
}
/// Adds the place into the used mutable variables set
}
rustc_index::newtype_index! {
- pub struct LocationIndex {
- DEBUG_FORMAT = "LocationIndex({})"
- }
+ #[debug_format = "LocationIndex({})"]
+ pub struct LocationIndex {}
}
#[derive(Copy, Clone, Debug)]
}
rustc_index::newtype_index! {
- pub(crate) struct NllMemberConstraintIndex {
- DEBUG_FORMAT = "MemberConstraintIndex({})"
- }
+ #[debug_format = "MemberConstraintIndex({})"]
+ pub(crate) struct NllMemberConstraintIndex {}
}
impl Default for MemberConstraintSet<'_, ty::RegionVid> {
let mir_def_id = body.source.def_id();
self.propagate_constraints(body);
- let mut errors_buffer = RegionErrors::new();
+ let mut errors_buffer = RegionErrors::new(infcx.tcx);
// If this is a closure, we can propagate unsatisfied
// `outlives_requirements` to our creator, so create a vector
if self.eval_verify_bound(
infcx,
param_env,
- body,
generic_ty,
type_test.lower_bound,
&type_test.verify_bound,
// where `ur` is a local bound -- we are sometimes in a
// position to prove things that our caller cannot. See
// #53570 for an example.
- if self.eval_verify_bound(
- infcx,
- param_env,
- body,
- generic_ty,
- ur,
- &type_test.verify_bound,
- ) {
+ if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) {
continue;
}
&self,
infcx: &InferCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- body: &Body<'tcx>,
generic_ty: Ty<'tcx>,
lower_bound: RegionVid,
verify_bound: &VerifyBound<'tcx>,
}
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
- self.eval_verify_bound(
- infcx,
- param_env,
- body,
- generic_ty,
- lower_bound,
- verify_bound,
- )
+ self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound)
}),
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
- self.eval_verify_bound(
- infcx,
- param_env,
- body,
- generic_ty,
- lower_bound,
- verify_bound,
- )
+ self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound)
}),
}
}
let longer_fr_scc = self.constraint_sccs.scc(longer_fr);
debug!("check_bound_universal_region: longer_fr_scc={:?}", longer_fr_scc,);
- // If we have some bound universal region `'a`, then the only
- // elements it can contain is itself -- we don't know anything
- // else about it!
- let Some(error_element) = ({
- self.scc_values.elements_contained_in(longer_fr_scc).find(|element| match element {
- RegionElement::Location(_) => true,
- RegionElement::RootUniversalRegion(_) => true,
- RegionElement::PlaceholderRegion(placeholder1) => placeholder != *placeholder1,
- })
- }) else {
- return;
- };
- debug!("check_bound_universal_region: error_element = {:?}", error_element);
+ for error_element in self.scc_values.elements_contained_in(longer_fr_scc) {
+ match error_element {
+ RegionElement::Location(_) | RegionElement::RootUniversalRegion(_) => {}
+ // If we have some bound universal region `'a`, then the only
+ // elements it can contain is itself -- we don't know anything
+ // else about it!
+ RegionElement::PlaceholderRegion(placeholder1) => {
+ if placeholder == placeholder1 {
+ continue;
+ }
+ }
+ }
- // Find the region that introduced this `error_element`.
- errors_buffer.push(RegionErrorKind::BoundUniversalRegionError {
- longer_fr,
- error_element,
- placeholder,
- });
+ errors_buffer.push(RegionErrorKind::BoundUniversalRegionError {
+ longer_fr,
+ error_element,
+ placeholder,
+ });
+
+ // Stop after the first error, it gets too noisy otherwise, and does not provide more information.
+ break;
+ }
+ debug!("check_bound_universal_region: all bounds satisfied");
}
#[instrument(level = "debug", skip(self, infcx, errors_buffer))]
rustc_index::newtype_index! {
/// A single integer representing a `Location` in the MIR control-flow
/// graph. Constructed efficiently from `RegionValueElements`.
- pub struct PointIndex { DEBUG_FORMAT = "PointIndex({})" }
+ #[debug_format = "PointIndex({})"]
+ pub struct PointIndex {}
}
rustc_index::newtype_index! {
/// A single integer representing a `ty::Placeholder`.
- pub struct PlaceholderIndex { DEBUG_FORMAT = "PlaceholderIndex({})" }
+ #[debug_format = "PlaceholderIndex({})"]
+ pub struct PlaceholderIndex {}
}
/// An individual element in a region value -- the value of a
/// outlives `fr` and (b) is not local.
///
/// (*) If there are multiple competing choices, we return all of them.
- pub(crate) fn non_local_upper_bounds<'a>(&'a self, fr: RegionVid) -> Vec<RegionVid> {
+ pub(crate) fn non_local_upper_bounds(&self, fr: RegionVid) -> Vec<RegionVid> {
debug!("non_local_upper_bound(fr={:?})", fr);
let res = self.non_local_bounds(&self.inverse_outlives, fr);
assert!(!res.is_empty(), "can't find an upper bound!?");
/// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`.
/// Repeatedly invokes `postdom_parent` until we find something that is not
/// local. Returns `None` if we never do so.
- fn non_local_bounds<'a>(
+ fn non_local_bounds(
&self,
- relation: &'a TransitiveRelation<RegionVid>,
+ relation: &TransitiveRelation<RegionVid>,
fr0: RegionVid,
) -> Vec<RegionVid> {
// This method assumes that `fr0` is one of the universally
}
rustc_index::newtype_index! {
- pub struct AppearanceIndex { .. }
+ pub struct AppearanceIndex {}
}
impl vll::LinkElem for Appearance {
let locations = location.to_locations();
for constraint in constraints.outlives().iter() {
- let mut constraint = constraint.clone();
+ let mut constraint = *constraint;
constraint.locations = locations;
if let ConstraintCategory::Return(_)
| ConstraintCategory::UseAsConst
category: ConstraintCategory<'tcx>,
) -> Fallible<()> {
let annotated_type = self.user_type_annotations[user_ty.base].inferred_ty;
+ trace!(?annotated_type);
let mut curr_projected_ty = PlaceTy::from_ty(annotated_type);
let tcx = self.infcx.tcx;
for proj in &user_ty.projs {
+ if let ty::Alias(ty::Opaque, ..) = curr_projected_ty.ty.kind() {
+ // There is nothing that we can compare here if we go through an opaque type.
+ // We're always in its defining scope as we can otherwise not project through
+ // it, so we're constraining it anyways.
+ return Ok(());
+ }
let projected_ty = curr_projected_ty.projection_ty_core(
tcx,
self.param_env,
proj,
- |this, field, _| {
+ |this, field, ()| {
let ty = this.field_ty(tcx, field);
self.normalize(ty, locations)
},
);
curr_projected_ty = projected_ty;
}
- debug!(
- "user_ty base: {:?} freshened: {:?} projs: {:?} yields: {:?}",
- user_ty.base, annotated_type, user_ty.projs, curr_projected_ty
- );
+ trace!(?curr_projected_ty);
let ty = curr_projected_ty.ty;
self.relate_types(ty, v.xform(ty::Variance::Contravariant), a, locations, category)?;
///
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the suggestion will be incorrect.
-fn err_duplicate_option<'a>(p: &mut Parser<'a>, symbol: Symbol, span: Span) {
+fn err_duplicate_option(p: &mut Parser<'_>, symbol: Symbol, span: Span) {
let mut err = p
.sess
.span_diagnostic
(item, true, ecx.with_def_site_ctxt(ty.span))
} else {
ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
- return vec![orig_item.clone()]
+ return vec![orig_item];
};
// Generate a bunch of new items using the AllocFnFactory
// use proc_macro::bridge::client::ProcMacro;
//
// #[rustc_proc_macro_decls]
+// #[used]
// #[allow(deprecated)]
// static DECLS: &[ProcMacro] = &[
// ProcMacro::custom_derive($name_trait1, &[], ::$name1);
)
.map(|mut i| {
i.attrs.push(cx.attr_word(sym::rustc_proc_macro_decls, span));
+ i.attrs.push(cx.attr_word(sym::used, span));
i.attrs.push(cx.attr_nested_word(sym::allow, sym::deprecated, span));
i
});
cx.attr_nested_word(sym::cfg, sym::test, attr_sp),
// #[rustc_test_marker = "test_case_sort_key"]
cx.attr_name_value_str(sym::rustc_test_marker, test_path_symbol, attr_sp),
- ]
- .into(),
+ ],
// const $ident: test::TestDescAndFn =
ast::ItemKind::Const(
ast::Defaultness::Final,
let res = CValue::by_val(res, arg.layout());
ret.write_cvalue(fx, res);
}
- sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => {
+ sym::assert_inhabited | sym::assert_zero_valid | sym::assert_mem_uninitialized_valid => {
intrinsic_args!(fx, args => (); intrinsic);
let layout = fx.layout_of(substs.type_at(0));
return;
}
- if intrinsic == sym::assert_uninit_valid && !fx.tcx.permits_uninit_init(layout) {
+ if intrinsic == sym::assert_mem_uninitialized_valid
+ && !fx.tcx.permits_uninit_init(layout)
+ {
with_no_trimmed_paths!({
crate::base::codegen_panic(
fx,
}
}
-pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_128bit_integers: bool) -> (ModuleCodegen<GccContext>, u64) {
+pub fn compile_codegen_unit(tcx: TyCtxt<'_>, cgu_name: Symbol, supports_128bit_integers: bool) -> (ModuleCodegen<GccContext>, u64) {
let prof_timer = tcx.prof.generic_activity("codegen_module");
let start_time = Instant::now();
context.new_array_constructor(None, typ, &elements)
}
-pub fn type_is_pointer<'gcc>(typ: Type<'gcc>) -> bool {
+pub fn type_is_pointer(typ: Type<'_>) -> bool {
typ.get_pointee().is_some()
}
mods
}
- fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
+ fn compile_codegen_unit(&self, tcx: TyCtxt<'_>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
base::compile_codegen_unit(tcx, cgu_name, *self.supports_128bit_integers.lock().expect("lock"))
}
// We prefer the latter because it matches the behavior of
// Clang.
if late && matches!(reg, InlineAsmRegOrRegClass::Reg(_)) {
- constraints.push(format!("{}", reg_to_llvm(reg, Some(&in_value.layout))));
+ constraints.push(reg_to_llvm(reg, Some(&in_value.layout)).to_string());
} else {
constraints.push(format!("{}", op_idx[&idx]));
}
}
}
+fn nojumptables_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
+ if !cx.sess().opts.unstable_opts.no_jump_tables {
+ return None;
+ }
+
+ Some(llvm::CreateAttrStringValue(cx.llcx, "no-jump-tables", "true"))
+}
+
fn probestack_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
// Currently stack probes seem somewhat incompatible with the address
// sanitizer and thread sanitizer. With asan we're already protected from
// FIXME: none of these three functions interact with source level attributes.
to_add.extend(frame_pointer_type_attr(cx));
to_add.extend(instrument_function_attr(cx));
+ to_add.extend(nojumptables_attr(cx));
to_add.extend(probestack_attr(cx));
to_add.extend(stackprotector_attr(cx));
info!("going for that thin, thin LTO");
let green_modules: FxHashMap<_, _> =
- cached_modules.iter().map(|&(_, ref wp)| (wp.cgu_name.clone(), wp.clone())).collect();
+ cached_modules.iter().map(|(_, wp)| (wp.cgu_name.clone(), wp.clone())).collect();
let full_scope_len = modules.len() + serialized_modules.len() + cached_modules.len();
let mut thin_buffers = Vec::with_capacity(modules.len());
// Set KCFI operand bundle
let is_indirect_call = unsafe { llvm::LLVMIsAFunction(llfn).is_none() };
let kcfi_bundle =
- if self.tcx.sess.is_sanitizer_kcfi_enabled() && fn_abi.is_some() && is_indirect_call {
- let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi.unwrap());
+ if self.tcx.sess.is_sanitizer_kcfi_enabled() && let Some(fn_abi) = fn_abi && is_indirect_call {
+ let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi);
Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)]))
} else {
None
use crate::callee::get_fn;
use crate::coverageinfo;
use crate::debuginfo;
-use crate::errors::BranchProtectionRequiresAArch64;
use crate::llvm;
use crate::llvm_util;
use crate::type_::Type;
}
if let Some(BranchProtection { bti, pac_ret }) = sess.opts.unstable_opts.branch_protection {
- if sess.target.arch != "aarch64" {
- sess.emit_err(BranchProtectionRequiresAArch64);
+ let behavior = if llvm_version >= (15, 0, 0) {
+ llvm::LLVMModFlagBehavior::Min
} else {
+ llvm::LLVMModFlagBehavior::Error
+ };
+
+ if sess.target.arch == "aarch64" {
llvm::LLVMRustAddModuleFlag(
llmod,
- llvm::LLVMModFlagBehavior::Error,
+ behavior,
"branch-target-enforcement\0".as_ptr().cast(),
bti.into(),
);
llvm::LLVMRustAddModuleFlag(
llmod,
- llvm::LLVMModFlagBehavior::Error,
+ behavior,
"sign-return-address\0".as_ptr().cast(),
pac_ret.is_some().into(),
);
let pac_opts = pac_ret.unwrap_or(PacRet { leaf: false, key: PAuthKey::A });
llvm::LLVMRustAddModuleFlag(
llmod,
- llvm::LLVMModFlagBehavior::Error,
+ behavior,
"sign-return-address-all\0".as_ptr().cast(),
pac_opts.leaf.into(),
);
llvm::LLVMRustAddModuleFlag(
llmod,
- llvm::LLVMModFlagBehavior::Error,
+ behavior,
"sign-return-address-with-bkey\0".as_ptr().cast(),
u32::from(pac_opts.key == PAuthKey::B),
);
+ } else {
+ bug!(
+ "branch-protection used on non-AArch64 target; \
+ this should be checked in rustc_session."
+ );
}
}
/// implementing this Rust version, and though the format documentation is very explicit and
/// detailed, some undocumented details in Clang's implementation (that may or may not be important)
/// were also replicated for Rust's Coverage Map.
-pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
+pub fn finalize(cx: &CodegenCx<'_, '_>) {
let tcx = cx.tcx;
// Ensure the installed version of LLVM supports at least Coverage Map
/// "code coverage dead code cgu" during the partitioning process. This prevents us from generating
/// code regions for the same function more than once which can lead to linker errors regarding
/// duplicate symbols.
-fn add_unused_functions<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
+fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
assert!(cx.codegen_unit.is_code_coverage_dead_code_cgu());
let tcx = cx.tcx;
/// Extract size and alignment from a TyAndLayout.
#[inline]
-fn size_and_align_of<'tcx>(ty_and_layout: TyAndLayout<'tcx>) -> (Size, Align) {
+fn size_and_align_of(ty_and_layout: TyAndLayout<'_>) -> (Size, Align) {
(ty_and_layout.size, ty_and_layout.align.abi)
}
pub symbol_name: &'a str,
}
-#[derive(Diagnostic)]
-#[diag(codegen_llvm_branch_protection_requires_aarch64)]
-pub(crate) struct BranchProtectionRequiresAArch64;
-
#[derive(Diagnostic)]
#[diag(codegen_llvm_invalid_minimum_alignment)]
pub(crate) struct InvalidMinimumAlignment {
// module.
//
// When modifying, make sure that the type_name string exactly matches
- // the one used in src/libpanic_unwind/seh.rs.
+ // the one used in library/panic_unwind/src/seh.rs.
let type_info_vtable = bx.declare_global("??_7type_info@@6B@", bx.type_i8p());
let type_name = bx.const_bytes(b"rust_panic\0");
let type_info =
Append = 5,
AppendUnique = 6,
Max = 7,
+ Min = 8,
}
// Consts for the LLVM CallConv type, pre-cast to usize.
pub fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine);
- pub fn LLVMRustBuildOperandBundleDef<'a>(
+ pub fn LLVMRustBuildOperandBundleDef(
Name: *const c_char,
- Inputs: *const &'a Value,
+ Inputs: *const &'_ Value,
NumInputs: c_uint,
- ) -> &'a mut OperandBundleDef<'a>;
+ ) -> &mut OperandBundleDef<'_>;
pub fn LLVMRustFreeOperandBundleDef<'a>(Bundle: &'a mut OperandBundleDef<'a>);
pub fn LLVMRustPositionBuilderAtStart<'a>(B: &Builder<'a>, BB: &'a BasicBlock);
val
}
+fn emit_s390x_va_arg<'ll, 'tcx>(
+ bx: &mut Builder<'_, 'll, 'tcx>,
+ list: OperandRef<'tcx, &'ll Value>,
+ target_ty: Ty<'tcx>,
+) -> &'ll Value {
+ // Implementation of the s390x ELF ABI calling convention for va_args see
+ // https://github.com/IBM/s390x-abi (chapter 1.2.4)
+ let va_list_addr = list.immediate();
+ let va_list_layout = list.deref(bx.cx).layout;
+ let va_list_ty = va_list_layout.llvm_type(bx);
+ let layout = bx.cx.layout_of(target_ty);
+
+ let in_reg = bx.append_sibling_block("va_arg.in_reg");
+ let in_mem = bx.append_sibling_block("va_arg.in_mem");
+ let end = bx.append_sibling_block("va_arg.end");
+
+ // FIXME: vector ABI not yet supported.
+ let target_ty_size = bx.cx.size_of(target_ty).bytes();
+ let indirect: bool = target_ty_size > 8 || !target_ty_size.is_power_of_two();
+ let unpadded_size = if indirect { 8 } else { target_ty_size };
+ let padded_size = 8;
+ let padding = padded_size - unpadded_size;
+
+ let gpr_type = indirect || !layout.is_single_fp_element(bx.cx);
+ let (max_regs, reg_count_field, reg_save_index, reg_padding) =
+ if gpr_type { (5, 0, 2, padding) } else { (4, 1, 16, 0) };
+
+ // Check whether the value was passed in a register or in memory.
+ let reg_count = bx.struct_gep(
+ va_list_ty,
+ va_list_addr,
+ va_list_layout.llvm_field_index(bx.cx, reg_count_field),
+ );
+ let reg_count_v = bx.load(bx.type_i64(), reg_count, Align::from_bytes(8).unwrap());
+ let use_regs = bx.icmp(IntPredicate::IntULT, reg_count_v, bx.const_u64(max_regs));
+ bx.cond_br(use_regs, in_reg, in_mem);
+
+ // Emit code to load the value if it was passed in a register.
+ bx.switch_to_block(in_reg);
+
+ // Work out the address of the value in the register save area.
+ let reg_ptr =
+ bx.struct_gep(va_list_ty, va_list_addr, va_list_layout.llvm_field_index(bx.cx, 3));
+ let reg_ptr_v = bx.load(bx.type_i8p(), reg_ptr, bx.tcx().data_layout.pointer_align.abi);
+ let scaled_reg_count = bx.mul(reg_count_v, bx.const_u64(8));
+ let reg_off = bx.add(scaled_reg_count, bx.const_u64(reg_save_index * 8 + reg_padding));
+ let reg_addr = bx.gep(bx.type_i8(), reg_ptr_v, &[reg_off]);
+
+ // Update the register count.
+ let new_reg_count_v = bx.add(reg_count_v, bx.const_u64(1));
+ bx.store(new_reg_count_v, reg_count, Align::from_bytes(8).unwrap());
+ bx.br(end);
+
+ // Emit code to load the value if it was passed in memory.
+ bx.switch_to_block(in_mem);
+
+ // Work out the address of the value in the argument overflow area.
+ let arg_ptr =
+ bx.struct_gep(va_list_ty, va_list_addr, va_list_layout.llvm_field_index(bx.cx, 2));
+ let arg_ptr_v = bx.load(bx.type_i8p(), arg_ptr, bx.tcx().data_layout.pointer_align.abi);
+ let arg_off = bx.const_u64(padding);
+ let mem_addr = bx.gep(bx.type_i8(), arg_ptr_v, &[arg_off]);
+
+ // Update the argument overflow area pointer.
+ let arg_size = bx.cx().const_u64(padded_size);
+ let new_arg_ptr_v = bx.inbounds_gep(bx.type_i8(), arg_ptr_v, &[arg_size]);
+ bx.store(new_arg_ptr_v, arg_ptr, bx.tcx().data_layout.pointer_align.abi);
+ bx.br(end);
+
+ // Return the appropriate result.
+ bx.switch_to_block(end);
+ let val_addr = bx.phi(bx.type_i8p(), &[reg_addr, mem_addr], &[in_reg, in_mem]);
+ let val_type = layout.llvm_type(bx);
+ let val_addr = if indirect {
+ let ptr_type = bx.cx.type_ptr_to(val_type);
+ let ptr_addr = bx.bitcast(val_addr, bx.cx.type_ptr_to(ptr_type));
+ bx.load(ptr_type, ptr_addr, bx.tcx().data_layout.pointer_align.abi)
+ } else {
+ bx.bitcast(val_addr, bx.cx.type_ptr_to(val_type))
+ };
+ bx.load(val_type, val_addr, layout.align.abi)
+}
+
pub(super) fn emit_va_arg<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
addr: OperandRef<'tcx, &'ll Value>,
emit_ptr_va_arg(bx, addr, target_ty, false, Align::from_bytes(8).unwrap(), true)
}
"aarch64" => emit_aapcs_va_arg(bx, addr, target_ty),
+ "s390x" => emit_s390x_va_arg(bx, addr, target_ty),
// Windows x86_64
"x86_64" if target.is_like_windows => {
let target_ty_size = bx.cx.size_of(target_ty).bytes();
) -> io::Result<Option<(&'a [u8], u64)>> {
let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
- let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
+ let desired = match archs.iter().find(|a| a.architecture() == target_arch) {
Some(a) => a,
None => return Ok(None),
};
};
for &cnum in crates {
match fmts.get(cnum.as_usize() - 1) {
- Some(&Linkage::NotLinked | &Linkage::IncludedFromDylib) => continue,
+ Some(&Linkage::NotLinked | &Linkage::Dynamic | &Linkage::IncludedFromDylib) => continue,
Some(_) => {}
None => return Err(errors::LinkRlibError::MissingFormat),
}
}
impl<Relocations> ThorinSession<Relocations> {
- fn alloc_mmap<'arena>(&'arena self, data: Mmap) -> &'arena Mmap {
+ fn alloc_mmap(&self, data: Mmap) -> &Mmap {
(*self.arena_mmap.alloc(data)).borrow()
}
}
impl<Relocations> thorin::Session<Relocations> for ThorinSession<Relocations> {
- fn alloc_data<'arena>(&'arena self, data: Vec<u8>) -> &'arena [u8] {
+ fn alloc_data(&self, data: Vec<u8>) -> &[u8] {
(*self.arena_data.alloc(data)).borrow()
}
- fn alloc_relocation<'arena>(&'arena self, data: Relocations) -> &'arena Relocations {
+ fn alloc_relocation(&self, data: Relocations) -> &Relocations {
(*self.arena_relocations.alloc(data)).borrow()
}
- fn read_input<'arena>(&'arena self, path: &Path) -> std::io::Result<&'arena [u8]> {
+ fn read_input(&self, path: &Path) -> std::io::Result<&[u8]> {
let file = File::open(&path)?;
let mmap = (unsafe { Mmap::map(file) })?;
Ok(self.alloc_mmap(mmap))
linker::disable_localization(&mut cmd);
- for &(ref k, ref v) in sess.target.link_env.as_ref() {
+ for (k, v) in sess.target.link_env.as_ref() {
cmd.env(k.as_ref(), v.as_ref());
}
for k in sess.target.link_env_remove.as_ref() {
if sess.target.is_like_msvc {
if let Some(ref tool) = msvc_tool {
cmd.args(tool.args());
- for &(ref k, ref v) in tool.env() {
+ for (k, v) in tool.env() {
if k == "PATH" {
new_path.extend(env::split_paths(v));
msvc_changed_path = true;
tcx.reachable_non_generics(def_id.krate).contains_key(&def_id)
}
-fn exported_symbols_provider_local<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn exported_symbols_provider_local(
+ tcx: TyCtxt<'_>,
cnum: CrateNum,
-) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
+) -> &[(ExportedSymbol<'_>, SymbolExportInfo)] {
assert_eq!(cnum, LOCAL_CRATE);
if !tcx.sess.opts.output_types.should_codegen() {
/// NOTE: This is somewhat inconsistent right now: For empty enums and enums with a single
/// fieldless variant, we generate DW_TAG_struct_type, although a
/// DW_TAG_enumeration_type would be a better fit.
-pub fn wants_c_like_enum_debuginfo<'tcx>(enum_type_and_layout: TyAndLayout<'tcx>) -> bool {
+pub fn wants_c_like_enum_debuginfo(enum_type_and_layout: TyAndLayout<'_>) -> bool {
match enum_type_and_layout.ty.kind() {
ty::Adt(adt_def, _) => {
if !adt_def.is_enum() {
let align = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ALIGN)
.get_usize(bx, vtable);
+ // Size is always <= isize::MAX.
+ let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
+ bx.range_metadata(size, WrappingRange { start: 0, end: size_bound });
// Alignment is always nonzero.
bx.range_metadata(align, WrappingRange { start: 1, end: !0 });
/// This takes a valid `self` receiver type and extracts the principal trait
/// ref of the type.
-fn expect_dyn_trait_in_self<'tcx>(ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'tcx> {
+fn expect_dyn_trait_in_self(ty: Ty<'_>) -> ty::PolyExistentialTraitRef<'_> {
for arg in ty.peel_refs().walk() {
if let GenericArgKind::Type(ty) = arg.unpack() {
if let ty::Dynamic(data, _, _) = ty.kind() {
enum AssertIntrinsic {
Inhabited,
ZeroValid,
- UninitValid,
+ MemUninitializedValid,
}
let panic_intrinsic = intrinsic.and_then(|i| match i {
sym::assert_inhabited => Some(AssertIntrinsic::Inhabited),
sym::assert_zero_valid => Some(AssertIntrinsic::ZeroValid),
- sym::assert_uninit_valid => Some(AssertIntrinsic::UninitValid),
+ sym::assert_mem_uninitialized_valid => Some(AssertIntrinsic::MemUninitializedValid),
_ => None,
});
if let Some(intrinsic) = panic_intrinsic {
let do_panic = match intrinsic {
Inhabited => layout.abi.is_uninhabited(),
ZeroValid => !bx.tcx().permits_zero_init(layout),
- UninitValid => !bx.tcx().permits_uninit_init(layout),
+ MemUninitializedValid => !bx.tcx().permits_uninit_init(layout),
};
Some(if do_panic {
let msg_str = with_no_visible_paths!({
_ => bug!(),
};
let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable);
- if name == sym::vtable_align {
+ match name {
+ // Size is always <= isize::MAX.
+ sym::vtable_size => {
+ let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128;
+ bx.range_metadata(value, WrappingRange { start: 0, end: size_bound });
+ },
// Alignment is always nonzero.
- bx.range_metadata(value, WrappingRange { start: 1, end: !0 });
- };
+ sym::vtable_align => bx.range_metadata(value, WrappingRange { start: 1, end: !0 }),
+ _ => {}
+ }
value
}
sym::pref_align_of
/// Computes the set of target features used in a function for the purposes of
/// inline assembly.
-fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> {
+fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxHashSet<Symbol> {
let mut target_features = tcx.sess.unstable_target_features.clone();
if tcx.def_kind(did).has_codegen_attrs() {
let attrs = tcx.codegen_fn_attrs(did);
Immediate::new_slice(ptr, length.eval_usize(*self.tcx, self.param_env), self);
self.write_immediate(val, dest)
}
- (&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
+ (ty::Dynamic(data_a, ..), ty::Dynamic(data_b, ..)) => {
let val = self.read_immediate(src)?;
if data_a.principal() == data_b.principal() {
// A NOP cast that doesn't actually change anything, should be allowed even with mismatching vtables.
}
sym::offset => {
let ptr = self.read_pointer(&args[0])?;
- let offset_count = self.read_scalar(&args[1])?.to_machine_isize(self)?;
+ let offset_count = self.read_machine_isize(&args[1])?;
let pointee_ty = substs.type_at(0);
let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?;
}
sym::arith_offset => {
let ptr = self.read_pointer(&args[0])?;
- let offset_count = self.read_scalar(&args[1])?.to_machine_isize(self)?;
+ let offset_count = self.read_machine_isize(&args[1])?;
let pointee_ty = substs.type_at(0);
let pointee_size = i64::try_from(self.layout_of(pointee_ty)?.size.bytes()).unwrap();
sym::transmute => {
self.copy_op(&args[0], dest, /*allow_transmute*/ true)?;
}
- sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => {
+ sym::assert_inhabited
+ | sym::assert_zero_valid
+ | sym::assert_mem_uninitialized_valid => {
let ty = instance.substs.type_at(0);
let layout = self.layout_of(ty)?;
}
}
- if intrinsic_name == sym::assert_uninit_valid {
+ if intrinsic_name == sym::assert_mem_uninitialized_valid {
let should_panic = !self.tcx.permits_uninit_init(layout);
if should_panic {
count: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>,
nonoverlapping: bool,
) -> InterpResult<'tcx> {
- let count = self.read_scalar(&count)?.to_machine_usize(self)?;
+ let count = self.read_machine_usize(&count)?;
let layout = self.layout_of(src.layout.ty.builtin_deref(true).unwrap().ty)?;
let (size, align) = (layout.size, layout.align.abi);
// `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max),
let dst = self.read_pointer(&dst)?;
let byte = self.read_scalar(&byte)?.to_u8()?;
- let count = self.read_scalar(&count)?.to_machine_usize(self)?;
+ let count = self.read_machine_usize(&count)?;
// `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
impl<Prov: Provenance> From<Scalar<Prov>> for Immediate<Prov> {
#[inline(always)]
fn from(val: Scalar<Prov>) -> Self {
- Immediate::Scalar(val.into())
+ Immediate::Scalar(val)
}
}
}
pub fn new_slice(val: Scalar<Prov>, len: u64, cx: &impl HasDataLayout) -> Self {
- Immediate::ScalarPair(val.into(), Scalar::from_machine_usize(len, cx).into())
+ Immediate::ScalarPair(val, Scalar::from_machine_usize(len, cx))
}
pub fn new_dyn_trait(
vtable: Pointer<Option<Prov>>,
cx: &impl HasDataLayout,
) -> Self {
- Immediate::ScalarPair(val.into(), Scalar::from_maybe_pointer(vtable, cx))
+ Immediate::ScalarPair(val, Scalar::from_maybe_pointer(vtable, cx))
}
#[inline]
alloc_range(b_offset, b_size),
/*read_provenance*/ b.is_ptr(),
)?;
- Some(ImmTy {
- imm: Immediate::ScalarPair(a_val.into(), b_val.into()),
- layout: mplace.layout,
- })
+ Some(ImmTy { imm: Immediate::ScalarPair(a_val, b_val), layout: mplace.layout })
}
_ => {
// Neither a scalar nor scalar pair.
Ok(self.read_immediate(op)?.to_scalar())
}
+ // Pointer-sized reads are fairly common and need target layout access, so we wrap them in
+ // convenience functions.
+
/// Read a pointer from a place.
pub fn read_pointer(
&self,
) -> InterpResult<'tcx, Pointer<Option<M::Provenance>>> {
self.read_scalar(op)?.to_pointer(self)
}
+ /// Read a pointer-sized unsigned integer from a place.
+ pub fn read_machine_usize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, u64> {
+ self.read_scalar(op)?.to_machine_usize(self)
+ }
+ /// Read a pointer-sized signed integer from a place.
+ pub fn read_machine_isize(&self, op: &OpTy<'tcx, M::Provenance>) -> InterpResult<'tcx, i64> {
+ self.read_scalar(op)?.to_machine_isize(self)
+ }
/// Turn the wide MPlace into a string (must already be dereferenced!)
pub fn read_str(&self, mplace: &MPlaceTy<'tcx, M::Provenance>) -> InterpResult<'tcx, &str> {
ty::ConstKind::Unevaluated(uv) => {
let instance = self.resolve(uv.def, uv.substs)?;
let cid = GlobalId { instance, promoted: None };
- self.ctfe_query(span, |tcx| tcx.eval_to_valtree(self.param_env.and(cid)))?
- .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"))
+ self.ctfe_query(span, |tcx| {
+ tcx.eval_to_valtree(self.param_env.with_const().and(cid))
+ })?
+ .unwrap_or_else(|| bug!("unable to create ValTree for {uv:?}"))
}
ty::ConstKind::Bound(..) | ty::ConstKind::Infer(..) => {
span_bug!(self.cur_span(), "unexpected ConstKind in ctfe: {val:?}")
if let Abi::ScalarPair(..) = dest.layout.abi {
// We can use the optimized path and avoid `place_field` (which might do
// `force_allocation`).
- let pair = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
+ let pair = Immediate::ScalarPair(val, Scalar::from_bool(overflowed));
self.write_immediate(pair, dest)?;
} else {
assert!(self.tcx.sess.opts.unstable_opts.randomize_layout);
match self.meta {
MemPlaceMeta::None => Immediate::from(Scalar::from_maybe_pointer(self.ptr, cx)),
MemPlaceMeta::Meta(meta) => {
- Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx).into(), meta.into())
+ Immediate::ScalarPair(Scalar::from_maybe_pointer(self.ptr, cx), meta)
}
}
}
Index(local) => {
let layout = self.layout_of(self.tcx.types.usize)?;
let n = self.local_to_op(self.frame(), local, Some(layout))?;
- let n = self.read_scalar(&n)?.to_machine_usize(self)?;
+ let n = self.read_machine_usize(&n)?;
self.place_index(base, n)?
}
ConstantIndex { offset, min_length, from_end } => {
Index(local) => {
let layout = self.layout_of(self.tcx.types.usize)?;
let n = self.local_to_op(self.frame(), local, Some(layout))?;
- let n = self.read_scalar(&n)?.to_machine_usize(self)?;
+ let n = self.read_machine_usize(&n)?;
self.operand_index(base, n)?
}
ConstantIndex { offset, min_length, from_end } => {
}
Drop { place, target, unwind } => {
+ let frame = self.frame();
+ let ty = place.ty(&frame.body.local_decls, *self.tcx).ty;
+ let ty = self.subst_from_frame_and_normalize_erasing_regions(frame, ty)?;
+ let instance = Instance::resolve_drop_in_place(*self.tcx, ty);
+ if let ty::InstanceDef::DropGlue(_, None) = instance.def {
+ // This is the branch we enter if and only if the dropped type has no drop glue
+ // whatsoever. This can happen as a result of monomorphizing a drop of a
+ // generic. In order to make sure that generic and non-generic code behaves
+ // roughly the same (and in keeping with Mir semantics) we do nothing here.
+ self.go_to_block(target);
+ return Ok(());
+ }
let place = self.eval_place(place)?;
- let ty = place.layout.ty;
trace!("TerminatorKind::drop: {:?}, type {}", place, ty);
-
- let instance = Instance::resolve_drop_in_place(*self.tcx, ty);
self.drop_in_place(&place, instance, target, unwind)?;
}
use rustc_middle::mir::{
traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping,
Local, Location, MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef,
- ProjectionElem, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
- TerminatorKind, UnOp, START_BLOCK,
+ ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
+ Terminator, TerminatorKind, UnOp, START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeVisitable};
use rustc_mir_dataflow::impls::MaybeStorageLive;
self.fail(location, "`Deinit`is not allowed until deaggregation");
}
}
- StatementKind::Retag(_, _) => {
+ StatementKind::Retag(kind, _) => {
// FIXME(JakobDegen) The validator should check that `self.mir_phase <
// DropsLowered`. However, this causes ICEs with generation of drop shims, which
// seem to fail to set their `MirPhase` correctly.
+ if *kind == RetagKind::Raw || *kind == RetagKind::TwoPhase {
+ self.fail(location, format!("explicit `{:?}` is forbidden", kind));
+ }
}
StatementKind::StorageLive(..)
| StatementKind::StorageDead(..)
[dependencies]
arrayvec = { version = "0.7", default-features = false }
bitflags = "1.2.1"
-cfg-if = "0.1.2"
+cfg-if = "1.0"
ena = "0.14"
indexmap = { version = "1.9.1" }
jobserver_crate = { version = "0.1.13", package = "jobserver" }
rustc_index = { path = "../rustc_index", package = "rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
-smallvec = { version = "1.8.1", features = ["const_generics", "union", "may_dangle"] }
+smallvec = { version = "1.8.1", features = [
+ "const_generics",
+ "union",
+ "may_dangle",
+] }
stable_deref_trait = "1.0.0"
stacker = "0.1.15"
tempfile = "3.2"
}
rustc_index::newtype_index! {
- struct PreorderIndex { .. }
+ struct PreorderIndex {}
}
pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
/// VerboseTimingGuard returned from this call is dropped. In addition to recording
/// a measureme event, "verbose" generic activities also print a timing entry to
/// stderr if the compiler is invoked with -Ztime-passes.
- pub fn verbose_generic_activity<'a>(
- &'a self,
- event_label: &'static str,
- ) -> VerboseTimingGuard<'a> {
+ pub fn verbose_generic_activity(&self, event_label: &'static str) -> VerboseTimingGuard<'_> {
let message =
if self.print_verbose_generic_activities { Some(event_label.to_owned()) } else { None };
}
/// Like `verbose_generic_activity`, but with an extra arg.
- pub fn verbose_generic_activity_with_arg<'a, A>(
- &'a self,
+ pub fn verbose_generic_activity_with_arg<A>(
+ &self,
event_label: &'static str,
event_arg: A,
- ) -> VerboseTimingGuard<'a>
+ ) -> VerboseTimingGuard<'_>
where
A: Borrow<str> + Into<String>,
{
/// Viewing the relation as a graph, computes the "mutual
/// immediate postdominator" of a set of points (if one
/// exists). See `postdom_upper_bound` for details.
- pub fn mutual_immediate_postdominator<'a>(&'a self, mut mubs: Vec<T>) -> Option<T> {
+ pub fn mutual_immediate_postdominator(&self, mut mubs: Vec<T>) -> Option<T> {
loop {
match mubs.len() {
0 => return None,
}
#[inline]
- pub fn items<'a>(&'a self) -> UnordItems<&'a V, impl Iterator<Item = &'a V>> {
+ pub fn items(&self) -> UnordItems<&V, impl Iterator<Item = &V>> {
UnordItems(self.inner.iter())
}
}
#[inline]
- pub fn items<'a>(&'a self) -> UnordItems<(&'a K, &'a V), impl Iterator<Item = (&'a K, &'a V)>> {
+ pub fn items(&self) -> UnordItems<(&K, &V), impl Iterator<Item = (&K, &V)>> {
UnordItems(self.inner.iter())
}
}
#[inline]
- pub fn items<'a>(&'a self) -> UnordItems<&'a V, impl Iterator<Item = &'a V>> {
+ pub fn items(&self) -> UnordItems<&V, impl Iterator<Item = &V>> {
UnordItems(self.inner.iter())
}
};
// Invoke the default handler, which prints the actual panic message and optionally a backtrace
- (*DEFAULT_HOOK)(info);
+ // Don't do this for `ExplicitBug`, which has an unhelpful message and backtrace.
+ if !info.payload().is::<rustc_errors::ExplicitBug>() {
+ (*DEFAULT_HOOK)(info);
- // Separate the output with an empty line
- eprintln!();
+ // Separate the output with an empty line
+ eprintln!();
+ }
// Print the ICE message
report_ice(info, BUG_REPORT_URL);
E0312: include_str!("./error_codes/E0312.md"),
E0316: include_str!("./error_codes/E0316.md"),
E0317: include_str!("./error_codes/E0317.md"),
+E0320: include_str!("./error_codes/E0320.md"),
E0321: include_str!("./error_codes/E0321.md"),
E0322: include_str!("./error_codes/E0322.md"),
E0323: include_str!("./error_codes/E0323.md"),
E0374: include_str!("./error_codes/E0374.md"),
E0375: include_str!("./error_codes/E0375.md"),
E0376: include_str!("./error_codes/E0376.md"),
+E0377: include_str!("./error_codes/E0377.md"),
E0378: include_str!("./error_codes/E0378.md"),
E0379: include_str!("./error_codes/E0379.md"),
E0380: include_str!("./error_codes/E0380.md"),
E0453: include_str!("./error_codes/E0453.md"),
E0454: include_str!("./error_codes/E0454.md"),
E0455: include_str!("./error_codes/E0455.md"),
+E0457: include_str!("./error_codes/E0457.md"),
E0458: include_str!("./error_codes/E0458.md"),
E0459: include_str!("./error_codes/E0459.md"),
+E0460: include_str!("./error_codes/E0460.md"),
+E0462: include_str!("./error_codes/E0462.md"),
E0463: include_str!("./error_codes/E0463.md"),
E0464: include_str!("./error_codes/E0464.md"),
E0466: include_str!("./error_codes/E0466.md"),
E0468: include_str!("./error_codes/E0468.md"),
E0469: include_str!("./error_codes/E0469.md"),
+E0472: include_str!("./error_codes/E0472.md"),
E0477: include_str!("./error_codes/E0477.md"),
E0478: include_str!("./error_codes/E0478.md"),
E0482: include_str!("./error_codes/E0482.md"),
// E0314, // closure outlives stack frame
// E0315, // cannot invoke closure outside of its lifetime
// E0319, // trait impls for defaulted traits allowed just for structs/enums
- E0320, // recursive overflow during dropck
// E0372, // coherence not object safe
- E0377, // the trait `CoerceUnsized` may only be implemented for a coercion
- // between structures with the same definition
// E0385, // {} in an aliasable location
// E0402, // cannot use an outer type parameter in this context
// E0406, // merged into 420
// E0421, // merged into 531
// E0427, // merged into 530
// E0456, // plugin `..` is not available for triple `..`
- E0457, // plugin `..` only found in rlib format, but must be available...
- E0460, // found possibly newer version of crate `..`
E0461, // couldn't find crate `..` with expected target triple ..
- E0462, // found staticlib `..` instead of rlib or dylib
E0465, // multiple .. candidates for `..` found
// E0467, // removed
// E0470, // removed
// E0471, // constant evaluation error (in pattern)
- E0472, // llvm_asm! is unsupported on this target
// E0473, // dereference of reference outside its lifetime
// E0474, // captured variable `..` does not outlive the enclosing closure
// E0475, // index of slice outside its lifetime
-An associated const has been referenced in a pattern.
+An associated `const`, `const` parameter or `static` has been referenced
+in a pattern.
Erroneous code example:
```compile_fail,E0158
-enum EFoo { A, B, C, D }
+enum Foo {
+ One,
+ Two
+}
-trait Foo {
- const X: EFoo;
+trait Bar {
+ const X: Foo;
}
-fn test<A: Foo>(arg: EFoo) {
+fn test<A: Bar>(arg: Foo) {
match arg {
- A::X => { // error!
- println!("A::X");
- }
+ A::X => println!("A::X"), // error: E0158: associated consts cannot be
+ // referenced in patterns
+ Foo::Two => println!("Two")
}
}
```
-`const` and `static` mean different things. A `const` is a compile-time
-constant, an alias for a literal value. This property means you can match it
-directly within a pattern.
+Associated `const`s cannot be referenced in patterns because it is impossible
+for the compiler to prove exhaustiveness (that some pattern will always match).
+Take the above example, because Rust does type checking in the *generic*
+method, not the *monomorphized* specific instance. So because `Bar` could have
+theoretically infinite implementations, there's no way to always be sure that
+`A::X` is `Foo::One`. So this code must be rejected. Even if code can be
+proven exhaustive by a programmer, the compiler cannot currently prove this.
-The `static` keyword, on the other hand, guarantees a fixed location in memory.
-This does not always mean that the value is constant. For example, a global
-mutex can be declared `static` as well.
+The same holds true of `const` parameters and `static`s.
-If you want to match against a `static`, consider using a guard instead:
+If you want to match against an associated `const`, `const` parameter or
+`static` consider using a guard instead:
```
-static FORTY_TWO: i32 = 42;
+trait Trait {
+ const X: char;
+}
+
+static FOO: char = 'j';
-match Some(42) {
- Some(x) if x == FORTY_TWO => {}
- _ => {}
+fn test<A: Trait, const Y: char>(arg: char) {
+ match arg {
+ c if c == A::X => println!("A::X"),
+ c if c == Y => println!("Y"),
+ c if c == FOO => println!("FOO"),
+ _ => ()
+ }
}
```
--- /dev/null
+Recursion limit reached while creating drop-check rules.
+
+Example of erroneous code:
+
+```compile_fail,E0320
+enum A<T> {
+ B,
+ C(T, Box<A<(T, T)>>)
+}
+
+fn foo<T>() {
+ A::<T>::B; // error: overflow while adding drop-check rules for A<T>
+}
+```
+
+The Rust compiler must be able to reason about how a type is [`Drop`]ped, and
+by extension the types of its fields, to be able to generate the glue to
+properly drop a value. The code example above shows a type where this inference
+is impossible because it is recursive. Note that this is *not* the same as
+[E0072](E0072.html), where a type has an infinite size; the type here has a
+finite size but any attempt to `Drop` it would recurse infinitely. For more
+information, read [the `Drop` docs](../std/ops/trait.Drop.html).
+
+It is not possible to define a type with recursive drop-check rules. All such
+recursion must be removed.
+
+[`Drop`]: ../std/ops/trait.Drop.html
--- /dev/null
+The trait `CoerceUnsized` may only be implemented for a coercion between
+structures with the same definition.
+
+Example of erroneous code:
+
+```compile_fail,E0377
+#![feature(coerce_unsized)]
+use std::ops::CoerceUnsized;
+
+pub struct Foo<T: ?Sized> {
+ field_with_unsized_type: T,
+}
+
+pub struct Bar<T: ?Sized> {
+ field_with_unsized_type: T,
+}
+
+// error: the trait `CoerceUnsized` may only be implemented for a coercion
+// between structures with the same definition
+impl<T, U> CoerceUnsized<Bar<U>> for Foo<T> where T: CoerceUnsized<U> {}
+```
+
+When attempting to implement `CoerceUnsized`, the `impl` signature must look
+like: `impl CoerceUnsized<Type<U>> for Type<T> where T: CoerceUnsized<U>`;
+the *implementer* and *`CoerceUnsized` type parameter* must be the same
+type. In this example, `Bar` and `Foo` (even though structurally identical)
+are *not* the same type and are rejected. Learn more about the `CoerceUnsized`
+trait and DST coercion in
+[the `CoerceUnsized` docs](../std/ops/trait.CoerceUnsized.html).
--- /dev/null
+Plugin `..` only found in rlib format, but must be available in dylib format.
+
+Erroronous code example:
+
+`rlib-plugin.rs`
+```ignore (needs-linkage-with-other-tests)
+#![crate_type = "rlib"]
+#![feature(rustc_private)]
+
+extern crate rustc_middle;
+extern crate rustc_driver;
+
+use rustc_driver::plugin::Registry;
+
+#[no_mangle]
+fn __rustc_plugin_registrar(_: &mut Registry) {}
+```
+
+`main.rs`
+```ignore (needs-linkage-with-other-tests)
+#![feature(plugin)]
+#![plugin(rlib_plugin)] // error: plugin `rlib_plugin` only found in rlib
+ // format, but must be available in dylib
+
+fn main() {}
+```
+
+The compiler exposes a plugin interface to allow altering the compile process
+(adding lints, etc). Plugins must be defined in their own crates (similar to
+[proc-macro](../reference/procedural-macros.html) isolation) and then compiled
+and linked to another crate. Plugin crates *must* be compiled to the
+dynamically-linked dylib format, and not the statically-linked rlib format.
+Learn more about different output types in
+[this section](../reference/linkage.html) of the Rust reference.
+
+This error is easily fixed by recompiling the plugin crate in the dylib format.
--- /dev/null
+Found possibly newer version of crate `..` which `..` depends on.
+
+Consider these erroneous files:
+
+`a1.rs`
+```ignore (needs-linkage-with-other-tests)
+#![crate_name = "a"]
+
+pub fn foo<T>() {}
+```
+
+`a2.rs`
+```ignore (needs-linkage-with-other-tests)
+#![crate_name = "a"]
+
+pub fn foo<T>() {
+ println!("foo<T>()");
+}
+```
+
+`b.rs`
+```ignore (needs-linkage-with-other-tests)
+#![crate_name = "b"]
+
+extern crate a; // linked with `a1.rs`
+
+pub fn foo() {
+ a::foo::<isize>();
+}
+```
+
+`main.rs`
+```ignore (needs-linkage-with-other-tests)
+extern crate a; // linked with `a2.rs`
+extern crate b; // error: found possibly newer version of crate `a` which `b`
+ // depends on
+
+fn main() {}
+```
+
+The dependency graph of this program can be represented as follows:
+```text
+ crate `main`
+ |
+ +-------------+
+ | |
+ | v
+depends: | crate `b`
+ `a` v1 | |
+ | | depends:
+ | | `a` v2
+ v |
+ crate `a` <------+
+```
+
+Crate `main` depends on crate `a` (version 1) and crate `b` which in turn
+depends on crate `a` (version 2); this discrepancy in versions cannot be
+reconciled. This difference in versions typically occurs when one crate is
+compiled and linked, then updated and linked to another crate. The crate
+"version" is a SVH (Strict Version Hash) of the crate in an
+implementation-specific way. Note that this error can *only* occur when
+directly compiling and linking with `rustc`; [Cargo] automatically resolves
+dependencies, without using the compiler's own dependency management that
+causes this issue.
+
+This error can be fixed by:
+ * Using [Cargo], the Rust package manager, automatically fixing this issue.
+ * Recompiling crate `a` so that both crate `b` and `main` have a uniform
+ version to depend on.
+
+[Cargo]: ../cargo/index.html
--- /dev/null
+Found `staticlib` `..` instead of `rlib` or `dylib`.
+
+Consider the following two files:
+
+`a.rs`
+```ignore (cannot-link-with-other-tests)
+#![crate_type = "staticlib"]
+
+fn foo() {}
+```
+
+`main.rs`
+```ignore (cannot-link-with-other-tests)
+extern crate a;
+
+fn main() {
+ a::foo();
+}
+```
+
+Crate `a` is compiled as a `staticlib`. A `staticlib` is a system-dependant
+library only intended for linking with non-Rust applications (C programs). Note
+that `staticlib`s include all upstream dependencies (`core`, `std`, other user
+dependencies, etc) which makes them significantly larger than `dylib`s:
+prefer `staticlib` for linking with C programs. Learn more about different
+`crate_type`s in [this section of the Reference](../reference/linkage.html).
+
+This error can be fixed by:
+ * Using [Cargo](../cargo/index.html), the Rust package manager, automatically
+ fixing this issue.
+ * Recompiling the crate as a `rlib` or `dylib`; formats suitable for Rust
+ linking.
--- /dev/null
+Inline assembly (`asm!`) is not supported on this target.
+
+Example of erroneous code:
+
+```ignore (cannot-change-target)
+// compile-flags: --target sparc64-unknown-linux-gnu
+#![no_std]
+
+use core::arch::asm;
+
+fn main() {
+ unsafe {
+ asm!(""); // error: inline assembly is not supported on this target
+ }
+}
+```
+
+The Rust compiler does not support inline assembly, with the `asm!` macro
+(previously `llvm_asm!`), for all targets. All Tier 1 targets do support this
+macro but support among Tier 2 and 3 targets is not guaranteed (even when they
+have `std` support). Note that this error is related to
+`error[E0658]: inline assembly is not stable yet on this architecture`, but
+distinct in that with `E0472` support is not planned or in progress.
+
+There is no way to easily fix this issue, however:
+ * Consider if you really need inline assembly, is there some other way to
+ achieve your goal (intrinsics, etc)?
+ * Consider writing your assembly externally, linking with it and calling it
+ from Rust.
+ * Consider contributing to <https://github.com/rust-lang/rust> and help
+ integrate support for your target!
codegen_llvm_symbol_already_defined =
symbol `{$symbol_name}` is already defined
-codegen_llvm_branch_protection_requires_aarch64 =
- -Zbranch-protection is only supported on aarch64
-
codegen_llvm_invalid_minimum_alignment =
invalid minimum global alignment: {$err}
lint_builtin_unstable_features = unstable feature
+lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op
+ .label = this function will not propagate the caller location
+
lint_builtin_unreachable_pub = unreachable `pub` {$what}
.suggestion = consider restricting its visibility
.help = or consider exporting it for use by other crates
metadata_global_alloc_required =
no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait
-metadata_alloc_func_required =
- `#[alloc_error_handler]` function required, but not found
-
-metadata_missing_alloc_error_handler =
- use `#![feature(default_alloc_error_handler)]` for a default error handler
-
metadata_no_transitive_needs_dep =
the crate `{$crate_name}` cannot depend on a crate that needs {$needs_crate_name}, but it depends on `{$deps_crate_name}`
--- /dev/null
+mir_build_unconditional_recursion = function cannot return without recursing
+ .label = cannot return without recursing
+ .help = a `loop` may express intention better if this is on purpose
+
+mir_build_unconditional_recursion_call_site_label = recursive call site
+
+mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe =
+ call to unsafe function `{$function}` is unsafe and requires unsafe block (error E0133)
+ .note = consult the function's documentation for information on how to avoid undefined behavior
+ .label = call to unsafe function
+
+mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless =
+ call to unsafe function is unsafe and requires unsafe block (error E0133)
+ .note = consult the function's documentation for information on how to avoid undefined behavior
+ .label = call to unsafe function
+
+mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe =
+ use of inline assembly is unsafe and requires unsafe block (error E0133)
+ .note = inline assembly is entirely unchecked and can cause undefined behavior
+ .label = use of inline assembly
+
+mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe =
+ initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe
+ block (error E0133)
+ .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
+ .label = initializing type with `rustc_layout_scalar_valid_range` attr
+
+mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe =
+ use of mutable static is unsafe and requires unsafe block (error E0133)
+ .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
+ .label = use of mutable static
+
+mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe =
+ use of extern static is unsafe and requires unsafe block (error E0133)
+ .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
+ .label = use of extern static
+
+mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe =
+ dereference of raw pointer is unsafe and requires unsafe block (error E0133)
+ .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+ .label = dereference of raw pointer
+
+mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe =
+ access to union field is unsafe and requires unsafe block (error E0133)
+ .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
+ .label = access to union field
+
+mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe =
+ mutation of layout constrained field is unsafe and requires unsafe block (error E0133)
+ .note = mutating layout constrained fields cannot statically be checked for valid values
+ .label = mutation of layout constrained field
+
+mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe =
+ borrow of layout constrained field with interior mutability is unsafe and requires unsafe block (error E0133)
+ .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
+ .label = borrow of layout constrained field with interior mutability
+
+mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe =
+ call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block (error E0133)
+ .note = can only be called if the required target features are available
+ .label = call to function with `#[target_feature]`
+
+mir_build_call_to_unsafe_fn_requires_unsafe =
+ call to unsafe function `{$function}` is unsafe and requires unsafe block
+ .note = consult the function's documentation for information on how to avoid undefined behavior
+ .label = call to unsafe function
+
+mir_build_call_to_unsafe_fn_requires_unsafe_nameless =
+ call to unsafe function is unsafe and requires unsafe block
+ .note = consult the function's documentation for information on how to avoid undefined behavior
+ .label = call to unsafe function
+
+mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ call to unsafe function `{$function}` is unsafe and requires unsafe function or block
+ .note = consult the function's documentation for information on how to avoid undefined behavior
+ .label = call to unsafe function
+
+mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed =
+ call to unsafe function is unsafe and requires unsafe function or block
+ .note = consult the function's documentation for information on how to avoid undefined behavior
+ .label = call to unsafe function
+
+mir_build_inline_assembly_requires_unsafe =
+ use of inline assembly is unsafe and requires unsafe block
+ .note = inline assembly is entirely unchecked and can cause undefined behavior
+ .label = use of inline assembly
+
+mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ use of inline assembly is unsafe and requires unsafe function or block
+ .note = inline assembly is entirely unchecked and can cause undefined behavior
+ .label = use of inline assembly
+
+mir_build_initializing_type_with_requires_unsafe =
+ initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe block
+ .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
+ .label = initializing type with `rustc_layout_scalar_valid_range` attr
+
+mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ initializing type with `rustc_layout_scalar_valid_range` attr is unsafe and requires unsafe function or block
+ .note = initializing a layout restricted type's field with a value outside the valid range is undefined behavior
+ .label = initializing type with `rustc_layout_scalar_valid_range` attr
+
+mir_build_mutable_static_requires_unsafe =
+ use of mutable static is unsafe and requires unsafe block
+ .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
+ .label = use of mutable static
+
+mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ use of mutable static is unsafe and requires unsafe function or block
+ .note = mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
+ .label = use of mutable static
+
+mir_build_extern_static_requires_unsafe =
+ use of extern static is unsafe and requires unsafe block
+ .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
+ .label = use of extern static
+
+mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ use of extern static is unsafe and requires unsafe function or block
+ .note = extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
+ .label = use of extern static
+
+mir_build_deref_raw_pointer_requires_unsafe =
+ dereference of raw pointer is unsafe and requires unsafe block
+ .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+ .label = dereference of raw pointer
+
+mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ dereference of raw pointer is unsafe and requires unsafe function or block
+ .note = raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
+ .label = dereference of raw pointer
+
+mir_build_union_field_requires_unsafe =
+ access to union field is unsafe and requires unsafe block
+ .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
+ .label = access to union field
+
+mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ access to union field is unsafe and requires unsafe function or block
+ .note = the field may not be properly initialized: using uninitialized data will cause undefined behavior
+ .label = access to union field
+
+mir_build_mutation_of_layout_constrained_field_requires_unsafe =
+ mutation of layout constrained field is unsafe and requires unsafe block
+ .note = mutating layout constrained fields cannot statically be checked for valid values
+ .label = mutation of layout constrained field
+
+mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ mutation of layout constrained field is unsafe and requires unsafe function or block
+ .note = mutating layout constrained fields cannot statically be checked for valid values
+ .label = mutation of layout constrained field
+
+mir_build_borrow_of_layout_constrained_field_requires_unsafe =
+ borrow of layout constrained field with interior mutability is unsafe and requires unsafe block
+ .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
+ .label = borrow of layout constrained field with interior mutability
+
+mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ borrow of layout constrained field with interior mutability is unsafe and requires unsafe function or block
+ .note = references to fields of layout constrained fields lose the constraints. Coupled with interior mutability, the field can be changed to invalid values
+ .label = borrow of layout constrained field with interior mutability
+
+mir_build_call_to_fn_with_requires_unsafe =
+ call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe block
+ .note = can only be called if the required target features are available
+ .label = call to function with `#[target_feature]`
+
+mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed =
+ call to function `{$function}` with `#[target_feature]` is unsafe and requires unsafe function or block
+ .note = can only be called if the required target features are available
+ .label = call to function with `#[target_feature]`
+
+mir_build_unused_unsafe = unnecessary `unsafe` block
+ .label = unnecessary `unsafe` block
+
+mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
+mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn
+
+mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
+ .def_note = `{$peeled_ty}` defined here
+ .type_note = the matched value is of type `{$ty}`
+ .non_exhaustive_type_note = the matched value is of type `{$ty}`, which is marked as non-exhaustive
+ .reference_note = references are always considered inhabited
+ .suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
+ .help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern
+
+mir_build_static_in_pattern = statics cannot be referenced in patterns
+
+mir_build_assoc_const_in_pattern = associated consts cannot be referenced in patterns
+
+mir_build_const_param_in_pattern = const parameters cannot be referenced in patterns
+
+mir_build_non_const_path = runtime values cannot be referenced in patterns
+
+mir_build_unreachable_pattern = unreachable pattern
+ .label = unreachable pattern
+ .catchall_label = matches any value
+
+mir_build_const_pattern_depends_on_generic_parameter =
+ constant pattern depends on a generic parameter
+
+mir_build_could_not_eval_const_pattern = could not evaluate constant pattern
+
+mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper =
+ lower range bound must be less than or equal to upper
+ .label = lower bound larger than upper bound
+ .teach_note = When matching against a range, the compiler verifies that the range is non-empty. Range patterns include both end-points, so this is equivalent to requiring the start of the range to be less than or equal to the end of the range.
+
+mir_build_lower_range_bound_must_be_less_than_upper = lower range bound must be less than upper
+
+mir_build_leading_irrefutable_let_patterns = leading irrefutable {$count ->
+ [one] pattern
+ *[other] patterns
+ } in let chain
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match
+ .help = consider moving {$count ->
+ [one] it
+ *[other] them
+ } outside of the construct
+
+mir_build_trailing_irrefutable_let_patterns = trailing irrefutable {$count ->
+ [one] pattern
+ *[other] patterns
+ } in let chain
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match
+ .help = consider moving {$count ->
+ [one] it
+ *[other] them
+ } into the body
+
+mir_build_bindings_with_variant_name =
+ pattern binding `{$ident}` is named the same as one of the variants of the type `{$ty_path}`
+ .suggestion = to match on the variant, qualify the path
+
+mir_build_irrefutable_let_patterns_generic_let = irrefutable `let` {$count ->
+ [one] pattern
+ *[other] patterns
+ }
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match, so the `let` is useless
+ .help = consider removing `let`
+
+mir_build_irrefutable_let_patterns_if_let = irrefutable `if let` {$count ->
+ [one] pattern
+ *[other] patterns
+ }
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match, so the `if let` is useless
+ .help = consider replacing the `if let` with a `let`
+
+mir_build_irrefutable_let_patterns_if_let_guard = irrefutable `if let` guard {$count ->
+ [one] pattern
+ *[other] patterns
+ }
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match, so the guard is useless
+ .help = consider removing the guard and adding a `let` inside the match arm
+
+mir_build_irrefutable_let_patterns_let_else = irrefutable `let...else` {$count ->
+ [one] pattern
+ *[other] patterns
+ }
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match, so the `else` clause is useless
+ .help = consider removing the `else` clause
+
+mir_build_irrefutable_let_patterns_while_let = irrefutable `while let` {$count ->
+ [one] pattern
+ *[other] patterns
+ }
+ .note = {$count ->
+ [one] this pattern
+ *[other] these patterns
+ } will always match, so the loop will never exit
+ .help = consider instead using a `loop {"{"} ... {"}"}` with a `let` inside it
+
+mir_build_borrow_of_moved_value = borrow of moved value
+ .label = value moved into `{$name}` here
+ .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait
+ .value_borrowed_label = value borrowed here after move
+ .suggestion = borrow this binding in the pattern to avoid moving the value
+
+mir_build_multiple_mut_borrows = cannot borrow value as mutable more than once at a time
+ .label = first mutable borrow, by `{$name}`, occurs here
+ .mutable_borrow = another mutable borrow, by `{$name_mut}`, occurs here
+ .immutable_borrow = also borrowed as immutable, by `{$name_immut}`, here
+ .moved = also moved into `{$name_moved}` here
session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored
+session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64
+
session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform
session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions
lint => "../locales/en-US/lint.ftl",
metadata => "../locales/en-US/metadata.ftl",
middle => "../locales/en-US/middle.ftl",
+ mir_build => "../locales/en-US/mir_build.ftl",
mir_dataflow => "../locales/en-US/mir_dataflow.ftl",
monomorphize => "../locales/en-US/monomorphize.ftl",
parse => "../locales/en-US/parse.ftl",
}
}
-/// A workaround for "good path" ICEs when formatting types in disables lints.
+/// A workaround for "good path" ICEs when formatting types in disabled lints.
///
/// Delays formatting until `.into(): DiagnosticMessage` is used.
pub struct DelayDm<F>(pub F);
icu_locid::Locale::try_from_bytes(lang.to_string().as_bytes()).ok()
}
-pub fn fluent_value_from_str_list_sep_by_and<'source>(
- l: Vec<Cow<'source, str>>,
-) -> FluentValue<'source> {
+pub fn fluent_value_from_str_list_sep_by_and(l: Vec<Cow<'_, str>>) -> FluentValue<'_> {
// Fluent requires 'static value here for its AnyEq usages.
#[derive(Clone, PartialEq, Debug)]
struct FluentStrListSepByAnd(Vec<String>);
// We check that the meta-variable is correctly used.
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
}
- (NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del))
+ (NestedMacroState::MacroName, TokenTree::Delimited(_, del))
if del.delim == Delimiter::Parenthesis =>
{
state = NestedMacroState::MacroNameParen;
valid,
);
}
- (NestedMacroState::MacroNameParen, &TokenTree::Delimited(_, ref del))
+ (NestedMacroState::MacroNameParen, TokenTree::Delimited(_, del))
if del.delim == Delimiter::Brace =>
{
state = NestedMacroState::Empty;
TokenTree::Sequence(sp, ref seq_rep) => {
let subfirst_owned;
let subfirst = match self.first.get(&sp.entire()) {
- Some(&Some(ref subfirst)) => subfirst,
+ Some(Some(subfirst)) => subfirst,
Some(&None) => {
subfirst_owned = self.first(&seq_rep.tts);
&subfirst_owned
// `$$` or a meta-variable is the lhs of a macro but shouldn't.
//
// For example, `macro_rules! foo { ( ${length()} ) => {} }`
-fn span_dollar_dollar_or_metavar_in_the_lhs_err<'sess>(sess: &'sess ParseSess, token: &Token) {
+fn span_dollar_dollar_or_metavar_in_the_lhs_err(sess: &ParseSess, token: &Token) {
sess.span_diagnostic
.span_err(token.span, &format!("unexpected token: {}", pprust::token_to_string(token)));
sess.span_diagnostic.span_note_without_error(
(accepted, copy_closures, "1.26.0", Some(44490), None),
/// Allows `crate` in paths.
(accepted, crate_in_paths, "1.30.0", Some(45477), None),
+ /// Allows rustc to inject a default alloc_error_handler
+ (accepted, default_alloc_error_handler, "CURRENT_RUSTC_VERSION", Some(66741), None),
/// Allows using assigning a default type to type parameters in algebraic data type definitions.
(accepted, default_type_params, "1.0.0", None, None),
/// Allows `#[deprecated]` attribute.
(active, debugger_visualizer, "1.62.0", Some(95939), None),
/// Allows declarative macros 2.0 (`macro`).
(active, decl_macro, "1.17.0", Some(39412), None),
- /// Allows rustc to inject a default alloc_error_handler
- (active, default_alloc_error_handler, "1.48.0", Some(66741), None),
/// Allows default type parameters to influence type inference.
(active, default_type_parameter_fallback, "1.3.0", Some(27336), None),
/// Allows using `#[deprecated_safe]` to deprecate the safeness of a function or trait
//! fn node_id(&'a self, n: &Nd) -> dot::Id<'a> {
//! dot::Id::new(format!("N{}", n)).unwrap()
//! }
-//! fn node_label<'b>(&'b self, n: &Nd) -> dot::LabelText<'b> {
+//! fn node_label(&self, n: &Nd) -> dot::LabelText<'_> {
//! dot::LabelText::LabelStr(self.nodes[*n].into())
//! }
//! fn edge_label<'b>(&'b self, _: &Ed) -> dot::LabelText<'b> {
/// an "item-like" to something else can be implemented by a `Vec` instead of a
/// tree or hash map.
#[derive(HashStable_Generic)]
- pub struct ItemLocalId { .. }
+ pub struct ItemLocalId {}
}
impl ItemLocalId {
// FIXME(swatinem): the following lang items are used for async lowering and
// should become obsolete eventually.
+ ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
IdentityFuture, sym::identity_future, identity_future_fn, Target::Fn, GenericRequirement::None;
+ GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
- Context, sym::Context, context, Target::Struct, GenericRequirement::None;
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
FromFrom, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
use rustc_middle::middle::stability::AllowUnstable;
use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
-use rustc_middle::ty::DynKind;
use rustc_middle::ty::GenericParamDefKind;
-use rustc_middle::ty::{
- self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeVisitable,
-};
+use rustc_middle::ty::{self, Const, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeVisitable};
+use rustc_middle::ty::{DynKind, EarlyBinder};
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
use rustc_span::edition::Edition;
use rustc_span::lev_distance::find_best_match_for_name;
self.astconv
.normalize_ty(
self.span,
- EarlyBinder(tcx.at(self.span).type_of(param.def_id))
+ tcx.at(self.span)
+ .bound_type_of(param.def_id)
.subst(tcx, substs),
)
.into()
item_segment: &hir::PathSegment<'_>,
) -> Ty<'tcx> {
let substs = self.ast_path_substs_for_ty(span, did, item_segment);
- self.normalize_ty(
- span,
- EarlyBinder(self.tcx().at(span).type_of(did)).subst(self.tcx(), substs),
- )
+ self.normalize_ty(span, self.tcx().at(span).bound_type_of(did).subst(self.tcx(), substs))
}
fn conv_object_ty_poly_trait_ref(
),
"s",
),
- [only] => (format!("{only}"), ""),
+ [only] => (only.to_string(), ""),
[] => unreachable!(),
};
let last_span = *arg_spans.last().unwrap();
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- span: Span,
) -> bool {
// We don't just accept all !needs_drop fields, due to semver concerns.
match ty.kind() {
ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
ty::Tuple(tys) => {
// allow tuples of allowed types
- tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
+ tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env))
}
ty::Array(elem, _len) => {
// Like `Copy`, we do *not* special-case length 0.
- allowed_union_field(*elem, tcx, param_env, span)
+ allowed_union_field(*elem, tcx, param_env)
}
_ => {
// Fallback case: allow `ManuallyDrop` and things that are `Copy`.
for field in &def.non_enum_variant().fields {
let field_ty = field.ty(tcx, substs);
- if !allowed_union_field(field_ty, tcx, param_env, span) {
+ if !allowed_union_field(field_ty, tcx, param_env) {
let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
// We are currently checking the type this field came from, so it must be local.
Some(Node::Field(field)) => (field.span, field.ty.span),
}
/// Check that a `static` is inhabited.
-fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
// Make sure statics are inhabited.
// Other parts of the compiler assume that there are no uninhabited places. In principle it
// would be enough to check this for `extern` statics, as statics with an initializer will
/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
/// projections that would result in "inheriting lifetimes".
-fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
+fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
let item = tcx.hir().item(id);
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item");
/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
/// in "inheriting lifetimes".
#[instrument(level = "debug", skip(tcx, span))]
-pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(super) fn check_opaque_for_inheriting_lifetimes(
+ tcx: TyCtxt<'_>,
def_id: LocalDefId,
span: Span,
) {
matches!(field.ty(tcx, substs).kind(), ty::FnPtr(..) | ty::Ref(..))
}
-fn check_static_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) {
if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() {
if match tcx.type_of(def_id).kind() {
ty::RawPtr(_) => false,
}
}
-fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
+fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
debug!(
"check_item_type(it.def_id={:?}, it.name={})",
id.owner_id,
}
#[allow(trivial_numeric_casts)]
-fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let def = tcx.adt_def(def_id);
def.destructor(tcx); // force the destructor to be evaluated
return;
}
- if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
- {
+ if let Err(_) = compare_predicate_entailment(
+ tcx,
+ impl_m,
+ impl_m_span,
+ trait_m,
+ impl_trait_ref,
+ CheckImpliedWfMode::Check,
+ ) {
return;
}
}
impl_m_span: Span,
trait_m: &ty::AssocItem,
impl_trait_ref: ty::TraitRef<'tcx>,
+ check_implied_wf: CheckImpliedWfMode,
) -> Result<(), ErrorGuaranteed> {
let trait_to_impl_substs = impl_trait_ref.substs;
let mut wf_tys = FxIndexSet::default();
- let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
+ let unnormalized_impl_sig = infcx.replace_bound_vars_with_fresh_vars(
impl_m_span,
infer::HigherRankedType,
tcx.fn_sig(impl_m.def_id),
);
+ let unnormalized_impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(unnormalized_impl_sig));
let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
- let impl_sig = ocx.normalize(&norm_cause, param_env, impl_sig);
- let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
+ let impl_fty = ocx.normalize(&norm_cause, param_env, unnormalized_impl_fty);
debug!("compare_impl_method: impl_fty={:?}", impl_fty);
let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
return Err(emitted);
}
+ if check_implied_wf == CheckImpliedWfMode::Check {
+ // We need to check that the impl's args are well-formed given
+ // the hybrid param-env (impl + trait method where-clauses).
+ ocx.register_obligation(traits::Obligation::new(
+ infcx.tcx,
+ ObligationCause::dummy(),
+ param_env,
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(unnormalized_impl_fty.into())),
+ ));
+ }
+ let emit_implied_wf_lint = || {
+ infcx.tcx.struct_span_lint_hir(
+ rustc_session::lint::builtin::IMPLIED_BOUNDS_ENTAILMENT,
+ impl_m_hir_id,
+ infcx.tcx.def_span(impl_m.def_id),
+ "impl method assumes more implied bounds than the corresponding trait method",
+ |lint| lint,
+ );
+ };
+
// Check that all obligations are satisfied by the implementation's
// version.
let errors = ocx.select_all_or_error();
if !errors.is_empty() {
- let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
- return Err(reported);
+ match check_implied_wf {
+ CheckImpliedWfMode::Check => {
+ return compare_predicate_entailment(
+ tcx,
+ impl_m,
+ impl_m_span,
+ trait_m,
+ impl_trait_ref,
+ CheckImpliedWfMode::Skip,
+ )
+ .map(|()| {
+ // If the skip-mode was successful, emit a lint.
+ emit_implied_wf_lint();
+ });
+ }
+ CheckImpliedWfMode::Skip => {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+ return Err(reported);
+ }
+ }
}
// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
- let outlives_environment = OutlivesEnvironment::with_bounds(
+ let outlives_env = OutlivesEnvironment::with_bounds(
param_env,
Some(infcx),
- infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys.clone()),
);
- infcx.check_region_obligations_and_report_errors(
- impl_m.def_id.expect_local(),
- &outlives_environment,
+ infcx.process_registered_region_obligations(
+ outlives_env.region_bound_pairs(),
+ outlives_env.param_env,
);
+ let errors = infcx.resolve_regions(&outlives_env);
+ if !errors.is_empty() {
+ // FIXME(compiler-errors): This can be simplified when IMPLIED_BOUNDS_ENTAILMENT
+ // becomes a hard error (i.e. ideally we'd just call `resolve_regions_and_report_errors`
+ match check_implied_wf {
+ CheckImpliedWfMode::Check => {
+ return compare_predicate_entailment(
+ tcx,
+ impl_m,
+ impl_m_span,
+ trait_m,
+ impl_trait_ref,
+ CheckImpliedWfMode::Skip,
+ )
+ .map(|()| {
+ // If the skip-mode was successful, emit a lint.
+ emit_implied_wf_lint();
+ });
+ }
+ CheckImpliedWfMode::Skip => {
+ if infcx.tainted_by_errors().is_none() {
+ infcx.err_ctxt().report_region_errors(impl_m.def_id.expect_local(), &errors);
+ }
+ return Err(tcx
+ .sess
+ .delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted"));
+ }
+ }
+ }
Ok(())
}
+#[derive(Debug, PartialEq, Eq)]
+enum CheckImpliedWfMode {
+ /// Checks implied well-formedness of the impl method. If it fails, we will
+ /// re-check with `Skip`, and emit a lint if it succeeds.
+ Check,
+ /// Skips checking implied well-formedness of the impl method, but will emit
+ /// a lint if the `compare_predicate_entailment` succeeded. This means that
+ /// the reason that we had failed earlier during `Check` was due to the impl
+ /// having stronger requirements than the trait.
+ Skip,
+}
+
fn compare_asyncness<'tcx>(
tcx: TyCtxt<'tcx>,
impl_m: &ty::AssocItem,
tcx.fn_sig(impl_m.def_id),
),
);
+ impl_sig.error_reported()?;
let impl_return_ty = impl_sig.output();
// Normalize the trait signature with liberated bound vars, passing it through
)
.fold_with(&mut collector);
let trait_sig = ocx.normalize(&norm_cause, param_env, unnormalized_trait_sig);
+ trait_sig.error_reported()?;
let trait_return_ty = trait_sig.output();
let wf_tys = FxIndexSet::from_iter(
}
/// Use `tcx.compare_assoc_const_impl_item_with_trait_item` instead
-pub(crate) fn raw_compare_const_impl<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(crate) fn raw_compare_const_impl(
+ tcx: TyCtxt<'_>,
(impl_const_item_def, trait_const_item_def): (LocalDefId, DefId),
) -> Result<(), ErrorGuaranteed> {
let impl_const_item = tcx.associated_item(impl_const_item_def);
sym::abort
| sym::assert_inhabited
| sym::assert_zero_valid
- | sym::assert_uninit_valid
+ | sym::assert_mem_uninitialized_valid
| sym::size_of
| sym::min_align_of
| sym::needs_drop
}
sym::rustc_peek => (1, vec![param(0)], param(0)),
sym::caller_location => (0, vec![], tcx.caller_location_ty()),
- sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => {
- (1, Vec::new(), tcx.mk_unit())
- }
+ sym::assert_inhabited
+ | sym::assert_zero_valid
+ | sym::assert_mem_uninitialized_valid => (1, Vec::new(), tcx.mk_unit()),
sym::forget => (1, vec![param(0)], tcx.mk_unit()),
sym::transmute => (2, vec![param(0)], param(1)),
sym::prefetch_read_data
/// Given a `DefId` for an opaque type in return position, find its parent item's return
/// expressions.
-fn get_owner_return_paths<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn get_owner_return_paths(
+ tcx: TyCtxt<'_>,
def_id: LocalDefId,
-) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> {
+) -> Option<(LocalDefId, ReturnsVisitor<'_>)> {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let parent_id = tcx.hir().get_parent_item(hir_id).def_id;
tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| {
}
}
-fn e0307<'tcx>(tcx: TyCtxt<'tcx>, span: Span, receiver_ty: Ty<'_>) {
+fn e0307(tcx: TyCtxt<'_>, span: Span, receiver_ty: Ty<'_>) {
struct_span_err!(
tcx.sess.diagnostic(),
span,
}
}
-fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
+fn visit_implementation_of_coerce_unsized(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
// Just compute this for the side-effects, in particular reporting
tcx.at(span).coerce_unsized_info(impl_did);
}
-fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
+fn visit_implementation_of_dispatch_from_dyn(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
// entire graph when there are many connected regions.
rustc_index::newtype_index! {
- pub struct RegionId {
- ENCODABLE = custom
- }
+ #[custom_encodable]
+ pub struct RegionId {}
}
+
struct ConnectedRegion {
idents: SmallVec<[Symbol; 8]>,
impl_blocks: FxHashSet<usize>,
ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()),
_ => ty,
};
- let this = "this".to_string();
- let (ty, postfix) = match &ty.kind() {
- ty::Slice(_) => (this, " because slices are always foreign"),
- ty::Array(..) => (this, " because arrays are always foreign"),
- ty::Tuple(..) => (this, " because tuples are always foreign"),
+ let msg = |ty: &str, postfix: &str| {
+ format!("{ty} is not defined in the current crate{postfix}")
+ };
+ let this = |name: &str| msg("this", &format!(" because {name} are always foreign"));
+ let msg = match &ty.kind() {
+ ty::Slice(_) => this("slices"),
+ ty::Array(..) => this("arrays"),
+ ty::Tuple(..) => this("tuples"),
+ ty::Alias(ty::Opaque, ..) => {
+ "type alias impl trait is treated as if it were foreign, \
+ because its hidden type could be from a foreign crate"
+ .to_string()
+ }
ty::RawPtr(ptr_ty) => {
emit_newtype_suggestion_for_raw_ptr(
full_impl_span,
&mut err,
);
- (format!("`{}`", ty), " because raw pointers are always foreign")
+ msg(&format!("`{ty}`"), " because raw pointers are always foreign")
}
- _ => (format!("`{}`", ty), ""),
+ _ => msg(&format!("`{ty}`"), ""),
};
- let msg = format!("{} is not defined in the current crate{}", ty, postfix);
if is_target_ty {
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
err.span_label(self_ty_span, &msg);
)
}
-fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
+fn adt_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtDef<'_> {
use rustc_hir::*;
let def_id = def_id.expect_local();
rl
}
-fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind {
+fn late_region_as_bound_region(tcx: TyCtxt<'_>, region: &Region) -> ty::BoundVariableKind {
match region {
Region::LateBound(_, _, def_id) => {
let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
}
}
-fn object_lifetime_default<'tcx>(tcx: TyCtxt<'tcx>, param_def_id: DefId) -> ObjectLifetimeDefault {
+fn object_lifetime_default(tcx: TyCtxt<'_>, param_def_id: DefId) -> ObjectLifetimeDefault {
debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam);
let param_def_id = param_def_id.expect_local();
let parent_def_id = tcx.local_parent(param_def_id);
}
}
-fn const_evaluatable_predicates_of<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn const_evaluatable_predicates_of(
+ tcx: TyCtxt<'_>,
def_id: LocalDefId,
-) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> {
+) -> FxIndexSet<(ty::Predicate<'_>, Span)> {
struct ConstCollector<'tcx> {
tcx: TyCtxt<'tcx>,
preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
/// ```
///
/// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`.
-fn get_impl_substs<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn get_impl_substs(
+ tcx: TyCtxt<'_>,
impl1_def_id: LocalDefId,
impl2_node: Node,
-) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> {
+) -> Option<(SubstsRef<'_>, SubstsRef<'_>)> {
let infcx = &tcx.infer_ctxt().build();
let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);
/// `global_inferred_outlives`: this is initially the empty map that
/// was generated by walking the items in the crate. This will
/// now be filled with inferred predicates.
-pub(super) fn infer_predicates<'tcx>(
- tcx: TyCtxt<'tcx>,
-) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>> {
+pub(super) fn infer_predicates(
+ tcx: TyCtxt<'_>,
+) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'_>>> {
debug!("infer_predicates");
let mut explicit_map = ExplicitPredicatesMap::new();
use crate::type_error_struct;
use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, StashKey};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, StashKey};
use rustc_hir as hir;
use rustc_hir::def::{self, CtorKind, Namespace, Res};
use rustc_hir::def_id::DefId;
}
ty::FnPtr(sig) => (sig, None),
_ => {
+ for arg in arg_exprs {
+ self.check_expr(arg);
+ }
+
if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = &callee_expr.kind
&& let [segment] = path.segments
&& let Some(mut diag) = self
}
}
- self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
-
- // This is the "default" function signature, used in case of error.
- // In that case, we check each argument against "error" in order to
- // set up all the node type bindings.
- (
- ty::Binder::dummy(self.tcx.mk_fn_sig(
- self.err_args(arg_exprs.len()).into_iter(),
- self.tcx.ty_error(),
- false,
- hir::Unsafety::Normal,
- abi::Abi::Rust,
- )),
- None,
- )
+ let err = self.report_invalid_callee(call_expr, callee_expr, callee_ty, arg_exprs);
+
+ return self.tcx.ty_error_with_guaranteed(err);
}
};
expected: Expectation<'tcx>,
) -> Option<Ty<'tcx>> {
if let [callee_expr, rest @ ..] = arg_exprs {
- let callee_ty = self.check_expr(callee_expr);
+ let callee_ty = self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)?;
+
// First, do a probe with `IsSuggestion(true)` to avoid emitting
// any strange errors. If it's successful, then we'll do a true
// method lookup.
callee_expr: &'tcx hir::Expr<'tcx>,
callee_ty: Ty<'tcx>,
arg_exprs: &'tcx [hir::Expr<'tcx>],
- ) {
+ ) -> ErrorGuaranteed {
let mut unit_variant = None;
if let hir::ExprKind::Path(qpath) = &callee_expr.kind
&& let Res::Def(def::DefKind::Ctor(kind, CtorKind::Const), _)
err.span_label(span, label);
}
}
- err.emit();
+ err.emit()
}
fn confirm_deferred_closure_call(
(Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast),
- (_, DynStar) | (DynStar, _) => {
+ (_, DynStar) => {
if fcx.tcx.features().dyn_star {
bug!("should be handled by `try_coerce`")
} else {
Err(CastError::IllegalCast)
}
}
+
+ (DynStar, _) => Err(CastError::IllegalCast),
}
}
vec![]
}
-fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>> {
+fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'_>> {
move |target| vec![Adjustment { kind, target }]
}
cause,
expected,
found,
- coercion_error.clone(),
+ coercion_error,
fcx,
parent_id,
expression,
cause,
expected,
found,
- coercion_error.clone(),
+ coercion_error,
fcx,
id,
expression,
cause,
expected,
found,
- coercion_error.clone(),
+ coercion_error,
);
}
}
use std::iter;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
- pub fn emit_coerce_suggestions(
+ pub fn emit_type_mismatch_suggestions(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'tcx>,
expr_ty: Ty<'tcx>,
expected: Ty<'tcx>,
expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- error: Option<TypeError<'tcx>>,
+ _error: Option<TypeError<'tcx>>,
) {
if expr_ty == expected {
return;
}
- self.annotate_expected_due_to_let_ty(err, expr, error);
-
// Use `||` to give these suggestions a precedence
let _ = self.suggest_missing_parentheses(err, expr)
+ || self.suggest_associated_const(err, expr, expected)
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
+ || self.suggest_option_to_bool(err, expr, expr_ty, expected)
|| self.suggest_compatible_variants(err, expr, expected, expr_ty)
|| self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty)
|| self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty)
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
- || self.suggest_option_to_bool(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected);
+ }
+ pub fn emit_coerce_suggestions(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ expr_ty: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+ error: Option<TypeError<'tcx>>,
+ ) {
+ if expr_ty == expected {
+ return;
+ }
+
+ self.annotate_expected_due_to_let_ty(err, expr, error);
+ self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
let expr = expr.peel_drop_temps();
let cause = self.misc(expr.span);
let expr_ty = self.resolve_vars_with_obligations(checked_ty);
- let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e.clone());
+ let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e);
let is_insufficiently_polymorphic =
matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..));
}
let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
- .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)"));
+ .then(|| " (its field is private, but it's local to this crate and its privacy can be changed)".to_string());
let sole_field_ty = sole_field.ty(self.tcx, substs);
if self.can_coerce(expr_ty, sole_field_ty) {
};
match (&expected_ty.kind(), &checked_ty.kind()) {
- (&ty::Int(ref exp), &ty::Int(ref found)) => {
+ (ty::Int(exp), ty::Int(found)) => {
let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
{
(Some(exp), Some(found)) if exp < found => (true, false),
suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
true
}
- (&ty::Uint(ref exp), &ty::Uint(ref found)) => {
+ (ty::Uint(exp), ty::Uint(found)) => {
let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width())
{
(Some(exp), Some(found)) if exp < found => (true, false),
suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible);
true
}
- (&ty::Float(ref exp), &ty::Float(ref found)) => {
+ (ty::Float(exp), ty::Float(found)) => {
if found.bit_width() < exp.bit_width() {
suggest_to_change_suffix_or_into(err, false, true);
} else if literal_is_ty_suffixed(expr) {
}
true
}
- (&ty::Float(ref exp), &ty::Uint(ref found)) => {
+ (ty::Float(exp), ty::Uint(found)) => {
// if `found` is `None` (meaning found is `usize`), don't suggest `.into()`
if exp.bit_width() > found.bit_width().unwrap_or(256) {
err.multipart_suggestion_verbose(
}
true
}
- (&ty::Float(ref exp), &ty::Int(ref found)) => {
+ (ty::Float(exp), ty::Int(found)) => {
// if `found` is `None` (meaning found is `isize`), don't suggest `.into()`
if exp.bit_width() > found.bit_width().unwrap_or(256) {
err.multipart_suggestion_verbose(
}
if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) {
- // FIXME(compiler-errors): We probably should fold some of the
- // `suggest_` functions from `emit_coerce_suggestions` into here,
- // since some of those aren't necessarily just coerce suggestions.
- let _ = self.suggest_deref_ref_or_into(
+ let _ = self.emit_type_mismatch_suggestions(
&mut err,
expr.peel_drop_temps(),
- expected_ty,
ty,
+ expected_ty,
None,
- ) || self.suggest_option_to_bool(&mut err, expr, ty, expected_ty);
+ None,
+ );
extend_err(&mut err);
err.emit();
}
// I don't use 'is_range_literal' because only double-sided, half-open ranges count.
if let ExprKind::Struct(
QPath::LangItem(LangItem::Range, ..),
- &[ref range_start, ref range_end],
+ [range_start, range_end],
_,
) = last_expr_field.expr.kind
&& let variant_field =
/// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing
/// closure as the DefId.
fn walk_captures(&mut self, closure_expr: &hir::Closure<'_>) {
- fn upvar_is_local_variable<'tcx>(
- upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>,
+ fn upvar_is_local_variable(
+ upvars: Option<&FxIndexMap<hir::HirId, hir::Upvar>>,
upvar_id: hir::HirId,
body_owner_is_closure: bool,
) -> bool {
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{
- self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, Ty, UserType,
+ self, AdtKind, CanonicalUserType, DefIdTree, GenericParamDefKind, Ty, UserType,
};
use rustc_middle::ty::{GenericArgKind, InternalSubsts, SubstsRef, UserSelfTy, UserSubsts};
use rustc_session::lint;
}
}
- /// Basically whenever we are converting from a type scheme into
- /// the fn body space, we always want to normalize associated
- /// types as well. This function combines the two.
- fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: T) -> T
- where
- T: TypeFoldable<'tcx>,
- {
- debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs);
- let value = EarlyBinder(value).subst(self.tcx, substs);
- let result = self.normalize(span, value);
- debug!("instantiate_type_scheme = {:?}", result);
- result
- }
-
- /// As `instantiate_type_scheme`, but for the bounds found in a
- /// generic type scheme.
+ /// Instantiates and normalizes the bounds for a given item
pub(in super::super) fn instantiate_bounds(
&self,
span: Span,
};
let def_id = res.def_id();
- // The things we are substituting into the type should not contain
- // escaping late-bound regions, and nor should the base type scheme.
- let ty = tcx.type_of(def_id);
-
let arg_count = GenericArgCountResult {
explicit_late_bound,
correct: if infer_args_for_err.is_empty() {
},
)
});
- assert!(!substs.has_escaping_bound_vars());
- assert!(!ty.has_escaping_bound_vars());
// First, store the "user substs" for later.
self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty);
// Substitute the values for the type parameters into the type of
// the referenced item.
- let ty_substituted = self.instantiate_type_scheme(span, &substs, ty);
+ let ty = tcx.bound_type_of(def_id);
+ assert!(!substs.has_escaping_bound_vars());
+ assert!(!ty.0.has_escaping_bound_vars());
+ let ty_substituted = self.normalize(span, ty.subst(tcx, substs));
if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty {
// In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method`
// type parameters, which we can infer by unifying the provided `Self`
// with the substituted impl type.
// This also occurs for an enum variant on a type alias.
- let ty = tcx.type_of(impl_def_id);
-
- let impl_ty = self.instantiate_type_scheme(span, &substs, ty);
+ let impl_ty = self.normalize(span, tcx.bound_type_of(impl_def_id).subst(tcx, substs));
match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) {
Ok(ok) => self.register_infer_ok_obligations(ok),
Err(_) => {
use rustc_middle::ty::error::TypeError;
rustc_index::newtype_index! {
- pub(crate) struct ExpectedIdx {
- DEBUG_FORMAT = "ExpectedIdx({})",
- }
+ #[debug_format = "ExpectedIdx({})"]
+ pub(crate) struct ExpectedIdx {}
}
rustc_index::newtype_index! {
- pub(crate) struct ProvidedIdx {
- DEBUG_FORMAT = "ProvidedIdx({})",
- }
+ #[debug_format = "ProvidedIdx({})"]
+ pub(crate) struct ProvidedIdx {}
}
impl ExpectedIdx {
use rustc_middle::ty::visit::TypeVisitable;
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
use rustc_session::Session;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, Ident};
use rustc_span::{self, sym, Span};
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
"cannot use call notation; the first type parameter \
for the function trait is neither a tuple nor unit"
)
- .delay_as_bug();
+ .emit();
(self.err_args(provided_args.len()), None)
}
}
} else {
args_span
};
- labels.push((span, format!("multiple arguments are missing")));
+ labels.push((span, "multiple arguments are missing".to_string()));
suggestion_text = match suggestion_text {
SuggestionText::None | SuggestionText::Provide(_) => {
SuggestionText::Provide(true)
"()".to_string()
} else if expected_ty.is_suggestable(tcx, false) {
format!("/* {} */", expected_ty)
+ } else if let Some(fn_def_id) = fn_def_id
+ && self.tcx.def_kind(fn_def_id).is_fn_like()
+ && let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
+ && let Some(arg) = self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit)
+ && arg.name != kw::SelfLower
+ {
+ format!("/* {} */", arg.name)
} else {
"/* value */".to_string()
}
use super::FnCtxt;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
+use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{
self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
+ TypeVisitable,
};
use rustc_session::errors::ExprParenthesesNeeded;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{Span, Symbol};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::error_reporting::DefIdOrName;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
}
}
- err.multipart_suggestion_verbose(
- format!("use parentheses to call these"),
- sugg,
- applicability,
- );
+ err.multipart_suggestion_verbose("use parentheses to call these", sugg, applicability);
true
} else {
return true
}
}
- &hir::FnRetTy::Return(ref ty) => {
+ hir::FnRetTy::Return(ty) => {
// Only point to return type if the expected type is the return type, as if they
// are not, the expectation must have been caused by something else.
debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
}
let hir = self.tcx.hir();
- let cond_parent = hir.parent_iter(expr.hir_id).skip_while(|(_, node)| {
- matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
- }).next();
+ let cond_parent = hir.parent_iter(expr.hir_id).find(|(_, node)| {
+ !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(op, _, _), .. }) if op.node == hir::BinOpKind::And)
+ });
// Don't suggest:
// `let Some(_) = a.is_some() && b`
// ++++++++++
}
}
+ pub(crate) fn suggest_associated_const(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ let Some((DefKind::AssocFn, old_def_id)) = self.typeck_results.borrow().type_dependent_def(expr.hir_id) else {
+ return false;
+ };
+ let old_item_name = self.tcx.item_name(old_def_id);
+ let capitalized_name = Symbol::intern(&old_item_name.as_str().to_uppercase());
+ if old_item_name == capitalized_name {
+ return false;
+ }
+ let (item, segment) = match expr.kind {
+ hir::ExprKind::Path(QPath::Resolved(
+ Some(ty),
+ hir::Path { segments: [segment], .. },
+ ))
+ | hir::ExprKind::Path(QPath::TypeRelative(ty, segment)) => {
+ let self_ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
+ if let Ok(pick) = self.probe_for_name(
+ Mode::Path,
+ Ident::new(capitalized_name, segment.ident.span),
+ IsSuggestion(true),
+ self_ty,
+ expr.hir_id,
+ ProbeScope::TraitsInScope,
+ ) {
+ (pick.item, segment)
+ } else {
+ return false;
+ }
+ }
+ hir::ExprKind::Path(QPath::Resolved(
+ None,
+ hir::Path { segments: [.., segment], .. },
+ )) => {
+ // we resolved through some path that doesn't end in the item name,
+ // better not do a bad suggestion by accident.
+ if old_item_name != segment.ident.name {
+ return false;
+ }
+ if let Some(item) = self
+ .tcx
+ .associated_items(self.tcx.parent(old_def_id))
+ .filter_by_name_unhygienic(capitalized_name)
+ .next()
+ {
+ (*item, segment)
+ } else {
+ return false;
+ }
+ }
+ _ => return false,
+ };
+ if item.def_id == old_def_id || self.tcx.def_kind(item.def_id) != DefKind::AssocConst {
+ // Same item
+ return false;
+ }
+ let item_ty = self.tcx.type_of(item.def_id);
+ // FIXME(compiler-errors): This check is *so* rudimentary
+ if item_ty.needs_subst() {
+ return false;
+ }
+ if self.can_coerce(item_ty, expected_ty) {
+ err.span_suggestion_verbose(
+ segment.ident.span,
+ format!("try referring to the associated const `{capitalized_name}` instead",),
+ capitalized_name,
+ Applicability::MachineApplicable,
+ );
+ true
+ } else {
+ false
+ }
+ }
+
fn is_loop(&self, id: hir::HirId) -> bool {
let node = self.tcx.hir().get(id);
matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. }))
self.tcx()
.sess
.delay_span_bug(expr.span, format!("could not resolve infer vars in `{ty}`"));
+ return;
}
let ty = self.tcx().erase_regions(ty);
let m = self.tcx().parent_module(expr.hir_id).to_def_id();
/// result of `foo`. On the other hand, if `place` points to `x` then `f` will
/// be called both on the `ExprKind::Path` node that represents the expression
/// as well as the HirId of the local `x` itself.
-fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
+fn for_each_consumable(hir: Map<'_>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) {
f(place);
let node = hir.find(place.hir_id());
if let Some(Node::Expr(expr)) = node {
}
rustc_index::newtype_index! {
- pub struct PostOrderId {
- DEBUG_FORMAT = "id({})",
- }
+ #[debug_format = "id({})"]
+ pub struct PostOrderId {}
}
rustc_index::newtype_index! {
- pub struct TrackedValueIndex {
- DEBUG_FORMAT = "hidx({})",
- }
+ #[debug_format = "hidx({})"]
+ pub struct TrackedValueIndex {}
}
/// Identifies a value whose drop state we need to track.
ty::Tuple(fields) => {
let mut has_emitted = false;
let comps = match data.expr.map(|e| &e.kind) {
- Some(hir::ExprKind::Tup(comps)) => {
- debug_assert_eq!(comps.len(), fields.len());
- Some(comps)
- }
+ Some(hir::ExprKind::Tup(comps)) if comps.len() == fields.len() => Some(comps),
_ => None,
};
for (i, ty) in fields.iter().enumerate() {
));
}
-fn has_expected_num_generic_args<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn has_expected_num_generic_args(
+ tcx: TyCtxt<'_>,
trait_did: Option<DefId>,
expected: usize,
) -> bool {
// Instantiate late-bound regions and substitute the trait
// parameters into the method type to get the actual method type.
//
- // N.B., instantiate late-bound regions first so that
- // `instantiate_type_scheme` can normalize associated types that
- // may reference those regions.
+ // N.B., instantiate late-bound regions before normalizing the
+ // function signature so that normalization does not need to deal
+ // with bound regions.
let fn_sig = tcx.bound_fn_sig(def_id);
let fn_sig = fn_sig.subst(self.tcx, substs);
let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig);
let entry = spanned_predicates.entry(spans);
entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p);
}
+ Some(Node::Item(hir::Item {
+ kind: hir::ItemKind::Trait(rustc_ast::ast::IsAuto::Yes, ..),
+ span: item_span,
+ ..
+ })) => {
+ tcx.sess.delay_span_bug(
+ *item_span,
+ "auto trait is invoked with no method error, but no error reported?",
+ );
+ }
Some(_) => unreachable!(),
None => (),
}
if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
err.span_suggestion(
span,
- &format!("there is a method with a similar name",),
+ "there is a method with a similar name",
lev_candidate.name,
Applicability::MaybeIncorrect,
);
}
/// Dereferences a single level of immutable referencing.
-fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
+fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> {
match ty.kind() {
ty::Ref(_, ty, hir::Mutability::Not) => *ty,
_ => ty,
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
{
let ty = self.resolve_vars_if_possible(ti.expected);
- let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(&mut err, snippet.clone(), ty);
+ let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(ty);
match is_slice_or_array_or_vector.1.kind() {
ty::Adt(adt_def, _)
if self.tcx.is_diagnostic_item(sym::Option, adt_def.did())
err.emit();
}
- fn is_slice_or_array_or_vector(
- &self,
- err: &mut Diagnostic,
- snippet: String,
- ty: Ty<'tcx>,
- ) -> (bool, Ty<'tcx>) {
+ fn is_slice_or_array_or_vector(&self, ty: Ty<'tcx>) -> (bool, Ty<'tcx>) {
match ty.kind() {
ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) => {
(true, ty)
}
- ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(err, snippet, *ty),
+ ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(*ty),
ty::Slice(..) | ty::Array(..) => (true, ty),
_ => (false, ty),
}
}
/// Returns the Span of where the value with the provided HirId would be dropped
-fn drop_location_span<'tcx>(tcx: TyCtxt<'tcx>, hir_id: hir::HirId) -> Span {
+fn drop_location_span(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> Span {
let owner_id = tcx.hir().get_enclosing_scope(hir_id).unwrap();
let owner_node = tcx.hir().get(owner_id);
/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture
/// them completely.
/// - No projections are applied on top of Union ADTs, since these require unsafe blocks.
-fn restrict_precision_for_unsafe<'tcx>(
- mut place: Place<'tcx>,
+fn restrict_precision_for_unsafe(
+ mut place: Place<'_>,
mut curr_mode: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
if place.base_ty.is_unsafe_ptr() {
truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0);
}
/// - No Index projections are captured, since arrays are captured completely.
/// - No unsafe block is required to capture `place`
/// Returns the truncated place and updated capture mode.
-fn restrict_capture_precision<'tcx>(
- place: Place<'tcx>,
+fn restrict_capture_precision(
+ place: Place<'_>,
curr_mode: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode);
if place.projections.is_empty() {
}
/// Truncate deref of any reference.
-fn adjust_for_move_closure<'tcx>(
- mut place: Place<'tcx>,
+fn adjust_for_move_closure(
+ mut place: Place<'_>,
mut kind: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
if let Some(idx) = first_deref {
/// Adjust closure capture just that if taking ownership of data, only move data
/// from enclosing stack frame.
-fn adjust_for_non_move_closure<'tcx>(
- mut place: Place<'tcx>,
+fn adjust_for_non_move_closure(
+ mut place: Place<'_>,
mut kind: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let contains_deref =
place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref);
/// // it is constrained to `'a`
/// }
/// ```
-fn truncate_capture_for_optimization<'tcx>(
- mut place: Place<'tcx>,
+fn truncate_capture_for_optimization(
+ mut place: Place<'_>,
mut curr_mode: ty::UpvarCapture,
-) -> (Place<'tcx>, ty::UpvarCapture) {
+) -> (Place<'_>, ty::UpvarCapture) {
let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not));
// Find the right-most deref (if any). All the projections that come after this
// dump a .txt file with just the edges:
let txt_path = format!("{}.txt", path);
let mut file = BufWriter::new(File::create(&txt_path).unwrap());
- for &(ref source, ref target) in &edges {
+ for (source, target) in &edges {
write!(file, "{:?} -> {:?}\n", source, target).unwrap();
}
}
}
}
-fn filter_edges<'q>(
- query: &'q DepGraphQuery,
- nodes: &FxHashSet<DepKind>,
-) -> Vec<(DepKind, DepKind)> {
+fn filter_edges(query: &DepGraphQuery, nodes: &FxHashSet<DepKind>) -> Vec<(DepKind, DepKind)> {
let uniq: FxHashSet<_> = query
.edges()
.into_iter()
// Allows the macro invocation below to work
use crate as rustc_index;
-rustc_macros::newtype_index!(struct MyIdx { MAX = 0xFFFF_FFFA });
+rustc_macros::newtype_index! {
+ #[max = 0xFFFF_FFFA]
+ struct MyIdx {}
+}
#[test]
fn index_size_is_optimized() {
})
}
- fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> {
+ /// FIXME: This method should only be used for canonical queries and therefore be private.
+ ///
+ /// As the new solver does canonicalization slightly differently, this is also used there
+ /// for now. This should hopefully change fairly soon.
+ pub fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> {
self.inner
.borrow_mut()
.opaque_type_storage
let text = if br.has_name() {
format!("the lifetime `{}` as defined here", br.name)
} else {
- format!("the anonymous lifetime as defined here")
+ "the anonymous lifetime as defined here".to_string()
};
(text, sp)
}
sp = param.span;
}
let text = if name == kw::UnderscoreLifetime {
- format!("the anonymous lifetime as defined here")
+ "the anonymous lifetime as defined here".to_string()
} else {
format!("the lifetime `{}` as defined here", name)
};
);
}
- fn binding_suggestion<'tcx, S: fmt::Display>(
+ fn binding_suggestion<S: fmt::Display>(
err: &mut Diagnostic,
type_param_span: Option<(Span, bool)>,
- bound_kind: GenericKind<'tcx>,
+ bound_kind: GenericKind<'_>,
sub: S,
add_lt_sugg: Option<(Span, String)>,
) {
);
}
(Some(sub_span), Some(sup_span), _, Some(sup_symbol)) => {
- err.span_note(sub_span, format!("the lifetime defined here..."));
+ err.span_note(sub_span, "the lifetime defined here...");
err.span_note(
sup_span,
format!("...must outlive the lifetime `{sup_symbol}` defined here"),
sub_span,
format!("the lifetime `{sub_symbol}` defined here..."),
);
- err.span_note(
- sup_span,
- format!("...must outlive the lifetime defined here"),
- );
+ err.span_note(sup_span, "...must outlive the lifetime defined here");
}
(Some(sub_span), Some(sup_span), _, _) => {
- err.span_note(sub_span, format!("the lifetime defined here..."));
- err.span_note(
- sup_span,
- format!("...must outlive the lifetime defined here"),
- );
+ err.span_note(sub_span, "the lifetime defined here...");
+ err.span_note(sup_span, "...must outlive the lifetime defined here");
}
_ => {}
}
// If this empty region is from a universe that can
// name the placeholder, then the placeholder is
// larger; otherwise, the only ancestor is `'static`.
- if a_ui.can_name(placeholder.universe) { true } else { false }
+ return a_ui.can_name(placeholder.universe);
}
}
}
&self,
generic_param_scope: LocalDefId,
outlives_env: &OutlivesEnvironment<'tcx>,
- ) {
+ ) -> Option<ErrorGuaranteed> {
let errors = self.resolve_regions(outlives_env);
if let None = self.tainted_by_errors() {
// errors from silly ones.
self.report_region_errors(generic_param_scope, &errors);
}
+
+ (!errors.is_empty()).then(|| {
+ self.tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted")
+ })
}
// [Note-Type-error-reporting]
) -> TypeTrace<'tcx> {
TypeTrace {
cause: cause.clone(),
- values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+ values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b)),
}
}
};
use crate::traits::{ObligationCause, ObligationCauseCode};
use rustc_data_structures::undo_log::UndoLogs;
+use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::mir::ConstraintCategory;
&self,
generic_param_scope: LocalDefId,
outlives_env: &OutlivesEnvironment<'tcx>,
- ) {
+ ) -> Option<ErrorGuaranteed> {
self.process_registered_region_obligations(
outlives_env.region_bound_pairs(),
outlives_env.param_env,
use rustc_data_structures::sso::SsoHashSet;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::GenericArg;
-use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, SubstsRef, Ty, TyCtxt};
+use rustc_middle::ty::{self, OutlivesPredicate, SubstsRef, Ty, TyCtxt};
use smallvec::smallvec;
substs: SubstsRef<'tcx>,
) -> impl Iterator<Item = ty::Region<'tcx>> {
let tcx = self.tcx;
- let bounds = tcx.item_bounds(def_id);
- trace!("{:#?}", bounds);
+ let bounds = tcx.bound_item_bounds(def_id);
+ trace!("{:#?}", bounds.0);
bounds
- .into_iter()
+ .subst_iter(tcx, substs)
.filter_map(|p| p.to_opt_type_outlives())
.filter_map(|p| p.no_bound_vars())
- .map(|b| b.1)
- .map(move |r| EarlyBinder(r).subst(tcx, substs))
+ .map(|OutlivesPredicate(_, r)| r)
}
/// Searches through a predicate list for a predicate `T: 'a`.
}
rustc_index::newtype_index! {
- struct LeakCheckNode {
- DEBUG_FORMAT = "LeakCheckNode({})"
- }
+ #[debug_format = "LeakCheckNode({})"]
+ struct LeakCheckNode {}
}
rustc_index::newtype_index! {
- struct LeakCheckScc {
- DEBUG_FORMAT = "LeakCheckScc({})"
- }
+ #[debug_format = "LeakCheckScc({})"]
+ struct LeakCheckScc {}
}
/// Represents the graph of constraints. For each `R1: R2` constraint we create
/// The combined undo log for all the various unification tables. For each change to the storage
/// for any kind of inference variable, we record an UndoLog entry in the vector here.
-#[derive(Clone)]
+#[derive(Clone, Default)]
pub(crate) struct InferCtxtUndoLogs<'tcx> {
logs: Vec<UndoLog<'tcx>>,
num_open_snapshots: usize,
}
-impl Default for InferCtxtUndoLogs<'_> {
- fn default() -> Self {
- Self { logs: Default::default(), num_open_snapshots: Default::default() }
- }
-}
-
/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any
/// action that is convertible into an UndoLog (per the From impls above).
impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx>
pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>, result: EvaluationResult) {
let mut map = self.map();
match map.get(&key) {
- Some(&ProjectionCacheEntry::NormalizedTy { ref ty, complete: _ }) => {
+ Some(ProjectionCacheEntry::NormalizedTy { ty, complete: _ }) => {
info!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty);
let mut ty = ty.clone();
if result.must_apply_considering_regions() {
pub fn register_plugins(&self) -> Result<&Query<(ast::Crate, Lrc<LintStore>)>> {
self.register_plugins.compute(|| {
- let crate_name = self.crate_name()?.peek().clone();
+ let crate_name = *self.crate_name()?.peek();
let krate = self.parse()?.take();
let empty: &(dyn Fn(&Session, &mut LintStore) + Sync + Send) = &|_, _| {};
tracked!(move_size_limit, Some(4096));
tracked!(mutable_noalias, Some(true));
tracked!(no_generate_arange_section, true);
+ tracked!(no_jump_tables, true);
tracked!(no_link, true);
tracked!(no_profiler_runtime, true);
tracked!(no_unique_section_names, true);
types::{transparent_newtype_field, CItemKind},
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
};
+use hir::IsAsync;
use rustc_ast::attr;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::visit::{FnCtxt, FnKind};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
-use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin};
+use rustc_hir::intravisit::FnKind as HirFnKind;
+use rustc_hir::{
+ Body, FnDecl, ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin,
+};
use rustc_index::vec::Idx;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
|lint| {
let suggested_ident =
format!("{}{}", binding_annot.prefix_str(), ident);
- lint.set_arg("ident", ident.clone()).span_suggestion(
+ lint.set_arg("ident", ident).span_suggestion(
fieldpat.span,
fluent::suggestion,
suggested_ident,
}
}
+declare_lint! {
+ /// The `ungated_async_fn_track_caller` lint warns when the
+ /// `#[track_caller]` attribute is used on an async function, method, or
+ /// closure, without enabling the corresponding unstable feature flag.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// #[track_caller]
+ /// async fn foo() {}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// The attribute must be used in conjunction with the
+ /// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]`
+ /// annotation will function as as no-op.
+ ///
+ /// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html
+ UNGATED_ASYNC_FN_TRACK_CALLER,
+ Warn,
+ "enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled"
+}
+
+declare_lint_pass!(
+ /// Explains corresponding feature flag must be enabled for the `#[track_caller] attribute to
+ /// do anything
+ UngatedAsyncFnTrackCaller => [UNGATED_ASYNC_FN_TRACK_CALLER]
+);
+
+impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'_>,
+ fn_kind: HirFnKind<'_>,
+ _: &'tcx FnDecl<'_>,
+ _: &'tcx Body<'_>,
+ span: Span,
+ hir_id: HirId,
+ ) {
+ if fn_kind.asyncness() == IsAsync::Async
+ && !cx.tcx.features().closure_track_caller
+ && let attrs = cx.tcx.hir().attrs(hir_id)
+ // Now, check if the function has the `#[track_caller]` attribute
+ && let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller))
+ {
+ cx.struct_span_lint(
+ UNGATED_ASYNC_FN_TRACK_CALLER,
+ attr.span,
+ fluent::lint_ungated_async_fn_track_caller,
+ |lint| {
+ lint.span_label(span, fluent::label);
+ rustc_session::parse::add_feature_diagnostics(
+ lint,
+ &cx.tcx.sess.parse_sess,
+ sym::closure_track_caller,
+ );
+ lint
+ },
+ );
+ }
+ }
+}
+
declare_lint! {
/// The `unreachable_pub` lint triggers for `pub` items not reachable from
/// the crate root.
ident.span,
fluent::lint_builtin_keyword_idents,
|lint| {
- lint.set_arg("kw", ident.clone()).set_arg("next", next_edition).span_suggestion(
+ lint.set_arg("kw", ident).set_arg("next", next_edition).span_suggestion(
ident.span,
fluent::suggestion,
format!("r#{}", ident),
return CheckLintNameResult::Tool(Ok(&lint_ids));
}
},
- Some(&Id(ref id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
+ Some(Id(id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))),
// If the lint was registered as removed or renamed by the lint tool, we don't need
// to treat tool_lints and rustc lints different and can use the code below.
_ => {}
}
}
match self.by_name.get(&complete_name) {
- Some(&Renamed(ref new_name, _)) => CheckLintNameResult::Warning(
+ Some(Renamed(new_name, _)) => CheckLintNameResult::Warning(
format!("lint `{}` has been renamed to `{}`", complete_name, new_name),
Some(new_name.to_owned()),
),
- Some(&Removed(ref reason)) => CheckLintNameResult::Warning(
+ Some(Removed(reason)) => CheckLintNameResult::Warning(
format!("lint `{}` has been removed: {}", complete_name, reason),
None,
),
CheckLintNameResult::Ok(&lint_ids)
}
},
- Some(&Id(ref id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
+ Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)),
Some(&Ignored) => CheckLintNameResult::Ok(&[]),
}
}
return CheckLintNameResult::NoLint(Some(Symbol::intern(&name_lower)));
}
// ...if not, search for lints with a similar name
- let groups = self.lint_groups.keys().copied().map(Symbol::intern);
+ // Note: find_best_match_for_name depends on the sort order of its input vector.
+ // To ensure deterministic output, sort elements of the lint_groups hash map.
+ // Also, never suggest deprecated lint groups.
+ let mut groups: Vec<_> = self
+ .lint_groups
+ .iter()
+ .filter_map(|(k, LintGroup { depr, .. })| if depr.is_none() { Some(k) } else { None })
+ .collect();
+ groups.sort();
+ let groups = groups.iter().map(|k| Symbol::intern(k));
let lints = self.lints.iter().map(|l| Symbol::intern(&l.name_lower()));
let names: Vec<Symbol> = groups.chain(lints).collect();
let suggestion = find_best_match_for_name(&names, Symbol::intern(&name_lower), None);
CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name)))
}
},
- Some(&Id(ref id)) => {
+ Some(Id(id)) => {
CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
}
Some(other) => {
);
} else {
lint.multipart_suggestion_verbose(
- format!("to check pattern in a loop use `while let`"),
+ "to check pattern in a loop use `while let`",
vec![
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
(expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
- (pat.span.between(arg.span), format!(") = ")),
+ (pat.span.between(arg.span), ") = ".to_string()),
],
Applicability::MaybeIncorrect
);
vec![
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
(expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
- (pat.span.between(arg.span), format!(") = ")),
+ (pat.span.between(arg.span), ") = ".to_string()),
],
Applicability::MaybeIncorrect,
)
}
rustc_index::newtype_index! {
+ #[custom_encodable] // we don't need encoding
struct LintStackIndex {
- ENCODABLE = custom, // we don't need encoding
- const COMMAND_LINE = 0,
+ const COMMAND_LINE = 0;
}
}
// May Depend on constants elsewhere
UnusedBrokenConst: UnusedBrokenConst,
UnstableFeatures: UnstableFeatures,
+ UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller,
ArrayIntoIter: ArrayIntoIter::default(),
DropTraitConstraints: DropTraitConstraints,
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
/// Given the span of `some_macro!(args);`, gives the span of `(` and `)`,
/// and the type of (opening) delimiter used.
-fn find_delimiters<'tcx>(cx: &LateContext<'tcx>, span: Span) -> Option<(Span, Span, char)> {
+fn find_delimiters(cx: &LateContext<'_>, span: Span) -> Option<(Span, Span, char)> {
let snippet = cx.sess().parse_sess.source_map().span_to_snippet(span).ok()?;
let (open, open_ch) = snippet.char_indices().find(|&(_, c)| "([{".contains(c))?;
let close = snippet.rfind(|c| ")]}".contains(c))?;
return;
}
- match it.kind {
+ match &it.kind {
ast::ItemKind::TyAlias(..)
| ast::ItemKind::Enum(..)
| ast::ItemKind::Struct(..)
| ast::ItemKind::Union(..) => self.check_case(cx, "type", &it.ident),
ast::ItemKind::Trait(..) => self.check_case(cx, "trait", &it.ident),
ast::ItemKind::TraitAlias(..) => self.check_case(cx, "trait alias", &it.ident),
+
+ // N.B. This check is only for inherent associated types, so that we don't lint against
+ // trait impls where we should have warned for the trait definition already.
+ ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => {
+ for it in items {
+ if let ast::AssocItemKind::Type(..) = it.kind {
+ self.check_case(cx, "associated type", &it.ident);
+ }
+ }
+ }
_ => (),
}
}
fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
// Recursively check nested UseTrees
- for &(ref tree, _) in items {
+ for (tree, _) in items {
self.check_use_tree(cx, tree, item);
}
/// struct S;
///
/// impl S {
- /// fn late<'a, 'b>(self, _: &'a u8, _: &'b u8) {}
+ /// fn late(self, _: &u8, _: &u8) {}
/// }
///
/// fn main() {
FFI_UNWIND_CALLS,
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
NAMED_ARGUMENTS_USED_POSITIONALLY,
+ IMPLIED_BOUNDS_ENTAILMENT,
]
}
Warn,
"named arguments in format used positionally"
}
+
+declare_lint! {
+ /// The `implied_bounds_entailment` lint detects cases where the arguments of an impl method
+ /// have stronger implied bounds than those from the trait method it's implementing.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// #![deny(implied_bounds_entailment)]
+ ///
+ /// trait Trait {
+ /// fn get<'s>(s: &'s str, _: &'static &'static ()) -> &'static str;
+ /// }
+ ///
+ /// impl Trait for () {
+ /// fn get<'s>(s: &'s str, _: &'static &'s ()) -> &'static str {
+ /// s
+ /// }
+ /// }
+ ///
+ /// let val = <() as Trait>::get(&String::from("blah blah blah"), &&());
+ /// println!("{}", val);
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Neither the trait method, which provides no implied bounds about `'s`, nor the impl,
+ /// requires the main function to prove that 's: 'static, but the impl method is allowed
+ /// to assume that `'s: 'static` within its own body.
+ ///
+ /// This can be used to implement an unsound API if used incorrectly.
+ pub IMPLIED_BOUNDS_ENTAILMENT,
+ Warn,
+ "impl method assumes more implied bounds than its corresponding trait method",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>",
+ reason: FutureIncompatibilityReason::FutureReleaseError,
+ };
+}
let init = match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
- .help(format!(
- "specify the slug as the first argument to the `#[diag(...)]` \
- attribute, such as `#[diag(hir_analysis_example_error)]`",
- ))
+ .help("specify the slug as the first argument to the `#[diag(...)]` \
+ attribute, such as `#[diag(hir_analysis_example_error)]`")
.emit();
return DiagnosticDeriveError::ErrorHandled.to_compile_error();
}
match builder.slug.value_ref() {
None => {
span_err(builder.span, "diagnostic slug not specified")
- .help(format!(
- "specify the slug as the first argument to the attribute, such as \
- `#[diag(compiletest_example)]`",
- ))
+ .help("specify the slug as the first argument to the attribute, such as \
+ `#[diag(compiletest_example)]`")
.emit();
DiagnosticDeriveError::ErrorHandled.to_compile_error()
}
Self { diag, f }
}
- pub(crate) fn into_tokens<'a>(self, mut structure: Structure<'a>) -> TokenStream {
+ pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream {
let implementation = {
let ast = structure.ast();
let span = ast.span().unwrap();
/// Build the mapping of field names to fields. This allows attributes to peek values from
/// other fields.
-pub(super) fn build_field_mapping<'v>(variant: &VariantInfo<'v>) -> HashMap<String, TokenStream> {
+pub(super) fn build_field_mapping(variant: &VariantInfo<'_>) -> HashMap<String, TokenStream> {
let mut fields_map = FieldMap::new();
for binding in variant.bindings() {
if let Some(ident) = &binding.ast().ident {
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::parse::*;
-use syn::punctuated::Punctuated;
use syn::*;
-mod kw {
- syn::custom_keyword!(derive);
- syn::custom_keyword!(DEBUG_FORMAT);
- syn::custom_keyword!(MAX);
- syn::custom_keyword!(ENCODABLE);
- syn::custom_keyword!(custom);
- syn::custom_keyword!(ORD_IMPL);
-}
-
-#[derive(Debug)]
-enum DebugFormat {
- // The user will provide a custom `Debug` impl, so we shouldn't generate
- // one
- Custom,
- // Use the specified format string in the generated `Debug` impl
- // By default, this is "{}"
- Format(String),
-}
-
// We parse the input and emit the output in a single step.
// This field stores the final macro output
struct Newtype(TokenStream);
impl Parse for Newtype {
fn parse(input: ParseStream<'_>) -> Result<Self> {
- let attrs = input.call(Attribute::parse_outer)?;
+ let mut attrs = input.call(Attribute::parse_outer)?;
let vis: Visibility = input.parse()?;
input.parse::<Token![struct]>()?;
let name: Ident = input.parse()?;
// Any additional `#[derive]` macro paths to apply
let mut derive_paths: Vec<Path> = Vec::new();
- let mut debug_format: Option<DebugFormat> = None;
+ let mut debug_format: Option<Lit> = None;
let mut max = None;
let mut consts = Vec::new();
let mut encodable = true;
let mut ord = true;
- // Parse an optional trailing comma
- let try_comma = || -> Result<()> {
- if body.lookahead1().peek(Token![,]) {
- body.parse::<Token![,]>()?;
- }
- Ok(())
- };
-
- if body.lookahead1().peek(Token![..]) {
- body.parse::<Token![..]>()?;
- } else {
- loop {
- if body.lookahead1().peek(kw::derive) {
- body.parse::<kw::derive>()?;
- let derives;
- bracketed!(derives in body);
- let derives: Punctuated<Path, Token![,]> =
- derives.parse_terminated(Path::parse)?;
- try_comma()?;
- derive_paths.extend(derives);
- continue;
- }
- if body.lookahead1().peek(kw::DEBUG_FORMAT) {
- body.parse::<kw::DEBUG_FORMAT>()?;
- body.parse::<Token![=]>()?;
- let new_debug_format = if body.lookahead1().peek(kw::custom) {
- body.parse::<kw::custom>()?;
- DebugFormat::Custom
- } else {
- let format_str: LitStr = body.parse()?;
- DebugFormat::Format(format_str.value())
- };
- try_comma()?;
- if let Some(old) = debug_format.replace(new_debug_format) {
- panic!("Specified multiple debug format options: {:?}", old);
- }
- continue;
- }
- if body.lookahead1().peek(kw::MAX) {
- body.parse::<kw::MAX>()?;
- body.parse::<Token![=]>()?;
- let val: Lit = body.parse()?;
- try_comma()?;
- if let Some(old) = max.replace(val) {
- panic!("Specified multiple MAX: {:?}", old);
- }
- continue;
- }
- if body.lookahead1().peek(kw::ENCODABLE) {
- body.parse::<kw::ENCODABLE>()?;
- body.parse::<Token![=]>()?;
- body.parse::<kw::custom>()?;
- try_comma()?;
+ attrs.retain(|attr| match attr.path.get_ident() {
+ Some(ident) => match &*ident.to_string() {
+ "custom_encodable" => {
encodable = false;
- continue;
+ false
}
- if body.lookahead1().peek(kw::ORD_IMPL) {
- body.parse::<kw::ORD_IMPL>()?;
- body.parse::<Token![=]>()?;
- body.parse::<kw::custom>()?;
+ "no_ord_impl" => {
ord = false;
- continue;
+ false
}
+ "max" => {
+ let Ok(Meta::NameValue(literal) )= attr.parse_meta() else {
+ panic!("#[max = NUMBER] attribute requires max value");
+ };
+
+ if let Some(old) = max.replace(literal.lit) {
+ panic!("Specified multiple max: {:?}", old);
+ }
- // We've parsed everything that the user provided, so we're done
- if body.is_empty() {
- break;
+ false
}
+ "debug_format" => {
+ let Ok(Meta::NameValue(literal) )= attr.parse_meta() else {
+ panic!("#[debug_format = FMT] attribute requires a format");
+ };
+
+ if let Some(old) = debug_format.replace(literal.lit) {
+ panic!("Specified multiple debug format options: {:?}", old);
+ }
- // Otherwise, we are parsing a user-defined constant
- let const_attrs = body.call(Attribute::parse_outer)?;
- body.parse::<Token![const]>()?;
- let const_name: Ident = body.parse()?;
- body.parse::<Token![=]>()?;
- let const_val: Expr = body.parse()?;
- try_comma()?;
- consts.push(quote! { #(#const_attrs)* #vis const #const_name: #name = #name::from_u32(#const_val); });
+ false
+ }
+ _ => true,
+ },
+ _ => true,
+ });
+
+ loop {
+ // We've parsed everything that the user provided, so we're done
+ if body.is_empty() {
+ break;
}
+
+ // Otherwise, we are parsing a user-defined constant
+ let const_attrs = body.call(Attribute::parse_outer)?;
+ body.parse::<Token![const]>()?;
+ let const_name: Ident = body.parse()?;
+ body.parse::<Token![=]>()?;
+ let const_val: Expr = body.parse()?;
+ body.parse::<Token![;]>()?;
+ consts.push(quote! { #(#const_attrs)* #vis const #const_name: #name = #name::from_u32(#const_val); });
}
- let debug_format = debug_format.unwrap_or(DebugFormat::Format("{}".to_string()));
+ let debug_format =
+ debug_format.unwrap_or_else(|| Lit::Str(LitStr::new("{}", Span::call_site())));
+
// shave off 256 indices at the end to allow space for packing these indices into enums
let max = max.unwrap_or_else(|| Lit::Int(LitInt::new("0xFFFF_FF00", Span::call_site())));
quote! {}
};
- let debug_impl = match debug_format {
- DebugFormat::Custom => quote! {},
- DebugFormat::Format(format) => {
- quote! {
- impl ::std::fmt::Debug for #name {
- fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
- write!(fmt, #format, self.as_u32())
- }
- }
+ let debug_impl = quote! {
+ impl ::std::fmt::Debug for #name {
+ fn fmt(&self, fmt: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+ write!(fmt, #debug_format, self.as_u32())
}
}
};
+
let spec_partial_eq_impl = if let Lit::Int(max) = &max {
if let Ok(max_val) = max.base10_parse::<u32>() {
quote! {
//! Validates all used crates and extern libraries and loads their metadata
use crate::errors::{
- AllocFuncRequired, ConflictingAllocErrorHandler, ConflictingGlobalAlloc, CrateNotPanicRuntime,
- GlobalAllocRequired, MissingAllocErrorHandler, NoMultipleAllocErrorHandler,
- NoMultipleGlobalAlloc, NoPanicStrategy, NoTransitiveNeedsDep, NotProfilerRuntime,
- ProfilerBuiltinsNeedsCore,
+ ConflictingAllocErrorHandler, ConflictingGlobalAlloc, CrateNotPanicRuntime,
+ GlobalAllocRequired, NoMultipleAllocErrorHandler, NoMultipleGlobalAlloc, NoPanicStrategy,
+ NoTransitiveNeedsDep, NotProfilerRuntime, ProfilerBuiltinsNeedsCore,
};
use crate::locator::{CrateError, CrateLocator, CratePaths};
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
}))
}
- fn resolve_crate<'b>(
- &'b mut self,
+ fn resolve_crate(
+ &mut self,
name: Symbol,
span: Span,
dep_kind: CrateDepKind,
} else {
// The alloc crate provides a default allocation error handler if
// one isn't specified.
- if !self.sess.features_untracked().default_alloc_error_handler {
- self.sess.emit_err(AllocFuncRequired);
- self.sess.emit_note(MissingAllocErrorHandler);
- }
self.cstore.alloc_error_handler_kind = Some(AllocatorKind::Default);
}
}
#[diag(metadata_global_alloc_required)]
pub struct GlobalAllocRequired;
-#[derive(Diagnostic)]
-#[diag(metadata_alloc_func_required)]
-pub struct AllocFuncRequired;
-
-#[derive(Diagnostic)]
-#[diag(metadata_missing_alloc_error_handler)]
-pub struct MissingAllocErrorHandler;
-
#[derive(Diagnostic)]
#[diag(metadata_no_transitive_needs_dep)]
pub struct NoTransitiveNeedsDep<'a> {
sess.emit_err(SymbolConflictsOthers { span, crate_name: root_name });
}
CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
- sess.emit_err(StableCrateIdCollision {
- span,
- crate_name0: crate_name0,
- crate_name1: crate_name1,
- });
+ sess.emit_err(StableCrateIdCollision { span, crate_name0, crate_name1 });
}
CrateError::DlOpen(s) | CrateError::DlSym(s) => {
sess.emit_err(DlError { span, err: s });
}
sess.emit_err(NoCrateWithTriple {
span,
- crate_name: crate_name,
+ crate_name,
locator_triple: locator.triple.triple(),
add_info,
found_crates,
blob: MetadataBlob,
// --- Some data pre-decoded from the metadata blob, usually for performance ---
- /// NOTE(eddyb) we pass `'static` to a `'tcx` parameter because this
- /// lifetime is only used behind `LazyValue`, `LazyArray`, or `LazyTable`, and therefore acts like a
- /// universal (`for<'tcx>`), that is paired up with whichever `TyCtxt`
- /// is being used to decode those values.
root: CrateRoot,
/// Trait impl data.
/// FIXME: Used only from queries and can use query cache,
pub(crate) fn get_root(&self) -> CrateRoot {
let slice = &self.blob()[..];
let offset = METADATA_HEADER.len();
- let pos = (((slice[offset + 0] as u32) << 24)
- | ((slice[offset + 1] as u32) << 16)
- | ((slice[offset + 2] as u32) << 8)
- | ((slice[offset + 3] as u32) << 0)) as usize;
+
+ let pos_bytes = slice[offset..][..4].try_into().unwrap();
+ let pos = u32::from_be_bytes(pos_bytes) as usize;
+
LazyValue::<CrateRoot>::from_position(NonZeroUsize::new(pos).unwrap()).decode(self)
}
writeln!(out, "hash {} stable_crate_id {:?}", root.hash, root.stable_crate_id)?;
writeln!(out, "proc_macro {:?}", root.proc_macro_data.is_some())?;
writeln!(out, "=External Dependencies=")?;
+
for (i, dep) in root.crate_deps.decode(self).enumerate() {
+ let CrateDep { name, extra_filename, hash, host_hash, kind } = dep;
+ let number = i + 1;
+
writeln!(
out,
- "{} {}{} hash {} host_hash {:?} kind {:?}",
- i + 1,
- dep.name,
- dep.extra_filename,
- dep.hash,
- dep.host_hash,
- dep.kind
+ "{number} {name}{extra_filename} hash {hash} host_hash {host_hash:?} kind {kind:?}"
)?;
}
write!(out, "\n")?;
s.emit_str(self.as_str());
}
Entry::Occupied(o) => {
- let x = o.get().clone();
+ let x = *o.get();
s.emit_u8(SYMBOL_OFFSET);
s.emit_usize(x);
}
}
}
-fn should_encode_trait_impl_trait_tys<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
+fn should_encode_trait_impl_trait_tys(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
if tcx.def_kind(def_id) != DefKind::AssocFn {
return false;
}
// the assumption that they are numbered 1 to n.
// FIXME (#2166): This is not nearly enough to support correct versioning
// but is enough to get transitive crate dependencies working.
- self.lazy_array(deps.iter().map(|&(_, ref dep)| dep))
+ self.lazy_array(deps.iter().map(|(_, dep)| dep))
}
fn encode_lib_features(&mut self) -> LazyArray<(Symbol, Option<Symbol>)> {
self.lazy_array(
exported_symbols
.iter()
- .filter(|&&(ref exported_symbol, _)| match *exported_symbol {
+ .filter(|&(exported_symbol, _)| match *exported_symbol {
ExportedSymbol::NoDefId(symbol_name) => symbol_name != metadata_symbol_name,
_ => true,
})
is_non_exhaustive: bool,
}
-#[derive(TyEncodable, TyDecodable)]
-struct GeneratorData<'tcx> {
- layout: mir::GeneratorLayout<'tcx>,
-}
-
// Tags used for encoding Spans:
const TAG_VALID_SPAN_LOCAL: u8 = 0;
const TAG_VALID_SPAN_FOREIGN: u8 = 1;
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
+# Used for intra-doc links
+rustc_error_messages = { path = "../rustc_error_messages" }
rustc_feature = { path = "../rustc_feature" }
rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
// Interned types
[] tys: rustc_type_ir::WithCachedTypeInfo<rustc_middle::ty::TyKind<'tcx>>,
[] predicates: rustc_type_ir::WithCachedTypeInfo<rustc_middle::ty::PredicateKind<'tcx>>,
- [] consts: rustc_middle::ty::ConstS<'tcx>,
+ [] consts: rustc_middle::ty::ConstData<'tcx>,
// Note that this deliberately duplicates items in the `rustc_hir::arena`,
// since we need to allocate this type on both the `rustc_hir` arena
/// DepNode. Condition (2) might not be fulfilled if a DepNode
/// refers to something from the previous compilation session that
/// has been removed.
- fn extract_def_id<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Option<DefId> {
+ fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
if tcx.fingerprint_style(self.kind) == FingerprintStyle::DefPathHash {
Some(tcx.def_path_hash_to_def_id(DefPathHash(self.hash.into()), &mut || {
panic!("Failed to extract DefId: {:?} {}", self.kind, self.hash)
use rustc_target::spec::abi::Abi;
#[inline]
-pub fn associated_body<'hir>(node: Node<'hir>) -> Option<BodyId> {
+pub fn associated_body(node: Node<'_>) -> Option<BodyId> {
match node {
Node::Item(Item {
kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
}
}
-fn is_body_owner<'hir>(node: Node<'hir>, hir_id: HirId) -> bool {
+fn is_body_owner(node: Node<'_>, hir_id: HirId) -> bool {
match associated_body(node) {
Some(b) => b.hir_id == hir_id,
None => false,
}
#[inline]
+ #[track_caller]
pub fn local_def_id(self, hir_id: HirId) -> LocalDefId {
self.opt_local_def_id(hir_id).unwrap_or_else(|| {
bug!(
}
}
+ #[track_caller]
pub fn get_parent_node(self, hir_id: HirId) -> HirId {
self.find_parent_node(hir_id)
.unwrap_or_else(|| bug!("No parent for node {:?}", self.node_to_string(hir_id)))
}
/// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found.
+ #[track_caller]
pub fn get(self, id: HirId) -> Node<'hir> {
self.find(id).unwrap_or_else(|| bug!("couldn't find hir id {} in the HIR map", id))
}
/// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found.
#[inline]
+ #[track_caller]
pub fn get_by_def_id(self, id: LocalDefId) -> Node<'hir> {
self.find_by_def_id(id).unwrap_or_else(|| bug!("couldn't find {:?} in the HIR map", id))
}
self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies[&id.hir_id.local_id]
}
+ #[track_caller]
pub fn fn_decl_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> {
if let Some(node) = self.find(hir_id) {
node.fn_decl()
}
}
+ #[track_caller]
pub fn fn_sig_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> {
if let Some(node) = self.find(hir_id) {
node.fn_sig()
}
}
+ #[track_caller]
pub fn enclosing_body_owner(self, hir_id: HirId) -> LocalDefId {
for (_, node) in self.parent_iter(hir_id) {
if let Some(body) = associated_body(node) {
/// item (possibly associated), a closure, or a `hir::AnonConst`.
pub fn body_owner(self, BodyId { hir_id }: BodyId) -> HirId {
let parent = self.get_parent_node(hir_id);
- assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id)));
+ assert!(self.find(parent).map_or(false, |n| is_body_owner(n, hir_id)), "{hir_id:?}");
parent
}
/// Given a `LocalDefId`, returns the `BodyId` associated with it,
/// if the node is a body owner, otherwise returns `None`.
pub fn maybe_body_owned_by(self, id: LocalDefId) -> Option<BodyId> {
- self.get_if_local(id.to_def_id()).map(associated_body).flatten()
+ self.find_by_def_id(id).and_then(associated_body)
}
/// Given a body owner's id, returns the `BodyId` associated with it.
+ #[track_caller]
pub fn body_owned_by(self, id: LocalDefId) -> BodyId {
self.maybe_body_owned_by(id).unwrap_or_else(|| {
let hir_id = self.local_def_id_to_hir_id(id);
let Canonical { max_universe, variables, value } = self;
Canonical { max_universe, variables, value: map_op(value) }
}
+
+ /// Allows you to map the `value` of a canonical while keeping the same set of
+ /// bound variables.
+ ///
+ /// **WARNING:** This function is very easy to mis-use, hence the name! See
+ /// the comment of [Canonical::unchecked_map] for more details.
+ pub fn unchecked_rebind<W>(self, value: W) -> Canonical<'tcx, W> {
+ let Canonical { max_universe, variables, value: _ } = self;
+ Canonical { max_universe, variables, value }
+ }
}
pub type QueryOutlivesConstraint<'tcx> = (
pub fn public_at_level(&self, id: LocalDefId) -> Option<Level> {
self.effective_vis(id).and_then(|effective_vis| {
- for level in Level::all_levels() {
- if effective_vis.is_public_at_level(level) {
- return Some(level);
- }
- }
- None
+ Level::all_levels().into_iter().find(|&level| effective_vis.is_public_at_level(level))
})
}
///
/// * The subscope with `first_statement_index == 1` is scope of `c`,
/// and thus does not include EXPR_2, but covers the `...`.
- pub struct FirstStatementIndex {
- derive [HashStable]
- }
+ #[derive(HashStable)]
+ pub struct FirstStatementIndex {}
}
// compilation error if size of `ScopeData` is not the same as a `u32`
)
}
-pub fn early_report_deprecation<'a>(
- lint_buffer: &'a mut LintBuffer,
+pub fn early_report_deprecation(
+ lint_buffer: &mut LintBuffer,
message: &str,
suggestion: Option<Symbol>,
lint: &'static Lint,
/// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32()
/// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a
/// constant value of `0`.
+ #[derive(HashStable)]
+ #[max = 0xFFFF_FFFF]
+ #[debug_format = "ExpressionOperandId({})"]
pub struct ExpressionOperandId {
- derive [HashStable]
- DEBUG_FORMAT = "ExpressionOperandId({})",
- MAX = 0xFFFF_FFFF,
}
}
}
rustc_index::newtype_index! {
- pub struct CounterValueReference {
- derive [HashStable]
- DEBUG_FORMAT = "CounterValueReference({})",
- MAX = 0xFFFF_FFFF,
- }
+ #[derive(HashStable)]
+ #[max = 0xFFFF_FFFF]
+ #[debug_format = "CounterValueReference({})"]
+ pub struct CounterValueReference {}
}
impl CounterValueReference {
/// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32()
///
/// Values descend from u32::MAX.
- pub struct InjectedExpressionId {
- derive [HashStable]
- DEBUG_FORMAT = "InjectedExpressionId({})",
- MAX = 0xFFFF_FFFF,
- }
+ #[derive(HashStable)]
+ #[max = 0xFFFF_FFFF]
+ #[debug_format = "InjectedExpressionId({})"]
+ pub struct InjectedExpressionId {}
}
rustc_index::newtype_index! {
/// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32()
///
/// Values ascend from 0.
- pub struct InjectedExpressionIndex {
- derive [HashStable]
- DEBUG_FORMAT = "InjectedExpressionIndex({})",
- MAX = 0xFFFF_FFFF,
- }
+ #[derive(HashStable)]
+ #[max = 0xFFFF_FFFF]
+ #[debug_format = "InjectedExpressionIndex({})"]
+ pub struct InjectedExpressionIndex {}
}
rustc_index::newtype_index! {
/// MappedExpressionIndex values ascend from zero, and are recalculated indexes based on their
/// array position in the LLVM coverage map "Expressions" array, which is assembled during the
/// "mapgen" process. They cannot be computed algorithmically, from the other `newtype_index`s.
- pub struct MappedExpressionIndex {
- derive [HashStable]
- DEBUG_FORMAT = "MappedExpressionIndex({})",
- MAX = 0xFFFF_FFFF,
- }
+ #[derive(HashStable)]
+ #[max = 0xFFFF_FFFF]
+ #[debug_format = "MappedExpressionIndex({})"]
+ pub struct MappedExpressionIndex {}
}
impl From<CounterValueReference> for ExpressionOperandId {
// Variables and temps
rustc_index::newtype_index! {
+ #[derive(HashStable)]
+ #[debug_format = "_{}"]
pub struct Local {
- derive [HashStable]
- DEBUG_FORMAT = "_{}",
- const RETURN_PLACE = 0,
+ const RETURN_PLACE = 0;
}
}
/// https://rustc-dev-guide.rust-lang.org/appendix/background.html#what-is-a-dataflow-analysis
/// [`CriticalCallEdges`]: ../../rustc_const_eval/transform/add_call_guards/enum.AddCallGuards.html#variant.CriticalCallEdges
/// [guide-mir]: https://rustc-dev-guide.rust-lang.org/mir/
+ #[derive(HashStable)]
+ #[debug_format = "bb{}"]
pub struct BasicBlock {
- derive [HashStable]
- DEBUG_FORMAT = "bb{}",
- const START_BLOCK = 0,
+ const START_BLOCK = 0;
}
}
/// [wrapper]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html#newtype
/// [CFG]: https://rustc-dev-guide.rust-lang.org/appendix/background.html#cfg
/// [mir-datatypes]: https://rustc-dev-guide.rust-lang.org/mir/index.html#mir-data-types
- pub struct Field {
- derive [HashStable]
- DEBUG_FORMAT = "field[{}]"
- }
+ #[derive(HashStable)]
+ #[debug_format = "field[{}]"]
+ pub struct Field {}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
// Scopes
rustc_index::newtype_index! {
+ #[derive(HashStable)]
+ #[debug_format = "scope[{}]"]
pub struct SourceScope {
- derive [HashStable]
- DEBUG_FORMAT = "scope[{}]",
- const OUTERMOST_SOURCE_SCOPE = 0,
+ const OUTERMOST_SOURCE_SCOPE = 0;
}
}
/// Finds the original HirId this MIR item came from.
/// This is necessary after MIR optimizations, as otherwise we get a HirId
/// from the function that was inlined instead of the function call site.
- pub fn lint_root<'tcx>(
+ pub fn lint_root(
self,
- source_scopes: &IndexVec<SourceScope, SourceScopeData<'tcx>>,
+ source_scopes: &IndexVec<SourceScope, SourceScopeData<'_>>,
) -> Option<HirId> {
let mut data = &source_scopes[self];
// FIXME(oli-obk): we should be able to just walk the `inlined_parent_scope`, but it
}
rustc_index::newtype_index! {
- pub struct Promoted {
- derive [HashStable]
- DEBUG_FORMAT = "promoted[{}]"
- }
+ #[derive(HashStable)]
+ #[debug_format = "promoted[{}]"]
+ pub struct Promoted {}
}
impl<'tcx> Debug for Constant<'tcx> {
dump_matched_mir_node(tcx, pass_num, pass_name, disambiguator, body, extra_data);
}
-pub fn dump_enabled<'tcx>(tcx: TyCtxt<'tcx>, pass_name: &str, def_id: DefId) -> bool {
+pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
let Some(ref filters) = tcx.sess.opts.unstable_opts.dump_mir else {
return false;
};
}
}
-fn use_verbose<'tcx>(ty: Ty<'tcx>, fn_def: bool) -> bool {
+fn use_verbose(ty: Ty<'_>, fn_def: bool) -> bool {
match *ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
// Unit type
// FIXME: this is a poor version of `pretty_print_const_value`.
let fmt_val = |val: &ConstValue<'tcx>| match val {
- ConstValue::ZeroSized => format!("<ZST>"),
+ ConstValue::ZeroSized => "<ZST>".to_string(),
ConstValue::Scalar(s) => format!("Scalar({:?})", s),
- ConstValue::Slice { .. } => format!("Slice(..)"),
- ConstValue::ByRef { .. } => format!("ByRef(..)"),
+ ConstValue::Slice { .. } => "Slice(..)".to_string(),
+ ConstValue::ByRef { .. } => "ByRef(..)".to_string(),
};
let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree {
ty::ValTree::Leaf(leaf) => format!("ValTree::Leaf({:?})", leaf),
- ty::ValTree::Branch(_) => format!("ValTree::Branch(..)"),
+ ty::ValTree::Branch(_) => "ValTree::Branch(..)".to_string(),
};
let val = match literal {
}
rustc_index::newtype_index! {
- pub struct GeneratorSavedLocal {
- derive [HashStable]
- DEBUG_FORMAT = "_{}",
- }
+ #[derive(HashStable)]
+ #[debug_format = "_{}"]
+ pub struct GeneratorSavedLocal {}
}
/// The layout of generator state.
}
/// Format a string showing the start line and column, and end line and column within a file.
-pub fn source_range_no_file<'tcx>(tcx: TyCtxt<'tcx>, span: Span) -> String {
+pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String {
let source_map = tcx.sess.source_map();
let start = source_map.lookup_char_pos(span.lo());
let end = source_map.lookup_char_pos(span.hi());
Some(SpanViewable { bb, span, id, tooltip })
}
-fn compute_block_span<'tcx>(data: &BasicBlockData<'tcx>, body_span: Span) -> Span {
+fn compute_block_span(data: &BasicBlockData<'_>, body_span: Span) -> Span {
let mut span = data.terminator().source_info.span;
for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
// Only combine Spans from the root context, and within the function's body_span.
}
#[inline(always)]
-fn write_coverage_gap<'tcx, W>(
- tcx: TyCtxt<'tcx>,
- lo: BytePos,
- hi: BytePos,
- w: &mut W,
-) -> io::Result<()>
+fn write_coverage_gap<W>(tcx: TyCtxt<'_>, lo: BytePos, hi: BytePos, w: &mut W) -> io::Result<()>
where
W: Write,
{
Ok(())
}
-fn make_html_snippet<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn make_html_snippet(
+ tcx: TyCtxt<'_>,
span: Span,
some_viewable: Option<&SpanViewable>,
) -> Option<String> {
if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) }
}
-fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span {
+fn fn_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {
let fn_decl_span = tcx.def_span(def_id);
if let Some(body_span) = hir_body(tcx, def_id).map(|hir_body| hir_body.value.span) {
if fn_decl_span.eq_ctxt(body_span) { fn_decl_span.to(body_span) } else { body_span }
}
}
-fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<&'tcx rustc_hir::Body<'tcx>> {
+fn hir_body(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&rustc_hir::Body<'_>> {
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
hir::map::associated_body(hir_node).map(|fn_body_id| tcx.hir().body(fn_body_id))
}
/// <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/> for
/// more details.
///
- /// For code that is not specific to stacked borrows, you should consider retags to read
- /// and modify the place in an opaque way.
+ /// For code that is not specific to stacked borrows, you should consider retags to read and
+ /// modify the place in an opaque way.
+ ///
+ /// Only `RetagKind::Default` and `RetagKind::FnEntry` are permitted.
Retag(RetagKind, Box<Place<'tcx>>),
/// Encodes a user's type ascription. These need to be preserved
Unreachable,
/// The behavior of this statement differs significantly before and after drop elaboration.
- /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which
- /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop
- /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an
- /// issue tracking if drop glue has any interesting semantics in addition to those of a function
- /// call?)
- ///
- /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the
- /// `Drop` will be executed if...
+ ///
+ /// After drop elaboration: `Drop` terminators are a complete nop for types that have no drop
+ /// glue. For other types, `Drop` terminators behave exactly like a call to
+ /// `core::mem::drop_in_place` with a pointer to the given place.
+ ///
+ /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically,
+ /// the `Drop` will be executed if...
///
/// **Needs clarification**: End of that sentence. This in effect should document the exact
/// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure:
/// not carry a `Ty` for `T`.)
///
/// Note that the resulting type has not been normalized.
+ #[instrument(level = "debug", skip(tcx), ret)]
pub fn field_ty(self, tcx: TyCtxt<'tcx>, f: Field) -> Ty<'tcx> {
- let answer = match self.ty.kind() {
+ match self.ty.kind() {
ty::Adt(adt_def, substs) => {
let variant_def = match self.variant_index {
None => adt_def.non_enum_variant(),
}
ty::Tuple(tys) => tys[f.index()],
_ => bug!("extracting field of non-tuple non-adt: {:?}", self),
- };
- debug!("field_ty self: {:?} f: {:?} yields: {:?}", self, f, answer);
- answer
+ }
}
/// Convenience wrapper around `projection_ty_core` for
{
match self {
&Operand::Copy(ref l) | &Operand::Move(ref l) => l.ty(local_decls, tcx).ty,
- &Operand::Constant(ref c) => c.literal.ty(),
+ Operand::Constant(c) => c.literal.ty(),
}
}
}
}
/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
-pub fn reachable_as_bitset<'tcx>(body: &Body<'tcx>) -> BitSet<BasicBlock> {
+pub fn reachable_as_bitset(body: &Body<'_>) -> BitSet<BasicBlock> {
let mut iter = preorder(body);
(&mut iter).for_each(drop);
iter.visited
/// The `Key` trait controls what types can legally be used as the key
/// for a query.
pub trait Key: Sized {
- type CacheSelector = DefaultCacheSelector<Self>;
+ // N.B. Most of the keys down below have `type CacheSelector = DefaultCacheSelector<Self>;`,
+ // it would be reasonable to use associated type defaults, to remove the duplication...
+ //
+ // ...But r-a doesn't support them yet and using a default here causes r-a to not infer
+ // return types of queries which is very annoying. Thus, until r-a support associated
+ // type defaults, plese restrain from using them here <3
+ //
+ // r-a issue: <https://github.com/rust-lang/rust-analyzer/issues/13693>
+ type CacheSelector;
/// Given an instance of this key, what crate is it referring to?
/// This is used to find the provider.
}
impl Key for () {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for ty::InstanceDef<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for ty::Instance<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for mir::interpret::GlobalId<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for mir::interpret::LitToConstInput<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for DefId {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.krate == LOCAL_CRATE
}
impl Key for ty::WithOptConstParam<LocalDefId> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for SimplifiedType {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for (DefId, DefId) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.krate == LOCAL_CRATE
}
impl<'tcx> Key for (ty::Instance<'tcx>, LocalDefId) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for (DefId, LocalDefId) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.krate == LOCAL_CRATE
}
impl Key for (LocalDefId, DefId) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for (LocalDefId, LocalDefId) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for (DefId, Option<Ident>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.krate == LOCAL_CRATE
}
impl Key for (DefId, LocalDefId, Ident) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.krate == LOCAL_CRATE
}
impl Key for (CrateNum, DefId) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0 == LOCAL_CRATE
}
impl Key for (CrateNum, SimplifiedType) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0 == LOCAL_CRATE
}
impl Key for (DefId, SimplifiedType) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.krate == LOCAL_CRATE
}
impl<'tcx> Key for SubstsRef<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (DefId, SubstsRef<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.krate == LOCAL_CRATE
}
impl<'tcx> Key for (ty::UnevaluatedConst<'tcx>, ty::UnevaluatedConst<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
(self.0).def.did.krate == LOCAL_CRATE
}
impl<'tcx> Key for (LocalDefId, DefId, SubstsRef<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.1.def_id().krate == LOCAL_CRATE
}
impl<'tcx> Key for (ty::Const<'tcx>, mir::Field) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for mir::interpret::ConstAlloc<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for ty::PolyTraitRef<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.def_id().krate == LOCAL_CRATE
}
impl<'tcx> Key for ty::PolyExistentialTraitRef<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.def_id().krate == LOCAL_CRATE
}
impl<'tcx> Key for (ty::PolyTraitRef<'tcx>, ty::PolyTraitRef<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.0.def_id().krate == LOCAL_CRATE
}
impl<'tcx> Key for GenericArg<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for mir::ConstantKind<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for ty::Const<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for Ty<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for TyAndLayout<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (Ty<'tcx>, Ty<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for &'tcx ty::List<ty::Predicate<'tcx>> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for ty::ParamEnv<'tcx> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
self.value.query_crate_is_local()
}
impl Key for Symbol {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for Option<Symbol> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
/// Canonical query goals correspond to abstract trait operations that
/// are not tied to any crate in particular.
impl<'tcx, T> Key for Canonical<'tcx, T> {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for (Symbol, u32, u32) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (ty::Predicate<'tcx>, traits::WellFormedLoc) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (ty::PolyFnSig<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
impl Key for HirId {
+ type CacheSelector = DefaultCacheSelector<Self>;
+
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
$(
newtype_index! {
#[derive(HashStable)]
- pub struct $id {
- DEBUG_FORMAT = $format
- }
+ #[debug_format = $format]
+ pub struct $id {}
}
)*
Box::new(chalk_ir::TyData { kind: ty, flags: flags })
}
- fn ty_data<'a>(self, ty: &'a Self::InternedType) -> &'a chalk_ir::TyData<Self> {
+ fn ty_data(self, ty: &Self::InternedType) -> &chalk_ir::TyData<Self> {
ty
}
Box::new(lifetime)
}
- fn lifetime_data<'a>(
- self,
- lifetime: &'a Self::InternedLifetime,
- ) -> &'a chalk_ir::LifetimeData<Self> {
+ fn lifetime_data(self, lifetime: &Self::InternedLifetime) -> &chalk_ir::LifetimeData<Self> {
&lifetime
}
Box::new(constant)
}
- fn const_data<'a>(self, constant: &'a Self::InternedConst) -> &'a chalk_ir::ConstData<Self> {
+ fn const_data(self, constant: &Self::InternedConst) -> &chalk_ir::ConstData<Self> {
&constant
}
Box::new(data)
}
- fn generic_arg_data<'a>(
- self,
- data: &'a Self::InternedGenericArg,
- ) -> &'a chalk_ir::GenericArgData<Self> {
+ fn generic_arg_data(self, data: &Self::InternedGenericArg) -> &chalk_ir::GenericArgData<Self> {
&data
}
Box::new(goal)
}
- fn goal_data<'a>(self, goal: &'a Self::InternedGoal) -> &'a chalk_ir::GoalData<Self> {
+ fn goal_data(self, goal: &Self::InternedGoal) -> &chalk_ir::GoalData<Self> {
&goal
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn goals_data<'a>(self, goals: &'a Self::InternedGoals) -> &'a [chalk_ir::Goal<Self>] {
+ fn goals_data(self, goals: &Self::InternedGoals) -> &[chalk_ir::Goal<Self>] {
goals
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn substitution_data<'a>(
+ fn substitution_data(
self,
- substitution: &'a Self::InternedSubstitution,
- ) -> &'a [chalk_ir::GenericArg<Self>] {
+ substitution: &Self::InternedSubstitution,
+ ) -> &[chalk_ir::GenericArg<Self>] {
substitution
}
Box::new(data)
}
- fn program_clause_data<'a>(
+ fn program_clause_data(
self,
- clause: &'a Self::InternedProgramClause,
- ) -> &'a chalk_ir::ProgramClauseData<Self> {
+ clause: &Self::InternedProgramClause,
+ ) -> &chalk_ir::ProgramClauseData<Self> {
&clause
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn program_clauses_data<'a>(
+ fn program_clauses_data(
self,
- clauses: &'a Self::InternedProgramClauses,
- ) -> &'a [chalk_ir::ProgramClause<Self>] {
+ clauses: &Self::InternedProgramClauses,
+ ) -> &[chalk_ir::ProgramClause<Self>] {
clauses
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn quantified_where_clauses_data<'a>(
+ fn quantified_where_clauses_data(
self,
- clauses: &'a Self::InternedQuantifiedWhereClauses,
- ) -> &'a [chalk_ir::QuantifiedWhereClause<Self>] {
+ clauses: &Self::InternedQuantifiedWhereClauses,
+ ) -> &[chalk_ir::QuantifiedWhereClause<Self>] {
clauses
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn variable_kinds_data<'a>(
+ fn variable_kinds_data(
self,
- parameter_kinds: &'a Self::InternedVariableKinds,
- ) -> &'a [chalk_ir::VariableKind<Self>] {
+ parameter_kinds: &Self::InternedVariableKinds,
+ ) -> &[chalk_ir::VariableKind<Self>] {
parameter_kinds
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn canonical_var_kinds_data<'a>(
+ fn canonical_var_kinds_data(
self,
- canonical_var_kinds: &'a Self::InternedCanonicalVarKinds,
- ) -> &'a [chalk_ir::CanonicalVarKind<Self>] {
+ canonical_var_kinds: &Self::InternedCanonicalVarKinds,
+ ) -> &[chalk_ir::CanonicalVarKind<Self>] {
canonical_var_kinds
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn constraints_data<'a>(
+ fn constraints_data(
self,
- constraints: &'a Self::InternedConstraints,
- ) -> &'a [chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
+ constraints: &Self::InternedConstraints,
+ ) -> &[chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>] {
constraints
}
data.into_iter().collect::<Result<Vec<_>, _>>()
}
- fn variances_data<'a>(
- self,
- variances: &'a Self::InternedVariances,
- ) -> &'a [chalk_ir::Variance] {
+ fn variances_data(self, variances: &Self::InternedVariances) -> &[chalk_ir::Variance] {
variances
}
}
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
-#[derive(Copy, Clone, Debug, HashStable)]
+#[derive(Copy, Clone, Debug, HashStable, PartialEq, Eq)]
pub struct NoSolution;
pub type Fallible<T> = Result<T, NoSolution>;
}
impl OverlapMode {
- pub fn get<'tcx>(tcx: TyCtxt<'tcx>, trait_id: DefId) -> OverlapMode {
+ pub fn get(tcx: TyCtxt<'_>, trait_id: DefId) -> OverlapMode {
let with_negative_coherence = tcx.features().with_negative_coherence;
let strict_coherence = tcx.has_attr(trait_id, sym::rustc_strict_coherence);
}
/// Information about the most specialized definition of an associated item.
+#[derive(Debug)]
pub struct LeafDef {
/// The associated item described by this `LeafDef`.
pub item: ty::AssocItem,
///
/// Returns `Err` if an error was reported while building the specialization
/// graph.
-pub fn ancestors<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub fn ancestors(
+ tcx: TyCtxt<'_>,
trait_def_id: DefId,
start_from_impl: DefId,
-) -> Result<Ancestors<'tcx>, ErrorGuaranteed> {
+) -> Result<Ancestors<'_>, ErrorGuaranteed> {
let specialization_graph = tcx.specialization_graph_of(trait_def_id);
if let Some(reported) = specialization_graph.has_errored {
}
}
-fn symbols_for_closure_captures<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: (LocalDefId, LocalDefId),
-) -> Vec<Symbol> {
+fn symbols_for_closure_captures(tcx: TyCtxt<'_>, def_id: (LocalDefId, LocalDefId)) -> Vec<Symbol> {
let typeck_results = tcx.typeck(def_id.0);
let captures = typeck_results.closure_min_captures_flattened(def_id.1);
captures.into_iter().map(|captured_place| captured_place.to_symbol(tcx)).collect()
impl<'tcx, D: TyDecoder<I = TyCtxt<'tcx>>> Decodable<D> for ty::Const<'tcx> {
fn decode(decoder: &mut D) -> Self {
- let consts: ty::ConstS<'tcx> = Decodable::decode(decoder);
+ let consts: ty::ConstData<'tcx> = Decodable::decode(decoder);
decoder.interner().mk_const(consts.kind, consts.ty)
}
}
pub use kind::*;
pub use valtree::*;
-/// Use this rather than `ConstS`, whenever possible.
+/// Use this rather than `ConstData, whenever possible.
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
#[rustc_pass_by_value]
-pub struct Const<'tcx>(pub Interned<'tcx, ConstS<'tcx>>);
+pub struct Const<'tcx>(pub(super) Interned<'tcx, ConstData<'tcx>>);
impl<'tcx> fmt::Debug for Const<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// Typed constant value.
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, HashStable, TyEncodable, TyDecodable)]
-pub struct ConstS<'tcx> {
+pub struct ConstData<'tcx> {
pub ty: Ty<'tcx>,
pub kind: ConstKind<'tcx>,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ConstS<'_>, 40);
+static_assert_size!(ConstData<'_>, 40);
impl<'tcx> Const<'tcx> {
#[inline]
}
}
-pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Const<'tcx> {
+pub fn const_param_default(tcx: TyCtxt<'_>, def_id: DefId) -> Const<'_> {
let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) {
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Const { default: Some(ac), .. },
}
#[inline]
- pub fn try_to_machine_usize<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
+ pub fn try_to_machine_usize(&self, tcx: TyCtxt<'_>) -> Result<u64, Size> {
Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
}
use crate::traits;
use crate::ty::query::{self, TyCtxtAt};
use crate::ty::{
- self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstS, DefIdTree, FloatTy, FloatVar,
+ self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, DefIdTree, FloatTy, FloatVar,
FloatVid, GenericParamDefKind, ImplPolarity, InferTy, IntTy, IntVar, IntVid, List, ParamConst,
ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate, PredicateKind, Region, RegionKind,
ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVar, TyVid, TypeAndMut, TypeckResults, UintTy,
predicates: InternedSet<'tcx, List<Predicate<'tcx>>>,
projs: InternedSet<'tcx, List<ProjectionKind>>,
place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
- const_: InternedSet<'tcx, ConstS<'tcx>>,
+ const_: InternedSet<'tcx, ConstData<'tcx>>,
const_allocation: InternedSet<'tcx, Allocation>,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
layout: InternedSet<'tcx, LayoutS<VariantIdx>>,
};
CommonConsts {
- unit: mk_const(ty::ConstS {
+ unit: mk_const(ty::ConstData {
kind: ty::ConstKind::Value(ty::ValTree::zst()),
ty: types.unit,
}),
#[allow(rustc::usage_of_ty_tykind)]
impl<'tcx, T> Borrow<T> for InternedInSet<'tcx, WithCachedTypeInfo<T>> {
- fn borrow<'a>(&'a self) -> &'a T {
+ fn borrow(&self) -> &T {
&self.0.internee
}
}
}
impl<'tcx, T> Borrow<[T]> for InternedInSet<'tcx, List<T>> {
- fn borrow<'a>(&'a self) -> &'a [T] {
+ fn borrow(&self) -> &[T] {
&self.0[..]
}
}
direct_interners! {
region: mk_region(RegionKind<'tcx>): Region -> Region<'tcx>,
- const_: mk_const_internal(ConstS<'tcx>): Const -> Const<'tcx>,
+ const_: mk_const_internal(ConstData<'tcx>): Const -> Const<'tcx>,
const_allocation: intern_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
layout: intern_layout(LayoutS<VariantIdx>): Layout -> Layout<'tcx>,
adt_def: intern_adt_def(AdtDefData): AdtDef -> AdtDef<'tcx>,
#[inline]
pub fn mk_const(self, kind: impl Into<ty::ConstKind<'tcx>>, ty: Ty<'tcx>) -> Const<'tcx> {
- self.mk_const_internal(ty::ConstS { kind: kind.into(), ty })
+ self.mk_const_internal(ty::ConstData { kind: kind.into(), ty })
}
#[inline]
result
}
- pub fn for_predicate<'tcx>(binder: ty::Binder<'tcx, ty::PredicateKind<'_>>) -> FlagComputation {
+ pub fn for_predicate(binder: ty::Binder<'_, ty::PredicateKind<'_>>) -> FlagComputation {
let mut result = FlagComputation::new();
result.add_predicate(binder);
result
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
- &ty::Generator(_, ref substs, _) => {
+ ty::Generator(_, substs, _) => {
let substs = substs.as_generator();
let should_remove_further_specializable =
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
&ty::Slice(tt) => self.add_ty(tt),
- &ty::RawPtr(ref m) => {
+ ty::RawPtr(m) => {
self.add_ty(m.ty);
}
}
}
- pub fn has_default(&self) -> bool {
- match self.kind {
- GenericParamDefKind::Type { has_default, .. }
- | GenericParamDefKind::Const { has_default } => has_default,
- GenericParamDefKind::Lifetime => false,
- }
- }
-
pub fn is_anonymous_lifetime(&self) -> bool {
match self.kind {
GenericParamDefKind::Lifetime => {
/// might (from a foreign exception or similar).
#[inline]
#[tracing::instrument(level = "debug", skip(tcx))]
-pub fn fn_can_unwind<'tcx>(tcx: TyCtxt<'tcx>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool {
+pub fn fn_can_unwind(tcx: TyCtxt<'_>, fn_def_id: Option<DefId>, abi: SpecAbi) -> bool {
if let Some(did) = fn_def_id {
// Special attribute for functions which can't unwind.
if tcx.codegen_fn_attrs(did).flags.contains(CodegenFnAttrFlags::NEVER_UNWIND) {
CAPTURE_STRUCT_LOCAL,
};
pub use self::consts::{
- Const, ConstInt, ConstKind, ConstS, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
+ Const, ConstData, ConstInt, ConstKind, Expr, InferConst, ScalarInt, UnevaluatedConst, ValTree,
};
pub use self::context::{
tls, CtxtInterners, DeducedParamAttrs, FreeRegionInfo, GlobalCtxt, Lift, OnDiskCache, TyCtxt,
self
}
+ #[instrument(level = "debug", skip(tcx), ret)]
+ pub fn is_coinductive(self, tcx: TyCtxt<'tcx>) -> bool {
+ match self.kind().skip_binder() {
+ ty::PredicateKind::Clause(ty::Clause::Trait(data)) => {
+ tcx.trait_is_coinductive(data.def_id())
+ }
+ ty::PredicateKind::WellFormed(_) => true,
+ _ => false,
+ }
+ }
+
/// Whether this projection can be soundly normalized.
///
/// Wf predicates must not be normalized, as normalization
&*((ptr & !TAG_MASK) as *const WithCachedTypeInfo<ty::TyKind<'tcx>>),
))),
CONST_TAG => TermKind::Const(ty::Const(Interned::new_unchecked(
- &*((ptr & !TAG_MASK) as *const ty::ConstS<'tcx>),
+ &*((ptr & !TAG_MASK) as *const ty::ConstData<'tcx>),
))),
_ => core::intrinsics::unreachable(),
}
TermKind::Const(ct) => {
// Ensure we can use the tag bits.
assert_eq!(mem::align_of_val(&*ct.0.0) & TAG_MASK, 0);
- (CONST_TAG, ct.0.0 as *const ty::ConstS<'tcx> as usize)
+ (CONST_TAG, ct.0.0 as *const ty::ConstData<'tcx> as usize)
}
};
pub term: Term<'tcx>,
}
+impl<'tcx> ProjectionPredicate<'tcx> {
+ pub fn self_ty(self) -> Ty<'tcx> {
+ self.projection_ty.self_ty()
+ }
+
+ pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> ProjectionPredicate<'tcx> {
+ Self { projection_ty: self.projection_ty.with_self_ty(tcx, self_ty), ..self }
+ }
+
+ pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
+ self.projection_ty.trait_def_id(tcx)
+ }
+
+ pub fn def_id(self) -> DefId {
+ self.projection_ty.def_id
+ }
+}
+
pub type PolyProjectionPredicate<'tcx> = Binder<'tcx, ProjectionPredicate<'tcx>>;
impl<'tcx> PolyProjectionPredicate<'tcx> {
}
}
-impl<'tcx> ProjectionPredicate<'tcx> {
- pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
- Self {
- projection_ty: tcx.mk_alias_ty(
- self.projection_ty.def_id,
- [self_ty.into()].into_iter().chain(self.projection_ty.substs.iter().skip(1)),
- ),
- ..self
- }
- }
-}
-
pub trait ToPolyTraitRef<'tcx> {
fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
}
/// `std::vec::Vec` to just `Vec`, as long as there is no other `Vec` importable anywhere.
///
/// The implementation uses similar import discovery logic to that of 'use' suggestions.
+///
+/// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths`].
fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap<DefId, Symbol> {
let mut map: FxHashMap<DefId, Symbol> = FxHashMap::default();
if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths {
+ // Trimming paths is expensive and not optimized, since we expect it to only be used for error reporting.
+ //
// For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths`
// wrapper can be used to suppress this query, in exchange for full paths being formatted.
- tcx.sess.delay_good_path_bug("trimmed_def_paths constructed");
+ tcx.sess.delay_good_path_bug(
+ "trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging",
+ );
}
let unique_symbols_rev: &mut FxHashMap<(Namespace, Symbol), Option<DefId>> =
Ok(a)
}
- (&ty::Param(ref a_p), &ty::Param(ref b_p)) if a_p.index == b_p.index => Ok(a),
+ (ty::Param(a_p), ty::Param(b_p)) if a_p.index == b_p.index => Ok(a),
(ty::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => Ok(a),
}
}
-impl fmt::Debug for ty::RegionVid {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "'_#{}r", self.index())
- }
-}
-
impl<'tcx> fmt::Debug for ty::TraitRef<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
with_no_trimmed_paths!(fmt::Display::fmt(self, f))
}
impl<'tcx> AliasTy<'tcx> {
- pub fn trait_def_id(&self, tcx: TyCtxt<'tcx>) -> DefId {
+ pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
match tcx.def_kind(self.def_id) {
DefKind::AssocTy | DefKind::AssocConst => tcx.parent(self.def_id),
DefKind::ImplTraitPlaceholder => {
/// For example, if this is a projection of `<T as StreamingIterator>::Item<'a>`,
/// then this function would return a `T: Iterator` trait reference and `['a]` as the own substs
pub fn trait_ref_and_own_substs(
- &self,
+ self,
tcx: TyCtxt<'tcx>,
) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
debug_assert!(matches!(tcx.def_kind(self.def_id), DefKind::AssocTy | DefKind::AssocConst));
/// WARNING: This will drop the substs for generic associated types
/// consider calling [Self::trait_ref_and_own_substs] to get those
/// as well.
- pub fn trait_ref(&self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+ pub fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
let def_id = self.trait_def_id(tcx);
tcx.mk_trait_ref(def_id, self.substs.truncate_to(tcx, tcx.generics_of(def_id)))
}
- pub fn self_ty(&self) -> Ty<'tcx> {
+ pub fn self_ty(self) -> Ty<'tcx> {
self.substs.type_at(0)
}
+
+ pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+ tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.substs.iter().skip(1)))
+ }
}
#[derive(Copy, Clone, Debug, TypeFoldable, TypeVisitable, Lift)]
rustc_index::newtype_index! {
/// A **region** (lifetime) **v**ariable **ID**.
#[derive(HashStable)]
- pub struct RegionVid {
- DEBUG_FORMAT = custom,
- }
+ #[debug_format = "'_#{}r"]
+ pub struct RegionVid {}
}
impl Atom for RegionVid {
rustc_index::newtype_index! {
#[derive(HashStable)]
- pub struct BoundVar { .. }
+ pub struct BoundVar {}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
GenericArgKind::Const(ct) => {
// Ensure we can use the tag bits.
assert_eq!(mem::align_of_val(&*ct.0.0) & TAG_MASK, 0);
- (CONST_TAG, ct.0.0 as *const ty::ConstS<'tcx> as usize)
+ (CONST_TAG, ct.0.0 as *const ty::ConstData<'tcx> as usize)
}
};
&*((ptr & !TAG_MASK) as *const WithCachedTypeInfo<ty::TyKind<'tcx>>),
))),
CONST_TAG => GenericArgKind::Const(ty::Const(Interned::new_unchecked(
- &*((ptr & !TAG_MASK) as *const ty::ConstS<'tcx>),
+ &*((ptr & !TAG_MASK) as *const ty::ConstData<'tcx>),
))),
_ => intrinsics::unreachable(),
}
substs.reserve(defs.params.len());
for param in &defs.params {
let kind = mk_kind(param, substs);
- assert_eq!(param.index as usize, substs.len());
+ assert_eq!(param.index as usize, substs.len(), "{substs:#?}, {defs:#?}");
substs.push(kind);
}
}
}
#[inline]
+ #[track_caller]
pub fn type_at(&self, i: usize) -> Ty<'tcx> {
if let GenericArgKind::Type(ty) = self[i].unpack() {
ty
}
#[inline]
+ #[track_caller]
pub fn region_at(&self, i: usize) -> ty::Region<'tcx> {
if let GenericArgKind::Lifetime(lt) = self[i].unpack() {
lt
}
#[inline]
+ #[track_caller]
pub fn const_at(&self, i: usize) -> ty::Const<'tcx> {
if let GenericArgKind::Const(ct) = self[i].unpack() {
ct
}
#[inline]
+ #[track_caller]
pub fn type_for_def(&self, def: &ty::GenericParamDef) -> GenericArg<'tcx> {
self.type_at(def.index as usize).into()
}
pub fn rebind<U>(&self, value: U) -> EarlyBinder<U> {
EarlyBinder(value)
}
+
+ pub fn skip_binder(self) -> T {
+ self.0
+ }
}
impl<T> EarlyBinder<Option<T>> {
}
rustc_index::newtype_index! {
+ #[derive(HashStable)]
+ #[debug_format = "UserType({})"]
pub struct UserTypeAnnotationIndex {
- derive [HashStable]
- DEBUG_FORMAT = "UserType({})",
- const START_INDEX = 0,
+ const START_INDEX = 0;
}
}
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir;
use crate::ty::layout::IntegerExt;
+use crate::ty::query::TyCtxtAt;
use crate::ty::{
self, DefIdTree, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
TypeVisitable,
}
}
+impl<'tcx> TyCtxtAt<'tcx> {
+ pub fn bound_type_of(self, def_id: DefId) -> ty::EarlyBinder<Ty<'tcx>> {
+ ty::EarlyBinder(self.type_of(def_id))
+ }
+}
+
struct OpaqueTypeExpander<'tcx> {
// Contains the DefIds of the opaque types that are currently being
// expanded. When we expand an opaque type we insert the DefId of
}
}
-pub fn is_trivially_const_drop<'tcx>(ty: Ty<'tcx>) -> bool {
+pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool {
match *ty.kind() {
ty::Bool
| ty::Char
self.has_vars_bound_at_or_above(ty::INNERMOST)
}
- #[instrument(level = "trace", ret)]
fn has_type_flags(&self, flags: TypeFlags) -> bool {
- self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags)
+ let res =
+ self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags);
+ trace!(?self, ?flags, ?res, "has_type_flags");
+ res
}
fn has_projections(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_PROJECTION)
type BreakTy = FoundFlags;
#[inline]
- #[instrument(skip(self), level = "trace", ret)]
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = t.flags();
- trace!(t.flags=?t.flags());
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
}
#[inline]
- #[instrument(skip(self), level = "trace", ret)]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = r.type_flags();
- trace!(r.flags=?flags);
if flags.intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
}
#[inline]
- #[instrument(level = "trace", ret)]
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = FlagComputation::for_const(c);
trace!(r.flags=?flags);
}
#[inline]
- #[instrument(level = "trace", ret)]
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
- debug!(
- "HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}",
- predicate,
- predicate.flags(),
- self.flags
- );
if predicate.flags().intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
let fn_ptr = Pointer::from(fn_alloc_id);
Scalar::from_pointer(fn_ptr, &tcx)
}
- VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size).into(),
- VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size).into(),
+ VtblEntry::MetadataSize => Scalar::from_uint(size, ptr_size),
+ VtblEntry::MetadataAlign => Scalar::from_uint(align, ptr_size),
VtblEntry::Vacant => continue,
VtblEntry::Method(instance) => {
// Prepare the fn ptr we write into the vtable.
+use crate::dep_graph::DepKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
use rustc_hir as hir;
use std::fmt::Write;
-impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
- fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Ty<'_> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
// SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
// FIXME: Represent the above fact in the trait system somehow.
unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(tcx.ty_error()) }
}
}
-impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
- fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo]) -> Self {
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::SymbolName<'_> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo<DepKind>]) -> Self {
// SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
// FIXME: Represent the above fact in the trait system somehow.
unsafe {
}
}
-impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
- fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo]) -> Self {
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for ty::Binder<'_, ty::FnSig<'_>> {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo<DepKind>]) -> Self {
let err = tcx.ty_error();
let arity = if let Some(frame) = stack.get(0)
- && frame.query.name == "fn_sig"
+ && frame.query.dep_kind == DepKind::fn_sig
&& let Some(def_id) = frame.query.def_id
&& let Some(node) = tcx.hir().get_if_local(def_id)
&& let Some(sig) = node.fn_sig()
}
}
-impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
- fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo]) -> Self {
+impl<'tcx> Value<TyCtxt<'tcx>, DepKind> for Representability {
+ fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<DepKind>]) -> Self {
let mut item_and_field_ids = Vec::new();
let mut representable_ids = FxHashSet::default();
for info in cycle {
- if info.query.name == "representability"
+ if info.query.dep_kind == DepKind::representability
&& let Some(field_id) = info.query.def_id
&& let Some(field_id) = field_id.as_local()
&& let Some(DefKind::Field) = info.query.def_kind
}
}
for info in cycle {
- if info.query.name == "representability_adt_ty"
+ if info.query.dep_kind == DepKind::representability_adt_ty
&& let Some(def_id) = info.query.ty_adt_id
&& let Some(def_id) = def_id.as_local()
&& !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
rustc_errors = { path = "../rustc_errors" }
rustc_hir = { path = "../rustc_hir" }
rustc_infer = { path = "../rustc_infer" }
+rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
@call("mir_retag", args) => {
Ok(StatementKind::Retag(RetagKind::Default, Box::new(self.parse_place(args[0])?)))
},
- @call("mir_retag_raw", args) => {
- Ok(StatementKind::Retag(RetagKind::Raw, Box::new(self.parse_place(args[0])?)))
- },
@call("mir_set_discriminant", args) => {
let place = self.parse_place(args[0])?;
let var = self.parse_integer_literal(args[1])? as u32;
@call("mir_goto", args) => {
Ok(TerminatorKind::Goto { target: self.parse_block(args[0])? } )
},
+ @call("mir_unreachable", _args) => {
+ Ok(TerminatorKind::Unreachable)
+ },
+ @call("mir_drop", args) => {
+ Ok(TerminatorKind::Drop {
+ place: self.parse_place(args[0])?,
+ target: self.parse_block(args[1])?,
+ unwind: None,
+ })
+ },
+ @call("mir_drop_and_replace", args) => {
+ Ok(TerminatorKind::DropAndReplace {
+ place: self.parse_place(args[0])?,
+ value: self.parse_operand(args[1])?,
+ target: self.parse_block(args[2])?,
+ unwind: None,
+ })
+ },
+ @call("mir_call", args) => {
+ let destination = self.parse_place(args[0])?;
+ let target = self.parse_block(args[1])?;
+ self.parse_call(args[2], destination, target)
+ },
ExprKind::Match { scrutinee, arms } => {
let discr = self.parse_operand(*scrutinee)?;
self.parse_match(arms, expr.span).map(|t| TerminatorKind::SwitchInt { discr, targets: t })
let Some((otherwise, rest)) = arms.split_last() else {
return Err(ParseError {
span,
- item_description: format!("no arms"),
+ item_description: "no arms".to_string(),
expected: "at least one arm".to_string(),
})
};
Ok(SwitchTargets::new(values.into_iter().zip(targets), otherwise))
}
+ fn parse_call(
+ &self,
+ expr_id: ExprId,
+ destination: Place<'tcx>,
+ target: BasicBlock,
+ ) -> PResult<TerminatorKind<'tcx>> {
+ parse_by_kind!(self, expr_id, _, "function call",
+ ExprKind::Call { fun, args, from_hir_call, fn_span, .. } => {
+ let fun = self.parse_operand(*fun)?;
+ let args = args
+ .iter()
+ .map(|arg| self.parse_operand(*arg))
+ .collect::<PResult<Vec<_>>>()?;
+ Ok(TerminatorKind::Call {
+ func: fun,
+ args,
+ destination,
+ target: Some(target),
+ cleanup: None,
+ from_hir_call: *from_hir_call,
+ fn_span: *fn_span,
+ })
+ },
+ )
+ }
+
fn parse_rvalue(&self, expr_id: ExprId) -> PResult<Rvalue<'tcx>> {
parse_by_kind!(self, expr_id, _, "rvalue",
@call("mir_discriminant", args) => self.parse_place(args[0]).map(Rvalue::Discriminant),
let literal = ConstantKind::Val(ConstValue::Scalar(Scalar::Int(lit)), ty);
- Constant { span, user_ty: user_ty, literal }
+ Constant { span, user_ty, literal }
}
ExprKind::ZstLiteral { ref user_ty } => {
let user_ty = user_ty.as_ref().map(push_cuta).flatten();
let literal = ConstantKind::Val(ConstValue::ZeroSized, ty);
- Constant { span, user_ty: user_ty, literal }
+ Constant { span, user_ty, literal }
}
ExprKind::NamedConst { def_id, substs, ref user_ty } => {
let user_ty = user_ty.as_ref().map(push_cuta).flatten();
/// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
/// value to the stack.
///
- /// See #68034 for more details.
+ /// See #68304 for more details.
pub(crate) fn as_local_call_operand(
&mut self,
block: BasicBlock,
/// ProjectionElems `Downcast`, `ConstantIndex`, `Index`, or `Subslice` because those will never be
/// part of a path that is captured by a closure. We stop applying projections once we see the first
/// projection that isn't captured by a closure.
-fn convert_to_hir_projections_and_truncate_for_capture<'tcx>(
- mir_projections: &[PlaceElem<'tcx>],
+fn convert_to_hir_projections_and_truncate_for_capture(
+ mir_projections: &[PlaceElem<'_>],
) -> Vec<HirProjectionKind> {
let mut hir_projections = Vec::new();
let mut variant = None;
BindingMode::ByValue => ty::BindingMode::BindByValue(mutability),
BindingMode::ByRef(_) => ty::BindingMode::BindByReference(mutability),
};
- let local = LocalDecl::<'tcx> {
+ let local = LocalDecl {
mutability,
ty: var_ty,
user_ty: if user_ty.is_empty() { None } else { Some(Box::new(user_ty)) },
//
// FIXME(#29623) we could use PatKind::Range to rule
// things out here, in some cases.
- (
- &TestKind::SwitchInt { switch_ty: _, ref options },
- &PatKind::Constant { ref value },
- ) if is_switch_ty(match_pair.pattern.ty) => {
+ (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Constant { value })
+ if is_switch_ty(match_pair.pattern.ty) =>
+ {
let index = options.get_index_of(value).unwrap();
self.candidate_without_match_pair(match_pair_index, candidate);
Some(index)
}
- (&TestKind::SwitchInt { switch_ty: _, ref options }, &PatKind::Range(ref range)) => {
+ (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Range(range)) => {
let not_contained =
self.values_not_contained_in_range(&*range, options).unwrap_or(false);
(
&TestKind::Len { len: test_len, op: BinOp::Eq },
- &PatKind::Slice { ref prefix, ref slice, ref suffix },
+ PatKind::Slice { prefix, slice, suffix },
) => {
let pat_len = (prefix.len() + suffix.len()) as u64;
match (test_len.cmp(&pat_len), slice) {
(
&TestKind::Len { len: test_len, op: BinOp::Ge },
- &PatKind::Slice { ref prefix, ref slice, ref suffix },
+ PatKind::Slice { prefix, slice, suffix },
) => {
// the test is `$actual_len >= test_len`
let pat_len = (prefix.len() + suffix.len()) as u64;
}
}
- (&TestKind::Range(ref test), &PatKind::Range(ref pat)) => {
+ (TestKind::Range(test), PatKind::Range(pat)) => {
use std::cmp::Ordering::*;
if test == pat {
no_overlap
}
- (&TestKind::Range(ref range), &PatKind::Constant { value }) => {
+ (TestKind::Range(range), &PatKind::Constant { value }) => {
if let Some(false) = self.const_range_contains(&*range, value) {
// `value` is not contained in the testing range,
// so `value` can be matched only if this test fails.
use super::lints;
-pub(crate) fn mir_built<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(crate) fn mir_built(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx rustc_data_structures::steal::Steal<Body<'tcx>> {
+) -> &rustc_data_structures::steal::Steal<Body<'_>> {
if let Some(def) = def.try_upgrade(tcx) {
return tcx.mir_built(def);
}
}
rustc_index::newtype_index! {
- struct ScopeId { .. }
+ struct ScopeId {}
}
#[derive(Debug)]
///
/// This is required because we may still want to run MIR passes on an item
/// with type errors, but normal MIR construction can't handle that in general.
-fn construct_error<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn construct_error(
+ tcx: TyCtxt<'_>,
def: LocalDefId,
body_owner_kind: hir::BodyOwnerKind,
err: ErrorGuaranteed,
-) -> Body<'tcx> {
+) -> Body<'_> {
let span = tcx.def_span(def);
let hir_id = tcx.hir().local_def_id_to_hir_id(def);
let generator_kind = tcx.generator_kind(def);
}
rustc_index::newtype_index! {
- struct DropIdx { .. }
+ struct DropIdx {}
}
const ROOT_NODE: DropIdx = DropIdx::from_u32(0);
use crate::build::ExprCategory;
+use crate::errors::*;
use rustc_middle::thir::visit::{self, Visitor};
-use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_middle::mir::BorrowKind;
use rustc_middle::thir::*;
use rustc_span::symbol::Symbol;
use rustc_span::Span;
-use std::borrow::Cow;
use std::ops::Bound;
struct UnsafetyVisitor<'a, 'tcx> {
self.warn_unused_unsafe(
hir_id,
block_span,
- Some((self.tcx.sess.source_map().guess_head_span(enclosing_span), "block")),
+ Some(UnusedUnsafeEnclosing::Block {
+ span: self.tcx.sess.source_map().guess_head_span(enclosing_span),
+ }),
);
f(self);
} else {
hir_id,
span,
if self.unsafe_op_in_unsafe_fn_allowed() {
- self.body_unsafety.unsafe_fn_sig_span().map(|span| (span, "fn"))
+ self.body_unsafety
+ .unsafe_fn_sig_span()
+ .map(|span| UnusedUnsafeEnclosing::Function { span })
} else {
None
},
}
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
SafetyContext::UnsafeFn => {
- let (description, note) = kind.description_and_note(self.tcx);
// unsafe_op_in_unsafe_fn is disallowed
- self.tcx.struct_span_lint_hir(
- UNSAFE_OP_IN_UNSAFE_FN,
- self.hir_context,
- span,
- format!("{} is unsafe and requires unsafe block (error E0133)", description,),
- |lint| lint.span_label(span, kind.simple_description()).note(note),
- )
+ kind.emit_unsafe_op_in_unsafe_fn_lint(self.tcx, self.hir_context, span);
}
SafetyContext::Safe => {
- let (description, note) = kind.description_and_note(self.tcx);
- let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
- struct_span_err!(
- self.tcx.sess,
- span,
- E0133,
- "{} is unsafe and requires unsafe{} block",
- description,
- fn_sugg,
- )
- .span_label(span, kind.simple_description())
- .note(note)
- .emit();
+ kind.emit_requires_unsafe_err(self.tcx, span, unsafe_op_in_unsafe_fn_allowed);
}
}
}
&self,
hir_id: hir::HirId,
block_span: Span,
- enclosing_unsafe: Option<(Span, &'static str)>,
+ enclosing_unsafe: Option<UnusedUnsafeEnclosing>,
) {
let block_span = self.tcx.sess.source_map().guess_head_span(block_span);
- let msg = "unnecessary `unsafe` block";
- self.tcx.struct_span_lint_hir(UNUSED_UNSAFE, hir_id, block_span, msg, |lint| {
- lint.span_label(block_span, msg);
- if let Some((span, kind)) = enclosing_unsafe {
- lint.span_label(span, format!("because it's nested under this `unsafe` {}", kind));
- }
- lint
- });
+ self.tcx.emit_spanned_lint(
+ UNUSED_UNSAFE,
+ hir_id,
+ block_span,
+ UnusedUnsafe { span: block_span, enclosing: enclosing_unsafe },
+ );
}
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node.
use UnsafeOpKind::*;
impl UnsafeOpKind {
- pub fn simple_description(&self) -> &'static str {
- match self {
- CallToUnsafeFunction(..) => "call to unsafe function",
- UseOfInlineAssembly => "use of inline assembly",
- InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr",
- UseOfMutableStatic => "use of mutable static",
- UseOfExternStatic => "use of extern static",
- DerefOfRawPointer => "dereference of raw pointer",
- AccessToUnionField => "access to union field",
- MutationOfLayoutConstrainedField => "mutation of layout constrained field",
- BorrowOfLayoutConstrainedField => {
- "borrow of layout constrained field with interior mutability"
- }
- CallToFunctionWith(..) => "call to function with `#[target_feature]`",
- }
- }
-
- pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
+ pub fn emit_unsafe_op_in_unsafe_fn_lint(
+ &self,
+ tcx: TyCtxt<'_>,
+ hir_id: hir::HirId,
+ span: Span,
+ ) {
match self {
- CallToUnsafeFunction(did) => (
- if let Some(did) = did {
- Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did)))
- } else {
- Cow::Borrowed(self.simple_description())
+ CallToUnsafeFunction(did) if did.is_some() => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
+ span,
+ function: &tcx.def_path_str(did.unwrap()),
},
- "consult the function's documentation for information on how to avoid undefined \
- behavior",
),
- UseOfInlineAssembly => (
- Cow::Borrowed(self.simple_description()),
- "inline assembly is entirely unchecked and can cause undefined behavior",
+ CallToUnsafeFunction(..) => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless { span },
),
- InitializingTypeWith => (
- Cow::Borrowed(self.simple_description()),
- "initializing a layout restricted type's field with a value outside the valid \
- range is undefined behavior",
+ UseOfInlineAssembly => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe { span },
),
- UseOfMutableStatic => (
- Cow::Borrowed(self.simple_description()),
- "mutable statics can be mutated by multiple threads: aliasing violations or data \
- races will cause undefined behavior",
+ InitializingTypeWith => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe { span },
),
- UseOfExternStatic => (
- Cow::Borrowed(self.simple_description()),
- "extern statics are not controlled by the Rust type system: invalid data, \
- aliasing violations or data races will cause undefined behavior",
+ UseOfMutableStatic => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe { span },
),
- DerefOfRawPointer => (
- Cow::Borrowed(self.simple_description()),
- "raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
- and cause data races: all of these are undefined behavior",
+ UseOfExternStatic => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe { span },
),
- AccessToUnionField => (
- Cow::Borrowed(self.simple_description()),
- "the field may not be properly initialized: using uninitialized data will cause \
- undefined behavior",
+ DerefOfRawPointer => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe { span },
),
- MutationOfLayoutConstrainedField => (
- Cow::Borrowed(self.simple_description()),
- "mutating layout constrained fields cannot statically be checked for valid values",
+ AccessToUnionField => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe { span },
),
- BorrowOfLayoutConstrainedField => (
- Cow::Borrowed(self.simple_description()),
- "references to fields of layout constrained fields lose the constraints. Coupled \
- with interior mutability, the field can be changed to invalid values",
+ MutationOfLayoutConstrainedField => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe { span },
),
- CallToFunctionWith(did) => (
- Cow::from(format!(
- "call to function `{}` with `#[target_feature]`",
- tcx.def_path_str(*did)
- )),
- "can only be called if the required target features are available",
+ BorrowOfLayoutConstrainedField => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe { span },
+ ),
+ CallToFunctionWith(did) => tcx.emit_spanned_lint(
+ UNSAFE_OP_IN_UNSAFE_FN,
+ hir_id,
+ span,
+ UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
+ span,
+ function: &tcx.def_path_str(*did),
+ },
),
}
}
+
+ pub fn emit_requires_unsafe_err(
+ &self,
+ tcx: TyCtxt<'_>,
+ span: Span,
+ unsafe_op_in_unsafe_fn_allowed: bool,
+ ) {
+ match self {
+ CallToUnsafeFunction(did) if did.is_some() && unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ span,
+ function: &tcx.def_path_str(did.unwrap()),
+ });
+ }
+ CallToUnsafeFunction(did) if did.is_some() => {
+ tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
+ span,
+ function: &tcx.def_path_str(did.unwrap()),
+ });
+ }
+ CallToUnsafeFunction(..) if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess.emit_err(
+ CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
+ );
+ }
+ CallToUnsafeFunction(..) => {
+ tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
+ }
+ UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess
+ .emit_err(UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
+ }
+ UseOfInlineAssembly => {
+ tcx.sess.emit_err(UseOfInlineAssemblyRequiresUnsafe { span });
+ }
+ InitializingTypeWith if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess
+ .emit_err(InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
+ }
+ InitializingTypeWith => {
+ tcx.sess.emit_err(InitializingTypeWithRequiresUnsafe { span });
+ }
+ UseOfMutableStatic if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess
+ .emit_err(UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
+ }
+ UseOfMutableStatic => {
+ tcx.sess.emit_err(UseOfMutableStaticRequiresUnsafe { span });
+ }
+ UseOfExternStatic if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess
+ .emit_err(UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
+ }
+ UseOfExternStatic => {
+ tcx.sess.emit_err(UseOfExternStaticRequiresUnsafe { span });
+ }
+ DerefOfRawPointer if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess
+ .emit_err(DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
+ }
+ DerefOfRawPointer => {
+ tcx.sess.emit_err(DerefOfRawPointerRequiresUnsafe { span });
+ }
+ AccessToUnionField if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess
+ .emit_err(AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span });
+ }
+ AccessToUnionField => {
+ tcx.sess.emit_err(AccessToUnionFieldRequiresUnsafe { span });
+ }
+ MutationOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess.emit_err(
+ MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ span,
+ },
+ );
+ }
+ MutationOfLayoutConstrainedField => {
+ tcx.sess.emit_err(MutationOfLayoutConstrainedFieldRequiresUnsafe { span });
+ }
+ BorrowOfLayoutConstrainedField if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess.emit_err(
+ BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed { span },
+ );
+ }
+ BorrowOfLayoutConstrainedField => {
+ tcx.sess.emit_err(BorrowOfLayoutConstrainedFieldRequiresUnsafe { span });
+ }
+ CallToFunctionWith(did) if unsafe_op_in_unsafe_fn_allowed => {
+ tcx.sess.emit_err(CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ span,
+ function: &tcx.def_path_str(*did),
+ });
+ }
+ CallToFunctionWith(did) => {
+ tcx.sess.emit_err(CallToFunctionWithRequiresUnsafe {
+ span,
+ function: &tcx.def_path_str(*did),
+ });
+ }
+ }
+ }
}
-pub fn check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def: ty::WithOptConstParam<LocalDefId>) {
+pub fn check_unsafety(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) {
// THIR unsafeck is gated under `-Z thir-unsafeck`
if !tcx.sess.opts.unstable_opts.thir_unsafeck {
return;
visitor.visit_expr(&thir[expr]);
}
-pub(crate) fn thir_check_unsafety<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+pub(crate) fn thir_check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
tcx.thir_check_unsafety_for_const_arg(def)
} else {
}
}
-pub(crate) fn thir_check_unsafety_for_const_arg<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(crate) fn thir_check_unsafety_for_const_arg(
+ tcx: TyCtxt<'_>,
(did, param_did): (LocalDefId, DefId),
) {
check_unsafety(tcx, ty::WithOptConstParam { did, const_param_did: Some(param_did) })
--- /dev/null
+use crate::thir::pattern::MatchCheckCtxt;
+use rustc_errors::Handler;
+use rustc_errors::{
+ error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
+};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{symbol::Ident, Span};
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unconditional_recursion)]
+#[help]
+pub struct UnconditionalRecursion {
+ #[label]
+ pub span: Span,
+ #[label(mir_build_unconditional_recursion_call_site_label)]
+ pub call_sites: Vec<Span>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe<'a> {
+ #[label]
+ pub span: Span,
+ pub function: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_unsafe_fn_requires_unsafe_nameless)]
+#[note]
+pub struct UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafeNameless {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_inline_assembly_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnUseOfInlineAssemblyRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_initializing_type_with_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnInitializingTypeWithRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_mutable_static_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnUseOfMutableStaticRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_extern_static_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnUseOfExternStaticRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_deref_raw_pointer_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnDerefOfRawPointerRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_union_field_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnAccessToUnionFieldRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_mutation_of_layout_constrained_field_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnMutationOfLayoutConstrainedFieldRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_borrow_of_layout_constrained_field_requires_unsafe)]
+pub struct UnsafeOpInUnsafeFnBorrowOfLayoutConstrainedFieldRequiresUnsafe {
+ #[label]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unsafe_op_in_unsafe_fn_call_to_fn_with_requires_unsafe)]
+#[note]
+pub struct UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe<'a> {
+ #[label]
+ pub span: Span,
+ pub function: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_call_to_unsafe_fn_requires_unsafe, code = "E0133")]
+#[note]
+pub struct CallToUnsafeFunctionRequiresUnsafe<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub function: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_nameless, code = "E0133")]
+#[note]
+pub struct CallToUnsafeFunctionRequiresUnsafeNameless {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_call_to_unsafe_fn_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub function: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(
+ mir_build_call_to_unsafe_fn_requires_unsafe_nameless_unsafe_op_in_unsafe_fn_allowed,
+ code = "E0133"
+)]
+#[note]
+pub struct CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_inline_assembly_requires_unsafe, code = "E0133")]
+#[note]
+pub struct UseOfInlineAssemblyRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_inline_assembly_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct UseOfInlineAssemblyRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_initializing_type_with_requires_unsafe, code = "E0133")]
+#[note]
+pub struct InitializingTypeWithRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(
+ mir_build_initializing_type_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
+ code = "E0133"
+)]
+#[note]
+pub struct InitializingTypeWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_mutable_static_requires_unsafe, code = "E0133")]
+#[note]
+pub struct UseOfMutableStaticRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_mutable_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct UseOfMutableStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_extern_static_requires_unsafe, code = "E0133")]
+#[note]
+pub struct UseOfExternStaticRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_extern_static_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct UseOfExternStaticRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_deref_raw_pointer_requires_unsafe, code = "E0133")]
+#[note]
+pub struct DerefOfRawPointerRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_deref_raw_pointer_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct DerefOfRawPointerRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_union_field_requires_unsafe, code = "E0133")]
+#[note]
+pub struct AccessToUnionFieldRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_union_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct AccessToUnionFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_mutation_of_layout_constrained_field_requires_unsafe, code = "E0133")]
+#[note]
+pub struct MutationOfLayoutConstrainedFieldRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(
+ mir_build_mutation_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
+ code = "E0133"
+)]
+#[note]
+pub struct MutationOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_borrow_of_layout_constrained_field_requires_unsafe, code = "E0133")]
+#[note]
+pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafe {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(
+ mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe_fn_allowed,
+ code = "E0133"
+)]
+#[note]
+pub struct BorrowOfLayoutConstrainedFieldRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_call_to_fn_with_requires_unsafe, code = "E0133")]
+#[note]
+pub struct CallToFunctionWithRequiresUnsafe<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub function: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_call_to_fn_with_requires_unsafe_unsafe_op_in_unsafe_fn_allowed, code = "E0133")]
+#[note]
+pub struct CallToFunctionWithRequiresUnsafeUnsafeOpInUnsafeFnAllowed<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub function: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unused_unsafe)]
+pub struct UnusedUnsafe {
+ #[label]
+ pub span: Span,
+ #[subdiagnostic]
+ pub enclosing: Option<UnusedUnsafeEnclosing>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum UnusedUnsafeEnclosing {
+ #[label(mir_build_unused_unsafe_enclosing_block_label)]
+ Block {
+ #[primary_span]
+ span: Span,
+ },
+ #[label(mir_build_unused_unsafe_enclosing_fn_label)]
+ Function {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
+ pub cx: &'m MatchCheckCtxt<'p, 'tcx>,
+ pub expr_span: Span,
+ pub span: Span,
+ pub ty: Ty<'tcx>,
+}
+
+impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
+ fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ let mut diag = handler.struct_span_err_with_code(
+ self.span,
+ rustc_errors::fluent::mir_build_non_exhaustive_patterns_type_not_empty,
+ error_code!(E0004),
+ );
+
+ let peeled_ty = self.ty.peel_refs();
+ diag.set_arg("ty", self.ty);
+ diag.set_arg("peeled_ty", peeled_ty);
+
+ if let ty::Adt(def, _) = peeled_ty.kind() {
+ let def_span = self
+ .cx
+ .tcx
+ .hir()
+ .get_if_local(def.did())
+ .and_then(|node| node.ident())
+ .map(|ident| ident.span)
+ .unwrap_or_else(|| self.cx.tcx.def_span(def.did()));
+
+ // workaround to make test pass
+ let mut span: MultiSpan = def_span.into();
+ span.push_span_label(def_span, "");
+
+ diag.span_note(span, rustc_errors::fluent::def_note);
+ }
+
+ let is_variant_list_non_exhaustive = match self.ty.kind() {
+ ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => {
+ true
+ }
+ _ => false,
+ };
+
+ if is_variant_list_non_exhaustive {
+ diag.note(rustc_errors::fluent::non_exhaustive_type_note);
+ } else {
+ diag.note(rustc_errors::fluent::type_note);
+ }
+
+ if let ty::Ref(_, sub_ty, _) = self.ty.kind() {
+ if !sub_ty.is_inhabited_from(self.cx.tcx, self.cx.module, self.cx.param_env) {
+ diag.note(rustc_errors::fluent::reference_note);
+ }
+ }
+
+ let mut suggestion = None;
+ let sm = self.cx.tcx.sess.source_map();
+ if self.span.eq_ctxt(self.expr_span) {
+ // Get the span for the empty match body `{}`.
+ let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
+ (format!("\n{}", snippet), " ")
+ } else {
+ (" ".to_string(), "")
+ };
+ suggestion = Some((
+ self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
+ format!(
+ " {{{indentation}{more}_ => todo!(),{indentation}}}",
+ indentation = indentation,
+ more = more,
+ ),
+ ));
+ }
+
+ if let Some((span, sugg)) = suggestion {
+ diag.span_suggestion_verbose(
+ span,
+ rustc_errors::fluent::suggestion,
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ } else {
+ diag.help(rustc_errors::fluent::help);
+ }
+
+ diag
+ }
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_static_in_pattern, code = "E0158")]
+pub struct StaticInPattern {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_assoc_const_in_pattern, code = "E0158")]
+pub struct AssocConstInPattern {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_param_in_pattern, code = "E0158")]
+pub struct ConstParamInPattern {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_non_const_path, code = "E0080")]
+pub struct NonConstPath {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_unreachable_pattern)]
+pub struct UnreachablePattern {
+ #[label]
+ pub span: Option<Span>,
+ #[label(catchall_label)]
+ pub catchall: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_const_pattern_depends_on_generic_parameter)]
+pub struct ConstPatternDependsOnGenericParameter {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_could_not_eval_const_pattern)]
+pub struct CouldNotEvalConstPattern {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_lower_range_bound_must_be_less_than_or_equal_to_upper, code = "E0030")]
+pub struct LowerRangeBoundMustBeLessThanOrEqualToUpper {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[note(teach_note)]
+ pub teach: Option<()>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_lower_range_bound_must_be_less_than_upper, code = "E0579")]
+pub struct LowerRangeBoundMustBeLessThanUpper {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_leading_irrefutable_let_patterns)]
+#[note]
+#[help]
+pub struct LeadingIrrefutableLetPatterns {
+ pub count: usize,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_trailing_irrefutable_let_patterns)]
+#[note]
+#[help]
+pub struct TrailingIrrefutableLetPatterns {
+ pub count: usize,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_bindings_with_variant_name, code = "E0170")]
+pub struct BindingsWithVariantName {
+ #[suggestion(code = "{ty_path}::{ident}", applicability = "machine-applicable")]
+ pub suggestion: Option<Span>,
+ pub ty_path: String,
+ pub ident: Ident,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_irrefutable_let_patterns_generic_let)]
+#[note]
+#[help]
+pub struct IrrefutableLetPatternsGenericLet {
+ pub count: usize,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_irrefutable_let_patterns_if_let)]
+#[note]
+#[help]
+pub struct IrrefutableLetPatternsIfLet {
+ pub count: usize,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_irrefutable_let_patterns_if_let_guard)]
+#[note]
+#[help]
+pub struct IrrefutableLetPatternsIfLetGuard {
+ pub count: usize,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_irrefutable_let_patterns_let_else)]
+#[note]
+#[help]
+pub struct IrrefutableLetPatternsLetElse {
+ pub count: usize,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(mir_build_irrefutable_let_patterns_while_let)]
+#[note]
+#[help]
+pub struct IrrefutableLetPatternsWhileLet {
+ pub count: usize,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_borrow_of_moved_value)]
+pub struct BorrowOfMovedValue<'tcx> {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ #[label(occurs_because_label)]
+ pub binding_span: Span,
+ #[label(value_borrowed_label)]
+ pub conflicts_ref: Vec<Span>,
+ pub name: Ident,
+ pub ty: Ty<'tcx>,
+ #[suggestion(code = "ref ", applicability = "machine-applicable")]
+ pub suggest_borrowing: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(mir_build_multiple_mut_borrows)]
+pub struct MultipleMutBorrows {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub binding_span: Span,
+ #[subdiagnostic]
+ pub occurences: Vec<MultipleMutBorrowOccurence>,
+ pub name: Ident,
+}
+
+#[derive(Subdiagnostic)]
+pub enum MultipleMutBorrowOccurence {
+ #[label(mutable_borrow)]
+ Mutable {
+ #[primary_span]
+ span: Span,
+ name_mut: Ident,
+ },
+ #[label(immutable_borrow)]
+ Immutable {
+ #[primary_span]
+ span: Span,
+ name_immut: Ident,
+ },
+ #[label(moved)]
+ Moved {
+ #[primary_span]
+ span: Span,
+ name_moved: Ident,
+ },
+}
mod build;
mod check_unsafety;
+mod errors;
mod lints;
pub mod thir;
+use crate::errors::UnconditionalRecursion;
use rustc_data_structures::graph::iterate::{
NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
};
let sp = tcx.def_span(def_id);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
- tcx.struct_span_lint_hir(
+ tcx.emit_spanned_lint(
UNCONDITIONAL_RECURSION,
hir_id,
sp,
- "function cannot return without recursing",
- |lint| {
- lint.span_label(sp, "cannot return without recursing");
- // offer some help to the programmer.
- for call_span in vis.reachable_recursive_calls {
- lint.span_label(call_span, "recursive call site");
- }
- lint.help("a `loop` may express intention better if this is on purpose")
- },
+ UnconditionalRecursion { span: sp, call_sites: vis.reachable_recursive_calls },
);
}
}
use rustc_middle::ty::{self, RvalueScopes, TyCtxt};
use rustc_span::Span;
-pub(crate) fn thir_body<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub(crate) fn thir_body(
+ tcx: TyCtxt<'_>,
owner_def: ty::WithOptConstParam<LocalDefId>,
-) -> Result<(&'tcx Steal<Thir<'tcx>>, ExprId), ErrorGuaranteed> {
+) -> Result<(&Steal<Thir<'_>>, ExprId), ErrorGuaranteed> {
let hir = tcx.hir();
let body = hir.body(hir.body_owned_by(owner_def.did));
let mut cx = Cx::new(tcx, owner_def);
Ok((tcx.alloc_steal_thir(cx.thir), expr))
}
-pub(crate) fn thir_tree<'tcx>(
- tcx: TyCtxt<'tcx>,
- owner_def: ty::WithOptConstParam<LocalDefId>,
-) -> String {
+pub(crate) fn thir_tree(tcx: TyCtxt<'_>, owner_def: ty::WithOptConstParam<LocalDefId>) -> String {
match thir_body(tcx, owner_def) {
Ok((thir, _)) => format!("{:#?}", thir.steal()),
Err(_) => "error".into(),
};
use super::{PatCtxt, PatternError};
+use crate::errors::*;
+
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_errors::{
- error_code, pluralize, struct_span_err, Applicability, DelayDm, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, MultiSpan,
+ pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+ MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::*;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{HirId, Pat};
+use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
+
use rustc_session::lint::builtin::{
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
};
for error in &self.errors {
match *error {
PatternError::StaticInPattern(span) => {
- self.span_e0158(span, "statics cannot be referenced in patterns")
+ self.tcx.sess.emit_err(StaticInPattern { span });
}
PatternError::AssocConstInPattern(span) => {
- self.span_e0158(span, "associated consts cannot be referenced in patterns")
+ self.tcx.sess.emit_err(AssocConstInPattern { span });
}
PatternError::ConstParamInPattern(span) => {
- self.span_e0158(span, "const parameters cannot be referenced in patterns")
+ self.tcx.sess.emit_err(ConstParamInPattern { span });
}
PatternError::NonConstPath(span) => {
- rustc_middle::mir::interpret::struct_error(
- self.tcx.at(span),
- "runtime values cannot be referenced in patterns",
- )
- .emit();
+ self.tcx.sess.emit_err(NonConstPath { span });
}
}
}
}
-
- fn span_e0158(&self, span: Span, text: &str) {
- struct_span_err!(self.tcx.sess, span, E0158, "{}", text).emit();
- }
}
impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
);
return true;
}
- let lint_affix = |affix: &[Option<(Span, bool)>], kind, suggestion| {
- let span_start = affix[0].unwrap().0;
- let span_end = affix.last().unwrap().unwrap().0;
- let span = span_start.to(span_end);
- let cnt = affix.len();
- let s = pluralize!(cnt);
- cx.tcx.struct_span_lint_hir(
- IRREFUTABLE_LET_PATTERNS,
- top,
- span,
- format!("{kind} irrefutable pattern{s} in let chain"),
- |lint| {
- lint.note(format!(
- "{these} pattern{s} will always match",
- these = pluralize!("this", cnt),
- ))
- .help(format!(
- "consider moving {} {suggestion}",
- if cnt > 1 { "them" } else { "it" }
- ))
- },
- );
- };
if let Some(until) = chain_refutabilities.iter().position(|r| !matches!(*r, Some((_, false)))) && until > 0 {
// The chain has a non-zero prefix of irrefutable `let` statements.
if !matches!(let_source, LetSource::WhileLet | LetSource::IfLetGuard) {
// Emit the lint
let prefix = &chain_refutabilities[..until];
- lint_affix(prefix, "leading", "outside of the construct");
+ let span_start = prefix[0].unwrap().0;
+ let span_end = prefix.last().unwrap().unwrap().0;
+ let span = span_start.to(span_end);
+ let count = prefix.len();
+ cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, LeadingIrrefutableLetPatterns { count });
}
}
if let Some(from) = chain_refutabilities.iter().rposition(|r| !matches!(*r, Some((_, false)))) && from != (chain_refutabilities.len() - 1) {
// The chain has a non-empty suffix of irrefutable `let` statements
let suffix = &chain_refutabilities[from + 1..];
- lint_affix(suffix, "trailing", "into the body");
+ let span_start = suffix[0].unwrap().0;
+ let span_end = suffix.last().unwrap().unwrap().0;
+ let span = span_start.to(span_end);
+ let count = suffix.len();
+ cx.tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, top, span, TrailingIrrefutableLetPatterns { count });
}
true
}
})
{
let variant_count = edef.variants().len();
- cx.tcx.struct_span_lint_hir(
+ let ty_path = with_no_trimmed_paths!({
+ cx.tcx.def_path_str(edef.did())
+ });
+ cx.tcx.emit_spanned_lint(
BINDINGS_WITH_VARIANT_NAME,
p.hir_id,
p.span,
- DelayDm(|| format!(
- "pattern binding `{}` is named the same as one \
- of the variants of the type `{}`",
- ident, cx.tcx.def_path_str(edef.did())
- )),
- |lint| {
- let ty_path = cx.tcx.def_path_str(edef.did());
- lint.code(error_code!(E0170));
-
+ BindingsWithVariantName {
// If this is an irrefutable pattern, and there's > 1 variant,
// then we can't actually match on this. Applying the below
// suggestion would produce code that breaks on `check_irrefutable`.
- if rf == Refutable || variant_count == 1 {
- lint.span_suggestion(
- p.span,
- "to match on the variant, qualify the path",
- format!("{}::{}", ty_path, ident),
- Applicability::MachineApplicable,
- );
- }
-
- lint
+ suggestion: if rf == Refutable || variant_count == 1 {
+ Some(p.span)
+ } else { None },
+ ty_path,
+ ident,
},
)
}
}
fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<Span>) {
- tcx.struct_span_lint_hir(UNREACHABLE_PATTERNS, id, span, "unreachable pattern", |lint| {
- if let Some(catchall) = catchall {
- // We had a catchall pattern, hint at that.
- lint.span_label(span, "unreachable pattern");
- lint.span_label(catchall, "matches any value");
- }
- lint
- });
+ tcx.emit_spanned_lint(
+ UNREACHABLE_PATTERNS,
+ id,
+ span,
+ UnreachablePattern { span: if catchall.is_some() { Some(span) } else { None }, catchall },
+ );
}
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, id: HirId, span: Span) {
span: Span,
) {
macro_rules! emit_diag {
- (
- $lint:expr,
- $source_name:expr,
- $note_sufix:expr,
- $help_sufix:expr
- ) => {{
- let s = pluralize!(count);
- let these = pluralize!("this", count);
- tcx.struct_span_lint_hir(
- IRREFUTABLE_LET_PATTERNS,
- id,
- span,
- format!("irrefutable {} pattern{s}", $source_name),
- |lint| {
- lint.note(&format!(
- "{these} pattern{s} will always match, so the {}",
- $note_sufix
- ))
- .help(concat!("consider ", $help_sufix))
- },
- )
+ ($lint:tt) => {{
+ tcx.emit_spanned_lint(IRREFUTABLE_LET_PATTERNS, id, span, $lint { count });
}};
}
match source {
- LetSource::GenericLet => {
- emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
- }
- LetSource::IfLet => {
- emit_diag!(
- lint,
- "`if let`",
- "`if let` is useless",
- "replacing the `if let` with a `let`"
- );
- }
- LetSource::IfLetGuard => {
- emit_diag!(
- lint,
- "`if let` guard",
- "guard is useless",
- "removing the guard and adding a `let` inside the match arm"
- );
- }
- LetSource::LetElse => {
- emit_diag!(
- lint,
- "`let...else`",
- "`else` clause is useless",
- "removing the `else` clause"
- );
- }
- LetSource::WhileLet => {
- emit_diag!(
- lint,
- "`while let`",
- "loop will never exit",
- "instead using a `loop { ... }` with a `let` inside it"
- );
- }
- };
+ LetSource::GenericLet => emit_diag!(IrrefutableLetPatternsGenericLet),
+ LetSource::IfLet => emit_diag!(IrrefutableLetPatternsIfLet),
+ LetSource::IfLetGuard => emit_diag!(IrrefutableLetPatternsIfLetGuard),
+ LetSource::LetElse => emit_diag!(IrrefutableLetPatternsLetElse),
+ LetSource::WhileLet => emit_diag!(IrrefutableLetPatternsWhileLet),
+ }
}
fn is_let_irrefutable<'p, 'tcx>(
// informative.
let mut err;
let pattern;
- let mut patterns_len = 0;
+ let patterns_len;
if is_empty_match && !non_empty_enum {
- err = create_e0004(
- cx.tcx.sess,
- sp,
- format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
- );
- pattern = "_".to_string();
+ cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
+ cx,
+ expr_span,
+ span: sp,
+ ty: scrut_ty,
+ });
+ return;
} else {
+ // FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,
}
});
if !conflicts_ref.is_empty() {
- let occurs_because = format!(
- "move occurs because `{}` has type `{}` which does not implement the `Copy` trait",
+ sess.emit_err(BorrowOfMovedValue {
+ span: pat.span,
+ binding_span,
+ conflicts_ref,
name,
- typeck_results.node_type(pat.hir_id),
- );
- let mut err = sess.struct_span_err(pat.span, "borrow of moved value");
- err.span_label(binding_span, format!("value moved into `{}` here", name))
- .span_label(binding_span, occurs_because)
- .span_labels(conflicts_ref, "value borrowed here after move");
- if pat.span.contains(binding_span) {
- err.span_suggestion_verbose(
- binding_span.shrink_to_lo(),
- "borrow this binding in the pattern to avoid moving the value",
- "ref ".to_string(),
- Applicability::MachineApplicable,
- );
- }
- err.emit();
+ ty: typeck_results.node_type(pat.hir_id),
+ suggest_borrowing: pat
+ .span
+ .contains(binding_span)
+ .then(|| binding_span.shrink_to_lo()),
+ });
}
return;
}
// Report errors if any.
if !conflicts_mut_mut.is_empty() {
// Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
- let mut err = sess
- .struct_span_err(pat.span, "cannot borrow value as mutable more than once at a time");
- err.span_label(binding_span, format!("first mutable borrow, by `{}`, occurs here", name));
- for (span, name) in conflicts_mut_mut {
- err.span_label(span, format!("another mutable borrow, by `{}`, occurs here", name));
+ let mut occurences = vec![];
+
+ for (span, name_mut) in conflicts_mut_mut {
+ occurences.push(MultipleMutBorrowOccurence::Mutable { span, name_mut });
}
- for (span, name) in conflicts_mut_ref {
- err.span_label(span, format!("also borrowed as immutable, by `{}`, here", name));
+ for (span, name_immut) in conflicts_mut_ref {
+ occurences.push(MultipleMutBorrowOccurence::Immutable { span, name_immut });
}
- for (span, name) in conflicts_move {
- err.span_label(span, format!("also moved into `{}` here", name));
+ for (span, name_moved) in conflicts_move {
+ occurences.push(MultipleMutBorrowOccurence::Moved { span, name_moved });
}
- err.emit();
+ sess.emit_err(MultipleMutBorrows { span: pat.span, binding_span, occurences, name });
} else if !conflicts_mut_ref.is_empty() {
// Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
let (primary, also) = match mut_outer {
/// hoops to get a reference to the value.
pub(super) struct FallbackToConstRef(());
- pub(super) fn fallback_to_const_ref<'tcx>(c2p: &super::ConstToPat<'tcx>) -> FallbackToConstRef {
+ pub(super) fn fallback_to_const_ref(c2p: &super::ConstToPat<'_>) -> FallbackToConstRef {
assert!(c2p.behind_reference.get());
FallbackToConstRef(())
}
}
/// Iterate over the contained ranges.
- fn iter<'a>(&'a self) -> impl Iterator<Item = IntRange> + Captures<'a> {
+ fn iter(&self) -> impl Iterator<Item = IntRange> + Captures<'_> {
use IntBorder::*;
let self_range = Self::to_borders(self.range.clone());
}
/// Iterate over the partition of this slice.
- fn iter<'a>(&'a self) -> impl Iterator<Item = Slice> + Captures<'a> {
+ fn iter(&self) -> impl Iterator<Item = Slice> + Captures<'_> {
let smaller_lengths = match self.array_len {
// The only admissible fixed-length slice is one of the array size. Whether `max_slice`
// is fixed-length or variable-length, it will be the only relevant slice to output
mod usefulness;
pub(crate) use self::check_match::check_match;
+pub(crate) use self::usefulness::MatchCheckCtxt;
+use crate::errors::*;
use crate::thir::util::UserAnnotatedTyHelpers;
-use rustc_errors::struct_span_err;
+use rustc_errors::error_code;
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
}
// `x..y` where `x >= y`. The range is empty => error.
(RangeEnd::Excluded, _) => {
- struct_span_err!(
- self.tcx.sess,
- span,
- E0579,
- "lower range bound must be less than upper"
- )
- .emit();
+ self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanUpper { span });
PatKind::Wild
}
// `x..=y` where `x == y`.
}
// `x..=y` where `x > y` hence the range is empty => error.
(RangeEnd::Included, _) => {
- let mut err = struct_span_err!(
- self.tcx.sess,
+ self.tcx.sess.emit_err(LowerRangeBoundMustBeLessThanOrEqualToUpper {
span,
- E0030,
- "lower range bound must be less than or equal to upper"
- );
- err.span_label(span, "lower bound larger than upper bound");
- if self.tcx.sess.teach(&err.get_code().unwrap()) {
- err.note(
- "When matching against a range, the compiler \
- verifies that the range is non-empty. Range \
- patterns include both end-points, so this is \
- equivalent to requiring the start of the range \
- to be less than or equal to the end of the range.",
- );
- }
- err.emit();
+ teach: if self.tcx.sess.teach(&error_code!(E0030)) { Some(()) } else { None },
+ });
PatKind::Wild
}
}
}
Err(_) => {
- self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ self.tcx.sess.emit_err(CouldNotEvalConstPattern { span });
return pat_from_kind(PatKind::Wild);
}
};
Err(ErrorHandled::TooGeneric) => {
// While `Reported | Linted` cases will have diagnostics emitted already
// it is not true for TooGeneric case, so we need to give user more information.
- self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter");
+ self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
pat_from_kind(PatKind::Wild)
}
Err(_) => {
- self.tcx.sess.span_err(span, "could not evaluate constant pattern");
+ self.tcx.sess.emit_err(CouldNotEvalConstPattern { span });
pat_from_kind(PatKind::Wild)
}
}
mir::ConstantKind::Val(_, _) => self.const_to_pat(value, id, span, false).kind,
mir::ConstantKind::Unevaluated(..) => {
// If we land here it means the const can't be evaluated because it's `TooGeneric`.
- self.tcx.sess.span_err(span, "constant pattern depends on a generic parameter");
+ self.tcx.sess.emit_err(ConstPatternDependsOnGenericParameter { span });
return PatKind::Wild;
}
}
}
impl DefUse {
- fn apply<'tcx>(trans: &mut impl GenKill<Local>, place: Place<'tcx>, context: PlaceContext) {
+ fn apply(trans: &mut impl GenKill<Local>, place: Place<'_>, context: PlaceContext) {
match DefUse::for_place(place, context) {
Some(DefUse::Def) => trans.kill(place.local),
Some(DefUse::Use) => trans.gen(place.local),
}
}
- fn for_place<'tcx>(place: Place<'tcx>, context: PlaceContext) -> Option<DefUse> {
+ fn for_place(place: Place<'_>, context: PlaceContext) -> Option<DefUse> {
match context {
PlaceContext::NonUse(_) => None,
mod abs_domain;
rustc_index::newtype_index! {
- pub struct MovePathIndex {
- DEBUG_FORMAT = "mp{}"
- }
+ #[debug_format = "mp{}"]
+ pub struct MovePathIndex {}
}
impl polonius_engine::Atom for MovePathIndex {
}
rustc_index::newtype_index! {
- pub struct MoveOutIndex {
- DEBUG_FORMAT = "mo{}"
- }
+ #[debug_format = "mo{}"]
+ pub struct MoveOutIndex {}
}
rustc_index::newtype_index! {
- pub struct InitIndex {
- DEBUG_FORMAT = "in{}"
- }
+ #[debug_format = "in{}"]
+ pub struct InitIndex {}
}
impl MoveOutIndex {
}
/// Returns all locals with projections that have their reference or address taken.
-fn excluded_locals<'tcx>(body: &Body<'tcx>) -> IndexVec<Local, bool> {
+fn excluded_locals(body: &Body<'_>) -> IndexVec<Local, bool> {
struct Collector {
result: IndexVec<Local, bool>,
}
unused_unsafes
}
-fn unsafety_check_result<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn unsafety_check_result(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx UnsafetyCheckResult {
+) -> &UnsafetyCheckResult {
debug!("unsafety_violations({:?})", def);
// N.B., this borrow is valid because all the consumers of
-//! This module provides a pass to replacing the following statements with
-//! [`Nop`]s
+//! This module provides a pass that removes parts of MIR that are no longer relevant after
+//! analysis phase and borrowck. In particular, it removes false edges, user type annotations and
+//! replaces following statements with [`Nop`]s:
//!
//! - [`AscribeUserType`]
//! - [`FakeRead`]
//! - [`Assign`] statements with a [`Shallow`] borrow
//!
-//! The `CleanFakeReadsAndBorrows` "pass" is actually implemented as two
-//! traversals (aka visits) of the input MIR. The first traversal,
-//! `DeleteAndRecordFakeReads`, deletes the fake reads and finds the
-//! temporaries read by [`ForMatchGuard`] reads, and `DeleteFakeBorrows`
-//! deletes the initialization of those temporaries.
-//!
//! [`AscribeUserType`]: rustc_middle::mir::StatementKind::AscribeUserType
-//! [`Shallow`]: rustc_middle::mir::BorrowKind::Shallow
-//! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead
//! [`Assign`]: rustc_middle::mir::StatementKind::Assign
-//! [`ForMatchGuard`]: rustc_middle::mir::FakeReadCause::ForMatchGuard
+//! [`FakeRead`]: rustc_middle::mir::StatementKind::FakeRead
//! [`Nop`]: rustc_middle::mir::StatementKind::Nop
+//! [`Shallow`]: rustc_middle::mir::BorrowKind::Shallow
use crate::MirPass;
-use rustc_middle::mir::visit::MutVisitor;
-use rustc_middle::mir::{Body, BorrowKind, Location, Rvalue};
-use rustc_middle::mir::{Statement, StatementKind};
+use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind};
use rustc_middle::ty::TyCtxt;
-pub struct CleanupNonCodegenStatements;
+pub struct CleanupPostBorrowck;
-pub struct DeleteNonCodegenStatements<'tcx> {
- tcx: TyCtxt<'tcx>,
-}
+impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck {
+ fn run_pass(&self, _tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ for basic_block in body.basic_blocks.as_mut() {
+ for statement in basic_block.statements.iter_mut() {
+ match statement.kind {
+ StatementKind::AscribeUserType(..)
+ | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Shallow, _)))
+ | StatementKind::FakeRead(..) => statement.make_nop(),
+ _ => (),
+ }
+ }
+ let terminator = basic_block.terminator_mut();
+ match terminator.kind {
+ TerminatorKind::FalseEdge { real_target, .. }
+ | TerminatorKind::FalseUnwind { real_target, .. } => {
+ terminator.kind = TerminatorKind::Goto { target: real_target };
+ }
+ _ => {}
+ }
+ }
-impl<'tcx> MirPass<'tcx> for CleanupNonCodegenStatements {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let mut delete = DeleteNonCodegenStatements { tcx };
- delete.visit_body_preserves_cfg(body);
body.user_type_annotations.raw.clear();
for decl in &mut body.local_decls {
}
}
}
-
-impl<'tcx> MutVisitor<'tcx> for DeleteNonCodegenStatements<'tcx> {
- fn tcx(&self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
- match statement.kind {
- StatementKind::AscribeUserType(..)
- | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Shallow, _)))
- | StatementKind::FakeRead(..) => statement.make_nop(),
- _ => (),
- }
- self.super_statement(statement, location);
- }
-}
BinOp::Mul if const_arg.layout.ty.is_integral() && arg_value == 0 => {
if let Rvalue::CheckedBinaryOp(_, _) = rvalue {
let val = Immediate::ScalarPair(
- const_arg.to_scalar().into(),
- Scalar::from_bool(false).into(),
+ const_arg.to_scalar(),
+ Scalar::from_bool(false),
);
this.ecx.write_immediate(val, &dest)
} else {
rustc_index::newtype_index! {
/// A node in the control-flow graph of CoverageGraph.
+ #[debug_format = "bcb{}"]
pub(super) struct BasicCoverageBlock {
- DEBUG_FORMAT = "bcb{}",
- const START_BCB = 0,
+ const START_BCB = 0;
}
}
}
}
-fn fn_sig_and_body<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn fn_sig_and_body(
+ tcx: TyCtxt<'_>,
def_id: DefId,
-) -> (Option<&'tcx rustc_hir::FnSig<'tcx>>, &'tcx rustc_hir::Body<'tcx>) {
+) -> (Option<&rustc_hir::FnSig<'_>>, &rustc_hir::Body<'_>) {
// FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
// to HIR for it.
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
coverage_visitor.info
}
-fn covered_code_regions<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Vec<&'tcx CodeRegion> {
+fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
let body = mir_body(tcx, def_id);
body.basic_blocks
.iter()
/// This function ensures we obtain the correct MIR for the given item irrespective of
/// whether that means const mir or runtime mir. For `const fn` this opts for runtime
/// mir.
-fn mir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx mir::Body<'tcx> {
+fn mir_body(tcx: TyCtxt<'_>, def_id: DefId) -> &mir::Body<'_> {
let id = ty::WithOptConstParam::unknown(def_id);
let def = ty::InstanceDef::Item(id);
tcx.instance_mir(def)
}
}
-fn debug_basic_blocks<'tcx>(mir_body: &Body<'tcx>) -> String {
+fn debug_basic_blocks(mir_body: &Body<'_>) -> String {
format!(
"{:?}",
mir_body
}
/// Returns true if values of a given type will never be passed indirectly, regardless of ABI.
-fn type_will_always_be_passed_directly<'tcx>(ty: Ty<'tcx>) -> bool {
+fn type_will_always_be_passed_directly(ty: Ty<'_>) -> bool {
matches!(
ty.kind(),
ty::Bool
use std::collections::hash_map::{Entry, OccupiedEntry};
+use crate::simplify::remove_dead_blocks;
use crate::MirPass;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::bit_set::BitSet;
apply_merges(body, tcx, &merges, &merged_locals);
}
+ if round_count != 0 {
+ // Merging can introduce overlap between moved arguments and/or call destination in an
+ // unreachable code, which validator considers to be ill-formed.
+ remove_dead_blocks(tcx, body);
+ }
+
trace!(round_count);
}
}
}
}
- fn add_place<'tcx>(&mut self, place: Place<'tcx>) {
+ fn add_place(&mut self, place: Place<'_>) {
self.writes.push(place.local);
}
mod multiple_return_terminators;
mod normalize_array_len;
mod nrvo;
-// This pass is public to allow external drivers to perform MIR cleanup
-pub mod remove_false_edges;
mod remove_noop_landing_pads;
mod remove_storage_markers;
mod remove_uninit_drops;
/// Make MIR ready for const evaluation. This is run on all MIR, not just on consts!
/// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
/// We used to have this for pre-miri MIR based const eval.
-fn mir_const<'tcx>(
- tcx: TyCtxt<'tcx>,
- def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx Steal<Body<'tcx>> {
+fn mir_const(tcx: TyCtxt<'_>, def: ty::WithOptConstParam<LocalDefId>) -> &Steal<Body<'_>> {
if let Some(def) = def.try_upgrade(tcx) {
return tcx.mir_const(def);
}
}
/// Compute the main MIR body and the list of MIR bodies of the promoteds.
-fn mir_promoted<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn mir_promoted(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
+) -> (&Steal<Body<'_>>, &Steal<IndexVec<Promoted, Body<'_>>>) {
if let Some(def) = def.try_upgrade(tcx) {
return tcx.mir_promoted(def);
}
}
/// Compute the MIR that is used during CTFE (and thus has no optimizations run on it)
-fn mir_for_ctfe<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Body<'tcx> {
+fn mir_for_ctfe(tcx: TyCtxt<'_>, def_id: DefId) -> &Body<'_> {
let did = def_id.expect_local();
if let Some(def) = ty::WithOptConstParam::try_lookup(did, tcx) {
tcx.mir_for_ctfe_of_const_arg(def)
/// we'd get cycle errors with `mir_for_ctfe`, because typeck would need to typeck
/// the const parameter while type checking the main body, which in turn would try
/// to type check the main body again.
-fn mir_for_ctfe_of_const_arg<'tcx>(
- tcx: TyCtxt<'tcx>,
- (did, param_did): (LocalDefId, DefId),
-) -> &'tcx Body<'tcx> {
+fn mir_for_ctfe_of_const_arg(tcx: TyCtxt<'_>, (did, param_did): (LocalDefId, DefId)) -> &Body<'_> {
tcx.arena.alloc(inner_mir_for_ctfe(
tcx,
ty::WithOptConstParam { did, const_param_did: Some(param_did) },
/// Obtain just the main MIR (no promoteds) and run some cleanups on it. This also runs
/// mir borrowck *before* doing so in order to ensure that borrowck can be run and doesn't
/// end up missing the source MIR due to stealing happening.
-fn mir_drops_elaborated_and_const_checked<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn mir_drops_elaborated_and_const_checked(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx Steal<Body<'tcx>> {
+) -> &Steal<Body<'_>> {
if let Some(def) = def.try_upgrade(tcx) {
return tcx.mir_drops_elaborated_and_const_checked(def);
}
/// After this series of passes, no lifetime analysis based on borrowing can be done.
fn run_analysis_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let passes: &[&dyn MirPass<'tcx>] = &[
- &remove_false_edges::RemoveFalseEdges,
+ &cleanup_post_borrowck::CleanupPostBorrowck,
&simplify_branches::SimplifyConstCondition::new("initial"),
&remove_noop_landing_pads::RemoveNoopLandingPads,
- &cleanup_post_borrowck::CleanupNonCodegenStatements,
&simplify::SimplifyCfg::new("early-opt"),
&deref_separator::Derefer,
];
}
/// Optimize the MIR and prepare it for codegen.
-fn optimized_mir<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx Body<'tcx> {
+fn optimized_mir(tcx: TyCtxt<'_>, did: DefId) -> &Body<'_> {
let did = did.expect_local();
assert_eq!(ty::WithOptConstParam::try_lookup(did, tcx), None);
tcx.arena.alloc(inner_optimized_mir(tcx, did))
/// Fetch all the promoteds of an item and prepare their MIR bodies to be ready for
/// constant evaluation once all substitutions become known.
-fn promoted_mir<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn promoted_mir(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx IndexVec<Promoted, Body<'tcx>> {
+) -> &IndexVec<Promoted, Body<'_>> {
if tcx.is_constructor(def.did.to_def_id()) {
return tcx.arena.alloc(IndexVec::new());
}
+++ /dev/null
-use rustc_middle::mir::{Body, TerminatorKind};
-use rustc_middle::ty::TyCtxt;
-
-use crate::MirPass;
-
-/// Removes `FalseEdge` and `FalseUnwind` terminators from the MIR.
-///
-/// These are only needed for borrow checking, and can be removed afterwards.
-///
-/// FIXME: This should probably have its own MIR phase.
-pub struct RemoveFalseEdges;
-
-impl<'tcx> MirPass<'tcx> for RemoveFalseEdges {
- fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- for block in body.basic_blocks_mut() {
- let terminator = block.terminator_mut();
- terminator.kind = match terminator.kind {
- TerminatorKind::FalseEdge { real_target, .. } => {
- TerminatorKind::Goto { target: real_target }
- }
- TerminatorKind::FalseUnwind { real_target, .. } => {
- TerminatorKind::Goto { target: real_target }
- }
-
- _ => continue,
- }
- }
- }
-}
let mut body =
new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span);
+ // The first argument (index 0), but add 1 for the return value.
+ let mut dropee_ptr = Place::from(Local::new(1 + 0));
+ if tcx.sess.opts.unstable_opts.mir_emit_retag {
+ // We want to treat the function argument as if it was passed by `&mut`. As such, we
+ // generate
+ // ```
+ // temp = &mut *arg;
+ // Retag(temp, FnEntry)
+ // ```
+ // It's important that we do this first, before anything that depends on `dropee_ptr`
+ // has been put into the body.
+ let reborrow = Rvalue::Ref(
+ tcx.lifetimes.re_erased,
+ BorrowKind::Mut { allow_two_phase_borrow: false },
+ tcx.mk_place_deref(dropee_ptr),
+ );
+ let ref_ty = reborrow.ty(body.local_decls(), tcx);
+ dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into();
+ let new_statements = [
+ StatementKind::Assign(Box::new((dropee_ptr, reborrow))),
+ StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)),
+ ];
+ for s in new_statements {
+ body.basic_blocks_mut()[START_BLOCK]
+ .statements
+ .push(Statement { source_info, kind: s });
+ }
+ }
+
if ty.is_some() {
- // The first argument (index 0), but add 1 for the return value.
- let dropee_ptr = Place::from(Local::new(1 + 0));
let patch = {
let param_env = tcx.param_env_reveal_all_normalized(def_id);
let mut elaborator =
}
/// Match on `((_LOCAL as Variant).FIELD: TY)`.
-fn match_variant_field_place<'tcx>(place: Place<'tcx>) -> Option<(Local, VarField<'tcx>)> {
+fn match_variant_field_place(place: Place<'_>) -> Option<(Local, VarField<'_>)> {
match place.as_ref() {
PlaceRef {
local,
let mut fragments = IndexVec::new();
for (k, v) in &replacements.fields {
fragments.ensure_contains_elem(k.local, || Vec::new());
- fragments[k.local].push((&k.projection[..], *v));
+ fragments[k.local].push((k.projection, *v));
}
debug!(?fragments);
let def_path_str = tcx.def_path_str(def_id);
let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance);
let mut path = PathBuf::new();
- let was_written = if written_to_path.is_some() {
- path = written_to_path.unwrap();
+ let was_written = if let Some(written_to_path) = written_to_path {
+ path = written_to_path;
Some(())
} else {
None
}
}
-fn collect_and_partition_mono_items<'tcx>(
- tcx: TyCtxt<'tcx>,
- (): (),
-) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) {
+fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) {
let collection_mode = match tcx.sess.opts.unstable_opts.print_mono_items {
Some(ref s) => {
let mode_string = s.to_lowercase();
Ok(())
}
-fn codegened_and_inlined_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> &'tcx DefIdSet {
+fn codegened_and_inlined_items(tcx: TyCtxt<'_>, (): ()) -> &DefIdSet {
let (items, cgus) = tcx.collect_and_partition_mono_items(());
let mut visited = DefIdSet::default();
let mut result = items.clone();
use rustc_ast::util::case::Case;
use rustc_ast::AttrId;
use rustc_ast::DUMMY_NODE_ID;
-use rustc_ast::{self as ast, AnonConst, AttrStyle, AttrVec, Const, DelimArgs, Extern};
+use rustc_ast::{self as ast, AnonConst, AttrStyle, Const, DelimArgs, Extern};
use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, MacDelimiter, Mutability, StrLit};
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
use rustc_ast_pretty::pprust;
value: self.mk_expr(blk.span, ExprKind::Block(blk, None)),
};
let blk_span = anon_const.value.span;
- Ok(self.mk_expr_with_attrs(
- span.to(blk_span),
- ExprKind::ConstBlock(anon_const),
- AttrVec::from(attrs),
- ))
+ Ok(self.mk_expr_with_attrs(span.to(blk_span), ExprKind::ConstBlock(anon_const), attrs))
}
/// Parses mutability (`mut` or nothing).
if let Some(arg) = args
.iter()
.rev()
- .skip_while(|arg| matches!(arg, AngleBracketedArg::Constraint(_)))
- .next()
+ .find(|arg| !matches!(arg, AngleBracketedArg::Constraint(_)))
{
err.span_suggestion_verbose(
arg.span().shrink_to_hi(),
}
}
-fn check_trait_item<'tcx>(tcx: TyCtxt<'tcx>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) {
+fn check_trait_item(tcx: TyCtxt<'_>, worklist: &mut Vec<LocalDefId>, id: hir::TraitItemId) {
use hir::TraitItemKind::{Const, Fn};
if matches!(tcx.def_kind(id.owner_id), DefKind::AssocConst | DefKind::AssocFn) {
let trait_item = tcx.hir().trait_item(id);
}
}
-fn check_foreign_item<'tcx>(
- tcx: TyCtxt<'tcx>,
- worklist: &mut Vec<LocalDefId>,
- id: hir::ForeignItemId,
-) {
+fn check_foreign_item(tcx: TyCtxt<'_>, worklist: &mut Vec<LocalDefId>, id: hir::ForeignItemId) {
if matches!(tcx.def_kind(id.owner_id), DefKind::Static(_) | DefKind::Fn)
&& has_allow_dead_code_or_lang_attr(tcx, id.hir_id())
{
}
}
-fn create_and_seed_worklist<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn create_and_seed_worklist(
+ tcx: TyCtxt<'_>,
) -> (Vec<LocalDefId>, FxHashMap<LocalDefId, LocalDefId>) {
let effective_visibilities = &tcx.effective_visibilities(());
// see `MarkSymbolVisitor::struct_constructors`
(worklist, struct_constructors)
}
-fn live_symbols_and_ignored_derived_traits<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn live_symbols_and_ignored_derived_traits(
+ tcx: TyCtxt<'_>,
(): (),
) -> (FxHashSet<LocalDefId>, FxHashMap<LocalDefId, Vec<(DefId, DefId)>>) {
let (worklist, struct_constructors) = create_and_seed_worklist(tcx);
let mut dead_codes = dead_codes
.iter()
.filter(|v| !v.name.as_str().starts_with('_'))
- .map(|v| v)
.collect::<Vec<&DeadVariant>>();
if dead_codes.is_empty() {
return;
use crate::errors::DebugVisualizerUnreadable;
-fn check_for_debugger_visualizer<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn check_for_debugger_visualizer(
+ tcx: TyCtxt<'_>,
hir_id: HirId,
debugger_visualizers: &mut FxHashSet<DebuggerVisualizerFile>,
) {
}
/// Traverses and collects the debugger visualizers for a specific crate.
-fn debugger_visualizers<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> Vec<DebuggerVisualizerFile> {
+fn debugger_visualizers(tcx: TyCtxt<'_>, cnum: CrateNum) -> Vec<DebuggerVisualizerFile> {
assert_eq!(cnum, LOCAL_CRATE);
// Initialize the collector.
use crate::errors::{DuplicateDiagnosticItem, DuplicateDiagnosticItemInCrate};
-fn observe_item<'tcx>(
- tcx: TyCtxt<'tcx>,
- diagnostic_items: &mut DiagnosticItems,
- def_id: LocalDefId,
-) {
+fn observe_item(tcx: TyCtxt<'_>, diagnostic_items: &mut DiagnosticItems, def_id: LocalDefId) {
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let attrs = tcx.hir().attrs(hir_id);
if let Some(name) = extract(attrs) {
}
/// Traverse and collect the diagnostic items in the current
-fn diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, cnum: CrateNum) -> DiagnosticItems {
+fn diagnostic_items(tcx: TyCtxt<'_>, cnum: CrateNum) -> DiagnosticItems {
assert_eq!(cnum, LOCAL_CRATE);
// Initialize the collector.
}
/// Traverse and collect all the diagnostic items in all crates.
-fn all_diagnostic_items<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> DiagnosticItems {
+fn all_diagnostic_items(tcx: TyCtxt<'_>, (): ()) -> DiagnosticItems {
// Initialize the collector.
let mut items = DiagnosticItems::default();
fn print(&self, title: &str, prefix: &str) {
let mut nodes: Vec<_> = self.nodes.iter().collect();
- nodes.sort_by_key(|&(_, ref node)| node.stats.count * node.stats.size);
+ nodes.sort_by_key(|(_, node)| node.stats.count * node.stats.size);
let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum();
);
if !node.subnodes.is_empty() {
let mut subnodes: Vec<_> = node.subnodes.iter().collect();
- subnodes.sort_by_key(|&(_, ref subnode)| subnode.count * subnode.size);
+ subnodes.sort_by_key(|(_, subnode)| subnode.count * subnode.size);
for (label, subnode) in subnodes {
let size = subnode.count * subnode.size;
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(", ")
- .into()
};
let first_defined_span = self.tcx.hir().span_if_local(original_def_id);
let mut orig_crate_name = Empty;
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(", ")
- .into()
};
if first_defined_span.is_none() {
orig_crate_name = self.tcx.crate_name(original_def_id.krate);
}
}
-fn dump_layout_of<'tcx>(tcx: TyCtxt<'tcx>, item_def_id: LocalDefId, attr: &Attribute) {
+fn dump_layout_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
let tcx = tcx;
let param_env = tcx.param_env(item_def_id);
let ty = tcx.type_of(item_def_id);
mod rwu_table;
rustc_index::newtype_index! {
- pub struct Variable {
- DEBUG_FORMAT = "v({})",
- }
+ #[debug_format = "v({})"]
+ pub struct Variable {}
}
rustc_index::newtype_index! {
- pub struct LiveNode {
- DEBUG_FORMAT = "ln({})",
- }
+ #[debug_format = "ln({})"]
+ pub struct LiveNode {}
}
#[derive(Copy, Clone, PartialEq, Debug)]
}
}
-fn has_custom_linkage<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
+fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
// Anything which has custom linkage gets thrown on the worklist no
// matter where it is in the crate, along with "special std symbols"
// which are currently akin to allocator symbols.
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
}
-fn reachable_set<'tcx>(tcx: TyCtxt<'tcx>, (): ()) -> FxHashSet<LocalDefId> {
+fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> FxHashSet<LocalDefId> {
let effective_visibilities = &tcx.effective_visibilities(());
let any_library =
/// Check whether a path is a `use` item that has been marked as unstable.
///
/// See issue #94972 for details on why this is a special case
-fn is_unstable_reexport<'tcx>(tcx: TyCtxt<'tcx>, id: hir::HirId) -> bool {
+fn is_unstable_reexport(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
// Get the LocalDefId so we can lookup the item to check the kind.
let Some(def_id) = tcx.hir().opt_local_def_id(id) else { return false; };
/// Checks the crate for usage of weak lang items, returning a vector of all the
/// language items required by this crate, but not defined yet.
-pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, items: &mut lang_items::LanguageItems) {
+pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems) {
// These are never called by user code, they're generated by the compiler.
// They will never implicitly be added to the `missing` array unless we do
// so here.
verify(tcx, items);
}
-fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
+fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) {
// We only need to check for the presence of weak lang items if we're
// emitting something that's not an rlib.
let needs_check = tcx.sess.crate_types().iter().any(|kind| match *kind {
*self.serialized_data.write() = None;
}
- fn serialize<'tcx>(&self, tcx: TyCtxt<'tcx>, encoder: FileEncoder) -> FileEncodeResult {
+ fn serialize(&self, tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult {
// Serializing the `DepGraph` should not modify it.
tcx.dep_graph.with_ignore(|| {
// Allocate `SourceFileIndex`es.
s.emit_str(self.as_str());
}
Entry::Occupied(o) => {
- let x = o.get().clone();
+ let x = *o.get();
s.emit_u8(SYMBOL_OFFSET);
s.emit_usize(x);
}
tls::with_related_context(**self, |icx| icx.query)
}
- fn try_collect_active_jobs(&self) -> Option<QueryMap> {
+ fn try_collect_active_jobs(&self) -> Option<QueryMap<DepKind>> {
self.queries.try_collect_active_jobs(**self)
}
#[derive(Clone, Copy)]
pub(crate) struct QueryStruct<'tcx> {
- pub try_collect_active_jobs: fn(QueryCtxt<'tcx>, &mut QueryMap) -> Option<()>,
+ pub try_collect_active_jobs: fn(QueryCtxt<'tcx>, &mut QueryMap<DepKind>) -> Option<()>,
pub alloc_self_profile_query_strings: fn(TyCtxt<'tcx>, &mut QueryKeyStringCache),
pub encode_query_results:
Option<fn(QueryCtxt<'tcx>, &mut CacheEncoder<'_, 'tcx>, &mut EncodedDepNodeIndex)>,
key: K,
kind: DepKind,
name: &'static str,
-) -> QueryStackFrame {
+) -> QueryStackFrame<DepKind> {
// Disable visible paths printing for performance reasons.
// Showing visible path instead of any path is not that important in production.
let description = ty::print::with_no_visible_paths!(
};
let ty_adt_id = key.ty_adt_id();
- QueryStackFrame::new(name, description, span, def_id, def_kind, ty_adt_id, hash)
+ QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_adt_id, hash)
}
fn try_load_from_on_disk_cache<'tcx, Q>(tcx: TyCtxt<'tcx>, dep_node: DepNode)
where
Q: QueryConfig<QueryCtxt<'tcx>>,
Q::Key: DepNodeParams<TyCtxt<'tcx>>,
- Q::Value: Value<TyCtxt<'tcx>>,
+ Q::Value: Value<TyCtxt<'tcx>, DepKind>,
{
// We must avoid ever having to call `force_from_dep_node()` for a
// `DepNode::codegen_unit`:
#[cfg(debug_assertions)]
let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
let tcx = QueryCtxt::from_tcx(tcx);
- force_query::<Q, _>(tcx, key, dep_node);
+ force_query::<Q, _, DepKind>(tcx, key, dep_node);
true
} else {
false
type Cache = query_storage::$name<'tcx>;
#[inline(always)]
- fn query_state<'a>(tcx: QueryCtxt<'tcx>) -> &'a QueryState<Self::Key>
+ fn query_state<'a>(tcx: QueryCtxt<'tcx>) -> &'a QueryState<Self::Key, crate::dep_graph::DepKind>
where QueryCtxt<'tcx>: 'a
{
&tcx.queries.$name
use $crate::plumbing::{QueryStruct, QueryCtxt};
use $crate::profiling_support::QueryKeyStringCache;
use rustc_query_system::query::QueryMap;
+ use rustc_middle::dep_graph::DepKind;
pub(super) const fn dummy_query_struct<'tcx>() -> QueryStruct<'tcx> {
- fn noop_try_collect_active_jobs(_: QueryCtxt<'_>, _: &mut QueryMap) -> Option<()> {
+ fn noop_try_collect_active_jobs(_: QueryCtxt<'_>, _: &mut QueryMap<DepKind>) -> Option<()> {
None
}
fn noop_alloc_self_profile_query_strings(_: TyCtxt<'_>, _: &mut QueryKeyStringCache) {}
$(
$(#[$attr])*
$name: QueryState<
- <queries::$name<'tcx> as QueryConfig<QueryCtxt<'tcx>>>::Key
+ <queries::$name<'tcx> as QueryConfig<QueryCtxt<'tcx>>>::Key,
+ rustc_middle::dep_graph::DepKind,
>,
)*
}
pub(crate) fn try_collect_active_jobs(
&'tcx self,
tcx: TyCtxt<'tcx>,
- ) -> Option<QueryMap> {
+ ) -> Option<QueryMap<rustc_middle::dep_graph::DepKind>> {
let tcx = QueryCtxt { tcx, queries: self };
let mut jobs = QueryMap::default();
mode: QueryMode,
) -> Option<query_stored::$name<'tcx>> {
let qcx = QueryCtxt { tcx, queries: self };
- get_query::<queries::$name<'tcx>, _>(qcx, span, key, mode)
+ get_query::<queries::$name<'tcx>, _, rustc_middle::dep_graph::DepKind>(qcx, span, key, mode)
})*
}
};
/// If we are recording only summary data, the ids will point to
/// just the query names. If we are recording query keys too, we
/// allocate the corresponding strings here.
-pub fn alloc_self_profile_query_strings<'tcx>(tcx: TyCtxt<'tcx>) {
+pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) {
if !tcx.prof.enabled() {
return;
}
}
rustc_index::newtype_index! {
- pub struct DepNodeIndex { .. }
+ pub struct DepNodeIndex {}
}
impl DepNodeIndex {
// Index type for `DepNodeData`'s edges.
rustc_index::newtype_index! {
- struct EdgeIndex { .. }
+ struct EdgeIndex {}
}
/// `CurrentDepGraph` stores the dependency graph for the current session. It
// unused so that we can store multiple index types in `CompressedHybridIndex`,
// and use those bits to encode which index type it contains.
rustc_index::newtype_index! {
- pub struct SerializedDepNodeIndex {
- MAX = 0x7FFF_FFFF
- }
+ #[max = 0x7FFF_FFFF]
+ pub struct SerializedDepNodeIndex {}
}
/// Data for use when recompiling the **current crate**.
type Cache: QueryCache<Key = Self::Key, Stored = Self::Stored, Value = Self::Value>;
// Don't use this method to access query results, instead use the methods on TyCtxt
- fn query_state<'a>(tcx: Qcx) -> &'a QueryState<Self::Key>
+ fn query_state<'a>(tcx: Qcx) -> &'a QueryState<Self::Key, Qcx::DepKind>
where
Qcx: 'a;
+use crate::dep_graph::DepKind;
use crate::error::CycleStack;
use crate::query::plumbing::CycleError;
use crate::query::{QueryContext, QueryStackFrame};
+use core::marker::PhantomData;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
/// Represents a span and a query key.
#[derive(Clone, Debug)]
-pub struct QueryInfo {
+pub struct QueryInfo<D: DepKind> {
/// The span corresponding to the reason for which this query was required.
pub span: Span,
- pub query: QueryStackFrame,
+ pub query: QueryStackFrame<D>,
}
-pub type QueryMap = FxHashMap<QueryJobId, QueryJobInfo>;
+pub type QueryMap<D> = FxHashMap<QueryJobId, QueryJobInfo<D>>;
/// A value uniquely identifying an active query job.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct QueryJobId(pub NonZeroU64);
impl QueryJobId {
- fn query(self, map: &QueryMap) -> QueryStackFrame {
+ fn query<D: DepKind>(self, map: &QueryMap<D>) -> QueryStackFrame<D> {
map.get(&self).unwrap().query.clone()
}
#[cfg(parallel_compiler)]
- fn span(self, map: &QueryMap) -> Span {
+ fn span<D: DepKind>(self, map: &QueryMap<D>) -> Span {
map.get(&self).unwrap().job.span
}
#[cfg(parallel_compiler)]
- fn parent(self, map: &QueryMap) -> Option<QueryJobId> {
+ fn parent<D: DepKind>(self, map: &QueryMap<D>) -> Option<QueryJobId> {
map.get(&self).unwrap().job.parent
}
#[cfg(parallel_compiler)]
- fn latch<'a>(self, map: &'a QueryMap) -> Option<&'a QueryLatch> {
+ fn latch<D: DepKind>(self, map: &QueryMap<D>) -> Option<&QueryLatch<D>> {
map.get(&self).unwrap().job.latch.as_ref()
}
}
#[derive(Clone)]
-pub struct QueryJobInfo {
- pub query: QueryStackFrame,
- pub job: QueryJob,
+pub struct QueryJobInfo<D: DepKind> {
+ pub query: QueryStackFrame<D>,
+ pub job: QueryJob<D>,
}
/// Represents an active query job.
#[derive(Clone)]
-pub struct QueryJob {
+pub struct QueryJob<D: DepKind> {
pub id: QueryJobId,
/// The span corresponding to the reason for which this query was required.
/// The latch that is used to wait on this job.
#[cfg(parallel_compiler)]
- latch: Option<QueryLatch>,
+ latch: Option<QueryLatch<D>>,
+ spooky: core::marker::PhantomData<D>,
}
-impl QueryJob {
+impl<D: DepKind> QueryJob<D> {
/// Creates a new query job.
#[inline]
pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
parent,
#[cfg(parallel_compiler)]
latch: None,
+ spooky: PhantomData,
}
}
#[cfg(parallel_compiler)]
- pub(super) fn latch(&mut self) -> QueryLatch {
+ pub(super) fn latch(&mut self) -> QueryLatch<D> {
if self.latch.is_none() {
self.latch = Some(QueryLatch::new());
}
#[cold]
#[inline(never)]
#[cfg(not(parallel_compiler))]
- pub(super) fn find_cycle_in_stack(
+ pub(super) fn find_cycle_in_stack<D: DepKind>(
&self,
- query_map: QueryMap,
+ query_map: QueryMap<D>,
current_job: &Option<QueryJobId>,
span: Span,
- ) -> CycleError {
+ ) -> CycleError<D> {
// Find the waitee amongst `current_job` parents
let mut cycle = Vec::new();
let mut current_job = Option::clone(current_job);
#[cold]
#[inline(never)]
- pub fn try_find_layout_root(&self, query_map: QueryMap) -> Option<(QueryJobInfo, usize)> {
+ pub fn try_find_layout_root<D: DepKind>(
+ &self,
+ query_map: QueryMap<D>,
+ ) -> Option<(QueryJobInfo<D>, usize)> {
let mut last_layout = None;
let mut current_id = Some(*self);
let mut depth = 0;
while let Some(id) = current_id {
let info = query_map.get(&id).unwrap();
- if info.query.name == "layout_of" {
+ // FIXME: This string comparison should probably not be done.
+ if format!("{:?}", info.query.dep_kind) == "layout_of" {
depth += 1;
last_layout = Some((info.clone(), depth));
}
}
#[cfg(parallel_compiler)]
-struct QueryWaiter {
+struct QueryWaiter<D: DepKind> {
query: Option<QueryJobId>,
condvar: Condvar,
span: Span,
- cycle: Lock<Option<CycleError>>,
+ cycle: Lock<Option<CycleError<D>>>,
}
#[cfg(parallel_compiler)]
-impl QueryWaiter {
+impl<D: DepKind> QueryWaiter<D> {
fn notify(&self, registry: &rayon_core::Registry) {
rayon_core::mark_unblocked(registry);
self.condvar.notify_one();
}
#[cfg(parallel_compiler)]
-struct QueryLatchInfo {
+struct QueryLatchInfo<D: DepKind> {
complete: bool,
- waiters: Vec<Lrc<QueryWaiter>>,
+ waiters: Vec<Lrc<QueryWaiter<D>>>,
}
#[cfg(parallel_compiler)]
#[derive(Clone)]
-pub(super) struct QueryLatch {
- info: Lrc<Mutex<QueryLatchInfo>>,
+pub(super) struct QueryLatch<D: DepKind> {
+ info: Lrc<Mutex<QueryLatchInfo<D>>>,
}
#[cfg(parallel_compiler)]
-impl QueryLatch {
+impl<D: DepKind> QueryLatch<D> {
fn new() -> Self {
QueryLatch {
info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
}
/// Awaits for the query job to complete.
- pub(super) fn wait_on(&self, query: Option<QueryJobId>, span: Span) -> Result<(), CycleError> {
+ pub(super) fn wait_on(
+ &self,
+ query: Option<QueryJobId>,
+ span: Span,
+ ) -> Result<(), CycleError<D>> {
let waiter =
Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() });
self.wait_on_inner(&waiter);
}
/// Awaits the caller on this latch by blocking the current thread.
- fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter>) {
+ fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter<D>>) {
let mut info = self.info.lock();
if !info.complete {
// We push the waiter on to the `waiters` list. It can be accessed inside
/// Removes a single waiter from the list of waiters.
/// This is used to break query cycles.
- fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter> {
+ fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter<D>> {
let mut info = self.info.lock();
debug_assert!(!info.complete);
// Remove the waiter from the list of waiters
/// required information to resume the waiter.
/// If all `visit` calls returns None, this function also returns None.
#[cfg(parallel_compiler)]
-fn visit_waiters<F>(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option<Option<Waiter>>
+fn visit_waiters<F, D>(
+ query_map: &QueryMap<D>,
+ query: QueryJobId,
+ mut visit: F,
+) -> Option<Option<Waiter>>
where
F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
+ D: DepKind,
{
// Visit the parent query which is a non-resumable waiter since it's on the same stack
if let Some(parent) = query.parent(query_map) {
/// If a cycle is detected, this initial value is replaced with the span causing
/// the cycle.
#[cfg(parallel_compiler)]
-fn cycle_check(
- query_map: &QueryMap,
+fn cycle_check<D: DepKind>(
+ query_map: &QueryMap<D>,
query: QueryJobId,
span: Span,
stack: &mut Vec<(Span, QueryJobId)>,
/// from `query` without going through any of the queries in `visited`.
/// This is achieved with a depth first search.
#[cfg(parallel_compiler)]
-fn connected_to_root(
- query_map: &QueryMap,
+fn connected_to_root<D: DepKind>(
+ query_map: &QueryMap<D>,
query: QueryJobId,
visited: &mut FxHashSet<QueryJobId>,
) -> bool {
// Deterministically pick an query from a list
#[cfg(parallel_compiler)]
-fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T
+fn pick_query<'a, T, F, D>(query_map: &QueryMap<D>, queries: &'a [T], f: F) -> &'a T
where
F: Fn(&T) -> (Span, QueryJobId),
+ D: DepKind,
{
// Deterministically pick an entry point
// FIXME: Sort this instead
/// If a cycle was not found, the starting query is removed from `jobs` and
/// the function returns false.
#[cfg(parallel_compiler)]
-fn remove_cycle(
- query_map: &QueryMap,
+fn remove_cycle<D: DepKind>(
+ query_map: &QueryMap<D>,
jobs: &mut Vec<QueryJobId>,
- wakelist: &mut Vec<Lrc<QueryWaiter>>,
+ wakelist: &mut Vec<Lrc<QueryWaiter<D>>>,
) -> bool {
let mut visited = FxHashSet::default();
let mut stack = Vec::new();
/// There may be multiple cycles involved in a deadlock, so this searches
/// all active queries for cycles before finally resuming all the waiters at once.
#[cfg(parallel_compiler)]
-pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
+pub fn deadlock<D: DepKind>(query_map: QueryMap<D>, registry: &rayon_core::Registry) {
let on_panic = OnDrop(|| {
eprintln!("deadlock handler panicked, aborting process");
process::abort();
#[inline(never)]
#[cold]
-pub(crate) fn report_cycle<'a>(
+pub(crate) fn report_cycle<'a, D: DepKind>(
sess: &'a Session,
- CycleError { usage, cycle: stack }: &CycleError,
+ CycleError { usage, cycle: stack }: &CycleError<D>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
assert!(!stack.is_empty());
};
let mut diag = Diagnostic::new(
Level::FailureNote,
- &format!("#{} [{}] {}", i, query_info.query.name, query_info.query.description),
+ &format!("#{} [{:?}] {}", i, query_info.query.dep_kind, query_info.query.description),
);
diag.span = query_info.job.span.into();
handler.force_print_diagnostic(diag);
mod config;
pub use self::config::{QueryConfig, QueryVTable};
+use crate::dep_graph::DepKind;
use crate::dep_graph::{DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
use rustc_data_structures::sync::Lock;
use rustc_errors::Diagnostic;
///
/// This is mostly used in case of cycles for error reporting.
#[derive(Clone, Debug)]
-pub struct QueryStackFrame {
- pub name: &'static str,
+pub struct QueryStackFrame<D: DepKind> {
pub description: String,
span: Option<Span>,
pub def_id: Option<DefId>,
pub def_kind: Option<DefKind>,
pub ty_adt_id: Option<DefId>,
+ pub dep_kind: D,
/// This hash is used to deterministically pick
/// a query to remove cycles in the parallel compiler.
#[cfg(parallel_compiler)]
hash: u64,
}
-impl QueryStackFrame {
+impl<D: DepKind> QueryStackFrame<D> {
#[inline]
pub fn new(
- name: &'static str,
description: String,
span: Option<Span>,
def_id: Option<DefId>,
def_kind: Option<DefKind>,
+ dep_kind: D,
ty_adt_id: Option<DefId>,
_hash: impl FnOnce() -> u64,
) -> Self {
Self {
- name,
description,
span,
def_id,
def_kind,
ty_adt_id,
+ dep_kind,
#[cfg(parallel_compiler)]
hash: _hash(),
}
/// Get the query information from the TLS context.
fn current_query_job(&self) -> Option<QueryJobId>;
- fn try_collect_active_jobs(&self) -> Option<QueryMap>;
+ fn try_collect_active_jobs(&self) -> Option<QueryMap<Self::DepKind>>;
/// Load side effects associated to the node in the previous session.
fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects;
//! generate the actual methods on tcx which find and execute the provider,
//! manage the caches, and so forth.
-use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
+use crate::dep_graph::{DepContext, DepKind, DepNode, DepNodeIndex, DepNodeParams};
use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::config::QueryVTable;
use super::QueryConfig;
-pub struct QueryState<K> {
+pub struct QueryState<K, D: DepKind> {
#[cfg(parallel_compiler)]
- active: Sharded<FxHashMap<K, QueryResult>>,
+ active: Sharded<FxHashMap<K, QueryResult<D>>>,
#[cfg(not(parallel_compiler))]
- active: Lock<FxHashMap<K, QueryResult>>,
+ active: Lock<FxHashMap<K, QueryResult<D>>>,
}
/// Indicates the state of a query for a given key in a query map.
-enum QueryResult {
+enum QueryResult<D: DepKind> {
/// An already executing query. The query job can be used to await for its completion.
- Started(QueryJob),
+ Started(QueryJob<D>),
/// The query panicked. Queries trying to wait on this will raise a fatal error which will
/// silently panic.
Poisoned,
}
-impl<K> QueryState<K>
+impl<K, D> QueryState<K, D>
where
K: Eq + Hash + Clone + Debug,
+ D: DepKind,
{
pub fn all_inactive(&self) -> bool {
#[cfg(parallel_compiler)]
pub fn try_collect_active_jobs<Qcx: Copy>(
&self,
qcx: Qcx,
- make_query: fn(Qcx, K) -> QueryStackFrame,
- jobs: &mut QueryMap,
+ make_query: fn(Qcx, K) -> QueryStackFrame<D>,
+ jobs: &mut QueryMap<D>,
) -> Option<()> {
#[cfg(parallel_compiler)]
{
}
}
-impl<K> Default for QueryState<K> {
- fn default() -> QueryState<K> {
+impl<K, D: DepKind> Default for QueryState<K, D> {
+ fn default() -> QueryState<K, D> {
QueryState { active: Default::default() }
}
}
/// A type representing the responsibility to execute the job in the `job` field.
/// This will poison the relevant query if dropped.
-struct JobOwner<'tcx, K>
+struct JobOwner<'tcx, K, D: DepKind>
where
K: Eq + Hash + Clone,
{
- state: &'tcx QueryState<K>,
+ state: &'tcx QueryState<K, D>,
key: K,
id: QueryJobId,
}
#[cold]
#[inline(never)]
-fn mk_cycle<Qcx, V, R>(
+fn mk_cycle<Qcx, V, R, D: DepKind>(
qcx: Qcx,
- cycle_error: CycleError,
+ cycle_error: CycleError<D>,
handler: HandleCycleError,
cache: &dyn crate::query::QueryStorage<Value = V, Stored = R>,
) -> R
where
- Qcx: QueryContext,
- V: std::fmt::Debug + Value<Qcx::DepContext>,
+ Qcx: QueryContext + crate::query::HasDepContext<DepKind = D>,
+ V: std::fmt::Debug + Value<Qcx::DepContext, Qcx::DepKind>,
R: Clone,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
fn handle_cycle_error<Tcx, V>(
tcx: Tcx,
- cycle_error: &CycleError,
+ cycle_error: &CycleError<Tcx::DepKind>,
mut error: DiagnosticBuilder<'_, ErrorGuaranteed>,
handler: HandleCycleError,
) -> V
where
Tcx: DepContext,
- V: Value<Tcx>,
+ V: Value<Tcx, Tcx::DepKind>,
{
use HandleCycleError::*;
match handler {
}
}
-impl<'tcx, K> JobOwner<'tcx, K>
+impl<'tcx, K, D: DepKind> JobOwner<'tcx, K, D>
where
K: Eq + Hash + Clone,
{
#[inline(always)]
fn try_start<'b, Qcx>(
qcx: &'b Qcx,
- state: &'b QueryState<K>,
+ state: &'b QueryState<K, Qcx::DepKind>,
span: Span,
key: K,
- ) -> TryGetJob<'b, K>
+ ) -> TryGetJob<'b, K, D>
where
- Qcx: QueryContext,
+ Qcx: QueryContext + crate::query::HasDepContext<DepKind = D>,
{
#[cfg(parallel_compiler)]
let mut state_lock = state.active.get_shard_by_value(&key).lock();
}
}
-impl<'tcx, K> Drop for JobOwner<'tcx, K>
+impl<'tcx, K, D> Drop for JobOwner<'tcx, K, D>
where
K: Eq + Hash + Clone,
+ D: DepKind,
{
#[inline(never)]
#[cold]
}
#[derive(Clone)]
-pub(crate) struct CycleError {
+pub(crate) struct CycleError<D: DepKind> {
/// The query and related span that uses the cycle.
- pub usage: Option<(Span, QueryStackFrame)>,
- pub cycle: Vec<QueryInfo>,
+ pub usage: Option<(Span, QueryStackFrame<D>)>,
+ pub cycle: Vec<QueryInfo<D>>,
}
/// The result of `try_start`.
-enum TryGetJob<'tcx, K>
+enum TryGetJob<'tcx, K, D>
where
K: Eq + Hash + Clone,
+ D: DepKind,
{
/// The query is not yet started. Contains a guard to the cache eventually used to start it.
- NotYetStarted(JobOwner<'tcx, K>),
+ NotYetStarted(JobOwner<'tcx, K, D>),
/// The query was already completed.
/// Returns the result of the query and its dep-node index
JobCompleted(TimingGuard<'tcx>),
/// Trying to execute the query resulted in a cycle.
- Cycle(CycleError),
+ Cycle(CycleError<D>),
}
/// Checks if the query is already computed and in the cache.
/// which will be used if the query is not in the cache and we need
/// to compute it.
#[inline]
-pub fn try_get_cached<'a, Tcx, C, R, OnHit>(
+pub fn try_get_cached<Tcx, C, R, OnHit>(
tcx: Tcx,
- cache: &'a C,
+ cache: &C,
key: &C::Key,
// `on_hit` can be called while holding a lock to the query cache
on_hit: OnHit,
fn try_execute_query<Qcx, C>(
qcx: Qcx,
- state: &QueryState<C::Key>,
+ state: &QueryState<C::Key, Qcx::DepKind>,
cache: &C,
span: Span,
key: C::Key,
where
C: QueryCache,
C::Key: Clone + DepNodeParams<Qcx::DepContext>,
- C::Value: Value<Qcx::DepContext>,
+ C::Value: Value<Qcx::DepContext, Qcx::DepKind>,
C::Stored: Debug + std::borrow::Borrow<C::Value>,
Qcx: QueryContext,
{
- match JobOwner::<'_, C::Key>::try_start(&qcx, state, span, key.clone()) {
+ match JobOwner::<'_, C::Key, Qcx::DepKind>::try_start(&qcx, state, span, key.clone()) {
TryGetJob::NotYetStarted(job) => {
let (result, dep_node_index) = execute_job(qcx, key.clone(), dep_node, query, job.id);
if query.feedable {
Ensure,
}
-pub fn get_query<Q, Qcx>(qcx: Qcx, span: Span, key: Q::Key, mode: QueryMode) -> Option<Q::Stored>
+pub fn get_query<Q, Qcx, D>(qcx: Qcx, span: Span, key: Q::Key, mode: QueryMode) -> Option<Q::Stored>
where
+ D: DepKind,
Q: QueryConfig<Qcx>,
Q::Key: DepNodeParams<Qcx::DepContext>,
- Q::Value: Value<Qcx::DepContext>,
+ Q::Value: Value<Qcx::DepContext, D>,
Qcx: QueryContext,
{
let query = Q::make_vtable(qcx, &key);
Some(result)
}
-pub fn force_query<Q, Qcx>(qcx: Qcx, key: Q::Key, dep_node: DepNode<Qcx::DepKind>)
+pub fn force_query<Q, Qcx, D>(qcx: Qcx, key: Q::Key, dep_node: DepNode<Qcx::DepKind>)
where
+ D: DepKind,
Q: QueryConfig<Qcx>,
Q::Key: DepNodeParams<Qcx::DepContext>,
- Q::Value: Value<Qcx::DepContext>,
+ Q::Value: Value<Qcx::DepContext, D>,
Qcx: QueryContext,
{
// We may be concurrently trying both execute and force a query.
-use crate::dep_graph::DepContext;
+use crate::dep_graph::{DepContext, DepKind};
use crate::query::QueryInfo;
-pub trait Value<Tcx: DepContext>: Sized {
- fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo]) -> Self;
+pub trait Value<Tcx: DepContext, D: DepKind>: Sized {
+ fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo<D>]) -> Self;
}
-impl<Tcx: DepContext, T> Value<Tcx> for T {
- default fn from_cycle_error(tcx: Tcx, _: &[QueryInfo]) -> T {
+impl<Tcx: DepContext, T, D: DepKind> Value<Tcx, D> for T {
+ default fn from_cycle_error(tcx: Tcx, _: &[QueryInfo<D>]) -> T {
tcx.sess().abort_if_errors();
// Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
// non-trivial to define it earlier.
// Ensure there is at most one `self` in the list
let self_spans = items
.iter()
- .filter_map(|&(ref use_tree, _)| {
+ .filter_map(|(use_tree, _)| {
if let ast::UseTreeKind::Simple(..) = use_tree.kind {
if use_tree.ident().name == kw::SelfLower {
return Some(use_tree.span);
let override_suggestion =
if ["true", "false"].contains(&item_str.to_string().to_lowercase().as_str()) {
let item_typo = item_str.to_string().to_lowercase();
- Some((
- item_span,
- "you may want to use a bool value instead",
- format!("{}", item_typo),
- ))
+ Some((item_span, "you may want to use a bool value instead", item_typo))
// FIXME(vincenzopalazzo): make the check smarter,
// and maybe expand with levenshtein distance checks
} else if item_str.as_str() == "printf" {
let message = format!("consider introducing lifetime `{}` here", name);
should_continue = suggest(err, false, span, &message, sugg);
} else {
- let message = format!("consider introducing a named lifetime parameter");
+ let message = "consider introducing a named lifetime parameter";
should_continue = suggest(err, false, span, &message, sugg);
}
}
label_res_map: self.label_res_map.clone(),
lifetimes_res_map: self.lifetimes_res_map.clone(),
extra_lifetime_params_map: self.extra_lifetime_params_map.clone(),
- next_node_id: self.next_node_id.clone(),
+ next_node_id: self.next_node_id,
node_id_to_def_id: self.node_id_to_def_id.clone(),
def_id_to_node_id: self.def_id_to_node_id.clone(),
trait_map: self.trait_map.clone(),
.or_insert_with(|| self.arenas.alloc_name_resolution())
}
+ /// Test if AmbiguityError ambi is any identical to any one inside ambiguity_errors
+ fn matches_previous_ambiguity_error(&mut self, ambi: &AmbiguityError<'_>) -> bool {
+ for ambiguity_error in &self.ambiguity_errors {
+ // if the span location and ident as well as its span are the same
+ if ambiguity_error.kind == ambi.kind
+ && ambiguity_error.ident == ambi.ident
+ && ambiguity_error.ident.span == ambi.ident.span
+ && ambiguity_error.b1.span == ambi.b1.span
+ && ambiguity_error.b2.span == ambi.b2.span
+ && ambiguity_error.misc1 == ambi.misc1
+ && ambiguity_error.misc2 == ambi.misc2
+ {
+ return true;
+ }
+ }
+ false
+ }
+
fn record_use(
&mut self,
ident: Ident,
is_lexical_scope: bool,
) {
if let Some((b2, kind)) = used_binding.ambiguity {
- self.ambiguity_errors.push(AmbiguityError {
+ let ambiguity_error = AmbiguityError {
kind,
ident,
b1: used_binding,
b2,
misc1: AmbiguityErrorMisc::None,
misc2: AmbiguityErrorMisc::None,
- });
+ };
+ if !self.matches_previous_ambiguity_error(&ambiguity_error) {
+ // avoid dumplicated span information to be emitt out
+ self.ambiguity_errors.push(ambiguity_error);
+ }
}
if let NameBindingKind::Import { import, binding, ref used } = used_binding.kind {
// Avoid marking `extern crate` items that refer to a name from extern prelude,
}
}
-pub fn process_crate<'l, 'tcx, H: SaveHandler>(
- tcx: TyCtxt<'tcx>,
+pub fn process_crate<H: SaveHandler>(
+ tcx: TyCtxt<'_>,
cratename: Symbol,
- input: &'l Input,
+ input: &Input,
config: Option<Config>,
mut handler: H,
) {
pub target_triple: &'a TargetTriple,
}
+#[derive(Diagnostic)]
+#[diag(session_branch_protection_requires_aarch64)]
+pub(crate) struct BranchProtectionRequiresAArch64;
+
#[derive(Diagnostic)]
#[diag(session_split_debuginfo_unstable_platform)]
pub struct SplitDebugInfoUnstablePlatform {
let target = crate::config::host_triple();
let mut sysroot_candidates: SmallVec<[PathBuf; 2]> =
smallvec![get_or_default_sysroot().expect("Failed finding sysroot")];
- let path = current_dll_path().and_then(|s| Ok(s.canonicalize().map_err(|e| e.to_string())?));
+ let path = current_dll_path().and_then(|s| s.canonicalize().map_err(|e| e.to_string()));
if let Ok(dll) = path {
// use `parent` twice to chop off the file name and then also the
// directory containing the dll which should be either `lib` or `bin`.
}
fn default_from_rustc_driver_dll() -> Result<PathBuf, String> {
- let dll = current_dll_path().and_then(|s| Ok(canonicalize(s)))?;
+ let dll = current_dll_path().map(|s| canonicalize(s))?;
// `dll` will be in one of the following two:
// - compiler's libdir: $sysroot/lib/*.dll
"run all passes except codegen; no output"),
no_generate_arange_section: bool = (false, parse_no_flag, [TRACKED],
"omit DWARF address ranges that give faster lookups"),
+ no_jump_tables: bool = (false, parse_no_flag, [TRACKED],
+ "disable the jump tables and lookup tables that can be generated from a switch case lowering"),
no_leak_check: bool = (false, parse_no_flag, [UNTRACKED],
"disable the 'leak check' for subtyping; unsound, but useful for tests"),
no_link: bool = (false, parse_no_flag, [TRACKED],
/// Construct a future incompatibility diagnostic for a feature gate.
///
/// This diagnostic is only a warning and *does not cause compilation to fail*.
-pub fn feature_warn<'a>(sess: &'a ParseSess, feature: Symbol, span: Span, explain: &str) {
+pub fn feature_warn(sess: &ParseSess, feature: Symbol, span: Span, explain: &str) {
feature_warn_issue(sess, feature, span, GateIssue::Language, explain);
}
/// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`.
#[allow(rustc::diagnostic_outside_of_impl)]
#[allow(rustc::untranslatable_diagnostic)]
-pub fn feature_warn_issue<'a>(
- sess: &'a ParseSess,
+pub fn feature_warn_issue(
+ sess: &ParseSess,
feature: Symbol,
span: Span,
issue: GateIssue,
}
/// Adds the diagnostics for a feature to an existing error.
-pub fn add_feature_diagnostics<'a>(err: &mut Diagnostic, sess: &'a ParseSess, feature: Symbol) {
+pub fn add_feature_diagnostics(err: &mut Diagnostic, sess: &ParseSess, feature: Symbol) {
add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language);
}
/// This variant allows you to control whether it is a library or language feature.
/// Almost always, you want to use this for a language feature. If so, prefer
/// `add_feature_diagnostics`.
-pub fn add_feature_diagnostics_for_issue<'a>(
+pub fn add_feature_diagnostics_for_issue(
err: &mut Diagnostic,
- sess: &'a ParseSess,
+ sess: &ParseSess,
feature: Symbol,
issue: GateIssue,
) {
pub use crate::code_stats::{DataTypeKind, FieldInfo, SizeKind, VariantInfo};
use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
use crate::errors::{
- CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers, LinkerPluginToWindowsNotSupported,
- NotCircumventFeature, ProfileSampleUseFileDoesNotExist, ProfileUseFileDoesNotExist,
- SanitizerCfiEnabled, SanitizerNotSupported, SanitizersNotSupported, SkippingConstChecks,
- SplitDebugInfoUnstablePlatform, StackProtectorNotSupportedForTarget,
+ BranchProtectionRequiresAArch64, CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers,
+ LinkerPluginToWindowsNotSupported, NotCircumventFeature, ProfileSampleUseFileDoesNotExist,
+ ProfileUseFileDoesNotExist, SanitizerCfiEnabled, SanitizerNotSupported, SanitizersNotSupported,
+ SkippingConstChecks, SplitDebugInfoUnstablePlatform, StackProtectorNotSupportedForTarget,
TargetRequiresUnwindTables, UnleashedFeatureHelp, UnstableVirtualFunctionElimination,
UnsupportedDwarfVersion,
};
let warnings_allow = sopts
.lint_opts
.iter()
- .rfind(|&&(ref key, _)| *key == "warnings")
+ .rfind(|&(key, _)| *key == "warnings")
.map_or(false, |&(_, level)| level == lint::Allow);
let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow);
let can_emit_warnings = !(warnings_allow || cap_lints_allow);
}
}
+ if sess.opts.unstable_opts.branch_protection.is_some() && sess.target.arch != "aarch64" {
+ sess.emit_err(BranchProtectionRequiresAArch64);
+ }
+
if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version {
if dwarf_version > 5 {
sess.emit_err(UnsupportedDwarfVersion { dwarf_version });
use std::path::{Path, PathBuf};
impl Session {
- pub fn timer<'a>(&'a self, what: &'static str) -> VerboseTimingGuard<'a> {
+ pub fn timer(&self, what: &'static str) -> VerboseTimingGuard<'_> {
self.prof.verbose_generic_activity(what)
}
pub fn time<R>(&self, what: &'static str, f: impl FnOnce() -> R) -> R {
rustc_arena = { path = "../rustc_arena" }
scoped-tls = "1.0"
unicode-width = "0.1.4"
-cfg-if = "0.1.2"
+cfg-if = "1.0"
tracing = "0.1"
sha1 = { package = "sha-1", version = "0.10.0" }
sha2 = "0.10.1"
use std::hash::{Hash, Hasher};
rustc_index::newtype_index! {
- pub struct CrateNum {
- ENCODABLE = custom
- DEBUG_FORMAT = "crate{}"
- }
+ #[custom_encodable]
+ #[debug_format = "crate{}"]
+ pub struct CrateNum {}
}
/// Item definitions in the currently-compiled crate would have the `CrateNum`
/// A DefIndex is an index into the hir-map for a crate, identifying a
/// particular definition. It should really be considered an interned
/// shorthand for a particular DefPath.
+ #[custom_encodable] // (only encodable in metadata)
+ #[debug_format = "DefIndex({})"]
pub struct DefIndex {
- ENCODABLE = custom // (only encodable in metadata)
-
- DEBUG_FORMAT = "DefIndex({})",
/// The crate root is always assigned index 0 by the AST Map code,
/// thanks to `NodeCollector::new`.
- const CRATE_DEF_INDEX = 0,
+ const CRATE_DEF_INDEX = 0;
}
}
rustc_index::newtype_index! {
/// A unique ID associated with a macro invocation and expansion.
- pub struct ExpnIndex {
- ENCODABLE = custom
- }
+ #[custom_encodable]
+ pub struct ExpnIndex {}
}
/// A unique ID associated with a macro invocation and expansion.
rustc_index::newtype_index! {
/// A unique ID associated with a macro invocation and expansion.
- pub struct LocalExpnId {
- ENCODABLE = custom
- ORD_IMPL = custom
- DEBUG_FORMAT = "expn{}"
- }
+ #[custom_encodable]
+ #[no_ord_impl]
+ #[debug_format = "expn{}"]
+ pub struct LocalExpnId {}
}
// To ensure correctness of incremental compilation,
Capture,
Center,
Clone,
- Context,
Continue,
Copy,
Count,
Relaxed,
Release,
Result,
+ ResumeTy,
Return,
Right,
Rust,
assert_eq_macro,
assert_inhabited,
assert_macro,
+ assert_mem_uninitialized_valid,
assert_ne_macro,
assert_receiver_is_total_eq,
- assert_uninit_valid,
assert_zero_valid,
asserting,
associated_const_equality,
generic_associated_types_extended,
generic_const_exprs,
generic_param_attrs,
+ get_context,
global_allocator,
global_asm,
globs,
pub struct Symbol(SymbolIndex);
rustc_index::newtype_index! {
- struct SymbolIndex { .. }
+ struct SymbolIndex {}
}
impl Symbol {
ty::Adt(adt_def, ..) => {
let def_id = adt_def.0.did;
let crate_name = tcx.crate_name(def_id.krate);
- if tcx.item_name(def_id).as_str() == "c_void"
+ tcx.item_name(def_id).as_str() == "c_void"
&& (crate_name == sym::core || crate_name == sym::std || crate_name == sym::libc)
- {
- true
- } else {
- false
- }
}
_ => false,
}
) -> String {
// <predicate1[..predicateN]>E as part of vendor extended type
let mut s = String::new();
- let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> =
- predicates.iter().map(|predicate| predicate).collect();
+ let predicates: Vec<ty::PolyExistentialPredicate<'tcx>> = predicates.iter().collect();
for predicate in predicates {
s.push_str(&encode_predicate(tcx, predicate, dict, options));
}
) -> String {
// [I<subst1..substN>E] as part of vendor extended type
let mut s = String::new();
- let substs: Vec<GenericArg<'_>> = substs.iter().map(|subst| subst).collect();
+ let substs: Vec<GenericArg<'_>> = substs.iter().collect();
if !substs.is_empty() {
s.push('I');
for subst in substs {
}
/// Encodes a ty:Ty name, including its crate and path disambiguators and names.
-fn encode_ty_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> String {
+fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
// Encode <name> for use in u<length><name>[I<element-type1..element-typeN>E], where
// <element-type> is <subst>, using v0's <path> without v0's extended form of paths:
//
tcx.layout_of(param_env.and(ty)).map_or(false, |layout| layout.is_zst());
!is_zst
});
- if field.is_none() {
- // Transform repr(transparent) types without non-ZST field into ()
- ty = tcx.mk_unit();
- } else {
- let ty0 = tcx.type_of(field.unwrap().did);
+ if let Some(field) = field {
+ let ty0 = tcx.type_of(field.did);
// Generalize any repr(transparent) user-defined type that is either a pointer
// or reference, and either references itself or any other type that contains or
// references itself, to avoid a reference cycle.
} else {
ty = transform_ty(tcx, ty0, options);
}
+ } else {
+ // Transform repr(transparent) types without non-ZST field into ()
+ ty = tcx.mk_unit();
}
} else {
ty = tcx.mk_adt(*adt_def, transform_substs(tcx, substs, options));
#[derive(Copy, Clone)]
struct CannotUseFpConv;
-fn is_loongarch_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool {
+fn is_loongarch_aggregate<Ty>(arg: &ArgAbi<'_, Ty>) -> bool {
match arg.layout.abi {
Abi::Vector { .. } => true,
_ => arg.layout.is_aggregate(),
}
}
-fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) {
+fn extend_integer_width<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) {
if let Abi::Scalar(scalar) = arg.layout.abi {
if let abi::Int(i, _) = scalar.primitive() {
// 32-bit integers are always sign-extended
#[derive(Copy, Clone)]
struct CannotUseFpConv;
-fn is_riscv_aggregate<'a, Ty>(arg: &ArgAbi<'a, Ty>) -> bool {
+fn is_riscv_aggregate<Ty>(arg: &ArgAbi<'_, Ty>) -> bool {
match arg.layout.abi {
Abi::Vector { .. } => true,
_ => arg.layout.is_aggregate(),
}
}
-fn extend_integer_width<'a, Ty>(arg: &mut ArgAbi<'a, Ty>, xlen: u64) {
+fn extend_integer_width<Ty>(arg: &mut ArgAbi<'_, Ty>, xlen: u64) {
if let Abi::Scalar(scalar) = arg.layout.abi {
if let abi::Int(i, _) = scalar.primitive() {
// 32-bit integers are always sign-extended
}
rustc_index::newtype_index! {
- pub struct VariantIdx {
- derive [HashStable_Generic]
- }
+ #[derive(HashStable_Generic)]
+ pub struct VariantIdx {}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
}
impl Target {
- pub fn parse_data_layout<'a>(&'a self) -> Result<TargetDataLayout, TargetDataLayoutErrors<'a>> {
+ pub fn parse_data_layout(&self) -> Result<TargetDataLayout, TargetDataLayoutErrors<'_>> {
let mut dl = TargetDataLayout::parse_from_llvm_datalayout_string(&self.data_layout)?;
// Perform consistency checks against the Target information.
#![feature(let_chains)]
#![feature(if_let_guard)]
#![feature(never_type)]
+#![feature(result_option_inspect)]
#![feature(type_alias_impl_trait)]
#![recursion_limit = "512"] // For rustdoc
pub mod autoderef;
pub mod errors;
pub mod infer;
+pub mod solve;
pub mod traits;
--- /dev/null
+//! Code shared by trait and projection goals for candidate assembly.
+
+use super::infcx_ext::InferCtxtExt;
+use super::{
+ fixme_instantiate_canonical_query_response, CanonicalGoal, CanonicalResponse, Certainty,
+ EvalCtxt, Goal,
+};
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{
+ canonical::{CanonicalVarValues, OriginalQueryValues},
+ InferCtxt,
+};
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::ty::TypeFoldable;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+use std::fmt::Debug;
+
+/// A candidate is a possible way to prove a goal.
+///
+/// It consists of both the `source`, which describes how that goal would be proven,
+/// and the `result` when using the given `source`.
+///
+/// For the list of possible candidates, please look at the documentation of
+/// [super::trait_goals::CandidateSource] and [super::project_goals::CandidateSource].
+#[derive(Debug, Clone)]
+pub(super) struct Candidate<'tcx, G: GoalKind<'tcx>> {
+ pub(super) source: G::CandidateSource,
+ pub(super) result: CanonicalResponse<'tcx>,
+}
+
+pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy {
+ type CandidateSource: Debug + Copy;
+
+ fn self_ty(self) -> Ty<'tcx>;
+
+ fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
+
+ fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
+
+ fn consider_impl_candidate(
+ acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+ goal: Goal<'tcx, Self>,
+ impl_def_id: DefId,
+ );
+}
+
+/// An abstraction which correctly deals with the canonical results for candidates.
+///
+/// It also deduplicates the behavior between trait and projection predicates.
+pub(super) struct AssemblyCtxt<'a, 'tcx, G: GoalKind<'tcx>> {
+ pub(super) cx: &'a mut EvalCtxt<'tcx>,
+ pub(super) infcx: &'a InferCtxt<'tcx>,
+ var_values: CanonicalVarValues<'tcx>,
+ candidates: Vec<Candidate<'tcx, G>>,
+}
+
+impl<'a, 'tcx, G: GoalKind<'tcx>> AssemblyCtxt<'a, 'tcx, G> {
+ pub(super) fn assemble_and_evaluate_candidates(
+ cx: &'a mut EvalCtxt<'tcx>,
+ goal: CanonicalGoal<'tcx, G>,
+ ) -> Vec<Candidate<'tcx, G>> {
+ let (ref infcx, goal, var_values) =
+ cx.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &goal);
+ let mut acx = AssemblyCtxt { cx, infcx, var_values, candidates: Vec::new() };
+
+ acx.assemble_candidates_after_normalizing_self_ty(goal);
+
+ acx.assemble_impl_candidates(goal);
+
+ acx.candidates
+ }
+
+ pub(super) fn try_insert_candidate(
+ &mut self,
+ source: G::CandidateSource,
+ certainty: Certainty,
+ ) {
+ match self.infcx.make_canonical_response(self.var_values.clone(), certainty) {
+ Ok(result) => self.candidates.push(Candidate { source, result }),
+ Err(NoSolution) => debug!(?source, ?certainty, "failed leakcheck"),
+ }
+ }
+
+ /// If the self type of a goal is a projection, computing the relevant candidates is difficult.
+ ///
+ /// To deal with this, we first try to normalize the self type and add the candidates for the normalized
+ /// self type to the list of candidates in case that succeeds. Note that we can't just eagerly return in
+ /// this case as projections as self types add `
+ fn assemble_candidates_after_normalizing_self_ty(&mut self, goal: Goal<'tcx, G>) {
+ let tcx = self.cx.tcx;
+ // FIXME: We also have to normalize opaque types, not sure where to best fit that in.
+ let &ty::Alias(ty::Projection, projection_ty) = goal.predicate.self_ty().kind() else {
+ return
+ };
+ self.infcx.probe(|_| {
+ let normalized_ty = self.infcx.next_ty_infer();
+ let normalizes_to_goal = goal.with(
+ tcx,
+ ty::Binder::dummy(ty::ProjectionPredicate {
+ projection_ty,
+ term: normalized_ty.into(),
+ }),
+ );
+ let normalization_certainty =
+ match self.cx.evaluate_goal(&self.infcx, normalizes_to_goal) {
+ Ok((_, certainty)) => certainty,
+ Err(NoSolution) => return,
+ };
+
+ // NOTE: Alternatively we could call `evaluate_goal` here and only have a `Normalized` candidate.
+ // This doesn't work as long as we use `CandidateSource` in both winnowing and to resolve associated items.
+ let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
+ let mut orig_values = OriginalQueryValues::default();
+ let goal = self.infcx.canonicalize_query(goal, &mut orig_values);
+ let normalized_candidates =
+ AssemblyCtxt::assemble_and_evaluate_candidates(self.cx, goal);
+
+ // Map each candidate from being canonical wrt the current inference context to being
+ // canonical wrt the caller.
+ for Candidate { source, result } in normalized_candidates {
+ self.infcx.probe(|_| {
+ let candidate_certainty = fixme_instantiate_canonical_query_response(
+ &self.infcx,
+ &orig_values,
+ result,
+ );
+
+ // FIXME: This is a bit scary if the `normalizes_to_goal` overflows.
+ //
+ // If we have an ambiguous candidate it hides that normalization
+ // caused an overflow which may cause issues.
+ self.try_insert_candidate(
+ source,
+ normalization_certainty.unify_and(candidate_certainty),
+ )
+ })
+ }
+ })
+ }
+
+ fn assemble_impl_candidates(&mut self, goal: Goal<'tcx, G>) {
+ self.cx.tcx.for_each_relevant_impl(
+ goal.predicate.trait_def_id(self.cx.tcx),
+ goal.predicate.self_ty(),
+ |impl_def_id| G::consider_impl_candidate(self, goal, impl_def_id),
+ );
+ }
+}
--- /dev/null
+//! This module both handles the global cache which stores "finished" goals,
+//! and the provisional cache which contains partially computed goals.
+//!
+//! The provisional cache is necessary when dealing with coinductive cycles.
+//!
+//! For more information about the provisional cache and coinduction in general,
+//! check out the relevant section of the rustc-dev-guide.
+//!
+//! FIXME(@lcnr): Write that section, feel free to ping me if you need help here
+//! before then or if I still haven't done that before January 2023.
+use super::overflow::OverflowData;
+use super::CanonicalGoal;
+use super::{EvalCtxt, QueryResult};
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_middle::ty::TyCtxt;
+use std::{cmp::Ordering, collections::hash_map::Entry};
+
+#[derive(Debug, Clone)]
+struct ProvisionalEntry<'tcx> {
+ // In case we have a coinductive cycle, this is the
+ // the currently least restrictive result of this goal.
+ response: QueryResult<'tcx>,
+ // The lowest element on the stack on which this result
+ // relies on. Starts out as just being the depth at which
+ // we've proven this obligation, but gets lowered to the
+ // depth of another goal if we rely on it in a cycle.
+ depth: usize,
+}
+
+struct StackElem<'tcx> {
+ goal: CanonicalGoal<'tcx>,
+ has_been_used: bool,
+}
+
+/// The cache used for goals which are currently in progress or which depend
+/// on in progress results.
+///
+/// Once we're done with a goal we can store it in the global trait solver
+/// cache of the `TyCtxt`. For goals which we're currently proving, or which
+/// have only been proven via a coinductive cycle using a goal still on our stack
+/// we have to use this separate data structure.
+///
+/// The current data structure is not perfect, so there may still be room for
+/// improvement here. We have the following requirements:
+///
+/// ## Is there is a provisional entry for the given goal:
+///
+/// ```ignore (for syntax highlighting)
+/// self.entries.get(goal)
+/// ```
+///
+/// ## Get all goals on the stack involved in a cycle:
+///
+/// ```ignore (for syntax highlighting)
+/// let entry = self.entries.get(goal).unwrap();
+/// let involved_goals = self.stack.iter().skip(entry.depth);
+/// ```
+///
+/// ## Capping the depth of all entries
+///
+/// Needed whenever we encounter a cycle. The current implementation always
+/// iterates over all entries instead of only the ones with a larger depth.
+/// Changing this may result in notable performance improvements.
+///
+/// ```ignore (for syntax highlighting)
+/// let cycle_depth = self.entries.get(goal).unwrap().depth;
+/// for e in &mut self.entries {
+/// e.depth = e.depth.min(cycle_depth);
+/// }
+/// ```
+///
+/// ## Checking whether we have to rerun the current goal
+///
+/// A goal has to be rerun if its provisional result was used in a cycle
+/// and that result is different from its final result. We update
+/// [StackElem::has_been_used] for the deepest stack element involved in a cycle.
+///
+/// ## Moving all finished goals into the global cache
+///
+/// If `stack_elem.has_been_used` is true, iterate over all entries, moving the ones
+/// with equal depth. If not, simply move this single entry.
+pub(super) struct ProvisionalCache<'tcx> {
+ stack: Vec<StackElem<'tcx>>,
+ entries: FxHashMap<CanonicalGoal<'tcx>, ProvisionalEntry<'tcx>>,
+}
+
+impl<'tcx> ProvisionalCache<'tcx> {
+ pub(super) fn empty() -> ProvisionalCache<'tcx> {
+ ProvisionalCache { stack: Vec::new(), entries: Default::default() }
+ }
+
+ pub(super) fn current_depth(&self) -> usize {
+ self.stack.len()
+ }
+}
+
+impl<'tcx> EvalCtxt<'tcx> {
+ /// Tries putting the new goal on the stack, returning an error if it is already cached.
+ ///
+ /// This correctly updates the provisional cache if there is a cycle.
+ pub(super) fn try_push_stack(
+ &mut self,
+ goal: CanonicalGoal<'tcx>,
+ ) -> Result<(), QueryResult<'tcx>> {
+ // FIXME: start by checking the global cache
+
+ // Look at the provisional cache to check for cycles.
+ let cache = &mut self.provisional_cache;
+ match cache.entries.entry(goal) {
+ // No entry, simply push this goal on the stack after dealing with overflow.
+ Entry::Vacant(v) => {
+ if self.overflow_data.has_overflow(cache.stack.len()) {
+ return Err(self.deal_with_overflow());
+ }
+
+ v.insert(ProvisionalEntry {
+ response: fixme_response_yes_no_constraints(),
+ depth: cache.stack.len(),
+ });
+ cache.stack.push(StackElem { goal, has_been_used: false });
+ Ok(())
+ }
+ // We have a nested goal which relies on a goal `root` deeper in the stack.
+ //
+ // We first store that we may have to rerun `evaluate_goal` for `root` in case the
+ // provisional response is not equal to the final response. We also update the depth
+ // of all goals which recursively depend on our current goal to depend on `root`
+ // instead.
+ //
+ // Finally we can return either the provisional response for that goal if we have a
+ // coinductive cycle or an ambiguous result if the cycle is inductive.
+ Entry::Occupied(entry) => {
+ // FIXME: `ProvisionalEntry` should be `Copy`.
+ let entry = entry.get().clone();
+ cache.stack[entry.depth].has_been_used = true;
+ for provisional_entry in cache.entries.values_mut() {
+ provisional_entry.depth = provisional_entry.depth.min(entry.depth);
+ }
+
+ // NOTE: The goals on the stack aren't the only goals involved in this cycle.
+ // We can also depend on goals which aren't part of the stack but coinductively
+ // depend on the stack themselves. We already checked whether all the goals
+ // between these goals and their root on the stack. This means that as long as
+ // each goal in a cycle is checked for coinductivity by itself simply checking
+ // the stack is enough.
+ if cache.stack[entry.depth..]
+ .iter()
+ .all(|g| g.goal.value.predicate.is_coinductive(self.tcx))
+ {
+ Err(entry.response)
+ } else {
+ Err(fixme_response_maybe_no_constraints())
+ }
+ }
+ }
+ }
+
+ /// We cannot simply store the result of [EvalCtxt::compute_goal] as we have to deal with
+ /// coinductive cycles.
+ ///
+ /// When we encounter a coinductive cycle, we have to prove the final result of that cycle
+ /// while we are still computing that result. Because of this we continously recompute the
+ /// cycle until the result of the previous iteration is equal to the final result, at which
+ /// point we are done.
+ ///
+ /// This function returns `true` if we were able to finalize the goal and `false` if it has
+ /// updated the provisional cache and we have to recompute the current goal.
+ ///
+ /// FIXME: Refer to the rustc-dev-guide entry once it exists.
+ pub(super) fn try_finalize_goal(
+ &mut self,
+ actual_goal: CanonicalGoal<'tcx>,
+ response: QueryResult<'tcx>,
+ ) -> bool {
+ let cache = &mut self.provisional_cache;
+ let StackElem { goal, has_been_used } = cache.stack.pop().unwrap();
+ assert_eq!(goal, actual_goal);
+
+ let provisional_entry = cache.entries.get_mut(&goal).unwrap();
+ // Check whether the current stack entry is the root of a cycle.
+ //
+ // If so, we either move all participants of that cycle to the global cache
+ // or, in case the provisional response used in the cycle is not equal to the
+ // final response, have to recompute the goal after updating the provisional
+ // response to the final response of this iteration.
+ if has_been_used {
+ if provisional_entry.response == response {
+ // We simply drop all entries according to an immutable condition, so
+ // query instability is not a concern here.
+ #[allow(rustc::potential_query_instability)]
+ cache.entries.retain(|goal, entry| match entry.depth.cmp(&cache.stack.len()) {
+ Ordering::Less => true,
+ Ordering::Equal => {
+ Self::try_move_finished_goal_to_global_cache(
+ self.tcx,
+ &mut self.overflow_data,
+ &cache.stack,
+ // FIXME: these should be `Copy` :(
+ goal.clone(),
+ entry.response.clone(),
+ );
+ false
+ }
+ Ordering::Greater => bug!("entry with greater depth than the current leaf"),
+ });
+
+ true
+ } else {
+ provisional_entry.response = response;
+ cache.stack.push(StackElem { goal, has_been_used: false });
+ false
+ }
+ } else {
+ Self::try_move_finished_goal_to_global_cache(
+ self.tcx,
+ &mut self.overflow_data,
+ &cache.stack,
+ goal,
+ response,
+ );
+ cache.entries.remove(&goal);
+ true
+ }
+ }
+
+ fn try_move_finished_goal_to_global_cache(
+ tcx: TyCtxt<'tcx>,
+ overflow_data: &mut OverflowData,
+ stack: &[StackElem<'tcx>],
+ goal: CanonicalGoal<'tcx>,
+ response: QueryResult<'tcx>,
+ ) {
+ // We move goals to the global cache if we either did not hit an overflow or if it's
+ // the root goal as that will now always hit the same overflow limit.
+ //
+ // NOTE: We cannot move any non-root goals to the global cache even if their final result
+ // isn't impacted by the overflow as that goal still has unstable query dependencies
+ // because it didn't go its full depth.
+ //
+ // FIXME(@lcnr): We could still cache subtrees which are not impacted by overflow though.
+ // Tracking that info correctly isn't trivial, so I haven't implemented it for now.
+ let should_cache_globally = !overflow_data.did_overflow() || stack.is_empty();
+ if should_cache_globally {
+ // FIXME: move the provisional entry to the global cache.
+ let _ = (tcx, goal, response);
+ }
+ }
+}
+
+fn fixme_response_yes_no_constraints<'tcx>() -> QueryResult<'tcx> {
+ unimplemented!()
+}
+
+fn fixme_response_maybe_no_constraints<'tcx>() -> QueryResult<'tcx> {
+ unimplemented!()
+}
--- /dev/null
+use std::mem;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_infer::{
+ infer::InferCtxt,
+ traits::{query::NoSolution, FulfillmentError, PredicateObligation, TraitEngine},
+};
+use rustc_middle::ty;
+
+use super::{Certainty, EvalCtxt};
+
+/// A trait engine using the new trait solver.
+///
+/// This is mostly identical to how `evaluate_all` works inside of the
+/// solver, except that the requirements are slightly different.
+///
+/// Unlike `evaluate_all` it is possible to add new obligations later on
+/// and we also have to track diagnostics information by using `Obligation`
+/// instead of `Goal`.
+///
+/// It is also likely that we want to use slightly different datastructures
+/// here as this will have to deal with far more root goals than `evaluate_all`.
+pub struct FulfillmentCtxt<'tcx> {
+ obligations: Vec<PredicateObligation<'tcx>>,
+}
+
+impl<'tcx> FulfillmentCtxt<'tcx> {
+ pub fn new() -> FulfillmentCtxt<'tcx> {
+ FulfillmentCtxt { obligations: Vec::new() }
+ }
+}
+
+impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
+ fn register_predicate_obligation(
+ &mut self,
+ _infcx: &InferCtxt<'tcx>,
+ obligation: PredicateObligation<'tcx>,
+ ) {
+ self.obligations.push(obligation);
+ }
+
+ fn select_all_or_error(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ let errors = self.select_where_possible(infcx);
+ if !errors.is_empty() {
+ return errors;
+ }
+
+ if self.obligations.is_empty() {
+ Vec::new()
+ } else {
+ unimplemented!("ambiguous obligations")
+ }
+ }
+
+ fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<FulfillmentError<'tcx>> {
+ let errors = Vec::new();
+ for i in 0.. {
+ if !infcx.tcx.recursion_limit().value_within_limit(i) {
+ unimplemented!("overflow")
+ }
+
+ let mut has_changed = false;
+ for o in mem::take(&mut self.obligations) {
+ let mut cx = EvalCtxt::new(infcx.tcx);
+ let (changed, certainty) = match cx.evaluate_goal(infcx, o.clone().into()) {
+ Ok(result) => result,
+ Err(NoSolution) => unimplemented!("error"),
+ };
+
+ has_changed |= changed;
+ match certainty {
+ Certainty::Yes => {}
+ Certainty::Maybe(_) => self.obligations.push(o),
+ }
+ }
+
+ if !has_changed {
+ break;
+ }
+ }
+
+ errors
+ }
+
+ fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
+ self.obligations.clone()
+ }
+
+ fn relationships(&mut self) -> &mut FxHashMap<ty::TyVid, ty::FoundRelationships> {
+ unimplemented!("Should be moved out of `TraitEngine`")
+ }
+}
--- /dev/null
+use rustc_infer::infer::canonical::CanonicalVarValues;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::InferCtxt;
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::ty::Ty;
+use rustc_span::DUMMY_SP;
+
+use crate::solve::ExternalConstraints;
+
+use super::{Certainty, QueryResult, Response};
+
+/// Methods used inside of the canonical queries of the solver.
+pub(super) trait InferCtxtExt<'tcx> {
+ fn next_ty_infer(&self) -> Ty<'tcx>;
+
+ fn make_canonical_response(
+ &self,
+ var_values: CanonicalVarValues<'tcx>,
+ certainty: Certainty,
+ ) -> QueryResult<'tcx>;
+}
+
+impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
+ fn next_ty_infer(&self) -> Ty<'tcx> {
+ self.next_ty_var(TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: DUMMY_SP,
+ })
+ }
+
+ fn make_canonical_response(
+ &self,
+ var_values: CanonicalVarValues<'tcx>,
+ certainty: Certainty,
+ ) -> QueryResult<'tcx> {
+ let external_constraints = take_external_constraints(self)?;
+
+ Ok(self.canonicalize_response(Response { var_values, external_constraints, certainty }))
+ }
+}
+
+#[instrument(level = "debug", skip(infcx), ret)]
+fn take_external_constraints<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+) -> Result<ExternalConstraints<'tcx>, NoSolution> {
+ let region_obligations = infcx.take_registered_region_obligations();
+ let opaque_types = infcx.take_opaque_types_for_query_response();
+ Ok(ExternalConstraints {
+ // FIXME: Now that's definitely wrong :)
+ //
+ // Should also do the leak check here I think
+ regions: drop(region_obligations),
+ opaque_types,
+ })
+}
--- /dev/null
+//! The new trait solver, currently still WIP.
+//!
+//! As a user of the trait system, you can use `TyCtxt::evaluate_goal` to
+//! interact with this solver.
+//!
+//! For a high-level overview of how this solver works, check out the relevant
+//! section of the rustc-dev-guide.
+//!
+//! FIXME(@lcnr): Write that section. If you read this before then ask me
+//! about it on zulip.
+
+// FIXME: Instead of using `infcx.canonicalize_query` we have to add a new routine which
+// preserves universes and creates a unique var (in the highest universe) for each
+// appearance of a region.
+
+// FIXME: `CanonicalVarValues` should be interned and `Copy`.
+
+// FIXME: uses of `infcx.at` need to enable deferred projection equality once that's implemented.
+
+use std::mem;
+
+use rustc_infer::infer::canonical::OriginalQueryValues;
+use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::Obligation;
+use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{RegionOutlivesPredicate, ToPredicate, TypeOutlivesPredicate};
+use rustc_span::DUMMY_SP;
+
+use self::infcx_ext::InferCtxtExt;
+
+mod assembly;
+mod cache;
+mod fulfill;
+mod infcx_ext;
+mod overflow;
+mod project_goals;
+mod trait_goals;
+
+pub use fulfill::FulfillmentCtxt;
+
+/// A goal is a statement, i.e. `predicate`, we want to prove
+/// given some assumptions, i.e. `param_env`.
+///
+/// Most of the time the `param_env` contains the `where`-bounds of the function
+/// we're currently typechecking while the `predicate` is some trait bound.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub struct Goal<'tcx, P> {
+ param_env: ty::ParamEnv<'tcx>,
+ predicate: P,
+}
+
+impl<'tcx, P> Goal<'tcx, P> {
+ pub fn new(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ predicate: impl ToPredicate<'tcx, P>,
+ ) -> Goal<'tcx, P> {
+ Goal { param_env, predicate: predicate.to_predicate(tcx) }
+ }
+
+ /// Updates the goal to one with a different `predicate` but the same `param_env`.
+ fn with<Q>(self, tcx: TyCtxt<'tcx>, predicate: impl ToPredicate<'tcx, Q>) -> Goal<'tcx, Q> {
+ Goal { param_env: self.param_env, predicate: predicate.to_predicate(tcx) }
+ }
+}
+
+impl<'tcx, P> From<Obligation<'tcx, P>> for Goal<'tcx, P> {
+ fn from(obligation: Obligation<'tcx, P>) -> Goal<'tcx, P> {
+ Goal { param_env: obligation.param_env, predicate: obligation.predicate }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
+pub struct Response<'tcx> {
+ pub var_values: CanonicalVarValues<'tcx>,
+ /// Additional constraints returned by this query.
+ pub external_constraints: ExternalConstraints<'tcx>,
+ pub certainty: Certainty,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub enum Certainty {
+ Yes,
+ Maybe(MaybeCause),
+}
+
+impl Certainty {
+ /// When proving multiple goals using **AND**, e.g. nested obligations for an impl,
+ /// use this function to unify the certainty of these goals
+ pub fn unify_and(self, other: Certainty) -> Certainty {
+ match (self, other) {
+ (Certainty::Yes, Certainty::Yes) => Certainty::Yes,
+ (Certainty::Yes, Certainty::Maybe(_)) => other,
+ (Certainty::Maybe(_), Certainty::Yes) => self,
+ (Certainty::Maybe(MaybeCause::Overflow), Certainty::Maybe(MaybeCause::Overflow)) => {
+ Certainty::Maybe(MaybeCause::Overflow)
+ }
+ // If at least one of the goals is ambiguous, hide the overflow as the ambiguous goal
+ // may still result in failure.
+ (Certainty::Maybe(MaybeCause::Ambiguity), Certainty::Maybe(_))
+ | (Certainty::Maybe(_), Certainty::Maybe(MaybeCause::Ambiguity)) => {
+ Certainty::Maybe(MaybeCause::Ambiguity)
+ }
+ }
+ }
+}
+
+/// Why we failed to evaluate a goal.
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
+pub enum MaybeCause {
+ /// We failed due to ambiguity. This ambiguity can either
+ /// be a true ambiguity, i.e. there are multiple different answers,
+ /// or we hit a case where we just don't bother, e.g. `?x: Trait` goals.
+ Ambiguity,
+ /// We gave up due to an overflow, most often by hitting the recursion limit.
+ Overflow,
+}
+
+/// Additional constraints returned on success.
+#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
+pub struct ExternalConstraints<'tcx> {
+ // FIXME: implement this.
+ regions: (),
+ opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
+}
+
+type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
+type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
+/// The result of evaluating a canonical query.
+///
+/// FIXME: We use a different type than the existing canonical queries. This is because
+/// we need to add a `Certainty` for `overflow` and may want to restructure this code without
+/// having to worry about changes to currently used code. Once we've made progress on this
+/// solver, merge the two responses again.
+pub type QueryResult<'tcx> = Result<CanonicalResponse<'tcx>, NoSolution>;
+
+pub trait TyCtxtExt<'tcx> {
+ fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx>;
+}
+
+impl<'tcx> TyCtxtExt<'tcx> for TyCtxt<'tcx> {
+ fn evaluate_goal(self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
+ let mut cx = EvalCtxt::new(self);
+ cx.evaluate_canonical_goal(goal)
+ }
+}
+
+struct EvalCtxt<'tcx> {
+ tcx: TyCtxt<'tcx>,
+
+ provisional_cache: cache::ProvisionalCache<'tcx>,
+ overflow_data: overflow::OverflowData,
+}
+
+impl<'tcx> EvalCtxt<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>) -> EvalCtxt<'tcx> {
+ EvalCtxt {
+ tcx,
+ provisional_cache: cache::ProvisionalCache::empty(),
+ overflow_data: overflow::OverflowData::new(tcx),
+ }
+ }
+
+ /// Recursively evaluates `goal`, returning whether any inference vars have
+ /// been constrained and the certainty of the result.
+ fn evaluate_goal(
+ &mut self,
+ infcx: &InferCtxt<'tcx>,
+ goal: Goal<'tcx, ty::Predicate<'tcx>>,
+ ) -> Result<(bool, Certainty), NoSolution> {
+ let mut orig_values = OriginalQueryValues::default();
+ let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
+ let canonical_response = self.evaluate_canonical_goal(canonical_goal)?;
+ Ok((
+ true, // FIXME: check whether `var_values` are an identity substitution.
+ fixme_instantiate_canonical_query_response(infcx, &orig_values, canonical_response),
+ ))
+ }
+
+ fn evaluate_canonical_goal(&mut self, goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
+ match self.try_push_stack(goal) {
+ Ok(()) => {}
+ // Our goal is already on the stack, eager return.
+ Err(response) => return response,
+ }
+
+ // We may have to repeatedly recompute the goal in case of coinductive cycles,
+ // check out the `cache` module for more information.
+ //
+ // FIXME: Similar to `evaluate_all`, this has to check for overflow.
+ loop {
+ let result = self.compute_goal(goal);
+
+ // FIXME: `Response` should be `Copy`
+ if self.try_finalize_goal(goal, result.clone()) {
+ return result;
+ }
+ }
+ }
+
+ fn compute_goal(&mut self, canonical_goal: CanonicalGoal<'tcx>) -> QueryResult<'tcx> {
+ // WARNING: We're looking at a canonical value without instantiating it here.
+ //
+ // We have to be incredibly careful to not change the order of bound variables or
+ // remove any. As we go from `Goal<'tcx, Predicate>` to `Goal` with the variants
+ // of `PredicateKind` this is the case and it is and faster than instantiating and
+ // recanonicalizing.
+ let Goal { param_env, predicate } = canonical_goal.value;
+ if let Some(kind) = predicate.kind().no_bound_vars() {
+ match kind {
+ ty::PredicateKind::Clause(ty::Clause::Trait(predicate)) => self.compute_trait_goal(
+ canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
+ ),
+ ty::PredicateKind::Clause(ty::Clause::Projection(predicate)) => self
+ .compute_projection_goal(
+ canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
+ ),
+ ty::PredicateKind::Clause(ty::Clause::TypeOutlives(predicate)) => self
+ .compute_type_outlives_goal(
+ canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
+ ),
+ ty::PredicateKind::Clause(ty::Clause::RegionOutlives(predicate)) => self
+ .compute_region_outlives_goal(
+ canonical_goal.unchecked_rebind(Goal { param_env, predicate }),
+ ),
+ // FIXME: implement these predicates :)
+ ty::PredicateKind::WellFormed(_)
+ | ty::PredicateKind::ObjectSafe(_)
+ | ty::PredicateKind::ClosureKind(_, _, _)
+ | ty::PredicateKind::Subtype(_)
+ | ty::PredicateKind::Coerce(_)
+ | ty::PredicateKind::ConstEvaluatable(_)
+ | ty::PredicateKind::ConstEquate(_, _)
+ | ty::PredicateKind::TypeWellFormedFromEnv(_)
+ | ty::PredicateKind::Ambiguous => unimplemented!(),
+ }
+ } else {
+ let (infcx, goal, var_values) =
+ self.tcx.infer_ctxt().build_with_canonical(DUMMY_SP, &canonical_goal);
+ let kind = infcx.replace_bound_vars_with_placeholders(goal.predicate.kind());
+ let goal = goal.with(self.tcx, ty::Binder::dummy(kind));
+ let (_, certainty) = self.evaluate_goal(&infcx, goal)?;
+ infcx.make_canonical_response(var_values, certainty)
+ }
+ }
+
+ fn compute_type_outlives_goal(
+ &mut self,
+ _goal: CanonicalGoal<'tcx, TypeOutlivesPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ todo!()
+ }
+
+ fn compute_region_outlives_goal(
+ &mut self,
+ _goal: CanonicalGoal<'tcx, RegionOutlivesPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ todo!()
+ }
+}
+
+impl<'tcx> EvalCtxt<'tcx> {
+ fn evaluate_all(
+ &mut self,
+ infcx: &InferCtxt<'tcx>,
+ mut goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+ ) -> Result<Certainty, NoSolution> {
+ let mut new_goals = Vec::new();
+ self.repeat_while_none(|this| {
+ let mut has_changed = Err(Certainty::Yes);
+ for goal in goals.drain(..) {
+ let (changed, certainty) = match this.evaluate_goal(infcx, goal) {
+ Ok(result) => result,
+ Err(NoSolution) => return Some(Err(NoSolution)),
+ };
+
+ if changed {
+ has_changed = Ok(());
+ }
+
+ match certainty {
+ Certainty::Yes => {}
+ Certainty::Maybe(_) => {
+ new_goals.push(goal);
+ has_changed = has_changed.map_err(|c| c.unify_and(certainty));
+ }
+ }
+ }
+
+ match has_changed {
+ Ok(()) => {
+ mem::swap(&mut new_goals, &mut goals);
+ None
+ }
+ Err(certainty) => Some(Ok(certainty)),
+ }
+ })
+ }
+}
+
+fn fixme_instantiate_canonical_query_response<'tcx>(
+ _: &InferCtxt<'tcx>,
+ _: &OriginalQueryValues<'tcx>,
+ _: CanonicalResponse<'tcx>,
+) -> Certainty {
+ unimplemented!()
+}
--- /dev/null
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::ty::TyCtxt;
+use rustc_session::Limit;
+
+use super::{Certainty, EvalCtxt, MaybeCause, QueryResult};
+
+/// When detecting a solver overflow, we return ambiguity. Overflow can be
+/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**.
+///
+/// This is in issue in case of exponential blowup, e.g. if each goal on the stack
+/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit
+/// used by the solver when hitting the default limit for the first time.
+///
+/// FIXME: Get tests where always using the `default_limit` results in a hang and refer
+/// to them here. We can also improve the overflow strategy if necessary.
+pub(super) struct OverflowData {
+ default_limit: Limit,
+ current_limit: Limit,
+ /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals.
+ ///
+ /// Because of this each iteration also increases the depth in addition to the stack
+ /// depth.
+ additional_depth: usize,
+}
+
+impl OverflowData {
+ pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData {
+ let default_limit = tcx.recursion_limit();
+ OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 }
+ }
+
+ #[inline]
+ pub(super) fn did_overflow(&self) -> bool {
+ self.default_limit.0 != self.current_limit.0
+ }
+
+ #[inline]
+ pub(super) fn has_overflow(&self, depth: usize) -> bool {
+ self.current_limit.value_within_limit(depth + self.additional_depth)
+ }
+
+ /// Updating the current limit when hitting overflow.
+ fn deal_with_overflow(&mut self) {
+ // When first hitting overflow we reduce the overflow limit
+ // for all future goals to prevent hangs if there's an exponental
+ // blowup.
+ self.current_limit.0 = self.default_limit.0 / 8;
+ }
+}
+
+impl<'tcx> EvalCtxt<'tcx> {
+ pub(super) fn deal_with_overflow(&mut self) -> QueryResult<'tcx> {
+ self.overflow_data.deal_with_overflow();
+ fixme_response_overflow_no_constraints()
+ }
+
+ /// A `while`-loop which tracks overflow.
+ pub(super) fn repeat_while_none(
+ &mut self,
+ mut loop_body: impl FnMut(&mut Self) -> Option<Result<Certainty, NoSolution>>,
+ ) -> Result<Certainty, NoSolution> {
+ let start_depth = self.overflow_data.additional_depth;
+ let depth = self.provisional_cache.current_depth();
+ while !self.overflow_data.has_overflow(depth) {
+ if let Some(result) = loop_body(self) {
+ self.overflow_data.additional_depth = start_depth;
+ return result;
+ }
+
+ self.overflow_data.additional_depth += 1;
+ }
+ self.overflow_data.additional_depth = start_depth;
+ self.overflow_data.deal_with_overflow();
+ Ok(Certainty::Maybe(MaybeCause::Overflow))
+ }
+}
+
+fn fixme_response_overflow_no_constraints<'tcx>() -> QueryResult<'tcx> {
+ unimplemented!()
+}
--- /dev/null
+use crate::traits::{specialization_graph, translate_substs};
+
+use super::assembly::{self, AssemblyCtxt};
+use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::specialization_graph::LeafDef;
+use rustc_infer::traits::{ObligationCause, Reveal};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::ProjectionPredicate;
+use rustc_middle::ty::TypeVisitable;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+use std::iter;
+
+#[allow(dead_code)] // FIXME: implement and use all variants.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum CandidateSource {
+ Impl(DefId),
+ ParamEnv(usize),
+ Builtin,
+}
+
+type Candidate<'tcx> = assembly::Candidate<'tcx, ProjectionPredicate<'tcx>>;
+
+impl<'tcx> EvalCtxt<'tcx> {
+ pub(super) fn compute_projection_goal(
+ &mut self,
+ goal: CanonicalGoal<'tcx, ProjectionPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
+ self.merge_project_candidates(candidates)
+ }
+
+ fn merge_project_candidates(
+ &mut self,
+ mut candidates: Vec<Candidate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ match candidates.len() {
+ 0 => return Err(NoSolution),
+ 1 => return Ok(candidates.pop().unwrap().result),
+ _ => {}
+ }
+
+ if candidates.len() > 1 {
+ let mut i = 0;
+ 'outer: while i < candidates.len() {
+ for j in (0..candidates.len()).filter(|&j| i != j) {
+ if self.project_candidate_should_be_dropped_in_favor_of(
+ &candidates[i],
+ &candidates[j],
+ ) {
+ debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
+ candidates.swap_remove(i);
+ continue 'outer;
+ }
+ }
+
+ debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
+ // If there are *STILL* multiple candidates, give up
+ // and report ambiguity.
+ i += 1;
+ if i > 1 {
+ debug!("multiple matches, ambig");
+ // FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
+ unimplemented!();
+ }
+ }
+ }
+
+ Ok(candidates.pop().unwrap().result)
+ }
+
+ fn project_candidate_should_be_dropped_in_favor_of(
+ &self,
+ candidate: &Candidate<'tcx>,
+ other: &Candidate<'tcx>,
+ ) -> bool {
+ // FIXME: implement this
+ match (candidate.source, other.source) {
+ (CandidateSource::Impl(_), _)
+ | (CandidateSource::ParamEnv(_), _)
+ | (CandidateSource::Builtin, _) => unimplemented!(),
+ }
+ }
+}
+
+impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
+ type CandidateSource = CandidateSource;
+
+ fn self_ty(self) -> Ty<'tcx> {
+ self.self_ty()
+ }
+
+ fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+ self.with_self_ty(tcx, self_ty)
+ }
+
+ fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
+ self.trait_def_id(tcx)
+ }
+
+ fn consider_impl_candidate(
+ acx: &mut AssemblyCtxt<'_, 'tcx, ProjectionPredicate<'tcx>>,
+ goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
+ impl_def_id: DefId,
+ ) {
+ let tcx = acx.cx.tcx;
+ let goal_trait_ref = goal.predicate.projection_ty.trait_ref(tcx);
+ let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
+ if iter::zip(goal_trait_ref.substs, impl_trait_ref.skip_binder().substs)
+ .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
+ {
+ return;
+ }
+
+ acx.infcx.probe(|_| {
+ let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ let impl_trait_ref = impl_trait_ref.subst(tcx, impl_substs);
+
+ let Ok(InferOk { obligations, .. }) = acx
+ .infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .define_opaque_types(false)
+ .eq(goal_trait_ref, impl_trait_ref)
+ .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
+ else {
+ return
+ };
+
+ let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
+ let Ok(trait_ref_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+
+ let Some(assoc_def) = fetch_eligible_assoc_item_def(
+ acx.infcx,
+ goal.param_env,
+ goal_trait_ref,
+ goal.predicate.def_id(),
+ impl_def_id
+ ) else {
+ return
+ };
+
+ if !assoc_def.item.defaultness(tcx).has_value() {
+ tcx.sess.delay_span_bug(
+ tcx.def_span(assoc_def.item.def_id),
+ "missing value for assoc item in impl",
+ );
+ }
+
+ // Getting the right substitutions here is complex, e.g. given:
+ // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
+ // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
+ // - and the impl which defines `Assoc` being `impl<T, U> Trait<U> for Vec<T>`
+ //
+ // We first rebase the goal substs onto the impl, going from `[Vec<u32>, i32, u64]`
+ // to `[u32, u64]`.
+ //
+ // And then map these substs to the substs of the defining impl of `Assoc`, going
+ // from `[u32, u64]` to `[u32, i32, u64]`.
+ let impl_substs_with_gat = goal.predicate.projection_ty.substs.rebase_onto(
+ tcx,
+ goal_trait_ref.def_id,
+ impl_trait_ref.substs,
+ );
+ let substs = translate_substs(
+ acx.infcx,
+ goal.param_env,
+ impl_def_id,
+ impl_substs_with_gat,
+ assoc_def.defining_node,
+ );
+
+ // Finally we construct the actual value of the associated type.
+ let is_const = matches!(tcx.def_kind(assoc_def.item.def_id), DefKind::AssocConst);
+ let ty = tcx.bound_type_of(assoc_def.item.def_id);
+ let term: ty::EarlyBinder<ty::Term<'tcx>> = if is_const {
+ let identity_substs = ty::InternalSubsts::identity_for_item(tcx, assoc_def.item.def_id);
+ let did = ty::WithOptConstParam::unknown(assoc_def.item.def_id);
+ let kind =
+ ty::ConstKind::Unevaluated(ty::UnevaluatedConst::new(did, identity_substs));
+ ty.map_bound(|ty| tcx.mk_const(kind, ty).into())
+ } else {
+ ty.map_bound(|ty| ty.into())
+ };
+
+ let Ok(InferOk { obligations, .. }) = acx
+ .infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .define_opaque_types(false)
+ .eq(goal.predicate.term, term.subst(tcx, substs))
+ .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
+ else {
+ return
+ };
+
+ let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
+ let Ok(rhs_certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+
+ let certainty = trait_ref_certainty.unify_and(rhs_certainty);
+ acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ })
+ }
+}
+
+/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
+///
+/// FIXME: We should merge these 3 implementations as it's likely that they otherwise
+/// diverge.
+#[instrument(level = "debug", skip(infcx, param_env), ret)]
+fn fetch_eligible_assoc_item_def<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ goal_trait_ref: ty::TraitRef<'tcx>,
+ trait_assoc_def_id: DefId,
+ impl_def_id: DefId,
+) -> Option<LeafDef> {
+ let node_item = specialization_graph::assoc_def(infcx.tcx, impl_def_id, trait_assoc_def_id)
+ .map_err(|ErrorGuaranteed { .. }| ())
+ .ok()?;
+
+ let eligible = if node_item.is_final() {
+ // Non-specializable items are always projectable.
+ true
+ } else {
+ // Only reveal a specializable default if we're past type-checking
+ // and the obligation is monomorphic, otherwise passes such as
+ // transmute checking and polymorphic MIR optimizations could
+ // get a result which isn't correct for all monomorphizations.
+ if param_env.reveal() == Reveal::All {
+ let poly_trait_ref = infcx.resolve_vars_if_possible(goal_trait_ref);
+ !poly_trait_ref.still_further_specializable()
+ } else {
+ debug!(?node_item.item.def_id, "not eligible due to default");
+ false
+ }
+ };
+
+ if eligible { Some(node_item) } else { None }
+}
--- /dev/null
+//! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
+
+use std::iter;
+
+use super::assembly::{self, AssemblyCtxt};
+use super::{CanonicalGoal, EvalCtxt, Goal, QueryResult};
+use rustc_hir::def_id::DefId;
+use rustc_infer::infer::InferOk;
+use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::ObligationCause;
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
+use rustc_middle::ty::TraitPredicate;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+
+#[allow(dead_code)] // FIXME: implement and use all variants.
+#[derive(Debug, Clone, Copy)]
+pub(super) enum CandidateSource {
+ /// Some user-defined impl with the given `DefId`.
+ Impl(DefId),
+ /// The n-th caller bound in the `param_env` of our goal.
+ ///
+ /// This is pretty much always a bound from the `where`-clauses of the
+ /// currently checked item.
+ ParamEnv(usize),
+ /// A bound on the `self_ty` in case it is a projection or an opaque type.
+ ///
+ /// # Examples
+ ///
+ /// ```ignore (for syntax highlighting)
+ /// trait Trait {
+ /// type Assoc: OtherTrait;
+ /// }
+ /// ```
+ ///
+ /// We know that `<Whatever as Trait>::Assoc: OtherTrait` holds by looking at
+ /// the bounds on `Trait::Assoc`.
+ AliasBound(usize),
+ /// A builtin implementation for some specific traits, used in cases
+ /// where we cannot rely an ordinary library implementations.
+ ///
+ /// The most notable examples are `Sized`, `Copy` and `Clone`. This is also
+ /// used for the `DiscriminantKind` and `Pointee` trait, both of which have
+ /// an associated type.
+ Builtin,
+ /// An automatic impl for an auto trait, e.g. `Send`. These impls recursively look
+ /// at the constituent types of the `self_ty` to check whether the auto trait
+ /// is implemented for those.
+ AutoImpl,
+}
+
+type Candidate<'tcx> = assembly::Candidate<'tcx, TraitPredicate<'tcx>>;
+
+impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
+ type CandidateSource = CandidateSource;
+
+ fn self_ty(self) -> Ty<'tcx> {
+ self.self_ty()
+ }
+
+ fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+ self.with_self_ty(tcx, self_ty)
+ }
+
+ fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
+ self.def_id()
+ }
+
+ fn consider_impl_candidate(
+ acx: &mut AssemblyCtxt<'_, 'tcx, Self>,
+ goal: Goal<'tcx, TraitPredicate<'tcx>>,
+ impl_def_id: DefId,
+ ) {
+ let impl_trait_ref = acx.cx.tcx.bound_impl_trait_ref(impl_def_id).unwrap();
+ let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsPlaceholder };
+ if iter::zip(goal.predicate.trait_ref.substs, impl_trait_ref.skip_binder().substs)
+ .any(|(goal, imp)| !drcx.generic_args_may_unify(goal, imp))
+ {
+ return;
+ }
+
+ acx.infcx.probe(|_| {
+ let impl_substs = acx.infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
+ let impl_trait_ref = impl_trait_ref.subst(acx.cx.tcx, impl_substs);
+
+ let Ok(InferOk { obligations, .. }) = acx
+ .infcx
+ .at(&ObligationCause::dummy(), goal.param_env)
+ .define_opaque_types(false)
+ .eq(goal.predicate.trait_ref, impl_trait_ref)
+ .map_err(|e| debug!("failed to equate trait refs: {e:?}"))
+ else {
+ return
+ };
+
+ let nested_goals = obligations.into_iter().map(|o| o.into()).collect();
+
+ let Ok(certainty) = acx.cx.evaluate_all(acx.infcx, nested_goals) else { return };
+ acx.try_insert_candidate(CandidateSource::Impl(impl_def_id), certainty);
+ })
+ }
+}
+
+impl<'tcx> EvalCtxt<'tcx> {
+ pub(super) fn compute_trait_goal(
+ &mut self,
+ goal: CanonicalGoal<'tcx, TraitPredicate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ let candidates = AssemblyCtxt::assemble_and_evaluate_candidates(self, goal);
+ self.merge_trait_candidates_discard_reservation_impls(candidates)
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ pub(super) fn merge_trait_candidates_discard_reservation_impls(
+ &mut self,
+ mut candidates: Vec<Candidate<'tcx>>,
+ ) -> QueryResult<'tcx> {
+ match candidates.len() {
+ 0 => return Err(NoSolution),
+ 1 => return Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result),
+ _ => {}
+ }
+
+ if candidates.len() > 1 {
+ let mut i = 0;
+ 'outer: while i < candidates.len() {
+ for j in (0..candidates.len()).filter(|&j| i != j) {
+ if self.trait_candidate_should_be_dropped_in_favor_of(
+ &candidates[i],
+ &candidates[j],
+ ) {
+ debug!(candidate = ?candidates[i], "Dropping candidate #{}/{}", i, candidates.len());
+ candidates.swap_remove(i);
+ continue 'outer;
+ }
+ }
+
+ debug!(candidate = ?candidates[i], "Retaining candidate #{}/{}", i, candidates.len());
+ // If there are *STILL* multiple candidates, give up
+ // and report ambiguity.
+ i += 1;
+ if i > 1 {
+ debug!("multiple matches, ambig");
+ // FIXME: return overflow if all candidates overflow, otherwise return ambiguity.
+ unimplemented!();
+ }
+ }
+ }
+
+ Ok(self.discard_reservation_impl(candidates.pop().unwrap()).result)
+ }
+
+ fn trait_candidate_should_be_dropped_in_favor_of(
+ &self,
+ candidate: &Candidate<'tcx>,
+ other: &Candidate<'tcx>,
+ ) -> bool {
+ // FIXME: implement this
+ match (candidate.source, other.source) {
+ (CandidateSource::Impl(_), _)
+ | (CandidateSource::ParamEnv(_), _)
+ | (CandidateSource::AliasBound(_), _)
+ | (CandidateSource::Builtin, _)
+ | (CandidateSource::AutoImpl, _) => unimplemented!(),
+ }
+ }
+
+ fn discard_reservation_impl(&self, candidate: Candidate<'tcx>) -> Candidate<'tcx> {
+ if let CandidateSource::Impl(def_id) = candidate.source {
+ if let ty::ImplPolarity::Reservation = self.tcx.impl_polarity(def_id) {
+ debug!("Selected reservation impl");
+ // FIXME: reduce candidate to ambiguous
+ // FIXME: replace `var_values` with identity, yeet external constraints.
+ unimplemented!()
+ }
+ }
+
+ candidate
+ }
+}
orig_env,
orig_env,
&mut fresh_preds,
- false,
) else {
return AutoTraitResult::NegativeImpl;
};
let (full_env, full_user_env) = self
- .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds, true)
+ .evaluate_predicates(&infcx, trait_did, ty, new_env, user_env, &mut fresh_preds)
.unwrap_or_else(|| {
panic!("Failed to fully process: {:?} {:?} {:?}", ty, trait_did, orig_env)
});
param_env: ty::ParamEnv<'tcx>,
user_env: ty::ParamEnv<'tcx>,
fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
- only_projections: bool,
) -> Option<(ty::ParamEnv<'tcx>, ty::ParamEnv<'tcx>)> {
let tcx = infcx.tcx;
fresh_preds,
&mut predicates,
&mut select,
- only_projections,
) {
return None;
}
fresh_preds: &mut FxHashSet<ty::Predicate<'tcx>>,
predicates: &mut VecDeque<ty::PolyTraitPredicate<'tcx>>,
selcx: &mut SelectionContext<'_, 'tcx>,
- only_projections: bool,
) -> bool {
let dummy_cause = ObligationCause::dummy();
fresh_preds,
predicates,
selcx,
- only_projections,
) {
return false;
}
/// with a suitably-freshened `ImplHeader` with those types
/// substituted. Otherwise, returns `None`.
#[instrument(skip(tcx, skip_leak_check), level = "debug")]
-pub fn overlapping_impls<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub fn overlapping_impls(
+ tcx: TyCtxt<'_>,
impl1_def_id: DefId,
impl2_def_id: DefId,
skip_leak_check: SkipLeakCheck,
overlap_mode: OverlapMode,
-) -> Option<OverlapResult<'tcx>> {
+) -> Option<OverlapResult<'_>> {
// Before doing expensive operations like entering an inference context, do
// a quick check via fast_reject to tell if the impl headers could possibly
// unify.
/// Given impl1 and impl2 check if both impls are never satisfied by a common type (including
/// where-clauses) If so, return true, they are disjoint and false otherwise.
-fn negative_impl<'tcx>(tcx: TyCtxt<'tcx>, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
+fn negative_impl(tcx: TyCtxt<'_>, impl1_def_id: DefId, impl2_def_id: DefId) -> bool {
debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
// Create an infcx, taking the predicates of impl1 as assumptions:
} else if uv.has_non_region_param() {
NotConstEvaluatable::MentionsParam
} else {
- let guar = infcx.tcx.sess.delay_span_bug(
- span,
- format!("Missing value for constant, but no error reported?"),
- );
+ let guar = infcx
+ .tcx
+ .sess
+ .delay_span_bug(span, "Missing value for constant, but no error reported?");
NotConstEvaluatable::Error(guar)
};
fn tag(&self) -> &'static str {
"CollectAllMismatches"
}
+
fn tcx(&self) -> TyCtxt<'tcx> {
self.infcx.tcx
}
+
fn intercrate(&self) -> bool {
false
}
+
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
+
fn a_is_expected(&self) -> bool {
true
- } // irrelevant
+ }
+
fn mark_ambiguous(&mut self) {
bug!()
}
+
fn relate_with_variance<T: Relate<'tcx>>(
&mut self,
_: ty::Variance,
) -> RelateResult<'tcx, T> {
self.relate(a, b)
}
+
fn regions(
&mut self,
a: ty::Region<'tcx>,
) -> RelateResult<'tcx, ty::Region<'tcx>> {
Ok(a)
}
+
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
- if a == b || matches!(a.kind(), ty::Infer(_)) || matches!(b.kind(), ty::Infer(_)) {
- return Ok(a);
- }
- relate::super_relate_tys(self, a, b).or_else(|e| {
- self.errors.push(e);
- Ok(a)
+ self.infcx.probe(|_| {
+ if a.is_ty_infer() || b.is_ty_infer() {
+ Ok(a)
+ } else {
+ self.infcx.super_combine_tys(self, a, b).or_else(|e| {
+ self.errors.push(e);
+ Ok(a)
+ })
+ }
})
}
+
fn consts(
&mut self,
a: ty::Const<'tcx>,
}
relate::super_relate_consts(self, a, b) // could do something similar here for constants!
}
+
fn binders<T: Relate<'tcx>>(
&mut self,
a: ty::Binder<'tcx, T>,
let arg_length = arguments.len();
let distinct = matches!(other, &[ArgKind::Tuple(..)]);
match (arg_length, arguments.get(0)) {
- (1, Some(&ArgKind::Tuple(_, ref fields))) => {
+ (1, Some(ArgKind::Tuple(_, fields))) => {
format!("a single {}-tuple as argument", fields.len())
}
_ => format!(
&error.obligation.cause,
expected_found.expected,
expected_found.found,
- err.clone(),
+ *err,
)
.emit();
}
&error.obligation.cause,
expected_found.expected,
expected_found.found,
- err.clone(),
+ *err,
);
let code = error.obligation.cause.code().peel_derives().peel_match_impls();
if let ObligationCauseCode::BindingObligation(..)
values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| {
infer::ValuePairs::Terms(ExpectedFound::new(
is_normalized_ty_expected,
- normalized_ty.into(),
- expected_ty.into(),
+ normalized_ty,
+ expected_ty,
))
}),
err,
// get rid of :: between Trait and <type>
// must be '::' between them, otherwise the parser won't accept the code
suggestions.push((between_span, "".to_string(),));
- suggestions.push((generic_arg.span_ext.shrink_to_hi(), format!(">")));
+ suggestions.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
} else {
- suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), format!(">")));
+ suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), ">".to_string()));
}
err.multipart_suggestion(
message,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
- fn function_argument_obligation(
+ fn note_function_argument_obligation(
&self,
arg_hir_id: HirId,
err: &mut Diagnostic,
self.note_conflicting_closure_bounds(cause, &mut err);
if let Some(found_node) = found_node {
- hint_missing_borrow(span, found_span, found, expected, found_node, &mut err);
+ hint_missing_borrow(span, found, expected, found_node, &mut err);
}
err
}
}
GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
- // `Some(ref_ty)` if `target_ty` is `&T` and `T` fails to impl `Sync`
- let refers_to_non_sync = match target_ty.kind() {
- ty::Ref(_, ref_ty, _) => match self.evaluate_obligation(&obligation) {
- Ok(eval) if !eval.may_apply() => Some(ref_ty),
+ // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send`
+ let non_send = match target_ty.kind() {
+ ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) {
+ Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())),
_ => None,
},
_ => None,
};
- let (span_label, span_note) = match refers_to_non_sync {
- // if `target_ty` is `&T` and `T` fails to impl `Sync`,
- // include suggestions to make `T: Sync` so that `&T: Send`
- Some(ref_ty) => (
- format!(
- "has type `{}` which {}, because `{}` is not `Sync`",
- target_ty, trait_explanation, ref_ty
- ),
- format!(
- "captured value {} because `&` references cannot be sent unless their referent is `Sync`",
- trait_explanation
- ),
- ),
+ let (span_label, span_note) = match non_send {
+ // if `target_ty` is `&T` or `&mut T` and fails to impl `Send`,
+ // include suggestions to make `T: Sync` so that `&T: Send`,
+ // or to make `T: Send` so that `&mut T: Send`
+ Some((ref_ty, is_mut)) => {
+ let ref_ty_trait = if is_mut { "Send" } else { "Sync" };
+ let ref_kind = if is_mut { "&mut" } else { "&" };
+ (
+ format!(
+ "has type `{}` which {}, because `{}` is not `{}`",
+ target_ty, trait_explanation, ref_ty, ref_ty_trait
+ ),
+ format!(
+ "captured value {} because `{}` references cannot be sent unless their referent is `{}`",
+ trait_explanation, ref_kind, ref_ty_trait
+ ),
+ )
+ }
None => (
format!("has type `{}` which {}", target_ty, trait_explanation),
format!("captured value {}", trait_explanation),
}
ty::Closure(def_id, _) => err.span_note(
self.tcx.def_span(def_id),
- &format!("required because it's used within this closure"),
+ "required because it's used within this closure",
),
_ => err.note(&msg),
};
ref parent_code,
..
} => {
- self.function_argument_obligation(
+ self.note_function_argument_obligation(
arg_hir_id,
err,
parent_code,
);
}
}
- fn function_argument_obligation(
+ fn note_function_argument_obligation(
&self,
arg_hir_id: HirId,
err: &mut Diagnostic,
parent_code: &ObligationCauseCode<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- predicate: ty::Predicate<'tcx>,
+ failed_pred: ty::Predicate<'tcx>,
call_hir_id: HirId,
) {
let tcx = self.tcx;
let hir = tcx.hir();
- if let Some(Node::Expr(expr)) = hir.find(arg_hir_id) {
- let parent_id = hir.get_parent_item(arg_hir_id);
- let typeck_results: &TypeckResults<'tcx> = match &self.typeck_results {
- Some(t) if t.hir_owner == parent_id => t,
- _ => self.tcx.typeck(parent_id.def_id),
- };
+ if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
+ && let Some(typeck_results) = &self.typeck_results
+ {
if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
let expr = expr.peel_blocks();
let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error());
let mut type_diffs = vec![];
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref()
- && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
- && let Some(pred) = predicates.predicates.get(*idx)
+ && let Some(node_substs) = typeck_results.node_substs_opt(call_hir_id)
+ && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
+ && let Some(where_pred) = where_clauses.predicates.get(*idx)
{
- if let Ok(trait_pred) = pred.kind().try_map_bound(|pred| match pred {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
- _ => Err(()),
- })
- && let Ok(trait_predicate) = predicate.kind().try_map_bound(|pred| match pred {
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) => Ok(trait_pred),
- _ => Err(()),
- })
+ if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
+ && let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
{
let mut c = CollectAllMismatches {
infcx: self.infcx,
param_env,
errors: vec![],
};
- if let Ok(_) = c.relate(trait_pred, trait_predicate) {
+ if let Ok(_) = c.relate(where_pred, failed_pred) {
type_diffs = c.errors;
}
- } else if let ty::PredicateKind::Clause(
- ty::Clause::Projection(proj)
- ) = pred.kind().skip_binder()
- && let ty::PredicateKind::Clause(
- ty::Clause::Projection(projection)
- ) = predicate.kind().skip_binder()
+ } else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
+ && let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
+ && let Some(found) = failed_pred.skip_binder().term.ty()
{
type_diffs = vec![
Sorts(ty::error::ExpectedFound {
- expected: self.tcx.mk_ty(ty::Alias(ty::Projection, proj.projection_ty)),
- found: projection.term.ty().unwrap(),
+ expected: self.tcx.mk_ty(ty::Alias(ty::Projection, where_pred.skip_binder().projection_ty)),
+ found,
}),
];
}
// If the expression we're calling on is a binding, we want to point at the
// `let` when talking about the type. Otherwise we'll point at every part
// of the method chain with the type.
- self.point_at_chain(binding_expr, typeck_results, type_diffs, param_env, err);
+ self.point_at_chain(binding_expr, &typeck_results, type_diffs, param_env, err);
} else {
- self.point_at_chain(expr, typeck_results, type_diffs, param_env, err);
+ self.point_at_chain(expr, &typeck_results, type_diffs, param_env, err);
}
}
let call_node = hir.find(call_hir_id);
}
err.span_note(
multi_span,
- format!("the method call chain might not have had the expected associated types"),
+ "the method call chain might not have had the expected associated types",
);
}
}
/// Add a hint to add a missing borrow or remove an unnecessary one.
fn hint_missing_borrow<'tcx>(
span: Span,
- found_span: Span,
found: Ty<'tcx>,
expected: Ty<'tcx>,
found_node: Node<'_>,
}
};
- let fn_decl = found_node
- .fn_decl()
- .unwrap_or_else(|| span_bug!(found_span, "found node must be a function"));
+ // This could be a variant constructor, for example.
+ let Some(fn_decl) = found_node.fn_decl() else { return; };
let arg_spans = fn_decl.inputs.iter().map(|ty| ty.span);
- fn get_deref_type_and_refs<'tcx>(mut ty: Ty<'tcx>) -> (Ty<'tcx>, usize) {
+ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
let mut refs = 0;
while let ty::Ref(_, new_ty, _) = ty.kind() {
///
/// This only considers predicates that reference the impl's generics, and not
/// those that reference the method's generics.
-fn is_impossible_method<'tcx>(
- tcx: TyCtxt<'tcx>,
- (impl_def_id, trait_item_def_id): (DefId, DefId),
-) -> bool {
+fn is_impossible_method(tcx: TyCtxt<'_>, (impl_def_id, trait_item_def_id): (DefId, DefId)) -> bool {
struct ReferencesOnlyParentGenerics<'tcx> {
tcx: TyCtxt<'tcx>,
generics: &'tcx ty::Generics,
/// argument types are well-formed. This may imply certain relationships
/// between generic parameters. For example:
/// ```
- /// fn foo<'a,T>(x: &'a T) {}
+ /// fn foo<T>(x: &T) {}
/// ```
/// can only be called with a `'a` and `T` such that `&'a T` is WF.
/// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind;
-use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_infer::infer::at::At;
use rustc_infer::infer::resolve::OpportunisticRegionResolver;
// NOTE: This should be kept in sync with the similar code in
// `rustc_ty_utils::instance::resolve_associated_item()`.
let node_item =
- assoc_def(selcx, impl_data.impl_def_id, obligation.predicate.def_id)
+ specialization_graph::assoc_def(selcx.tcx(), impl_data.impl_def_id, obligation.predicate.def_id)
.map_err(|ErrorGuaranteed { .. }| ())?;
if node_item.is_final() {
let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
let param_env = obligation.param_env;
- let Ok(assoc_ty) = assoc_def(selcx, impl_def_id, assoc_item_id) else {
+ let Ok(assoc_ty) = specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) else {
return Progress { term: tcx.ty_error().into(), obligations: nested };
};
let mut obligations = data.nested;
let trait_fn_def_id = tcx.impl_trait_in_trait_parent(obligation.predicate.def_id);
- let Ok(leaf_def) = assoc_def(selcx, data.impl_def_id, trait_fn_def_id) else {
+ let Ok(leaf_def) = specialization_graph::assoc_def(tcx, data.impl_def_id, trait_fn_def_id) else {
return Progress { term: tcx.ty_error().into(), obligations };
};
if !leaf_def.item.defaultness(tcx).has_value() {
}
}
-/// Locate the definition of an associated type in the specialization hierarchy,
-/// starting from the given impl.
-///
-/// Based on the "projection mode", this lookup may in fact only examine the
-/// topmost impl. See the comments for `Reveal` for more details.
-fn assoc_def(
- selcx: &SelectionContext<'_, '_>,
- impl_def_id: DefId,
- assoc_def_id: DefId,
-) -> Result<specialization_graph::LeafDef, ErrorGuaranteed> {
- let tcx = selcx.tcx();
- let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
- let trait_def = tcx.trait_def(trait_def_id);
-
- // This function may be called while we are still building the
- // specialization graph that is queried below (via TraitDef::ancestors()),
- // so, in order to avoid unnecessary infinite recursion, we manually look
- // for the associated item at the given impl.
- // If there is no such item in that impl, this function will fail with a
- // cycle error if the specialization graph is currently being built.
- if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
- let item = tcx.associated_item(impl_item_id);
- let impl_node = specialization_graph::Node::Impl(impl_def_id);
- return Ok(specialization_graph::LeafDef {
- item: *item,
- defining_node: impl_node,
- finalizing_node: if item.defaultness(tcx).is_default() {
- None
- } else {
- Some(impl_node)
- },
- });
- }
-
- let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
- if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
- Ok(assoc_item)
- } else {
- // This is saying that neither the trait nor
- // the impl contain a definition for this
- // associated type. Normally this situation
- // could only arise through a compiler bug --
- // if the user wrote a bad item name, it
- // should have failed in astconv.
- bug!(
- "No associated type `{}` for {}",
- tcx.item_name(assoc_def_id),
- tcx.def_path_str(impl_def_id)
- )
- }
-}
-
pub(crate) trait ProjectionCacheKeyExt<'cx, 'tcx>: Sized {
fn from_poly_projection_predicate(
selcx: &mut SelectionContext<'cx, 'tcx>,
where
I: Iterator<Item = ty::Predicate<'tcx>>,
{
- cycle.all(|predicate| self.coinductive_predicate(predicate))
- }
-
- fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool {
- let result = match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(ty::Clause::Trait(ref data)) => {
- self.tcx().trait_is_coinductive(data.def_id())
- }
- ty::PredicateKind::WellFormed(_) => true,
- _ => false,
- };
- debug!(?predicate, ?result, "coinductive_predicate");
- result
+ cycle.all(|predicate| predicate.is_coinductive(self.tcx()))
}
/// Further evaluates `candidate` to decide whether all type parameters match and whether nested
use super::OverlapError;
use crate::traits;
+use rustc_errors::ErrorGuaranteed;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams};
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
self.children.entry(parent).or_default().insert_blindly(tcx, child);
}
}
+
+/// Locate the definition of an associated type in the specialization hierarchy,
+/// starting from the given impl.
+pub(crate) fn assoc_def(
+ tcx: TyCtxt<'_>,
+ impl_def_id: DefId,
+ assoc_def_id: DefId,
+) -> Result<LeafDef, ErrorGuaranteed> {
+ let trait_def_id = tcx.impl_trait_ref(impl_def_id).unwrap().def_id;
+ let trait_def = tcx.trait_def(trait_def_id);
+
+ // This function may be called while we are still building the
+ // specialization graph that is queried below (via TraitDef::ancestors()),
+ // so, in order to avoid unnecessary infinite recursion, we manually look
+ // for the associated item at the given impl.
+ // If there is no such item in that impl, this function will fail with a
+ // cycle error if the specialization graph is currently being built.
+ if let Some(&impl_item_id) = tcx.impl_item_implementor_ids(impl_def_id).get(&assoc_def_id) {
+ let &item = tcx.associated_item(impl_item_id);
+ let impl_node = Node::Impl(impl_def_id);
+ return Ok(LeafDef {
+ item,
+ defining_node: impl_node,
+ finalizing_node: if item.defaultness(tcx).is_default() {
+ None
+ } else {
+ Some(impl_node)
+ },
+ });
+ }
+
+ let ancestors = trait_def.ancestors(tcx, impl_def_id)?;
+ if let Some(assoc_item) = ancestors.leaf_def(tcx, assoc_def_id) {
+ Ok(assoc_item)
+ } else {
+ // This is saying that neither the trait nor
+ // the impl contain a definition for this
+ // associated type. Normally this situation
+ // could only arise through a compiler bug --
+ // if the user wrote a bad item name, it
+ // should have failed in astconv.
+ bug!(
+ "No associated type `{}` for {}",
+ tcx.item_name(assoc_def_id),
+ tcx.def_path_str(impl_def_id)
+ )
+ }
+}
});
}
-fn own_existential_vtable_entries<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId) -> &'tcx [DefId] {
+fn own_existential_vtable_entries(tcx: TyCtxt<'_>, trait_def_id: DefId) -> &[DefId] {
let trait_methods = tcx
.associated_items(trait_def_id)
.in_definition_order()
/// var bound at index `0`. For types, we use a `BoundVar` index equal to
/// the type parameter index. For regions, we use the `BoundRegionKind::BrNamed`
/// variant (which has a `DefId`).
-fn bound_vars_for_item<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> SubstsRef<'tcx> {
+fn bound_vars_for_item(tcx: TyCtxt<'_>, def_id: DefId) -> SubstsRef<'_> {
InternalSubsts::for_item(tcx, def_id, |param, substs| match param.kind {
ty::GenericParamDefKind::Type { .. } => tcx
.mk_ty(ty::Bound(
}
}
-fn not_outlives_predicate<'tcx>(p: ty::Predicate<'tcx>) -> bool {
+fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool {
match p.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => false,
let fix_state = |state| if state == other.start { self.accepting } else { state };
let entry = transitions.entry(fix_state(source)).or_default();
for (edge, destinations) in transition {
- let entry = entry.entry(edge.clone()).or_default();
+ let entry = entry.entry(edge).or_default();
for destination in destinations {
entry.insert(fix_state(destination));
}
}
let entry = transitions.entry(source).or_default();
for (edge, destinations) in transition {
- let entry = entry.entry(edge.clone()).or_default();
+ let entry = entry.entry(*edge).or_default();
for &(mut destination) in destinations {
// if dest is accepting state of `other`, replace with accepting state of `self`
if destination == other.accepting {
}
};
- let ret = if self.visibility(def_id).is_accessible_from(parent, *self) {
- true
- } else {
- false
- };
+ let ret: bool = self.visibility(def_id).is_accessible_from(parent, *self);
trace!(?ret, "ret");
ret
thir: &'a thir::Thir<'tcx>,
}
-fn error<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn error(
+ tcx: TyCtxt<'_>,
sub: GenericConstantTooComplexSub,
root_span: Span,
) -> Result<!, ErrorGuaranteed> {
Err(reported)
}
-fn maybe_supported_error<'tcx>(
- tcx: TyCtxt<'tcx>,
+fn maybe_supported_error(
+ tcx: TyCtxt<'_>,
sub: GenericConstantTooComplexSub,
root_span: Span,
) -> Result<!, ErrorGuaranteed> {
}
/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
-pub fn thir_abstract_const<'tcx>(
- tcx: TyCtxt<'tcx>,
+pub fn thir_abstract_const(
+ tcx: TyCtxt<'_>,
def: ty::WithOptConstParam<LocalDefId>,
-) -> Result<Option<ty::Const<'tcx>>, ErrorGuaranteed> {
+) -> Result<Option<ty::Const<'_>>, ErrorGuaranteed> {
if tcx.features().generic_const_exprs {
match tcx.def_kind(def.did) {
// FIXME(generic_const_exprs): We currently only do this for anonymous constants,
*providers = ty::query::Providers { assumed_wf_types, ..*providers };
}
-fn assumed_wf_types<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx ty::List<Ty<'tcx>> {
+fn assumed_wf_types(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Ty<'_>> {
match tcx.def_kind(def_id) {
DefKind::Fn => {
let sig = tcx.fn_sig(def_id);
// of any variant.
/// Compute the eligibility and assignment of each local.
-fn generator_saved_local_eligibility<'tcx>(
- info: &GeneratorLayout<'tcx>,
+fn generator_saved_local_eligibility(
+ info: &GeneratorLayout<'_>,
) -> (BitSet<GeneratorSavedLocal>, IndexVec<GeneratorSavedLocal, SavedLocalEligibility>) {
use SavedLocalEligibility::*;
/// that are assumed to be well-formed (because they come from the environment).
///
/// Used only in chalk mode.
-fn well_formed_types_in_env<'tcx>(
- tcx: TyCtxt<'tcx>,
- def_id: DefId,
-) -> &'tcx ty::List<Predicate<'tcx>> {
+fn well_formed_types_in_env(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::List<Predicate<'_>> {
use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
use rustc_middle::ty::subst::GenericArgKind;
///
/// [dbi]: https://en.wikipedia.org/wiki/De_Bruijn_index
#[derive(HashStable_Generic)]
+ #[debug_format = "DebruijnIndex({})"]
pub struct DebruijnIndex {
- DEBUG_FORMAT = "DebruijnIndex({})",
- const INNERMOST = 0,
+ const INNERMOST = 0;
}
}
rustc_index::newtype_index! {
/// A **ty**pe **v**ariable **ID**.
- pub struct TyVid {
- DEBUG_FORMAT = "_#{}t"
- }
+ #[debug_format = "_#{}t"]
+ pub struct TyVid {}
}
/// An **int**egral (`u32`, `i32`, `usize`, etc.) type **v**ariable **ID**.
/// type -- an idealized representative of "types in general" that we
/// use for checking generic functions.
#[derive(HashStable_Generic)]
- pub struct UniverseIndex {
- DEBUG_FORMAT = "U{}",
- }
+ #[debug_format = "U{}"]
+ pub struct UniverseIndex {}
}
impl UniverseIndex {
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized, A: Allocator> CoerceUnsized<Box<U, A>> for Box<T, A> {}
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
///
/// let deque: VecDeque<u32> = VecDeque::new();
/// ```
- // FIXME: This should probably be const
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_stable(feature = "const_vec_deque_new", since = "CURRENT_RUSTC_VERSION")]
#[must_use]
- pub fn new() -> VecDeque<T> {
- VecDeque::new_in(Global)
+ pub const fn new() -> VecDeque<T> {
+ // FIXME: This should just be `VecDeque::new_in(Global)` once that hits stable.
+ VecDeque { head: 0, len: 0, buf: RawVec::NEW }
}
/// Creates an empty deque with space for at least `capacity` elements.
/// The deque is assumed to be partitioned according to the given predicate.
/// This means that all elements for which the predicate returns true are at the start of the deque
/// and all elements for which the predicate returns false are at the end.
- /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0
+ /// For example, `[7, 15, 3, 5, 4, 12, 6]` is partitioned under the predicate `x % 2 != 0`
/// (all odd numbers are at the start, all even at the end).
///
/// If the deque is not partitioned, the returned result is unspecified and meaningless,
//! // documentation for details, and the function `pad` can be used
//! // to pad strings.
//! let decimals = f.precision().unwrap_or(3);
-//! let string = format!("{:.*}", decimals, magnitude);
+//! let string = format!("{magnitude:.decimals$}");
//! f.pad_integral(true, "", &string)
//! }
//! }
//! write!(&mut some_writer, "{}", format_args!("print with a {}", "macro"));
//!
//! fn my_fmt_fn(args: fmt::Arguments) {
-//! write!(&mut io::stdout(), "{}", args);
+//! write!(&mut io::stdout(), "{args}");
//! }
//! my_fmt_fn(format_args!(", or a {} too", "function"));
//! ```
#[stable(feature = "rc_ref_unwind_safe", since = "1.58.0")]
impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for Rc<T> {}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Rc<U>> for Rc<T> {}
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
#[stable(feature = "rc_weak", since = "1.4.0")]
impl<T: ?Sized> !marker::Sync for Weak<T> {}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Weak<U>> for Weak<T> {}
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
#[stable(feature = "catch_unwind", since = "1.9.0")]
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
#[stable(feature = "arc_weak", since = "1.4.0")]
unsafe impl<T: ?Sized + Sync + Send> Sync for Weak<T> {}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Weak<U>> for Weak<T> {}
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Weak<U>> for Weak<T> {}
// to avoid dropping the allocator twice we need to wrap it into ManuallyDrop
pub(super) alloc: ManuallyDrop<A>,
pub(super) ptr: *const T,
- pub(super) end: *const T,
+ pub(super) end: *const T, // If T is a ZST, this is actually ptr+len. This encoding is picked so that
+ // ptr == end is a quick test for the Iterator being empty, that works
+ // for both ZST and non-ZST.
}
#[stable(feature = "vec_intoiter_debug", since = "1.13.0")]
/// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed.
pub(crate) fn forget_remaining_elements(&mut self) {
- self.ptr = self.end;
+ // For th ZST case, it is crucial that we mutate `end` here, not `ptr`.
+ // `ptr` must stay aligned, while `end` may be unaligned.
+ self.end = self.ptr;
}
#[cfg(not(no_global_oom_handling))]
if self.ptr == self.end {
None
} else if T::IS_ZST {
- // purposefully don't use 'ptr.offset' because for
- // vectors with 0-size elements this would return the
- // same pointer.
- self.ptr = self.ptr.wrapping_byte_add(1);
+ // `ptr` has to stay where it is to remain aligned, so we reduce the length by 1 by
+ // reducing the `end`.
+ self.end = self.end.wrapping_byte_sub(1);
// Make up a value of this ZST.
Some(unsafe { mem::zeroed() })
let step_size = self.len().min(n);
let to_drop = ptr::slice_from_raw_parts_mut(self.ptr as *mut T, step_size);
if T::IS_ZST {
- // SAFETY: due to unchecked casts of unsigned amounts to signed offsets the wraparound
- // effectively results in unsigned pointers representing positions 0..usize::MAX,
- // which is valid for ZSTs.
- self.ptr = self.ptr.wrapping_byte_add(step_size);
+ // See `next` for why we sub `end` here.
+ self.end = self.end.wrapping_byte_sub(step_size);
} else {
// SAFETY: the min() above ensures that step_size is in bounds
self.ptr = unsafe { self.ptr.add(step_size) };
return Err(unsafe { array::IntoIter::new_unchecked(raw_ary, 0..len) });
}
- self.ptr = self.ptr.wrapping_byte_add(N);
+ self.end = self.end.wrapping_byte_sub(N);
// Safety: ditto
return Ok(unsafe { raw_ary.transpose().assume_init() });
}
/// if any element creation was unsuccessful.
///
/// The return type of this function depends on the return type of the closure.
-/// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N]; E>`.
+/// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N], E>`.
/// If you return `Option<T>` from the closure, you'll get an `Option<[T; N]>`.
///
/// # Arguments
/// return an array the same size as `self` or the first error encountered.
///
/// The return type of this function depends on the return type of the closure.
- /// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N]; E>`.
+ /// If you return `Result<T, E>` from the closure, you'll get a `Result<[T; N], E>`.
/// If you return `Option<T>` from the closure, you'll get an `Option<[T; N]>`.
///
/// # Examples
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<Cell<U>> for Cell<T> {}
impl<T> Cell<[T]> {
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<RefCell<U>> for RefCell<T> {}
struct BorrowRef<'b> {
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'b, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Ref<'b, U>> for Ref<'b, T> {}
#[stable(feature = "std_guard_impls", since = "1.20.0")]
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'b, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<RefMut<'b, U>> for RefMut<'b, T> {}
#[stable(feature = "std_guard_impls", since = "1.20.0")]
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<UnsafeCell<U>> for UnsafeCell<T> {}
/// [`UnsafeCell`], but [`Sync`].
}
}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
//#[unstable(feature = "sync_unsafe_cell", issue = "95439")]
impl<T: CoerceUnsized<U>, U> CoerceUnsized<SyncUnsafeCell<U>> for SyncUnsafeCell<T> {}
impl<'a, $($var,)* ClosureArguments, Function, ClosureReturnValue> const
FnOnce<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
where
- Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue+ ~const Destruct,
+ Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue + ~const Destruct,
{
type Output = ClosureReturnValue;
impl<'a, $($var,)* ClosureArguments, Function, ClosureReturnValue> const
FnMut<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
where
- Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue,
+ Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue + ~const Destruct,
{
extern "rust-call" fn call_mut(&mut self, args: ClosureArguments) -> Self::Output {
#[allow(non_snake_case)]
// Float -> Float
impl_from! { f32, f64, #[stable(feature = "lossless_float_conv", since = "1.6.0")] }
+// bool -> Float
+#[stable(feature = "float_from_bool", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")]
+impl const From<bool> for f32 {
+ /// Converts `bool` to `f32` losslessly.
+ #[inline]
+ fn from(small: bool) -> Self {
+ small as u8 as Self
+ }
+}
+#[stable(feature = "float_from_bool", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_const_unstable(feature = "const_num_from_num", issue = "87852")]
+impl const From<bool> for f64 {
+ /// Converts `bool` to `f64` losslessly.
+ #[inline]
+ fn from(small: bool) -> Self {
+ small as u8 as Self
+ }
+}
+
// no possible bounds violation
macro_rules! try_from_unbounded {
($source:ty, $($target:ty),*) => {$(
/// Basic implementation of a `va_list`.
// The name is WIP, using `VaListImpl` for now.
#[cfg(any(
- all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
+ all(
+ not(target_arch = "aarch64"),
+ not(target_arch = "powerpc"),
+ not(target_arch = "s390x"),
+ not(target_arch = "x86_64")
+ ),
all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
target_family = "wasm",
target_arch = "asmjs",
}
#[cfg(any(
- all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
+ all(
+ not(target_arch = "aarch64"),
+ not(target_arch = "powerpc"),
+ not(target_arch = "s390x"),
+ not(target_arch = "x86_64")
+ ),
all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
target_family = "wasm",
target_arch = "asmjs",
_marker: PhantomData<&'f mut &'f c_void>,
}
+/// s390x ABI implementation of a `va_list`.
+#[cfg(target_arch = "s390x")]
+#[repr(C)]
+#[derive(Debug)]
+#[unstable(
+ feature = "c_variadic",
+ reason = "the `c_variadic` feature has not been properly tested on \
+ all supported platforms",
+ issue = "44930"
+)]
+#[lang = "va_list"]
+pub struct VaListImpl<'f> {
+ gpr: i64,
+ fpr: i64,
+ overflow_arg_area: *mut c_void,
+ reg_save_area: *mut c_void,
+ _marker: PhantomData<&'f mut &'f c_void>,
+}
+
/// x86_64 ABI implementation of a `va_list`.
#[cfg(all(target_arch = "x86_64", not(target_os = "uefi"), not(windows)))]
#[repr(C)]
all(
not(target_arch = "aarch64"),
not(target_arch = "powerpc"),
+ not(target_arch = "s390x"),
not(target_arch = "x86_64")
),
all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
inner: VaListImpl<'f>,
#[cfg(all(
- any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
+ any(
+ target_arch = "aarch64",
+ target_arch = "powerpc",
+ target_arch = "s390x",
+ target_arch = "x86_64"
+ ),
any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
not(target_family = "wasm"),
not(target_arch = "asmjs"),
}
#[cfg(any(
- all(not(target_arch = "aarch64"), not(target_arch = "powerpc"), not(target_arch = "x86_64")),
+ all(
+ not(target_arch = "aarch64"),
+ not(target_arch = "powerpc"),
+ not(target_arch = "s390x"),
+ not(target_arch = "x86_64")
+ ),
all(target_arch = "aarch64", any(target_os = "macos", target_os = "ios")),
target_family = "wasm",
target_arch = "asmjs",
}
#[cfg(all(
- any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
+ any(
+ target_arch = "aarch64",
+ target_arch = "powerpc",
+ target_arch = "s390x",
+ target_arch = "x86_64"
+ ),
any(not(target_arch = "aarch64"), not(any(target_os = "macos", target_os = "ios"))),
not(target_family = "wasm"),
not(target_arch = "asmjs"),
/// 1. The `pieces` slice must be at least as long as `fmt`.
/// 2. Every [`rt::v1::Argument::position`] value within `fmt` must be a
/// valid index of `args`.
- /// 3. Every [`Count::Param`] within `fmt` must contain a valid index of
+ /// 3. Every [`rt::v1::Count::Param`] within `fmt` must contain a valid index of
/// `args`.
#[doc(hidden)]
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized> Pointer for *const T {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
- // Cast is needed here because `.addr()` requires `T: Sized`.
- pointer_fmt_inner((*self as *const ()).addr(), f)
+ // Cast is needed here because `.expose_addr()` requires `T: Sized`.
+ pointer_fmt_inner((*self as *const ()).expose_addr(), f)
}
}
/// non-Send/Sync as well, and we don't want that.
///
/// It also simplifies the HIR lowering of `.await`.
-// FIXME(swatinem): This type can be removed when bumping the bootstrap compiler
+#[cfg_attr(not(bootstrap), lang = "ResumeTy")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[derive(Debug, Copy, Clone)]
/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
// This is `const` to avoid extra errors after we recover from `const async fn`
-// FIXME(swatinem): This fn can be removed when bumping the bootstrap compiler
#[cfg_attr(bootstrap, lang = "from_generator")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
GenFuture(gen)
}
-// FIXME(swatinem): This fn can be removed when bumping the bootstrap compiler
-#[cfg_attr(bootstrap, lang = "get_context")]
+#[lang = "get_context"]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
#[must_use]
unsafe { &mut *cx.0.as_ptr().cast() }
}
-// FIXME(swatinem): This fn is currently needed to work around shortcomings
-// in type and lifetime inference.
-// See the comment at the bottom of `LoweringContext::make_async_expr` and
-// <https://github.com/rust-lang/rust/issues/104826>.
#[cfg_attr(not(bootstrap), lang = "identity_future")]
#[doc(hidden)]
#[unstable(feature = "gen_future", issue = "50547")]
/// println!("Hash is {:x}!", hasher.finish());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
- fn hash<H: Hasher>(&self, state: &mut H);
+ fn hash<H: ~const Hasher>(&self, state: &mut H);
/// Feeds a slice of this type into the given [`Hasher`].
///
#[rustc_const_unstable(feature = "const_hash", issue = "104061")]
impl<T: ?Sized + ~const Hash> const Hash for &mut T {
#[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
+ fn hash<H: ~const Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
#[rustc_safe_intrinsic]
pub fn assert_zero_valid<T>();
- /// A guard for unsafe functions that cannot ever be executed if `T` has invalid
- /// bit patterns: This will statically either panic, or do nothing.
+ /// A guard for `std::mem::uninitialized`. This will statically either panic, or do nothing.
///
/// This intrinsic does not have a stable counterpart.
#[rustc_const_unstable(feature = "const_assert_type2", issue = "none")]
#[rustc_safe_intrinsic]
- pub fn assert_uninit_valid<T>();
+ #[cfg(not(bootstrap))]
+ pub fn assert_mem_uninitialized_valid<T>();
/// Gets a reference to a static `Location` indicating where it was called.
///
//! if you want your MIR to be modified by the full MIR pipeline, or `#![custom_mir(dialect =
//! "runtime", phase = "optimized")] if you don't.
//!
-//! [dialect docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
+//! [dialect docs]:
+//! https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.MirPhase.html
//!
//! The input to the [`mir!`] macro is:
//!
//! Return()
//! })
//! }
+//!
+//! #[custom_mir(dialect = "runtime", phase = "optimized")]
+//! fn push_and_pop<T>(v: &mut Vec<T>, value: T) {
+//! mir!(
+//! let unused;
+//! let popped;
+//!
+//! {
+//! Call(unused, pop, Vec::push(v, value))
+//! }
+//!
+//! pop = {
+//! Call(popped, drop, Vec::pop(v))
+//! }
+//!
+//! drop = {
+//! Drop(popped, ret)
+//! }
+//!
+//! ret = {
+//! Return()
+//! }
+//! )
+//! }
//! ```
//!
//! We can also set off compilation failures that happen in sufficiently late stages of the
//!
//! #### Terminators
//!
-//! - [`Goto`] and [`Return`] have associated functions.
+//! Custom MIR does not currently support cleanup blocks or non-trivial unwind paths. As such, there
+//! are no resume and abort terminators, and terminators that might unwind do not have any way to
+//! indicate the unwind block.
+//!
+//! - [`Goto`], [`Return`], [`Unreachable`], [`Drop`](Drop()), and [`DropAndReplace`] have associated functions.
//! - `match some_int_operand` becomes a `SwitchInt`. Each arm should be `literal => basic_block`
//! - The exception is the last arm, which must be `_ => basic_block` and corresponds to the
//! otherwise branch.
+//! - [`Call`] has an associated function as well. The third argument of this function is a normal
+//! function call expresion, for example `my_other_function(a, 5)`.
//!
#![unstable(
define!("mir_return", fn Return() -> BasicBlock);
define!("mir_goto", fn Goto(destination: BasicBlock) -> BasicBlock);
+define!("mir_unreachable", fn Unreachable() -> BasicBlock);
+define!("mir_drop", fn Drop<T>(place: T, goto: BasicBlock));
+define!("mir_drop_and_replace", fn DropAndReplace<T>(place: T, value: T, goto: BasicBlock));
+define!("mir_call", fn Call<T>(place: T, goto: BasicBlock, call: T));
define!("mir_retag", fn Retag<T>(place: T));
-define!("mir_retag_raw", fn RetagRaw<T>(place: T));
define!("mir_move", fn Move<T>(place: T) -> T);
define!("mir_static", fn Static<T>(s: T) -> &'static T);
define!("mir_static_mut", fn StaticMut<T>(s: T) -> *mut T);
/// the first true result or the first error.
///
/// The return type of this method depends on the return type of the closure.
- /// If you return `Result<bool, E>` from the closure, you'll get a `Result<Option<Self::Item>; E>`.
+ /// If you return `Result<bool, E>` from the closure, you'll get a `Result<Option<Self::Item>, E>`.
/// If you return `Option<bool>` from the closure, you'll get an `Option<Option<Self::Item>>`.
///
/// # Examples
/// [`Rc`]: ../../std/rc/struct.Rc.html
/// [RFC982]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md
/// [nomicon-coerce]: ../../nomicon/coercions.html
-#[unstable(feature = "unsize", issue = "27732")]
+#[unstable(feature = "unsize", issue = "18598")]
#[lang = "unsize"]
#[cfg_attr(not(bootstrap), rustc_deny_explicit_impl)]
pub trait Unsize<T: ?Sized> {
pub unsafe fn uninitialized<T>() -> T {
// SAFETY: the caller must guarantee that an uninitialized value is valid for `T`.
unsafe {
- intrinsics::assert_uninit_valid::<T>();
+ #[cfg(not(bootstrap))] // If the compiler hits this itself then it deserves the UB.
+ intrinsics::assert_mem_uninitialized_valid::<T>();
let mut val = MaybeUninit::<T>::uninit();
// Fill memory with 0x01, as an imperfect mitigation for old code that uses this function on
#[doc(alias = "]")]
#[doc(alias = "[]")]
#[const_trait]
-pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
+pub trait IndexMut<Idx: ?Sized>: ~const Index<Idx> {
/// Performs the mutable indexing (`container[index]`) operation.
///
/// # Panics
#[unstable(feature = "generator_trait", issue = "43122")]
pub use self::generator::{Generator, GeneratorState};
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
pub use self::unsize::CoerceUnsized;
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
/// [dst-coerce]: https://github.com/rust-lang/rfcs/blob/master/text/0982-dst-coercion.md
/// [unsize]: crate::marker::Unsize
/// [nomicon-coerce]: ../../nomicon/coercions.html
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T: ?Sized> {
// Empty.
}
// &mut T -> &mut U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
// &mut T -> &U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b mut T {}
// &mut T -> *mut U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for &'a mut T {}
// &mut T -> *const U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a mut T {}
// &T -> &U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
// &T -> *const U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for &'a T {}
// *mut T -> *mut U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
// *mut T -> *const U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *mut T {}
// *const T -> *const U
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
/// `DispatchFromDyn` is used in the implementation of object safety checks (specifically allowing
///
/// Unlike `Pin::new_unchecked`, this method is safe because the pointer
/// `P` dereferences to an [`Unpin`] type, which cancels the pinning guarantees.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::pin::Pin;
+ ///
+ /// let mut val: u8 = 5;
+ /// // We can pin the value, since it doesn't care about being moved
+ /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val);
+ /// ```
#[inline(always)]
#[rustc_const_unstable(feature = "const_pin", issue = "76654")]
#[stable(feature = "pin", since = "1.33.0")]
/// Unwraps this `Pin<P>` returning the underlying pointer.
///
- /// This requires that the data inside this `Pin` is [`Unpin`] so that we
+ /// This requires that the data inside this `Pin` implements [`Unpin`] so that we
/// can ignore the pinning invariants when unwrapping it.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::pin::Pin;
+ ///
+ /// let mut val: u8 = 5;
+ /// let pinned: Pin<&mut u8> = Pin::new(&mut val);
+ /// // Unwrap the pin to get a reference to the value
+ /// let r = Pin::into_inner(pinned);
+ /// assert_eq!(*r, 5);
+ /// ```
#[inline(always)]
#[rustc_const_unstable(feature = "const_pin", issue = "76654")]
#[stable(feature = "pin_into_inner", since = "1.39.0")]
///
/// This overwrites pinned data, but that is okay: its destructor gets
/// run before being overwritten, so no pinning guarantee is violated.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use std::pin::Pin;
+ ///
+ /// let mut val: u8 = 5;
+ /// let mut pinned: Pin<&mut u8> = Pin::new(&mut val);
+ /// println!("{}", pinned); // 5
+ /// pinned.as_mut().set(10);
+ /// println!("{}", pinned); // 10
+ /// ```
#[stable(feature = "pin", since = "1.33.0")]
#[inline(always)]
pub fn set(&mut self, value: P::Target)
#[stable(feature = "nonnull", since = "1.25.0")]
impl<T: ?Sized> Copy for NonNull<T> {}
-#[unstable(feature = "coerce_unsized", issue = "27732")]
+#[unstable(feature = "coerce_unsized", issue = "18598")]
impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
#[unstable(feature = "dispatch_from_dyn", issue = "none")]
#[stable(feature = "chunks_exact", since = "1.31.0")]
#[inline]
pub fn chunks_exact(&self, chunk_size: usize) -> ChunksExact<'_, T> {
- assert_ne!(chunk_size, 0);
+ assert_ne!(chunk_size, 0, "chunks cannot have a size of zero");
ChunksExact::new(self, chunk_size)
}
#[stable(feature = "chunks_exact", since = "1.31.0")]
#[inline]
pub fn chunks_exact_mut(&mut self, chunk_size: usize) -> ChunksExactMut<'_, T> {
- assert_ne!(chunk_size, 0);
+ assert_ne!(chunk_size, 0, "chunks cannot have a size of zero");
ChunksExactMut::new(self, chunk_size)
}
/// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]);
/// assert_eq!(remainder, &['m']);
/// ```
+ ///
+ /// If you expect the slice to be an exact multiple, you can combine
+ /// `let`-`else` with an empty slice pattern:
+ /// ```
+ /// #![feature(slice_as_chunks)]
+ /// let slice = ['R', 'u', 's', 't'];
+ /// let (chunks, []) = slice.as_chunks::<2>() else {
+ /// panic!("slice didn't have even length")
+ /// };
+ /// assert_eq!(chunks, &[['R', 'u'], ['s', 't']]);
+ /// ```
#[unstable(feature = "slice_as_chunks", issue = "74985")]
#[inline]
#[must_use]
pub fn as_chunks<const N: usize>(&self) -> (&[[T; N]], &[T]) {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "chunks cannot have a size of zero");
let len = self.len() / N;
let (multiple_of_n, remainder) = self.split_at(len * N);
// SAFETY: We already panicked for zero, and ensured by construction
#[inline]
#[must_use]
pub fn as_rchunks<const N: usize>(&self) -> (&[T], &[[T; N]]) {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "chunks cannot have a size of zero");
let len = self.len() / N;
let (remainder, multiple_of_n) = self.split_at(self.len() - len * N);
// SAFETY: We already panicked for zero, and ensured by construction
#[unstable(feature = "array_chunks", issue = "74985")]
#[inline]
pub fn array_chunks<const N: usize>(&self) -> ArrayChunks<'_, T, N> {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "chunks cannot have a size of zero");
ArrayChunks::new(self)
}
#[inline]
#[must_use]
pub fn as_chunks_mut<const N: usize>(&mut self) -> (&mut [[T; N]], &mut [T]) {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "chunks cannot have a size of zero");
let len = self.len() / N;
let (multiple_of_n, remainder) = self.split_at_mut(len * N);
// SAFETY: We already panicked for zero, and ensured by construction
#[inline]
#[must_use]
pub fn as_rchunks_mut<const N: usize>(&mut self) -> (&mut [T], &mut [[T; N]]) {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "chunks cannot have a size of zero");
let len = self.len() / N;
let (remainder, multiple_of_n) = self.split_at_mut(self.len() - len * N);
// SAFETY: We already panicked for zero, and ensured by construction
#[unstable(feature = "array_chunks", issue = "74985")]
#[inline]
pub fn array_chunks_mut<const N: usize>(&mut self) -> ArrayChunksMut<'_, T, N> {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "chunks cannot have a size of zero");
ArrayChunksMut::new(self)
}
#[unstable(feature = "array_windows", issue = "75027")]
#[inline]
pub fn array_windows<const N: usize>(&self) -> ArrayWindows<'_, T, N> {
- assert_ne!(N, 0);
+ assert_ne!(N, 0, "windows cannot have a size of zero");
ArrayWindows::new(self)
}
/// The slice is assumed to be partitioned according to the given predicate.
/// This means that all elements for which the predicate returns true are at the start of the slice
/// and all elements for which the predicate returns false are at the end.
- /// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0
+ /// For example, `[7, 15, 3, 5, 4, 12, 6]` is partitioned under the predicate `x % 2 != 0`
/// (all odd numbers are at the start, all even at the end).
///
/// If this slice is not partitioned, the returned result is unspecified and meaningless,
/// An iterator over the lines of a string, as string slices.
///
- /// Lines are ended with either a newline (`\n`) or a carriage return with
- /// a line feed (`\r\n`).
+ /// Lines are split at line endings that are either newlines (`\n`) or
+ /// sequences of a carriage return followed by a line feed (`\r\n`).
+ ///
+ /// Line terminators are not included in the lines returned by the iterator.
///
/// The final line ending is optional. A string that ends with a final line
/// ending will return the same lines as an otherwise identical string
/// pointer.
wake_by_ref: unsafe fn(*const ()),
- /// This function gets called when a [`RawWaker`] gets dropped.
+ /// This function gets called when a [`Waker`] gets dropped.
///
/// The implementation of this function must make sure to release any
/// resources that are associated with this instance of a [`RawWaker`] and
///
/// # `drop`
///
- /// This function gets called when a [`RawWaker`] gets dropped.
+ /// This function gets called when a [`Waker`] gets dropped.
///
/// The implementation of this function must make sure to release any
/// resources that are associated with this instance of a [`RawWaker`] and
/// Currently, `Context` only serves to provide access to a [`&Waker`](Waker)
/// which can be used to wake the current task.
#[stable(feature = "futures_api", since = "1.36.0")]
-#[cfg_attr(not(bootstrap), lang = "Context")]
pub struct Context<'a> {
waker: &'a Waker,
// Ensure we future-proof against variance changes by forcing
assert_eq!(ft1, ft2);
}
}
+
+/// Regression test for https://github.com/rust-lang/rust/issues/50619.
+#[test]
+#[cfg(target_os = "linux")]
+fn test_read_dir_infinite_loop() {
+ use crate::io::ErrorKind;
+ use crate::process::Command;
+
+ // Create a zombie child process
+ let Ok(mut child) = Command::new("echo").spawn() else { return };
+
+ // Make sure the process is (un)dead
+ match child.kill() {
+ // InvalidInput means the child already exited
+ Err(e) if e.kind() != ErrorKind::InvalidInput => return,
+ _ => {}
+ }
+
+ // open() on this path will succeed, but readdir() will fail
+ let id = child.id();
+ let path = format!("/proc/{id}/net");
+
+ // Skip the test if we can't open the directory in the first place
+ let Ok(dir) = fs::read_dir(path) else { return };
+
+ // Check for duplicate errors
+ assert!(dir.filter(|e| e.is_err()).take(2).count() < 2);
+}
}
}
+#[stable(feature = "path_buf_deref_mut", since = "CURRENT_RUSTC_VERSION")]
+impl ops::DerefMut for PathBuf {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Path {
+ Path::from_inner_mut(&mut self.inner)
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl Borrow<Path> for PathBuf {
#[inline]
unsafe { &*(s.as_ref() as *const OsStr as *const Path) }
}
+ fn from_inner_mut(inner: &mut OsStr) -> &mut Path {
+ // SAFETY: Path is just a wrapper around OsStr,
+ // therefore converting &mut OsStr to &mut Path is safe.
+ unsafe { &mut *(inner as *mut OsStr as *mut Path) }
+ }
+
/// Yields the underlying [`OsStr`] slice.
///
/// # Examples
/// #![feature(path_as_mut_os_str)]
/// use std::path::{Path, PathBuf};
///
- /// let mut path = PathBuf::from("/Foo.TXT").into_boxed_path();
+ /// let mut path = PathBuf::from("Foo.TXT");
///
- /// assert_ne!(&*path, Path::new("/foo.txt"));
+ /// assert_ne!(path, Path::new("foo.txt"));
///
/// path.as_mut_os_str().make_ascii_lowercase();
- /// assert_eq!(&*path, Path::new("/foo.txt"));
+ /// assert_eq!(path, Path::new("foo.txt"));
/// ```
#[unstable(feature = "path_as_mut_os_str", issue = "105021")]
#[must_use]
fn is_read_vectored(&self) -> bool {
self.inner.is_read_vectored()
}
+
+ fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.inner.read_to_end(buf)
+ }
}
impl AsInner<AnonPipe> for ChildStdout {
/// ```
#[stable(feature = "process", since = "1.0.0")]
pub fn output(&mut self) -> io::Result<Output> {
- self.inner
- .spawn(imp::Stdio::MakePipe, false)
- .map(Child::from_inner)
- .and_then(|p| p.wait_with_output())
+ let (status, stdout, stderr) = self.inner.output()?;
+ Ok(Output { status: ExitStatus(status), stdout, stderr })
}
/// Executes a command as a child process, waiting for it to finish and
pub struct ReadDir {
inner: Arc<InnerReadDir>,
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
end_of_stream: bool,
}
+impl ReadDir {
+ fn new(inner: InnerReadDir) -> Self {
+ Self { inner: Arc::new(inner), end_of_stream: false }
+ }
+}
+
struct Dir(*mut libc::DIR);
unsafe impl Send for Dir {}
target_os = "illumos"
))]
fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ if self.end_of_stream {
+ return None;
+ }
+
unsafe {
loop {
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
super::os::set_errno(0);
let entry_ptr = readdir64(self.inner.dirp.0);
if entry_ptr.is_null() {
- // null can mean either the end is reached or an error occurred.
- // So we had to clear errno beforehand to check for an error now.
+ // We either encountered an error, or reached the end. Either way,
+ // the next call to next() should return None.
+ self.end_of_stream = true;
+
+ // To distinguish between errors and end-of-directory, we had to clear
+ // errno beforehand to check for an error now.
return match super::os::errno() {
0 => None,
e => Some(Err(Error::from_raw_os_error(e))),
} else {
let root = path.to_path_buf();
let inner = InnerReadDir { dirp: Dir(ptr), root };
- Ok(ReadDir {
- inner: Arc::new(inner),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- })
+ Ok(ReadDir::new(inner))
}
}
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use crate::os::unix::prelude::{OwnedFd, RawFd};
use crate::path::{Path, PathBuf};
- use crate::sync::Arc;
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::{cvt, cvt_r};
// a valid root is not needed because we do not call any functions involving the full path
// of the DirEntrys.
let dummy_root = PathBuf::new();
- Ok((
- ReadDir {
- inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- },
- new_parent_fd,
- ))
+ let inner = InnerReadDir { dirp, root: dummy_root };
+ Ok((ReadDir::new(inner), new_parent_fd))
}
#[cfg(any(
self.0.is_read_vectored()
}
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.0.read_to_end(buf)
+ }
+
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
Ok((Process { handle: Handle::new(process_handle) }, ours))
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
+
pub fn exec(&mut self, default: Stdio) -> io::Error {
if self.saw_nul() {
return io::const_io_error!(
}
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
+
// Attempts to fork the process. If successful, returns Ok((0, -1))
// in the child, and Ok((child_pid, -1)) in the parent.
#[cfg(not(target_os = "linux"))]
unsupported()
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ unsupported()
+ }
+
pub fn exec(&mut self, _default: Stdio) -> io::Error {
unsupported_err()
}
}
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
+
pub fn exec(&mut self, default: Stdio) -> io::Error {
let ret = Command::spawn(self, default, false);
match ret {
pub mod io;
pub mod locks;
pub mod net;
+pub mod once;
pub mod os;
#[path = "../unix/os_str.rs"]
pub mod os_str;
--- /dev/null
+use crate::cell::Cell;
+use crate::sync as public;
+
+pub struct Once {
+ state: Cell<State>,
+}
+
+pub struct OnceState {
+ poisoned: bool,
+ set_state_to: Cell<State>,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum State {
+ Incomplete,
+ Poisoned,
+ Running,
+ Complete,
+}
+
+struct CompletionGuard<'a> {
+ state: &'a Cell<State>,
+ set_state_on_drop_to: State,
+}
+
+impl<'a> Drop for CompletionGuard<'a> {
+ fn drop(&mut self) {
+ self.state.set(self.set_state_on_drop_to);
+ }
+}
+
+// Safety: threads are not supported on this platform.
+unsafe impl Sync for Once {}
+
+impl Once {
+ #[inline]
+ #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
+ pub const fn new() -> Once {
+ Once { state: Cell::new(State::Incomplete) }
+ }
+
+ #[inline]
+ pub fn is_completed(&self) -> bool {
+ self.state.get() == State::Complete
+ }
+
+ #[cold]
+ #[track_caller]
+ pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
+ let state = self.state.get();
+ match state {
+ State::Poisoned if !ignore_poisoning => {
+ // Panic to propagate the poison.
+ panic!("Once instance has previously been poisoned");
+ }
+ State::Incomplete | State::Poisoned => {
+ self.state.set(State::Running);
+ // `guard` will set the new state on drop.
+ let mut guard =
+ CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned };
+ // Run the function, letting it know if we're poisoned or not.
+ let f_state = public::OnceState {
+ inner: OnceState {
+ poisoned: state == State::Poisoned,
+ set_state_to: Cell::new(State::Complete),
+ },
+ };
+ f(&f_state);
+ guard.set_state_on_drop_to = f_state.inner.set_state_to.get();
+ }
+ State::Running => {
+ panic!("one-time initialization may not be performed recursively");
+ }
+ State::Complete => {}
+ }
+ }
+}
+
+impl OnceState {
+ #[inline]
+ pub fn is_poisoned(&self) -> bool {
+ self.poisoned
+ }
+
+ #[inline]
+ pub fn poison(&self) {
+ self.set_state_to.set(State::Poisoned)
+ }
+}
self.0
}
+ pub fn read_to_end(&self, _buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.0
+ }
+
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
self.0
}
) -> io::Result<(Process, StdioPipes)> {
unsupported()
}
+
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ unsupported()
+ }
}
impl From<AnonPipe> for Stdio {
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
pub mod net;
+#[path = "../unsupported/once.rs"]
+pub mod once;
pub mod os;
#[path = "../unix/os_str.rs"]
pub mod os_str;
} else {
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
+ #[path = "../unsupported/once.rs"]
+ pub mod once;
#[path = "../unsupported/thread.rs"]
pub mod thread;
}
use crate::os::windows::prelude::*;
use crate::ffi::OsStr;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::path::Path;
use crate::ptr;
self.inner.is_read_vectored()
}
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.handle().read_to_end(buf)
+ }
+
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
unsafe {
let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
))
}
}
+
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
}
impl fmt::Debug for Command {
+++ /dev/null
-// Each `Once` has one word of atomic state, and this state is CAS'd on to
-// determine what to do. There are four possible state of a `Once`:
-//
-// * Incomplete - no initialization has run yet, and no thread is currently
-// using the Once.
-// * Poisoned - some thread has previously attempted to initialize the Once, but
-// it panicked, so the Once is now poisoned. There are no other
-// threads currently accessing this Once.
-// * Running - some thread is currently attempting to run initialization. It may
-// succeed, so all future threads need to wait for it to finish.
-// Note that this state is accompanied with a payload, described
-// below.
-// * Complete - initialization has completed and all future calls should finish
-// immediately.
-//
-// With 4 states we need 2 bits to encode this, and we use the remaining bits
-// in the word we have allocated as a queue of threads waiting for the thread
-// responsible for entering the RUNNING state. This queue is just a linked list
-// of Waiter nodes which is monotonically increasing in size. Each node is
-// allocated on the stack, and whenever the running closure finishes it will
-// consume the entire queue and notify all waiters they should try again.
-//
-// You'll find a few more details in the implementation, but that's the gist of
-// it!
-//
-// Atomic orderings:
-// When running `Once` we deal with multiple atomics:
-// `Once.state_and_queue` and an unknown number of `Waiter.signaled`.
-// * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the
-// result of the `Once`, and (3) for synchronizing `Waiter` nodes.
-// - At the end of the `call` function we have to make sure the result
-// of the `Once` is acquired. So every load which can be the only one to
-// load COMPLETED must have at least acquire ordering, which means all
-// three of them.
-// - `WaiterQueue::drop` is the only place that may store COMPLETED, and
-// must do so with release ordering to make the result available.
-// - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and
-// needs to make the nodes available with release ordering. The load in
-// its `compare_exchange` can be relaxed because it only has to compare
-// the atomic, not to read other data.
-// - `WaiterQueue::drop` must see the `Waiter` nodes, so it must load
-// `state_and_queue` with acquire ordering.
-// - There is just one store where `state_and_queue` is used only as a
-// state flag, without having to synchronize data: switching the state
-// from INCOMPLETE to RUNNING in `call`. This store can be Relaxed,
-// but the read has to be Acquire because of the requirements mentioned
-// above.
-// * `Waiter.signaled` is both used as a flag, and to protect a field with
-// interior mutability in `Waiter`. `Waiter.thread` is changed in
-// `WaiterQueue::drop` which then sets `signaled` with release ordering.
-// After `wait` loads `signaled` with acquire ordering and sees it is true,
-// it needs to see the changes to drop the `Waiter` struct correctly.
-// * There is one place where the two atomics `Once.state_and_queue` and
-// `Waiter.signaled` come together, and might be reordered by the compiler or
-// processor. Because both use acquire ordering such a reordering is not
-// allowed, so no need for `SeqCst`.
-
-use crate::cell::Cell;
-use crate::fmt;
-use crate::ptr;
-use crate::sync as public;
-use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
-use crate::thread::{self, Thread};
-
-type Masked = ();
-
-pub struct Once {
- state_and_queue: AtomicPtr<Masked>,
-}
-
-pub struct OnceState {
- poisoned: bool,
- set_state_on_drop_to: Cell<*mut Masked>,
-}
-
-// Four states that a Once can be in, encoded into the lower bits of
-// `state_and_queue` in the Once structure.
-const INCOMPLETE: usize = 0x0;
-const POISONED: usize = 0x1;
-const RUNNING: usize = 0x2;
-const COMPLETE: usize = 0x3;
-
-// Mask to learn about the state. All other bits are the queue of waiters if
-// this is in the RUNNING state.
-const STATE_MASK: usize = 0x3;
-
-// Representation of a node in the linked list of waiters, used while in the
-// RUNNING state.
-// Note: `Waiter` can't hold a mutable pointer to the next thread, because then
-// `wait` would both hand out a mutable reference to its `Waiter` node, and keep
-// a shared reference to check `signaled`. Instead we hold shared references and
-// use interior mutability.
-#[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
-struct Waiter {
- thread: Cell<Option<Thread>>,
- signaled: AtomicBool,
- next: *const Waiter,
-}
-
-// Head of a linked list of waiters.
-// Every node is a struct on the stack of a waiting thread.
-// Will wake up the waiters when it gets dropped, i.e. also on panic.
-struct WaiterQueue<'a> {
- state_and_queue: &'a AtomicPtr<Masked>,
- set_state_on_drop_to: *mut Masked,
-}
-
-impl Once {
- #[inline]
- #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
- pub const fn new() -> Once {
- Once { state_and_queue: AtomicPtr::new(ptr::invalid_mut(INCOMPLETE)) }
- }
-
- #[inline]
- pub fn is_completed(&self) -> bool {
- // An `Acquire` load is enough because that makes all the initialization
- // operations visible to us, and, this being a fast path, weaker
- // ordering helps with performance. This `Acquire` synchronizes with
- // `Release` operations on the slow path.
- self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
- }
-
- // This is a non-generic function to reduce the monomorphization cost of
- // using `call_once` (this isn't exactly a trivial or small implementation).
- //
- // Additionally, this is tagged with `#[cold]` as it should indeed be cold
- // and it helps let LLVM know that calls to this function should be off the
- // fast path. Essentially, this should help generate more straight line code
- // in LLVM.
- //
- // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
- // currently no way to take an `FnOnce` and call it via virtual dispatch
- // without some allocation overhead.
- #[cold]
- #[track_caller]
- pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) {
- let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
- loop {
- match state_and_queue.addr() {
- COMPLETE => break,
- POISONED if !ignore_poisoning => {
- // Panic to propagate the poison.
- panic!("Once instance has previously been poisoned");
- }
- POISONED | INCOMPLETE => {
- // Try to register this thread as the one RUNNING.
- let exchange_result = self.state_and_queue.compare_exchange(
- state_and_queue,
- ptr::invalid_mut(RUNNING),
- Ordering::Acquire,
- Ordering::Acquire,
- );
- if let Err(old) = exchange_result {
- state_and_queue = old;
- continue;
- }
- // `waiter_queue` will manage other waiting threads, and
- // wake them up on drop.
- let mut waiter_queue = WaiterQueue {
- state_and_queue: &self.state_and_queue,
- set_state_on_drop_to: ptr::invalid_mut(POISONED),
- };
- // Run the initialization function, letting it know if we're
- // poisoned or not.
- let init_state = public::OnceState {
- inner: OnceState {
- poisoned: state_and_queue.addr() == POISONED,
- set_state_on_drop_to: Cell::new(ptr::invalid_mut(COMPLETE)),
- },
- };
- init(&init_state);
- waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get();
- break;
- }
- _ => {
- // All other values must be RUNNING with possibly a
- // pointer to the waiter queue in the more significant bits.
- assert!(state_and_queue.addr() & STATE_MASK == RUNNING);
- wait(&self.state_and_queue, state_and_queue);
- state_and_queue = self.state_and_queue.load(Ordering::Acquire);
- }
- }
- }
- }
-}
-
-fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
- // Note: the following code was carefully written to avoid creating a
- // mutable reference to `node` that gets aliased.
- loop {
- // Don't queue this thread if the status is no longer running,
- // otherwise we will not be woken up.
- if current_state.addr() & STATE_MASK != RUNNING {
- return;
- }
-
- // Create the node for our current thread.
- let node = Waiter {
- thread: Cell::new(Some(thread::current())),
- signaled: AtomicBool::new(false),
- next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter,
- };
- let me = &node as *const Waiter as *const Masked as *mut Masked;
-
- // Try to slide in the node at the head of the linked list, making sure
- // that another thread didn't just replace the head of the linked list.
- let exchange_result = state_and_queue.compare_exchange(
- current_state,
- me.with_addr(me.addr() | RUNNING),
- Ordering::Release,
- Ordering::Relaxed,
- );
- if let Err(old) = exchange_result {
- current_state = old;
- continue;
- }
-
- // We have enqueued ourselves, now lets wait.
- // It is important not to return before being signaled, otherwise we
- // would drop our `Waiter` node and leave a hole in the linked list
- // (and a dangling reference). Guard against spurious wakeups by
- // reparking ourselves until we are signaled.
- while !node.signaled.load(Ordering::Acquire) {
- // If the managing thread happens to signal and unpark us before we
- // can park ourselves, the result could be this thread never gets
- // unparked. Luckily `park` comes with the guarantee that if it got
- // an `unpark` just before on an unparked thread it does not park.
- thread::park();
- }
- break;
- }
-}
-
-#[stable(feature = "std_debug", since = "1.16.0")]
-impl fmt::Debug for Once {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Once").finish_non_exhaustive()
- }
-}
-
-impl Drop for WaiterQueue<'_> {
- fn drop(&mut self) {
- // Swap out our state with however we finished.
- let state_and_queue =
- self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel);
-
- // We should only ever see an old state which was RUNNING.
- assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING);
-
- // Walk the entire linked list of waiters and wake them up (in lifo
- // order, last to register is first to wake up).
- unsafe {
- // Right after setting `node.signaled = true` the other thread may
- // free `node` if there happens to be has a spurious wakeup.
- // So we have to take out the `thread` field and copy the pointer to
- // `next` first.
- let mut queue =
- state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter;
- while !queue.is_null() {
- let next = (*queue).next;
- let thread = (*queue).thread.take().unwrap();
- (*queue).signaled.store(true, Ordering::Release);
- // ^- FIXME (maybe): This is another case of issue #55005
- // `store()` has a potentially dangling ref to `signaled`.
- queue = next;
- thread.unpark();
- }
- }
- }
-}
-
-impl OnceState {
- #[inline]
- pub fn is_poisoned(&self) -> bool {
- self.poisoned
- }
-
- #[inline]
- pub fn poison(&self) {
- self.set_state_on_drop_to.set(ptr::invalid_mut(POISONED));
- }
-}
// As a result, we end up implementing it ourselves in the standard library.
// This also gives us the opportunity to optimize the implementation a bit which
// should help the fast path on call sites.
-//
-// So to recap, the guarantees of a Once are that it will call the
-// initialization closure at most once, and it will never return until the one
-// that's running has finished running. This means that we need some form of
-// blocking here while the custom callback is running at the very least.
-// Additionally, we add on the restriction of **poisoning**. Whenever an
-// initialization closure panics, the Once enters a "poisoned" state which means
-// that all future calls will immediately panic as well.
-//
-// So to implement this, one might first reach for a `Mutex`, but those cannot
-// be put into a `static`. It also gets a lot harder with poisoning to figure
-// out when the mutex needs to be deallocated because it's not after the closure
-// finishes, but after the first successful closure finishes.
-//
-// All in all, this is instead implemented with atomics and lock-free
-// operations! Whee!
cfg_if::cfg_if! {
if #[cfg(any(
))] {
mod futex;
pub use futex::{Once, OnceState};
+ } else if #[cfg(any(
+ windows,
+ target_family = "unix",
+ all(target_vendor = "fortanix", target_env = "sgx"),
+ target_os = "solid_asp3",
+ ))] {
+ mod queue;
+ pub use queue::{Once, OnceState};
} else {
- mod generic;
- pub use generic::{Once, OnceState};
+ pub use crate::sys::once::{Once, OnceState};
}
}
--- /dev/null
+// Each `Once` has one word of atomic state, and this state is CAS'd on to
+// determine what to do. There are four possible state of a `Once`:
+//
+// * Incomplete - no initialization has run yet, and no thread is currently
+// using the Once.
+// * Poisoned - some thread has previously attempted to initialize the Once, but
+// it panicked, so the Once is now poisoned. There are no other
+// threads currently accessing this Once.
+// * Running - some thread is currently attempting to run initialization. It may
+// succeed, so all future threads need to wait for it to finish.
+// Note that this state is accompanied with a payload, described
+// below.
+// * Complete - initialization has completed and all future calls should finish
+// immediately.
+//
+// With 4 states we need 2 bits to encode this, and we use the remaining bits
+// in the word we have allocated as a queue of threads waiting for the thread
+// responsible for entering the RUNNING state. This queue is just a linked list
+// of Waiter nodes which is monotonically increasing in size. Each node is
+// allocated on the stack, and whenever the running closure finishes it will
+// consume the entire queue and notify all waiters they should try again.
+//
+// You'll find a few more details in the implementation, but that's the gist of
+// it!
+//
+// Atomic orderings:
+// When running `Once` we deal with multiple atomics:
+// `Once.state_and_queue` and an unknown number of `Waiter.signaled`.
+// * `state_and_queue` is used (1) as a state flag, (2) for synchronizing the
+// result of the `Once`, and (3) for synchronizing `Waiter` nodes.
+// - At the end of the `call` function we have to make sure the result
+// of the `Once` is acquired. So every load which can be the only one to
+// load COMPLETED must have at least acquire ordering, which means all
+// three of them.
+// - `WaiterQueue::drop` is the only place that may store COMPLETED, and
+// must do so with release ordering to make the result available.
+// - `wait` inserts `Waiter` nodes as a pointer in `state_and_queue`, and
+// needs to make the nodes available with release ordering. The load in
+// its `compare_exchange` can be relaxed because it only has to compare
+// the atomic, not to read other data.
+// - `WaiterQueue::drop` must see the `Waiter` nodes, so it must load
+// `state_and_queue` with acquire ordering.
+// - There is just one store where `state_and_queue` is used only as a
+// state flag, without having to synchronize data: switching the state
+// from INCOMPLETE to RUNNING in `call`. This store can be Relaxed,
+// but the read has to be Acquire because of the requirements mentioned
+// above.
+// * `Waiter.signaled` is both used as a flag, and to protect a field with
+// interior mutability in `Waiter`. `Waiter.thread` is changed in
+// `WaiterQueue::drop` which then sets `signaled` with release ordering.
+// After `wait` loads `signaled` with acquire ordering and sees it is true,
+// it needs to see the changes to drop the `Waiter` struct correctly.
+// * There is one place where the two atomics `Once.state_and_queue` and
+// `Waiter.signaled` come together, and might be reordered by the compiler or
+// processor. Because both use acquire ordering such a reordering is not
+// allowed, so no need for `SeqCst`.
+
+use crate::cell::Cell;
+use crate::fmt;
+use crate::ptr;
+use crate::sync as public;
+use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
+use crate::thread::{self, Thread};
+
+type Masked = ();
+
+pub struct Once {
+ state_and_queue: AtomicPtr<Masked>,
+}
+
+pub struct OnceState {
+ poisoned: bool,
+ set_state_on_drop_to: Cell<*mut Masked>,
+}
+
+// Four states that a Once can be in, encoded into the lower bits of
+// `state_and_queue` in the Once structure.
+const INCOMPLETE: usize = 0x0;
+const POISONED: usize = 0x1;
+const RUNNING: usize = 0x2;
+const COMPLETE: usize = 0x3;
+
+// Mask to learn about the state. All other bits are the queue of waiters if
+// this is in the RUNNING state.
+const STATE_MASK: usize = 0x3;
+
+// Representation of a node in the linked list of waiters, used while in the
+// RUNNING state.
+// Note: `Waiter` can't hold a mutable pointer to the next thread, because then
+// `wait` would both hand out a mutable reference to its `Waiter` node, and keep
+// a shared reference to check `signaled`. Instead we hold shared references and
+// use interior mutability.
+#[repr(align(4))] // Ensure the two lower bits are free to use as state bits.
+struct Waiter {
+ thread: Cell<Option<Thread>>,
+ signaled: AtomicBool,
+ next: *const Waiter,
+}
+
+// Head of a linked list of waiters.
+// Every node is a struct on the stack of a waiting thread.
+// Will wake up the waiters when it gets dropped, i.e. also on panic.
+struct WaiterQueue<'a> {
+ state_and_queue: &'a AtomicPtr<Masked>,
+ set_state_on_drop_to: *mut Masked,
+}
+
+impl Once {
+ #[inline]
+ #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
+ pub const fn new() -> Once {
+ Once { state_and_queue: AtomicPtr::new(ptr::invalid_mut(INCOMPLETE)) }
+ }
+
+ #[inline]
+ pub fn is_completed(&self) -> bool {
+ // An `Acquire` load is enough because that makes all the initialization
+ // operations visible to us, and, this being a fast path, weaker
+ // ordering helps with performance. This `Acquire` synchronizes with
+ // `Release` operations on the slow path.
+ self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
+ }
+
+ // This is a non-generic function to reduce the monomorphization cost of
+ // using `call_once` (this isn't exactly a trivial or small implementation).
+ //
+ // Additionally, this is tagged with `#[cold]` as it should indeed be cold
+ // and it helps let LLVM know that calls to this function should be off the
+ // fast path. Essentially, this should help generate more straight line code
+ // in LLVM.
+ //
+ // Finally, this takes an `FnMut` instead of a `FnOnce` because there's
+ // currently no way to take an `FnOnce` and call it via virtual dispatch
+ // without some allocation overhead.
+ #[cold]
+ #[track_caller]
+ pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) {
+ let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
+ loop {
+ match state_and_queue.addr() {
+ COMPLETE => break,
+ POISONED if !ignore_poisoning => {
+ // Panic to propagate the poison.
+ panic!("Once instance has previously been poisoned");
+ }
+ POISONED | INCOMPLETE => {
+ // Try to register this thread as the one RUNNING.
+ let exchange_result = self.state_and_queue.compare_exchange(
+ state_and_queue,
+ ptr::invalid_mut(RUNNING),
+ Ordering::Acquire,
+ Ordering::Acquire,
+ );
+ if let Err(old) = exchange_result {
+ state_and_queue = old;
+ continue;
+ }
+ // `waiter_queue` will manage other waiting threads, and
+ // wake them up on drop.
+ let mut waiter_queue = WaiterQueue {
+ state_and_queue: &self.state_and_queue,
+ set_state_on_drop_to: ptr::invalid_mut(POISONED),
+ };
+ // Run the initialization function, letting it know if we're
+ // poisoned or not.
+ let init_state = public::OnceState {
+ inner: OnceState {
+ poisoned: state_and_queue.addr() == POISONED,
+ set_state_on_drop_to: Cell::new(ptr::invalid_mut(COMPLETE)),
+ },
+ };
+ init(&init_state);
+ waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get();
+ break;
+ }
+ _ => {
+ // All other values must be RUNNING with possibly a
+ // pointer to the waiter queue in the more significant bits.
+ assert!(state_and_queue.addr() & STATE_MASK == RUNNING);
+ wait(&self.state_and_queue, state_and_queue);
+ state_and_queue = self.state_and_queue.load(Ordering::Acquire);
+ }
+ }
+ }
+ }
+}
+
+fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
+ // Note: the following code was carefully written to avoid creating a
+ // mutable reference to `node` that gets aliased.
+ loop {
+ // Don't queue this thread if the status is no longer running,
+ // otherwise we will not be woken up.
+ if current_state.addr() & STATE_MASK != RUNNING {
+ return;
+ }
+
+ // Create the node for our current thread.
+ let node = Waiter {
+ thread: Cell::new(Some(thread::current())),
+ signaled: AtomicBool::new(false),
+ next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter,
+ };
+ let me = &node as *const Waiter as *const Masked as *mut Masked;
+
+ // Try to slide in the node at the head of the linked list, making sure
+ // that another thread didn't just replace the head of the linked list.
+ let exchange_result = state_and_queue.compare_exchange(
+ current_state,
+ me.with_addr(me.addr() | RUNNING),
+ Ordering::Release,
+ Ordering::Relaxed,
+ );
+ if let Err(old) = exchange_result {
+ current_state = old;
+ continue;
+ }
+
+ // We have enqueued ourselves, now lets wait.
+ // It is important not to return before being signaled, otherwise we
+ // would drop our `Waiter` node and leave a hole in the linked list
+ // (and a dangling reference). Guard against spurious wakeups by
+ // reparking ourselves until we are signaled.
+ while !node.signaled.load(Ordering::Acquire) {
+ // If the managing thread happens to signal and unpark us before we
+ // can park ourselves, the result could be this thread never gets
+ // unparked. Luckily `park` comes with the guarantee that if it got
+ // an `unpark` just before on an unparked thread it does not park.
+ thread::park();
+ }
+ break;
+ }
+}
+
+#[stable(feature = "std_debug", since = "1.16.0")]
+impl fmt::Debug for Once {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Once").finish_non_exhaustive()
+ }
+}
+
+impl Drop for WaiterQueue<'_> {
+ fn drop(&mut self) {
+ // Swap out our state with however we finished.
+ let state_and_queue =
+ self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel);
+
+ // We should only ever see an old state which was RUNNING.
+ assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING);
+
+ // Walk the entire linked list of waiters and wake them up (in lifo
+ // order, last to register is first to wake up).
+ unsafe {
+ // Right after setting `node.signaled = true` the other thread may
+ // free `node` if there happens to be has a spurious wakeup.
+ // So we have to take out the `thread` field and copy the pointer to
+ // `next` first.
+ let mut queue =
+ state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter;
+ while !queue.is_null() {
+ let next = (*queue).next;
+ let thread = (*queue).thread.take().unwrap();
+ (*queue).signaled.store(true, Ordering::Release);
+ // ^- FIXME (maybe): This is another case of issue #55005
+ // `store()` has a potentially dangling ref to `signaled`.
+ queue = next;
+ thread.unpark();
+ }
+ }
+ }
+}
+
+impl OnceState {
+ #[inline]
+ pub fn is_poisoned(&self) -> bool {
+ self.poisoned
+ }
+
+ #[inline]
+ pub fn poison(&self) {
+ self.set_state_on_drop_to.set(ptr::invalid_mut(POISONED));
+ }
+}
use crate::collections::BTreeMap;
use crate::env;
use crate::ffi::{OsStr, OsString};
-use crate::sys::process::EnvKey;
+use crate::io;
+use crate::sys::pipe::read2;
+use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
// Stores a set of changes to an environment
#[derive(Clone, Debug)]
self.iter.is_empty()
}
}
+
+pub fn wait_with_output(
+ mut process: Process,
+ mut pipes: StdioPipes,
+) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ drop(pipes.stdin.take());
+
+ let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
+ match (pipes.stdout.take(), pipes.stderr.take()) {
+ (None, None) => {}
+ (Some(out), None) => {
+ let res = out.read_to_end(&mut stdout);
+ res.unwrap();
+ }
+ (None, Some(err)) => {
+ let res = err.read_to_end(&mut stderr);
+ res.unwrap();
+ }
+ (Some(out), Some(err)) => {
+ let res = read2(out, &mut stdout, err, &mut stderr);
+ res.unwrap();
+ }
+ }
+
+ let status = process.wait()?;
+ Ok((status, stdout, stderr))
+}
Err(e) => {
return Err(format!(
"argument for --shuffle-seed must be a number \
- (error: {})",
- e
+ (error: {e})"
));
}
},
Err(e) => {
return Err(format!(
"argument for --test-threads must be a number > 0 \
- (error: {})",
- e
+ (error: {e})"
));
}
},
Some(v) => {
return Err(format!(
"argument for --format must be pretty, terse, json or junit (was \
- {})",
- v
+ {v})"
));
}
};
Some(v) => {
return Err(format!(
"argument for --color must be auto, always, or never (was \
- {})",
- v
+ {v})"
));
}
};
self.write_message(&*format!(r#", "stdout": "{}""#, EscapedString(stdout)))?;
}
if let Some(extra) = extra {
- self.write_message(&*format!(r#", {}"#, extra))?;
+ self.write_message(&*format!(r#", {extra}"#))?;
}
self.writeln_message(" }")
}
impl<T: Write> OutputFormatter for JsonFormatter<T> {
fn write_run_start(&mut self, test_count: usize, shuffle_seed: Option<u64>) -> io::Result<()> {
let shuffle_seed_json = if let Some(shuffle_seed) = shuffle_seed {
- format!(r#", "shuffle_seed": {}"#, shuffle_seed)
+ format!(r#", "shuffle_seed": {shuffle_seed}"#)
} else {
String::new()
};
self.writeln_message(&*format!(
- r#"{{ "type": "suite", "event": "started", "test_count": {}{} }}"#,
- test_count, shuffle_seed_json
+ r#"{{ "type": "suite", "event": "started", "test_count": {test_count}{shuffle_seed_json} }}"#
))
}
Some(_) => test_output.push(b'\n'),
None => (),
}
- writeln!(test_output, "---- {} stderr ----", test_name).unwrap();
+ writeln!(test_output, "---- {test_name} stderr ----").unwrap();
}
pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> {
if let Some(message) = message {
- self.write_short_result(&format!("ignored, {}", message), term::color::YELLOW)
+ self.write_short_result(&format!("ignored, {message}"), term::color::YELLOW)
} else {
self.write_short_result("ignored", term::color::YELLOW)
}
} else {
Err(format!(
"the test returned a termination value with a non-zero status code \
- ({}) which indicates a failure",
- code
+ ({code}) which indicates a failure"
))
}
}
})() {
Ok(r) => r,
Err(e) => {
- write!(&mut test_output, "Unexpected error: {}", e).unwrap();
+ write!(&mut test_output, "Unexpected error: {e}").unwrap();
TrFailed
}
};
} else if let Some(panic_str) = maybe_panic_str {
TestResult::TrFailedMsg(format!(
r#"panic did not contain expected string
- panic message: `{:?}`,
- expected substring: `{:?}`"#,
- panic_str, msg
+ panic message: `{panic_str:?}`,
+ expected substring: `{msg:?}`"#
))
} else {
TestResult::TrFailedMsg(format!(
let durations_str = env::var(env_var_name).ok()?;
let (warn_str, critical_str) = durations_str.split_once(',').unwrap_or_else(|| {
panic!(
- "Duration variable {} expected to have 2 numbers separated by comma, but got {}",
- env_var_name, durations_str
+ "Duration variable {env_var_name} expected to have 2 numbers separated by comma, but got {durations_str}"
)
});
let parse_u64 = |v| {
u64::from_str(v).unwrap_or_else(|_| {
panic!(
- "Duration value in variable {} is expected to be a number, but got {}",
- env_var_name, v
+ "Duration value in variable {env_var_name} is expected to be a number, but got {v}"
)
})
};
path = "bin/sccache-plus-cl.rs"
test = false
-[[bin]]
-name = "llvm-config-wrapper"
-path = "bin/llvm-config-wrapper.rs"
-test = false
-
[dependencies]
cmake = "0.1.38"
fd-lock = "3.0.8"
+++ /dev/null
-// The sheer existence of this file is an awful hack. See the comments in
-// `src/bootstrap/native.rs` for why this is needed when compiling LLD.
-
-use std::env;
-use std::io::{self, Write};
-use std::process::{self, Command, Stdio};
-
-fn main() {
- let real_llvm_config = env::var_os("LLVM_CONFIG_REAL").unwrap();
- let mut cmd = Command::new(real_llvm_config);
- cmd.args(env::args().skip(1)).stderr(Stdio::piped());
- let output = cmd.output().expect("failed to spawn llvm-config");
- let mut stdout = String::from_utf8_lossy(&output.stdout);
-
- if let Ok(to_replace) = env::var("LLVM_CONFIG_SHIM_REPLACE") {
- if let Ok(replace_with) = env::var("LLVM_CONFIG_SHIM_REPLACE_WITH") {
- stdout = stdout.replace(&to_replace, &replace_with).into();
- }
- }
-
- print!("{}", stdout.replace("\\", "/"));
- io::stdout().flush().unwrap();
- process::exit(output.status.code().unwrap_or(1));
-}
use crate::install;
use crate::native;
use crate::run;
+use crate::setup;
use crate::test;
use crate::tool::{self, SourceType};
use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
// single alias, which does not correspond to any on-disk path
pub fn alias(mut self, alias: &str) -> Self {
+ // exceptional case for `Kind::Setup` because its `library`
+ // and `compiler` options would otherwise naively match with
+ // `compiler` and `library` folders respectively.
assert!(
- !self.builder.src.join(alias).exists(),
+ self.kind == Kind::Setup || !self.builder.src.join(alias).exists(),
"use `builder.path()` for real paths: {}",
alias
);
doc::RustdocBook,
doc::RustByExample,
doc::RustcBook,
+ doc::Cargo,
doc::CargoBook,
doc::Clippy,
doc::ClippyBook,
run::CollectLicenseMetadata,
run::GenerateCopyright,
),
+ Kind::Setup => describe!(setup::Profile),
// These commands either don't use paths, or they're special-cased in Build::build()
- Kind::Clean | Kind::Format | Kind::Setup => vec![],
+ Kind::Clean | Kind::Format => vec![],
}
}
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
Subcommand::Run { ref paths, .. } => (Kind::Run, &paths[..]),
Subcommand::Format { .. } => (Kind::Format, &[][..]),
- Subcommand::Clean { .. } | Subcommand::Setup { .. } => {
+ Subcommand::Setup { profile: ref path } => (
+ Kind::Setup,
+ path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
+ ),
+ Subcommand::Clean { .. } => {
panic!()
}
};
/// check build or dry-run, where there's no need to build all of LLVM.
fn llvm_config(&self, target: TargetSelection) -> Option<PathBuf> {
if self.config.llvm_enabled() && self.kind != Kind::Check && !self.config.dry_run() {
- let llvm_config = self.ensure(native::Llvm { target });
+ let native::LlvmResult { llvm_config, .. } = self.ensure(native::Llvm { target });
if llvm_config.is_file() {
return Some(llvm_config);
}
};
if let Some(limit) = limit {
- rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={}", limit));
+ if stage == 0 || self.config.default_codegen_backend().unwrap_or_default() == "llvm"
+ {
+ rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={}", limit));
+ }
}
}
""
};
+ let mut features = String::new();
+
+ // Cranelift doesn't support `asm`.
+ if stage != 0 && builder.config.default_codegen_backend().unwrap_or_default() == "cranelift" {
+ features += " compiler-builtins-no-asm";
+ }
+
if builder.no_std(target) == Some(true) {
- let mut features = "compiler-builtins-mem".to_string();
+ features += " compiler-builtins-mem";
if !target.starts_with("bpf") {
features.push_str(compiler_builtins_c_feature);
}
.arg("--features")
.arg(features);
} else {
- let mut features = builder.std_features(target);
+ features += &builder.std_features(target);
features.push_str(compiler_builtins_c_feature);
cargo
.env("CFG_RELEASE_CHANNEL", &builder.config.channel)
.env("CFG_VERSION", builder.rust_version());
- if let Some(backend) = builder.config.rust_codegen_backends.get(0) {
+ if let Some(backend) = builder.config.default_codegen_backend() {
cargo.env("CFG_DEFAULT_CODEGEN_BACKEND", backend);
}
if builder.is_rust_llvm(target) {
cargo.env("LLVM_RUSTLLVM", "1");
}
- let llvm_config = builder.ensure(native::Llvm { target });
+ let native::LlvmResult { llvm_config, .. } = builder.ensure(native::Llvm { target });
cargo.env("LLVM_CONFIG", &llvm_config);
if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) {
cargo.env("CFG_LLVM_ROOT", s);
}
if builder.config.rust_codegen_backends.contains(&INTERNER.intern_str("llvm")) {
- let llvm_config_bin = builder.ensure(native::Llvm { target: target_compiler.host });
+ let native::LlvmResult { llvm_config, .. } =
+ builder.ensure(native::Llvm { target: target_compiler.host });
if !builder.config.dry_run() {
- let llvm_bin_dir = output(Command::new(llvm_config_bin).arg("--bindir"));
+ let llvm_bin_dir = output(Command::new(llvm_config).arg("--bindir"));
let llvm_bin_dir = Path::new(llvm_bin_dir.trim());
// Since we've already built the LLVM tools, install them to the sysroot.
dist_stage: Option<u32> = "dist-stage",
bench_stage: Option<u32> = "bench-stage",
patch_binaries_for_nix: Option<bool> = "patch-binaries-for-nix",
+ // NOTE: only parsed by bootstrap.py, `--feature build-metrics` enables metrics unconditionally
metrics: Option<bool> = "metrics",
}
}
self.submodules.unwrap_or(rust_info.is_managed_git_subrepository())
}
+ pub fn default_codegen_backend(&self) -> Option<Interned<String>> {
+ self.rust_codegen_backends.get(0).cloned()
+ }
+
/// Returns the commit to download, or `None` if we shouldn't download CI artifacts.
fn download_ci_rustc_commit(&self, download_rustc: Option<StringOrBool>) -> Option<String> {
// If `download-rustc` is not set, default to rebuilding.
use crate::compile;
use crate::config::TargetSelection;
use crate::doc::DocumentationFormat;
+use crate::native;
use crate::tarball::{GeneratedTarball, OverlayKind, Tarball};
use crate::tool::{self, Tool};
use crate::util::{exe, is_dylib, output, t, timeit};
builder.install(&llvm_dylib_path, dst_libdir, 0o644);
}
!builder.config.dry_run()
- } else if let Ok(llvm_config) = crate::native::prebuilt_llvm_config(builder, target) {
+ } else if let Ok(native::LlvmResult { llvm_config, .. }) =
+ native::prebuilt_llvm_config(builder, target)
+ {
let mut cmd = Command::new(llvm_config);
cmd.arg("--libfiles");
builder.verbose(&format!("running {:?}", cmd));
let tarball = Tarball::new(builder, "bootstrap", &target.triple);
let bootstrap_outdir = &builder.bootstrap_out;
- for file in &["bootstrap", "llvm-config-wrapper", "rustc", "rustdoc", "sccache-plus-cl"] {
+ for file in &["bootstrap", "rustc", "rustdoc", "sccache-plus-cl"] {
tarball.add_file(bootstrap_outdir.join(exe(file, target)), "bootstrap/bin", 0o755);
}
}
macro_rules! tool_doc {
- ($tool: ident, $should_run: literal, $path: literal, [$($krate: literal),+ $(,)?] $(,)?) => {
+ ($tool: ident, $should_run: literal, $path: literal, $(rustc_tool = $rustc_tool:literal, )? $(in_tree = $in_tree:literal, )? [$($krate: literal),+ $(,)?] $(,)?) => {
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct $tool {
target: TargetSelection,
let out = builder.compiler_doc_out(target);
t!(fs::create_dir_all(&out));
- // Build rustc docs so that we generate relative links.
- builder.ensure(Rustc { stage, target });
- // Rustdoc needs the rustc sysroot available to build.
- // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed
- // with strange errors, but only on a full bors test ...
let compiler = builder.compiler(stage, builder.config.build);
- builder.ensure(compile::Rustc::new(compiler, target));
+ builder.ensure(compile::Std::new(compiler, target));
+
+ if true $(&& $rustc_tool)? {
+ // Build rustc docs so that we generate relative links.
+ builder.ensure(Rustc { stage, target });
+
+ // Rustdoc needs the rustc sysroot available to build.
+ // FIXME: is there a way to only ensure `check::Rustc` here? Last time I tried it failed
+ // with strange errors, but only on a full bors test ...
+ builder.ensure(compile::Rustc::new(compiler, target));
+ }
+
+ let source_type = if true $(&& $in_tree)? {
+ SourceType::InTree
+ } else {
+ SourceType::Submodule
+ };
builder.info(
&format!(
);
// Symlink compiler docs to the output directory of rustdoc documentation.
- let out_dir = builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc");
- t!(fs::create_dir_all(&out_dir));
- t!(symlink_dir_force(&builder.config, &out, &out_dir));
+ let out_dirs = [
+ builder.stage_out(compiler, Mode::ToolRustc).join(target.triple).join("doc"),
+ // Cargo uses a different directory for proc macros.
+ builder.stage_out(compiler, Mode::ToolRustc).join("doc"),
+ ];
+ for out_dir in out_dirs {
+ t!(fs::create_dir_all(&out_dir));
+ t!(symlink_dir_force(&builder.config, &out, &out_dir));
+ }
// Build cargo command.
let mut cargo = prepare_tool_cargo(
target,
"doc",
$path,
- SourceType::InTree,
+ source_type,
&[],
);
);
tool_doc!(Clippy, "clippy", "src/tools/clippy", ["clippy_utils"]);
tool_doc!(Miri, "miri", "src/tools/miri", ["miri"]);
+tool_doc!(
+ Cargo,
+ "cargo",
+ "src/tools/cargo",
+ rustc_tool = false,
+ in_tree = false,
+ [
+ "cargo",
+ "cargo-platform",
+ "cargo-util",
+ "crates-io",
+ "cargo-test-macro",
+ "cargo-test-support",
+ "cargo-credential",
+ "cargo-credential-1password",
+ "mdman",
+ // FIXME: this trips a license check in tidy.
+ // "resolver-tests",
+ // FIXME: we should probably document these, but they're different per-platform so we can't use `tool_doc`.
+ // "cargo-credential-gnome-secret",
+ // "cargo-credential-macos-keychain",
+ // "cargo-credential-wincred",
+ ]
+);
#[derive(Ord, PartialOrd, Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct ErrorIndex {
args: Vec<String>,
},
Setup {
- profile: Option<Profile>,
+ profile: Option<PathBuf>,
},
}
// fn usage()
let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
- let config = Config::parse(&["build".to_string()]);
+ let config = Config::parse(&["setup".to_string()]);
let build = Build::new(config);
let paths = Builder::get_help(&build, subcommand);
}
Kind::Setup => {
let profile = if paths.len() > 1 {
- println!("\nat most one profile can be passed to setup\n");
+ eprintln!("\nerror: At most one profile can be passed to setup\n");
usage(1, &opts, verbose, &subcommand_help)
} else if let Some(path) = paths.pop() {
let profile_string = t!(path.into_os_string().into_string().map_err(
use std::process::{Command, Stdio};
use std::sync::mpsc::SyncSender;
-fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut() {
+fn rustfmt(src: &Path, rustfmt: &Path, paths: &[PathBuf], check: bool) -> impl FnMut(bool) -> bool {
let mut cmd = Command::new(&rustfmt);
// avoid the submodule config paths from coming into play,
// we only allow a single global config for the workspace for now
let cmd_debug = format!("{:?}", cmd);
let mut cmd = cmd.spawn().expect("running rustfmt");
// poor man's async: return a closure that'll wait for rustfmt's completion
- move || {
+ move |block: bool| -> bool {
+ if !block {
+ match cmd.try_wait() {
+ Ok(Some(_)) => {}
+ _ => return false,
+ }
+ }
let status = cmd.wait().unwrap();
if !status.success() {
eprintln!(
);
crate::detail_exit(1);
}
+ true
}
}
let child = rustfmt(&src, &rustfmt_path, paths.as_slice(), check);
children.push_back(child);
+ // poll completion before waiting
+ for i in (0..children.len()).rev() {
+ if children[i](false) {
+ children.swap_remove_back(i);
+ break;
+ }
+ }
+
if children.len() >= max_processes {
// await oldest child
- children.pop_front().unwrap()();
+ children.pop_front().unwrap()(true);
}
}
// await remaining children
for mut child in children {
- child();
+ child(true);
}
});
install_sh(builder, "clippy", self.compiler.stage, Some(self.target), &tarball);
};
Miri, alias = "miri", Self::should_build(_config), only_hosts: true, {
- let tarball = builder
- .ensure(dist::Miri { compiler: self.compiler, target: self.target })
- .expect("missing miri");
- install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
+ if let Some(tarball) = builder.ensure(dist::Miri { compiler: self.compiler, target: self.target }) {
+ install_sh(builder, "miri", self.compiler.stage, Some(self.target), &tarball);
+ } else {
+ // Miri is only available on nightly
+ builder.info(
+ &format!("skipping Install miri stage{} ({})", self.compiler.stage, self.target),
+ );
+ }
};
LlvmTools, alias = "llvm-tools", Self::should_build(_config), only_hosts: true, {
let tarball = builder
use std::env;
use std::fs::{self, File};
use std::io;
+use std::io::ErrorKind;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str;
use crate::builder::Kind;
use crate::config::{LlvmLibunwind, TargetSelection};
-use crate::util::{exe, libdir, mtime, output, run, run_suppressed, try_run_suppressed, CiEnv};
+use crate::util::{
+ exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed, CiEnv,
+};
mod bolt;
mod builder;
(None, "bootstrap", None),
(Some(Mode::Rustc), "parallel_compiler", None),
(Some(Mode::ToolRustc), "parallel_compiler", None),
+ (Some(Mode::ToolRustc), "emulate_second_only_system", None),
(Some(Mode::Codegen), "parallel_compiler", None),
(Some(Mode::Std), "stdarch_intel_sde", None),
(Some(Mode::Std), "no_fp_fmt_parse", None),
metadata::build(&mut build);
}
+ // Make a symbolic link so we can use a consistent directory in the documentation.
+ let build_triple = build.out.join(&build.build.triple);
+ let host = build.out.join("host");
+ if let Err(e) = symlink_dir(&build.config, &build_triple, &host) {
+ if e.kind() != ErrorKind::AlreadyExists {
+ panic!(
+ "symlink_dir({} => {}) failed with {}",
+ host.display(),
+ build_triple.display(),
+ e
+ );
+ }
+ }
+
build
}
return clean::clean(self, all);
}
- if let Subcommand::Setup { profile } = &self.config.cmd {
- return setup::setup(&self.config, *profile);
- }
-
// Download rustfmt early so that it can be used in rust-analyzer configs.
let _ = &builder::Builder::new(&self).initial_rustfmt();
/// Gets the space-separated set of activated features for the standard
/// library.
fn std_features(&self, target: TargetSelection) -> String {
- let mut features = "panic-unwind".to_string();
+ let mut features = " panic-unwind".to_string();
match self.config.llvm_libunwind(target) {
LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"),
prepare:
$(Q)$(BOOTSTRAP) build --stage 2 nonexistent/path/to/trigger/cargo/metadata
-check-stage2-T-arm-linux-androideabi-H-x86_64-unknown-linux-gnu:
- $(Q)$(BOOTSTRAP) test --stage 2 --target arm-linux-androideabi
-check-stage2-T-x86_64-unknown-linux-musl-H-x86_64-unknown-linux-gnu:
- $(Q)$(BOOTSTRAP) test --stage 2 --target x86_64-unknown-linux-musl
-
TESTS_IN_2 := \
src/test/ui \
src/tools/linkchecker
use crate::util::{self, exe, output, t, up_to_date};
use crate::{CLang, GitRepo};
+#[derive(Clone)]
+pub struct LlvmResult {
+ /// Path to llvm-config binary.
+ /// NB: This is always the host llvm-config!
+ pub llvm_config: PathBuf,
+ /// Path to LLVM cmake directory for the target.
+ pub llvm_cmake_dir: PathBuf,
+}
+
pub struct Meta {
stamp: HashStamp,
- build_llvm_config: PathBuf,
+ res: LlvmResult,
out_dir: PathBuf,
root: String,
}
pub fn prebuilt_llvm_config(
builder: &Builder<'_>,
target: TargetSelection,
-) -> Result<PathBuf, Meta> {
+) -> Result<LlvmResult, Meta> {
builder.config.maybe_download_ci_llvm();
// If we're using a custom LLVM bail out here, but we can only use a
if let Some(config) = builder.config.target_config.get(&target) {
if let Some(ref s) = config.llvm_config {
check_llvm_version(builder, s);
- return Ok(s.to_path_buf());
+ let llvm_config = s.to_path_buf();
+ let mut llvm_cmake_dir = llvm_config.clone();
+ llvm_cmake_dir.pop();
+ llvm_cmake_dir.pop();
+ llvm_cmake_dir.push("lib");
+ llvm_cmake_dir.push("cmake");
+ llvm_cmake_dir.push("llvm");
+ return Ok(LlvmResult { llvm_config, llvm_cmake_dir });
}
}
llvm_config_ret_dir.push("build");
}
llvm_config_ret_dir.push("bin");
-
let build_llvm_config = llvm_config_ret_dir.join(exe("llvm-config", builder.config.build));
+ let llvm_cmake_dir = out_dir.join("lib/cmake/llvm");
+ let res = LlvmResult { llvm_config: build_llvm_config, llvm_cmake_dir };
let stamp = out_dir.join("llvm-finished-building");
let stamp = HashStamp::new(stamp, builder.in_tree_llvm_info.sha());
Using a potentially stale build of LLVM; \
This may not behave well.",
);
- return Ok(build_llvm_config);
+ return Ok(res);
}
if stamp.is_done() {
stamp.path.display()
));
}
- return Ok(build_llvm_config);
+ return Ok(res);
}
- Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
+ Err(Meta { stamp, res, out_dir, root: root.into() })
}
/// This retrieves the LLVM sha we *want* to use, according to git history.
}
impl Step for Llvm {
- type Output = PathBuf; // path to llvm-config
+ type Output = LlvmResult;
const ONLY_HOSTS: bool = true;
}
/// Compile LLVM for `target`.
- fn run(self, builder: &Builder<'_>) -> PathBuf {
+ fn run(self, builder: &Builder<'_>) -> LlvmResult {
let target = self.target;
let target_native = if self.target.starts_with("riscv") {
// RISC-V target triples in Rust is not named the same as C compiler target triples.
target.to_string()
};
- let Meta { stamp, build_llvm_config, out_dir, root } =
- match prebuilt_llvm_config(builder, target) {
- Ok(p) => return p,
- Err(m) => m,
- };
+ let Meta { stamp, res, out_dir, root } = match prebuilt_llvm_config(builder, target) {
+ Ok(p) => return p,
+ Err(m) => m,
+ };
builder.update_submodule(&Path::new("src").join("llvm-project"));
if builder.llvm_link_shared() && target.contains("windows") {
// https://llvm.org/docs/HowToCrossCompileLLVM.html
if target != builder.config.build {
- let llvm_config = builder.ensure(Llvm { target: builder.config.build });
+ let LlvmResult { llvm_config, .. } =
+ builder.ensure(Llvm { target: builder.config.build });
if !builder.config.dry_run() {
let llvm_bindir = output(Command::new(&llvm_config).arg("--bindir"));
let host_bin = Path::new(llvm_bindir.trim());
// tools and libs on all platforms.
if builder.config.dry_run() {
- return build_llvm_config;
+ return res;
}
cfg.build();
// for a versioned path like libLLVM-14.dylib. Manually create a symbolic
// link to make llvm-config happy.
if builder.llvm_link_shared() && target.contains("apple-darwin") {
- let mut cmd = Command::new(&build_llvm_config);
+ let mut cmd = Command::new(&res.llvm_config);
let version = output(cmd.arg("--version"));
let major = version.split('.').next().unwrap();
let lib_name = match llvm_version_suffix {
// LLVM after a configuration change, so to rebuild it the build files have to be removed,
// which will also remove these modified files.
if builder.config.llvm_bolt_profile_generate {
- instrument_with_bolt_inplace(&get_built_llvm_lib_path(&build_llvm_config));
+ instrument_with_bolt_inplace(&get_built_llvm_lib_path(&res.llvm_config));
}
if let Some(path) = &builder.config.llvm_bolt_profile_use {
optimize_library_with_bolt_inplace(
- &get_built_llvm_lib_path(&build_llvm_config),
+ &get_built_llvm_lib_path(&res.llvm_config),
&Path::new(path),
);
}
t!(stamp.write());
- build_llvm_config
+ res
}
}
if target.starts_with("aarch64") {
// macOS uses a different name for building arm64
cfg.define("CMAKE_OSX_ARCHITECTURES", "arm64");
+ } else if target.starts_with("i686") {
+ // macOS uses a different name for building i386
+ cfg.define("CMAKE_OSX_ARCHITECTURES", "i386");
} else {
cfg.define("CMAKE_OSX_ARCHITECTURES", target.triple.split('-').next().unwrap());
}
}
let target = self.target;
- let llvm_config = builder.ensure(Llvm { target: self.target });
+ let LlvmResult { llvm_config, llvm_cmake_dir } =
+ builder.ensure(Llvm { target: self.target });
let out_dir = builder.lld_out(target);
let done_stamp = out_dir.join("lld-finished-building");
configure_cmake(builder, target, &mut cfg, true, ldflags);
configure_llvm(builder, target, &mut cfg);
- // This is an awful, awful hack. Discovered when we migrated to using
- // clang-cl to compile LLVM/LLD it turns out that LLD, when built out of
- // tree, will execute `llvm-config --cmakedir` and then tell CMake about
- // that directory for later processing. Unfortunately if this path has
- // forward slashes in it (which it basically always does on Windows)
- // then CMake will hit a syntax error later on as... something isn't
- // escaped it seems?
- //
- // Instead of attempting to fix this problem in upstream CMake and/or
- // LLVM/LLD we just hack around it here. This thin wrapper will take the
- // output from llvm-config and replace all instances of `\` with `/` to
- // ensure we don't hit the same bugs with escaping. It means that you
- // can't build on a system where your paths require `\` on Windows, but
- // there's probably a lot of reasons you can't do that other than this.
- let llvm_config_shim = env::current_exe().unwrap().with_file_name("llvm-config-wrapper");
-
// Re-use the same flags as llvm to control the level of debug information
// generated for lld.
let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
cfg.out_dir(&out_dir)
.profile(profile)
- .env("LLVM_CONFIG_REAL", &llvm_config)
- .define("LLVM_CONFIG_PATH", llvm_config_shim)
+ .define("LLVM_CMAKE_DIR", llvm_cmake_dir)
.define("LLVM_INCLUDE_TESTS", "OFF");
- // While we're using this horrible workaround to shim the execution of
- // llvm-config, let's just pile on more. I can't seem to figure out how
- // to build LLD as a standalone project and also cross-compile it at the
- // same time. It wants a natively executable `llvm-config` to learn
- // about LLVM, but then it learns about all the host configuration of
- // LLVM and tries to link to host LLVM libraries.
- //
- // To work around that we tell our shim to replace anything with the
- // build target with the actual target instead. This'll break parts of
- // LLD though which try to execute host tools, such as llvm-tblgen, so
- // we specifically tell it where to find those. This is likely super
- // brittle and will break over time. If anyone knows better how to
- // cross-compile LLD it would be much appreciated to fix this!
if target != builder.config.build {
- cfg.env("LLVM_CONFIG_SHIM_REPLACE", &builder.config.build.triple)
- .env("LLVM_CONFIG_SHIM_REPLACE_WITH", &target.triple)
- .define(
- "LLVM_TABLEGEN_EXE",
- llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
- );
+ // Use the host llvm-tblgen binary.
+ cfg.define(
+ "LLVM_TABLEGEN_EXE",
+ llvm_config.with_file_name("llvm-tblgen").with_extension(EXE_EXTENSION),
+ );
}
- // Explicitly set C++ standard, because upstream doesn't do so
- // for standalone builds.
- cfg.define("CMAKE_CXX_STANDARD", "14");
-
cfg.build();
t!(File::create(&done_stamp));
return runtimes;
}
- let llvm_config = builder.ensure(Llvm { target: builder.config.build });
+ let LlvmResult { llvm_config, .. } = builder.ensure(Llvm { target: builder.config.build });
if builder.config.dry_run() {
return runtimes;
}
+use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::Config;
use crate::{t, VERSION};
use std::env::consts::EXE_SUFFIX;
use std::str::FromStr;
use std::{fmt, fs, io};
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub enum Profile {
Compiler,
Codegen,
}
out
}
+
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Profile::Compiler => "compiler",
+ Profile::Codegen => "codegen",
+ Profile::Library => "library",
+ Profile::Tools => "tools",
+ Profile::User => "user",
+ }
+ }
}
impl FromStr for Profile {
impl fmt::Display for Profile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Profile::Compiler => write!(f, "compiler"),
- Profile::Codegen => write!(f, "codegen"),
- Profile::Library => write!(f, "library"),
- Profile::User => write!(f, "user"),
- Profile::Tools => write!(f, "tools"),
+ f.write_str(self.as_str())
+ }
+}
+
+impl Step for Profile {
+ type Output = ();
+ const DEFAULT: bool = true;
+
+ fn should_run(mut run: ShouldRun<'_>) -> ShouldRun<'_> {
+ for choice in Profile::all() {
+ run = run.alias(choice.as_str());
}
+ run
+ }
+
+ fn make_run(run: RunConfig<'_>) {
+ if run.builder.config.dry_run() {
+ return;
+ }
+
+ // for Profile, `run.paths` will have 1 and only 1 element
+ // this is because we only accept at most 1 path from user input.
+ // If user calls `x.py setup` without arguments, the interactive TUI
+ // will guide user to provide one.
+ let profile = if run.paths.len() > 1 {
+ // HACK: `builder` runs this step with all paths if no path was passed.
+ t!(interactive_path())
+ } else {
+ run.paths
+ .first()
+ .unwrap()
+ .assert_single_path()
+ .path
+ .as_path()
+ .as_os_str()
+ .to_str()
+ .unwrap()
+ .parse()
+ .unwrap()
+ };
+
+ run.builder.ensure(profile);
+ }
+
+ fn run(self, builder: &Builder<'_>) {
+ setup(&builder.build.config, self)
}
}
-pub fn setup(config: &Config, profile: Option<Profile>) {
- let profile = profile.unwrap_or_else(|| t!(interactive_path()));
+pub fn setup(config: &Config, profile: Profile) {
let stage_path =
["build", config.build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string());
if !rustup_installed() && profile != Profile::User {
eprintln!("`rustup` is not installed; cannot link `stage1` toolchain");
- } else if stage_dir_exists(&stage_path[..]) {
+ } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() {
attempt_toolchain_link(&stage_path[..]);
}
Profile::User => &["dist", "build"],
};
- t!(install_git_hook_maybe(&config));
+ if !config.dry_run() {
+ t!(install_git_hook_maybe(&config));
+ }
println!();
changelog-seen = {}\n",
profile, VERSION
);
+
t!(fs::write(path, settings));
let include_path = profile.include_path(&config.src);
let mut llvm_components_passed = false;
let mut copts_passed = false;
if builder.config.llvm_enabled() {
- let llvm_config = builder.ensure(native::Llvm { target: builder.config.build });
+ let native::LlvmResult { llvm_config, .. } =
+ builder.ensure(native::Llvm { target: builder.config.build });
if !builder.config.dry_run() {
let llvm_version = output(Command::new(&llvm_config).arg("--version"));
let llvm_components = output(Command::new(&llvm_config).arg("--components"));
--- /dev/null
+FROM ubuntu:18.04
+# FIXME: when bumping the version, remove the Python 3.6-specific changes in
+# the reuse-requirements.in file, regenerate reuse-requirements.txt and remove
+# this comment.
+
+RUN apt-get update && apt-get install -y --no-install-recommends \
+ g++ \
+ make \
+ ninja-build \
+ file \
+ curl \
+ ca-certificates \
+ python3 \
+ python3-pip \
+ python3-pkg-resources \
+ git \
+ cmake \
+ sudo \
+ gdb \
+ xz-utils \
+ libssl-dev \
+ pkg-config \
+ mingw-w64 \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY scripts/sccache.sh /scripts/
+RUN sh /scripts/sccache.sh
+
+COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/
+RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt
+
+COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
+COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
+
+ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
+ENV SCRIPT python3 ../x.py test --stage 0 src/tools/tidy
python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu --all-targets && \
python3 ../x.py build --stage 0 src/tools/build-manifest && \
python3 ../x.py test --stage 0 src/tools/compiletest && \
- python3 ../x.py test --stage 2 src/tools/tidy && \
python3 ../x.py test --stage 0 core alloc std test proc_macro && \
# Build both public and internal documentation.
- RUSTDOCFLAGS="--document-private-items" python3 ../x.py doc --stage 0 library/test && \
+ RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library/test && \
/scripts/validate-toolstate.sh && \
/scripts/validate-error-codes.sh && \
reuse lint && \
-0.13.2
\ No newline at end of file
+0.13.4
\ No newline at end of file
env:
<<: [*shared-ci-variables, *public-variables]
if: github.event_name == 'pull_request'
+ continue-on-error: ${{ matrix.tidy }}
strategy:
matrix:
include:
- name: mingw-check
<<: *job-linux-xl
+ tidy: false
+
+ - name: mingw-check-tidy
+ <<: *job-linux-xl
+ tidy: true
- name: x86_64-gnu-llvm-13
<<: *job-linux-xl
+ tidy: false
- name: x86_64-gnu-tools
+ <<: *job-linux-xl
+ tidy: false
env:
CI_ONLY_WHEN_SUBMODULES_CHANGED: 1
- <<: *job-linux-xl
auto:
permissions:
- name: dist-x86_64-apple
env:
SCRIPT: ./x.py dist bootstrap --include-default-paths --host=x86_64-apple-darwin --target=x86_64-apple-darwin
- RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
+ RUST_CONFIGURE_ARGS: --enable-full-tools --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false --set rust.lto=thin
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.7
SELECT_XCODE: /Applications/Xcode_13.4.1.app
if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics"
+ HAS_METRICS=1
fi
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --enable-sccache"
# sccache server at the start of the build, but no need to worry if this fails.
SCCACHE_IDLE_TIMEOUT=10800 sccache --start-server || true
-if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
- $SRC/configure --set rust.parallel-compiler
- CARGO_INCREMENTAL=0 $PYTHON ../x.py check
- rm -f config.toml
- rm -rf build
-fi
-
$SRC/configure $RUST_CONFIGURE_ARGS
retry make prepare
do_make "$RUST_CHECK_TARGET"
fi
+if [ "$RUN_CHECK_WITH_PARALLEL_QUERIES" != "" ]; then
+ rm -f config.toml
+ $SRC/configure --set rust.parallel-compiler
+
+ # Save the build metrics before we wipe the directory
+ if [ $HAS_METRICS = 1 ]; then
+ mv build/metrics.json .
+ fi
+ rm -rf build
+ if [ $HAS_METRICS = 1 ]; then
+ mkdir build
+ mv metrics.json build
+ fi
+
+ CARGO_INCREMENTAL=0 $PYTHON ../x.py check
+fi
+
sccache --show-stats || true
-Subproject commit ae406aa5287a9e025abb72343aaceec98458c117
+Subproject commit dd37e21ccee43918ed18a71581bb2af537ffe4fc
-Subproject commit a9869b4a3c4cac3bc6099b41f088679e268400b8
+Subproject commit 995df09b65c582eb6290ab7ea5d9485983eb4c37
-Subproject commit e269950a57fa6fcda356426545fb5aa3691a7ced
+Subproject commit 8b42eb5f57d3d8ed2257a22d0e850d9db52afed3
# `branch-protection`
This option lets you enable branch authentication instructions on AArch64.
-This option is ignored for non-AArch64 architectures.
+This option is only accepted when targeting AArch64 architectures.
It takes some combination of the following values, separated by a `,`.
- `pac-ret` - Enable pointer authentication for non-leaf functions.
# https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570
unset GIT_DIR
ROOT_DIR="$(git rev-parse --show-toplevel)"
-COMMAND="$ROOT_DIR/x.py test tidy"
-if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
- COMMAND="python $COMMAND"
-elif ! command -v python &> /dev/null; then
- COMMAND="python3 $COMMAND"
-fi
-
-echo "Running pre-push script '$COMMAND'"
+echo "Running pre-push script $ROOT_DIR/x test tidy"
cd "$ROOT_DIR"
-
-$COMMAND
+./x test tidy
"openbsd" => "OpenBSD",
"redox" => "Redox",
"solaris" => "Solaris",
+ "tvos" => "tvOS",
"wasi" => "WASI",
+ "watchos" => "watchOS",
"windows" => "Windows",
_ => "",
},
&'a self,
cx: &'a Context<'tcx>,
) -> impl fmt::Display + 'a + Captures<'tcx> {
- match self {
- clean::types::Term::Type(ty) => ty.print(cx),
- _ => todo!(),
- }
+ display_fn(move |f| match self {
+ clean::types::Term::Type(ty) => fmt::Display::fmt(&ty.print(cx), f),
+ clean::types::Term::Constant(ct) => fmt::Display::fmt(&ct.print(cx.tcx()), f),
+ })
}
}
inner: I,
started: bool,
depth: u32,
+ skipped_tags: u32,
}
impl<'a, I: Iterator<Item = Event<'a>>> SummaryLine<'a, I> {
fn new(iter: I) -> Self {
- SummaryLine { inner: iter, started: false, depth: 0 }
+ SummaryLine { inner: iter, started: false, depth: 0, skipped_tags: 0 }
}
}
let is_allowed_tag = match event {
Event::Start(ref c) => {
if is_forbidden_tag(c) {
+ self.skipped_tags += 1;
return None;
}
self.depth += 1;
}
Event::End(ref c) => {
if is_forbidden_tag(c) {
+ self.skipped_tags += 1;
return None;
}
self.depth -= 1;
}
_ => true,
};
+ if !is_allowed_tag {
+ self.skipped_tags += 1;
+ }
return if !is_allowed_tag {
if is_start {
Some(Event::Start(Tag::Paragraph))
}
impl MarkdownSummaryLine<'_> {
- pub(crate) fn into_string(self) -> String {
+ pub(crate) fn into_string_with_has_more_content(self) -> (String, bool) {
let MarkdownSummaryLine(md, links) = self;
// This is actually common enough to special-case
if md.is_empty() {
- return String::new();
+ return (String::new(), false);
}
let mut replacer = |broken_link: BrokenLink<'_>| {
.map(|link| (link.href.as_str().into(), link.new_text.as_str().into()))
};
- let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer));
+ let p = Parser::new_with_broken_link_callback(md, summary_opts(), Some(&mut replacer))
+ .peekable();
+ let mut summary = SummaryLine::new(p);
let mut s = String::new();
- let without_paragraphs = LinkReplacer::new(SummaryLine::new(p), links).filter(|event| {
+ let without_paragraphs = LinkReplacer::new(&mut summary, links).filter(|event| {
!matches!(event, Event::Start(Tag::Paragraph) | Event::End(Tag::Paragraph))
});
html::push_html(&mut s, without_paragraphs);
- s
+ let has_more_content =
+ matches!(summary.inner.peek(), Some(Event::Start(_))) || summary.skipped_tags > 0;
+
+ (s, has_more_content)
+ }
+
+ pub(crate) fn into_string(self) -> String {
+ self.into_string_with_has_more_content().0
}
}
return;
}
if let Some(s) = item.doc_value() {
- let mut summary_html = MarkdownSummaryLine(&s, &item.links(cx)).into_string();
+ let (mut summary_html, has_more_content) =
+ MarkdownSummaryLine(&s, &item.links(cx)).into_string_with_has_more_content();
- if s.contains('\n') {
+ if has_more_content {
let link = format!(r#" <a{}>Read more</a>"#, assoc_href_attr(item, link, cx));
if let Some(idx) = summary_html.rfind("</p>") {
</summary>\
<div class=\"hide-more\">Hide additional examples</div>\
<div class=\"more-scraped-examples\">\
- <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>\
- <div class=\"more-scraped-examples-inner\">"
+ <div class=\"toggle-line\"><div class=\"toggle-line-inner\"></div></div>"
);
// Only generate inline code for MAX_FULL_EXAMPLES number of examples. Otherwise we could
write!(w, "</ul></div>");
}
- write!(w, "</div></div></details>");
+ write!(w, "</div></details>");
}
write!(w, "</div>");
Ok((ret, krates))
}
- /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
+ /// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
/// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
///
/// This forms the payload of files that look like this:
margin-right: auto;
}
-.source .width-limiter {
- max-width: unset;
-}
-
details:not(.rustdoc-toggle) summary {
margin-bottom: .6em;
}
}
pre {
padding: 14px;
+ line-height: 1.5; /* https://github.com/rust-lang/rust/issues/105906 */
}
.item-decl pre {
overflow-x: auto;
overflow-y: hidden;
}
-.source .sidebar, #sidebar-toggle, #source-sidebar {
+.source .sidebar, #src-sidebar-toggle, #source-sidebar {
background-color: var(--sidebar-background-color);
}
-#sidebar-toggle > button:hover, #sidebar-toggle > button:focus {
+#src-sidebar-toggle > button:hover, #src-sidebar-toggle > button:focus {
background-color: var(--sidebar-background-color-hover);
}
-.source .sidebar > *:not(#sidebar-toggle) {
+.source .sidebar > *:not(#src-sidebar-toggle) {
visibility: hidden;
}
flex-basis: 300px;
}
-.source-sidebar-expanded .source .sidebar > *:not(#sidebar-toggle) {
+.source-sidebar-expanded .source .sidebar > *:not(#src-sidebar-toggle) {
visibility: visible;
}
.rustdoc .example-wrap > pre.example-line-numbers,
.rustdoc .example-wrap > pre.src-line-numbers {
flex-grow: 0;
+ min-width: fit-content; /* prevent collapsing into nothing in truncated scraped examples */
overflow: initial;
text-align: right;
-webkit-user-select: none;
.docblock-short {
overflow-wrap: break-word;
overflow-wrap: anywhere;
- overflow: hidden;
- text-overflow: ellipsis;
}
/* Wrap non-pre code blocks (`text`) but not (```text```). */
.docblock :not(pre) > code,
position: relative;
}
-.small-section-header:hover > .anchor {
+.small-section-header:hover > .anchor, .impl:hover > .anchor,
+.trait-impl:hover > .anchor, .variant:hover > .anchor {
display: initial;
}
-
-.impl:hover > .anchor, .trait-impl:hover > .anchor, .variant:hover > .anchor {
- display: inline-block;
- position: absolute;
-}
.anchor {
display: none;
position: absolute;
top: 5px;
}
-.example-wrap .tooltip::after {
- display: none;
+.example-wrap .tooltip:hover::after {
text-align: center;
padding: 5px 3px 3px 3px;
border-radius: 6px;
color: var(--tooltip-color);
}
-.example-wrap .tooltip::before {
+.example-wrap .tooltip:hover::before {
content: " ";
position: absolute;
top: 50%;
left: 16px;
margin-top: -5px;
- display: none;
z-index: 1;
border: 5px solid transparent;
border-right-color: var(--tooltip-background-color);
}
-.example-wrap.ignore .tooltip::after {
+.example-wrap.ignore .tooltip:hover::after {
content: "This example is not tested";
}
-.example-wrap.compile_fail .tooltip::after {
+.example-wrap.compile_fail .tooltip:hover::after {
content: "This example deliberately fails to compile";
}
-.example-wrap.should_panic .tooltip::after {
+.example-wrap.should_panic .tooltip:hover::after {
content: "This example panics";
}
-.example-wrap.edition .tooltip::after {
+.example-wrap.edition .tooltip:hover::after {
content: "This code runs with edition " attr(data-edition);
}
-.example-wrap .tooltip:hover::before, .example-wrap .tooltip:hover::after {
- display: inline;
-}
-
.example-wrap.compile_fail .tooltip,
.example-wrap.should_panic .tooltip,
.example-wrap.ignore .tooltip {
margin-right: auto;
}
-#titles {
+#search-tabs {
display: flex;
flex-direction: row;
gap: 1px;
margin-bottom: 4px;
}
-#titles > button {
+#search-tabs button {
text-align: center;
font-size: 1.125rem;
border: 0;
color: inherit;
}
-#titles > button > div.count {
- display: inline-block;
+#search-tabs .count {
font-size: 1rem;
+ color: var(--search-tab-title-count-color);
}
-#sidebar-toggle {
+#src-sidebar-toggle {
position: sticky;
top: 0;
left: 0;
#source-sidebar div.files > a.selected {
background-color: var(--source-sidebar-background-selected);
}
-#sidebar-toggle > button {
+#src-sidebar-toggle > button {
font-size: inherit;
font-weight: bold;
background: none;
display: none !important;
}
- #titles > button > div.count {
+ #search-tabs .count {
display: block;
}
left: -11px;
}
- #sidebar-toggle {
+ #src-sidebar-toggle {
position: fixed;
left: 1px;
top: 100px;
border-left: 0;
}
- .source-sidebar-expanded #sidebar-toggle {
+ .source-sidebar-expanded #src-sidebar-toggle {
left: unset;
top: unset;
width: unset;
width: 35px;
}
- #sidebar-toggle {
+ #src-sidebar-toggle {
top: 10px;
}
- .source-sidebar-expanded #sidebar-toggle {
+ .source-sidebar-expanded #src-sidebar-toggle {
top: unset;
}
}
}
.scraped-example .code-wrapper .example-wrap {
- display: grid;
- grid-template-columns: max-content auto;
width: 100%;
- overflow-x: auto;
overflow-y: hidden;
margin-bottom: 0;
}
overflow-x: hidden;
}
-.scraped-example .code-wrapper .example-wrap pre.rust {
- overflow-x: inherit;
- width: inherit;
- overflow-y: hidden;
-}
-
-
.more-examples-toggle {
max-width: calc(100% + 25px);
margin-top: 10px;
.more-examples-toggle .hide-more {
margin-left: 25px;
- margin-bottom: 5px;
cursor: pointer;
}
.more-scraped-examples {
- margin-left: 5px;
- display: flex;
- flex-direction: row;
-}
-
-.more-scraped-examples-inner {
- /* 20px is width of toggle-line + toggle-line-inner */
- width: calc(100% - 20px);
+ margin-left: 25px;
+ position: relative;
}
.toggle-line {
- align-self: stretch;
- margin-right: 10px;
- margin-top: 5px;
+ position: absolute;
+ top: 5px;
+ bottom: 0;
+ right: calc(100% + 10px);
padding: 0 4px;
cursor: pointer;
}
height: 100%;
}
-.more-scraped-examples .scraped-example {
- margin-bottom: 20px;
-}
-
-.more-scraped-examples .scraped-example:last-child {
- margin-bottom: 0;
+.more-scraped-examples .scraped-example, .example-links {
+ margin-top: 20px;
}
-.example-links a {
- margin-top: 20px;
+.more-scraped-examples .scraped-example:first-child {
+ margin-top: 5px;
}
.example-links ul {
cursor: pointer;
}
-.setting-line > .sub-settings {
- padding-left: 42px;
- width: 100%;
- display: block;
-}
-
#settings .setting-line {
margin: 1.2em 0.6em;
}
--search-color: #fff;
--search-results-alias-color: #c5c5c5;
--search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
--stab-background-color: #314559;
--stab-code-color: #e6e1cf;
--code-highlight-kw-color: #ff7733;
border-bottom: 1px solid rgba(242, 151, 24, 0.3);
}
-#titles > button > div.count {
- color: #888;
-}
-
/* rules that this theme does not need to set, here to satisfy the rule checker */
/* note that a lot of these are partially set in some way (meaning they are set
individually rather than as a group) */
.scraped-example .example-wrap .rust span.highlight.focus {
background: rgb(124, 75, 15);
}
-.scraped-example:not(.expanded) .code-wrapper:before {
+.scraped-example:not(.expanded) .code-wrapper::before {
background: linear-gradient(to bottom, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
}
-.scraped-example:not(.expanded) .code-wrapper:after {
+.scraped-example:not(.expanded) .code-wrapper::after {
background: linear-gradient(to top, rgba(15, 20, 25, 1), rgba(15, 20, 25, 0));
}
.toggle-line-inner {
--search-color: #111;
--search-results-alias-color: #fff;
--search-results-grey-color: #ccc;
+ --search-tab-title-count-color: #888;
--stab-background-color: #314559;
--stab-code-color: #e6e1cf;
--code-highlight-kw-color: #ab8ac1;
background-color: #353535;
}
-#titles > button > div.count {
- color: #888;
-}
-
.scraped-example-list .scrape-help {
border-color: #aaa;
color: #eee;
.scraped-example .example-wrap .rust span.highlight.focus {
background: rgb(124, 75, 15);
}
-.scraped-example:not(.expanded) .code-wrapper:before {
+.scraped-example:not(.expanded) .code-wrapper::before {
background: linear-gradient(to bottom, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
}
-.scraped-example:not(.expanded) .code-wrapper:after {
+.scraped-example:not(.expanded) .code-wrapper::after {
background: linear-gradient(to top, rgba(53, 53, 53, 1), rgba(53, 53, 53, 0));
}
.toggle-line-inner {
--search-color: #000;
--search-results-alias-color: #000;
--search-results-grey-color: #999;
+ --search-tab-title-count-color: #888;
--stab-background-color: #fff5d6;
--stab-code-color: #000;
--code-highlight-kw-color: #8959a8;
border-top-color: #0089ff;
}
-#titles > button > div.count {
- color: #888;
-}
-
.scraped-example-list .scrape-help {
border-color: #555;
color: #333;
.scraped-example .example-wrap .rust span.highlight.focus {
background: #f6fdb0;
}
-.scraped-example:not(.expanded) .code-wrapper:before {
+.scraped-example:not(.expanded) .code-wrapper::before {
background: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}
-.scraped-example:not(.expanded) .code-wrapper:after {
+.scraped-example:not(.expanded) .code-wrapper::after {
background: linear-gradient(to top, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
}
.toggle-line-inner {
hideSidebar();
});
- onEachLazy(document.getElementsByTagName("a"), el => {
+ onEachLazy(document.querySelectorAll("a[href^='#']"), el => {
// For clicks on internal links (<A> tags with a hash property), we expand the section we're
// jumping to *before* jumping there. We can't do this in onHashChange, because it changes
// the height of the document so we wind up scrolled to the wrong place.
- if (el.hash) {
- el.addEventListener("click", () => {
- expandSection(el.hash.slice(1));
- hideSidebar();
- });
- }
+ el.addEventListener("click", () => {
+ expandSection(el.hash.slice(1));
+ hideSidebar();
+ });
});
onEachLazy(document.querySelectorAll(".rustdoc-toggle > summary:not(.hideme)"), el => {
let iter = 0;
let foundCurrentTab = false;
let foundCurrentResultSet = false;
- onEachLazy(document.getElementById("titles").childNodes, elem => {
+ onEachLazy(document.getElementById("search-tabs").childNodes, elem => {
if (nb === iter) {
addClass(elem, "selected");
foundCurrentTab = true;
function focusSearchResult() {
const target = searchState.focusedByTab[searchState.currentTab] ||
document.querySelectorAll(".search-results.active a").item(0) ||
- document.querySelectorAll("#titles > button").item(searchState.currentTab);
+ document.querySelectorAll("#search-tabs button").item(searchState.currentTab);
searchState.focusedByTab[searchState.currentTab] = null;
if (target) {
target.focus();
function makeTabHeader(tabNb, text, nbElems) {
if (searchState.currentTab === tabNb) {
return "<button class=\"selected\">" + text +
- " <div class=\"count\">(" + nbElems + ")</div></button>";
+ " <span class=\"count\">(" + nbElems + ")</span></button>";
}
- return "<button>" + text + " <div class=\"count\">(" + nbElems + ")</div></button>";
+ return "<button>" + text + " <span class=\"count\">(" + nbElems + ")</span></button>";
}
/**
let output = `<h1 class="search-results-title">Results${crates}</h1>`;
if (results.query.error !== null) {
output += `<h3>Query parser error: "${results.query.error}".</h3>`;
- output += "<div id=\"titles\">" +
+ output += "<div id=\"search-tabs\">" +
makeTabHeader(0, "In Names", ret_others[1]) +
"</div>";
currentTab = 0;
} else if (results.query.foundElems <= 1 && results.query.returned.length === 0) {
- output += "<div id=\"titles\">" +
+ output += "<div id=\"search-tabs\">" +
makeTabHeader(0, "In Names", ret_others[1]) +
makeTabHeader(1, "In Parameters", ret_in_args[1]) +
makeTabHeader(2, "In Return Types", ret_returned[1]) +
results.query.elems.length === 0 ? "In Function Return Types" :
results.query.returned.length === 0 ? "In Function Parameters" :
"In Function Signatures";
- output += "<div id=\"titles\">" +
+ output += "<div id=\"search-tabs\">" +
makeTabHeader(0, signatureTabTitle, ret_others[1]) +
"</div>";
currentTab = 0;
search.appendChild(resultsElem);
// Reset focused elements.
searchState.showResults(search);
- const elems = document.getElementById("titles").childNodes;
+ const elems = document.getElementById("search-tabs").childNodes;
searchState.focusedByTab = [];
let i = 0;
for (const elem of elems) {
function createSidebarToggle() {
const sidebarToggle = document.createElement("div");
- sidebarToggle.id = "sidebar-toggle";
+ sidebarToggle.id = "src-sidebar-toggle";
const inner = document.createElement("button");
{{- sidebar|safe -}}
</nav> {#- -#}
<main> {#- -#}
- <div class="width-limiter"> {#- -#}
+ {%- if page.css_class != "source" -%}<div class="width-limiter">{%- endif -%}
<nav class="sub"> {#- -#}
{%- if page.css_class == "source" -%}
<a class="sub-logo-container" href="{{page.root_path|safe}}{{krate_with_trailing_slash|safe}}index.html"> {#- -#}
</form> {#- -#}
</nav> {#- -#}
<section id="main-content" class="content">{{- content|safe -}}</section> {#- -#}
- </div> {#- -#}
+ {%- if page.css_class != "source" -%}</div>{%- endif -%}
</main> {#- -#}
{{- layout.external_html.after_content|safe -}}
<div id="rustdoc-vars" {# -#}
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::{walk_item, Visitor};
use rustc_hir::Node;
use rustc_hir::CRATE_HIR_ID;
+use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
false
}
-// Also, is there some reason that this doesn't use the 'visit'
-// framework from syntax?.
-
pub(crate) struct RustdocVisitor<'a, 'tcx> {
cx: &'a mut core::DocContext<'tcx>,
view_item_stack: FxHashSet<hir::HirId>,
/// Are the current module and all of its parents public?
inside_public_path: bool,
exact_paths: FxHashMap<DefId, Vec<Symbol>>,
+ modules: Vec<Module<'tcx>>,
}
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// If the root is re-exported, terminate all recursion.
let mut stack = FxHashSet::default();
stack.insert(hir::CRATE_HIR_ID);
+ let om = Module::new(
+ cx.tcx.crate_name(LOCAL_CRATE),
+ hir::CRATE_HIR_ID,
+ cx.tcx.hir().root_module().spans.inner_span,
+ );
+
RustdocVisitor {
cx,
view_item_stack: stack,
inlining: false,
inside_public_path: true,
exact_paths: FxHashMap::default(),
+ modules: vec![om],
}
}
}
pub(crate) fn visit(mut self) -> Module<'tcx> {
- let mut top_level_module = self.visit_mod_contents(
- hir::CRATE_HIR_ID,
- self.cx.tcx.hir().root_module(),
- self.cx.tcx.crate_name(LOCAL_CRATE),
- None,
- );
+ let root_module = self.cx.tcx.hir().root_module();
+ self.visit_mod_contents(CRATE_HIR_ID, root_module);
+
+ let mut top_level_module = self.modules.pop().unwrap();
// `#[macro_export] macro_rules!` items are reexported at the top level of the
// crate, regardless of where they're defined. We want to document the
// macro in the same module.
let mut inserted = FxHashSet::default();
for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) {
- if let Res::Def(DefKind::Macro(_), def_id) = export.res {
- if let Some(local_def_id) = def_id.as_local() {
- if self.cx.tcx.has_attr(def_id, sym::macro_export) {
- if inserted.insert(def_id) {
- let item = self.cx.tcx.hir().expect_item(local_def_id);
- top_level_module.items.push((item, None, None));
- }
- }
- }
+ if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
+ let Some(local_def_id) = def_id.as_local() &&
+ self.cx.tcx.has_attr(def_id, sym::macro_export) &&
+ inserted.insert(def_id)
+ {
+ let item = self.cx.tcx.hir().expect_item(local_def_id);
+ top_level_module.items.push((item, None, None));
}
}
top_level_module
}
- fn visit_mod_contents(
- &mut self,
- id: hir::HirId,
- m: &'tcx hir::Mod<'tcx>,
- name: Symbol,
- parent_id: Option<hir::HirId>,
- ) -> Module<'tcx> {
- let mut om = Module::new(name, id, m.spans.inner_span);
+ /// This method will go through the given module items in two passes:
+ /// 1. The items which are not glob imports/reexports.
+ /// 2. The glob imports/reexports.
+ fn visit_mod_contents(&mut self, id: hir::HirId, m: &'tcx hir::Mod<'tcx>) {
+ debug!("Going through module {:?}", m);
let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
// Keep track of if there were any private modules in the path.
let orig_inside_public_path = self.inside_public_path;
self.inside_public_path &= self.cx.tcx.visibility(def_id).is_public();
+
+ // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in
+ // the second loop):
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
- if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- continue;
+ if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
+ self.visit_item(item);
}
- self.visit_item(item, None, &mut om, parent_id);
}
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
// Later passes in rustdoc will de-duplicate by name and kind, so if glob-
// imported items appear last, then they'll be the ones that get discarded.
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- self.visit_item(item, None, &mut om, parent_id);
+ self.visit_item(item);
}
}
self.inside_public_path = orig_inside_public_path;
- om
}
/// Tries to resolve the target of a `pub use` statement and inlines the
res: Res,
renamed: Option<Symbol>,
glob: bool,
- om: &mut Module<'tcx>,
please_inline: bool,
) -> bool {
debug!("maybe_inline_local res: {:?}", res);
let prev = mem::replace(&mut self.inlining, true);
for &i in m.item_ids {
let i = self.cx.tcx.hir().item(i);
- self.visit_item(i, None, om, Some(id));
+ self.visit_item_inner(i, None, Some(id));
}
self.inlining = prev;
true
}
Node::Item(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_item(it, renamed, om, Some(id));
+ self.visit_item_inner(it, renamed, Some(id));
self.inlining = prev;
true
}
Node::ForeignItem(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_foreign_item(it, renamed, om);
+ self.visit_foreign_item_inner(it, renamed);
self.inlining = prev;
true
}
ret
}
- fn visit_item(
+ #[inline]
+ fn add_to_current_mod(
&mut self,
item: &'tcx hir::Item<'_>,
renamed: Option<Symbol>,
- om: &mut Module<'tcx>,
parent_id: Option<hir::HirId>,
) {
+ self.modules.last_mut().unwrap().items.push((item, renamed, parent_id))
+ }
+
+ fn visit_item_inner(
+ &mut self,
+ item: &'tcx hir::Item<'_>,
+ renamed: Option<Symbol>,
+ parent_id: Option<hir::HirId>,
+ ) -> bool {
debug!("visiting item {:?}", item);
let name = renamed.unwrap_or(item.ident.name);
hir::ItemKind::ForeignMod { items, .. } => {
for item in items {
let item = self.cx.tcx.hir().foreign_item(item.id);
- self.visit_foreign_item(item, None, om);
+ self.visit_foreign_item_inner(item, None);
}
}
// If we're inlining, skip private items or item reexported as "_".
res,
ident,
is_glob,
- om,
please_inline,
) {
continue;
}
}
- om.items.push((item, renamed, parent_id))
+ self.add_to_current_mod(item, renamed, parent_id);
}
}
hir::ItemKind::Macro(ref macro_def, _) => {
let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export);
if is_macro_2_0 || nonexported || self.inlining {
- om.items.push((item, renamed, None));
+ self.add_to_current_mod(item, renamed, None);
}
}
hir::ItemKind::Mod(ref m) => {
- om.mods.push(self.visit_mod_contents(item.hir_id(), m, name, parent_id));
+ self.enter_mod(item.hir_id(), m, name);
}
hir::ItemKind::Fn(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)
- | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed, parent_id)),
+ | hir::ItemKind::TraitAlias(..) => {
+ self.add_to_current_mod(item, renamed, parent_id);
+ }
hir::ItemKind::Const(..) => {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if name != kw::Underscore {
- om.items.push((item, renamed, parent_id));
+ self.add_to_current_mod(item, renamed, parent_id);
}
}
hir::ItemKind::Impl(impl_) => {
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
// them up regardless of where they're located.
if !self.inlining && impl_.of_trait.is_none() {
- om.items.push((item, None, None));
+ self.add_to_current_mod(item, None, None);
}
}
}
+ true
}
- fn visit_foreign_item(
+ fn visit_foreign_item_inner(
&mut self,
item: &'tcx hir::ForeignItem<'_>,
renamed: Option<Symbol>,
- om: &mut Module<'tcx>,
) {
// If inlining we only want to include public functions.
if !self.inlining || self.cx.tcx.visibility(item.owner_id).is_public() {
- om.foreigns.push((item, renamed));
+ self.modules.last_mut().unwrap().foreigns.push((item, renamed));
}
}
+
+ /// This method will create a new module and push it onto the "modules stack" then call
+ /// `visit_mod_contents`. Once done, it'll remove it from the "modules stack" and instead
+ /// add into into the list of modules of the current module.
+ fn enter_mod(&mut self, id: hir::HirId, m: &'tcx hir::Mod<'tcx>, name: Symbol) {
+ self.modules.push(Module::new(name, id, m.spans.inner_span));
+
+ self.visit_mod_contents(id, m);
+
+ let last = self.modules.pop().unwrap();
+ self.modules.last_mut().unwrap().mods.push(last);
+ }
+}
+
+// We need to implement this visitor so it'll go everywhere and retrieve items we're interested in
+// such as impl blocks in const blocks.
+impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
+ let parent_id = if self.modules.len() > 1 {
+ Some(self.modules[self.modules.len() - 2].id)
+ } else {
+ None
+ };
+ if self.visit_item_inner(i, None, parent_id) {
+ walk_item(self, i);
+ }
+ }
+
+ fn visit_mod(&mut self, _: &hir::Mod<'tcx>, _: Span, _: hir::HirId) {
+ // Handled in `visit_item_inner`
+ }
+
+ fn visit_use(&mut self, _: &hir::UsePath<'tcx>, _: hir::HirId) {
+ // Handled in `visit_item_inner`
+ }
+
+ fn visit_path(&mut self, _: &hir::Path<'tcx>, _: hir::HirId) {
+ // Handled in `visit_item_inner`
+ }
+
+ fn visit_label(&mut self, _: &rustc_ast::Label) {
+ // Unneeded.
+ }
+
+ fn visit_infer(&mut self, _: &hir::InferArg) {
+ // Unneeded.
+ }
+
+ fn visit_lifetime(&mut self, _: &hir::Lifetime) {
+ // Unneeded.
+ }
}
--- /dev/null
+// Test that jump tables are (not) emitted when the `-Zno-jump-tables`
+// flag is (not) set.
+
+// revisions: unset set
+// assembly-output: emit-asm
+// compile-flags: -O
+// [set] compile-flags: -Zno-jump-tables
+// only-x86_64
+
+#![crate_type = "lib"]
+
+extern "C" {
+ fn bar1();
+ fn bar2();
+ fn bar3();
+ fn bar4();
+ fn bar5();
+ fn bar6();
+}
+
+// CHECK-LABEL: foo:
+#[no_mangle]
+pub unsafe fn foo(x: i32) {
+ // unset: LJTI0_0
+ // set-NOT: LJTI0_0
+ match x {
+ 1 => bar1(),
+ 2 => bar2(),
+ 3 => bar3(),
+ 4 => bar4(),
+ 5 => bar5(),
+ _ => bar6(),
+ }
+}
-// compile-flags: -O
+// compile-flags: -O -Z merge-functions=disabled
#![crate_type = "lib"]
+#![feature(core_intrinsics)]
// This test checks that we annotate alignment loads from vtables with nonzero range metadata,
// and that this allows LLVM to eliminate redundant `align >= 1` checks.
&x.dst
}
+// CHECK-LABEL: @align_load_from_align_of_val
+#[no_mangle]
+pub fn align_load_from_align_of_val(x: &dyn Trait) -> usize {
+ // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]]
+ core::mem::align_of_val(x)
+}
+
+// CHECK-LABEL: @align_load_from_vtable_align_intrinsic
+#[no_mangle]
+pub unsafe fn align_load_from_vtable_align_intrinsic(x: &dyn Trait) -> usize {
+ let (data, vtable): (*const (), *const ()) = core::mem::transmute(x);
+ // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]]
+ core::intrinsics::vtable_align(vtable)
+}
+
// CHECK: [[RANGE_META]] = !{[[USIZE]] 1, [[USIZE]] 0}
--- /dev/null
+// compile-flags: -O -Z merge-functions=disabled
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+
+// Check that we annotate size loads from vtables with 0..(isize::MAX + 1) range metadata.
+
+pub trait Trait {
+ fn f(&self);
+}
+
+// Note that rustc uses inclusive bounds, but LLVM uses exclusive bounds for range metadata.
+// CHECK-LABEL: @generate_exclusive_bound
+#[no_mangle]
+pub fn generate_exclusive_bound() -> usize {
+ // CHECK: ret [[USIZE:i[0-9]+]] [[EXCLUSIVE_BOUND:[-0-9]+]]
+ isize::MAX as usize + 1
+}
+
+// CHECK-LABEL: @size_load_from_size_of_val
+#[no_mangle]
+pub fn size_load_from_size_of_val(x: &dyn Trait) -> usize {
+ // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META:![0-9]+]]
+ core::mem::size_of_val(x)
+}
+
+// CHECK-LABEL: @size_load_from_vtable_size_intrinsic
+#[no_mangle]
+pub unsafe fn size_load_from_vtable_size_intrinsic(x: &dyn Trait) -> usize {
+ let (data, vtable): (*const (), *const ()) = core::mem::transmute(x);
+ // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]]
+ core::intrinsics::vtable_size(vtable)
+}
+
+// CHECK: [[RANGE_META]] = !{[[USIZE]] 0, [[USIZE]] [[EXCLUSIVE_BOUND]]}
// CHECK: define i8 @match1{{.*}}
// CHECK-NEXT: start:
-// CHECK-NEXT: %1 = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
-// CHECK-NEXT: switch i8 %1, label {{.*}} [
+// CHECK-NEXT: [[DISCR:%.*]] = {{.*}}call i8 @llvm.usub.sat.i8(i8 %0, i8 1)
+// CHECK-NEXT: switch i8 [[DISCR]], label {{.*}} [
#[no_mangle]
pub fn match1(e: Enum1) -> u8 {
use Enum1::*;
--- /dev/null
+// Test that the `no-jump-tables` function attribute are (not) emitted when
+// the `-Zno-jump-tables` flag is (not) set.
+
+// revisions: unset set
+// needs-llvm-components: x86
+// compile-flags: --target x86_64-unknown-linux-gnu
+// [set] compile-flags: -Zno-jump-tables
+
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+
+#[no_mangle]
+pub fn foo() {
+ // CHECK: @foo() unnamed_addr #0
+
+ // unset-NOT: attributes #0 = { {{.*}}"no-jump-tables"="true"{{.*}} }
+ // set: attributes #0 = { {{.*}}"no-jump-tables"="true"{{.*}} }
+}
pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 {
// CHECK-LABEL: define{{.*}}foo
- // FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
- // CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE1:[0-9]+]]
+ // CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE1:[0-9]+]]
// CHECK: call i32 %f(i32 %arg){{.*}}[ "kcfi"(i32 -1666898348) ]
f(arg)
}
pub fn bar(f: fn(i32, i32) -> i32, arg1: i32, arg2: i32) -> i32 {
// CHECK-LABEL: define{{.*}}bar
- // FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
- // CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE2:[0-9]+]]
+ // CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE2:[0-9]+]]
// CHECK: call i32 %f(i32 %arg1, i32 %arg2){{.*}}[ "kcfi"(i32 -1789026986) ]
f(arg1, arg2)
}
pub fn baz(f: fn(i32, i32, i32) -> i32, arg1: i32, arg2: i32, arg3: i32) -> i32 {
// CHECK-LABEL: define{{.*}}baz
- // FIXME(rcvalle): Change <unknown kind #36> to !kcfi_type when Rust is updated to LLVM 16
- // CHECK-SAME: {{.*}}!<unknown kind #36> ![[TYPE3:[0-9]+]]
+ // CHECK-SAME: {{.*}}!{{<unknown kind #36>|kcfi_type}} ![[TYPE3:[0-9]+]]
// CHECK: call i32 %f(i32 %arg1, i32 %arg2, i32 %arg3){{.*}}[ "kcfi"(i32 1248878270) ]
f(arg1, arg2, arg3)
}
// CHECK: @rust_item_that_can_unwind() unnamed_addr [[ATTR0:#[0-9]+]]
#[no_mangle]
pub unsafe extern "C-unwind" fn rust_item_that_can_unwind() {
- // CHECK: call void @_ZN4core9panicking15panic_no_unwind
+ // Handle both legacy and v0 symbol mangling.
+ // CHECK: call void @{{.*core9panicking15panic_no_unwind}}
may_unwind();
}
// CHECK: Function Attrs:{{.*}}nounwind
// CHECK-NEXT: define{{.*}}void @foo
-// CHECK: call void @_ZN4core9panicking15panic_no_unwind
+// Handle both legacy and v0 symbol mangling.
+// CHECK: call void @{{.*core9panicking15panic_no_unwind}}
#[no_mangle]
pub unsafe extern "C" fn foo() {
bar();
bb0: {
_2 = &raw const (*_1); // scope 0 at $DIR/references.rs:+5:13: +5:29
- Retag([raw] _2); // scope 0 at $DIR/references.rs:+6:13: +6:24
- _0 = &(*_2); // scope 0 at $DIR/references.rs:+7:13: +7:23
- Retag(_0); // scope 0 at $DIR/references.rs:+8:13: +8:23
- return; // scope 0 at $DIR/references.rs:+9:13: +9:21
+ _0 = &(*_2); // scope 0 at $DIR/references.rs:+6:13: +6:23
+ Retag(_0); // scope 0 at $DIR/references.rs:+7:13: +7:23
+ return; // scope 0 at $DIR/references.rs:+8:13: +8:21
}
}
bb0: {
_2 = &raw mut (*_1); // scope 0 at $DIR/references.rs:+5:13: +5:33
- Retag([raw] _2); // scope 0 at $DIR/references.rs:+6:13: +6:24
- _0 = &mut (*_2); // scope 0 at $DIR/references.rs:+7:13: +7:26
- Retag(_0); // scope 0 at $DIR/references.rs:+8:13: +8:23
- return; // scope 0 at $DIR/references.rs:+9:13: +9:21
+ _0 = &mut (*_2); // scope 0 at $DIR/references.rs:+6:13: +6:26
+ Retag(_0); // scope 0 at $DIR/references.rs:+7:13: +7:23
+ return; // scope 0 at $DIR/references.rs:+8:13: +8:21
}
}
{
t = addr_of_mut!(*x);
- RetagRaw(t);
RET = &mut *t;
Retag(RET);
Return()
{
t = addr_of!(*x);
- RetagRaw(t);
RET = & *t;
Retag(RET);
Return()
--- /dev/null
+// MIR for `assert_nonzero` after built
+
+fn assert_nonzero(_1: i32) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/terminators.rs:+0:27: +0:27
+
+ bb0: {
+ switchInt(_1) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/terminators.rs:+3:13: +6:14
+ }
+
+ bb1: {
+ unreachable; // scope 0 at $DIR/terminators.rs:+10:13: +10:26
+ }
+
+ bb2: {
+ return; // scope 0 at $DIR/terminators.rs:+14:13: +14:21
+ }
+}
--- /dev/null
+// MIR for `direct_call` after built
+
+fn direct_call(_1: i32) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/terminators.rs:+0:27: +0:30
+
+ bb0: {
+ _0 = ident::<i32>(_1) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:42
+ // mir::Constant
+ // + span: $DIR/terminators.rs:15:33: 15:38
+ // + literal: Const { ty: fn(i32) -> i32 {ident::<i32>}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+ }
+}
--- /dev/null
+// MIR for `drop_first` after built
+
+fn drop_first(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/terminators.rs:+0:59: +0:59
+
+ bb0: {
+ replace(_1 <- move _2) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:49
+ }
+
+ bb1: {
+ return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+ }
+}
--- /dev/null
+// MIR for `drop_second` after built
+
+fn drop_second(_1: WriteOnDrop<'_>, _2: WriteOnDrop<'_>) -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/terminators.rs:+0:60: +0:60
+
+ bb0: {
+ drop(_2) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:30
+ }
+
+ bb1: {
+ return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+ }
+}
--- /dev/null
+// MIR for `indirect_call` after built
+
+fn indirect_call(_1: i32, _2: fn(i32) -> i32) -> i32 {
+ let mut _0: i32; // return place in scope 0 at $DIR/terminators.rs:+0:48: +0:51
+
+ bb0: {
+ _0 = _2(_1) -> bb1; // scope 0 at $DIR/terminators.rs:+3:13: +3:38
+ }
+
+ bb1: {
+ return; // scope 0 at $DIR/terminators.rs:+7:13: +7:21
+ }
+}
--- /dev/null
+#![feature(custom_mir, core_intrinsics)]
+
+extern crate core;
+use core::intrinsics::mir::*;
+
+fn ident<T>(t: T) -> T {
+ t
+}
+
+// EMIT_MIR terminators.direct_call.built.after.mir
+#[custom_mir(dialect = "built")]
+fn direct_call(x: i32) -> i32 {
+ mir!(
+ {
+ Call(RET, retblock, ident(x))
+ }
+
+ retblock = {
+ Return()
+ }
+ )
+}
+
+// EMIT_MIR terminators.indirect_call.built.after.mir
+#[custom_mir(dialect = "built")]
+fn indirect_call(x: i32, f: fn(i32) -> i32) -> i32 {
+ mir!(
+ {
+ Call(RET, retblock, f(x))
+ }
+
+ retblock = {
+ Return()
+ }
+ )
+}
+
+struct WriteOnDrop<'a>(&'a mut i32, i32);
+
+impl<'a> Drop for WriteOnDrop<'a> {
+ fn drop(&mut self) {
+ *self.0 = self.1;
+ }
+}
+
+// EMIT_MIR terminators.drop_first.built.after.mir
+#[custom_mir(dialect = "built")]
+fn drop_first<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
+ mir!(
+ {
+ DropAndReplace(a, Move(b), retblock)
+ }
+
+ retblock = {
+ Return()
+ }
+ )
+}
+
+// EMIT_MIR terminators.drop_second.built.after.mir
+#[custom_mir(dialect = "built")]
+fn drop_second<'a>(a: WriteOnDrop<'a>, b: WriteOnDrop<'a>) {
+ mir!(
+ {
+ Drop(b, retblock)
+ }
+
+ retblock = {
+ Return()
+ }
+ )
+}
+
+// EMIT_MIR terminators.assert_nonzero.built.after.mir
+#[custom_mir(dialect = "built")]
+fn assert_nonzero(a: i32) {
+ mir!(
+ {
+ match a {
+ 0 => unreachable,
+ _ => retblock
+ }
+ }
+
+ unreachable = {
+ Unreachable()
+ }
+
+ retblock = {
+ Return()
+ }
+ )
+}
+
+fn main() {
+ assert_eq!(direct_call(5), 5);
+ assert_eq!(indirect_call(5, ident), 5);
+
+ let mut a = 0;
+ let mut b = 0;
+ drop_first(WriteOnDrop(&mut a, 1), WriteOnDrop(&mut b, 1));
+ assert_eq!((a, b), (1, 0));
+
+ let mut a = 0;
+ let mut b = 0;
+ drop_second(WriteOnDrop(&mut a, 1), WriteOnDrop(&mut b, 1));
+ assert_eq!((a, b), (0, 1));
+}
--- /dev/null
+- // MIR for `f` before DestinationPropagation
++ // MIR for `f` after DestinationPropagation
+
+ fn f(_1: T) -> () {
+ debug a => _1; // in scope 0 at $DIR/unreachable.rs:+0:19: +0:20
+ let mut _0: (); // return place in scope 0 at $DIR/unreachable.rs:+0:25: +0:25
+ let _2: T; // in scope 0 at $DIR/unreachable.rs:+1:9: +1:10
+ let mut _3: bool; // in scope 0 at $DIR/unreachable.rs:+2:8: +2:13
+ let _4: (); // in scope 0 at $DIR/unreachable.rs:+3:9: +3:16
+ let mut _5: T; // in scope 0 at $DIR/unreachable.rs:+3:11: +3:12
+ let mut _6: T; // in scope 0 at $DIR/unreachable.rs:+3:14: +3:15
+ let _7: (); // in scope 0 at $DIR/unreachable.rs:+5:9: +5:16
+ let mut _8: T; // in scope 0 at $DIR/unreachable.rs:+5:11: +5:12
+ let mut _9: T; // in scope 0 at $DIR/unreachable.rs:+5:14: +5:15
+ scope 1 {
+- debug b => _2; // in scope 1 at $DIR/unreachable.rs:+1:9: +1:10
++ debug b => _1; // in scope 1 at $DIR/unreachable.rs:+1:9: +1:10
+ }
+
+ bb0: {
+- StorageLive(_2); // scope 0 at $DIR/unreachable.rs:+1:9: +1:10
+- _2 = _1; // scope 0 at $DIR/unreachable.rs:+1:13: +1:14
++ nop; // scope 0 at $DIR/unreachable.rs:+1:9: +1:10
++ nop; // scope 0 at $DIR/unreachable.rs:+1:13: +1:14
+ StorageLive(_3); // scope 1 at $DIR/unreachable.rs:+2:8: +2:13
+ _3 = const false; // scope 1 at $DIR/unreachable.rs:+2:8: +2:13
+- goto -> bb3; // scope 1 at $DIR/unreachable.rs:+2:8: +2:13
++ goto -> bb1; // scope 1 at $DIR/unreachable.rs:+2:8: +2:13
+ }
+
+ bb1: {
+- StorageLive(_4); // scope 1 at $DIR/unreachable.rs:+3:9: +3:16
+- StorageLive(_5); // scope 1 at $DIR/unreachable.rs:+3:11: +3:12
+- _5 = _1; // scope 1 at $DIR/unreachable.rs:+3:11: +3:12
+- StorageLive(_6); // scope 1 at $DIR/unreachable.rs:+3:14: +3:15
+- _6 = _2; // scope 1 at $DIR/unreachable.rs:+3:14: +3:15
+- _4 = g::<T>(move _5, move _6) -> bb2; // scope 1 at $DIR/unreachable.rs:+3:9: +3:16
+- // mir::Constant
+- // + span: $DIR/unreachable.rs:11:9: 11:10
+- // + literal: Const { ty: fn(T, T) {g::<T>}, val: Value(<ZST>) }
+- }
+-
+- bb2: {
+- StorageDead(_6); // scope 1 at $DIR/unreachable.rs:+3:15: +3:16
+- StorageDead(_5); // scope 1 at $DIR/unreachable.rs:+3:15: +3:16
+- StorageDead(_4); // scope 1 at $DIR/unreachable.rs:+3:16: +3:17
+- _0 = const (); // scope 1 at $DIR/unreachable.rs:+2:14: +4:6
+- goto -> bb5; // scope 1 at $DIR/unreachable.rs:+2:5: +6:6
+- }
+-
+- bb3: {
+ StorageLive(_7); // scope 1 at $DIR/unreachable.rs:+5:9: +5:16
+- StorageLive(_8); // scope 1 at $DIR/unreachable.rs:+5:11: +5:12
+- _8 = _2; // scope 1 at $DIR/unreachable.rs:+5:11: +5:12
++ nop; // scope 1 at $DIR/unreachable.rs:+5:11: +5:12
++ nop; // scope 1 at $DIR/unreachable.rs:+5:11: +5:12
+ StorageLive(_9); // scope 1 at $DIR/unreachable.rs:+5:14: +5:15
+- _9 = _2; // scope 1 at $DIR/unreachable.rs:+5:14: +5:15
+- _7 = g::<T>(move _8, move _9) -> bb4; // scope 1 at $DIR/unreachable.rs:+5:9: +5:16
++ _9 = _1; // scope 1 at $DIR/unreachable.rs:+5:14: +5:15
++ _7 = g::<T>(move _1, move _9) -> bb2; // scope 1 at $DIR/unreachable.rs:+5:9: +5:16
+ // mir::Constant
+ // + span: $DIR/unreachable.rs:13:9: 13:10
+ // + literal: Const { ty: fn(T, T) {g::<T>}, val: Value(<ZST>) }
+ }
+
+- bb4: {
++ bb2: {
+ StorageDead(_9); // scope 1 at $DIR/unreachable.rs:+5:15: +5:16
+- StorageDead(_8); // scope 1 at $DIR/unreachable.rs:+5:15: +5:16
++ nop; // scope 1 at $DIR/unreachable.rs:+5:15: +5:16
+ StorageDead(_7); // scope 1 at $DIR/unreachable.rs:+5:16: +5:17
+ _0 = const (); // scope 1 at $DIR/unreachable.rs:+4:12: +6:6
+- goto -> bb5; // scope 1 at $DIR/unreachable.rs:+2:5: +6:6
++ goto -> bb3; // scope 1 at $DIR/unreachable.rs:+2:5: +6:6
+ }
+
+- bb5: {
++ bb3: {
+ StorageDead(_3); // scope 1 at $DIR/unreachable.rs:+6:5: +6:6
+- StorageDead(_2); // scope 0 at $DIR/unreachable.rs:+7:1: +7:2
++ nop; // scope 0 at $DIR/unreachable.rs:+7:1: +7:2
+ return; // scope 0 at $DIR/unreachable.rs:+7:2: +7:2
+ }
+ }
+
--- /dev/null
+// Check that unreachable code is removed after the destination propagation.
+// Regression test for issue #105428.
+//
+// compile-flags: --crate-type=lib -Zmir-opt-level=0
+// compile-flags: -Zmir-enable-passes=+ConstProp,+SimplifyConstCondition-after-const-prop,+DestinationPropagation
+
+// EMIT_MIR unreachable.f.DestinationPropagation.diff
+pub fn f<T: Copy>(a: T) {
+ let b = a;
+ if false {
+ g(a, b);
+ } else {
+ g(b, b);
+ }
+}
+
+#[inline(never)]
+pub fn g<T: Copy>(_: T, _: T) {}
+++ /dev/null
-- // MIR for `match_guard` before CleanupNonCodegenStatements
-+ // MIR for `match_guard` after CleanupNonCodegenStatements
-
- fn match_guard(_1: Option<&&i32>, _2: bool) -> i32 {
- debug x => _1; // in scope 0 at $DIR/remove_fake_borrows.rs:+0:16: +0:17
- debug c => _2; // in scope 0 at $DIR/remove_fake_borrows.rs:+0:34: +0:35
- let mut _0: i32; // return place in scope 0 at $DIR/remove_fake_borrows.rs:+0:46: +0:49
- let mut _3: isize; // in scope 0 at $DIR/remove_fake_borrows.rs:+2:9: +2:16
- let mut _4: &std::option::Option<&&i32>; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- let mut _5: &&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- let mut _6: &&&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- let mut _7: &i32; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- let mut _8: bool; // in scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-
- bb0: {
-- FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- _3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- switchInt(move _3) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
- }
-
- bb1: {
- _0 = const 1_i32; // scope 0 at $DIR/remove_fake_borrows.rs:+3:14: +3:15
- goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:+3:14: +3:15
- }
-
- bb2: {
- switchInt((*(*((_1 as Some).0: &&i32)))) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
- }
-
- bb3: {
- goto -> bb4; // scope 0 at $DIR/remove_fake_borrows.rs:+2:9: +2:16
- }
-
- bb4: {
-- _4 = &shallow _1; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-- _5 = &shallow (*((_1 as Some).0: &&i32)); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-- _6 = &shallow ((_1 as Some).0: &&i32); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-- _7 = &shallow (*(*((_1 as Some).0: &&i32))); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
- StorageLive(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- _8 = _2; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- switchInt(move _8) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- }
-
- bb5: {
- StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-- FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-- FakeRead(ForMatchGuard, _6); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-- FakeRead(ForMatchGuard, _7); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
-+ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- _0 = const 0_i32; // scope 0 at $DIR/remove_fake_borrows.rs:+2:25: +2:26
- goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:+2:25: +2:26
- }
-
- bb6: {
- StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- goto -> bb1; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
- }
-
- bb7: {
- return; // scope 0 at $DIR/remove_fake_borrows.rs:+5:2: +5:2
- }
-
- bb8 (cleanup): {
- resume; // scope 0 at $DIR/remove_fake_borrows.rs:+0:1: +5:2
- }
- }
-
--- /dev/null
+- // MIR for `match_guard` before CleanupPostBorrowck
++ // MIR for `match_guard` after CleanupPostBorrowck
+
+ fn match_guard(_1: Option<&&i32>, _2: bool) -> i32 {
+ debug x => _1; // in scope 0 at $DIR/remove_fake_borrows.rs:+0:16: +0:17
+ debug c => _2; // in scope 0 at $DIR/remove_fake_borrows.rs:+0:34: +0:35
+ let mut _0: i32; // return place in scope 0 at $DIR/remove_fake_borrows.rs:+0:46: +0:49
+ let mut _3: isize; // in scope 0 at $DIR/remove_fake_borrows.rs:+2:9: +2:16
+ let mut _4: &std::option::Option<&&i32>; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ let mut _5: &&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ let mut _6: &&&i32; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ let mut _7: &i32; // in scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ let mut _8: bool; // in scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+
+ bb0: {
+- FakeRead(ForMatchedPlace(None), _1); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ _3 = discriminant(_1); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ switchInt(move _3) -> [1: bb2, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
+ }
+
+ bb1: {
+ _0 = const 1_i32; // scope 0 at $DIR/remove_fake_borrows.rs:+3:14: +3:15
+ goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:+3:14: +3:15
+ }
+
+ bb2: {
+ switchInt((*(*((_1 as Some).0: &&i32)))) -> [0: bb3, otherwise: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+1:5: +1:12
+ }
+
+ bb3: {
+- falseEdge -> [real: bb4, imaginary: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+2:9: +2:16
++ goto -> bb4; // scope 0 at $DIR/remove_fake_borrows.rs:+2:9: +2:16
+ }
+
+ bb4: {
+- _4 = &shallow _1; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+- _5 = &shallow (*((_1 as Some).0: &&i32)); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+- _6 = &shallow ((_1 as Some).0: &&i32); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+- _7 = &shallow (*(*((_1 as Some).0: &&i32))); // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+1:11: +1:12
+ StorageLive(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+ _8 = _2; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+ switchInt(move _8) -> [0: bb6, otherwise: bb5]; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+ }
+
+ bb5: {
+ StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+- FakeRead(ForMatchGuard, _4); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+- FakeRead(ForMatchGuard, _5); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+- FakeRead(ForMatchGuard, _6); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+- FakeRead(ForMatchGuard, _7); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
++ nop; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+ _0 = const 0_i32; // scope 0 at $DIR/remove_fake_borrows.rs:+2:25: +2:26
+ goto -> bb7; // scope 0 at $DIR/remove_fake_borrows.rs:+2:25: +2:26
+ }
+
+ bb6: {
+ StorageDead(_8); // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+- falseEdge -> [real: bb1, imaginary: bb1]; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
++ goto -> bb1; // scope 0 at $DIR/remove_fake_borrows.rs:+2:20: +2:21
+ }
+
+ bb7: {
+ return; // scope 0 at $DIR/remove_fake_borrows.rs:+5:2: +5:2
+ }
+ }
+
// ignore-wasm32-bare compiled with panic=abort by default
-// EMIT_MIR remove_fake_borrows.match_guard.CleanupNonCodegenStatements.diff
+// EMIT_MIR remove_fake_borrows.match_guard.CleanupPostBorrowck.diff
fn match_guard(x: Option<&&i32>, c: bool) -> i32 {
match x {
Some(0) if c => 0,
fn std::ptr::drop_in_place(_1: *mut Test) -> () {
let mut _0: (); // return place in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
let mut _2: &mut Test; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- let mut _3: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ let mut _3: &mut Test; // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ let mut _4: (); // in scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
bb0: {
_2 = &mut (*_1); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
- _3 = <Test as Drop>::drop(move _2) -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ Retag([fn entry] _2); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ _3 = &mut (*_2); // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
+ _4 = <Test as Drop>::drop(move _3) -> bb1; // scope 0 at $SRC_DIR/core/src/ptr/mod.rs:+0:1: +0:56
// mir::Constant
// + span: $SRC_DIR/core/src/ptr/mod.rs:LL:COL
// + literal: Const { ty: for<'a> fn(&'a mut Test) {<Test as Drop>::drop}, val: Value(<ZST>) }
include ../tools.mk
all:
- $(RUSTC) --edition=2021 --crate-type=rlib ../../../../library/core/src/lib.rs --cfg no_fp_fmt_parse
+ $(RUSTC) --edition=2021 -Dwarnings --crate-type=rlib ../../../../library/core/src/lib.rs --cfg no_fp_fmt_parse
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: ".srclink"
+wait-for: ".src-line-numbers"
assert-count: (".src-line-numbers", 1)
// This test checks that the source code pages sidebar toggle is working as expected.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
click: ".srclink"
-wait-for: "#sidebar-toggle"
-click: "#sidebar-toggle"
+wait-for: "#src-sidebar-toggle"
+click: "#src-sidebar-toggle"
fail: true
assert-css: ("#source-sidebar", { "left": "-300px" })
--- /dev/null
+// Test that code blocks nested within <sub> do not have a line height of 0.
+goto: "file://" + |DOC_PATH| + "/test_docs/codeblock_sub/index.html"
+
+store-property: (codeblock_sub_1, "#codeblock-sub-1", "offsetHeight")
+assert-property-false: ("#codeblock-sub-3", { "offsetHeight": |codeblock_sub_1| })
{"border-left": "2px solid rgba(255, 0, 0, 0.5)"},
)),
- ("move-cursor-to", ".docblock .example-wrap.compile_fail"),
+ ("move-cursor-to", ".docblock .example-wrap.compile_fail .tooltip"),
("assert-css", (
".docblock .example-wrap.compile_fail .tooltip",
{"border-left": "2px solid rgba(255, 0, 0, 0.5)"},
)),
- ("move-cursor-to", ".docblock .example-wrap.should_panic"),
+ ("move-cursor-to", ".docblock .example-wrap.should_panic .tooltip"),
("assert-css", (
".docblock .example-wrap.should_panic .tooltip",
{"border-left": "2px solid rgba(255, 142, 0, 0.6)"},
)),
- ("move-cursor-to", ".docblock .example-wrap.ignore"),
+ ("move-cursor-to", ".docblock .example-wrap.ignore .tooltip"),
("assert-css", (
".docblock .example-wrap.ignore .tooltip",
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-css: ("#titles > button", {"cursor": "pointer"})
+wait-for: "#search-tabs"
+assert-css: ("#search-tabs > button", {"cursor": "pointer"})
// mobile sidebar toggle button
size: (500, 700)
// the sidebar toggle button on the source code pages
goto: "file://" + |DOC_PATH| + "/src/lib2/lib.rs.html"
-assert-css: ("#sidebar-toggle > button", {"cursor": "pointer"})
+assert-css: ("#src-sidebar-toggle > button", {"cursor": "pointer"})
--- /dev/null
+// Check that the line number column has the correct layout.
+goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
+
+// Check that it's not zero.
+assert-property-false: (
+ ".more-scraped-examples .scraped-example .code-wrapper .src-line-numbers",
+ {"clientWidth": "0"}
+)
+
+// Check that examples with very long lines have the same width as ones that don't.
+store-property: (
+ clientWidth,
+ ".more-scraped-examples .scraped-example:nth-child(2) .code-wrapper .src-line-numbers",
+ "clientWidth"
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(3) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(4) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(5) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+
+assert-property: (
+ ".more-scraped-examples .scraped-example:nth-child(6) .code-wrapper .src-line-numbers",
+ {"clientWidth": |clientWidth|}
+)
+// This tests checks that the "scraped examples" toggle is working as expected.
goto: "file://" + |DOC_PATH| + "/scrape_examples/fn.test_many.html"
// Clicking "More examples..." will open additional examples
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
assert-text: ("#results .externcrate", "test_docs")
wait-for: "#crate-search"
press-key: "ArrowDown"
press-key: "Enter"
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
assert-document-property: ({"URL": "&filter-crate="}, CONTAINS)
// We check that there is no more "test_docs" appearing.
assert-false: "#results .externcrate"
press-key: "ArrowUp"
press-key: "Enter"
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
assert-property: ("#crate-search", {"value": "all crates"})
// Checking that the URL parameter is taken into account for crate filtering.
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
// Now use the keyboard commands to switch to the third result.
press-key: "ArrowDown"
reload:
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
+assert-css: (
+ "#search-tabs > button > .count",
+ {"color": "rgb(136, 136, 136)"},
+ ALL,
+)
assert-css: (
"//*[@class='desc'][text()='Just a normal struct.']",
{"color": "rgb(197, 197, 197)"},
reload:
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
+assert-css: (
+ "#search-tabs > button > .count",
+ {"color": "rgb(136, 136, 136)"},
+ ALL,
+)
assert-css: (
"//*[@class='desc'][text()='Just a normal struct.']",
{"color": "rgb(221, 221, 221)"},
reload:
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
+assert-css: (
+ "#search-tabs > button > .count",
+ {"color": "rgb(136, 136, 136)"},
+ ALL,
+)
assert-css: (
"//*[@class='desc'][text()='Just a normal struct.']",
{"color": "rgb(0, 0, 0)"},
// To be SURE that the search will be run.
("press-key", 'Enter'),
// Waiting for the search results to appear...
- ("wait-for", "#titles"),
+ ("wait-for", "#search-tabs"),
// Checking that the colors for the alias element are the ones expected.
("assert-css", (".result-name > .alias", {"color": |alias|})),
("assert-css", (".result-name > .alias > .grey", {"color": |grey|})),
// This test is to ensure that the codeblocks are correctly rendered in the search results.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=some_more_function"
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
assert-text: (".search-results .desc code", "format!")
// We now check that we land on the search result page if "go_to_first" isn't set.
goto: "file://" + |DOC_PATH| + "/test_docs/index.html?search=struct%3AFoo"
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
assert-text-false: (".fqn", "Struct test_docs::Foo")
// Ensure that the search results are displayed, not the "normal" content.
assert-css: ("#main-content", {"display": "none"})
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
+wait-for: "#search-tabs"
// Note: The two next assert commands could be merged as one but readability would be
// less good.
//
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-assert-text: ("#titles > button:nth-of-type(1)", "In Names", STARTS_WITH)
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Names", STARTS_WITH)
assert: "input.search-input:focus"
// Use left-right keys
press-key: "ArrowDown"
assert: "#results > .search-results.active > a:nth-of-type(1):focus"
press-key: "ArrowRight"
-wait-for-attribute: ("#titles > button:nth-of-type(2)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(2)", {"class": "selected"})
press-key: "ArrowRight"
-wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected"})
press-key: "ArrowRight"
-wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
press-key: "ArrowLeft"
-wait-for-attribute: ("#titles > button:nth-of-type(3)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(3)", {"class": "selected"})
// Now try search-by-return
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
assert: "input.search-input:focus"
// Use left-right keys
press-key: "ArrowDown"
assert: "#results > .search-results.active > a:nth-of-type(1):focus"
press-key: "ArrowRight"
-wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
press-key: "ArrowRight"
-wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
press-key: "ArrowRight"
-wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
press-key: "ArrowLeft"
-wait-for-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
+wait-for-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
// Try with a search-by-return with no results
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-assert-text: ("#titles > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Return Types", STARTS_WITH)
// Try with a search-by-parameter
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-assert-text: ("#titles > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Parameters", STARTS_WITH)
// Try with a search-by-parameter-and-return
goto: "file://" + |DOC_PATH| + "/test_docs/index.html"
// To be SURE that the search will be run.
press-key: 'Enter'
// Waiting for the search results to appear...
-wait-for: "#titles"
-assert-attribute: ("#titles > button:nth-of-type(1)", {"class": "selected"})
-assert-text: ("#titles > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)
+wait-for: "#search-tabs"
+assert-attribute: ("#search-tabs > button:nth-of-type(1)", {"class": "selected"})
+assert-text: ("#search-tabs > button:nth-of-type(1)", "In Function Signatures", STARTS_WITH)
javascript: false
goto: "file://" + |DOC_PATH| + "/src/test_docs/lib.rs.html"
// Since the javascript is disabled, there shouldn't be a toggle.
-assert-false: "#sidebar-toggle"
+assert-false: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"display": "none"})
// Let's retry with javascript enabled.
javascript: true
reload:
-wait-for: "#sidebar-toggle"
-assert-css: ("#sidebar-toggle", {"visibility": "visible"})
-assert-css: (".sidebar > *:not(#sidebar-toggle)", {"visibility": "hidden"})
+wait-for: "#src-sidebar-toggle"
+assert-css: ("#src-sidebar-toggle", {"visibility": "visible"})
+assert-css: (".sidebar > *:not(#src-sidebar-toggle)", {"visibility": "hidden"})
// Let's expand the sidebar now.
-click: "#sidebar-toggle"
-wait-for-css: ("#sidebar-toggle", {"visibility": "visible"})
+click: "#src-sidebar-toggle"
+wait-for-css: ("#src-sidebar-toggle", {"visibility": "visible"})
// We now check that opening the sidebar and clicking a link will leave it open.
// The behavior here on desktop is different than the behavior on mobile,
[
("local-storage", {"rustdoc-theme": |theme|, "rustdoc-use-system-theme": "false"}),
("reload"),
- ("wait-for-css", ("#sidebar-toggle", {"visibility": "visible"})),
+ ("wait-for-css", ("#src-sidebar-toggle", {"visibility": "visible"})),
("assert-css", (
"#source-sidebar details[open] > .files a.selected",
{"color": |color_hover|, "background-color": |background|},
)),
// Without hover or focus.
- ("assert-css", ("#sidebar-toggle > button", {"background-color": |background_toggle|})),
+ ("assert-css", ("#src-sidebar-toggle > button", {"background-color": |background_toggle|})),
// With focus.
- ("focus", "#sidebar-toggle > button"),
+ ("focus", "#src-sidebar-toggle > button"),
("assert-css", (
- "#sidebar-toggle > button:focus",
+ "#src-sidebar-toggle > button:focus",
{"background-color": |background_toggle_hover|},
)),
("focus", ".search-input"),
// With hover.
- ("move-cursor-to", "#sidebar-toggle > button"),
+ ("move-cursor-to", "#src-sidebar-toggle > button"),
("assert-css", (
- "#sidebar-toggle > button:hover",
+ "#src-sidebar-toggle > button:hover",
{"background-color": |background_toggle_hover|},
)),
size: (500, 700)
reload:
// Waiting for the sidebar to be displayed...
-wait-for-css: ("#sidebar-toggle", {"visibility": "visible"})
+wait-for-css: ("#src-sidebar-toggle", {"visibility": "visible"})
// We now check it takes the full size of the display.
assert-property: ("body", {"clientWidth": "500", "clientHeight": "700"})
assert-property: (".sidebar", {"clientWidth": "500", "clientHeight": "700"})
// We now check the display of the toggle once the sidebar is expanded.
-assert-property: ("#sidebar-toggle", {"clientWidth": "500", "clientHeight": "39"})
+assert-property: ("#src-sidebar-toggle", {"clientWidth": "500", "clientHeight": "39"})
assert-css: (
- "#sidebar-toggle",
+ "#src-sidebar-toggle",
{
"border-top-width": "0px",
"border-right-width": "0px",
)
// We now check that the scroll position is kept when opening the sidebar.
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"width": "0px"})
// We scroll to line 117 to change the scroll position.
scroll-to: '//*[@id="117"]'
assert-window-property: {"pageYOffset": "2542"}
// Expanding the sidebar...
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"width": "500px"})
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: (".sidebar", {"width": "0px"})
// The "scrollTop" property should be the same.
assert-window-property: {"pageYOffset": "2542"}
// We now check that the scroll position is restored if the window is resized.
size: (500, 700)
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: ("#source-sidebar", {"visibility": "visible"})
assert-window-property: {"pageYOffset": "0"}
size: (900, 900)
assert-window-property: {"pageYOffset": "2542"}
size: (500, 700)
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
// We now check that opening the sidebar and clicking a link will close it.
// but common sense dictates that if you have a list of files that fills the entire screen, and
// you click one of them, you probably want to actually see the file's contents, and not just
// make it the current selection.
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: ("#source-sidebar", {"visibility": "visible"})
assert-local-storage: {"rustdoc-source-sidebar-show": "true"}
click: ".sidebar a.selected"
size: (1000, 1000)
wait-for-css: ("#source-sidebar", {"visibility": "hidden"})
assert-local-storage: {"rustdoc-source-sidebar-show": "false"}
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
wait-for-css: ("#source-sidebar", {"visibility": "visible"})
assert-local-storage: {"rustdoc-source-sidebar-show": "true"}
// Checking the source code sidebar.
// First we "open" it.
-click: "#sidebar-toggle"
+click: "#src-sidebar-toggle"
assert: ".source-sidebar-expanded"
// We check that the first entry of the sidebar is collapsed
fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
scrape_examples::test_many();
}
fn main() {
- scrape_examples::test_many();
+ // ignore-tidy-linelength
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
}
fn main() {
- scrape_examples::test_many();
+ // ignore-tidy-linelength
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
+ scrape_examples::test_many(); /* Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. */
}
fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
scrape_examples::test_many();
}
fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
scrape_examples::test_many();
}
fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
scrape_examples::test_many();
}
fn main() {
+ // all examples have same line count
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
+ scrape_examples::test_many();
scrape_examples::test_many();
}
/// fn doc
pub fn test_fn() {}
}
+
+/// <sub id="codeblock-sub-1">
+///
+/// ```
+/// one
+/// ```
+///
+/// </sub>
+///
+/// <sub id="codeblock-sub-3">
+///
+/// ```
+/// one
+/// two
+/// three
+/// ```
+///
+/// </sub>
+pub mod codeblock_sub {}
+// This test ensures that each field is on its own line (In other words, they have display: block).
goto: "file://" + |DOC_PATH| + "/test_docs/struct.StructWithPublicUndocumentedFields.html"
-// Both fields must be on their own line. In other words, they have display: block.
store-property: (first_top, "//*[@id='structfield.first']", "offsetTop")
assert-property-false: ("//*[@id='structfield.second']", { "offsetTop": |first_top| })
-// check-pass
// normalize-stderr-test: "`.*`" -> "`DEF_ID`"
// normalize-stdout-test: "`.*`" -> "`DEF_ID`"
// edition:2018
pub async fn f() -> impl std::fmt::Debug {
- // rustdoc doesn't care that this is infinitely sized
#[derive(Debug)]
- enum E {
+ enum E { //~ ERROR
This(E),
Unit,
}
--- /dev/null
+error[E0072]: recursive type `DEF_ID` has infinite size
+ --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5
+ |
+LL | enum E {
+ | ^^^^^^
+LL | This(E),
+ | - recursive without indirection
+ |
+help: insert some indirection (e.g., a `DEF_ID`) to break the cycle
+ |
+LL | This(Box<E>),
+ | ++++ +
+
+error: aborting due to previous error
+
+For more information about this error, try `DEF_ID`.
-// check-pass
-
fn f() -> impl Sized {
- // rustdoc doesn't care that this is infinitely sized
- enum E {
+ enum E { //~ ERROR
V(E),
}
unimplemented!()
--- /dev/null
+error[E0072]: recursive type `f::E` has infinite size
+ --> $DIR/infinite-recursive-type-impl-trait.rs:2:5
+ |
+LL | enum E {
+ | ^^^^^^
+LL | V(E),
+ | - recursive without indirection
+ |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+ |
+LL | V(Box<E>),
+ | ++++ +
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0072`.
-Z no-analysis=val -- parse and expand the source, but run no analysis
-Z no-codegen=val -- run all passes except codegen; no output
-Z no-generate-arange-section=val -- omit DWARF address ranges that give faster lookups
+ -Z no-jump-tables=val -- disable the jump tables and lookup tables that can be generated from a switch case lowering
-Z no-leak-check=val -- disable the 'leak check' for subtyping; unsound, but useful for tests
-Z no-link=val -- compile without linking
-Z no-parallel-llvm=val -- run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)
--- /dev/null
+// Regression test for #83026.
+// The goal of this test is to ensure that impl blocks inside
+// const expressions are documented as well.
+
+#![crate_name = "foo"]
+
+// @has 'foo/struct.A.html'
+// @has - '//*[@id="method.new"]/*[@class="code-header"]' 'pub fn new() -> A'
+// @has - '//*[@id="method.bar"]/*[@class="code-header"]' 'pub fn bar(&self)'
+// @has - '//*[@id="method.woo"]/*[@class="code-header"]' 'pub fn woo(&self)'
+// @has - '//*[@id="method.yoo"]/*[@class="code-header"]' 'pub fn yoo()'
+// @has - '//*[@id="method.yuu"]/*[@class="code-header"]' 'pub fn yuu()'
+pub struct A;
+
+const _: () = {
+ impl A {
+ const FOO: () = {
+ impl A {
+ pub fn woo(&self) {}
+ }
+ };
+
+ pub fn new() -> A {
+ A
+ }
+ }
+};
+pub const X: () = {
+ impl A {
+ pub fn bar(&self) {}
+ }
+};
+
+fn foo() {
+ impl A {
+ pub fn yoo() {}
+ }
+ const _: () = {
+ impl A {
+ pub fn yuu() {}
+ }
+ };
+}
--- /dev/null
+#![crate_name = "foo"]
+
+#![feature(associated_const_equality)]
+pub enum ParseMode {
+ Raw,
+}
+pub trait Parse {
+ const PARSE_MODE: ParseMode;
+}
+pub trait RenderRaw {}
+
+// @hasraw foo/trait.RenderRaw.html 'impl'
+// @hasraw foo/trait.RenderRaw.html 'ParseMode::Raw'
+impl<T: Parse<PARSE_MODE = { ParseMode::Raw }>> RenderRaw for T {}
--- /dev/null
+// Regression test for https://github.com/rust-lang/rust/issues/105677.
+// This test ensures that the "Read more" link is only generated when
+// there is actually more documentation to read after the short summary.
+
+#![crate_name = "foo"]
+
+pub trait MyFrom {
+ /// # Hello
+ /// ## Yolo
+ /// more!
+ fn try_from1();
+ /// a
+ /// b
+ /// c
+ fn try_from2();
+ /// a
+ ///
+ /// b
+ ///
+ /// c
+ fn try_from3();
+}
+
+pub struct NonZero;
+
+// @has 'foo/struct.NonZero.html'
+impl MyFrom for NonZero {
+ // @matches - '//*[@class="docblock"]' '^Hello Read more$'
+ fn try_from1() {}
+ // @matches - '//*[@class="docblock"]' '^a\sb\sc$'
+ fn try_from2() {}
+ // @matches - '//*[@class="docblock"]' '^a Read more$'
+ fn try_from3() {}
+}
// @has - '//*[@id="method.b"]/../../div[@class="docblock"]' 'These docs contain'
// @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a' 'reference link'
// @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a/@href' 'https://example.com'
- // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a' 'Read more'
- // @has - '//*[@id="method.b"]/../../div[@class="docblock"]/a/@href' 'trait.Trait.html#tymethod.b'
fn b() {}
// @!has - '//*[@id="method.c"]/../../div[@class="docblock"]' 'code block'
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0457`.
// compile-flags:-C panic=abort
// aux-build:helper.rs
-#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)]
+#![feature(rustc_private, lang_items)]
#![feature(alloc_error_handler)]
#![no_std]
+#![no_main]
extern crate alloc;
extern crate libc;
#[no_mangle]
pub fn __aeabi_unwind_cpp_pr1() {}
-use core::ptr::null_mut;
-use core::alloc::{GlobalAlloc, Layout};
use alloc::boxed::Box;
+use alloc::string::ToString;
+use core::alloc::{GlobalAlloc, Layout};
+use core::ptr::null_mut;
extern crate helper;
struct MyAllocator;
#[alloc_error_handler]
-fn my_oom(layout: Layout) -> !
-{
+fn my_oom(layout: Layout) -> ! {
use alloc::fmt::write;
unsafe {
let size = layout.size();
let mut s = alloc::string::String::new();
write(&mut s, format_args!("My OOM: failed to allocate {} bytes!\n", size)).unwrap();
- let s = s.as_str();
- libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
+ libc::write(libc::STDERR_FILENO, s.as_ptr() as *const _, s.len());
libc::exit(0)
}
}
unsafe impl GlobalAlloc for MyAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- if layout.size() < 4096 {
- libc::malloc(layout.size()) as _
- } else {
- null_mut()
- }
+ if layout.size() < 4096 { libc::malloc(layout.size()) as _ } else { null_mut() }
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}
#[panic_handler]
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
unsafe {
- if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
- const PSTR: &str = "panic occurred: ";
- const CR: &str = "\n";
- libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
- libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
- libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
- }
- if let Some(args) = panic_info.message() {
- let mut s = alloc::string::String::new();
- alloc::fmt::write(&mut s, *args).unwrap();
- let s = s.as_str();
- const PSTR: &str = "panic occurred: ";
- const CR: &str = "\n";
- libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
- libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
- libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
- } else {
- const PSTR: &str = "panic occurred\n";
- libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
- }
+ let s = panic_info.to_string();
+ const PSTR: &str = "panic occurred: ";
+ const CR: &str = "\n";
+ libc::write(libc::STDERR_FILENO, PSTR.as_ptr() as *const _, PSTR.len());
+ libc::write(libc::STDERR_FILENO, s.as_ptr() as *const _, s.len());
+ libc::write(libc::STDERR_FILENO, CR.as_ptr() as *const _, CR.len());
libc::exit(1)
}
}
// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
// unwind. So, for this test case we will define the symbol.
#[lang = "eh_personality"]
-extern fn rust_eh_personality() {}
+extern "C" fn rust_eh_personality() {}
-#[derive(Debug)]
+#[derive(Default, Debug)]
struct Page(#[allow(unused_tuple_struct_fields)] [[u64; 32]; 16]);
-#[start]
-pub fn main(_argc: isize, _argv: *const *const u8) -> isize {
- let zero = Box::<Page>::new_zeroed();
- let zero = unsafe { zero.assume_init() };
+#[no_mangle]
+fn main(_argc: i32, _argv: *const *const u8) -> isize {
+ let zero = Box::<Page>::new(Default::default());
helper::work_with(&zero);
1
}
// only-linux
// compile-flags:-C panic=abort
// aux-build:helper.rs
-// gate-test-default_alloc_error_handler
-#![feature(start, rustc_private, new_uninit, panic_info_message, lang_items)]
-#![feature(default_alloc_error_handler)]
+#![feature(rustc_private, lang_items)]
#![no_std]
+#![no_main]
extern crate alloc;
extern crate libc;
pub fn __aeabi_unwind_cpp_pr1() {}
use alloc::boxed::Box;
+use alloc::string::ToString;
use core::alloc::{GlobalAlloc, Layout};
use core::ptr::null_mut;
unsafe impl GlobalAlloc for MyAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- if layout.size() < 4096 {
- libc::malloc(layout.size()) as _
- } else {
- null_mut()
- }
+ if layout.size() < 4096 { libc::malloc(layout.size()) as _ } else { null_mut() }
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}
#[panic_handler]
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
unsafe {
- if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
- const PSTR: &str = "panic occurred: ";
- const CR: &str = "\n";
- libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
- libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
- libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
- }
- if let Some(args) = panic_info.message() {
- let mut s = alloc::string::String::new();
- alloc::fmt::write(&mut s, *args).unwrap();
- let s = s.as_str();
- const PSTR: &str = "panic occurred: ";
- const CR: &str = "\n";
- libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
- libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
- libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
- } else {
- const PSTR: &str = "panic occurred\n";
- libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
- }
+ let s = panic_info.to_string();
+ const PSTR: &str = "panic occurred: ";
+ const CR: &str = "\n";
+ libc::write(libc::STDERR_FILENO, PSTR.as_ptr() as *const _, PSTR.len());
+ libc::write(libc::STDERR_FILENO, s.as_ptr() as *const _, s.len());
+ libc::write(libc::STDERR_FILENO, CR.as_ptr() as *const _, CR.len());
libc::exit(0)
}
}
// in these libraries will refer to `rust_eh_personality` if LLVM can not *prove* the contents won't
// unwind. So, for this test case we will define the symbol.
#[lang = "eh_personality"]
-extern fn rust_eh_personality() {}
+extern "C" fn rust_eh_personality() {}
-#[derive(Debug)]
+#[derive(Default, Debug)]
struct Page(#[allow(unused_tuple_struct_fields)] [[u64; 32]; 16]);
-#[start]
-pub fn main(_argc: isize, _argv: *const *const u8) -> isize {
- let zero = Box::<Page>::new_zeroed();
- let zero = unsafe { zero.assume_init() };
+#[no_mangle]
+fn main(_argc: i32, _argv: *const *const u8) -> isize {
+ let zero = Box::<Page>::new(Default::default());
helper::work_with(&zero);
1
}
| ^^^
help: provide the argument
|
-LL | closure(/* value */);
- | ~~~~~~~~~~~~~
+LL | closure(/* x */);
+ | ~~~~~~~~~
error: aborting due to 6 previous errors
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0472`.
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0472`.
--- /dev/null
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features, dead_code)]
+#![deny(non_camel_case_types)]
+
+struct S;
+
+impl S {
+ type typ = ();
+ //~^ ERROR associated type `typ` should have an upper camel case name
+}
+
+fn main() {}
--- /dev/null
+error: associated type `typ` should have an upper camel case name
+ --> $DIR/style.rs:8:10
+ |
+LL | type typ = ();
+ | ^^^ help: convert the identifier to upper camel case: `Typ`
+ |
+note: the lint level is defined here
+ --> $DIR/style.rs:3:9
+ |
+LL | #![deny(non_camel_case_types)]
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
LL | | panic!()
LL | | }
| |_^
- = note: required because it captures the following types: `&mut Context<'_>`, `Option<bool>`, `impl Future<Output = !>`, `()`
+ = note: required because it captures the following types: `ResumeTy`, `Option<bool>`, `impl Future<Output = !>`, `()`
note: required because it's used within this `async fn` body
--> $DIR/async-await-let-else.rs:21:32
|
--- /dev/null
+// edition:2018
+
+fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
+
+fn main() {
+ // A normal future created by an async block takes a `&mut Context<'_>` argument.
+ // That should not leak through to the whole async block.
+ is_unwindsafe(async {
+ async {}.await; // this needs an inner await point
+ });
+
+ is_unwindsafe(async {
+ //~^ ERROR the type `&mut Context<'_>` may not be safely transferred across an unwind boundary
+ use std::ptr::null;
+ use std::task::{Context, RawWaker, RawWakerVTable, Waker};
+ let waker = unsafe {
+ Waker::from_raw(RawWaker::new(
+ null(),
+ &RawWakerVTable::new(|_| todo!(), |_| todo!(), |_| todo!(), |_| todo!()),
+ ))
+ };
+ let mut cx = Context::from_waker(&waker);
+ let cx_ref = &mut cx;
+
+ async {}.await; // this needs an inner await point
+
+ // in this case, `&mut Context<'_>` is *truly* alive across an await point
+ drop(cx_ref);
+ });
+}
--- /dev/null
+error[E0277]: the type `&mut Context<'_>` may not be safely transferred across an unwind boundary
+ --> $DIR/async-is-unwindsafe.rs:12:19
+ |
+LL | is_unwindsafe(async {
+ | ___________________^
+LL | |
+LL | | use std::ptr::null;
+LL | | use std::task::{Context, RawWaker, RawWakerVTable, Waker};
+... |
+LL | | drop(cx_ref);
+LL | | });
+ | | ^
+ | | |
+ | |_____`&mut Context<'_>` may not be safely transferred across an unwind boundary
+ | within this `[async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6]`
+ |
+ = help: within `[async block@$DIR/async-is-unwindsafe.rs:12:19: 29:6]`, the trait `UnwindSafe` is not implemented for `&mut Context<'_>`
+ = note: `UnwindSafe` is implemented for `&std::task::Context<'_>`, but not for `&mut std::task::Context<'_>`
+note: future does not implement `UnwindSafe` as this value is used across an await
+ --> $DIR/async-is-unwindsafe.rs:25:17
+ |
+LL | let cx_ref = &mut cx;
+ | ------ has type `&mut Context<'_>` which does not implement `UnwindSafe`
+LL |
+LL | async {}.await; // this needs an inner await point
+ | ^^^^^^ await occurs here, with `cx_ref` maybe used later
+...
+LL | });
+ | - `cx_ref` is later dropped here
+note: required by a bound in `is_unwindsafe`
+ --> $DIR/async-is-unwindsafe.rs:3:26
+ |
+LL | fn is_unwindsafe(_: impl std::panic::UnwindSafe) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_unwindsafe`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// edition:2021
+
+#![feature(async_fn_in_trait)]
+//~^ WARN the feature `async_fn_in_trait` is incomplete
+
+trait MyTrait {
+ async fn bar(&abc self);
+ //~^ ERROR expected identifier, found keyword `self`
+ //~| ERROR expected one of `:`, `@`, or `|`, found keyword `self`
+}
+
+impl MyTrait for () {
+ async fn bar(&self) {}
+}
+
+fn main() {}
--- /dev/null
+error: expected identifier, found keyword `self`
+ --> $DIR/bad-signatures.rs:7:23
+ |
+LL | async fn bar(&abc self);
+ | ^^^^ expected identifier, found keyword
+
+error: expected one of `:`, `@`, or `|`, found keyword `self`
+ --> $DIR/bad-signatures.rs:7:23
+ |
+LL | async fn bar(&abc self);
+ | -----^^^^
+ | | |
+ | | expected one of `:`, `@`, or `|`
+ | help: declare the type after the parameter binding: `<identifier>: <type>`
+
+warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/bad-signatures.rs:3:12
+ |
+LL | #![feature(async_fn_in_trait)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
|
LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `&mut Context<'_>`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `Ready<i32>`
+ = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `Ready<i32>`
note: required because it's used within this `async` block
--> $DIR/issue-68112.rs:60:20
|
|
LL | fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: required because it captures the following types: `&mut Context<'_>`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `i32`, `Ready<i32>`
+ = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Arc<RefCell<i32>>>`, `()`, `i32`, `Ready<i32>`
note: required because it's used within this `async` block
--> $DIR/issue-68112.rs:60:20
|
|
= 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
- = note: requirement occurs because of a mutable reference to `Context<'_>`
- = note: mutable references are invariant over their type parameter
- = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: aborting due to previous error
| ___________________________________________________________________^
LL | | }
| |_^
- = note: required because it captures the following types: `&mut Context<'_>`, `impl Future<Output = ()>`, `()`
+ = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async` block
--> $DIR/issue-70935-complex-spans.rs:16:5
|
--- /dev/null
+// edition:2021
+
+async fn foo() {}
+
+fn main() {
+ std::mem::size_of_val(foo());
+ //~^ ERROR: mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-102206.rs:6:27
+ |
+LL | std::mem::size_of_val(foo());
+ | --------------------- ^^^^^
+ | | |
+ | | expected reference, found opaque type
+ | | help: consider borrowing here: `&foo()`
+ | arguments to this function are incorrect
+ |
+note: while checking the return type of the `async fn`
+ --> $DIR/issue-102206.rs:3:16
+ |
+LL | async fn foo() {}
+ | ^ checked the `Output` of this `async fn`, found opaque type
+ = note: expected reference `&_`
+ found opaque type `impl Future<Output = ()>`
+note: function defined here
+ --> $SRC_DIR/core/src/mem/mod.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `NotSend`
= note: required because it appears within the type `(NotSend,)`
- = note: required because it captures the following types: `&mut Context<'_>`, `(NotSend,)`, `()`, `impl Future<Output = ()>`
+ = note: required because it captures the following types: `ResumeTy`, `(NotSend,)`, `()`, `impl Future<Output = ()>`
note: required because it's used within this `async fn` body
--> $DIR/partial-drop-partial-reinit.rs:31:16
|
|
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `NotSend`
= note: required because it appears within the type `(NotSend,)`
- = note: required because it captures the following types: `&mut Context<'_>`, `(NotSend,)`, `impl Future<Output = ()>`, `()`
+ = note: required because it captures the following types: `ResumeTy`, `(NotSend,)`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async fn` body
--> $DIR/partial-drop-partial-reinit.rs:31:16
|
fn main() {
let _ = #[track_caller] async || {
//~^ ERROR `#[track_caller]` on closures is currently unstable [E0658]
- //~| ERROR `#[track_caller]` on closures is currently unstable [E0658]
};
}
= note: see issue #87417 <https://github.com/rust-lang/rust/issues/87417> for more information
= help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
-error[E0658]: `#[track_caller]` on closures is currently unstable
- --> $DIR/async-closure-gate.rs:6:38
- |
-LL | let _ = #[track_caller] async || {
- | ______________________________________^
-LL | |
-LL | |
-LL | | };
- | |_____^
- |
- = note: see issue #87417 <https://github.com/rust-lang/rust/issues/87417> for more information
- = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+warning: `#[track_caller]` on async functions is a no-op
+ --> $DIR/panic-track-caller.rs:50:1
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+LL | / async fn bar_track_caller() {
+LL | | panic!()
+LL | | }
+ | |_- this function will not propagate the caller location
+ |
+ = note: see issue #87417 <https://github.com/rust-lang/rust/issues/87417> for more information
+ = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
+ = note: `#[warn(ungated_async_fn_track_caller)]` on by default
+
+warning: `#[track_caller]` on async functions is a no-op
+ --> $DIR/panic-track-caller.rs:62:5
+ |
+LL | #[track_caller]
+ | ^^^^^^^^^^^^^^^
+LL | / async fn bar_assoc() {
+LL | | panic!();
+LL | | }
+ | |_____- this function will not propagate the caller location
+ |
+ = note: see issue #87417 <https://github.com/rust-lang/rust/issues/87417> for more information
+ = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable
+
+warning: 2 warnings emitted
+
// run-pass
// edition:2021
+// revisions: feat nofeat
// needs-unwind
-#![feature(closure_track_caller, async_closure, stmt_expr_attributes)]
+#![feature(async_closure, stmt_expr_attributes)]
+#![cfg_attr(feat, feature(closure_track_caller))]
use std::future::Future;
use std::panic;
bar().await
}
-#[track_caller]
+#[track_caller] //[nofeat]~ WARN `#[track_caller]` on async functions is a no-op
async fn bar_track_caller() {
panic!()
}
struct Foo;
impl Foo {
- #[track_caller]
+ #[track_caller] //[nofeat]~ WARN `#[track_caller]` on async functions is a no-op
async fn bar_assoc() {
panic!();
}
Foo::bar_assoc().await
}
+// Since compilation is expected to fail for this fn when using
+// `nofeat`, we test that separately in `async-closure-gate.rs`
+#[cfg(feat)]
async fn foo_closure() {
let c = #[track_caller] async || {
panic!();
}
fn main() {
- assert_eq!(panicked_at(|| block_on(foo())), 41);
- assert_eq!(panicked_at(|| block_on(foo_track_caller())), 54);
- assert_eq!(panicked_at(|| block_on(foo_assoc())), 67);
- assert_eq!(panicked_at(|| block_on(foo_closure())), 74);
+ assert_eq!(panicked_at(|| block_on(foo())), 43);
+
+ #[cfg(feat)]
+ assert_eq!(panicked_at(|| block_on(foo_track_caller())), 56);
+ #[cfg(nofeat)]
+ assert_eq!(panicked_at(|| block_on(foo_track_caller())), 52);
+
+ #[cfg(feat)]
+ assert_eq!(panicked_at(|| block_on(foo_assoc())), 69);
+ #[cfg(nofeat)]
+ assert_eq!(panicked_at(|| block_on(foo_assoc())), 64);
+
+ #[cfg(feat)]
+ assert_eq!(panicked_at(|| block_on(foo_closure())), 79);
}
--- /dev/null
+// check-pass
+
+trait FnOnceForGenericRef<T>: FnOnce(&T) -> Self::FnOutput {
+ type FnOutput;
+}
+
+impl<T, R, F: FnOnce(&T) -> R> FnOnceForGenericRef<T> for F {
+ type FnOutput = R;
+}
+
+struct Data<T, D: FnOnceForGenericRef<T>> {
+ value: Option<T>,
+ output: Option<D::FnOutput>,
+}
+
+impl<T, D: FnOnceForGenericRef<T>> Data<T, D> {
+ fn new(value: T, f: D) -> Self {
+ let output = f(&value);
+ Self {
+ value: Some(value),
+ output: Some(output),
+ }
+ }
+}
+
+fn test() {
+ Data::new(String::new(), |_| {});
+}
+
+fn main() {}
--- /dev/null
+// run-pass
+// ^-- The above is needed as this issue is related to LLVM/codegen.
+// min-llvm-version:15.0.0
+// ^-- The above is needed as this issue is fixed by the opaque pointers.
+
+fn main() {
+ type_error(|x| &x);
+}
+
+fn type_error<T>(
+ _selector: for<'a> fn(&'a Vec<Box<dyn for<'b> Fn(&'b u8)>>) -> &'a Vec<Box<dyn Fn(T)>>,
+) {
+}
+++ /dev/null
-error: type parameters must be declared prior to const parameters
- --> $DIR/complex-unord-param.rs:8:41
- |
-LL | struct NestedArrays<'a, const N: usize, A: 'a, const M: usize, T:'a =u32> {
- | ---------------------^----------------------^--------- help: reorder the parameters: lifetimes, then types, then consts: `<'a, A: 'a, T: 'a = u32, const N: usize, const M: usize>`
-
-error: aborting due to previous error
-
--- /dev/null
+// check-pass
+
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+#![deny(const_evaluatable_unchecked)]
+
+pub struct If<const CONDITION: bool>;
+pub trait True {}
+impl True for If<true> {}
+
+pub struct FixedI8<const FRAC: u32> {
+ pub bits: i8,
+}
+
+impl<const FRAC_LHS: u32, const FRAC_RHS: u32> PartialEq<FixedI8<FRAC_RHS>> for FixedI8<FRAC_LHS>
+where
+ If<{ FRAC_RHS <= 8 }>: True,
+{
+ fn eq(&self, _rhs: &FixedI8<FRAC_RHS>) -> bool {
+ unimplemented!()
+ }
+}
+
+impl<const FRAC: u32> PartialEq<i8> for FixedI8<FRAC> {
+ fn eq(&self, rhs: &i8) -> bool {
+ let rhs_as_fixed = FixedI8::<0> { bits: *rhs };
+ PartialEq::eq(self, &rhs_as_fixed)
+ }
+}
+
+fn main() {}
+++ /dev/null
-error: type parameters must be declared prior to const parameters
- --> $DIR/type-after-const-ok.rs:8:26
- |
-LL | struct A<const N: usize, T>(T);
- | -----------------^- help: reorder the parameters: lifetimes, then types, then consts: `<T, const N: usize>`
-
-error: aborting due to previous error
-
//~^ERROR: evaluation of constant value failed
};
const _BAD2: () = {
- intrinsics::assert_uninit_valid::<&'static i32>();
+ intrinsics::assert_mem_uninitialized_valid::<&'static i32>();
//~^ERROR: evaluation of constant value failed
};
const _BAD3: () = {
error[E0080]: evaluation of constant value failed
--> $DIR/assert-type-intrinsics.rs:16:9
|
-LL | intrinsics::assert_uninit_valid::<&'static i32>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `&i32` uninitialized, which is invalid
+LL | intrinsics::assert_mem_uninitialized_valid::<&'static i32>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ aborted execution: attempted to leave type `&i32` uninitialized, which is invalid
error[E0080]: evaluation of constant value failed
--> $DIR/assert-type-intrinsics.rs:20:9
--- /dev/null
+// compile-flags: -Zmir-opt-level=3
+// check-pass
+
+#![feature(generic_const_exprs)]
+//~^ WARN the feature `generic_const_exprs` is incomplete
+
+#[inline(always)]
+fn from_fn_1<const N: usize, F: FnMut(usize) -> f32>(mut f: F) -> [f32; N] {
+ let mut result = [0.0; N];
+ let mut i = 0;
+ while i < N {
+ result[i] = f(i);
+ i += 1;
+ }
+ result
+}
+
+pub struct TestArray<const N: usize>
+where
+ [(); N / 2]:,
+{
+ array: [f32; N / 2],
+}
+
+impl<const N: usize> TestArray<N>
+where
+ [(); N / 2]:,
+{
+ fn from_fn_2<F: FnMut(usize) -> f32>(f: F) -> Self {
+ Self { array: from_fn_1(f) }
+ }
+}
+
+fn main() {
+ TestArray::<4>::from_fn_2(|i| 0.0);
+}
--- /dev/null
+warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/issue-104396.rs:4:12
+ |
+LL | #![feature(generic_const_exprs)]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
+ = note: `#[warn(incomplete_features)]` on by default
+
+warning: 1 warning emitted
+
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0320`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0320`.
error: aborting due to 2 previous errors
+For more information about this error, try `rustc --explain E0320`.
--- /dev/null
+#![feature(dyn_star)]
+#![allow(incomplete_features)]
+
+trait Tr {}
+
+fn f(x: dyn* Tr) -> usize {
+ x as usize
+ //~^ ERROR casting `(dyn* Tr + 'static)` as `usize` is invalid
+}
+
+fn main() {}
--- /dev/null
+error[E0606]: casting `(dyn* Tr + 'static)` as `usize` is invalid
+ --> $DIR/dyn-to-rigid.rs:7:5
+ |
+LL | x as usize
+ | ^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0606`.
| ^^^
help: provide the argument
|
-LL | let a = f(/* value */);
- | ~~~~~~~~~~~~~
+LL | let a = f(/* x */);
+ | ~~~~~~~~~
error[E0057]: this function takes 1 argument but 2 arguments were supplied
--> $DIR/E0057.rs:5:13
--- /dev/null
+#![feature(coerce_unsized)]
+use std::ops::CoerceUnsized;
+
+pub struct Foo<T: ?Sized> {
+ field_with_unsized_type: T,
+}
+
+pub struct Bar<T: ?Sized> {
+ field_with_unsized_type: T,
+}
+
+impl<T, U> CoerceUnsized<Bar<U>> for Foo<T> where T: CoerceUnsized<U> {} //~ ERROR E0377
+
+fn main() {}
--- /dev/null
+error[E0377]: the trait `CoerceUnsized` may only be implemented for a coercion between structures with the same definition; expected `Foo`, found `Bar`
+ --> $DIR/E0377.rs:12:1
+ |
+LL | impl<T, U> CoerceUnsized<Bar<U>> for Foo<T> where T: CoerceUnsized<U> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0377`.
--- /dev/null
+// aux-build:found-staticlib.rs
+
+// normalize-stderr-test: "\.nll/" -> "/"
+// normalize-stderr-test: "\\\?\\" -> ""
+// normalize-stderr-test: "(lib)?found_staticlib\.[a-z]+" -> "libfound_staticlib.somelib"
+
+extern crate found_staticlib; //~ ERROR E0462
+
+fn main() {
+ found_staticlib::foo();
+}
--- /dev/null
+error[E0462]: found staticlib `found_staticlib` instead of rlib or dylib
+ --> $DIR/E0462.rs:7:1
+ |
+LL | extern crate found_staticlib;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: the following crate versions were found:
+ crate `found_staticlib`: $TEST_BUILD_DIR/error-codes/E0462/auxiliary/libfound_staticlib.somelib
+ = help: please recompile that crate using --crate-type lib
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0462`.
--- /dev/null
+// no-prefer-dynamic
+#![crate_type = "staticlib"]
+
+pub fn foo() {}
--- /dev/null
+fn main() {
+ let needlesArr: Vec<char> = vec!['a', 'f'];
+ needlesArr.iter().fold(|x, y| {
+ //~^ ERROR this function takes 2 arguments but 1 argument was supplied
+ });
+}
--- /dev/null
+error[E0061]: this function takes 2 arguments but 1 argument was supplied
+ --> $DIR/issue-3044.rs:3:23
+ |
+LL | needlesArr.iter().fold(|x, y| {
+ | _______________________^^^^-
+LL | |
+LL | | });
+ | |______- an argument is missing
+ |
+note: associated function defined here
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+help: provide the argument
+ |
+LL ~ needlesArr.iter().fold(|x, y| {
+LL +
+LL ~ }, /* f */);
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0061`.
--- /dev/null
+// run-pass
+fn example_err(prog: &str, arg: &str) {
+ println!("{}: {}", prog, arg)
+}
+
+fn exit<F>(print: F, prog: &str, arg: &str) where F: FnOnce(&str, &str) {
+ print(prog, arg);
+}
+
+struct X<F> where F: FnOnce(&str, &str) {
+ err: F,
+}
+
+impl<F> X<F> where F: FnOnce(&str, &str) {
+ pub fn boom(self) {
+ exit(self.err, "prog", "arg");
+ }
+}
+
+pub fn main(){
+ let val = X {
+ err: example_err,
+ };
+ val.boom();
+}
--- /dev/null
+// For `Send` generators, suggest a `T: Sync` requirement for `&T` upvars,
+// and suggest a `T: Send` requirement for `&mut T` upvars.
+
+#![feature(generators)]
+
+fn assert_send<T: Send>(_: T) {}
+//~^ NOTE required by a bound in `assert_send`
+//~| NOTE required by this bound in `assert_send`
+//~| NOTE required by a bound in `assert_send`
+//~| NOTE required by this bound in `assert_send`
+
+fn main() {
+ let x: &*mut () = &std::ptr::null_mut();
+ let y: &mut *mut () = &mut std::ptr::null_mut();
+ assert_send(move || {
+ //~^ ERROR generator cannot be sent between threads safely
+ //~| NOTE generator is not `Send`
+ yield;
+ let _x = x;
+ });
+ //~^^ NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+ //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync`
+ assert_send(move || {
+ //~^ ERROR generator cannot be sent between threads safely
+ //~| NOTE generator is not `Send`
+ yield;
+ let _y = y;
+ });
+ //~^^ NOTE captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send`
+ //~| NOTE has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send`
+}
--- /dev/null
+error: generator cannot be sent between threads safely
+ --> $DIR/ref-upvar-not-send.rs:15:17
+ |
+LL | assert_send(move || {
+ | _________________^
+LL | |
+LL | |
+LL | | yield;
+LL | | let _x = x;
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: the trait `Sync` is not implemented for `*mut ()`
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+ --> $DIR/ref-upvar-not-send.rs:19:18
+ |
+LL | let _x = x;
+ | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync`
+note: required by a bound in `assert_send`
+ --> $DIR/ref-upvar-not-send.rs:6:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: generator cannot be sent between threads safely
+ --> $DIR/ref-upvar-not-send.rs:23:17
+ |
+LL | assert_send(move || {
+ | _________________^
+LL | |
+LL | |
+LL | | yield;
+LL | | let _y = y;
+LL | | });
+ | |_____^ generator is not `Send`
+ |
+ = help: within `[generator@$DIR/ref-upvar-not-send.rs:23:17: 23:24]`, the trait `Send` is not implemented for `*mut ()`
+note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send`
+ --> $DIR/ref-upvar-not-send.rs:27:18
+ |
+LL | let _y = y;
+ | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send`
+note: required by a bound in `assert_send`
+ --> $DIR/ref-upvar-not-send.rs:6:19
+ |
+LL | fn assert_send<T: Send>(_: T) {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// incremental
+// edition:2021
+// compile-flags: -Zdrop-tracking
+
+fn main() {
+ let _ = async {
+ let s = std::array::from_fn(|_| ()).await;
+ //~^ ERROR `[(); _]` is not a future
+ //~| ERROR type inside `async` block must be known in this context
+ //~| ERROR type inside `async` block must be known in this context
+ //~| ERROR type inside `async` block must be known in this context
+ //~| ERROR type inside `async` block must be known in this context
+ //~| ERROR type inside `async` block must be known in this context
+ };
+}
--- /dev/null
+error[E0277]: `[(); _]` is not a future
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:44
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ---------------------------^^^^^^
+ | | |
+ | | `[(); _]` is not a future
+ | | help: remove the `.await`
+ | this call returns `[(); _]`
+ |
+ = help: the trait `Future` is not implemented for `[(); _]`
+ = note: [(); _] must be a future or must implement `IntoFuture` to be awaited
+ = note: required for `[(); _]` to implement `IntoFuture`
+
+error[E0698]: type inside `async` block must be known in this context
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:17
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `from_fn`
+ |
+note: the type is part of the `async` block because of this `await`
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:44
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^
+
+error[E0698]: type inside `async` block must be known in this context
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:17
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `from_fn`
+ |
+note: the type is part of the `async` block because of this `await`
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:44
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^
+
+error[E0698]: type inside `async` block must be known in this context
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:17
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `from_fn`
+ |
+note: the type is part of the `async` block because of this `await`
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:44
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^
+
+error[E0698]: type inside `async` block must be known in this context
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:17
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `from_fn`
+ |
+note: the type is part of the `async` block because of this `await`
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:44
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^
+
+error[E0698]: type inside `async` block must be known in this context
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:17
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of const parameter `N` declared on the function `from_fn`
+ |
+note: the type is part of the `async` block because of this `await`
+ --> $DIR/unresolved-ct-var-drop-tracking.rs:7:44
+ |
+LL | let s = std::array::from_fn(|_| ()).await;
+ | ^^^^^^
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0277, E0698.
+For more information about an error, try `rustc --explain E0277`.
| ^ ----
help: provide the argument
|
-LL | f(&[f(/* value */)]);
- | ~~~~~~~~~~~~~
+LL | f(&[f(/* i */)]);
+ | ~~~~~~~~~
error: aborting due to previous error
--- /dev/null
+#![deny(implied_bounds_entailment)]
+
+trait Project {
+ type Ty;
+}
+impl Project for &'_ &'_ () {
+ type Ty = ();
+}
+trait Trait {
+ fn get<'s>(s: &'s str, _: ()) -> &'static str;
+}
+impl Trait for () {
+ fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+ //~^ ERROR impl method assumes more implied bounds than the corresponding trait method
+ //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ s
+ }
+}
+fn main() {
+ let val = <() as Trait>::get(&String::from("blah blah blah"), ());
+ println!("{}", val);
+}
--- /dev/null
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:13:5
+ |
+LL | fn get<'s>(s: &'s str, _: <&'static &'s () as Project>::Ty) -> &'static str {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility-unnormalized.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+#![deny(implied_bounds_entailment)]
+
+use std::cell::RefCell;
+
+pub struct MessageListeners<'a> {
+ listeners: RefCell<Vec<Box<dyn FnMut(()) + 'a>>>,
+}
+
+pub trait MessageListenersInterface {
+ fn listeners<'c>(&'c self) -> &'c MessageListeners<'c>;
+}
+
+impl<'a> MessageListenersInterface for MessageListeners<'a> {
+ fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+ //~^ ERROR impl method assumes more implied bounds than the corresponding trait method
+ //~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ self
+ }
+}
+
+fn main() {}
--- /dev/null
+error: impl method assumes more implied bounds than the corresponding trait method
+ --> $DIR/impl-implied-bounds-compatibility.rs:14:5
+ |
+LL | fn listeners<'b>(&'b self) -> &'a MessageListeners<'b> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #105572 <https://github.com/rust-lang/rust/issues/105572>
+note: the lint level is defined here
+ --> $DIR/impl-implied-bounds-compatibility.rs:1:9
+ |
+LL | #![deny(implied_bounds_entailment)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
}
exported!(); //~ ERROR `exported` is ambiguous
- //~| ERROR `exported` is ambiguous
mod inner2 {
define_exported!();
= help: consider adding an explicit import of `exported` to disambiguate
= note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0659]: `exported` is ambiguous
- --> $DIR/local-modularized-tricky-fail-1.rs:28:1
- |
-LL | exported!();
- | ^^^^^^^^ ambiguous name
- |
- = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
-note: `exported` could refer to the macro defined here
- --> $DIR/local-modularized-tricky-fail-1.rs:5:5
- |
-LL | / macro_rules! exported {
-LL | | () => ()
-LL | | }
- | |_____^
-...
-LL | define_exported!();
- | ------------------ in this macro invocation
-note: `exported` could also refer to the macro imported here
- --> $DIR/local-modularized-tricky-fail-1.rs:22:5
- |
-LL | use inner1::*;
- | ^^^^^^^^^
- = help: consider adding an explicit import of `exported` to disambiguate
- = note: this error originates in the macro `define_exported` (in Nightly builds, run with -Z macro-backtrace for more info)
-
error[E0659]: `panic` is ambiguous
- --> $DIR/local-modularized-tricky-fail-1.rs:36:5
+ --> $DIR/local-modularized-tricky-fail-1.rs:35:5
|
LL | panic!();
| ^^^^^ ambiguous name
= note: this error originates in the macro `define_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0659]: `include` is ambiguous
- --> $DIR/local-modularized-tricky-fail-1.rs:47:1
+ --> $DIR/local-modularized-tricky-fail-1.rs:46:1
|
LL | include!();
| ^^^^^^^ ambiguous name
= help: use `crate::include` to refer to this macro unambiguously
= note: this error originates in the macro `define_include` (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0659`.
mod m2 {
use two_macros::*;
m! { //~ ERROR ambiguous
- //~| ERROR ambiguous
use foo::m;
}
}
|
= note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
note: `m` could refer to the macro imported here
- --> $DIR/macros.rs:18:13
+ --> $DIR/macros.rs:17:13
|
LL | use foo::m;
| ^^^^^^
= help: consider adding an explicit import of `m` to disambiguate
error[E0659]: `m` is ambiguous
- --> $DIR/macros.rs:16:5
- |
-LL | m! {
- | ^ ambiguous name
- |
- = note: ambiguous because of a conflict between a name from a glob import and a macro-expanded name in the same module during import or macro resolution
-note: `m` could refer to the macro imported here
- --> $DIR/macros.rs:18:13
- |
-LL | use foo::m;
- | ^^^^^^
-note: `m` could also refer to the macro imported here
- --> $DIR/macros.rs:15:9
- |
-LL | use two_macros::*;
- | ^^^^^^^^^^^^^
- = help: consider adding an explicit import of `m` to disambiguate
-
-error[E0659]: `m` is ambiguous
- --> $DIR/macros.rs:30:9
+ --> $DIR/macros.rs:29:9
|
LL | m! {
| ^ ambiguous name
|
= note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
note: `m` could refer to the macro imported here
- --> $DIR/macros.rs:31:17
+ --> $DIR/macros.rs:30:17
|
LL | use two_macros::n as m;
| ^^^^^^^^^^^^^^^^^^
note: `m` could also refer to the macro imported here
- --> $DIR/macros.rs:23:9
+ --> $DIR/macros.rs:22:9
|
LL | use two_macros::m;
| ^^^^^^^^^^^^^
= help: use `self::m` to refer to this macro unambiguously
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0659`.
--- /dev/null
+#![allow(unreachable_code)]
+
+use std::marker::PhantomData;
+use std::ops::Deref;
+use std::sync::Arc;
+
+pub struct Guard<T> {
+ _phantom: PhantomData<T>,
+}
+impl<T> Deref for Guard<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ unimplemented!()
+ }
+}
+
+pub struct DirectDeref<T>(T);
+impl<T> Deref for DirectDeref<Arc<T>> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ unimplemented!()
+ }
+}
+
+pub trait Access<T> {
+ type Guard: Deref<Target = T>;
+ fn load(&self) -> Self::Guard {
+ unimplemented!()
+ }
+}
+impl<T, A: Access<T>, P: Deref<Target = A>> Access<T> for P {
+ //~^ NOTE: required for `Arc<ArcSwapAny<Arc<usize>>>` to implement `Access<_>`
+ type Guard = A::Guard;
+}
+impl<T> Access<T> for ArcSwapAny<T> {
+ //~^ NOTE: multiple `impl`s satisfying `ArcSwapAny<Arc<usize>>: Access<_>` found
+ type Guard = Guard<T>;
+}
+impl<T> Access<T> for ArcSwapAny<Arc<T>> {
+ type Guard = DirectDeref<Arc<T>>;
+}
+
+pub struct ArcSwapAny<T> {
+ _phantom_arc: PhantomData<T>,
+}
+
+pub fn foo() {
+ let s: Arc<ArcSwapAny<Arc<usize>>> = unimplemented!();
+ let guard: Guard<Arc<usize>> = s.load();
+ //~^ ERROR: type annotations needed
+ //~| HELP: try using a fully qualified path to specify the expected types
+}
+
+fn main() {}
--- /dev/null
+error[E0283]: type annotations needed
+ --> $DIR/issue-80816.rs:49:38
+ |
+LL | let guard: Guard<Arc<usize>> = s.load();
+ | ^^^^
+ |
+note: multiple `impl`s satisfying `ArcSwapAny<Arc<usize>>: Access<_>` found
+ --> $DIR/issue-80816.rs:35:1
+ |
+LL | impl<T> Access<T> for ArcSwapAny<T> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl<T> Access<T> for ArcSwapAny<Arc<T>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: required for `Arc<ArcSwapAny<Arc<usize>>>` to implement `Access<_>`
+ --> $DIR/issue-80816.rs:31:45
+ |
+LL | impl<T, A: Access<T>, P: Deref<Target = A>> Access<T> for P {
+ | ^^^^^^^^^ ^
+help: try using a fully qualified path to specify the expected types
+ |
+LL | let guard: Guard<Arc<usize>> = <Arc<ArcSwapAny<Arc<usize>>> as Access<T>>::load(&s);
+ | ++++++++++++++++++++++++++++++++++++++++++++++++++ ~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0283`.
--- /dev/null
+// compile-flags: -Z trace-macros
+
+#![recursion_limit = "5"]
+
+fn main() {
+ macro_rules! stack {
+ ($overflow:expr) => {
+ print!(stack!($overflow));
+ //~^ ERROR recursion limit reached while expanding
+ //~| ERROR format argument must be a string literal
+ };
+ }
+
+ stack!("overflow");
+}
--- /dev/null
+error: recursion limit reached while expanding `$crate::format_args!`
+ --> $DIR/issue-41731-infinite-macro-print.rs:14:5
+ |
+LL | stack!("overflow");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "10"]` attribute to your crate (`issue_41731_infinite_macro_print`)
+ = note: this error originates in the macro `print` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: trace_macro
+ --> $DIR/issue-41731-infinite-macro-print.rs:14:5
+ |
+LL | stack!("overflow");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: expanding `stack! { "overflow" }`
+ = note: to `print! (stack! ("overflow")) ;`
+ = note: expanding `print! { stack! ("overflow") }`
+ = note: to `{ $crate :: io :: _print($crate :: format_args! (stack! ("overflow"))) ; }`
+ = note: expanding `stack! { "overflow" }`
+ = note: to `print! (stack! ("overflow")) ;`
+ = note: expanding `print! { stack! ("overflow") }`
+ = note: to `{ $crate :: io :: _print($crate :: format_args! (stack! ("overflow"))) ; }`
+
+error: format argument must be a string literal
+ --> $DIR/issue-41731-infinite-macro-print.rs:14:5
+ |
+LL | stack!("overflow");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `print` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: you might be missing a string literal to format with
+ |
+LL | print!("{}", stack!($overflow));
+ | +++++
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// compile-flags: -Z trace-macros
+
+#![recursion_limit = "5"]
+
+fn main() {
+ macro_rules! stack {
+ ($overflow:expr) => {
+ println!(stack!($overflow));
+ //~^ ERROR recursion limit reached while expanding
+ //~| ERROR format argument must be a string literal
+ };
+ }
+
+ stack!("overflow");
+}
--- /dev/null
+error: recursion limit reached while expanding `$crate::format_args_nl!`
+ --> $DIR/issue-41731-infinite-macro-println.rs:14:5
+ |
+LL | stack!("overflow");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "10"]` attribute to your crate (`issue_41731_infinite_macro_println`)
+ = note: this error originates in the macro `println` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: trace_macro
+ --> $DIR/issue-41731-infinite-macro-println.rs:14:5
+ |
+LL | stack!("overflow");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: expanding `stack! { "overflow" }`
+ = note: to `println! (stack! ("overflow")) ;`
+ = note: expanding `println! { stack! ("overflow") }`
+ = note: to `{ $crate :: io :: _print($crate :: format_args_nl! (stack! ("overflow"))) ; }`
+ = note: expanding `stack! { "overflow" }`
+ = note: to `println! (stack! ("overflow")) ;`
+ = note: expanding `println! { stack! ("overflow") }`
+ = note: to `{ $crate :: io :: _print($crate :: format_args_nl! (stack! ("overflow"))) ; }`
+
+error: format argument must be a string literal
+ --> $DIR/issue-41731-infinite-macro-println.rs:14:5
+ |
+LL | stack!("overflow");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in the macro `println` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: you might be missing a string literal to format with
+ |
+LL | println!("{}", stack!($overflow));
+ | +++++
+
+error: aborting due to 2 previous errors
+
-error: -Zbranch-protection is only supported on aarch64
+error: `-Zbranch-protection` is only supported on aarch64
error: aborting due to previous error
// [BADFLAGS] check-fail
// [BADFLAGS] needs-llvm-components: aarch64
// [BADTARGET] compile-flags: --target=x86_64-unknown-linux-gnu -Zbranch-protection=bti
-// [BADTARGET] build-fail
+// [BADTARGET] check-fail
// [BADTARGET] needs-llvm-components: x86
#![crate_type = "lib"]
+++ /dev/null
-#![crate_type="lib"]
-
-pub enum Foo {
- FooV { data: () }
-}
+++ /dev/null
-pub fn main() {
- const z: &'static isize = {
- static p: isize = 3;
- &p //~ ERROR constants cannot refer to statics
- };
-}
+++ /dev/null
-error[E0013]: constants cannot refer to statics
- --> $DIR/issue-18118-2.rs:4:10
- |
-LL | &p
- | ^
- |
- = help: consider extracting the value of the `static` to a `const`, and referring to that
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0013`.
+++ /dev/null
-pub fn main() {
- const z: &'static isize = {
- let p = 3;
- &p //~ ERROR `p` does not live long enough
- };
-}
+++ /dev/null
-error[E0597]: `p` does not live long enough
- --> $DIR/issue-18118.rs:4:9
- |
-LL | &p
- | ^^
- | |
- | borrowed value does not live long enough
- | using this value as a constant requires that `p` is borrowed for `'static`
-LL | };
- | - `p` dropped here while still borrowed
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0597`.
+++ /dev/null
-// check-pass
-trait A<T: A<T>> {}
-
-fn main() {}
+++ /dev/null
-macro_rules! foo {
- ($e:expr) => { $e.foo() }
- //~^ ERROR no method named `foo` found
-}
-
-fn main() {
- let a = 1i32;
- foo!(a);
-
- foo!(1i32.foo());
- //~^ ERROR no method named `foo` found
-}
+++ /dev/null
-error[E0599]: no method named `foo` found for type `i32` in the current scope
- --> $DIR/issue-25385.rs:2:23
- |
-LL | ($e:expr) => { $e.foo() }
- | ^^^ method not found in `i32`
-...
-LL | foo!(a);
- | ------- in this macro invocation
- |
- = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error[E0599]: no method named `foo` found for type `i32` in the current scope
- --> $DIR/issue-25385.rs:10:15
- |
-LL | foo!(1i32.foo());
- | ^^^ method not found in `i32`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0599`.
+++ /dev/null
-fn main() {
- let needlesArr: Vec<char> = vec!['a', 'f'];
- needlesArr.iter().fold(|x, y| {
- //~^ ERROR this function takes 2 arguments but 1 argument was supplied
- });
-}
+++ /dev/null
-error[E0061]: this function takes 2 arguments but 1 argument was supplied
- --> $DIR/issue-3044.rs:3:23
- |
-LL | needlesArr.iter().fold(|x, y| {
- | _______________________^^^^-
-LL | |
-LL | | });
- | |______- an argument is missing
- |
-note: associated function defined here
- --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
-help: provide the argument
- |
-LL ~ needlesArr.iter().fold(|x, y| {
-LL +
-LL ~ }, /* value */);
- |
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0061`.
+++ /dev/null
-// aux-build:issue-30535.rs
-
-extern crate issue_30535 as foo;
-
-fn bar(
- _: foo::Foo::FooV //~ ERROR expected type, found variant `foo::Foo::FooV`
-) {}
-
-fn main() {}
+++ /dev/null
-error[E0573]: expected type, found variant `foo::Foo::FooV`
- --> $DIR/issue-30535.rs:6:8
- |
-LL | _: foo::Foo::FooV
- | ^^^^^^^^^^^^^^
- | |
- | not a type
- | help: try using the variant's enum: `foo::Foo`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0573`.
+++ /dev/null
-// run-pass
-// Regression test for #36381. The monomorphization collector was asserting that
-// there are no projection types, but the `<&str as
-// StreamOnce>::Position` projection contained a late-bound region,
-// and we don't currently normalize in that case until the function is
-// actually invoked.
-
-pub trait StreamOnce {
- type Position;
-}
-
-impl<'a> StreamOnce for &'a str {
- type Position = usize;
-}
-
-pub fn parser<F>(_: F) {
-}
-
-fn follow(_: &str) -> <&str as StreamOnce>::Position {
- panic!()
-}
-
-fn main() {
- parser(follow);
-}
+++ /dev/null
-// run-pass
-fn example_err(prog: &str, arg: &str) {
- println!("{}: {}", prog, arg)
-}
-
-fn exit<F>(print: F, prog: &str, arg: &str) where F: FnOnce(&str, &str) {
- print(prog, arg);
-}
-
-struct X<F> where F: FnOnce(&str, &str) {
- err: F,
-}
-
-impl<F> X<F> where F: FnOnce(&str, &str) {
- pub fn boom(self) {
- exit(self.err, "prog", "arg");
- }
-}
-
-pub fn main(){
- let val = X {
- err: example_err,
- };
- val.boom();
-}
+++ /dev/null
-trait Dim {
- fn dim() -> usize;
-}
-
-enum Dim3 {}
-
-impl Dim for Dim3 {
- fn dim() -> usize {
- 3
- }
-}
-
-fn main() {
- let array: [usize; Dim3::dim()]
- //~^ ERROR E0015
- = [0; Dim3::dim()];
- //~^ ERROR E0015
-}
+++ /dev/null
-error[E0015]: cannot call non-const fn `<Dim3 as Dim>::dim` in constants
- --> $DIR/issue-39559-2.rs:14:24
- |
-LL | let array: [usize; Dim3::dim()]
- | ^^^^^^^^^^^
- |
- = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
-error[E0015]: cannot call non-const fn `<Dim3 as Dim>::dim` in constants
- --> $DIR/issue-39559-2.rs:16:15
- |
-LL | = [0; Dim3::dim()];
- | ^^^^^^^^^^^
- |
- = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0015`.
+++ /dev/null
-trait Dim {
- fn dim() -> usize;
-}
-
-enum Dim3 {}
-
-impl Dim for Dim3 {
- fn dim() -> usize {
- 3
- }
-}
-
-pub struct Vector<T, D: Dim> {
- entries: [T; D::dim()],
- //~^ ERROR generic parameters may not be used
- _dummy: D,
-}
-
-fn main() {}
+++ /dev/null
-error: generic parameters may not be used in const operations
- --> $DIR/issue-39559.rs:14:18
- |
-LL | entries: [T; D::dim()],
- | ^^^^^^ cannot perform const operation using `D`
- |
- = note: type parameters may not be used in const expressions
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: aborting due to previous error
-
+++ /dev/null
-// run-pass
-#![allow(unused_imports)]
-// This test checks for namespace pollution by private tests.
-// Tests used to marked as public causing name conflicts with normal
-// functions only in test builds.
-
-// compile-flags: --test
-
-mod a {
- pub fn foo() -> bool {
- true
- }
-}
-
-mod b {
- #[test]
- fn foo() {
- local_name(); // ensure the local name still works
- }
-
- #[test]
- fn local_name() {}
-}
-
-use a::*;
-use b::*;
-
-pub fn conflict() {
- let _: bool = foo();
-}
+++ /dev/null
-// run-pass
-#![allow(dead_code)]
-
-enum Enum {
- Foo { foo: usize },
- Bar { bar: usize }
-}
-
-fn fun1(e1: &Enum, e2: &Enum) -> usize {
- match (e1, e2) {
- (&Enum::Foo { foo: _ }, &Enum::Foo { foo: _ }) => 0,
- (&Enum::Foo { foo: _ }, &Enum::Bar { bar: _ }) => 1,
- (&Enum::Bar { bar: _ }, &Enum::Bar { bar: _ }) => 2,
- (&Enum::Bar { bar: _ }, &Enum::Foo { foo: _ }) => 3,
- }
-}
-
-fn fun2(e1: &Enum, e2: &Enum) -> usize {
- match (e1, e2) {
- (&Enum::Foo { foo: _ }, &Enum::Foo { foo: _ }) => 0,
- (&Enum::Foo { foo: _ }, _ ) => 1,
- (&Enum::Bar { bar: _ }, &Enum::Bar { bar: _ }) => 2,
- (&Enum::Bar { bar: _ }, _ ) => 3,
- }
-}
-
-pub fn main() {
- let foo = Enum::Foo { foo: 1 };
- let bar = Enum::Bar { bar: 1 };
-
- assert_eq!(fun1(&foo, &foo), 0);
- assert_eq!(fun1(&foo, &bar), 1);
- assert_eq!(fun1(&bar, &bar), 2);
- assert_eq!(fun1(&bar, &foo), 3);
-
- assert_eq!(fun2(&foo, &foo), 0);
- assert_eq!(fun2(&foo, &bar), 1); // fun2 returns 0
- assert_eq!(fun2(&bar, &bar), 2);
- assert_eq!(fun2(&bar, &foo), 3); // fun2 returns 2
-}
+++ /dev/null
-// build-pass
-#![allow(dead_code)]
-#![allow(improper_ctypes)]
-// pretty-expanded FIXME #23616
-#![allow(non_snake_case)]
-
-pub mod Bar {
- pub struct Foo {
- v: isize,
- }
-
- extern "C" {
- pub fn foo(v: *const Foo) -> Foo;
- }
-}
-
-pub fn main() {}
<i32 as Sum<&'a i32>>
<i32 as Sum>
note: the method call chain might not have had the expected associated types
- --> $DIR/invalid-iterator-chain.rs:20:14
+ --> $DIR/invalid-iterator-chain.rs:25:14
|
LL | vec![0, 1]
| ---------- this expression has type `Vec<{integer}>`
LL | .iter()
| ------ `Iterator::Item` is `&{integer}` here
LL | .map(|x| x * 2)
- | ^^^^^^^^^^^^^^ `Iterator::Item` changed to `{integer}` here
+ | -------------- `Iterator::Item` changed to `{integer}` here
LL | .map(|x| x as f64)
| ----------------- `Iterator::Item` changed to `f64` here
LL | .map(|x| x as i64)
<i32 as Sum<&'a i32>>
<i32 as Sum>
note: the method call chain might not have had the expected associated types
- --> $DIR/invalid-iterator-chain.rs:32:14
+ --> $DIR/invalid-iterator-chain.rs:33:14
|
LL | vec![0, 1]
| ---------- this expression has type `Vec<{integer}>`
LL | .iter()
| ------ `Iterator::Item` is `&{integer}` here
LL | .map(|x| x * 2)
- | ^^^^^^^^^^^^^^ `Iterator::Item` changed to `{integer}` here
+ | -------------- `Iterator::Item` changed to `{integer}` here
LL | .map(|x| x as f64)
| ^^^^^^^^^^^^^^^^^ `Iterator::Item` changed to `f64` here
LL | .filter(|x| *x > 0.0)
--- /dev/null
+// run-pass
+// Regression test for #36381. The monomorphization collector was asserting that
+// there are no projection types, but the `<&str as
+// StreamOnce>::Position` projection contained a late-bound region,
+// and we don't currently normalize in that case until the function is
+// actually invoked.
+
+pub trait StreamOnce {
+ type Position;
+}
+
+impl<'a> StreamOnce for &'a str {
+ type Position = usize;
+}
+
+pub fn parser<F>(_: F) {
+}
+
+fn follow(_: &str) -> <&str as StreamOnce>::Position {
+ panic!()
+}
+
+fn main() {
+ parser(follow);
+}
match foo::Foo::Foo {
Foo => {}
-//~^ ERROR variable `Foo` should have a snake case name
-//~^^ WARN `Foo` is named the same as one of the variants of the type `Foo`
-//~^^^ WARN unused variable: `Foo`
+ //~^ ERROR variable `Foo` should have a snake case name
+ //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
+ //~^^^ WARN unused variable: `Foo`
}
let Foo = foo::Foo::Foo;
//~^ ERROR variable `Foo` should have a snake case name
- //~^^ WARN `Foo` is named the same as one of the variants of the type `Foo`
+ //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`
fn in_param(Foo: foo::Foo) {}
//~^ ERROR variable `Foo` should have a snake case name
- //~^^ WARN `Foo` is named the same as one of the variants of the type `Foo`
+ //~^^ WARN `Foo` is named the same as one of the variants of the type `foo::Foo`
//~^^^ WARN unused variable: `Foo`
test(1);
-warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo`
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
|
LL | Foo => {}
- | ^^^ help: to match on the variant, qualify the path: `Foo::Foo`
+ | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
|
= note: `#[warn(bindings_with_variant_name)]` on by default
-warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo`
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:28:9
|
LL | let Foo = foo::Foo::Foo;
- | ^^^ help: to match on the variant, qualify the path: `Foo::Foo`
+ | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
-warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `Foo`
+warning[E0170]: pattern binding `Foo` is named the same as one of the variants of the type `foo::Foo`
--> $DIR/lint-uppercase-variables.rs:33:17
|
LL | fn in_param(Foo: foo::Foo) {}
- | ^^^ help: to match on the variant, qualify the path: `Foo::Foo`
+ | ^^^ help: to match on the variant, qualify the path: `foo::Foo::Foo`
warning: unused variable: `Foo`
--> $DIR/lint-uppercase-variables.rs:22:9
--- /dev/null
+#![feature(generators)]
+
+fn main() {
+ let _generator = || {
+ yield ((), ((), ()));
+ yield ((), ());
+ //~^ ERROR mismatched types
+ };
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/tuple-mismatch.rs:6:20
+ |
+LL | yield ((), ());
+ | ^^ expected tuple, found `()`
+ |
+ = note: expected tuple `((), ())`
+ found unit type `()`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// Auxiliary crate for test issue-105637: the LTOed dylib which had duplicate symbols from libstd,
+// breaking the panic hook feature.
+//
+// This simulates the `rustc_driver` crate, and the main crate simulates rustc's main binary hooking
+// into this driver.
+
+// compile-flags: -Zdylib-lto -C lto=thin
+
+use std::panic;
+
+pub fn main() {
+ // Install the hook we want to see executed
+ panic::set_hook(Box::new(|_| {
+ eprintln!("LTOed auxiliary crate panic hook");
+ }));
+
+ // Trigger the panic hook with an ICE
+ run_compiler();
+}
+
+fn run_compiler() {
+ panic!("ICEing");
+}
--- /dev/null
+// Regression test for issue #105637: `-Zdylib-lto` with LTO duplicated symbols from other dylibs,
+// in this case from libstd.
+//
+// That manifested as both `rustc_driver` and rustc's "main" (`compiler/rustc`) having their own
+// `std::panicking::HOOK` static, and the hook in rustc's main (the default stdlib's) being executed
+// when rustc ICEs, instead of the overriden hook from `rustc_driver` (which also displays the query
+// stack and information on how to open a GH issue for the encountered ICE).
+//
+// In this test, we reproduce this setup by installing a panic hook in both the main and an LTOed
+// dylib: the last hook set should be the one being executed, the dylib's.
+
+// aux-build: thinlto-dylib.rs
+// run-fail
+// check-run-results
+
+extern crate thinlto_dylib;
+
+use std::panic;
+
+fn main() {
+ // We don't want to see this panic hook executed
+ std::panic::set_hook(Box::new(|_| {
+ eprintln!("main crate panic hook");
+ }));
+
+ // Have the LTOed dylib install its own hook and panic, we want to see its hook executed.
+ thinlto_dylib::main();
+}
--- /dev/null
+LTOed auxiliary crate panic hook
--- /dev/null
+macro_rules! foo {
+ ($e:expr) => { $e.foo() }
+ //~^ ERROR no method named `foo` found
+}
+
+fn main() {
+ let a = 1i32;
+ foo!(a);
+
+ foo!(1i32.foo());
+ //~^ ERROR no method named `foo` found
+}
--- /dev/null
+error[E0599]: no method named `foo` found for type `i32` in the current scope
+ --> $DIR/issue-25385.rs:2:23
+ |
+LL | ($e:expr) => { $e.foo() }
+ | ^^^ method not found in `i32`
+...
+LL | foo!(a);
+ | ------- in this macro invocation
+ |
+ = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0599]: no method named `foo` found for type `i32` in the current scope
+ --> $DIR/issue-25385.rs:10:15
+ |
+LL | foo!(1i32.foo());
+ | ^^^ method not found in `i32`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+
+enum Enum {
+ Foo { foo: usize },
+ Bar { bar: usize }
+}
+
+fn fun1(e1: &Enum, e2: &Enum) -> usize {
+ match (e1, e2) {
+ (&Enum::Foo { foo: _ }, &Enum::Foo { foo: _ }) => 0,
+ (&Enum::Foo { foo: _ }, &Enum::Bar { bar: _ }) => 1,
+ (&Enum::Bar { bar: _ }, &Enum::Bar { bar: _ }) => 2,
+ (&Enum::Bar { bar: _ }, &Enum::Foo { foo: _ }) => 3,
+ }
+}
+
+fn fun2(e1: &Enum, e2: &Enum) -> usize {
+ match (e1, e2) {
+ (&Enum::Foo { foo: _ }, &Enum::Foo { foo: _ }) => 0,
+ (&Enum::Foo { foo: _ }, _ ) => 1,
+ (&Enum::Bar { bar: _ }, &Enum::Bar { bar: _ }) => 2,
+ (&Enum::Bar { bar: _ }, _ ) => 3,
+ }
+}
+
+pub fn main() {
+ let foo = Enum::Foo { foo: 1 };
+ let bar = Enum::Bar { bar: 1 };
+
+ assert_eq!(fun1(&foo, &foo), 0);
+ assert_eq!(fun1(&foo, &bar), 1);
+ assert_eq!(fun1(&bar, &bar), 2);
+ assert_eq!(fun1(&bar, &foo), 3);
+
+ assert_eq!(fun2(&foo, &foo), 0);
+ assert_eq!(fun2(&foo, &bar), 1); // fun2 returns 0
+ assert_eq!(fun2(&bar, &bar), 2);
+ assert_eq!(fun2(&bar, &foo), 3); // fun2 returns 2
+}
--- /dev/null
+#![feature(auto_traits)]
+
+auto trait Foo {
+ fn g(&self); //~ ERROR auto traits cannot have associated items
+}
+
+trait Bar {
+ fn f(&self) {
+ self.g(); //~ ERROR the method `g` exists for reference `&Self`, but its trait bounds were not satisfied
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0380]: auto traits cannot have associated items
+ --> $DIR/issue-105732.rs:4:8
+ |
+LL | auto trait Foo {
+ | --- auto trait cannot have associated items
+LL | fn g(&self);
+ | ---^-------- help: remove these associated items
+
+error[E0599]: the method `g` exists for reference `&Self`, but its trait bounds were not satisfied
+ --> $DIR/issue-105732.rs:9:14
+ |
+LL | self.g();
+ | ^
+ |
+ = note: the following trait bounds were not satisfied:
+ `Self: Foo`
+ which is required by `&Self: Foo`
+ `&Self: Foo`
+ = help: items from traits can only be used if the type parameter is bounded by the trait
+help: the following trait defines an item `g`, perhaps you need to add a supertrait for it:
+ |
+LL | trait Bar: Foo {
+ | +++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0380, E0599.
+For more information about an error, try `rustc --explain E0380`.
--- /dev/null
+// Non-regression test ICE from issue #105809 and duplicates.
+
+// build-pass: the ICE is during codegen
+// compile-flags: --edition 2018 -Zmir-opt-level=1
+
+use std::{future::Future, pin::Pin};
+
+// Create a `T` without affecting analysis like `loop {}`.
+fn create<T>() -> T {
+ loop {}
+}
+
+async fn trivial_future() {}
+
+struct Connection<H> {
+ _h: H,
+}
+
+async fn complex_future<H>(conn: &Connection<H>) {
+ let small_fut = async move {
+ let _ = conn;
+ trivial_future().await;
+ };
+
+ let mut tuple = (small_fut,);
+ let (small_fut_again,) = &mut tuple;
+ let _ = small_fut_again;
+}
+
+fn main() {
+ let mut fut = complex_future(&Connection { _h: () });
+
+ let mut cx = create();
+ let future = unsafe { Pin::new_unchecked(&mut fut) };
+ let _ = future.poll(&mut cx);
+}
+++ /dev/null
-// compile-flags: -C panic=abort
-// no-prefer-dynamic
-
-#![no_std]
-#![crate_type = "staticlib"]
-#![feature(alloc_error_handler)]
-
-#[panic_handler]
-fn panic(_: &core::panic::PanicInfo) -> ! {
- loop {}
-}
-
-extern crate alloc;
-
-#[global_allocator]
-static A: MyAlloc = MyAlloc;
-
-struct MyAlloc;
-
-unsafe impl core::alloc::GlobalAlloc for MyAlloc {
- unsafe fn alloc(&self, _: core::alloc::Layout) -> *mut u8 { 0 as _ }
- unsafe fn dealloc(&self, _: *mut u8, _: core::alloc::Layout) {}
-}
+++ /dev/null
-error: `#[alloc_error_handler]` function required, but not found
-
-note: use `#![feature(default_alloc_error_handler)]` for a default error handler
-
-error: aborting due to previous error
-
{
extern crate proc_macro;
#[rustc_proc_macro_decls]
+ #[used]
#[allow(deprecated)]
static _DECLS: &[proc_macro::bridge::client::ProcMacro] = &[];
};
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0320`.
// This should fail because `T` ends up in the upvars of the closure.
fn bad_generic_fn<T: Copy>(t: T) {
assert_static(opaque(async move { t; }).next());
- //~^ ERROR the parameter type `T` may not live long enough
+ //~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
assert_static(opaque(move || { t; }).next());
//~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
assert_static(opaque(opaque(async move { t; }).next()).next());
- //~^ ERROR the parameter type `T` may not live long enough
+ //~^ ERROR the associated type `<impl Iterator as Iterator>::Item` may not live long enough
}
fn main() {}
-error[E0310]: the parameter type `T` may not live long enough
+error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
--> $DIR/closure-in-projection-issue-97405.rs:24:5
|
LL | assert_static(opaque(async move { t; }).next());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
- |
-help: consider adding an explicit lifetime bound...
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-LL | fn bad_generic_fn<T: Copy + 'static>(t: T) {
- | +++++++++
+ = help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
+ = note: ...so that the type `<impl Iterator as Iterator>::Item` will meet its required lifetime bounds
error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
--> $DIR/closure-in-projection-issue-97405.rs:26:5
= help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
= note: ...so that the type `<impl Iterator as Iterator>::Item` will meet its required lifetime bounds
-error[E0310]: the parameter type `T` may not live long enough
+error[E0310]: the associated type `<impl Iterator as Iterator>::Item` may not live long enough
--> $DIR/closure-in-projection-issue-97405.rs:28:5
|
LL | assert_static(opaque(opaque(async move { t; }).next()).next());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
- |
-help: consider adding an explicit lifetime bound...
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
-LL | fn bad_generic_fn<T: Copy + 'static>(t: T) {
- | +++++++++
+ = help: consider adding an explicit lifetime bound `<impl Iterator as Iterator>::Item: 'static`...
+ = note: ...so that the type `<impl Iterator as Iterator>::Item` will meet its required lifetime bounds
error: aborting due to 3 previous errors
--- /dev/null
+#![crate_type="lib"]
+
+pub enum Foo {
+ FooV { data: () }
+}
--- /dev/null
+// aux-build:issue-30535.rs
+
+extern crate issue_30535 as foo;
+
+fn bar(
+ _: foo::Foo::FooV //~ ERROR expected type, found variant `foo::Foo::FooV`
+) {}
+
+fn main() {}
--- /dev/null
+error[E0573]: expected type, found variant `foo::Foo::FooV`
+ --> $DIR/issue-30535.rs:6:8
+ |
+LL | _: foo::Foo::FooV
+ | ^^^^^^^^^^^^^^
+ | |
+ | not a type
+ | help: try using the variant's enum: `foo::Foo`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0573`.
--- /dev/null
+trait Dim {
+ fn dim() -> usize;
+}
+
+enum Dim3 {}
+
+impl Dim for Dim3 {
+ fn dim() -> usize {
+ 3
+ }
+}
+
+fn main() {
+ let array: [usize; Dim3::dim()]
+ //~^ ERROR E0015
+ = [0; Dim3::dim()];
+ //~^ ERROR E0015
+}
--- /dev/null
+error[E0015]: cannot call non-const fn `<Dim3 as Dim>::dim` in constants
+ --> $DIR/issue-39559-2.rs:14:24
+ |
+LL | let array: [usize; Dim3::dim()]
+ | ^^^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error[E0015]: cannot call non-const fn `<Dim3 as Dim>::dim` in constants
+ --> $DIR/issue-39559-2.rs:16:15
+ |
+LL | = [0; Dim3::dim()];
+ | ^^^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0015`.
--- /dev/null
+trait Dim {
+ fn dim() -> usize;
+}
+
+enum Dim3 {}
+
+impl Dim for Dim3 {
+ fn dim() -> usize {
+ 3
+ }
+}
+
+pub struct Vector<T, D: Dim> {
+ entries: [T; D::dim()],
+ //~^ ERROR generic parameters may not be used
+ _dummy: D,
+}
+
+fn main() {}
--- /dev/null
+error: generic parameters may not be used in const operations
+ --> $DIR/issue-39559.rs:14:18
+ |
+LL | entries: [T; D::dim()],
+ | ^^^^^^ cannot perform const operation using `D`
+ |
+ = note: type parameters may not be used in const expressions
+ = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+#![allow(incomplete_features)]
+#![feature(
+ associated_type_bounds,
+ const_trait_impl,
+ const_cmp,
+ return_position_impl_trait_in_trait,
+)]
+
+use std::marker::Destruct;
+
+const fn cmp(a: &impl ~const PartialEq) -> bool {
+ a == a
+}
+
+const fn wrap(x: impl ~const PartialEq + ~const Destruct)
+ -> impl ~const PartialEq + ~const Destruct
+{
+ x
+}
+
+#[const_trait]
+trait Foo {
+ fn huh() -> impl ~const PartialEq + ~const Destruct + Copy;
+}
+
+impl const Foo for () {
+ fn huh() -> impl ~const PartialEq + ~const Destruct + Copy {
+ 123
+ }
+}
+
+const _: () = {
+ assert!(cmp(&0xDEADBEEFu32));
+ assert!(cmp(&()));
+ assert!(wrap(123) == wrap(123));
+ assert!(wrap(123) != wrap(456));
+ let x = <() as Foo>::huh();
+ assert!(x == x);
+};
+
+#[const_trait]
+trait T {}
+struct S;
+impl const T for S {}
+
+const fn rpit() -> impl ~const T { S }
+
+const fn apit(_: impl ~const T + ~const Destruct) {}
+
+const fn rpit_assoc_bound() -> impl IntoIterator<Item: ~const T> { Some(S) }
+
+const fn apit_assoc_bound(_: impl IntoIterator<Item: ~const T> + ~const Destruct) {}
+
+fn main() {}
#![feature(const_trait_impl)]
#![feature(associated_type_bounds)]
-#[const_trait]
-trait T {}
-struct S;
-impl T for S {}
-
-fn rpit() -> impl ~const T { S }
-//~^ ERROR `~const` is not allowed
-
-fn apit(_: impl ~const T) {}
-//~^ ERROR `~const` is not allowed
-
-fn rpit_assoc_bound() -> impl IntoIterator<Item: ~const T> { Some(S) }
-//~^ ERROR `~const` is not allowed
-
-fn apit_assoc_bound(_: impl IntoIterator<Item: ~const T>) {}
-//~^ ERROR `~const` is not allowed
-
struct TildeQuestion<T: ~const ?Sized>(std::marker::PhantomData<T>);
//~^ ERROR `~const` and `?` are mutually exclusive
-error: `~const` is not allowed here
- --> $DIR/tilde-const-invalid-places.rs:9:19
- |
-LL | fn rpit() -> impl ~const T { S }
- | ^^^^^^^^
- |
- = note: `impl Trait`s cannot have `~const` trait bounds
-
-error: `~const` is not allowed here
- --> $DIR/tilde-const-invalid-places.rs:12:17
- |
-LL | fn apit(_: impl ~const T) {}
- | ^^^^^^^^
- |
- = note: `impl Trait`s cannot have `~const` trait bounds
-
-error: `~const` is not allowed here
- --> $DIR/tilde-const-invalid-places.rs:15:50
- |
-LL | fn rpit_assoc_bound() -> impl IntoIterator<Item: ~const T> { Some(S) }
- | ^^^^^^^^
- |
- = note: `impl Trait`s cannot have `~const` trait bounds
-
-error: `~const` is not allowed here
- --> $DIR/tilde-const-invalid-places.rs:18:48
- |
-LL | fn apit_assoc_bound(_: impl IntoIterator<Item: ~const T>) {}
- | ^^^^^^^^
- |
- = note: `impl Trait`s cannot have `~const` trait bounds
-
error: `~const` and `?` are mutually exclusive
- --> $DIR/tilde-const-invalid-places.rs:21:25
+ --> $DIR/tilde-const-invalid-places.rs:4:25
|
LL | struct TildeQuestion<T: ~const ?Sized>(std::marker::PhantomData<T>);
| ^^^^^^^^^^^^^
-error: aborting due to 5 previous errors
+error: aborting due to previous error
--- /dev/null
+pub fn main() {
+ const z: &'static isize = {
+ static p: isize = 3;
+ &p //~ ERROR constants cannot refer to statics
+ };
+}
--- /dev/null
+error[E0013]: constants cannot refer to statics
+ --> $DIR/issue-18118-2.rs:4:10
+ |
+LL | &p
+ | ^
+ |
+ = help: consider extracting the value of the `static` to a `const`, and referring to that
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0013`.
--- /dev/null
+pub fn main() {
+ const z: &'static isize = {
+ let p = 3;
+ &p //~ ERROR `p` does not live long enough
+ };
+}
--- /dev/null
+error[E0597]: `p` does not live long enough
+ --> $DIR/issue-18118.rs:4:9
+ |
+LL | &p
+ | ^^
+ | |
+ | borrowed value does not live long enough
+ | using this value as a constant requires that `p` is borrowed for `'static`
+LL | };
+ | - `p` dropped here while still borrowed
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0597`.
--- /dev/null
+struct MyS;
+
+impl MyS {
+ const FOO: i32 = 1;
+ fn foo() -> MyS {
+ MyS
+ }
+}
+
+fn main() {
+ let x: i32 = MyS::foo;
+ //~^ ERROR mismatched types
+ //~| HELP try referring to the
+
+ let z: i32 = i32::max;
+ //~^ ERROR mismatched types
+ //~| HELP try referring to the
+
+ // This example is still broken though... This is a hard suggestion to make,
+ // because we don't have access to the associated const probing code to make
+ // this suggestion where it's emitted, i.e. in trait selection.
+ let y: i32 = i32::max - 42;
+ //~^ ERROR cannot subtract
+ //~| HELP use parentheses
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/assoc-ct-for-assoc-method.rs:11:18
+ |
+LL | let x: i32 = MyS::foo;
+ | --- ^^^^^^^^ expected `i32`, found fn item
+ | |
+ | expected due to this
+ |
+ = note: expected type `i32`
+ found fn item `fn() -> MyS {MyS::foo}`
+help: try referring to the associated const `FOO` instead
+ |
+LL | let x: i32 = MyS::FOO;
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/assoc-ct-for-assoc-method.rs:15:18
+ |
+LL | let z: i32 = i32::max;
+ | --- ^^^^^^^^ expected `i32`, found fn item
+ | |
+ | expected due to this
+ |
+ = note: expected type `i32`
+ found fn item `fn(i32, i32) -> i32 {<i32 as Ord>::max}`
+help: try referring to the associated const `MAX` instead
+ |
+LL | let z: i32 = i32::MAX;
+ | ~~~
+
+error[E0369]: cannot subtract `{integer}` from `fn(i32, i32) -> i32 {<i32 as Ord>::max}`
+ --> $DIR/assoc-ct-for-assoc-method.rs:22:27
+ |
+LL | let y: i32 = i32::max - 42;
+ | -------- ^ -- {integer}
+ | |
+ | fn(i32, i32) -> i32 {<i32 as Ord>::max}
+ |
+help: use parentheses to call this associated function
+ |
+LL | let y: i32 = i32::max(/* i32 */, /* i32 */) - 42;
+ | ++++++++++++++++++++++
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0369.
+For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+pub enum Sexpr<'a> {
+ Ident(&'a str),
+}
+
+fn map<'a, F: Fn(String) -> Sexpr<'a>>(f: F) {}
+
+fn main() {
+ map(Sexpr::Ident);
+ //~^ ERROR type mismatch in function arguments
+}
--- /dev/null
+error[E0631]: type mismatch in function arguments
+ --> $DIR/enum-variant-arg-mismatch.rs:8:9
+ |
+LL | Ident(&'a str),
+ | ----- found signature defined here
+...
+LL | map(Sexpr::Ident);
+ | --- ^^^^^^^^^^^^ expected due to this
+ | |
+ | required by a bound introduced by this call
+ |
+ = note: expected function signature `fn(String) -> _`
+ found function signature `fn(&str) -> _`
+note: required by a bound in `map`
+ --> $DIR/enum-variant-arg-mismatch.rs:5:15
+ |
+LL | fn map<'a, F: Fn(String) -> Sexpr<'a>>(f: F) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0631`.
--- /dev/null
+fn main() -> Result<(), ()> {
+ a(b(c(d(e(
+ //~^ ERROR cannot find function `a` in this scope
+ //~| ERROR cannot find function `b` in this scope
+ //~| ERROR cannot find function `c` in this scope
+ //~| ERROR cannot find function `d` in this scope
+ //~| ERROR cannot find function `e` in this scope
+ z????????????????????????????????????????????????????????????????????????????????????????
+ ?????????????????????????????????????????????????????????????????????????????????????????
+ ??????????????????????????????????????????????????????????????????
+ //~^^^ ERROR cannot find value `z` in this scope
+ )))))
+}
--- /dev/null
+error[E0425]: cannot find value `z` in this scope
+ --> $DIR/fn-to-method-deeply-nested.rs:8:9
+ |
+LL | z????????????????????????????????????????????????????????????????????????????????????????
+ | ^ not found in this scope
+
+error[E0425]: cannot find function `e` in this scope
+ --> $DIR/fn-to-method-deeply-nested.rs:2:13
+ |
+LL | a(b(c(d(e(
+ | ^ not found in this scope
+
+error[E0425]: cannot find function `d` in this scope
+ --> $DIR/fn-to-method-deeply-nested.rs:2:11
+ |
+LL | a(b(c(d(e(
+ | ^ not found in this scope
+
+error[E0425]: cannot find function `c` in this scope
+ --> $DIR/fn-to-method-deeply-nested.rs:2:9
+ |
+LL | a(b(c(d(e(
+ | ^ not found in this scope
+
+error[E0425]: cannot find function `b` in this scope
+ --> $DIR/fn-to-method-deeply-nested.rs:2:7
+ |
+LL | a(b(c(d(e(
+ | ^ not found in this scope
+
+error[E0425]: cannot find function `a` in this scope
+ --> $DIR/fn-to-method-deeply-nested.rs:2:5
+ |
+LL | a(b(c(d(e(
+ | ^ not found in this scope
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
error: aborting due to previous error
+For more information about this error, try `rustc --explain E0460`.
--- /dev/null
+// run-pass
+#![allow(unused_imports)]
+// This test checks for namespace pollution by private tests.
+// Tests used to marked as public causing name conflicts with normal
+// functions only in test builds.
+
+// compile-flags: --test
+
+mod a {
+ pub fn foo() -> bool {
+ true
+ }
+}
+
+mod b {
+ #[test]
+ fn foo() {
+ local_name(); // ensure the local name still works
+ }
+
+ #[test]
+ fn local_name() {}
+}
+
+use a::*;
+use b::*;
+
+pub fn conflict() {
+ let _: bool = foo();
+}
--- /dev/null
+// known-bug
+
+// This should compile but fails with the current solver.
+//
+// This checks that the new solver uses `Ambiguous` when hitting the
+// inductive cycle here when proving `exists<^0, ^1> (): Trait<^0, ^1>`
+// which requires proving `Trait<?1, ?0>` but that has the same
+// canonical representation.
+trait Trait<T, U> {}
+
+impl<T, U> Trait<T, U> for ()
+where
+ (): Trait<U, T>,
+ T: OtherTrait,
+{}
+
+trait OtherTrait {}
+impl OtherTrait for u32 {}
+
+fn require_trait<T, U>()
+where
+ (): Trait<T, U>
+{}
+
+fn main() {
+ require_trait::<_, _>();
+ //~^ ERROR overflow evaluating
+}
--- /dev/null
+error[E0275]: overflow evaluating the requirement `_: Sized`
+ --> $DIR/inductive-canonical-cycle.rs:26:5
+ |
+LL | require_trait::<_, _>();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_canonical_cycle`)
+note: required for `()` to implement `Trait<_, _>`
+ --> $DIR/inductive-canonical-cycle.rs:11:12
+ |
+LL | impl<T, U> Trait<T, U> for ()
+ | ^^^^^^^^^^^ ^^
+ = note: 128 redundant requirements hidden
+ = note: required for `()` to implement `Trait<_, _>`
+note: required by a bound in `require_trait`
+ --> $DIR/inductive-canonical-cycle.rs:22:9
+ |
+LL | fn require_trait<T, U>()
+ | ------------- required by a bound in this
+LL | where
+LL | (): Trait<T, U>
+ | ^^^^^^^^^^^ required by this bound in `require_trait`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
--- /dev/null
+pub trait SomeTrait {}
+
+impl SomeTrait for () {}
+
+// Adding this `impl` would cause errors in this crate's dependent,
+// so it would be a breaking change. We explicitly don't add this impl,
+// as the dependent crate already assumes this impl exists and thus already
+// does not compile.
+//impl SomeTrait for i32 {}
LL | impl<T> foreign_crate::ForeignTrait for AliasOfForeignType<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^---------------------
| | |
- | | `AliasOfForeignType<T>` is not defined in the current crate
+ | | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
--- /dev/null
+// aux-build: coherence_cross_crate_trait_decl.rs
+// This test ensures that adding an `impl SomeTrait for i32` within
+// `coherence_cross_crate_trait_decl` is not a breaking change, by
+// making sure that even without such an impl this test fails to compile.
+
+#![feature(type_alias_impl_trait)]
+
+extern crate coherence_cross_crate_trait_decl;
+
+use coherence_cross_crate_trait_decl::SomeTrait;
+
+trait OtherTrait {}
+
+type Alias = impl SomeTrait;
+
+fn constrain() -> Alias {
+ ()
+}
+
+impl OtherTrait for Alias {}
+impl OtherTrait for i32 {}
+//~^ ERROR: conflicting implementations of trait `OtherTrait` for type `Alias`
+
+fn main() {}
--- /dev/null
+error[E0119]: conflicting implementations of trait `OtherTrait` for type `Alias`
+ --> $DIR/coherence_cross_crate.rs:21:1
+ |
+LL | impl OtherTrait for Alias {}
+ | ------------------------- first implementation here
+LL | impl OtherTrait for i32 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Alias`
+ |
+ = note: upstream crates may add a new impl of trait `coherence_cross_crate_trait_decl::SomeTrait` for type `i32` in future versions
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
--- /dev/null
+#![feature(type_alias_impl_trait)]
+
+// check-pass
+
+// issue: https://github.com/rust-lang/rust/issues/104551
+
+fn main() {
+ type T = impl Sized;
+ let (_a, _b): T = (1u32, 2u32);
+}
--- /dev/null
+fn main() {
+ a((), 1i32 == 2u32);
+ //~^ ERROR cannot find function `a` in this scope
+ //~| ERROR mismatched types
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/check-args-on-fn-err-2.rs:2:19
+ |
+LL | a((), 1i32 == 2u32);
+ | ---- ^^^^ expected `i32`, found `u32`
+ | |
+ | expected because this is `i32`
+ |
+help: change the type of the numeric literal from `u32` to `i32`
+ |
+LL | a((), 1i32 == 2i32);
+ | ~~~
+
+error[E0425]: cannot find function `a` in this scope
+ --> $DIR/check-args-on-fn-err-2.rs:2:5
+ |
+LL | a((), 1i32 == 2u32);
+ | ^ not found in this scope
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0308, E0425.
+For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+fn main() {
+ unknown(1, |glyf| {
+ //~^ ERROR: cannot find function `unknown` in this scope
+ let actual = glyf;
+ });
+}
--- /dev/null
+error[E0425]: cannot find function `unknown` in this scope
+ --> $DIR/check-args-on-fn-err.rs:2:5
+ |
+LL | unknown(1, |glyf| {
+ | ^^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
--- /dev/null
+// check-pass
+trait A<T: A<T>> {}
+
+fn main() {}
--- /dev/null
+#![feature(unboxed_closures)]
+#![feature(fn_traits)]
+
+fn main() {
+ let handlers: Option<Box<dyn for<'a> FnMut<&'a mut (), Output=()>>> = None;
+ handlers.unwrap().as_mut().call_mut(&mut ()); //~ ERROR: `&mut ()` is not a tuple
+}
--- /dev/null
+error[E0277]: `&mut ()` is not a tuple
+ --> $DIR/issue-57404.rs:6:41
+ |
+LL | handlers.unwrap().as_mut().call_mut(&mut ());
+ | -------- -^^^^^^
+ | | |
+ | | the trait `Tuple` is not implemented for `&mut ()`
+ | | help: consider removing the leading `&`-reference
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `call_mut`
+ --> $SRC_DIR/core/src/ops/function.rs:LL:COL
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+struct Person {
+ first_name: String,
+ age: u32,
+}
+
+fn first_woman(man: &Person) -> Person {
+ Person {
+ first_name: "Eve".to_string(),
+ ..man.clone() //~ ERROR: mismatched types
+ }
+}
+
+fn main() {
+ let adam = Person {
+ first_name: "Adam".to_string(),
+ age: 0,
+ };
+
+ let eve = first_woman(&adam);
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-96530.rs:9:11
+ |
+LL | ..man.clone()
+ | ^^^^^^^^^^^ expected struct `Person`, found `&Person`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+#![feature(fn_traits, unboxed_closures, tuple_trait)]
+
+use std::default::Default;
+use std::marker::Tuple;
+
+fn wrap<P: Tuple + Default, T>(func: impl Fn<P, Output = T>) {
+ let x: P = Default::default();
+ // Should be: `func.call(x);`
+ func(x);
+ //~^ ERROR cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
+}
+
+fn foo() {}
+
+fn main() {
+ wrap(foo);
+}
--- /dev/null
+error[E0059]: cannot use call notation; the first type parameter for the function trait is neither a tuple nor unit
+ --> $DIR/non-tupled-call.rs:9:5
+ |
+LL | func(x);
+ | ^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0059`.
-Subproject commit cc0a320879c17207bbfb96b5d778e28a2c62030d
+Subproject commit 2381cbdb4e9b07090f552d34a44a529b6e620e44
with:
github_token: "${{ secrets.github_token }}"
- - name: Install dependencies (Linux-i686)
- run: |
- sudo dpkg --add-architecture i386
- sudo apt-get update
- sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
- if: matrix.host == 'i686-unknown-linux-gnu'
-
- name: Checkout
uses: actions/checkout@v3.0.2
## Unreleased / Beta / In Rust Nightly
-[b52fb523...master](https://github.com/rust-lang/rust-clippy/compare/b52fb523...master)
+[4f142aa1...master](https://github.com/rust-lang/rust-clippy/compare/4f142aa1...master)
+
+## Rust 1.66
+
+Current stable, released 2022-12-15
+
+[b52fb523...4f142aa1](https://github.com/rust-lang/rust-clippy/compare/b52fb523...4f142aa1)
+
+### New Lints
+
+* [`manual_clamp`]
+ [#9484](https://github.com/rust-lang/rust-clippy/pull/9484)
+* [`missing_trait_methods`]
+ [#9670](https://github.com/rust-lang/rust-clippy/pull/9670)
+* [`unused_format_specs`]
+ [#9637](https://github.com/rust-lang/rust-clippy/pull/9637)
+* [`iter_kv_map`]
+ [#9409](https://github.com/rust-lang/rust-clippy/pull/9409)
+* [`manual_filter`]
+ [#9451](https://github.com/rust-lang/rust-clippy/pull/9451)
+* [`box_default`]
+ [#9511](https://github.com/rust-lang/rust-clippy/pull/9511)
+* [`implicit_saturating_add`]
+ [#9549](https://github.com/rust-lang/rust-clippy/pull/9549)
+* [`as_ptr_cast_mut`]
+ [#9572](https://github.com/rust-lang/rust-clippy/pull/9572)
+* [`disallowed_macros`]
+ [#9495](https://github.com/rust-lang/rust-clippy/pull/9495)
+* [`partial_pub_fields`]
+ [#9658](https://github.com/rust-lang/rust-clippy/pull/9658)
+* [`uninlined_format_args`]
+ [#9233](https://github.com/rust-lang/rust-clippy/pull/9233)
+* [`cast_nan_to_int`]
+ [#9617](https://github.com/rust-lang/rust-clippy/pull/9617)
+
+### Moves and Deprecations
+
+* `positional_named_format_parameters` was uplifted to rustc under the new name
+ `named_arguments_used_positionally`
+ [#8518](https://github.com/rust-lang/rust-clippy/pull/8518)
+* Moved [`implicit_saturating_sub`] to `style` (Now warn-by-default)
+ [#9584](https://github.com/rust-lang/rust-clippy/pull/9584)
+* Moved `derive_partial_eq_without_eq` to `nursery` (now allow-by-default)
+ [#9536](https://github.com/rust-lang/rust-clippy/pull/9536)
+
+### Enhancements
+
+* [`nonstandard_macro_braces`]: Now includes `matches!()` in the default lint config
+ [#9471](https://github.com/rust-lang/rust-clippy/pull/9471)
+* [`suboptimal_flops`]: Now supports multiplication and subtraction operations
+ [#9581](https://github.com/rust-lang/rust-clippy/pull/9581)
+* [`arithmetic_side_effects`]: Now detects cases with literals behind references
+ [#9587](https://github.com/rust-lang/rust-clippy/pull/9587)
+* [`upper_case_acronyms`]: Now also checks enum names
+ [#9580](https://github.com/rust-lang/rust-clippy/pull/9580)
+* [`needless_borrowed_reference`]: Now lints nested patterns
+ [#9573](https://github.com/rust-lang/rust-clippy/pull/9573)
+* [`unnecessary_cast`]: Now works for non-trivial non-literal expressions
+ [#9576](https://github.com/rust-lang/rust-clippy/pull/9576)
+* [`arithmetic_side_effects`]: Now detects operations with custom types
+ [#9559](https://github.com/rust-lang/rust-clippy/pull/9559)
+* [`disallowed_methods`], [`disallowed_types`]: Not correctly lints types, functions and macros
+ with the same path
+ [#9495](https://github.com/rust-lang/rust-clippy/pull/9495)
+* [`self_named_module_files`], [`mod_module_files`]: Now take remapped path prefixes into account
+ [#9475](https://github.com/rust-lang/rust-clippy/pull/9475)
+* [`bool_to_int_with_if`]: Now detects the inverse if case
+ [#9476](https://github.com/rust-lang/rust-clippy/pull/9476)
+
+### False Positive Fixes
+
+* [`arithmetic_side_effects`]: Now allows operations that can't overflow
+ [#9474](https://github.com/rust-lang/rust-clippy/pull/9474)
+* [`unnecessary_lazy_evaluations`]: No longer lints in external macros
+ [#9486](https://github.com/rust-lang/rust-clippy/pull/9486)
+* [`needless_borrow`], [`explicit_auto_deref`]: No longer lint on unions that require the reference
+ [#9490](https://github.com/rust-lang/rust-clippy/pull/9490)
+* [`almost_complete_letter_range`]: No longer lints in external macros
+ [#9467](https://github.com/rust-lang/rust-clippy/pull/9467)
+* [`drop_copy`]: No longer lints on idiomatic cases in match arms
+ [#9491](https://github.com/rust-lang/rust-clippy/pull/9491)
+* [`question_mark`]: No longer lints in const context
+ [#9487](https://github.com/rust-lang/rust-clippy/pull/9487)
+* [`collapsible_if`]: Suggestion now work in macros
+ [#9410](https://github.com/rust-lang/rust-clippy/pull/9410)
+* [`std_instead_of_core`]: No longer triggers on unstable modules
+ [#9545](https://github.com/rust-lang/rust-clippy/pull/9545)
+* [`unused_peekable`]: No longer lints, if the peak is done in a closure or function
+ [#9465](https://github.com/rust-lang/rust-clippy/pull/9465)
+* [`useless_attribute`]: No longer lints on `#[allow]` attributes for [`unsafe_removed_from_name`]
+ [#9593](https://github.com/rust-lang/rust-clippy/pull/9593)
+* [`unnecessary_lazy_evaluations`]: No longer suggest switching to early evaluation when type has
+ custom `Drop` implementation
+ [#9551](https://github.com/rust-lang/rust-clippy/pull/9551)
+* [`unnecessary_cast`]: No longer lints on negative hexadecimal literals when cast as floats
+ [#9609](https://github.com/rust-lang/rust-clippy/pull/9609)
+* [`use_self`]: No longer lints in proc macros
+ [#9454](https://github.com/rust-lang/rust-clippy/pull/9454)
+* [`never_loop`]: Now takes `let ... else` statements into consideration.
+ [#9496](https://github.com/rust-lang/rust-clippy/pull/9496)
+* [`default_numeric_fallback`]: Now ignores constants
+ [#9636](https://github.com/rust-lang/rust-clippy/pull/9636)
+* [`uninit_vec`]: No longer lints `Vec::set_len(0)`
+ [#9519](https://github.com/rust-lang/rust-clippy/pull/9519)
+* [`arithmetic_side_effects`]: Now ignores references to integer types
+ [#9507](https://github.com/rust-lang/rust-clippy/pull/9507)
+* [`large_stack_arrays`]: No longer lints inside static items
+ [#9466](https://github.com/rust-lang/rust-clippy/pull/9466)
+* [`ref_option_ref`]: No longer lints if the inner reference is mutable
+ [#9684](https://github.com/rust-lang/rust-clippy/pull/9684)
+* [`ptr_arg`]: No longer lints if the argument is used as an incomplete trait object
+ [#9645](https://github.com/rust-lang/rust-clippy/pull/9645)
+* [`should_implement_trait`]: Now also works for `default` methods
+ [#9546](https://github.com/rust-lang/rust-clippy/pull/9546)
+
+### Suggestion Fixes/Improvements
+
+* [`derivable_impls`]: The suggestion is now machine applicable
+ [#9429](https://github.com/rust-lang/rust-clippy/pull/9429)
+* [`match_single_binding`]: The suggestion now handles scrutinies with side effects better
+ [#9601](https://github.com/rust-lang/rust-clippy/pull/9601)
+* [`zero_prefixed_literal`]: Only suggests using octal numbers, if this is possible
+ [#9652](https://github.com/rust-lang/rust-clippy/pull/9652)
+* [`rc_buffer`]: The suggestion is no longer machine applicable to avoid semantic changes
+ [#9633](https://github.com/rust-lang/rust-clippy/pull/9633)
+* [`print_literal`], [`write_literal`], [`uninlined_format_args`]: The suggestion now ignores
+ comments after the macro call.
+ [#9586](https://github.com/rust-lang/rust-clippy/pull/9586)
+* [`expect_fun_call`]:Improved the suggestion for `format!` calls with captured variables
+ [#9586](https://github.com/rust-lang/rust-clippy/pull/9586)
+* [`nonstandard_macro_braces`]: The suggestion is now machine applicable and will no longer
+ replace brackets inside the macro argument.
+ [#9499](https://github.com/rust-lang/rust-clippy/pull/9499)
+* [`from_over_into`]: The suggestion is now a machine applicable and contains explanations
+ [#9649](https://github.com/rust-lang/rust-clippy/pull/9649)
+* [`needless_return`]: The automatic suggestion now removes all required semicolons
+ [#9497](https://github.com/rust-lang/rust-clippy/pull/9497)
+* [`to_string_in_format_args`]: The suggestion now keeps parenthesis around values
+ [#9590](https://github.com/rust-lang/rust-clippy/pull/9590)
+* [`manual_assert`]: The suggestion now preserves comments
+ [#9479](https://github.com/rust-lang/rust-clippy/pull/9479)
+* [`redundant_allocation`]: The suggestion applicability is now marked `MaybeIncorrect` to
+ avoid semantic changes
+ [#9634](https://github.com/rust-lang/rust-clippy/pull/9634)
+* [`assertions_on_result_states`]: The suggestion has been corrected, for cases where the
+ `assert!` is not in a statement.
+ [#9453](https://github.com/rust-lang/rust-clippy/pull/9453)
+* [`nonminimal_bool`]: The suggestion no longer expands macros
+ [#9457](https://github.com/rust-lang/rust-clippy/pull/9457)
+* [`collapsible_match`]: Now specifies field names, when a struct is destructed
+ [#9685](https://github.com/rust-lang/rust-clippy/pull/9685)
+* [`unnecessary_cast`]: The suggestion now adds parenthesis for negative numbers
+ [#9577](https://github.com/rust-lang/rust-clippy/pull/9577)
+* [`redundant_closure`]: The suggestion now works for `impl FnMut` arguments
+ [#9556](https://github.com/rust-lang/rust-clippy/pull/9556)
+
+### ICE Fixes
+
+* [`unnecessary_to_owned`]: Avoid ICEs in favor of false negatives if information is missing
+ [#9505](https://github.com/rust-lang/rust-clippy/pull/9505)
+* [`manual_range_contains`]: No longer ICEs on values behind references
+ [#9627](https://github.com/rust-lang/rust-clippy/pull/9627)
+* [`needless_pass_by_value`]: No longer ICEs on unsized `dyn Fn` arguments
+ [#9531](https://github.com/rust-lang/rust-clippy/pull/9531)
+* `*_interior_mutable_const` lints: no longer ICE on const unions containing `!Freeze` types
+ [#9539](https://github.com/rust-lang/rust-clippy/pull/9539)
+
+### Others
+
+* Released `rustc_tools_util` for version information on `Crates.io`. (Further adjustments will
+ not be published as part of this changelog)
## Rust 1.65
-Current stable, released 2022-11-03
+Released 2022-11-03
[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523)
[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
+[`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
+[`semicolon_inside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_inside_block
+[`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block
[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
[package]
name = "clippy"
-version = "0.1.67"
+version = "0.1.68"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
[dependencies]
clippy_lints = { path = "clippy_lints" }
semver = "1.0"
-rustc_tools_util = "0.2.1"
+rustc_tools_util = "0.3.0"
tempfile = { version = "3.2", optional = true }
termize = "0.1"
rustc-semver = "1.1"
[build-dependencies]
-rustc_tools_util = "0.2.1"
+rustc_tools_util = "0.3.0"
[features]
deny-warnings = ["clippy_lints/deny-warnings"]
# Clippy
-[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
+[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto)
[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license)
A collection of lints to catch common mistakes and improve your
println!("cargo:rustc-env=PROFILE={}", std::env::var("PROFILE").unwrap());
// Don't rebuild even if nothing changed
println!("cargo:rerun-if-changed=build.rs");
- // forward git repo hashes we build at
- println!(
- "cargo:rustc-env=GIT_HASH={}",
- rustc_tools_util::get_commit_hash().unwrap_or_default()
- );
- println!(
- "cargo:rustc-env=COMMIT_DATE={}",
- rustc_tools_util::get_commit_date().unwrap_or_default()
- );
- println!(
- "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
- rustc_tools_util::get_channel()
- );
+ rustc_tools_util::setup_version_info!();
}
[package]
name = "clippy_lints"
-version = "0.1.67"
+version = "0.1.68"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::{trim_span, walk_span_to_context};
-use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
-use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
- /// don't because they're a half open range.
- ///
- /// ### Why is this bad?
- /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
- ///
- /// ### Example
- /// ```rust
- /// let _ = 'a'..'z';
- /// ```
- /// Use instead:
- /// ```rust
- /// let _ = 'a'..='z';
- /// ```
- #[clippy::version = "1.63.0"]
- pub ALMOST_COMPLETE_LETTER_RANGE,
- suspicious,
- "almost complete letter range"
-}
-impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
-
-pub struct AlmostCompleteLetterRange {
- msrv: Msrv,
-}
-impl AlmostCompleteLetterRange {
- pub fn new(msrv: Msrv) -> Self {
- Self { msrv }
- }
-}
-impl EarlyLintPass for AlmostCompleteLetterRange {
- fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
- if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
- let ctxt = e.span.ctxt();
- let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
- && let Some(end) = walk_span_to_context(end.span, ctxt)
- && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
- {
- Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
- } else {
- None
- };
- check_range(cx, e.span, start, end, sugg);
- }
- }
-
- fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
- if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
- && matches!(kind.node, RangeEnd::Excluded)
- {
- let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
- "..="
- } else {
- "..."
- };
- check_range(cx, p.span, start, end, Some((kind.span, sugg)));
- }
- }
-
- extract_msrv_attr!(EarlyContext);
-}
-
-fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
- if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind
- && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind
- && matches!(
- (
- LitKind::from_token_lit(start_token_lit),
- LitKind::from_token_lit(end_token_lit),
- ),
- (
- Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
- Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
- )
- | (
- Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
- Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
- )
- )
- && !in_external_macro(cx.sess(), span)
- {
- span_lint_and_then(
- cx,
- ALMOST_COMPLETE_LETTER_RANGE,
- span,
- "almost complete ascii letter range",
- |diag| {
- if let Some((span, sugg)) = sugg {
- diag.span_suggestion(
- span,
- "use an inclusive range",
- sugg,
- Applicability::MaybeIncorrect,
- );
- }
- }
- );
- }
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::{trim_span, walk_span_to_context};
+use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for ranges which almost include the entire range of letters from 'a' to 'z'
+ /// or digits from '0' to '9', but don't because they're a half open range.
+ ///
+ /// ### Why is this bad?
+ /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let _ = 'a'..'z';
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let _ = 'a'..='z';
+ /// ```
+ #[clippy::version = "1.63.0"]
+ pub ALMOST_COMPLETE_RANGE,
+ suspicious,
+ "almost complete range"
+}
+impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]);
+
+pub struct AlmostCompleteRange {
+ msrv: Msrv,
+}
+impl AlmostCompleteRange {
+ pub fn new(msrv: Msrv) -> Self {
+ Self { msrv }
+ }
+}
+impl EarlyLintPass for AlmostCompleteRange {
+ fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
+ if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
+ let ctxt = e.span.ctxt();
+ let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
+ && let Some(end) = walk_span_to_context(end.span, ctxt)
+ && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
+ {
+ Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
+ } else {
+ None
+ };
+ check_range(cx, e.span, start, end, sugg);
+ }
+ }
+
+ fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
+ if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
+ && matches!(kind.node, RangeEnd::Excluded)
+ {
+ let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
+ "..="
+ } else {
+ "..."
+ };
+ check_range(cx, p.span, start, end, Some((kind.span, sugg)));
+ }
+ }
+
+ extract_msrv_attr!(EarlyContext);
+}
+
+fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
+ if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind
+ && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind
+ && matches!(
+ (
+ LitKind::from_token_lit(start_token_lit),
+ LitKind::from_token_lit(end_token_lit),
+ ),
+ (
+ Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
+ Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
+ )
+ | (
+ Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
+ Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
+ )
+ | (
+ Ok(LitKind::Byte(b'0') | LitKind::Char('0')),
+ Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
+ )
+ )
+ && !in_external_macro(cx.sess(), span)
+ {
+ span_lint_and_then(
+ cx,
+ ALMOST_COMPLETE_RANGE,
+ span,
+ "almost complete ascii range",
+ |diag| {
+ if let Some((span, sugg)) = sugg {
+ diag.span_suggestion(
+ span,
+ "use an inclusive range",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ );
+ }
+}
/// ```rust
/// let x: Box<String> = Box::default();
/// ```
- #[clippy::version = "1.65.0"]
+ #[clippy::version = "1.66.0"]
pub BOX_DEFAULT,
perf,
"Using Box::new(T::default()) instead of Box::default()"
/// ```rust,ignore
/// let _: = 0_u64;
/// ```
- #[clippy::version = "1.64.0"]
+ #[clippy::version = "1.66.0"]
pub CAST_NAN_TO_INT,
suspicious,
"casting a known floating-point NaN into an integer"
crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
- crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO,
+ crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,
crate::as_conversions::AS_CONVERSIONS_INFO,
crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO,
crate::returns::NEEDLESS_RETURN_INFO,
crate::same_name_method::SAME_NAME_METHOD_INFO,
crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO,
+ crate::semicolon_block::SEMICOLON_INSIDE_BLOCK_INFO,
+ crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO,
crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO,
crate::serde_api::SERDE_API_MISUSE_INFO,
crate::shadow::SHADOW_REUSE_INFO,
continue;
},
ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
- ty::Alias(ty::Projection, _) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty),
- ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Alias(ty::Opaque, ..) | ty::Placeholder(_) | ty::Dynamic(..) => {
- Position::ReborrowStable(precedence).into()
+ ty::Alias(ty::Projection, _) if ty.has_non_region_param() => {
+ TyPosition::new_deref_stable_for_result(precedence, ty)
},
+ ty::Infer(_)
+ | ty::Error(_)
+ | ty::Bound(..)
+ | ty::Alias(ty::Opaque, ..)
+ | ty::Placeholder(_)
+ | ty::Dynamic(..) => Position::ReborrowStable(precedence).into(),
ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
Position::ReborrowStable(precedence).into()
},
tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate {
- trait_ref: tcx.mk_trait_ref(
- eq_trait_id,
- [tcx.mk_param_from_def(param)],
- ),
+ trait_ref: tcx.mk_trait_ref(eq_trait_id, [tcx.mk_param_from_def(param)]),
constness: BoundConstness::NotConst,
polarity: ImplPolarity::Positive,
}))))
/// value: usize,
/// }
/// ```
- #[clippy::version = "1.65.0"]
+ #[clippy::version = "1.66.0"]
pub DISALLOWED_MACROS,
style,
"use of a disallowed macro"
use clippy_utils::is_diag_trait_item;
use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred};
use clippy_utils::macros::{
- is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage,
+ is_assert_macro, is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam,
+ FormatParamUsage,
};
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_opt;
///
/// If a format string contains a numbered argument that cannot be inlined
/// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`.
- #[clippy::version = "1.65.0"]
+ #[clippy::version = "1.66.0"]
pub UNINLINED_FORMAT_ARGS,
style,
"using non-inlined variables in `format!` calls"
if args.format_string.span.from_expansion() {
return;
}
- if call_site.edition() < Edition2021 && is_panic(cx, def_id) {
- // panic! before 2021 edition considers a single string argument as non-format
+ if call_site.edition() < Edition2021 && (is_panic(cx, def_id) || is_assert_macro(cx, def_id)) {
+ // panic!, assert!, and debug_assert! before 2021 edition considers a single string argument as
+ // non-format
return;
}
TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::nested_filter::OnlyBodies;
+use rustc_middle::{hir::nested_filter::OnlyBodies, ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, sym};
use rustc_span::{Span, Symbol};
&& let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
&& let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
&& cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
+ && !matches!(middle_trait_ref.substs.type_at(1).kind(), ty::Alias(ty::Opaque, _))
{
span_lint_and_then(
cx,
///
/// u = u.saturating_add(1);
/// ```
- #[clippy::version = "1.65.0"]
+ #[clippy::version = "1.66.0"]
pub IMPLICIT_SATURATING_ADD,
style,
"Perform saturating addition instead of implicitly checking max bound of data type"
//! lint on indexing and slicing operations
use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::higher;
use rustc_ast::ast::RangeLimits;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
/// ### What it does
"indexing/slicing usage"
}
-declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
+impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
+
+#[derive(Copy, Clone)]
+pub struct IndexingSlicing {
+ suppress_restriction_lint_in_const: bool,
+}
+
+impl IndexingSlicing {
+ pub fn new(suppress_restriction_lint_in_const: bool) -> Self {
+ Self {
+ suppress_restriction_lint_in_const,
+ }
+ }
+}
impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+ if self.suppress_restriction_lint_in_const && cx.tcx.hir().is_inside_const_context(expr.hir_id) {
return;
}
if let ExprKind::Index(array, index) = &expr.kind {
+ let note = "the suggestion might not be applicable in constant blocks";
let ty = cx.typeck_results().expr_ty(array).peel_refs();
if let Some(range) = higher::Range::hir(index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
(None, None) => return, // [..] is ok.
};
- span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg);
+ span_lint_and_then(cx, INDEXING_SLICING, expr.span, "slicing may panic", |diag| {
+ diag.help(help_msg);
+
+ if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+ diag.note(note);
+ }
+ });
} else {
// Catchall non-range index, i.e., [n] or [n << m]
if let ty::Array(..) = ty.kind() {
}
}
- span_lint_and_help(
- cx,
- INDEXING_SLICING,
- expr.span,
- "indexing may panic",
- None,
- "consider using `.get(n)` or `.get_mut(n)` instead",
- );
+ span_lint_and_then(cx, INDEXING_SLICING, expr.span, "indexing may panic", |diag| {
+ diag.help("consider using `.get(n)` or `.get_mut(n)` instead");
+
+ if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+ diag.note(note);
+ }
+ });
}
}
}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
+use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefIdSet;
use rustc_hir::{
def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
- ItemKind, Mutability, Node, TraitItemRef, TyKind,
+ ItemKind, Mutability, Node, TraitItemRef, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
source_map::{Span, Spanned, Symbol},
symbol::sym,
};
+use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
let mut applicability = Applicability::MachineApplicable;
+
+ let lit1 = peel_ref_operators(cx, lit1);
+ let mut lit_str = snippet_with_applicability(cx, lit1.span, "_", &mut applicability);
+
+ // Wrap the expression in parentheses if it's a deref expression. Otherwise operator precedence will
+ // cause the code to dereference boolean(won't compile).
+ if let ExprKind::Unary(UnOp::Deref, _) = lit1.kind {
+ lit_str = Cow::from(format!("({lit_str})"));
+ }
+
span_lint_and_sugg(
cx,
COMPARISON_TO_EMPTY,
span,
"comparison to empty slice",
&format!("using `{op}is_empty` is clearer and more explicit"),
- format!(
- "{op}{}.is_empty()",
- snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
- ),
+ format!("{op}{lit_str}.is_empty()"),
applicability,
);
}
mod renamed_lints;
// begin lints modules, do not remove this comment, it’s used in `update_lints`
-mod almost_complete_letter_range;
+mod almost_complete_range;
mod approx_const;
mod as_conversions;
mod asm_syntax;
mod returns;
mod same_name_method;
mod self_named_constructors;
+mod semicolon_block;
mod semicolon_if_nothing_returned;
mod serde_api;
mod shadow;
}
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
+ let arithmetic_side_effects_allowed_binary = conf.arithmetic_side_effects_allowed_binary.clone();
+ let arithmetic_side_effects_allowed_unary = conf.arithmetic_side_effects_allowed_unary.clone();
store.register_late_pass(move |_| {
Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
- arithmetic_side_effects_allowed.clone(),
+ arithmetic_side_effects_allowed
+ .iter()
+ .flat_map(|el| [[el.clone(), "*".to_string()], ["*".to_string(), el.clone()]])
+ .chain(arithmetic_side_effects_allowed_binary.clone())
+ .collect(),
+ arithmetic_side_effects_allowed
+ .iter()
+ .chain(arithmetic_side_effects_allowed_unary.iter())
+ .cloned()
+ .collect(),
))
});
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
- store.register_late_pass(|_| Box::new(misc::MiscLints));
+ store.register_late_pass(|_| Box::<misc::LintPass>::default());
store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
store.register_late_pass(|_| Box::new(mut_mut::MutMut));
store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
let allow_expect_in_tests = conf.allow_expect_in_tests;
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
+ let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const;
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
store.register_late_pass(move |_| {
Box::new(methods::Methods::new(
store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl));
store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
store.register_late_pass(|_| Box::new(unwrap::Unwrap));
- store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing));
+ store.register_late_pass(move |_| {
+ Box::new(indexing_slicing::IndexingSlicing::new(
+ suppress_restriction_lint_in_const,
+ ))
+ });
store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default());
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
- store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv())));
+ store.register_early_pass(move || Box::new(almost_complete_range::AlmostCompleteRange::new(msrv())));
store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
+ store.register_late_pass(|_| Box::new(semicolon_block::SemicolonBlock));
// add lints here, do not remove this comment, it's used in `new_lint`
}
cx: &'a LateContext<'tcx>, // context reference
states: HirIdMap<IncrementVisitorVarState>, // incremented variables
depth: u32, // depth of conditional expressions
- done: bool,
}
impl<'a, 'tcx> IncrementVisitor<'a, 'tcx> {
cx,
states: HirIdMap::default(),
depth: 0,
- done: false,
}
}
impl<'a, 'tcx> Visitor<'tcx> for IncrementVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
- if self.done {
- return;
- }
-
// If node is a variable
if let Some(def_id) = path_to_local(expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) {
walk_expr(self, expr);
self.depth -= 1;
} else if let ExprKind::Continue(_) = expr.kind {
- self.done = true;
+ // If we see a `continue` block, then we increment depth so that the IncrementVisitor
+ // state will be set to DontWarn if we see the variable being modified anywhere afterwards.
+ self.depth += 1;
} else {
walk_expr(self, expr);
}
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{root_macro_call, FormatArgsExpn};
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{peel_blocks_with_stmt, span_extract_comment, sugg};
+use clippy_utils::{is_else_clause, peel_blocks_with_stmt, span_extract_comment, sugg};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
if cx.tcx.item_name(macro_call.def_id) == sym::panic;
if !cx.tcx.sess.source_map().is_multiline(cond.span);
if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn);
+ // Don't change `else if foo { panic!(..) }` to `else { assert!(foo, ..) }` as it just
+ // shuffles the condition around.
+ // Should this have a config value?
+ if !is_else_clause(cx.tcx, expr);
then {
let mut applicability = Applicability::MachineApplicable;
let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability);
use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet};
+use clippy_utils::{diagnostics::span_lint_and_sugg, higher, in_constant, macros::root_macro_call, source::snippet};
+use rustc_ast::ast::RangeLimits;
use rustc_ast::LitKind::{Byte, Char};
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
+use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{def_id::DefId, sym};
+use rustc_span::{def_id::DefId, sym, Span};
declare_clippy_lint! {
/// ### What it does
/// assert!(matches!(b'X', b'A'..=b'Z'));
/// assert!(matches!('2', '0'..='9'));
/// assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
+ ///
+ /// ('0'..='9').contains(&'0');
+ /// ('a'..='z').contains(&'a');
+ /// ('A'..='Z').contains(&'A');
/// }
/// ```
/// Use instead:
/// assert!(b'X'.is_ascii_uppercase());
/// assert!('2'.is_ascii_digit());
/// assert!('x'.is_ascii_alphabetic());
+ ///
+ /// '0'.is_ascii_digit();
+ /// 'a'.is_ascii_lowercase();
+ /// 'A'.is_ascii_uppercase();
/// }
/// ```
#[clippy::version = "1.66.0"]
return;
}
- let Some(macro_call) = root_macro_call(expr.span) else { return };
-
- if is_matches_macro(cx, macro_call.def_id) {
+ if let Some(macro_call) = root_macro_call(expr.span)
+ && is_matches_macro(cx, macro_call.def_id) {
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
let range = check_pat(&arm.pat.kind);
-
- if let Some(sugg) = match range {
- CharRange::UpperChar => Some("is_ascii_uppercase"),
- CharRange::LowerChar => Some("is_ascii_lowercase"),
- CharRange::FullChar => Some("is_ascii_alphabetic"),
- CharRange::Digit => Some("is_ascii_digit"),
- CharRange::Otherwise => None,
- } {
- let default_snip = "..";
- // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
- // macro span, so we check applicability manually by comparing `recv` is not default.
- let recv = snippet(cx, recv.span, default_snip);
-
- let applicability = if recv == default_snip {
- Applicability::HasPlaceholders
- } else {
- Applicability::MachineApplicable
- };
-
- span_lint_and_sugg(
- cx,
- MANUAL_IS_ASCII_CHECK,
- macro_call.span,
- "manual check for common ascii range",
- "try",
- format!("{recv}.{sugg}()"),
- applicability,
- );
- }
+ check_is_ascii(cx, macro_call.span, recv, &range);
+ }
+ } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
+ && path.ident.name == sym!(contains)
+ && let Some(higher::Range { start: Some(start), end: Some(end), limits: RangeLimits::Closed })
+ = higher::Range::hir(receiver) {
+ let range = check_range(start, end);
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
+ check_is_ascii(cx, expr.span, e, &range);
+ } else {
+ check_is_ascii(cx, expr.span, arg, &range);
}
}
}
extract_msrv_attr!(LateContext);
}
+fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) {
+ if let Some(sugg) = match range {
+ CharRange::UpperChar => Some("is_ascii_uppercase"),
+ CharRange::LowerChar => Some("is_ascii_lowercase"),
+ CharRange::FullChar => Some("is_ascii_alphabetic"),
+ CharRange::Digit => Some("is_ascii_digit"),
+ CharRange::Otherwise => None,
+ } {
+ let default_snip = "..";
+ // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
+ // macro span, so we check applicability manually by comparing `recv` is not default.
+ let recv = snippet(cx, recv.span, default_snip);
+
+ let applicability = if recv == default_snip {
+ Applicability::HasPlaceholders
+ } else {
+ Applicability::MachineApplicable
+ };
+
+ span_lint_and_sugg(
+ cx,
+ MANUAL_IS_ASCII_CHECK,
+ span,
+ "manual check for common ascii range",
+ "try",
+ format!("{recv}.{sugg}()"),
+ applicability,
+ );
+ }
+}
+
fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
match pat_kind {
PatKind::Or(pats) => {
} else {
format!("{{ {sn_else} }}")
};
- let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};");
+ let sn_bl = if matches!(pat.kind, PatKind::Or(..)) {
+ format!("({sn_pat})")
+ } else {
+ sn_pat.into_owned()
+ };
+ let sugg = format!("let {sn_bl} = {sn_expr} else {else_bl};");
diag.span_suggestion(span, "consider writing", sugg, app);
},
);
&& seg.args.is_none()
&& let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
- && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
+ && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id)
+ {
check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv);
check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv);
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::peel_mid_ty_refs;
+use clippy_utils::ty::{implements_trait, peel_mid_ty_refs};
use clippy_utils::{is_diag_item_method, is_diag_trait_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
let (input_type, ref_count) = peel_mid_ty_refs(input_type);
if let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did()));
if return_type == input_type;
+ if let Some(clone_trait) = cx.tcx.lang_items().clone_trait();
+ if implements_trait(cx, return_type, clone_trait, &[]);
then {
let mut app = Applicability::MachineApplicable;
let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
/// let map: HashMap<u32, u32> = HashMap::new();
/// let values = map.values().collect::<Vec<_>>();
/// ```
- #[clippy::version = "1.65.0"]
+ #[clippy::version = "1.66.0"]
pub ITER_KV_MAP,
complexity,
"iterating on map using `iter` when `keys` or `values` would do"
no_effect_replace::check(cx, expr, arg1, arg2);
// Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
- if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
+ if self.msrv.meets(msrvs::PATTERN_TRAIT_CHAR_ARRAY)
+ && name == "replace"
+ && let Some(("replace", ..)) = method_call(recv)
+ {
collapsible_str_replace::check(cx, expr, arg1, arg2);
}
},
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, match_def_path, paths};
+use clippy_utils::{get_trait_def_id, is_expr_used_or_unified, match_def_path, paths};
use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
// Get receiver type
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
+ if is_expr_used_or_unified(cx.tcx, expr) {
+ return;
+ }
+
if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) &&
implements_trait(cx, ty, seek_trait_id, &[]) &&
let ExprKind::Call(func, args1) = arg.kind &&
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::hygiene::DesugaringKind;
use rustc_span::source_map::{ExpnKind, Span};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_literal, iter_input_pats, last_path_segment, SpanlessEq};
+use clippy_utils::{
+ get_parent_expr, in_constant, is_integer_literal, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq,
+};
declare_clippy_lint! {
/// ### What it does
"using `0 as *{const, mut} T`"
}
-declare_lint_pass!(MiscLints => [
+pub struct LintPass {
+ std_or_core: &'static str,
+}
+impl Default for LintPass {
+ fn default() -> Self {
+ Self { std_or_core: "std" }
+ }
+}
+impl_lint_pass!(LintPass => [
TOPLEVEL_REF_ARG,
USED_UNDERSCORE_BINDING,
SHORT_CIRCUIT_STATEMENT,
ZERO_PTR,
]);
-impl<'tcx> LateLintPass<'tcx> for MiscLints {
+impl<'tcx> LateLintPass<'tcx> for LintPass {
+ fn check_crate(&mut self, cx: &LateContext<'_>) {
+ if is_no_std_crate(cx) {
+ self.std_or_core = "core";
+ }
+ }
+
fn check_fn(
&mut self,
cx: &LateContext<'tcx>,
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Cast(e, ty) = expr.kind {
- check_cast(cx, expr.span, e, ty);
+ self.check_cast(cx, expr.span, e, ty);
return;
}
if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
}
}
-fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
- if_chain! {
- if let TyKind::Ptr(ref mut_ty) = ty.kind;
- if is_integer_literal(e, 0);
- if !in_constant(cx, e.hir_id);
- then {
- let (msg, sugg_fn) = match mut_ty.mutbl {
- Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"),
- Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"),
- };
+impl LintPass {
+ fn check_cast(&self, cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
+ if_chain! {
+ if let TyKind::Ptr(ref mut_ty) = ty.kind;
+ if is_integer_literal(e, 0);
+ if !in_constant(cx, e.hir_id);
+ then {
+ let (msg, sugg_fn) = match mut_ty.mutbl {
+ Mutability::Mut => ("`0 as *mut _` detected", "ptr::null_mut"),
+ Mutability::Not => ("`0 as *const _` detected", "ptr::null"),
+ };
- let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
- (format!("{sugg_fn}()"), Applicability::MachineApplicable)
- } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
- (format!("{sugg_fn}::<{mut_ty_snip}>()"), Applicability::MachineApplicable)
- } else {
- // `MaybeIncorrect` as type inference may not work with the suggested code
- (format!("{sugg_fn}()"), Applicability::MaybeIncorrect)
- };
- span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
+ let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
+ (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MachineApplicable)
+ } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
+ (format!("{}::{sugg_fn}::<{mut_ty_snip}>()", self.std_or_core), Applicability::MachineApplicable)
+ } else {
+ // `MaybeIncorrect` as type inference may not work with the suggested code
+ (format!("{}::{sugg_fn}()", self.std_or_core), Applicability::MaybeIncorrect)
+ };
+ span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
+ }
}
}
}
peel_hir_expr_refs,
};
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::impl_lint_pass;
use rustc_span::source_map::{Span, Spanned};
-const HARD_CODED_ALLOWED: &[&str] = &[
- "&str",
- "f32",
- "f64",
- "std::num::Saturating",
- "std::num::Wrapping",
- "std::string::String",
+const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[
+ ["f32", "f32"],
+ ["f64", "f64"],
+ ["std::num::Saturating", "std::num::Saturating"],
+ ["std::num::Wrapping", "std::num::Wrapping"],
+ ["std::string::String", "&str"],
];
+const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
#[derive(Debug)]
pub struct ArithmeticSideEffects {
- allowed: FxHashSet<String>,
+ allowed_binary: FxHashMap<String, FxHashSet<String>>,
+ allowed_unary: FxHashSet<String>,
// Used to check whether expressions are constants, such as in enum discriminants and consts
const_span: Option<Span>,
expr_span: Option<Span>,
impl ArithmeticSideEffects {
#[must_use]
- pub fn new(mut allowed: FxHashSet<String>) -> Self {
- allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
+ pub fn new(user_allowed_binary: Vec<[String; 2]>, user_allowed_unary: Vec<String>) -> Self {
+ let mut allowed_binary: FxHashMap<String, FxHashSet<String>> = <_>::default();
+ for [lhs, rhs] in user_allowed_binary.into_iter().chain(
+ HARD_CODED_ALLOWED_BINARY
+ .iter()
+ .copied()
+ .map(|[lhs, rhs]| [lhs.to_string(), rhs.to_string()]),
+ ) {
+ allowed_binary.entry(lhs).or_default().insert(rhs);
+ }
+ let allowed_unary = user_allowed_unary
+ .into_iter()
+ .chain(HARD_CODED_ALLOWED_UNARY.iter().copied().map(String::from))
+ .collect();
Self {
- allowed,
+ allowed_binary,
+ allowed_unary,
const_span: None,
expr_span: None,
}
}
- /// Checks if the given `expr` has any of the inner `allowed` elements.
- fn is_allowed_ty(&self, ty: Ty<'_>) -> bool {
- self.allowed
- .contains(ty.to_string().split('<').next().unwrap_or_default())
+ /// Checks if the lhs and the rhs types of a binary operation like "addition" or
+ /// "multiplication" are present in the inner set of allowed types.
+ fn has_allowed_binary(&self, lhs_ty: Ty<'_>, rhs_ty: Ty<'_>) -> bool {
+ let lhs_ty_string = lhs_ty.to_string();
+ let lhs_ty_string_elem = lhs_ty_string.split('<').next().unwrap_or_default();
+ let rhs_ty_string = rhs_ty.to_string();
+ let rhs_ty_string_elem = rhs_ty_string.split('<').next().unwrap_or_default();
+ if let Some(rhs_from_specific) = self.allowed_binary.get(lhs_ty_string_elem)
+ && {
+ let rhs_has_allowed_ty = rhs_from_specific.contains(rhs_ty_string_elem);
+ rhs_has_allowed_ty || rhs_from_specific.contains("*")
+ }
+ {
+ true
+ } else if let Some(rhs_from_glob) = self.allowed_binary.get("*") {
+ rhs_from_glob.contains(rhs_ty_string_elem)
+ } else {
+ false
+ }
+ }
+
+ /// Checks if the type of an unary operation like "negation" is present in the inner set of
+ /// allowed types.
+ fn has_allowed_unary(&self, ty: Ty<'_>) -> bool {
+ let ty_string = ty.to_string();
+ let ty_string_elem = ty_string.split('<').next().unwrap_or_default();
+ self.allowed_unary.contains(ty_string_elem)
}
// For example, 8i32 or &i64::MAX.
};
let lhs_ty = cx.typeck_results().expr_ty(lhs);
let rhs_ty = cx.typeck_results().expr_ty(rhs);
- let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty;
- if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) {
+ if self.has_allowed_binary(lhs_ty, rhs_ty) {
return;
}
let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
return;
}
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
- if self.is_allowed_ty(ty) {
+ if self.has_allowed_unary(ty) {
return;
}
let actual_un_expr = peel_hir_expr_refs(un_expr).0;
use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{clip, unsext};
+use clippy_utils::{clip, peel_hir_expr_refs, unsext};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
use rustc_lint::LateContext;
if !is_allowed(cx, op, left, right) {
match op {
BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
- check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
- check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
+ check_op(
+ cx,
+ left,
+ 0,
+ expr.span,
+ peel_hir_expr_refs(right).0.span,
+ needs_parenthesis(cx, expr, right),
+ );
+ check_op(
+ cx,
+ right,
+ 0,
+ expr.span,
+ peel_hir_expr_refs(left).0.span,
+ Parens::Unneeded,
+ );
},
BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
- check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
+ check_op(
+ cx,
+ right,
+ 0,
+ expr.span,
+ peel_hir_expr_refs(left).0.span,
+ Parens::Unneeded,
+ );
},
BinOpKind::Mul => {
- check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
- check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded);
+ check_op(
+ cx,
+ left,
+ 1,
+ expr.span,
+ peel_hir_expr_refs(right).0.span,
+ needs_parenthesis(cx, expr, right),
+ );
+ check_op(
+ cx,
+ right,
+ 1,
+ expr.span,
+ peel_hir_expr_refs(left).0.span,
+ Parens::Unneeded,
+ );
},
- BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded),
+ BinOpKind::Div => check_op(
+ cx,
+ right,
+ 1,
+ expr.span,
+ peel_hir_expr_refs(left).0.span,
+ Parens::Unneeded,
+ ),
BinOpKind::BitAnd => {
- check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
- check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded);
+ check_op(
+ cx,
+ left,
+ -1,
+ expr.span,
+ peel_hir_expr_refs(right).0.span,
+ needs_parenthesis(cx, expr, right),
+ );
+ check_op(
+ cx,
+ right,
+ -1,
+ expr.span,
+ peel_hir_expr_refs(left).0.span,
+ Parens::Unneeded,
+ );
},
BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
_ => (),
/// use rust_decimal::Decimal;
/// let _n = Decimal::MAX + Decimal::MAX;
/// ```
- ///
- /// ### Allowed types
- /// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter.
#[clippy::version = "1.64.0"]
pub ARITHMETIC_SIDE_EFFECTS,
restriction,
fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
if let ItemKind::Use(path, _) = item.kind {
- if path.res.iter().all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _))) {
+ if path
+ .res
+ .iter()
+ .all(|res| matches!(res, Res::Def(DefKind::Macro(MacroKind::Bang), _)))
+ {
return false;
}
} else if let ItemKind::Macro(..) = item.kind {
TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
let snip = snippet(cx, borrow_type.ty.span, "<type>");
- let sugg = format!("&{snip}");
+ let sugg = format!("&{}{snip}", borrow_type.mutbl.prefix_str());
span_lint_and_then(
cx,
REDUNDANT_STATIC_LIFETIMES,
#[rustfmt::skip]
pub static RENAMED_LINTS: &[(&str, &str)] = &[
+ ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"),
("clippy::blacklisted_name", "clippy::disallowed_names"),
("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
--- /dev/null
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Suggests moving the semicolon after a block to the inside of the block, after its last
+ /// expression.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine
+ /// and this lint suggests inside the block.
+ /// Take a look at `semicolon_outside_block` for the other alternative.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # fn f(_: u32) {}
+ /// # let x = 0;
+ /// unsafe { f(x) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # fn f(_: u32) {}
+ /// # let x = 0;
+ /// unsafe { f(x); }
+ /// ```
+ #[clippy::version = "1.66.0"]
+ pub SEMICOLON_INSIDE_BLOCK,
+ restriction,
+ "add a semicolon inside the block"
+}
+declare_clippy_lint! {
+ /// ### What it does
+ ///
+ /// Suggests moving the semicolon from a block's final expression outside of the block.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine
+ /// and this lint suggests outside the block.
+ /// Take a look at `semicolon_inside_block` for the other alternative.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # fn f(_: u32) {}
+ /// # let x = 0;
+ /// unsafe { f(x); }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// # fn f(_: u32) {}
+ /// # let x = 0;
+ /// unsafe { f(x) };
+ /// ```
+ #[clippy::version = "1.66.0"]
+ pub SEMICOLON_OUTSIDE_BLOCK,
+ restriction,
+ "add a semicolon outside the block"
+}
+declare_lint_pass!(SemicolonBlock => [SEMICOLON_INSIDE_BLOCK, SEMICOLON_OUTSIDE_BLOCK]);
+
+impl LateLintPass<'_> for SemicolonBlock {
+ fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+ match stmt.kind {
+ StmtKind::Expr(Expr {
+ kind: ExprKind::Block(block, _),
+ ..
+ }) if !block.span.from_expansion() => {
+ let Block {
+ expr: None,
+ stmts: [.., stmt],
+ ..
+ } = block else { return };
+ let &Stmt {
+ kind: StmtKind::Semi(expr),
+ span,
+ ..
+ } = stmt else { return };
+ semicolon_outside_block(cx, block, expr, span);
+ },
+ StmtKind::Semi(Expr {
+ kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _),
+ ..
+ }) if !block.span.from_expansion() => semicolon_inside_block(cx, block, tail, stmt.span),
+ _ => (),
+ }
+ }
+}
+
+fn semicolon_inside_block(cx: &LateContext<'_>, block: &Block<'_>, tail: &Expr<'_>, semi_span: Span) {
+ let insert_span = tail.span.source_callsite().shrink_to_hi();
+ let remove_span = semi_span.with_lo(block.span.hi());
+
+ span_lint_and_then(
+ cx,
+ SEMICOLON_INSIDE_BLOCK,
+ semi_span,
+ "consider moving the `;` inside the block for consistent formatting",
+ |diag| {
+ multispan_sugg_with_applicability(
+ diag,
+ "put the `;` here",
+ Applicability::MachineApplicable,
+ [(remove_span, String::new()), (insert_span, ";".to_owned())],
+ );
+ },
+ );
+}
+
+fn semicolon_outside_block(cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>, semi_span: Span) {
+ let insert_span = block.span.with_lo(block.span.hi());
+ // account for macro calls
+ let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
+ let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
+
+ span_lint_and_then(
+ cx,
+ SEMICOLON_OUTSIDE_BLOCK,
+ block.span,
+ "consider moving the `;` outside the block for consistent formatting",
+ |diag| {
+ multispan_sugg_with_applicability(
+ diag,
+ "put the `;` here",
+ Applicability::MachineApplicable,
+ [(remove_span, String::new()), (insert_span, ";".to_owned())],
+ );
+ },
+ );
+}
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_type_lang_item;
+use clippy_utils::{get_expr_use_or_unification_node, peel_blocks, SpanlessEq};
use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method_calls, paths};
-use clippy_utils::{peel_blocks, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
-use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, Node, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
declare_lint_pass!(StringLitAsBytes => [STRING_LIT_AS_BYTES, STRING_FROM_UTF8_AS_BYTES]);
impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
+ #[expect(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
use rustc_ast::LitKind;
&& lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
&& !receiver.span.from_expansion()
{
- span_lint_and_sugg(
- cx,
- STRING_LIT_AS_BYTES,
- e.span,
- "calling `as_bytes()` on a string literal",
- "consider using a byte string literal instead",
- format!(
- "b{}",
- snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability)
- ),
- applicability,
- );
+ if let Some((parent, id)) = get_expr_use_or_unification_node(cx.tcx, e)
+ && let Node::Expr(parent) = parent
+ && let ExprKind::Match(scrutinee, ..) = parent.kind
+ && scrutinee.hir_id == id
+ {
+ // Don't lint. Byte strings produce `&[u8; N]` whereas `as_bytes()` produces
+ // `&[u8]`. This change would prevent matching with different sized slices.
+ } else {
+ span_lint_and_sugg(
+ cx,
+ STRING_LIT_AS_BYTES,
+ e.span,
+ "calling `as_bytes()` on a string literal",
+ "consider using a byte string literal instead",
+ format!(
+ "b{}",
+ snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability)
+ ),
+ applicability,
+ );
+ }
}
}
}
}
define_Conf! {
- /// Lint: Arithmetic.
+ /// Lint: ARITHMETIC_SIDE_EFFECTS.
///
- /// Suppress checking of the passed type names.
+ /// Suppress checking of the passed type names in all types of operations.
+ ///
+ /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
+ ///
+ /// #### Example
+ ///
+ /// ```toml
+ /// arithmetic-side-effects-allowed = ["SomeType", "AnotherType"]
+ /// ```
+ ///
+ /// #### Noteworthy
+ ///
+ /// A type, say `SomeType`, listed in this configuration has the same behavior of `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
(arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
+ /// Lint: ARITHMETIC_SIDE_EFFECTS.
+ ///
+ /// Suppress checking of the passed type pair names in binary operations like addition or
+ /// multiplication.
+ ///
+ /// Supports the "*" wildcard to indicate that a certain type won't trigger the lint regardless
+ /// of the involved counterpart. For example, `["SomeType", "*"]` or `["*", "AnotherType"]`.
+ ///
+ /// Pairs are asymmetric, which means that `["SomeType", "AnotherType"]` is not the same as
+ /// `["AnotherType", "SomeType"]`.
+ ///
+ /// #### Example
+ ///
+ /// ```toml
+ /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
+ /// ```
+ (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()),
+ /// Lint: ARITHMETIC_SIDE_EFFECTS.
+ ///
+ /// Suppress checking of the passed type names in unary operations like "negation" (`-`).
+ ///
+ /// #### Example
+ ///
+ /// ```toml
+ /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
+ /// ```
+ (arithmetic_side_effects_allowed_unary: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
///
/// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
(allow_mixed_uninlined_format_args: bool = true),
+ /// Lint: INDEXING_SLICING
+ ///
+ /// Whether to suppress a restriction lint in constant code. In same
+ /// cases the restructured operation might not be unavoidable, as the
+ /// suggested counterparts are unavailable in constant code. This
+ /// configuration will cause restriction lints to trigger even
+ /// if no suggestion can be made.
+ (suppress_restriction_lint_in_const: bool = false),
}
/// Search for the configuration file.
use rustc_hir::Item;
use rustc_hir_analysis::hir_ty_to_ty;
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy};
+use rustc_middle::ty::{self, fast_reject::SimplifiedType, FloatTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::Symbol;
let lang_items = cx.tcx.lang_items();
// This list isn't complete, but good enough for our current list of paths.
let incoherent_impls = [
- SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
- SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
- SimplifiedTypeGen::SliceSimplifiedType,
- SimplifiedTypeGen::StrSimplifiedType,
+ SimplifiedType::FloatSimplifiedType(FloatTy::F32),
+ SimplifiedType::FloatSimplifiedType(FloatTy::F64),
+ SimplifiedType::SliceSimplifiedType,
+ SimplifiedType::StrSimplifiedType,
]
.iter()
.flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied());
[package]
name = "clippy_utils"
-version = "0.1.67"
+version = "0.1.68"
edition = "2021"
publish = false
let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
match cx.tcx.hir().get_by_def_id(parent_id) {
Node::Item(&Item {
- kind: ItemKind::Const(..) | ItemKind::Static(..),
+ kind: ItemKind::Const(..) | ItemKind::Static(..) | ItemKind::Enum(..),
..
})
| Node::TraitItem(&TraitItem {
)
}
+/// Is `def_id` of `assert!` or `debug_assert!`
+pub fn is_assert_macro(cx: &LateContext<'_>, def_id: DefId) -> bool {
+ let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
+ matches!(name, sym::assert_macro | sym::debug_assert_macro)
+}
+
pub enum PanicExpn<'a> {
/// No arguments - `panic!()`
Empty,
msrv_aliases! {
1,65,0 { LET_ELSE }
1,62,0 { BOOL_THEN_SOME }
- 1,58,0 { FORMAT_ARGS_CAPTURE }
+ 1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
-pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
check_operand(tcx, value, span, body)
},
- TerminatorKind::SwitchInt {
- discr,
- targets: _,
- } => check_operand(tcx, discr, span, body),
+ TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
- self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
- AliasTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
+ self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate,
+ PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
VariantDef, VariantDiscr,
};
use rustc_middle::ty::{GenericArg, GenericArgKind};
use crate::{match_def_path, path_res, paths};
-// Checks if the given type implements copy.
+/// Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
}
/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
/// will also return `true`.
pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
- ty.walk().any(|inner| match inner.unpack() {
- GenericArgKind::Type(inner_ty) => {
- if inner_ty == needle {
- return true;
- }
+ fn contains_ty_adt_constructor_opaque_inner<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty: Ty<'tcx>,
+ needle: Ty<'tcx>,
+ seen: &mut FxHashSet<DefId>,
+ ) -> bool {
+ ty.walk().any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => {
+ if inner_ty == needle {
+ return true;
+ }
- if inner_ty.ty_adt_def() == needle.ty_adt_def() {
- return true;
- }
+ if inner_ty.ty_adt_def() == needle.ty_adt_def() {
+ return true;
+ }
- if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() {
- for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
- match predicate.kind().skip_binder() {
- // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
- // and check substituions to find `U`.
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
- if trait_predicate
- .trait_ref
- .substs
- .types()
- .skip(1) // Skip the implicit `Self` generic parameter
- .any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle))
- {
- return true;
- }
- },
- // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
- // so we check the term for `U`.
- ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => {
- if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
- if contains_ty_adt_constructor_opaque(cx, ty, needle) {
+ if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = *inner_ty.kind() {
+ if !seen.insert(def_id) {
+ return false;
+ }
+
+ for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
+ match predicate.kind().skip_binder() {
+ // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
+ // and check substituions to find `U`.
+ ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ if trait_predicate
+ .trait_ref
+ .substs
+ .types()
+ .skip(1) // Skip the implicit `Self` generic parameter
+ .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen))
+ {
return true;
}
- };
- },
- _ => (),
+ },
+ // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
+ // so we check the term for `U`.
+ ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => {
+ if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
+ if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
+ return true;
+ }
+ };
+ },
+ _ => (),
+ }
}
}
- }
- false
- },
- GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
- })
+ false
+ },
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+ }
+
+ // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not
+ // visited twice.
+ let mut seen = FxHashSet::default();
+ contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen)
}
/// Resolves `<T as Iterator>::Item` for `T`
Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
},
ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
- ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id)),
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
+ sig_from_bounds(cx, ty, cx.tcx.item_bounds(def_id), cx.tcx.opt_parent(def_id))
+ },
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
ty::Dynamic(bounds, _, _) => {
let lang_items = cx.tcx.lang_items();
inputs = Some(i);
},
PredicateKind::Clause(ty::Clause::Projection(p))
- if Some(p.projection_ty.def_id) == lang_items.fn_once_output()
- && p.projection_ty.self_ty() == ty =>
+ if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty =>
{
if output.is_some() {
// Multiple different fn trait impls. Is this even allowed?
}
}
- Some(tcx.mk_alias_ty(
- assoc_item.def_id,
- substs,
- ))
+ Some(tcx.mk_alias_ty(assoc_item.def_id, substs))
}
helper(
tcx,
[package]
name = "declare_clippy_lint"
-version = "0.1.67"
+version = "0.1.68"
edition = "2021"
publish = false
[toolchain]
-channel = "nightly-2022-12-01"
+channel = "nightly-2022-12-17"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
--- /dev/null
+# Changelog
+
+## Version 0.3.0
+
+* Added `setup_version_info!();` macro for automated scripts.
+* `get_version_info!()` no longer requires the user to import `rustc_tools_util::VersionInfo` and `std::env`
[package]
name = "rustc_tools_util"
-version = "0.2.1"
+version = "0.3.0"
description = "small helper to generate version information for git packages"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
List rustc_tools_util as regular AND build dependency.
````toml
[dependencies]
-rustc_tools_util = "0.2.1"
+rustc_tools_util = "0.3.0"
[build-dependencies]
-rustc_tools_util = "0.2.1"
+rustc_tools_util = "0.3.0"
````
In `build.rs`, generate the data in your `main()`
-````rust
+
+```rust
fn main() {
- println!(
- "cargo:rustc-env=GIT_HASH={}",
- rustc_tools_util::get_commit_hash().unwrap_or_default()
- );
- println!(
- "cargo:rustc-env=COMMIT_DATE={}",
- rustc_tools_util::get_commit_date().unwrap_or_default()
- );
- println!(
- "cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}",
- rustc_tools_util::get_channel().unwrap_or_default()
- );
+ rustc_tools_util::setup_version_info!();
}
-
-````
+```
Use the version information in your main.rs
-````rust
-use rustc_tools_util::*;
+```rust
fn show_version() {
let version_info = rustc_tools_util::get_version_info!();
println!("{}", version_info);
}
-````
+```
+
This gives the following output in clippy:
-`clippy 0.0.212 (a416c5e 2018-12-14)`
+`clippy 0.1.66 (a28f3c8 2022-11-20)`
+
+## Repository
+
+This project is part of the rust-lang/rust-clippy repository. The source code
+can be found under `./rustc_tools_util/`.
+The changelog for `rustc_tools_util` is available under:
+[`rustc_tools_util/CHANGELOG.md`](https://github.com/rust-lang/rust-clippy/blob/master/rustc_tools_util/CHANGELOG.md)
## License
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-use std::env;
-
+/// This macro creates the version string during compilation from the
+/// current environment
#[macro_export]
macro_rules! get_version_info {
() => {{
- let major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
- let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
- let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
- let crate_name = String::from(env!("CARGO_PKG_NAME"));
+ let major = std::env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
+ let minor = std::env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
+ let patch = std::env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
+ let crate_name = String::from(std::env!("CARGO_PKG_NAME"));
- let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
- let commit_hash = option_env!("GIT_HASH").map(str::to_string);
- let commit_date = option_env!("COMMIT_DATE").map(str::to_string);
+ let host_compiler = std::option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
+ let commit_hash = std::option_env!("GIT_HASH").map(str::to_string);
+ let commit_date = std::option_env!("COMMIT_DATE").map(str::to_string);
- VersionInfo {
+ $crate::VersionInfo {
major,
minor,
patch,
}};
}
+/// This macro can be used in `build.rs` to automatically set the needed
+/// environment values, namely `GIT_HASH`, `COMMIT_DATE` and
+/// `RUSTC_RELEASE_CHANNEL`
+#[macro_export]
+macro_rules! setup_version_info {
+ () => {{
+ println!(
+ "cargo:rustc-env=GIT_HASH={}",
+ $crate::get_commit_hash().unwrap_or_default()
+ );
+ println!(
+ "cargo:rustc-env=COMMIT_DATE={}",
+ $crate::get_commit_date().unwrap_or_default()
+ );
+ println!("cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", $crate::get_channel());
+ }};
+}
+
// some code taken and adapted from RLS and cargo
pub struct VersionInfo {
pub major: u8,
#[must_use]
pub fn get_channel() -> String {
- match env::var("CFG_RELEASE_CHANNEL") {
+ match std::env::var("CFG_RELEASE_CHANNEL") {
Ok(channel) => channel,
Err(_) => {
// if that failed, try to ask rustc -V, do some parsing and find out
fn test_struct_local() {
let vi = get_version_info!();
assert_eq!(vi.major, 0);
- assert_eq!(vi.minor, 2);
- assert_eq!(vi.patch, 1);
+ assert_eq!(vi.minor, 3);
+ assert_eq!(vi.patch, 0);
assert_eq!(vi.crate_name, "rustc_tools_util");
// hard to make positive tests for these since they will always change
assert!(vi.commit_hash.is_none());
#[test]
fn test_display_local() {
let vi = get_version_info!();
- assert_eq!(vi.to_string(), "rustc_tools_util 0.2.1");
+ assert_eq!(vi.to_string(), "rustc_tools_util 0.3.0");
}
#[test]
let s = format!("{vi:?}");
assert_eq!(
s,
- "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 1 }"
+ "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 3, patch: 0 }"
);
}
}
use rustc_interface::interface;
use rustc_session::parse::ParseSess;
use rustc_span::symbol::Symbol;
-use rustc_tools_util::VersionInfo;
use std::borrow::Cow;
use std::env;
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
-use rustc_tools_util::VersionInfo;
use std::env;
use std::path::PathBuf;
use std::process::{self, Command};
= help: convert all references to use `sym::Deref`
= note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
-error: hardcoded path to a diagnostic item
- --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
- |
-LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: convert all references to use `sym::deref_method`
-
error: hardcoded path to a language item
--> $DIR/unnecessary_def_path_hardcoded_path.rs:11:40
|
|
= help: convert all references to use `LangItem::DerefMut`
+error: hardcoded path to a diagnostic item
+ --> $DIR/unnecessary_def_path_hardcoded_path.rs:12:43
+ |
+LL | const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: convert all references to use `sym::deref_method`
+
error: aborting due to 3 previous errors
use core::ops::{Add, Neg};
-#[derive(Clone, Copy)]
-struct Point {
- x: i32,
- y: i32,
+macro_rules! create {
+ ($name:ident) => {
+ #[allow(clippy::arithmetic_side_effects)]
+ #[derive(Clone, Copy)]
+ struct $name;
+
+ impl Add<$name> for $name {
+ type Output = $name;
+ fn add(self, other: $name) -> Self::Output {
+ todo!()
+ }
+ }
+
+ impl Add<i32> for $name {
+ type Output = $name;
+ fn add(self, other: i32) -> Self::Output {
+ todo!()
+ }
+ }
+
+ impl Add<$name> for i32 {
+ type Output = $name;
+ fn add(self, other: $name) -> Self::Output {
+ todo!()
+ }
+ }
+
+ impl Add<i64> for $name {
+ type Output = $name;
+ fn add(self, other: i64) -> Self::Output {
+ todo!()
+ }
+ }
+
+ impl Add<$name> for i64 {
+ type Output = $name;
+ fn add(self, other: $name) -> Self::Output {
+ todo!()
+ }
+ }
+
+ impl Neg for $name {
+ type Output = $name;
+ fn neg(self) -> Self::Output {
+ todo!()
+ }
+ }
+ };
}
-impl Add for Point {
- type Output = Self;
+create!(Foo);
+create!(Bar);
+create!(Baz);
+create!(OutOfNames);
- fn add(self, other: Self) -> Self {
- todo!()
- }
+fn lhs_and_rhs_are_equal() {
+ // is explicitly on the list
+ let _ = OutOfNames + OutOfNames;
+ // is explicitly on the list
+ let _ = Foo + Foo;
+ // is implicitly on the list
+ let _ = Bar + Bar;
+ // not on the list
+ let _ = Baz + Baz;
}
-impl Neg for Point {
- type Output = Self;
+fn lhs_is_different() {
+ // is explicitly on the list
+ let _ = 1i32 + OutOfNames;
+ // is explicitly on the list
+ let _ = 1i32 + Foo;
+ // is implicitly on the list
+ let _ = 1i32 + Bar;
+ // not on the list
+ let _ = 1i32 + Baz;
- fn neg(self) -> Self::Output {
- todo!()
- }
+ // not on the list
+ let _ = 1i64 + Foo;
+ // is implicitly on the list
+ let _ = 1i64 + Bar;
+ // not on the list
+ let _ = 1i64 + Baz;
}
-fn main() {
- let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 };
+fn rhs_is_different() {
+ // is explicitly on the list
+ let _ = OutOfNames + 1i32;
+ // is explicitly on the list
+ let _ = Foo + 1i32;
+ // is implicitly on the list
+ let _ = Bar + 1i32;
+ // not on the list
+ let _ = Baz + 1i32;
+
+ // not on the list
+ let _ = Foo + 1i64;
+ // is implicitly on the list
+ let _ = Bar + 1i64;
+ // not on the list
+ let _ = Baz + 1i64;
+}
- let point: Point = Point { x: 1, y: 0 };
- let _ = point + point;
- let _ = -point;
+fn unary() {
+ // is explicitly on the list
+ let _ = -OutOfNames;
+ // is specifically on the list
+ let _ = -Foo;
+ // not on the list
+ let _ = -Bar;
+ // not on the list
+ let _ = -Baz;
}
+
+fn main() {}
--- /dev/null
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:68:13
+ |
+LL | let _ = Baz + Baz;
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:79:13
+ |
+LL | let _ = 1i32 + Baz;
+ | ^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:82:13
+ |
+LL | let _ = 1i64 + Foo;
+ | ^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:86:13
+ |
+LL | let _ = 1i64 + Baz;
+ | ^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:97:13
+ |
+LL | let _ = Baz + 1i32;
+ | ^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:100:13
+ |
+LL | let _ = Foo + 1i64;
+ | ^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:104:13
+ |
+LL | let _ = Baz + 1i64;
+ | ^^^^^^^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:113:13
+ |
+LL | let _ = -Bar;
+ | ^^^^
+
+error: arithmetic operation that can potentially result in unexpected side-effects
+ --> $DIR/arithmetic_side_effects_allowed.rs:115:13
+ |
+LL | let _ = -Baz;
+ | ^^^^
+
+error: aborting due to 9 previous errors
+
-arithmetic-side-effects-allowed = ["Point"]
+arithmetic-side-effects-allowed = [
+ "OutOfNames"
+]
+arithmetic-side-effects-allowed-binary = [
+ ["Foo", "Foo"],
+ ["Foo", "i32"],
+ ["i32", "Foo"],
+ ["Bar", "*"],
+ ["*", "Bar"],
+]
+arithmetic-side-effects-allowed-unary = ["Foo"]
--- /dev/null
+suppress-restriction-lint-in-const = true
--- /dev/null
+#![feature(inline_const)]
+#![warn(clippy::indexing_slicing)]
+// We also check the out_of_bounds_indexing lint here, because it lints similar things and
+// we want to avoid false positives.
+#![warn(clippy::out_of_bounds_indexing)]
+#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)]
+
+const ARR: [i32; 2] = [1, 2];
+const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
+const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+
+const fn idx() -> usize {
+ 1
+}
+const fn idx4() -> usize {
+ 4
+}
+
+fn main() {
+ let x = [1, 2, 3, 4];
+ let index: usize = 1;
+ x[index];
+ x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+
+ x[0]; // Ok, should not produce stderr.
+ x[3]; // Ok, should not produce stderr.
+ x[const { idx() }]; // Ok, should not produce stderr.
+ x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ const { &ARR[idx()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
+ const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
+
+ let y = &x;
+ y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
+ y[4]; // Ok, rustc will handle references too.
+
+ let v = vec![0; 5];
+ v[0];
+ v[10];
+ v[1 << 3];
+
+ const N: usize = 15; // Out of bounds
+ const M: usize = 3; // In bounds
+ x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+ x[M]; // Ok, should not produce stderr.
+ v[N];
+ v[M];
+}
+
+/// An opaque integer representation
+pub struct Integer<'a> {
+ /// The underlying data
+ value: &'a [u8],
+}
+impl<'a> Integer<'a> {
+ // Check whether `self` holds a negative number or not
+ pub const fn is_negative(&self) -> bool {
+ self.value[0] & 0b1000_0000 != 0
+ }
+}
--- /dev/null
+error[E0080]: evaluation of `main::{constant#3}` failed
+ --> $DIR/test.rs:31:14
+ |
+LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
+ | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
+
+note: erroneous constant used
+ --> $DIR/test.rs:31:5
+ |
+LL | const { &ARR[idx4()] }; // Ok, should not produce stderr, since `suppress-restriction-lint-in-const` is set true.
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: indexing may panic
+ --> $DIR/test.rs:22:5
+ |
+LL | x[index];
+ | ^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+ = note: `-D clippy::indexing-slicing` implied by `-D warnings`
+
+error: indexing may panic
+ --> $DIR/test.rs:38:5
+ |
+LL | v[0];
+ | ^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/test.rs:39:5
+ |
+LL | v[10];
+ | ^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/test.rs:40:5
+ |
+LL | v[1 << 3];
+ | ^^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/test.rs:46:5
+ |
+LL | v[N];
+ | ^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error: indexing may panic
+ --> $DIR/test.rs:47:5
+ |
+LL | v[M];
+ | ^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+
+error[E0080]: evaluation of constant value failed
+ --> $DIR/test.rs:10:24
+ |
+LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+ | ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
allow-unwrap-in-tests
allowed-scripts
arithmetic-side-effects-allowed
+ arithmetic-side-effects-allowed-binary
+ arithmetic-side-effects-allowed-unary
array-size-threshold
avoid-breaking-exported-api
await-holding-invalid-types
pass-by-value-size-limit
single-char-binding-names-threshold
standard-macro-braces
+ suppress-restriction-lint-in-const
third-party
too-large-for-stack
too-many-arguments-threshold
+++ /dev/null
-// run-rustfix
-// edition:2018
-// aux-build:macro_rules.rs
-
-#![feature(exclusive_range_pattern)]
-#![feature(stmt_expr_attributes)]
-#![warn(clippy::almost_complete_letter_range)]
-#![allow(ellipsis_inclusive_range_patterns)]
-#![allow(clippy::needless_parens_on_range_literals)]
-
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! a {
- () => {
- 'a'
- };
-}
-
-macro_rules! b {
- () => {
- let _ = 'a'..='z';
- };
-}
-
-fn main() {
- #[rustfmt::skip]
- {
- let _ = ('a') ..='z';
- let _ = 'A' ..= ('Z');
- }
-
- let _ = 'b'..'z';
- let _ = 'B'..'Z';
-
- let _ = (b'a')..=(b'z');
- let _ = b'A'..=b'Z';
-
- let _ = b'b'..b'z';
- let _ = b'B'..b'Z';
-
- let _ = a!()..='z';
-
- let _ = match 0u8 {
- b'a'..=b'z' if true => 1,
- b'A'..=b'Z' if true => 2,
- b'b'..b'z' => 3,
- b'B'..b'Z' => 4,
- _ => 5,
- };
-
- let _ = match 'x' {
- 'a'..='z' if true => 1,
- 'A'..='Z' if true => 2,
- 'b'..'z' => 3,
- 'B'..'Z' => 4,
- _ => 5,
- };
-
- almost_complete_letter_range!();
- b!();
-}
-
-#[clippy::msrv = "1.25"]
-fn _under_msrv() {
- let _ = match 'a' {
- 'a'...'z' => 1,
- _ => 2,
- };
-}
-
-#[clippy::msrv = "1.26"]
-fn _meets_msrv() {
- let _ = 'a'..='z';
- let _ = match 'a' {
- 'a'..='z' => 1,
- _ => 2,
- };
-}
+++ /dev/null
-// run-rustfix
-// edition:2018
-// aux-build:macro_rules.rs
-
-#![feature(exclusive_range_pattern)]
-#![feature(stmt_expr_attributes)]
-#![warn(clippy::almost_complete_letter_range)]
-#![allow(ellipsis_inclusive_range_patterns)]
-#![allow(clippy::needless_parens_on_range_literals)]
-
-#[macro_use]
-extern crate macro_rules;
-
-macro_rules! a {
- () => {
- 'a'
- };
-}
-
-macro_rules! b {
- () => {
- let _ = 'a'..'z';
- };
-}
-
-fn main() {
- #[rustfmt::skip]
- {
- let _ = ('a') ..'z';
- let _ = 'A' .. ('Z');
- }
-
- let _ = 'b'..'z';
- let _ = 'B'..'Z';
-
- let _ = (b'a')..(b'z');
- let _ = b'A'..b'Z';
-
- let _ = b'b'..b'z';
- let _ = b'B'..b'Z';
-
- let _ = a!()..'z';
-
- let _ = match 0u8 {
- b'a'..b'z' if true => 1,
- b'A'..b'Z' if true => 2,
- b'b'..b'z' => 3,
- b'B'..b'Z' => 4,
- _ => 5,
- };
-
- let _ = match 'x' {
- 'a'..'z' if true => 1,
- 'A'..'Z' if true => 2,
- 'b'..'z' => 3,
- 'B'..'Z' => 4,
- _ => 5,
- };
-
- almost_complete_letter_range!();
- b!();
-}
-
-#[clippy::msrv = "1.25"]
-fn _under_msrv() {
- let _ = match 'a' {
- 'a'..'z' => 1,
- _ => 2,
- };
-}
-
-#[clippy::msrv = "1.26"]
-fn _meets_msrv() {
- let _ = 'a'..'z';
- let _ = match 'a' {
- 'a'..'z' => 1,
- _ => 2,
- };
-}
+++ /dev/null
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:29:17
- |
-LL | let _ = ('a') ..'z';
- | ^^^^^^--^^^
- | |
- | help: use an inclusive range: `..=`
- |
- = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:30:17
- |
-LL | let _ = 'A' .. ('Z');
- | ^^^^--^^^^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:36:13
- |
-LL | let _ = (b'a')..(b'z');
- | ^^^^^^--^^^^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:37:13
- |
-LL | let _ = b'A'..b'Z';
- | ^^^^--^^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:42:13
- |
-LL | let _ = a!()..'z';
- | ^^^^--^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:45:9
- |
-LL | b'a'..b'z' if true => 1,
- | ^^^^--^^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:46:9
- |
-LL | b'A'..b'Z' if true => 2,
- | ^^^^--^^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:53:9
- |
-LL | 'a'..'z' if true => 1,
- | ^^^--^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:54:9
- |
-LL | 'A'..'Z' if true => 2,
- | ^^^--^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:22:17
- |
-LL | let _ = 'a'..'z';
- | ^^^--^^^
- | |
- | help: use an inclusive range: `..=`
-...
-LL | b!();
- | ---- in this macro invocation
- |
- = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:67:9
- |
-LL | 'a'..'z' => 1,
- | ^^^--^^^
- | |
- | help: use an inclusive range: `...`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:74:13
- |
-LL | let _ = 'a'..'z';
- | ^^^--^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: almost complete ascii letter range
- --> $DIR/almost_complete_letter_range.rs:76:9
- |
-LL | 'a'..'z' => 1,
- | ^^^--^^^
- | |
- | help: use an inclusive range: `..=`
-
-error: aborting due to 13 previous errors
-
--- /dev/null
+// run-rustfix
+// edition:2018
+// aux-build:macro_rules.rs
+
+#![feature(exclusive_range_pattern)]
+#![feature(stmt_expr_attributes)]
+#![warn(clippy::almost_complete_range)]
+#![allow(ellipsis_inclusive_range_patterns)]
+#![allow(clippy::needless_parens_on_range_literals)]
+#![allow(clippy::double_parens)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! a {
+ () => {
+ 'a'
+ };
+}
+macro_rules! A {
+ () => {
+ 'A'
+ };
+}
+macro_rules! zero {
+ () => {
+ '0'
+ };
+}
+
+macro_rules! b {
+ () => {
+ let _ = 'a'..='z';
+ let _ = 'A'..='Z';
+ let _ = '0'..='9';
+ };
+}
+
+fn main() {
+ #[rustfmt::skip]
+ {
+ let _ = ('a') ..='z';
+ let _ = 'A' ..= ('Z');
+ let _ = ((('0'))) ..= ('9');
+ }
+
+ let _ = 'b'..'z';
+ let _ = 'B'..'Z';
+ let _ = '1'..'9';
+
+ let _ = (b'a')..=(b'z');
+ let _ = b'A'..=b'Z';
+ let _ = b'0'..=b'9';
+
+ let _ = b'b'..b'z';
+ let _ = b'B'..b'Z';
+ let _ = b'1'..b'9';
+
+ let _ = a!()..='z';
+ let _ = A!()..='Z';
+ let _ = zero!()..='9';
+
+ let _ = match 0u8 {
+ b'a'..=b'z' if true => 1,
+ b'A'..=b'Z' if true => 2,
+ b'0'..=b'9' if true => 3,
+ b'b'..b'z' => 4,
+ b'B'..b'Z' => 5,
+ b'1'..b'9' => 6,
+ _ => 7,
+ };
+
+ let _ = match 'x' {
+ 'a'..='z' if true => 1,
+ 'A'..='Z' if true => 2,
+ '0'..='9' if true => 3,
+ 'b'..'z' => 4,
+ 'B'..'Z' => 5,
+ '1'..'9' => 6,
+ _ => 7,
+ };
+
+ almost_complete_range!();
+ b!();
+}
+
+#[clippy::msrv = "1.25"]
+fn _under_msrv() {
+ let _ = match 'a' {
+ 'a'...'z' => 1,
+ 'A'...'Z' => 2,
+ '0'...'9' => 3,
+ _ => 4,
+ };
+}
+
+#[clippy::msrv = "1.26"]
+fn _meets_msrv() {
+ let _ = 'a'..='z';
+ let _ = 'A'..='Z';
+ let _ = '0'..='9';
+ let _ = match 'a' {
+ 'a'..='z' => 1,
+ 'A'..='Z' => 1,
+ '0'..='9' => 3,
+ _ => 4,
+ };
+}
--- /dev/null
+// run-rustfix
+// edition:2018
+// aux-build:macro_rules.rs
+
+#![feature(exclusive_range_pattern)]
+#![feature(stmt_expr_attributes)]
+#![warn(clippy::almost_complete_range)]
+#![allow(ellipsis_inclusive_range_patterns)]
+#![allow(clippy::needless_parens_on_range_literals)]
+#![allow(clippy::double_parens)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! a {
+ () => {
+ 'a'
+ };
+}
+macro_rules! A {
+ () => {
+ 'A'
+ };
+}
+macro_rules! zero {
+ () => {
+ '0'
+ };
+}
+
+macro_rules! b {
+ () => {
+ let _ = 'a'..'z';
+ let _ = 'A'..'Z';
+ let _ = '0'..'9';
+ };
+}
+
+fn main() {
+ #[rustfmt::skip]
+ {
+ let _ = ('a') ..'z';
+ let _ = 'A' .. ('Z');
+ let _ = ((('0'))) .. ('9');
+ }
+
+ let _ = 'b'..'z';
+ let _ = 'B'..'Z';
+ let _ = '1'..'9';
+
+ let _ = (b'a')..(b'z');
+ let _ = b'A'..b'Z';
+ let _ = b'0'..b'9';
+
+ let _ = b'b'..b'z';
+ let _ = b'B'..b'Z';
+ let _ = b'1'..b'9';
+
+ let _ = a!()..'z';
+ let _ = A!()..'Z';
+ let _ = zero!()..'9';
+
+ let _ = match 0u8 {
+ b'a'..b'z' if true => 1,
+ b'A'..b'Z' if true => 2,
+ b'0'..b'9' if true => 3,
+ b'b'..b'z' => 4,
+ b'B'..b'Z' => 5,
+ b'1'..b'9' => 6,
+ _ => 7,
+ };
+
+ let _ = match 'x' {
+ 'a'..'z' if true => 1,
+ 'A'..'Z' if true => 2,
+ '0'..'9' if true => 3,
+ 'b'..'z' => 4,
+ 'B'..'Z' => 5,
+ '1'..'9' => 6,
+ _ => 7,
+ };
+
+ almost_complete_range!();
+ b!();
+}
+
+#[clippy::msrv = "1.25"]
+fn _under_msrv() {
+ let _ = match 'a' {
+ 'a'..'z' => 1,
+ 'A'..'Z' => 2,
+ '0'..'9' => 3,
+ _ => 4,
+ };
+}
+
+#[clippy::msrv = "1.26"]
+fn _meets_msrv() {
+ let _ = 'a'..'z';
+ let _ = 'A'..'Z';
+ let _ = '0'..'9';
+ let _ = match 'a' {
+ 'a'..'z' => 1,
+ 'A'..'Z' => 1,
+ '0'..'9' => 3,
+ _ => 4,
+ };
+}
--- /dev/null
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:42:17
+ |
+LL | let _ = ('a') ..'z';
+ | ^^^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+ |
+ = note: `-D clippy::almost-complete-range` implied by `-D warnings`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:43:17
+ |
+LL | let _ = 'A' .. ('Z');
+ | ^^^^--^^^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:44:17
+ |
+LL | let _ = ((('0'))) .. ('9');
+ | ^^^^^^^^^^--^^^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:51:13
+ |
+LL | let _ = (b'a')..(b'z');
+ | ^^^^^^--^^^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:52:13
+ |
+LL | let _ = b'A'..b'Z';
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:53:13
+ |
+LL | let _ = b'0'..b'9';
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:59:13
+ |
+LL | let _ = a!()..'z';
+ | ^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:60:13
+ |
+LL | let _ = A!()..'Z';
+ | ^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:61:13
+ |
+LL | let _ = zero!()..'9';
+ | ^^^^^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:64:9
+ |
+LL | b'a'..b'z' if true => 1,
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:65:9
+ |
+LL | b'A'..b'Z' if true => 2,
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:66:9
+ |
+LL | b'0'..b'9' if true => 3,
+ | ^^^^--^^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:74:9
+ |
+LL | 'a'..'z' if true => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:75:9
+ |
+LL | 'A'..'Z' if true => 2,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:76:9
+ |
+LL | '0'..'9' if true => 3,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:33:17
+ |
+LL | let _ = 'a'..'z';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+...
+LL | b!();
+ | ---- in this macro invocation
+ |
+ = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:34:17
+ |
+LL | let _ = 'A'..'Z';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+...
+LL | b!();
+ | ---- in this macro invocation
+ |
+ = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:35:17
+ |
+LL | let _ = '0'..'9';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+...
+LL | b!();
+ | ---- in this macro invocation
+ |
+ = note: this error originates in the macro `b` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:90:9
+ |
+LL | 'a'..'z' => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `...`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:91:9
+ |
+LL | 'A'..'Z' => 2,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `...`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:92:9
+ |
+LL | '0'..'9' => 3,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `...`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:99:13
+ |
+LL | let _ = 'a'..'z';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:100:13
+ |
+LL | let _ = 'A'..'Z';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:101:13
+ |
+LL | let _ = '0'..'9';
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:103:9
+ |
+LL | 'a'..'z' => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:104:9
+ |
+LL | 'A'..'Z' => 1,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: almost complete ascii range
+ --> $DIR/almost_complete_range.rs:105:9
+ |
+LL | '0'..'9' => 3,
+ | ^^^--^^^
+ | |
+ | help: use an inclusive range: `..=`
+
+error: aborting due to 27 previous errors
+
-error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:78:13
- |
-LL | let _ = String::new() + "";
- | ^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
-
-error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:86:27
- |
-LL | let inferred_string = string + "";
- | ^^^^^^^^^^^
-
-error: arithmetic operation that can potentially result in unexpected side-effects
- --> $DIR/arithmetic_side_effects.rs:90:13
- |
-LL | let _ = inferred_string + "";
- | ^^^^^^^^^^^^^^^^^^^^
-
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:165:5
|
LL | _n += 1;
| ^^^^^^^
+ |
+ = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
error: arithmetic operation that can potentially result in unexpected side-effects
--> $DIR/arithmetic_side_effects.rs:166:5
LL | _n = -&_n;
| ^^^^
-error: aborting due to 58 previous errors
+error: aborting due to 55 previous errors
}
#[macro_export]
-macro_rules! almost_complete_letter_range {
+macro_rules! almost_complete_range {
() => {
let _ = 'a'..'z';
+ let _ = 'A'..'Z';
+ let _ = '0'..'9';
};
}
impl<T> std::ops::Deref for StaticRef<T> {
type Target = T;
- fn deref(&self) -> &'static T {
+ fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
}
}
}
+
+#[derive(PartialEq, Debug)]
+#[repr(i64)]
+enum Test {
+ A = u32::MAX as i64 + 1,
+}
}
}
}
+
+#[derive(PartialEq, Debug)]
+#[repr(i64)]
+enum Test {
+ A = u32::MAX as i64 + 1,
+}
// run-rustfix
+#![allow(unused)]
#![warn(clippy::collapsible_str_replace)]
fn get_filter() -> char {
.replace('u', iter.next().unwrap())
.replace('s', iter.next().unwrap());
}
+
+#[clippy::msrv = "1.57"]
+fn msrv_1_57() {
+ let _ = "".replace('a', "1.57").replace('b', "1.57");
+}
+
+#[clippy::msrv = "1.58"]
+fn msrv_1_58() {
+ let _ = "".replace(['a', 'b'], "1.58");
+}
// run-rustfix
+#![allow(unused)]
#![warn(clippy::collapsible_str_replace)]
fn get_filter() -> char {
.replace('u', iter.next().unwrap())
.replace('s', iter.next().unwrap());
}
+
+#[clippy::msrv = "1.57"]
+fn msrv_1_57() {
+ let _ = "".replace('a', "1.57").replace('b', "1.57");
+}
+
+#[clippy::msrv = "1.58"]
+fn msrv_1_58() {
+ let _ = "".replace('a', "1.58").replace('b', "1.58");
+}
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:19:27
+ --> $DIR/collapsible_str_replace.rs:20:27
|
LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
= note: `-D clippy::collapsible-str-replace` implied by `-D warnings`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:21:27
+ --> $DIR/collapsible_str_replace.rs:22:27
|
LL | let _ = "hesuo worpd".replace('s', l).replace('u', l);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:23:27
+ --> $DIR/collapsible_str_replace.rs:24:27
|
LL | let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:26:10
+ --> $DIR/collapsible_str_replace.rs:27:10
|
LL | .replace('s', "l")
| __________^
| |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:31:27
+ --> $DIR/collapsible_str_replace.rs:32:27
|
LL | let _ = "hesuo world".replace(s, "l").replace('u', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:33:27
+ --> $DIR/collapsible_str_replace.rs:34:27
|
LL | let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:35:27
+ --> $DIR/collapsible_str_replace.rs:36:27
|
LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:37:27
+ --> $DIR/collapsible_str_replace.rs:38:27
|
LL | let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:39:27
+ --> $DIR/collapsible_str_replace.rs:40:27
|
LL | let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:41:45
+ --> $DIR/collapsible_str_replace.rs:42:45
|
LL | let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:44:47
+ --> $DIR/collapsible_str_replace.rs:45:47
|
LL | let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:46:28
+ --> $DIR/collapsible_str_replace.rs:47:28
|
LL | let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")`
error: used consecutive `str::replace` call
- --> $DIR/collapsible_str_replace.rs:48:27
+ --> $DIR/collapsible_str_replace.rs:49:27
|
LL | let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")`
-error: aborting due to 13 previous errors
+error: used consecutive `str::replace` call
+ --> $DIR/collapsible_str_replace.rs:86:16
+ |
+LL | let _ = "".replace('a', "1.58").replace('b', "1.58");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['a', 'b'], "1.58")`
+
+error: aborting due to 14 previous errors
}
}
}
+
+mod issue_10058 {
+ pub fn test() {
+ // should not lint since we are increasing counter potentially more than once in the loop
+ let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1];
+ let mut counter = 0;
+ for value in values {
+ counter += 1;
+
+ if value == 0 {
+ continue;
+ }
+
+ counter += 1;
+ }
+ }
+
+ pub fn test2() {
+ // should not lint since we are increasing counter potentially more than once in the loop
+ let values = [0, 1, 0, 1, 1, 1, 0, 1, 0, 1];
+ let mut counter = 0;
+ for value in values {
+ counter += 1;
+
+ if value != 0 {
+ counter += 1;
+ }
+ }
+ }
+}
// run-rustfix
+#![feature(type_alias_impl_trait)]
#![warn(clippy::from_over_into)]
#![allow(unused)]
}
}
+type Opaque = impl Sized;
+struct IntoOpaque;
+impl Into<Opaque> for IntoOpaque {
+ fn into(self) -> Opaque {}
+}
+
fn main() {}
// run-rustfix
+#![feature(type_alias_impl_trait)]
#![warn(clippy::from_over_into)]
#![allow(unused)]
}
}
+type Opaque = impl Sized;
+struct IntoOpaque;
+impl Into<Opaque> for IntoOpaque {
+ fn into(self) -> Opaque {}
+}
+
fn main() {}
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:9:1
+ --> $DIR/from_over_into.rs:10:1
|
LL | impl Into<StringWrapper> for String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:17:1
+ --> $DIR/from_over_into.rs:18:1
|
LL | impl Into<SelfType> for String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:32:1
+ --> $DIR/from_over_into.rs:33:1
|
LL | impl Into<SelfKeywords> for X {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:44:1
+ --> $DIR/from_over_into.rs:45:1
|
LL | impl core::convert::Into<bool> for crate::ExplicitPaths {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
- --> $DIR/from_over_into.rs:77:5
+ --> $DIR/from_over_into.rs:78:5
|
LL | impl<T> Into<FromOverInto<T>> for Vec<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
42;
1;
42;
- &x;
+ x;
x;
let mut a = A(String::new());
2 * { a };
(({ a } + 4));
1;
+
+ // Issue #9904
+ let x = 0i32;
+ let _: i32 = x;
}
pub fn decide(a: bool, b: bool) -> u32 {
2 * (0 + { a });
1 * ({ a } + 4);
1 * 1;
+
+ // Issue #9904
+ let x = 0i32;
+ let _: i32 = &x + 0;
}
pub fn decide(a: bool, b: bool) -> u32 {
--> $DIR/identity_op.rs:68:5
|
LL | &x >> 0;
- | ^^^^^^^ help: consider reducing it to: `&x`
+ | ^^^^^^^ help: consider reducing it to: `x`
error: this operation has no effect
--> $DIR/identity_op.rs:69:5
| ^^^^^ help: consider reducing it to: `1`
error: this operation has no effect
- --> $DIR/identity_op.rs:118:5
+ --> $DIR/identity_op.rs:118:18
+ |
+LL | let _: i32 = &x + 0;
+ | ^^^^^^ help: consider reducing it to: `x`
+
+error: this operation has no effect
+ --> $DIR/identity_op.rs:122:5
|
LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })`
-error: aborting due to 39 previous errors
+error: aborting due to 40 previous errors
let pathbuf_ref = &pathbuf_ref;
let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf`
let _ = (**pathbuf_ref).clone();
+
+ struct NoClone;
+ impl ToOwned for NoClone {
+ type Owned = Self;
+ fn to_owned(&self) -> Self {
+ NoClone
+ }
+ }
+ let no_clone = &NoClone;
+ let _ = no_clone.to_owned();
}
let pathbuf_ref = &pathbuf_ref;
let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf`
let _ = pathbuf_ref.to_path_buf();
+
+ struct NoClone;
+ impl ToOwned for NoClone {
+ type Owned = Self;
+ fn to_owned(&self) -> Self {
+ NoClone
+ }
+ }
+ let no_clone = &NoClone;
+ let _ = no_clone.to_owned();
}
#![allow(unconditional_panic, clippy::no_effect, clippy::unnecessary_operation)]
const ARR: [i32; 2] = [1, 2];
-const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr.
+const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
const fn idx() -> usize {
x[3]; // Ok, should not produce stderr.
x[const { idx() }]; // Ok, should not produce stderr.
x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
- const { &ARR[idx()] }; // Ok, should not produce stderr.
- const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+ const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
+ const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
let y = &x;
y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:9:20
+ |
+LL | const REF: &i32 = &ARR[idx()]; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
+ | ^^^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+ = note: the suggestion might not be applicable in constant blocks
+ = note: `-D clippy::indexing-slicing` implied by `-D warnings`
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:10:24
+ |
+LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+ | ^^^^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+ = note: the suggestion might not be applicable in constant blocks
+
error[E0080]: evaluation of `main::{constant#3}` failed
--> $DIR/indexing_slicing_index.rs:31:14
|
-LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
note: erroneous constant used
--> $DIR/indexing_slicing_index.rs:31:5
|
-LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
| ^^^^^^^^^^^^^^^^^^^^^^
error: indexing may panic
| ^^^^^^^^
|
= help: consider using `.get(n)` or `.get_mut(n)` instead
- = note: `-D clippy::indexing-slicing` implied by `-D warnings`
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:30:14
+ |
+LL | const { &ARR[idx()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
+ | ^^^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+ = note: the suggestion might not be applicable in constant blocks
+
+error: indexing may panic
+ --> $DIR/indexing_slicing_index.rs:31:14
+ |
+LL | const { &ARR[idx4()] }; // This should be linted, since `suppress-restriction-lint-in-const` default is false.
+ | ^^^^^^^^^^^
+ |
+ = help: consider using `.get(n)` or `.get_mut(n)` instead
+ = note: the suggestion might not be applicable in constant blocks
error: indexing may panic
--> $DIR/indexing_slicing_index.rs:38:5
LL | const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
| ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
-error: aborting due to 8 previous errors
+error: aborting due to 12 previous errors
For more information about this error, try `rustc --explain E0080`.
#![warn(clippy::len_zero)]
#![allow(dead_code, unused, clippy::len_without_is_empty)]
+extern crate core;
+use core::ops::Deref;
+
pub struct One;
struct Wither;
}
}
+struct DerefToDerefToString;
+
+impl Deref for DerefToDerefToString {
+ type Target = DerefToString;
+
+ fn deref(&self) -> &Self::Target {
+ &DerefToString {}
+ }
+}
+
+struct DerefToString;
+
+impl Deref for DerefToString {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "Hello, world!"
+ }
+}
+
fn main() {
let x = [1, 2];
if x.is_empty() {
if "".is_empty() {}
+ let s = "Hello, world!";
+ let s1 = &s;
+ let s2 = &s1;
+ let s3 = &s2;
+ let s4 = &s3;
+ let s5 = &s4;
+ let s6 = &s5;
+ println!("{}", s1.is_empty());
+ println!("{}", s2.is_empty());
+ println!("{}", s3.is_empty());
+ println!("{}", s4.is_empty());
+ println!("{}", s5.is_empty());
+ println!("{}", (s6).is_empty());
+
+ let d2s = DerefToDerefToString {};
+ println!("{}", (**d2s).is_empty());
+
let y = One;
if y.len() == 0 {
// No error; `One` does not have `.is_empty()`.
#![warn(clippy::len_zero)]
#![allow(dead_code, unused, clippy::len_without_is_empty)]
+extern crate core;
+use core::ops::Deref;
+
pub struct One;
struct Wither;
}
}
+struct DerefToDerefToString;
+
+impl Deref for DerefToDerefToString {
+ type Target = DerefToString;
+
+ fn deref(&self) -> &Self::Target {
+ &DerefToString {}
+ }
+}
+
+struct DerefToString;
+
+impl Deref for DerefToString {
+ type Target = str;
+
+ fn deref(&self) -> &Self::Target {
+ "Hello, world!"
+ }
+}
+
fn main() {
let x = [1, 2];
if x.len() == 0 {
if "".len() == 0 {}
+ let s = "Hello, world!";
+ let s1 = &s;
+ let s2 = &s1;
+ let s3 = &s2;
+ let s4 = &s3;
+ let s5 = &s4;
+ let s6 = &s5;
+ println!("{}", *s1 == "");
+ println!("{}", **s2 == "");
+ println!("{}", ***s3 == "");
+ println!("{}", ****s4 == "");
+ println!("{}", *****s5 == "");
+ println!("{}", ******(s6) == "");
+
+ let d2s = DerefToDerefToString {};
+ println!("{}", &**d2s == "");
+
let y = One;
if y.len() == 0 {
// No error; `One` does not have `.is_empty()`.
error: length comparison to zero
- --> $DIR/len_zero.rs:61:8
+ --> $DIR/len_zero.rs:84:8
|
LL | if x.len() == 0 {
| ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `x.is_empty()`
= note: `-D clippy::len-zero` implied by `-D warnings`
error: length comparison to zero
- --> $DIR/len_zero.rs:65:8
+ --> $DIR/len_zero.rs:88:8
|
LL | if "".len() == 0 {}
| ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `"".is_empty()`
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:97:20
+ |
+LL | println!("{}", *s1 == "");
+ | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s1.is_empty()`
+ |
+ = note: `-D clippy::comparison-to-empty` implied by `-D warnings`
+
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:98:20
+ |
+LL | println!("{}", **s2 == "");
+ | ^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s2.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:99:20
+ |
+LL | println!("{}", ***s3 == "");
+ | ^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s3.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:100:20
+ |
+LL | println!("{}", ****s4 == "");
+ | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s4.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:101:20
+ |
+LL | println!("{}", *****s5 == "");
+ | ^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s5.is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:102:20
+ |
+LL | println!("{}", ******(s6) == "");
+ | ^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(s6).is_empty()`
+
+error: comparison to empty slice
+ --> $DIR/len_zero.rs:105:20
+ |
+LL | println!("{}", &**d2s == "");
+ | ^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `(**d2s).is_empty()`
+
error: length comparison to zero
- --> $DIR/len_zero.rs:80:8
+ --> $DIR/len_zero.rs:120:8
|
LL | if has_is_empty.len() == 0 {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:83:8
+ --> $DIR/len_zero.rs:123:8
|
LL | if has_is_empty.len() != 0 {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:86:8
+ --> $DIR/len_zero.rs:126:8
|
LL | if has_is_empty.len() > 0 {
| ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
error: length comparison to one
- --> $DIR/len_zero.rs:89:8
+ --> $DIR/len_zero.rs:129:8
|
LL | if has_is_empty.len() < 1 {
| ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
error: length comparison to one
- --> $DIR/len_zero.rs:92:8
+ --> $DIR/len_zero.rs:132:8
|
LL | if has_is_empty.len() >= 1 {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:103:8
+ --> $DIR/len_zero.rs:143:8
|
LL | if 0 == has_is_empty.len() {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:106:8
+ --> $DIR/len_zero.rs:146:8
|
LL | if 0 != has_is_empty.len() {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:109:8
+ --> $DIR/len_zero.rs:149:8
|
LL | if 0 < has_is_empty.len() {
| ^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
error: length comparison to one
- --> $DIR/len_zero.rs:112:8
+ --> $DIR/len_zero.rs:152:8
|
LL | if 1 <= has_is_empty.len() {
| ^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!has_is_empty.is_empty()`
error: length comparison to one
- --> $DIR/len_zero.rs:115:8
+ --> $DIR/len_zero.rs:155:8
|
LL | if 1 > has_is_empty.len() {
| ^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `has_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:129:8
+ --> $DIR/len_zero.rs:169:8
|
LL | if with_is_empty.len() == 0 {
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `with_is_empty.is_empty()`
error: length comparison to zero
- --> $DIR/len_zero.rs:142:8
+ --> $DIR/len_zero.rs:182:8
|
LL | if b.len() != 0 {}
| ^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!b.is_empty()`
-error: aborting due to 14 previous errors
+error: aborting due to 21 previous errors
panic!("panic5");
}
assert!(!a.is_empty(), "with expansion {}", one!());
+ if a.is_empty() {
+ let _ = 0;
+ } else if a.len() == 1 {
+ panic!("panic6");
+ }
}
fn issue7730(a: u8) {
assert!(!(b.is_empty() || a.is_empty()), "panic4");
assert!(!(a.is_empty() || !b.is_empty()), "panic5");
assert!(!a.is_empty(), "with expansion {}", one!());
+ if a.is_empty() {
+ let _ = 0;
+ } else if a.len() == 1 {
+ panic!("panic6");
+ }
}
fn issue7730(a: u8) {
| |_____^ help: try instead: `assert!(!a.is_empty(), "with expansion {}", one!());`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:73:5
+ --> $DIR/manual_assert.rs:78:5
|
LL | / if a > 2 {
LL | | // comment
if a.is_empty() {
panic!("with expansion {}", one!())
}
+ if a.is_empty() {
+ let _ = 0;
+ } else if a.len() == 1 {
+ panic!("panic6");
+ }
}
fn issue7730(a: u8) {
assert!('x'.is_ascii_alphabetic());
assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
+
+ b'0'.is_ascii_digit();
+ b'a'.is_ascii_lowercase();
+ b'A'.is_ascii_uppercase();
+
+ '0'.is_ascii_digit();
+ 'a'.is_ascii_lowercase();
+ 'A'.is_ascii_uppercase();
+
+ let cool_letter = &'g';
+ cool_letter.is_ascii_digit();
+ cool_letter.is_ascii_lowercase();
+ cool_letter.is_ascii_uppercase();
}
#[clippy::msrv = "1.23"]
assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
+
+ (b'0'..=b'9').contains(&b'0');
+ (b'a'..=b'z').contains(&b'a');
+ (b'A'..=b'Z').contains(&b'A');
+
+ ('0'..='9').contains(&'0');
+ ('a'..='z').contains(&'a');
+ ('A'..='Z').contains(&'A');
+
+ let cool_letter = &'g';
+ ('0'..='9').contains(cool_letter);
+ ('a'..='z').contains(cool_letter);
+ ('A'..='Z').contains(cool_letter);
}
#[clippy::msrv = "1.23"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:29:13
+ --> $DIR/manual_is_ascii_check.rs:19:5
+ |
+LL | (b'0'..=b'9').contains(&b'0');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'0'.is_ascii_digit()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:20:5
+ |
+LL | (b'a'..=b'z').contains(&b'a');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'a'.is_ascii_lowercase()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:21:5
+ |
+LL | (b'A'..=b'Z').contains(&b'A');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'A'.is_ascii_uppercase()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:23:5
+ |
+LL | ('0'..='9').contains(&'0');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'0'.is_ascii_digit()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:24:5
+ |
+LL | ('a'..='z').contains(&'a');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'a'.is_ascii_lowercase()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:25:5
+ |
+LL | ('A'..='Z').contains(&'A');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'A'.is_ascii_uppercase()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:28:5
+ |
+LL | ('0'..='9').contains(cool_letter);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:29:5
+ |
+LL | ('a'..='z').contains(cool_letter);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:30:5
+ |
+LL | ('A'..='Z').contains(cool_letter);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_uppercase()`
+
+error: manual check for common ascii range
+ --> $DIR/manual_is_ascii_check.rs:42:13
|
LL | assert!(matches!(b'1', b'0'..=b'9'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:30:13
+ --> $DIR/manual_is_ascii_check.rs:43:13
|
LL | assert!(matches!('X', 'A'..='Z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:31:13
+ --> $DIR/manual_is_ascii_check.rs:44:13
|
LL | assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
error: manual check for common ascii range
- --> $DIR/manual_is_ascii_check.rs:41:23
+ --> $DIR/manual_is_ascii_check.rs:54:23
|
LL | const FOO: bool = matches!('x', '0'..='9');
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
-error: aborting due to 11 previous errors
+error: aborting due to 20 previous errors
Ok(v) => v,
Err(()) => return,
};
+
+ let f = Variant::Bar(1);
+
+ let _value = match f {
+ Variant::Bar(_) | Variant::Baz(_) => (),
+ _ => return,
+ };
}
fn not_fire() {
LL | | (Some(_), Some(_)) | (None, None) => continue,
LL | | (Some(v), None) | (None, Some(v)) => v,
LL | | };
- | |__________^ help: consider writing: `let (Some(v), None) | (None, Some(v)) = h() else { continue };`
+ | |__________^ help: consider writing: `let ((Some(v), None) | (None, Some(v))) = h() else { continue };`
error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:49:9
LL | | _ => continue,
LL | | Variant::Bar(v) | Variant::Baz(v) => v,
LL | | };
- | |__________^ help: consider writing: `let Variant::Bar(v) | Variant::Baz(v) = build_enum() else { continue };`
+ | |__________^ help: consider writing: `let (Variant::Bar(v) | Variant::Baz(v)) = build_enum() else { continue };`
error: this could be rewritten as `let...else`
--> $DIR/manual_let_else_match.rs:57:5
LL | | };
| |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };`
-error: aborting due to 6 previous errors
+error: this could be rewritten as `let...else`
+ --> $DIR/manual_let_else_match.rs:70:5
+ |
+LL | / let _value = match f {
+LL | | Variant::Bar(_) | Variant::Baz(_) => (),
+LL | | _ => return,
+LL | | };
+ | |______^ help: consider writing: `let (Variant::Bar(_) | Variant::Baz(_)) = f else { return };`
+
+error: aborting due to 7 previous errors
// edition:2018
#![warn(clippy::needless_parens_on_range_literals)]
-#![allow(clippy::almost_complete_letter_range)]
+#![allow(clippy::almost_complete_range)]
fn main() {
let _ = 'a'..='z';
// edition:2018
#![warn(clippy::needless_parens_on_range_literals)]
-#![allow(clippy::almost_complete_letter_range)]
+#![allow(clippy::almost_complete_range)]
fn main() {
let _ = ('a')..=('z');
+#![feature(type_alias_impl_trait)]
#![warn(clippy::new_ret_no_self)]
#![allow(dead_code)]
}
}
}
+
+mod issue10041 {
+ struct Bomb;
+
+ impl Bomb {
+ // Hidden <Rhs = Self> default generic paramter.
+ pub fn new() -> impl PartialOrd {
+ 0i32
+ }
+ }
+
+ // TAIT with self-referencing bounds
+ type X = impl std::ops::Add<Output = X>;
+
+ struct Bomb2;
+
+ impl Bomb2 {
+ pub fn new() -> X {
+ 0i32
+ }
+ }
+}
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:49:5
+ --> $DIR/new_ret_no_self.rs:50:5
|
LL | / pub fn new(_: String) -> impl R<Item = u32> {
LL | | S3
= note: `-D clippy::new-ret-no-self` implied by `-D warnings`
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:81:5
+ --> $DIR/new_ret_no_self.rs:82:5
|
LL | / pub fn new() -> u32 {
LL | | unimplemented!();
| |_____^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:90:5
+ --> $DIR/new_ret_no_self.rs:91:5
|
LL | / pub fn new(_: String) -> u32 {
LL | | unimplemented!();
| |_____^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:126:5
+ --> $DIR/new_ret_no_self.rs:127:5
|
LL | / pub fn new() -> (u32, u32) {
LL | | unimplemented!();
| |_____^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:153:5
+ --> $DIR/new_ret_no_self.rs:154:5
|
LL | / pub fn new() -> *mut V {
LL | | unimplemented!();
| |_____^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:171:5
+ --> $DIR/new_ret_no_self.rs:172:5
|
LL | / pub fn new() -> Option<u32> {
LL | | unimplemented!();
| |_____^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:224:9
+ --> $DIR/new_ret_no_self.rs:225:9
|
LL | fn new() -> String;
| ^^^^^^^^^^^^^^^^^^^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:236:9
+ --> $DIR/new_ret_no_self.rs:237:9
|
LL | fn new(_: String) -> String;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:271:9
+ --> $DIR/new_ret_no_self.rs:272:9
|
LL | / fn new() -> (u32, u32) {
LL | | unimplemented!();
| |_________^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:298:9
+ --> $DIR/new_ret_no_self.rs:299:9
|
LL | / fn new() -> *mut V {
LL | | unimplemented!();
| |_________^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:368:9
+ --> $DIR/new_ret_no_self.rs:369:9
|
LL | / fn new(t: T) -> impl Into<i32> {
LL | | 1
| |_________^
error: methods called `new` usually return `Self`
- --> $DIR/new_ret_no_self.rs:389:9
+ --> $DIR/new_ret_no_self.rs:390:9
|
LL | / fn new(t: T) -> impl Trait2<(), i32> {
LL | | unimplemented!()
LL | | }
| |_________^
-error: aborting due to 12 previous errors
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:410:9
+ |
+LL | / pub fn new() -> impl PartialOrd {
+LL | | 0i32
+LL | | }
+ | |_________^
+
+error: methods called `new` usually return `Self`
+ --> $DIR/new_ret_no_self.rs:421:9
+ |
+LL | / pub fn new() -> X {
+LL | | 0i32
+LL | | }
+ | |_________^
+
+error: aborting due to 14 previous errors
static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
+static mut STATIC_MUT_SLICE: &mut [u32] = &mut [0];
+
fn main() {
let false_positive: &'static str = "test";
+
+ unsafe {
+ STATIC_MUT_SLICE[0] = 0;
+ }
}
trait Bar {
static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
+static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0];
+
fn main() {
let false_positive: &'static str = "test";
+
+ unsafe {
+ STATIC_MUT_SLICE[0] = 0;
+ }
}
trait Bar {
| -^^^^^^^-------- help: consider removing `'static`: `&[u8; 1]`
error: statics have by default a `'static` lifetime
- --> $DIR/redundant_static_lifetimes.rs:65:16
+ --> $DIR/redundant_static_lifetimes.rs:42:31
+ |
+LL | static mut STATIC_MUT_SLICE: &'static mut [u32] = &mut [0];
+ | -^^^^^^^---------- help: consider removing `'static`: `&mut [u32]`
+
+error: statics have by default a `'static` lifetime
+ --> $DIR/redundant_static_lifetimes.rs:71:16
|
LL | static V: &'static u8 = &17;
| -^^^^^^^--- help: consider removing `'static`: `&u8`
-error: aborting due to 17 previous errors
+error: aborting due to 18 previous errors
// run-rustfix
+#![allow(clippy::almost_complete_range)]
#![allow(clippy::disallowed_names)]
#![allow(clippy::blocks_in_if_conditions)]
#![allow(clippy::box_collection)]
#![allow(temporary_cstring_as_ptr)]
#![allow(unknown_lints)]
#![allow(unused_labels)]
+#![warn(clippy::almost_complete_range)]
#![warn(clippy::disallowed_names)]
#![warn(clippy::blocks_in_if_conditions)]
#![warn(clippy::blocks_in_if_conditions)]
// run-rustfix
+#![allow(clippy::almost_complete_range)]
#![allow(clippy::disallowed_names)]
#![allow(clippy::blocks_in_if_conditions)]
#![allow(clippy::box_collection)]
#![allow(temporary_cstring_as_ptr)]
#![allow(unknown_lints)]
#![allow(unused_labels)]
+#![warn(clippy::almost_complete_letter_range)]
#![warn(clippy::blacklisted_name)]
#![warn(clippy::block_in_if_condition_expr)]
#![warn(clippy::block_in_if_condition_stmt)]
+error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
+ --> $DIR/rename.rs:41:9
+ |
+LL | #![warn(clippy::almost_complete_letter_range)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
+ |
+ = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+
error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
- --> $DIR/rename.rs:40:9
+ --> $DIR/rename.rs:42:9
|
LL | #![warn(clippy::blacklisted_name)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
- |
- = note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:41:9
+ --> $DIR/rename.rs:43:9
|
LL | #![warn(clippy::block_in_if_condition_expr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:42:9
+ --> $DIR/rename.rs:44:9
|
LL | #![warn(clippy::block_in_if_condition_stmt)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
- --> $DIR/rename.rs:43:9
+ --> $DIR/rename.rs:45:9
|
LL | #![warn(clippy::box_vec)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
- --> $DIR/rename.rs:44:9
+ --> $DIR/rename.rs:46:9
|
LL | #![warn(clippy::const_static_lifetime)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
- --> $DIR/rename.rs:45:9
+ --> $DIR/rename.rs:47:9
|
LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
- --> $DIR/rename.rs:46:9
+ --> $DIR/rename.rs:48:9
|
LL | #![warn(clippy::disallowed_method)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
- --> $DIR/rename.rs:47:9
+ --> $DIR/rename.rs:49:9
|
LL | #![warn(clippy::disallowed_type)]
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
- --> $DIR/rename.rs:48:9
+ --> $DIR/rename.rs:50:9
|
LL | #![warn(clippy::eval_order_dependence)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
- --> $DIR/rename.rs:49:9
+ --> $DIR/rename.rs:51:9
|
LL | #![warn(clippy::identity_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
- --> $DIR/rename.rs:50:9
+ --> $DIR/rename.rs:52:9
|
LL | #![warn(clippy::if_let_some_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
- --> $DIR/rename.rs:51:9
+ --> $DIR/rename.rs:53:9
|
LL | #![warn(clippy::logic_bug)]
| ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
- --> $DIR/rename.rs:52:9
+ --> $DIR/rename.rs:54:9
|
LL | #![warn(clippy::new_without_default_derive)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
- --> $DIR/rename.rs:53:9
+ --> $DIR/rename.rs:55:9
|
LL | #![warn(clippy::option_and_then_some)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:54:9
+ --> $DIR/rename.rs:56:9
|
LL | #![warn(clippy::option_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:55:9
+ --> $DIR/rename.rs:57:9
|
LL | #![warn(clippy::option_map_unwrap_or)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:56:9
+ --> $DIR/rename.rs:58:9
|
LL | #![warn(clippy::option_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:57:9
+ --> $DIR/rename.rs:59:9
|
LL | #![warn(clippy::option_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
- --> $DIR/rename.rs:58:9
+ --> $DIR/rename.rs:60:9
|
LL | #![warn(clippy::ref_in_deref)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:59:9
+ --> $DIR/rename.rs:61:9
|
LL | #![warn(clippy::result_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:60:9
+ --> $DIR/rename.rs:62:9
|
LL | #![warn(clippy::result_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:61:9
+ --> $DIR/rename.rs:63:9
|
LL | #![warn(clippy::result_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
- --> $DIR/rename.rs:62:9
+ --> $DIR/rename.rs:64:9
|
LL | #![warn(clippy::single_char_push_str)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
- --> $DIR/rename.rs:63:9
+ --> $DIR/rename.rs:65:9
|
LL | #![warn(clippy::stutter)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
- --> $DIR/rename.rs:64:9
+ --> $DIR/rename.rs:66:9
|
LL | #![warn(clippy::to_string_in_display)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
- --> $DIR/rename.rs:65:9
+ --> $DIR/rename.rs:67:9
|
LL | #![warn(clippy::zero_width_space)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- --> $DIR/rename.rs:66:9
+ --> $DIR/rename.rs:68:9
|
LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:67:9
+ --> $DIR/rename.rs:69:9
|
LL | #![warn(clippy::for_loop_over_option)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:68:9
+ --> $DIR/rename.rs:70:9
|
LL | #![warn(clippy::for_loop_over_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
- --> $DIR/rename.rs:69:9
+ --> $DIR/rename.rs:71:9
|
LL | #![warn(clippy::for_loops_over_fallibles)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/rename.rs:70:9
+ --> $DIR/rename.rs:72:9
|
LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
- --> $DIR/rename.rs:71:9
+ --> $DIR/rename.rs:73:9
|
LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/rename.rs:72:9
+ --> $DIR/rename.rs:74:9
|
LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
- --> $DIR/rename.rs:73:9
+ --> $DIR/rename.rs:75:9
|
LL | #![warn(clippy::let_underscore_drop)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:74:9
+ --> $DIR/rename.rs:76:9
|
LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
- --> $DIR/rename.rs:75:9
+ --> $DIR/rename.rs:77:9
|
LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
- --> $DIR/rename.rs:76:9
+ --> $DIR/rename.rs:78:9
|
LL | #![warn(clippy::positional_named_format_parameters)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
- --> $DIR/rename.rs:77:9
+ --> $DIR/rename.rs:79:9
|
LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
- --> $DIR/rename.rs:78:9
+ --> $DIR/rename.rs:80:9
|
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::unused_label` has been renamed to `unused_labels`
- --> $DIR/rename.rs:79:9
+ --> $DIR/rename.rs:81:9
|
LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
-error: aborting due to 40 previous errors
+error: aborting due to 41 previous errors
t.seek(SeekFrom::End(0));
}
+// This should NOT trigger clippy warning because
+// expr is used here
+fn seek_to_start_in_let<T: Seek>(t: &mut T) {
+ let a = t.seek(SeekFrom::Start(0)).unwrap();
+}
+
fn main() {
let mut f = OpenOptions::new()
.write(true)
t.seek(SeekFrom::End(0));
}
+// This should NOT trigger clippy warning because
+// expr is used here
+fn seek_to_start_in_let<T: Seek>(t: &mut T) {
+ let a = t.seek(SeekFrom::Start(0)).unwrap();
+}
+
fn main() {
let mut f = OpenOptions::new()
.write(true)
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
error: used `seek` to go to the start of the stream
- --> $DIR/seek_to_start_instead_of_rewind.rs:128:7
+ --> $DIR/seek_to_start_instead_of_rewind.rs:134:7
|
LL | f.seek(SeekFrom::Start(0));
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
--- /dev/null
+// run-rustfix
+#![allow(
+ unused,
+ clippy::unused_unit,
+ clippy::unnecessary_operation,
+ clippy::no_effect,
+ clippy::single_element_loop
+)]
+#![warn(clippy::semicolon_inside_block)]
+
+macro_rules! m {
+ (()) => {
+ ()
+ };
+ (0) => {{
+ 0
+ };};
+ (1) => {{
+ 1;
+ }};
+ (2) => {{
+ 2;
+ }};
+}
+
+fn unit_fn_block() {
+ ()
+}
+
+#[rustfmt::skip]
+fn main() {
+ { unit_fn_block() }
+ unsafe { unit_fn_block() }
+
+ {
+ unit_fn_block()
+ }
+
+ { unit_fn_block(); }
+ unsafe { unit_fn_block(); }
+
+ { unit_fn_block(); }
+ unsafe { unit_fn_block(); }
+
+ { unit_fn_block(); };
+ unsafe { unit_fn_block(); };
+
+ {
+ unit_fn_block();
+ unit_fn_block();
+ }
+ {
+ unit_fn_block();
+ unit_fn_block();
+ }
+ {
+ unit_fn_block();
+ unit_fn_block();
+ };
+
+ { m!(()); }
+ { m!(()); }
+ { m!(()); };
+ m!(0);
+ m!(1);
+ m!(2);
+
+ for _ in [()] {
+ unit_fn_block();
+ }
+ for _ in [()] {
+ unit_fn_block()
+ }
+
+ let _d = || {
+ unit_fn_block();
+ };
+ let _d = || {
+ unit_fn_block()
+ };
+
+ { unit_fn_block(); };
+
+ unit_fn_block()
+}
--- /dev/null
+// run-rustfix
+#![allow(
+ unused,
+ clippy::unused_unit,
+ clippy::unnecessary_operation,
+ clippy::no_effect,
+ clippy::single_element_loop
+)]
+#![warn(clippy::semicolon_inside_block)]
+
+macro_rules! m {
+ (()) => {
+ ()
+ };
+ (0) => {{
+ 0
+ };};
+ (1) => {{
+ 1;
+ }};
+ (2) => {{
+ 2;
+ }};
+}
+
+fn unit_fn_block() {
+ ()
+}
+
+#[rustfmt::skip]
+fn main() {
+ { unit_fn_block() }
+ unsafe { unit_fn_block() }
+
+ {
+ unit_fn_block()
+ }
+
+ { unit_fn_block() };
+ unsafe { unit_fn_block() };
+
+ { unit_fn_block(); }
+ unsafe { unit_fn_block(); }
+
+ { unit_fn_block(); };
+ unsafe { unit_fn_block(); };
+
+ {
+ unit_fn_block();
+ unit_fn_block()
+ };
+ {
+ unit_fn_block();
+ unit_fn_block();
+ }
+ {
+ unit_fn_block();
+ unit_fn_block();
+ };
+
+ { m!(()) };
+ { m!(()); }
+ { m!(()); };
+ m!(0);
+ m!(1);
+ m!(2);
+
+ for _ in [()] {
+ unit_fn_block();
+ }
+ for _ in [()] {
+ unit_fn_block()
+ }
+
+ let _d = || {
+ unit_fn_block();
+ };
+ let _d = || {
+ unit_fn_block()
+ };
+
+ { unit_fn_block(); };
+
+ unit_fn_block()
+}
--- /dev/null
+error: consider moving the `;` inside the block for consistent formatting
+ --> $DIR/semicolon_inside_block.rs:39:5
+ |
+LL | { unit_fn_block() };
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::semicolon-inside-block` implied by `-D warnings`
+help: put the `;` here
+ |
+LL - { unit_fn_block() };
+LL + { unit_fn_block(); }
+ |
+
+error: consider moving the `;` inside the block for consistent formatting
+ --> $DIR/semicolon_inside_block.rs:40:5
+ |
+LL | unsafe { unit_fn_block() };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: put the `;` here
+ |
+LL - unsafe { unit_fn_block() };
+LL + unsafe { unit_fn_block(); }
+ |
+
+error: consider moving the `;` inside the block for consistent formatting
+ --> $DIR/semicolon_inside_block.rs:48:5
+ |
+LL | / {
+LL | | unit_fn_block();
+LL | | unit_fn_block()
+LL | | };
+ | |______^
+ |
+help: put the `;` here
+ |
+LL ~ unit_fn_block();
+LL ~ }
+ |
+
+error: consider moving the `;` inside the block for consistent formatting
+ --> $DIR/semicolon_inside_block.rs:61:5
+ |
+LL | { m!(()) };
+ | ^^^^^^^^^^^
+ |
+help: put the `;` here
+ |
+LL - { m!(()) };
+LL + { m!(()); }
+ |
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+// run-rustfix
+#![allow(
+ unused,
+ clippy::unused_unit,
+ clippy::unnecessary_operation,
+ clippy::no_effect,
+ clippy::single_element_loop
+)]
+#![warn(clippy::semicolon_outside_block)]
+
+macro_rules! m {
+ (()) => {
+ ()
+ };
+ (0) => {{
+ 0
+ };};
+ (1) => {{
+ 1;
+ }};
+ (2) => {{
+ 2;
+ }};
+}
+
+fn unit_fn_block() {
+ ()
+}
+
+#[rustfmt::skip]
+fn main() {
+ { unit_fn_block() }
+ unsafe { unit_fn_block() }
+
+ {
+ unit_fn_block()
+ }
+
+ { unit_fn_block() };
+ unsafe { unit_fn_block() };
+
+ { unit_fn_block() };
+ unsafe { unit_fn_block() };
+
+ { unit_fn_block(); };
+ unsafe { unit_fn_block(); };
+
+ {
+ unit_fn_block();
+ unit_fn_block()
+ };
+ {
+ unit_fn_block();
+ unit_fn_block()
+ };
+ {
+ unit_fn_block();
+ unit_fn_block();
+ };
+
+ { m!(()) };
+ { m!(()) };
+ { m!(()); };
+ m!(0);
+ m!(1);
+ m!(2);
+
+ for _ in [()] {
+ unit_fn_block();
+ }
+ for _ in [()] {
+ unit_fn_block()
+ }
+
+ let _d = || {
+ unit_fn_block();
+ };
+ let _d = || {
+ unit_fn_block()
+ };
+
+ { unit_fn_block(); };
+
+ unit_fn_block()
+}
--- /dev/null
+// run-rustfix
+#![allow(
+ unused,
+ clippy::unused_unit,
+ clippy::unnecessary_operation,
+ clippy::no_effect,
+ clippy::single_element_loop
+)]
+#![warn(clippy::semicolon_outside_block)]
+
+macro_rules! m {
+ (()) => {
+ ()
+ };
+ (0) => {{
+ 0
+ };};
+ (1) => {{
+ 1;
+ }};
+ (2) => {{
+ 2;
+ }};
+}
+
+fn unit_fn_block() {
+ ()
+}
+
+#[rustfmt::skip]
+fn main() {
+ { unit_fn_block() }
+ unsafe { unit_fn_block() }
+
+ {
+ unit_fn_block()
+ }
+
+ { unit_fn_block() };
+ unsafe { unit_fn_block() };
+
+ { unit_fn_block(); }
+ unsafe { unit_fn_block(); }
+
+ { unit_fn_block(); };
+ unsafe { unit_fn_block(); };
+
+ {
+ unit_fn_block();
+ unit_fn_block()
+ };
+ {
+ unit_fn_block();
+ unit_fn_block();
+ }
+ {
+ unit_fn_block();
+ unit_fn_block();
+ };
+
+ { m!(()) };
+ { m!(()); }
+ { m!(()); };
+ m!(0);
+ m!(1);
+ m!(2);
+
+ for _ in [()] {
+ unit_fn_block();
+ }
+ for _ in [()] {
+ unit_fn_block()
+ }
+
+ let _d = || {
+ unit_fn_block();
+ };
+ let _d = || {
+ unit_fn_block()
+ };
+
+ { unit_fn_block(); };
+
+ unit_fn_block()
+}
--- /dev/null
+error: consider moving the `;` outside the block for consistent formatting
+ --> $DIR/semicolon_outside_block.rs:42:5
+ |
+LL | { unit_fn_block(); }
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::semicolon-outside-block` implied by `-D warnings`
+help: put the `;` here
+ |
+LL - { unit_fn_block(); }
+LL + { unit_fn_block() };
+ |
+
+error: consider moving the `;` outside the block for consistent formatting
+ --> $DIR/semicolon_outside_block.rs:43:5
+ |
+LL | unsafe { unit_fn_block(); }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: put the `;` here
+ |
+LL - unsafe { unit_fn_block(); }
+LL + unsafe { unit_fn_block() };
+ |
+
+error: consider moving the `;` outside the block for consistent formatting
+ --> $DIR/semicolon_outside_block.rs:52:5
+ |
+LL | / {
+LL | | unit_fn_block();
+LL | | unit_fn_block();
+LL | | }
+ | |_____^
+ |
+help: put the `;` here
+ |
+LL ~ unit_fn_block()
+LL ~ };
+ |
+
+error: consider moving the `;` outside the block for consistent formatting
+ --> $DIR/semicolon_outside_block.rs:62:5
+ |
+LL | { m!(()); }
+ | ^^^^^^^^^^^
+ |
+help: put the `;` here
+ |
+LL - { m!(()); }
+LL + { m!(()) };
+ |
+
+error: aborting due to 4 previous errors
+
let includestr = include_bytes!("string_lit_as_bytes.rs");
let _ = b"string with newline\t\n";
+
+ let _ = match "x".as_bytes() {
+ b"xx" => 0,
+ [b'x', ..] => 1,
+ _ => 2,
+ };
}
fn main() {}
let includestr = include_str!("string_lit_as_bytes.rs").as_bytes();
let _ = "string with newline\t\n".as_bytes();
+
+ let _ = match "x".as_bytes() {
+ b"xx" => 0,
+ [b'x', ..] => 1,
+ _ => 2,
+ };
}
fn main() {}
panic!("p4 {var}");
}
}
+
+ assert!(var == 1, "p5 {}", var);
+ debug_assert!(var == 1, "p6 {}", var);
}
panic!("p4 {var}");
}
}
+
+ assert!(var == 1, "p5 {var}");
+ debug_assert!(var == 1, "p6 {var}");
}
LL + panic!("p3 {var}");
|
-error: aborting due to 4 previous errors
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args_panic.rs:30:5
+ |
+LL | assert!(var == 1, "p5 {}", var);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: change this to
+ |
+LL - assert!(var == 1, "p5 {}", var);
+LL + assert!(var == 1, "p5 {var}");
+ |
+
+error: variables can be used directly in the `format!` string
+ --> $DIR/uninlined_format_args_panic.rs:31:5
+ |
+LL | debug_assert!(var == 1, "p6 {}", var);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: change this to
+ |
+LL - debug_assert!(var == 1, "p6 {}", var);
+LL + debug_assert!(var == 1, "p6 {var}");
+ |
+
+error: aborting due to 6 previous errors
panic!("p4 {var}");
}
}
+
+ assert!(var == 1, "p5 {}", var);
+ debug_assert!(var == 1, "p6 {}", var);
}
Key(v.to_vec())
}
}
+
+// This is a watered down version of the code in: https://github.com/oxigraph/rio
+// The ICE is triggered by the call to `to_owned` on this line:
+// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116
+mod issue_10021 {
+ #![allow(unused)]
+
+ pub struct Iri<T>(T);
+
+ impl<T: AsRef<str>> Iri<T> {
+ pub fn parse(iri: T) -> Result<Self, ()> {
+ unimplemented!()
+ }
+ }
+
+ pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> {
+ let base_iri = Iri::parse(url.to_owned())?;
+ Ok(())
+ }
+}
Key(v.to_vec())
}
}
+
+// This is a watered down version of the code in: https://github.com/oxigraph/rio
+// The ICE is triggered by the call to `to_owned` on this line:
+// https://github.com/oxigraph/rio/blob/66635b9ff8e5423e58932353fa40d6e64e4820f7/testsuite/src/parser_evaluator.rs#L116
+mod issue_10021 {
+ #![allow(unused)]
+
+ pub struct Iri<T>(T);
+
+ impl<T: AsRef<str>> Iri<T> {
+ pub fn parse(iri: T) -> Result<Self, ()> {
+ unimplemented!()
+ }
+ }
+
+ pub fn parse_w3c_rdf_test_file(url: &str) -> Result<(), ()> {
+ let base_iri = Iri::parse(url.to_owned())?;
+ Ok(())
+ }
+}
--- /dev/null
+// run-rustfix
+
+#![feature(lang_items, start, libc)]
+#![no_std]
+#![deny(clippy::zero_ptr)]
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ let _ = core::ptr::null::<usize>();
+ let _ = core::ptr::null_mut::<f64>();
+ let _: *const u8 = core::ptr::null();
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
--- /dev/null
+// run-rustfix
+
+#![feature(lang_items, start, libc)]
+#![no_std]
+#![deny(clippy::zero_ptr)]
+
+#[start]
+fn main(_argc: isize, _argv: *const *const u8) -> isize {
+ let _ = 0 as *const usize;
+ let _ = 0 as *mut f64;
+ let _: *const u8 = 0 as *const _;
+ 0
+}
+
+#[panic_handler]
+fn panic(_info: &core::panic::PanicInfo) -> ! {
+ loop {}
+}
+
+#[lang = "eh_personality"]
+extern "C" fn eh_personality() {}
--- /dev/null
+error: `0 as *const _` detected
+ --> $DIR/zero_ptr_no_std.rs:9:13
+ |
+LL | let _ = 0 as *const usize;
+ | ^^^^^^^^^^^^^^^^^ help: try: `core::ptr::null::<usize>()`
+ |
+note: the lint level is defined here
+ --> $DIR/zero_ptr_no_std.rs:5:9
+ |
+LL | #![deny(clippy::zero_ptr)]
+ | ^^^^^^^^^^^^^^^^
+
+error: `0 as *mut _` detected
+ --> $DIR/zero_ptr_no_std.rs:10:13
+ |
+LL | let _ = 0 as *mut f64;
+ | ^^^^^^^^^^^^^ help: try: `core::ptr::null_mut::<f64>()`
+
+error: `0 as *const _` detected
+ --> $DIR/zero_ptr_no_std.rs:11:24
+ |
+LL | let _: *const u8 = 0 as *const _;
+ | ^^^^^^^^^^^^^ help: try: `core::ptr::null()`
+
+error: aborting due to 3 previous errors
+
#![warn(rust_2018_idioms, unused_lifetimes)]
#![allow(clippy::single_match_else)]
-use rustc_tools_util::VersionInfo;
use std::fs;
#[test]
[relabel]
allow-unauthenticated = [
"A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*",
- "good-first-issue"
+ "good-first-issue", "beta-nominated"
]
# Allows shortcuts like `@rustbot ready`
/// Absolute path to the directory to use for incremental compilation. Example:
/// /path/to/build/host-triple/test/ui/relative/testname.mode/testname.inc
-pub fn incremental_dir(config: &Config, testpaths: &TestPaths) -> PathBuf {
- output_base_name(config, testpaths, None).with_extension("inc")
+pub fn incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
+ output_base_name(config, testpaths, revision).with_extension("inc")
}
}
debug!("running {:?}", testpaths.file.display());
let mut props = TestProps::from_file(&testpaths.file, revision, &config);
+
+ // For non-incremental (i.e. regular UI) tests, the incremental directory
+ // takes into account the revision name, since the revisions are independent
+ // of each other and can race.
if props.incremental {
- props.incremental_dir = Some(incremental_dir(&config, testpaths));
+ props.incremental_dir = Some(incremental_dir(&config, testpaths, revision));
}
let cx = TestCx { config: &config, props: &props, testpaths, revision };
* [Data race in `thread::scope`](https://github.com/rust-lang/rust/issues/98498)
* [`regex` incorrectly handling unaligned `Vec<u8>` buffers](https://www.reddit.com/r/rust/comments/vq3mmu/comment/ienc7t0?context=3)
* [Incorrect use of `compare_exchange_weak` in `once_cell`](https://github.com/matklad/once_cell/issues/186)
+* [Dropping with unaligned pointers in `vec::IntoIter`](https://github.com/rust-lang/rust/pull/106084)
Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
this.machine.main_fn_ret_place.unwrap().ptr,
this.machine.layouts.isize,
);
- let exit_code = this.read_scalar(&ret_place.into())?.to_machine_isize(this)?;
+ let exit_code = this.read_machine_isize(&ret_place.into())?;
// Need to call this ourselves since we are not going to return to the scheduler
// loop, and we want the main thread TLS to not show up as memory leaks.
this.terminate_active_thread()?;
this.assert_target_os_is_unix("getcwd");
let buf = this.read_pointer(buf_op)?;
- let size = this.read_scalar(size_op)?.to_machine_usize(&*this.tcx)?;
+ let size = this.read_machine_usize(size_op)?;
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.reject_in_isolation("`getcwd`", reject_with)?;
// Standard C allocation
"malloc" => {
let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
- let size = this.read_scalar(size)?.to_machine_usize(this)?;
+ let size = this.read_machine_usize(size)?;
let res = this.malloc(size, /*zero_init:*/ false, MiriMemoryKind::C)?;
this.write_pointer(res, dest)?;
}
"calloc" => {
let [items, len] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
- let items = this.read_scalar(items)?.to_machine_usize(this)?;
- let len = this.read_scalar(len)?.to_machine_usize(this)?;
+ let items = this.read_machine_usize(items)?;
+ let len = this.read_machine_usize(len)?;
let size =
items.checked_mul(len).ok_or_else(|| err_ub_format!("overflow during calloc size computation"))?;
let res = this.malloc(size, /*zero_init:*/ true, MiriMemoryKind::C)?;
"realloc" => {
let [old_ptr, new_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let old_ptr = this.read_pointer(old_ptr)?;
- let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
+ let new_size = this.read_machine_usize(new_size)?;
let res = this.realloc(old_ptr, new_size, MiriMemoryKind::C)?;
this.write_pointer(res, dest)?;
}
// Rust allocation
"__rust_alloc" | "miri_alloc" => {
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
- let size = this.read_scalar(size)?.to_machine_usize(this)?;
- let align = this.read_scalar(align)?.to_machine_usize(this)?;
+ let size = this.read_machine_usize(size)?;
+ let align = this.read_machine_usize(align)?;
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
Self::check_alloc_request(size, align)?;
}
"__rust_alloc_zeroed" => {
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
- let size = this.read_scalar(size)?.to_machine_usize(this)?;
- let align = this.read_scalar(align)?.to_machine_usize(this)?;
+ let size = this.read_machine_usize(size)?;
+ let align = this.read_machine_usize(align)?;
return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| {
Self::check_alloc_request(size, align)?;
"__rust_dealloc" | "miri_dealloc" => {
let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
- let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
- let align = this.read_scalar(align)?.to_machine_usize(this)?;
+ let old_size = this.read_machine_usize(old_size)?;
+ let align = this.read_machine_usize(align)?;
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
let memory_kind = match link_name.as_str() {
"__rust_realloc" => {
let [ptr, old_size, align, new_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
- let old_size = this.read_scalar(old_size)?.to_machine_usize(this)?;
- let align = this.read_scalar(align)?.to_machine_usize(this)?;
- let new_size = this.read_scalar(new_size)?.to_machine_usize(this)?;
+ let old_size = this.read_machine_usize(old_size)?;
+ let align = this.read_machine_usize(align)?;
+ let new_size = this.read_machine_usize(new_size)?;
// No need to check old_size; we anyway check that they match the allocation.
return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| {
let [left, right, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let left = this.read_pointer(left)?;
let right = this.read_pointer(right)?;
- let n = Size::from_bytes(this.read_scalar(n)?.to_machine_usize(this)?);
+ let n = Size::from_bytes(this.read_machine_usize(n)?);
let result = {
let left_bytes = this.read_bytes_ptr_strip_provenance(left, n)?;
let [ptr, val, num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let val = this.read_scalar(val)?.to_i32()?;
- let num = this.read_scalar(num)?.to_machine_usize(this)?;
+ let num = this.read_machine_usize(num)?;
// The docs say val is "interpreted as unsigned char".
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let val = val as u8;
let [ptr, val, num] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let val = this.read_scalar(val)?.to_i32()?;
- let num = this.read_scalar(num)?.to_machine_usize(this)?;
+ let num = this.read_machine_usize(num)?;
// The docs say val is "interpreted as unsigned char".
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
let val = val as u8;
let ty_layout = this.layout_of(ty)?;
let val_byte = this.read_scalar(val_byte)?.to_u8()?;
let ptr = this.read_pointer(ptr)?;
- let count = this.read_scalar(count)?.to_machine_usize(this)?;
+ let count = this.read_machine_usize(count)?;
// `checked_mul` enforces a too small bound (the correct one would probably be machine_isize_max),
// but no actual allocation can be big enough for the difference to be noticeable.
let byte_count = ty_layout.size.checked_mul(count, this).ok_or_else(|| {
let [ptr, mask] = check_arg_count(args)?;
let ptr = this.read_pointer(ptr)?;
- let mask = this.read_scalar(mask)?.to_machine_usize(this)?;
+ let mask = this.read_machine_usize(mask)?;
let masked_addr = Size::from_bytes(ptr.addr().bytes() & mask);
return Ok(false);
}
- let req_align = this.read_scalar(align_op)?.to_machine_usize(this)?;
+ let req_align = this.read_machine_usize(align_op)?;
// Stop if the alignment is not a power of two.
if !req_align.is_power_of_two() {
let [fd, buf, count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
- let count = this.read_scalar(count)?.to_machine_usize(this)?;
+ let count = this.read_machine_usize(count)?;
let result = this.read(fd, buf, count)?;
this.write_scalar(Scalar::from_machine_isize(result, this), dest)?;
}
let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let fd = this.read_scalar(fd)?.to_i32()?;
let buf = this.read_pointer(buf)?;
- let count = this.read_scalar(n)?.to_machine_usize(this)?;
+ let count = this.read_machine_usize(n)?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, count);
let result = this.write(fd, buf, count)?;
// Now, `result` is the value we return back to the program.
let [fd, offset, len, advice] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.read_scalar(fd)?.to_i32()?;
- this.read_scalar(offset)?.to_machine_isize(this)?;
- this.read_scalar(len)?.to_machine_isize(this)?;
+ this.read_machine_isize(offset)?;
+ this.read_machine_isize(len)?;
this.read_scalar(advice)?.to_i32()?;
// fadvise is only informational, we can ignore it.
this.write_null(dest)?;
"posix_memalign" => {
let [ret, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let ret = this.deref_operand(ret)?;
- let align = this.read_scalar(align)?.to_machine_usize(this)?;
- let size = this.read_scalar(size)?.to_machine_usize(this)?;
+ let align = this.read_machine_usize(align)?;
+ let size = this.read_machine_usize(size)?;
// Align must be power of 2, and also at least ptr-sized (POSIX rules).
// But failure to adhere to this is not UB, it's an error condition.
if !align.is_power_of_two() || align < this.pointer_size().bytes() {
// Dynamic symbol loading
"dlsym" => {
let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
- this.read_scalar(handle)?.to_machine_usize(this)?;
+ this.read_machine_usize(handle)?;
let symbol = this.read_pointer(symbol)?;
let symbol_name = this.read_c_str(symbol)?;
if let Some(dlsym) = Dlsym::from_str(symbol_name, &this.tcx.sess.target.os)? {
let [errnum, buf, buflen] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let errnum = this.read_scalar(errnum)?;
let buf = this.read_pointer(buf)?;
- let buflen = this.read_scalar(buflen)?.to_machine_usize(this)?;
+ let buflen = this.read_machine_usize(buflen)?;
let error = this.try_errnum_to_io_error(errnum)?;
let formatted = match error {
let uid = this.read_scalar(uid)?.to_u32()?;
let pwd = this.deref_operand(pwd)?;
let buf = this.read_pointer(buf)?;
- let buflen = this.read_scalar(buflen)?.to_machine_usize(this)?;
+ let buflen = this.read_machine_usize(buflen)?;
let result = this.deref_operand(result)?;
// Must be for "us".
this.assert_target_os("linux", "readdir64");
- let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
+ let dirp = this.read_machine_usize(dirp_op)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
this.assert_target_os("macos", "readdir_r");
- let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
+ let dirp = this.read_machine_usize(dirp_op)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
fn closedir(&mut self, dirp_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
- let dirp = this.read_scalar(dirp_op)?.to_machine_usize(this)?;
+ let dirp = this.read_machine_usize(dirp_op)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
let pathname = this.read_path_from_c_str(this.read_pointer(pathname_op)?)?;
let buf = this.read_pointer(buf_op)?;
- let bufsize = this.read_scalar(bufsize_op)?.to_machine_usize(this)?;
+ let bufsize = this.read_machine_usize(bufsize_op)?;
// Reject if isolation is enabled.
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
"incorrect number of arguments for syscall: got 0, expected at least 1"
);
}
- match this.read_scalar(&args[0])?.to_machine_usize(this)? {
+ match this.read_machine_usize(&args[0])? {
// `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
// is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
id if id == sys_getrandom => {
let [pid, cpusetsize, mask] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.read_scalar(pid)?.to_i32()?;
- this.read_scalar(cpusetsize)?.to_machine_usize(this)?;
+ this.read_machine_usize(cpusetsize)?;
this.deref_operand(mask)?;
// FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
let einval = this.eval_libc("EINVAL");
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx> {
let ptr = this.read_pointer(ptr)?;
- let len = this.read_scalar(len)?.to_machine_usize(this)?;
+ let len = this.read_machine_usize(len)?;
// The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
// neither of which have any effect on our current PRNG.
Dlsym::getentropy => {
let [ptr, len] = check_arg_count(args)?;
let ptr = this.read_pointer(ptr)?;
- let len = this.read_scalar(len)?.to_machine_usize(this)?;
+ let len = this.read_machine_usize(len)?;
this.gen_random(ptr, len)?;
this.write_null(dest)?;
}
// Querying system information
"pthread_get_stackaddr_np" => {
let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
- this.read_scalar(thread)?.to_machine_usize(this)?;
+ this.read_machine_usize(thread)?;
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
this.write_scalar(stack_addr, dest)?;
}
"pthread_get_stacksize_np" => {
let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
- this.read_scalar(thread)?.to_machine_usize(this)?;
+ this.read_machine_usize(thread)?;
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
this.write_scalar(stack_size, dest)?;
}
throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
}
- let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?;
+ let thread_id = this.read_machine_usize(thread)?;
this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?;
Ok(0)
fn pthread_detach(&mut self, thread: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
- let thread_id = this.read_scalar(thread)?.to_machine_usize(this)?;
+ let thread_id = this.read_machine_usize(thread)?;
this.detach_thread(
thread_id.try_into().expect("thread ID should fit in u32"),
/*allow_terminated_joined*/ false,
byte_offset,
_key,
] = check_arg_count(args)?;
- let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
+ let handle = this.read_machine_isize(handle)?;
let buf = this.read_pointer(buf)?;
let n = this.read_scalar(n)?.to_u32()?;
- let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer
+ let byte_offset = this.read_machine_usize(byte_offset)?; // is actually a pointer
let io_status_block = this.deref_operand(io_status_block)?;
if byte_offset != 0 {
"HeapAlloc" => {
let [handle, flags, size] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
- this.read_scalar(handle)?.to_machine_isize(this)?;
+ this.read_machine_isize(handle)?;
let flags = this.read_scalar(flags)?.to_u32()?;
- let size = this.read_scalar(size)?.to_machine_usize(this)?;
+ let size = this.read_machine_usize(size)?;
let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY
let zero_init = (flags & heap_zero_memory) == heap_zero_memory;
let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
"HeapFree" => {
let [handle, flags, ptr] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
- this.read_scalar(handle)?.to_machine_isize(this)?;
+ this.read_machine_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let ptr = this.read_pointer(ptr)?;
this.free(ptr, MiriMemoryKind::WinHeap)?;
"HeapReAlloc" => {
let [handle, flags, ptr, size] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
- this.read_scalar(handle)?.to_machine_isize(this)?;
+ this.read_machine_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let ptr = this.read_pointer(ptr)?;
- let size = this.read_scalar(size)?.to_machine_usize(this)?;
+ let size = this.read_machine_usize(size)?;
let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
this.write_pointer(res, dest)?;
}
#[allow(non_snake_case)]
let [hModule, lpProcName] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
- this.read_scalar(hModule)?.to_machine_isize(this)?;
+ this.read_machine_isize(hModule)?;
let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
// `term` needs this, so we fake it.
let [console, buffer_info] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
- this.read_scalar(console)?.to_machine_isize(this)?;
+ this.read_machine_isize(console)?;
this.deref_operand(buffer_info)?;
// Indicate an error.
// FIXME: we should set last_error, but to what?
"GetConsoleMode" if this.frame_in_std() => {
let [console, mode] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
- this.read_scalar(console)?.to_machine_isize(this)?;
+ this.read_machine_isize(console)?;
this.deref_operand(mode)?;
// Indicate an error.
this.write_null(dest)?;
let ptr = this.read_pointer(ptr_op)?;
let compare = this.read_pointer(compare_op)?;
- let size = this.read_scalar(size_op)?.to_machine_usize(this)?;
+ let size = this.read_machine_usize(size_op)?;
let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?;
let thread = this.get_active_thread();
let security = this.read_pointer(security_op)?;
// stacksize is ignored, but still needs to be a valid usize
- this.read_scalar(stacksize_op)?.to_machine_usize(this)?;
+ this.read_machine_usize(stacksize_op)?;
let start_routine = this.read_pointer(start_op)?;
let func_arg = this.read_immediate(arg_op)?;
let flags = this.read_scalar(flags_op)?.to_u32()?;
ptr: &'static i32,
}
-// But we need to gurantee some alignment
+// But we need to guarantee some alignment
struct Wrapper {
align: u64,
data: Data,
--- /dev/null
+//! Test that drop_in_place retags the entire place,
+//! invalidating all aliases to it.
+
+// A zero-sized drop type -- the retagging of `fn drop` itself won't
+// do anything (since it is zero-sized); we are entirely relying on the retagging
+// in `drop_in_place` here.
+#[repr(transparent)]
+struct HasDrop;
+impl Drop for HasDrop {
+ fn drop(&mut self) {
+ unsafe {
+ let _val = *P;
+ //~^ ERROR: /not granting access .* because that would remove .* which is strongly protected/
+ }
+ }
+}
+
+static mut P: *mut u8 = core::ptr::null_mut();
+
+fn main() {
+ unsafe {
+ let mut x = (HasDrop, 0u8);
+ let x = core::ptr::addr_of_mut!(x);
+ P = x.cast();
+ core::ptr::drop_in_place(x);
+ }
+}
--- /dev/null
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
+ --> $DIR/drop_in_place_protector.rs:LL:CC
+ |
+LL | let _val = *P;
+ | ^^ not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected because it is an argument of call ID
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
+help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x1]
+ --> $DIR/drop_in_place_protector.rs:LL:CC
+ |
+LL | let x = core::ptr::addr_of_mut!(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: <TAG> is this argument
+ --> $DIR/drop_in_place_protector.rs:LL:CC
+ |
+LL | core::ptr::drop_in_place(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: BACKTRACE:
+ = note: inside `<HasDrop as std::ops::Drop>::drop` at $DIR/drop_in_place_protector.rs:LL:CC
+ = note: inside `std::ptr::drop_in_place::<HasDrop> - shim(Some(HasDrop))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
+ = note: inside `std::ptr::drop_in_place::<(HasDrop, u8)> - shim(Some((HasDrop, u8)))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
+note: inside `main`
+ --> $DIR/drop_in_place_protector.rs:LL:CC
+ |
+LL | core::ptr::drop_in_place(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `core::ptr::addr_of_mut` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
--- /dev/null
+//! Test that drop_in_place mutably retags the entire place, even for a type that does not need
+//! dropping, ensuring among other things that it is writeable
+
+//@error-pattern: /retag .* for Unique permission .* only grants SharedReadOnly permission/
+
+fn main() {
+ unsafe {
+ let x = 0u8;
+ let x = core::ptr::addr_of!(x);
+ core::ptr::drop_in_place(x.cast_mut());
+ }
+}
--- /dev/null
+error: Undefined Behavior: trying to retag from <TAG> for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
+ --> RUSTLIB/core/src/ptr/mod.rs:LL:CC
+ |
+LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | trying to retag from <TAG> for Unique permission at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location
+ | this error occurs as part of retag at ALLOC[0x0..0x1]
+ |
+ = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
+ = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
+help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x1]
+ --> $DIR/drop_in_place_retag.rs:LL:CC
+ |
+LL | let x = core::ptr::addr_of!(x);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ = note: BACKTRACE:
+ = note: inside `std::ptr::drop_in_place::<u8> - shim(None)` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
+note: inside `main`
+ --> $DIR/drop_in_place_retag.rs:LL:CC
+ |
+LL | core::ptr::drop_in_place(x.cast_mut());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: this error originates in the macro `core::ptr::addr_of` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
--- /dev/null
+#[repr(transparent)]
+struct HasDrop(u8);
+
+impl Drop for HasDrop {
+ fn drop(&mut self) {}
+}
+
+#[repr(C, align(2))]
+struct PartialDrop {
+ a: HasDrop,
+ b: u8,
+}
+
+//@error-pattern: /alignment 2 is required/
+fn main() {
+ unsafe {
+ // Create an unaligned pointer
+ let mut x = [0_u16; 2];
+ let p = core::ptr::addr_of_mut!(x).cast::<u8>();
+ let p = p.add(1);
+ let p = p.cast::<PartialDrop>();
+
+ core::ptr::drop_in_place(p);
+ }
+}
--- /dev/null
+error: Undefined Behavior: accessing memory with alignment ALIGN, but alignment ALIGN is required
+ --> RUSTLIB/core/src/ptr/mod.rs:LL:CC
+ |
+LL | pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ accessing memory with alignment ALIGN, but alignment ALIGN is required
+ |
+ = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+ = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+ = note: BACKTRACE:
+ = note: inside `std::ptr::drop_in_place::<PartialDrop> - shim(Some(PartialDrop))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
+note: inside `main`
+ --> $DIR/drop_in_place.rs:LL:CC
+ |
+LL | core::ptr::drop_in_place(p);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
//@compile-flags: -Zmiri-strict-provenance
+#![feature(iter_advance_by, iter_next_chunk)]
+
// Gather all references from a mutable iterator and make sure Miri notices if
// using them is dangerous.
fn test_all_refs<'a, T: 'a>(dummy: &mut T, iter: impl Iterator<Item = &'a mut T>) {
}
fn vec_into_iter_rev() -> u8 {
- vec![1, 2, 3, 4].into_iter().map(|x| x * x).fold(0, |x, y| x + y)
+ vec![1, 2, 3, 4].into_iter().rev().map(|x| x * x).fold(0, |x, y| x + y)
}
-fn vec_into_iter_zst() -> usize {
- vec![[0u64; 0], [0u64; 0]].into_iter().rev().map(|x| x.len()).sum()
+fn vec_into_iter_zst() {
+ for _ in vec![[0u64; 0]].into_iter() {}
+ let v = vec![[0u64; 0], [0u64; 0]].into_iter().map(|x| x.len()).sum::<usize>();
+ assert_eq!(v, 0);
+
+ let mut it = vec![[0u64; 0], [0u64; 0]].into_iter();
+ it.advance_by(1);
+ drop(it);
+
+ let mut it = vec![[0u64; 0], [0u64; 0]].into_iter();
+ it.next_chunk::<1>().unwrap();
+ drop(it);
+
+ let mut it = vec![[0u64; 0], [0u64; 0]].into_iter();
+ it.next_chunk::<4>().unwrap_err();
+ drop(it);
}
-fn vec_into_iter_rev_zst() -> usize {
- vec![[0u64; 0], [0u64; 0]].into_iter().rev().map(|x| x.len()).sum()
+fn vec_into_iter_rev_zst() {
+ for _ in vec![[0u64; 0]; 5].into_iter().rev() {}
+ let v = vec![[0u64; 0], [0u64; 0]].into_iter().rev().map(|x| x.len()).sum::<usize>();
+ assert_eq!(v, 0);
}
fn vec_iter_and_mut() {
assert_eq!(vec_into_iter(), 30);
assert_eq!(vec_into_iter_rev(), 30);
vec_iter_and_mut();
- assert_eq!(vec_into_iter_zst(), 0);
- assert_eq!(vec_into_iter_rev_zst(), 0);
+ vec_into_iter_zst();
+ vec_into_iter_rev_zst();
vec_iter_and_mut_rev();
assert_eq!(make_vec().capacity(), 4);
struct Config {
verbose: bool,
sequential: bool,
+ batch: bool,
bind: SocketAddr,
}
Config {
verbose: false,
sequential: false,
+ batch: false,
bind: if cfg!(target_os = "android") || cfg!(windows) {
([0, 0, 0, 0], 12345).into()
} else {
}
"--bind" => next_is_bind = true,
"--sequential" => config.sequential = true,
+ "--batch" => config.batch = true,
"--verbose" | "-v" => config.verbose = true,
"--help" | "-h" => {
show_help();
OPTIONS:
--bind <IP>:<PORT> Specify IP address and port to listen for requests, e.g. "0.0.0.0:12345"
--sequential Run only one test at a time
+ --batch Send stdout and stderr in batch instead of streaming
-v, --verbose Show status messages
-h, --help Show this help screen
"#,
// Some tests assume RUST_TEST_TMPDIR exists
cmd.env("RUST_TEST_TMPDIR", tmp.to_owned());
- // Spawn the child and ferry over stdout/stderr to the socket in a framed
- // fashion (poor man's style)
- let mut child =
- t!(cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());
- drop(lock);
- let mut stdout = child.stdout.take().unwrap();
- let mut stderr = child.stderr.take().unwrap();
let socket = Arc::new(Mutex::new(reader.into_inner()));
- let socket2 = socket.clone();
- let thread = thread::spawn(move || my_copy(&mut stdout, 0, &*socket2));
- my_copy(&mut stderr, 1, &*socket);
- thread.join().unwrap();
- // Finally send over the exit status.
- let status = t!(child.wait());
+ let status = if config.batch {
+ let child =
+ t!(cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).output());
+ batch_copy(&child.stdout, 0, &*socket);
+ batch_copy(&child.stderr, 1, &*socket);
+ child.status
+ } else {
+ // Spawn the child and ferry over stdout/stderr to the socket in a framed
+ // fashion (poor man's style)
+ let mut child =
+ t!(cmd.stdin(Stdio::null()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn());
+ drop(lock);
+ let mut stdout = child.stdout.take().unwrap();
+ let mut stderr = child.stderr.take().unwrap();
+ let socket2 = socket.clone();
+ let thread = thread::spawn(move || my_copy(&mut stdout, 0, &*socket2));
+ my_copy(&mut stderr, 1, &*socket);
+ thread.join().unwrap();
+ t!(child.wait())
+ };
+ // Finally send over the exit status.
let (which, code) = get_status_code(&status);
t!(socket.lock().unwrap().write_all(&[
loop {
let n = t!(src.read(&mut b));
let mut dst = dst.lock().unwrap();
- t!(dst.write_all(&[
- which,
- (n >> 24) as u8,
- (n >> 16) as u8,
- (n >> 8) as u8,
- (n >> 0) as u8,
- ]));
+ t!(dst.write_all(&create_header(which, n as u32)));
if n > 0 {
t!(dst.write_all(&b[..n]));
} else {
}
}
+fn batch_copy(buf: &[u8], which: u8, dst: &Mutex<dyn Write>) {
+ let n = buf.len();
+ let mut dst = dst.lock().unwrap();
+ t!(dst.write_all(&create_header(which, n as u32)));
+ if n > 0 {
+ t!(dst.write_all(buf));
+ // Marking buf finished
+ t!(dst.write_all(&[which, 0, 0, 0, 0,]));
+ }
+}
+
+const fn create_header(which: u8, n: u32) -> [u8; 5] {
+ let bytes = n.to_be_bytes();
+ [which, bytes[0], bytes[1], bytes[2], bytes[3]]
+}
+
fn read_u32(r: &mut dyn Read) -> u32 {
let mut len = [0; 4];
t!(r.read_exact(&mut len));
- ((len[0] as u32) << 24)
- | ((len[1] as u32) << 16)
- | ((len[2] as u32) << 8)
- | ((len[3] as u32) << 0)
+ u32::from_be_bytes(len)
}
// A few of those error codes can't be tested but all the others can and *should* be tested!
const EXEMPTED_FROM_TEST: &[&str] = &[
- "E0313", "E0377", "E0461", "E0462", "E0465", "E0476", "E0490", "E0514", "E0519", "E0523",
- "E0554", "E0640", "E0717", "E0729", "E0789",
+ "E0313", "E0461", "E0465", "E0476", "E0490", "E0514", "E0519", "E0523", "E0554", "E0640",
+ "E0717", "E0729", "E0789",
];
// Some error codes don't have any tests apparently...
let bad = std::sync::Arc::new(AtomicBool::new(false));
+ let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
+ // poll all threads for completion before awaiting the oldest one
+ for i in (0..handles.len()).rev() {
+ if handles[i].is_finished() {
+ handles.swap_remove_back(i).unwrap().join().unwrap();
+ }
+ }
+
+ while handles.len() >= concurrency.get() {
+ handles.pop_front().unwrap().join().unwrap();
+ }
+ };
+
scope(|s| {
let mut handles: VecDeque<ScopedJoinHandle<'_, ()>> =
VecDeque::with_capacity(concurrency.get());
macro_rules! check {
($p:ident $(, $args:expr)* ) => {
- while handles.len() >= concurrency.get() {
- handles.pop_front().unwrap().join().unwrap();
- }
+ drain_handles(&mut handles);
let handle = s.spawn(|| {
let mut flag = false;
check!(alphabetical, &library_path);
let collected = {
- while handles.len() >= concurrency.get() {
- handles.pop_front().unwrap().join().unwrap();
- }
+ drain_handles(&mut handles);
+
let mut flag = false;
let r = features::check(&src_path, &compiler_path, &library_path, &mut flag, verbose);
if flag {
//! `// ignore-tidy-CHECK-NAME`.
use crate::walk::{filter_dirs, walk};
-use regex::Regex;
+use regex::{Regex, RegexSet};
use std::path::Path;
/// Error code markdown is restricted to 80 columns because they can be
.chain(PROBLEMATIC_CONSTS.iter().map(|v| format!("{:x}", v)))
.chain(PROBLEMATIC_CONSTS.iter().map(|v| format!("{:X}", v)))
.collect();
+ let problematic_regex = RegexSet::new(problematic_consts_strings.as_slice()).unwrap();
walk(path, &mut skip, &mut |entry, contents| {
let file = entry.path();
let filename = file.file_name().unwrap().to_string_lossy();
let mut trailing_new_lines = 0;
let mut lines = 0;
let mut last_safety_comment = false;
+ let is_test = file.components().any(|c| c.as_os_str() == "tests");
+ // scanning the whole file for multiple needles at once is more efficient than
+ // executing lines times needles separate searches.
+ let any_problematic_line = problematic_regex.is_match(contents);
for (i, line) in contents.split('\n').enumerate() {
+ if line.is_empty() {
+ if i == 0 {
+ leading_new_lines = true;
+ }
+ trailing_new_lines += 1;
+ continue;
+ } else {
+ trailing_new_lines = 0;
+ }
+
+ let trimmed = line.trim();
+
+ if !trimmed.starts_with("//") {
+ lines += 1;
+ }
+
let mut err = |msg: &str| {
tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
};
suppressible_tidy_err!(err, skip_cr, "CR character");
}
if filename != "style.rs" {
- if line.contains("TODO") {
+ if trimmed.contains("TODO") {
err("TODO is deprecated; use FIXME")
}
- if line.contains("//") && line.contains(" XXX") {
+ if trimmed.contains("//") && trimmed.contains(" XXX") {
err("XXX is deprecated; use FIXME")
}
- for s in problematic_consts_strings.iter() {
- if line.contains(s) {
- err("Don't use magic numbers that spell things (consider 0x12345678)");
+ if any_problematic_line {
+ for s in problematic_consts_strings.iter() {
+ if trimmed.contains(s) {
+ err("Don't use magic numbers that spell things (consider 0x12345678)");
+ }
}
}
}
- let is_test = || file.components().any(|c| c.as_os_str() == "tests");
// for now we just check libcore
- if line.contains("unsafe {") && !line.trim().starts_with("//") && !last_safety_comment {
- if file.components().any(|c| c.as_os_str() == "core") && !is_test() {
+ if trimmed.contains("unsafe {") && !trimmed.starts_with("//") && !last_safety_comment {
+ if file.components().any(|c| c.as_os_str() == "core") && !is_test {
suppressible_tidy_err!(err, skip_undocumented_unsafe, "undocumented unsafe");
}
}
- if line.contains("// SAFETY:") {
+ if trimmed.contains("// SAFETY:") {
last_safety_comment = true;
- } else if line.trim().starts_with("//") || line.trim().is_empty() {
+ } else if trimmed.starts_with("//") || trimmed.is_empty() {
// keep previous value
} else {
last_safety_comment = false;
if (line.starts_with("// Copyright")
|| line.starts_with("# Copyright")
|| line.starts_with("Copyright"))
- && (line.contains("Rust Developers") || line.contains("Rust Project Developers"))
+ && (trimmed.contains("Rust Developers")
+ || trimmed.contains("Rust Project Developers"))
{
suppressible_tidy_err!(
err,
if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
err(LLVM_UNREACHABLE_INFO);
}
- if line.is_empty() {
- if i == 0 {
- leading_new_lines = true;
- }
- trailing_new_lines += 1;
- } else {
- trailing_new_lines = 0;
- }
-
- if !line.trim().starts_with("//") {
- lines += 1;
- }
}
if leading_new_lines {
let mut err = |_| {
const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually.
const ROOT_ENTRY_LIMIT: usize = 939;
-const ISSUES_ENTRY_LIMIT: usize = 2040;
+const ISSUES_ENTRY_LIMIT: usize = 2020;
fn check_entries(path: &Path, bad: &mut bool) {
for dir in Walk::new(&path.join("test/ui")) {
-//! Run `x.py` from any subdirectory of a rust compiler checkout.
+//! Run bootstrap from any subdirectory of a rust compiler checkout.
//!
//! We prefer `exec`, to avoid adding an extra process in the process tree.
//! However, since `exec` isn't available on Windows, we indirect through
//! `exec_or_status`, which will call `exec` on unix and `status` on Windows.
//!
-//! We use `python`, `python3`, or `python2` as the python interpreter to run
-//! `x.py`, in that order of preference.
+//! We use `powershell.exe x.ps1` on Windows, and `sh -c x` on Unix, those are
+//! the ones that call `x.py`. We use `sh -c` on Unix, because it is a standard.
+//! We also don't use `pwsh` on Windows, because it is not installed by default;
use std::{
- env::{self, consts::EXE_EXTENSION},
- io,
+ env, io,
+ path::Path,
process::{self, Command, ExitStatus},
};
-const PYTHON: &str = "python";
-const PYTHON2: &str = "python2";
-const PYTHON3: &str = "python3";
-
-fn python() -> &'static str {
- let val = match env::var_os("PATH") {
- Some(val) => val,
- None => return PYTHON,
- };
-
- let mut python2 = false;
- let mut python3 = false;
-
- for dir in env::split_paths(&val) {
- // `python` should always take precedence over python2 / python3 if it exists
- if dir.join(PYTHON).with_extension(EXE_EXTENSION).exists() {
- return PYTHON;
- }
+#[cfg(windows)]
+fn x_command(dir: &Path) -> Command {
+ let mut cmd = Command::new("powershell.exe");
+ cmd.args([
+ "-NoLogo",
+ "-NoProfile",
+ "-NonInteractive",
+ "-ExecutionPolicy",
+ "RemoteSigned",
+ "-Command",
+ "./x.ps1",
+ ])
+ .current_dir(dir);
+ cmd
+}
- python2 |= dir.join(PYTHON2).with_extension(EXE_EXTENSION).exists();
- python3 |= dir.join(PYTHON3).with_extension(EXE_EXTENSION).exists();
- }
+#[cfg(unix)]
+fn x_command(dir: &Path) -> Command {
+ Command::new(dir.join("x"))
+}
- // try 3 before 2
- if python3 {
- PYTHON3
- } else if python2 {
- PYTHON2
- } else {
- // Python was not found on path, so exit
- eprintln!("Unable to find python in your PATH. Please check it is installed.");
- process::exit(1);
- }
+#[cfg(not(any(windows, unix)))]
+fn x_command(_dir: &Path) -> Command {
+ compile_error!("Unsupported platform");
}
#[cfg(unix)]
let candidate = dir.join("x.py");
if candidate.exists() {
- let mut python = Command::new(python());
+ let mut cmd = x_command(dir);
- python.arg(&candidate).args(env::args().skip(1)).current_dir(dir);
+ cmd.args(env::args().skip(1)).current_dir(dir);
- let result = exec_or_status(&mut python);
+ let result = exec_or_status(&mut cmd);
match result {
Err(error) => {
- eprintln!("Failed to invoke `{}`: {}", candidate.display(), error);
+ eprintln!("Failed to invoke `{:?}`: {}", cmd, error);
}
Ok(status) => {
process::exit(status.code().unwrap_or(1));
message = "Some changes occured in `rustc_ty_utils::consts.rs`"
cc = ["@BoxyUwU"]
+[mentions."compiler/rustc_trait_selection/src/solve/"]
+message = "Some changes occurred to the core trait solver"
+cc = ["@lcnr"]
+
[mentions."compiler/rustc_trait_selection/src/traits/engine.rs"]
message = """
Some changes occurred in engine.rs, potentially modifying the public API \