NO_DEBUG_ASSERTIONS: 1
NO_OVERFLOW_CHECKS: 1
os: macos-latest
- - name: x86_64-apple
+ - name: x86_64-apple-1
env:
- SCRIPT: "./x.py --stage 2 test"
+ SCRIPT: "./x.py --stage 2 test --exclude src/test/ui --exclude src/test/rustdoc --exclude src/test/run-make-fulldeps"
+ RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
+ RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
+ MACOSX_DEPLOYMENT_TARGET: 10.8
+ MACOSX_STD_DEPLOYMENT_TARGET: 10.7
+ NO_LLVM_ASSERTIONS: 1
+ NO_DEBUG_ASSERTIONS: 1
+ NO_OVERFLOW_CHECKS: 1
+ os: macos-latest
+ - name: x86_64-apple-2
+ env:
+ SCRIPT: "./x.py --stage 2 test src/test/ui src/test/rustdoc src/test/run-make-fulldeps"
RUST_CONFIGURE_ARGS: "--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false"
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.8
## Node
node_modules
package-lock.json
+package.json
## Rustdoc GUI tests
src/test/rustdoc-gui/src/**.lock
[submodule "src/llvm-project"]
path = src/llvm-project
url = https://github.com/rust-lang/llvm-project.git
- branch = rustc/13.0-2021-09-30
+ branch = rustc/14.0-2022-02-09
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book
url = https://github.com/rust-embedded/book.git
Kyle J Strand <batmanaod@gmail.com> <kyle.strand@pieinsurance.com>
Kyle J Strand <batmanaod@gmail.com> <kyle.strand@rms.com>
Laurențiu Nicola <lnicola@dend.ro>
-lcnr <bastian_kauschke@hotmail.de>
+lcnr <rust@lcnr.de> <bastian_kauschke@hotmail.de>
Lee Jeffery <leejeffery@gmail.com> Lee Jeffery <lee@leejeffery.co.uk>
Lee Wondong <wdlee91@gmail.com>
Lennart Kudling <github@kudling.de>
"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"
[[package]]
name = "block-buffer"
-version = "0.9.0"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array 0.14.4",
]
"pretty_assertions",
"serde",
"serde_json",
- "time",
"toml",
"winapi",
]
"pretty_env_logger",
"rustc-workspace-hack",
"rustfix 0.6.0",
- "semver 1.0.3",
+ "semver",
"serde",
"serde_ignored",
"serde_json",
"winapi",
]
-[[package]]
-name = "cargo_metadata"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345"
-dependencies = [
- "semver 0.11.0",
- "serde",
- "serde_json",
-]
-
[[package]]
name = "cargo_metadata"
version = "0.14.0"
dependencies = [
"camino",
"cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "semver 1.0.3",
+ "semver",
"serde",
"serde_json",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
- "ansi_term 0.12.1",
+ "ansi_term",
"atty",
"bitflags",
"strsim 0.8.0",
name = "clippy"
version = "0.1.60"
dependencies = [
- "cargo_metadata 0.14.0",
+ "cargo_metadata",
"clippy_lints",
"clippy_utils",
"compiletest_rs",
"derive-new",
"filetime",
- "futures 0.3.12",
+ "futures 0.3.19",
"if_chain",
"itertools 0.10.1",
"parking_lot",
"regex",
"rustc-workspace-hack",
"rustc_tools_util 0.2.0",
- "semver 1.0.3",
+ "semver",
"serde",
"syn",
"tempfile",
version = "0.0.1"
dependencies = [
"bytecount",
- "cargo_metadata 0.14.0",
+ "cargo_metadata",
"clap 2.34.0",
"indoc",
"itertools 0.10.1",
name = "clippy_lints"
version = "0.1.60"
dependencies = [
- "cargo_metadata 0.14.0",
+ "cargo_metadata",
"clippy_utils",
"if_chain",
"itertools 0.10.1",
"quine-mc_cluskey",
"regex-syntax",
"rustc-semver",
- "semver 1.0.3",
+ "semver",
"serde",
"serde_json",
"toml",
[[package]]
name = "compiler_builtins"
-version = "0.1.67"
+version = "0.1.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a68c69e9451f1df4b215c9588c621670c12286b53e60fb5ec4b59aaa1138d18e"
+checksum = "80873f979f0a344a4ade87c2f70d9ccf5720b83b10c97ec7cd745895d021e85a"
dependencies = [
"cc",
"rustc-std-workspace-core",
version = "0.0.0"
[[package]]
-name = "cpuid-bool"
-version = "0.1.2"
+name = "cpufeatures"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
+dependencies = [
+ "libc",
+]
[[package]]
name = "crates-io"
"lazy_static",
]
+[[package]]
+name = "crypto-common"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06"
+dependencies = [
+ "generic-array 0.14.4",
+]
+
[[package]]
name = "crypto-hash"
version = "0.3.4"
[[package]]
name = "digest"
-version = "0.9.0"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
+checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837"
dependencies = [
- "generic-array 0.14.4",
+ "block-buffer 0.10.2",
+ "crypto-common",
]
[[package]]
[[package]]
name = "futures"
-version = "0.1.29"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef"
+checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da9052a1a50244d8d5aa9bf55cbc2fb6f357c86cc52e46c62ed390a7180cf150"
+checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
dependencies = [
"futures-channel",
"futures-core",
[[package]]
name = "futures-channel"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2d31b7ec7efab6eefc7c57233bb10b847986139d88cc2f5a02a1ae6871a1846"
+checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
dependencies = [
"futures-core",
"futures-sink",
[[package]]
name = "futures-core"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79e5145dde8da7d1b3892dad07a9c98fc04bc39892b1ecc9692cf53e2b780a65"
+checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
[[package]]
name = "futures-executor"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9e59fdc009a4b3096bf94f740a0f2424c082521f20a9b08c5c07c48d90fd9b9"
+checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
dependencies = [
"futures-core",
"futures-task",
[[package]]
name = "futures-io"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28be053525281ad8259d47e4de5de657b25e7bac113458555bb4b70bc6870500"
+checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
[[package]]
name = "futures-macro"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c287d25add322d9f9abdcdc5927ca398917996600182178774032e9f8258fedd"
+checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
dependencies = [
- "proc-macro-hack",
"proc-macro2",
"quote",
"syn",
[[package]]
name = "futures-sink"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "caf5c69029bda2e743fddd0582d1083951d65cc9539aebf8812f36c3491342d6"
+checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
[[package]]
name = "futures-task"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13de07eb8ea81ae445aca7b69f5f7bf15d7bf4912d8ca37d6645c77ae8a58d86"
-dependencies = [
- "once_cell",
-]
+checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
[[package]]
name = "futures-util"
-version = "0.3.12"
+version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "632a8cd0f2a4b3fdea1657f08bde063848c3bd00f9bbf6e256b8be78802e624b"
+checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
dependencies = [
- "futures 0.1.29",
+ "futures 0.1.31",
"futures-channel",
"futures-core",
"futures-io",
"memchr",
"pin-project-lite",
"pin-utils",
- "proc-macro-hack",
- "proc-macro-nested",
"slab",
]
checksum = "d2b99d4207e2a04fb4581746903c2bb7eb376f88de9c699d0f3e10feeac0cd3a"
dependencies = [
"derive_more",
- "futures 0.3.12",
+ "futures 0.3.19",
"jsonrpc-core",
"jsonrpc-pubsub",
"jsonrpc-server-utils",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb"
dependencies = [
- "futures 0.3.12",
+ "futures 0.3.19",
"futures-executor",
"futures-util",
"log",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b51da17abecbdab3e3d4f26b01c5ec075e88d3abe3ab3b05dc9aa69392764ec0"
dependencies = [
- "futures 0.3.12",
+ "futures 0.3.19",
"jsonrpc-client-transports",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "382bb0206323ca7cda3dcd7e245cea86d37d02457a02a975e3378fb149a48845"
dependencies = [
- "futures 0.3.12",
+ "futures 0.3.19",
"jsonrpc-core",
"jsonrpc-server-utils",
"log",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240f87695e6c6f62fb37f05c02c04953cf68d6408b8c1c89de85c7a0125b1011"
dependencies = [
- "futures 0.3.12",
+ "futures 0.3.19",
"jsonrpc-core",
"lazy_static",
"log",
checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4"
dependencies = [
"bytes",
- "futures 0.3.12",
+ "futures 0.3.19",
"globset",
"jsonrpc-core",
"lazy_static",
[[package]]
name = "linked-hash-map"
-version = "0.5.3"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "lint-docs"
[[package]]
name = "markup5ever"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aae38d669396ca9b707bfc3db254bc382ddb94f57cc5c235f34623a669a01dab"
+checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
dependencies = [
"log",
"phf",
"phf_codegen",
- "serde",
- "serde_derive",
- "serde_json",
"string_cache",
"string_cache_codegen",
"tendril",
[[package]]
name = "md-5"
-version = "0.9.1"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
+checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae"
dependencies = [
- "block-buffer 0.9.0",
- "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "digest 0.10.2",
]
[[package]]
[[package]]
name = "mio"
-version = "0.7.13"
+version = "0.7.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
+checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
dependencies = [
"libc",
"log",
[[package]]
name = "num_cpus"
-version = "1.13.0"
+version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
-[[package]]
-name = "opaque-debug"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
-
[[package]]
name = "opener"
version = "0.5.0"
[[package]]
name = "openssl-src"
-version = "111.16.0+1.1.1l"
+version = "111.17.0+1.1.1m"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ab2173f69416cf3ec12debb5823d244127d23a9b127d5a5189aa97c5fa2859f"
+checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4"
dependencies = [
"cc",
]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9981e32fb75e004cc148f5fb70342f393830e0a4aa62e3cc93b50976218d42b6"
dependencies = [
- "futures 0.3.12",
+ "futures 0.3.19",
"libc",
"log",
"rand 0.7.3",
[[package]]
name = "pretty_assertions"
-version = "0.6.1"
+version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
+checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
dependencies = [
- "ansi_term 0.11.0",
+ "ansi_term",
"ctor",
- "difference",
+ "diff",
"output_vt100",
]
"version_check",
]
-[[package]]
-name = "proc-macro-hack"
-version = "0.5.19"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
-
-[[package]]
-name = "proc-macro-nested"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086"
-
[[package]]
name = "proc-macro2"
version = "1.0.30"
"anyhow",
"cargo",
"cargo-util",
- "cargo_metadata 0.14.0",
+ "cargo_metadata",
"clippy_lints",
"crossbeam-channel",
"difference",
"env_logger 0.9.0",
- "futures 0.3.12",
+ "futures 0.3.19",
"heck",
"home",
"itertools 0.10.1",
"tokio-stream",
"tokio-util",
"toml",
+ "toml_edit",
"url 2.2.2",
"walkdir",
]
[[package]]
name = "rls-analysis"
-version = "0.18.2"
+version = "0.18.3"
dependencies = [
"derive-new",
"env_logger 0.9.0",
dependencies = [
"clippy_lints",
"env_logger 0.9.0",
- "futures 0.3.12",
+ "futures 0.3.19",
"log",
"rand 0.8.4",
"rls-data",
"rustc_macros",
"rustc_serialize",
"scoped-tls",
- "sha-1 0.9.1",
+ "sha-1 0.10.0",
"sha2",
"tracing",
"unicode-width",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
- "semver 1.0.3",
+ "semver",
]
[[package]]
"askama",
"atty",
"expect-test",
- "itertools 0.9.0",
+ "itertools 0.10.1",
"minifier",
"pulldown-cmark",
"rayon",
"annotate-snippets",
"anyhow",
"bytecount",
- "cargo_metadata 0.14.0",
+ "cargo_metadata",
"derive-new",
"diff",
"dirs",
"libc",
]
-[[package]]
-name = "semver"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
-dependencies = [
- "semver-parser",
- "serde",
-]
-
[[package]]
name = "semver"
version = "1.0.3"
"serde",
]
-[[package]]
-name = "semver-parser"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
-dependencies = [
- "pest",
-]
-
[[package]]
name = "serde"
version = "1.0.125"
"block-buffer 0.7.3",
"digest 0.8.1",
"fake-simd",
- "opaque-debug 0.2.3",
+ "opaque-debug",
]
[[package]]
name = "sha-1"
-version = "0.9.1"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
+checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
- "block-buffer 0.9.0",
- "cfg-if 0.1.10",
- "cpuid-bool",
- "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest 0.10.2",
]
[[package]]
name = "sha2"
-version = "0.9.1"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
+checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
dependencies = [
- "block-buffer 0.9.0",
- "cfg-if 0.1.10",
- "cpuid-bool",
- "digest 0.9.0",
- "opaque-debug 0.3.0",
+ "cfg-if 1.0.0",
+ "cpufeatures",
+ "digest 0.10.2",
]
[[package]]
name = "tidy"
version = "0.1.0"
dependencies = [
- "cargo_metadata 0.12.0",
+ "cargo_metadata",
"crossbeam-utils",
"lazy_static",
"regex",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3"
dependencies = [
- "ansi_term 0.12.1",
+ "ansi_term",
"lazy_static",
"matchers",
"parking_lot",
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ce989c9962c7f61fe084dd4a230eec784649dfc2392467c790007c3a6e134e7"
dependencies = [
- "ansi_term 0.12.1",
+ "ansi_term",
"atty",
"tracing-core",
"tracing-log",
- [Macro attributes may follow `#[derive]` and will see the original (pre-`cfg`) input.][87220]
- [Accept curly-brace macros in expressions, like `m!{ .. }.method()` and `m!{ .. }?`.][88690]
- [Allow panicking in constant evaluation.][89508]
+- [Ignore derived `Clone` and `Debug` implementations during dead code analysis.][85200]
Compiler
--------
Compatibility notes
-------------------
+- [Ignore derived `Clone` and `Debug` implementations during dead code analysis.][85200]
+ This will break some builds that set `#![deny(dead_code)]`.
+
Internal changes
----------------
These changes provide no direct user facing benefits, but represent significant
- [Added an experimental backend for codegen with `libgccjit`.][87260]
+[85200]: https://github.com/rust-lang/rust/pull/85200/
[86191]: https://github.com/rust-lang/rust/pull/86191/
[87220]: https://github.com/rust-lang/rust/pull/87220/
[87260]: https://github.com/rust-lang/rust/pull/87260/
/// ```
///
/// `'outer` is a label.
-#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic)]
+#[derive(Clone, Encodable, Decodable, Copy, HashStable_Generic, Eq, PartialEq)]
pub struct Label {
pub ident: Ident,
}
if i != 0 || j != lines.len() { Some((i, j)) } else { None }
}
- fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option<usize> {
+ fn get_horizontal_trim<'a>(lines: &'a [&str], kind: CommentKind) -> Option<String> {
let mut i = usize::MAX;
let mut first = true;
// present. However, we first need to strip the empty lines so they don't get in the middle
// when we try to compute the "horizontal trim".
let lines = if kind == CommentKind::Block {
- let mut i = 0;
+ // Whatever happens, we skip the first line.
+ let mut i = if lines[0].trim_start().starts_with('*') { 0 } else { 1 };
let mut j = lines.len();
while i < j && lines[i].trim().is_empty() {
return None;
}
}
- Some(i)
+ if lines.is_empty() { None } else { Some(lines[0][..i].into()) }
}
let data_s = data.as_str();
changes = true;
// remove a "[ \t]*\*" block from each line, if possible
for line in lines.iter_mut() {
- if horizontal + 1 < line.len() {
- *line = &line[horizontal + 1..];
+ if let Some(tmp) = line.strip_prefix(&horizontal) {
+ *line = tmp;
+ if kind == CommentKind::Block
+ && (*line == "*" || line.starts_with("* ") || line.starts_with("**"))
+ {
+ *line = &line[1..];
+ }
}
}
}
create_default_session_globals_then(|| {
let comment = "\n let a: *i32;\n *a = 5;\n";
let stripped = beautify_doc_string(Symbol::intern(comment), CommentKind::Block);
- assert_eq!(stripped.as_str(), " let a: *i32;\n *a = 5;");
+ assert_eq!(stripped.as_str(), "let a: *i32;\n*a = 5;");
})
}
assert_eq!(stripped.as_str(), "!test");
})
}
+
+#[test]
+fn test_doc_blocks() {
+ create_default_session_globals_then(|| {
+ let stripped =
+ beautify_doc_string(Symbol::intern(" # Returns\n *\n "), CommentKind::Block);
+ assert_eq!(stripped.as_str(), " # Returns\n\n");
+
+ let stripped = beautify_doc_string(
+ Symbol::intern("\n * # Returns\n *\n "),
+ CommentKind::Block,
+ );
+ assert_eq!(stripped.as_str(), " # Returns\n\n");
+
+ let stripped = beautify_doc_string(Symbol::intern("\n * a\n "), CommentKind::Block);
+ assert_eq!(stripped.as_str(), " a\n");
+ })
+}
err.emit();
}
Entry::Vacant(v) => {
- v.insert(idx);
+ if r == reg {
+ v.insert(idx);
+ }
}
}
};
AssocItemKind::MacCall(..) => panic!("`TyMac` should have been expanded by now"),
};
- // Since `default impl` is not yet implemented, this is always true in impls.
- let has_value = true;
- let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
let hir_id = self.lower_node_id(i.id);
self.lower_attrs(hir_id, &i.attrs);
let item = hir::ImplItem {
ident: self.lower_ident(i.ident),
generics,
vis: self.lower_visibility(&i.vis),
- defaultness,
kind,
span: self.lower_span(i.span),
};
"thiscall-unwind ABI is experimental and subject to change"
);
}
+ "cdecl-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "cdecl-unwind ABI is experimental and subject to change"
+ );
+ }
+ "fastcall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "fastcall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "vectorcall-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "vectorcall-unwind ABI is experimental and subject to change"
+ );
+ }
+ "aapcs-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "aapcs-unwind ABI is experimental and subject to change"
+ );
+ }
+ "win64-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "win64-unwind ABI is experimental and subject to change"
+ );
+ }
+ "sysv64-unwind" => {
+ gate_feature_post!(
+ &self,
+ c_unwind,
+ span,
+ "sysv64-unwind ABI is experimental and subject to change"
+ );
+ }
"wasm" => {
gate_feature_post!(
&self,
//! methods called `Printer::scan_*`, and the 'PRINT' process is the
//! method called `Printer::print`.
+mod convenience;
mod ring;
use ring::RingBuffer;
Inconsistent,
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq)]
enum IndentStyle {
/// Vertically aligned under whatever column this block begins at.
///
Block { offset: isize },
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Default, PartialEq)]
pub struct BreakToken {
offset: isize,
blank_space: isize,
+ pre_break: Option<char>,
}
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, PartialEq)]
pub struct BeginToken {
indent: IndentStyle,
breaks: Breaks,
}
-#[derive(Clone)]
+#[derive(Clone, PartialEq)]
pub enum Token {
// In practice a string token contains either a `&'static str` or a
// `String`. `Cow` is overkill for this because we never modify the data,
End,
}
-impl Token {
- pub fn is_hardbreak_tok(&self) -> bool {
- matches!(self, Token::Break(BreakToken { offset: 0, blank_space: SIZE_INFINITY }))
- }
-}
-
#[derive(Copy, Clone)]
enum PrintFrame {
Fits,
}
}
+ pub fn offset(&mut self, offset: isize) {
+ if let Some(BufEntry { token: Token::Break(token), .. }) = &mut self.buf.last_mut() {
+ token.offset += offset;
+ }
+ }
+
fn check_stream(&mut self) {
while self.right_total - self.left_total > self.space {
if *self.scan_stack.front().unwrap() == self.buf.index_of_first() {
if size > self.space {
self.print_stack.push(PrintFrame::Broken { indent: self.indent, breaks: token.breaks });
self.indent = match token.indent {
- IndentStyle::Block { offset } => (self.indent as isize + offset) as usize,
+ IndentStyle::Block { offset } => {
+ usize::try_from(self.indent as isize + offset).unwrap()
+ }
IndentStyle::Visual => (MARGIN - self.space) as usize,
};
} else {
self.pending_indentation += token.blank_space;
self.space -= token.blank_space;
} else {
+ if let Some(pre_break) = token.pre_break {
+ self.out.push(pre_break);
+ }
self.out.push('\n');
let indent = self.indent as isize + token.offset;
self.pending_indentation = indent;
self.out.push_str(string);
self.space -= string.len() as isize;
}
-
- // Convenience functions to talk to the printer.
-
- /// "raw box"
- pub fn rbox(&mut self, indent: usize, breaks: Breaks) {
- self.scan_begin(BeginToken {
- indent: IndentStyle::Block { offset: indent as isize },
- breaks,
- })
- }
-
- /// Inconsistent breaking box
- pub fn ibox(&mut self, indent: usize) {
- self.rbox(indent, Breaks::Inconsistent)
- }
-
- /// Consistent breaking box
- pub fn cbox(&mut self, indent: usize) {
- self.rbox(indent, Breaks::Consistent)
- }
-
- pub fn visual_align(&mut self) {
- self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
- }
-
- pub fn break_offset(&mut self, n: usize, off: isize) {
- self.scan_break(BreakToken { offset: off, blank_space: n as isize })
- }
-
- pub fn end(&mut self) {
- self.scan_end()
- }
-
- pub fn eof(mut self) -> String {
- self.scan_eof();
- self.out
- }
-
- pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
- let string = wrd.into();
- self.scan_string(string)
- }
-
- fn spaces(&mut self, n: usize) {
- self.break_offset(n, 0)
- }
-
- pub fn zerobreak(&mut self) {
- self.spaces(0)
- }
-
- pub fn space(&mut self) {
- self.spaces(1)
- }
-
- pub fn hardbreak(&mut self) {
- self.spaces(SIZE_INFINITY as usize)
- }
-
- pub fn is_beginning_of_line(&self) -> bool {
- match self.last_token() {
- Some(last_token) => last_token.is_hardbreak_tok(),
- None => true,
- }
- }
-
- pub fn hardbreak_tok_offset(off: isize) -> Token {
- Token::Break(BreakToken { offset: off, blank_space: SIZE_INFINITY })
- }
}
--- /dev/null
+use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, Token, SIZE_INFINITY};
+use std::borrow::Cow;
+
+impl Printer {
+ /// "raw box"
+ pub fn rbox(&mut self, indent: isize, breaks: Breaks) {
+ self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks })
+ }
+
+ /// Inconsistent breaking box
+ pub fn ibox(&mut self, indent: isize) {
+ self.rbox(indent, Breaks::Inconsistent)
+ }
+
+ /// Consistent breaking box
+ pub fn cbox(&mut self, indent: isize) {
+ self.rbox(indent, Breaks::Consistent)
+ }
+
+ pub fn visual_align(&mut self) {
+ self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent });
+ }
+
+ pub fn break_offset(&mut self, n: usize, off: isize) {
+ self.scan_break(BreakToken {
+ offset: off,
+ blank_space: n as isize,
+ ..BreakToken::default()
+ });
+ }
+
+ pub fn end(&mut self) {
+ self.scan_end()
+ }
+
+ pub fn eof(mut self) -> String {
+ self.scan_eof();
+ self.out
+ }
+
+ pub fn word<S: Into<Cow<'static, str>>>(&mut self, wrd: S) {
+ let string = wrd.into();
+ self.scan_string(string)
+ }
+
+ fn spaces(&mut self, n: usize) {
+ self.break_offset(n, 0)
+ }
+
+ pub fn zerobreak(&mut self) {
+ self.spaces(0)
+ }
+
+ pub fn space(&mut self) {
+ self.spaces(1)
+ }
+
+ pub fn hardbreak(&mut self) {
+ self.spaces(SIZE_INFINITY as usize)
+ }
+
+ pub fn is_beginning_of_line(&self) -> bool {
+ match self.last_token() {
+ Some(last_token) => last_token.is_hardbreak_tok(),
+ None => true,
+ }
+ }
+
+ pub fn hardbreak_tok_offset(off: isize) -> Token {
+ Token::Break(BreakToken {
+ offset: off,
+ blank_space: SIZE_INFINITY,
+ ..BreakToken::default()
+ })
+ }
+
+ pub fn trailing_comma(&mut self) {
+ self.scan_break(BreakToken { pre_break: Some(','), ..BreakToken::default() });
+ }
+
+ pub fn trailing_comma_or_space(&mut self) {
+ self.scan_break(BreakToken {
+ blank_space: 1,
+ pre_break: Some(','),
+ ..BreakToken::default()
+ });
+ }
+}
+
+impl Token {
+ pub fn is_hardbreak_tok(&self) -> bool {
+ *self == Printer::hardbreak_tok_offset(0)
+ }
+}
+mod delimited;
mod expr;
mod item;
use std::borrow::Cow;
+pub use self::delimited::IterDelimited;
+
pub enum MacHeader<'a> {
Path(&'a ast::Path),
Keyword(&'static str),
ann: &'a (dyn PpAnn + 'a),
}
-crate const INDENT_UNIT: usize = 4;
+crate const INDENT_UNIT: isize = 4;
/// Requires you to pass an input filename and reader so that
/// it can scan the input text for comments to copy forward.
--- /dev/null
+use std::iter::Peekable;
+use std::mem;
+use std::ops::Deref;
+
+pub struct Delimited<I: Iterator> {
+ is_first: bool,
+ iter: Peekable<I>,
+}
+
+pub trait IterDelimited: Iterator + Sized {
+ fn delimited(self) -> Delimited<Self> {
+ Delimited { is_first: true, iter: self.peekable() }
+ }
+}
+
+impl<I: Iterator> IterDelimited for I {}
+
+pub struct IteratorItem<T> {
+ value: T,
+ pub is_first: bool,
+ pub is_last: bool,
+}
+
+impl<I: Iterator> Iterator for Delimited<I> {
+ type Item = IteratorItem<I::Item>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let value = self.iter.next()?;
+ let is_first = mem::replace(&mut self.is_first, false);
+ let is_last = self.iter.peek().is_none();
+ Some(IteratorItem { value, is_first, is_last })
+ }
+}
+
+impl<T> Deref for IteratorItem<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.value
+ }
+}
-use crate::pp::Breaks::{Consistent, Inconsistent};
-use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
+use crate::pp::Breaks::Inconsistent;
+use crate::pprust::state::{AnnNode, IterDelimited, PrintState, State, INDENT_UNIT};
use rustc_ast::ptr::P;
use rustc_ast::util::parser::{self, AssocOp, Fixity};
} else {
self.print_path(path, true, 0);
}
+ self.nbsp();
self.word("{");
- self.commasep_cmnt(
- Consistent,
- fields,
- |s, field| {
- s.print_outer_attributes(&field.attrs);
- s.ibox(INDENT_UNIT);
- if !field.is_shorthand {
- s.print_ident(field.ident);
- s.word_space(":");
- }
- s.print_expr(&field.expr);
- s.end();
- },
- |f| f.span,
- );
- match rest {
- ast::StructRest::Base(_) | ast::StructRest::Rest(_) => {
- self.ibox(INDENT_UNIT);
- if !fields.is_empty() {
- self.word(",");
- self.space();
- }
- self.word("..");
- if let ast::StructRest::Base(ref expr) = *rest {
- self.print_expr(expr);
- }
- self.end();
+ let has_rest = match rest {
+ ast::StructRest::Base(_) | ast::StructRest::Rest(_) => true,
+ ast::StructRest::None => false,
+ };
+ if fields.is_empty() && !has_rest {
+ self.word("}");
+ return;
+ }
+ self.cbox(0);
+ for field in fields.iter().delimited() {
+ self.maybe_print_comment(field.span.hi());
+ self.print_outer_attributes(&field.attrs);
+ if field.is_first {
+ self.space_if_not_bol();
+ }
+ if !field.is_shorthand {
+ self.print_ident(field.ident);
+ self.word_nbsp(":");
+ }
+ self.print_expr(&field.expr);
+ if !field.is_last || has_rest {
+ self.word_space(",");
+ } else {
+ self.trailing_comma_or_space();
}
- ast::StructRest::None if !fields.is_empty() => self.word(","),
- _ => {}
}
+ if has_rest {
+ if fields.is_empty() {
+ self.space();
+ }
+ self.word("..");
+ if let ast::StructRest::Base(expr) = rest {
+ self.print_expr(expr);
+ }
+ self.space();
+ }
+ self.offset(-INDENT_UNIT);
+ self.end();
self.word("}");
}
use crate::pp::Breaks::Inconsistent;
-use crate::pprust::state::{AnnNode, PrintState, State};
+use crate::pprust::state::delimited::IterDelimited;
+use crate::pprust::state::{AnnNode, PrintState, State, INDENT_UNIT};
use rustc_ast as ast;
use rustc_ast::GenericBound;
self.end(); // end outer head-block
}
ast::ItemKind::Use(ref tree) => {
- self.head(visibility_qualified(&item.vis, "use"));
+ self.print_visibility(&item.vis);
+ self.word_nbsp("use");
self.print_use_tree(tree);
self.word(";");
- self.end(); // end inner head-block
- self.end(); // end outer head-block
}
ast::ItemKind::Static(ref ty, mutbl, ref body) => {
let def = ast::Defaultness::Final;
ast::UseTreeKind::Simple(rename, ..) => {
self.print_path(&tree.prefix, false, 0);
if let Some(rename) = rename {
- self.space();
- self.word_space("as");
+ self.nbsp();
+ self.word_nbsp("as");
self.print_ident(rename);
}
}
self.word("*");
}
ast::UseTreeKind::Nested(ref items) => {
- if tree.prefix.segments.is_empty() {
- self.word("{");
- } else {
+ if !tree.prefix.segments.is_empty() {
self.print_path(&tree.prefix, false, 0);
- self.word("::{");
+ self.word("::");
+ }
+ if items.is_empty() {
+ self.word("{}");
+ } else if items.len() == 1 {
+ self.print_use_tree(&items[0].0);
+ } else {
+ self.cbox(INDENT_UNIT);
+ self.word("{");
+ self.zerobreak();
+ self.ibox(0);
+ for use_tree in items.iter().delimited() {
+ self.print_use_tree(&use_tree.0);
+ if !use_tree.is_last {
+ self.word(",");
+ if let ast::UseTreeKind::Nested(_) = use_tree.0.kind {
+ self.hardbreak();
+ } else {
+ self.space();
+ }
+ }
+ }
+ self.end();
+ self.trailing_comma();
+ self.offset(-INDENT_UNIT);
+ self.word("}");
+ self.end();
}
- self.commasep(Inconsistent, &items, |this, &(ref tree, _)| {
- this.print_use_tree(tree)
- });
- self.word("}");
}
}
}
}
}
-#[derive(Debug, Encodable, Decodable, Clone, HashStable_Generic)]
+#[derive(Copy, Debug, Encodable, Decodable, Clone, HashStable_Generic)]
pub struct Deprecation {
pub since: Option<Symbol>,
/// The note to issue a reason.
verb: &str,
optional_adverb_for_moved: &str,
moved_path: Option<String>,
- ) -> DiagnosticBuilder<'cx> {
+ ) -> DiagnosticBuilder<'tcx> {
let moved_path = moved_path.map(|mp| format!(": `{}`", mp)).unwrap_or_default();
struct_span_err!(
/// We sometimes have `region` within an rvalue, or within a
/// call. Make them live at the location where they appear.
- fn visit_region(&mut self, region: &ty::Region<'tcx>, location: Location) {
- self.add_regular_live_constraint(*region, location);
+ fn visit_region(&mut self, region: ty::Region<'tcx>, location: Location) {
+ self.add_regular_live_constraint(region, location);
self.super_region(region);
}
found,
TypeError::RegionsPlaceholderMismatch,
);
- err.buffer(&mut mbcx.errors_buffer);
+ mbcx.buffer_error(err);
}
UniverseInfoInner::TypeOp(ref type_op_info) => {
type_op_info.report_error(mbcx, placeholder, error_element, cause);
// FIXME: This error message isn't great, but it doesn't show
// up in the existing UI tests. Consider investigating this
// some more.
- mbcx.infcx
- .tcx
- .sess
- .struct_span_err(cause.span, "higher-ranked subtype error")
- .buffer(&mut mbcx.errors_buffer);
+ mbcx.buffer_error(
+ mbcx.infcx.tcx.sess.struct_span_err(cause.span, "higher-ranked subtype error"),
+ );
}
}
}
let tcx = mbcx.infcx.tcx;
let base_universe = self.base_universe();
- let adjusted_universe = if let Some(adjusted) =
+ let Some(adjusted_universe) =
placeholder.universe.as_u32().checked_sub(base_universe.as_u32())
- {
- adjusted
- } else {
- self.fallback_error(tcx, cause.span).buffer(&mut mbcx.errors_buffer);
+ else {
+ mbcx.buffer_error(self.fallback_error(tcx, cause.span));
return;
};
let nice_error = self.nice_error(tcx, cause, placeholder_region, error_region);
if let Some(nice_error) = nice_error {
- nice_error.buffer(&mut mbcx.errors_buffer);
+ mbcx.buffer_error(nice_error);
} else {
- self.fallback_error(tcx, span).buffer(&mut mbcx.errors_buffer);
+ mbcx.buffer_error(self.fallback_error(tcx, span));
}
}
}
})?;
debug!(?sub_region, "cause = {:#?}", cause);
- let nice_error = match (error_region, sub_region) {
- (Some(error_region), &ty::ReVar(vid)) => NiceRegionError::new(
+ let nice_error = match (error_region, *sub_region) {
+ (Some(error_region), ty::ReVar(vid)) => NiceRegionError::new(
infcx,
RegionResolutionError::SubSupConflict(
vid,
RegionResolutionError::ConcreteFailure(cause.clone(), error_region, placeholder_region),
),
// Note universe here is wrong...
- (None, &ty::ReVar(vid)) => NiceRegionError::new(
+ (None, ty::ReVar(vid)) => NiceRegionError::new(
infcx,
RegionResolutionError::UpperBoundUniverseConflict(
vid,
use either::Either;
+use rustc_const_eval::util::{CallDesugaringKind, CallKind};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use super::{
explain_borrow::{BorrowExplanation, LaterUseKind},
- FnSelfUseKind, IncludingDowncast, RegionName, RegionNameSource, UseSpans,
+ IncludingDowncast, RegionName, RegionNameSource, UseSpans,
};
#[derive(Debug)]
format!("{} occurs due to use{}", desired_action.as_noun(), use_spans.describe()),
);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
} else {
- if let Some((reported_place, _)) = self.move_error_reported.get(&move_out_indices) {
+ if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {
if self.prefixes(*reported_place, PrefixSet::All).any(|p| p == used_place) {
debug!(
"report_use_of_moved_or_uninitialized place: error suppressed \
.map(|n| format!("`{}`", n))
.unwrap_or_else(|| "value".to_owned());
match kind {
- FnSelfUseKind::FnOnceCall => {
+ CallKind::FnCall { fn_trait_id, .. }
+ if Some(fn_trait_id) == self.infcx.tcx.lang_items().fn_once_trait() =>
+ {
err.span_label(
fn_call_span,
&format!(
"this value implements `FnOnce`, which causes it to be moved when called",
);
}
- FnSelfUseKind::Operator { self_arg } => {
+ CallKind::Operator { self_arg, .. } => {
+ let self_arg = self_arg.unwrap();
err.span_label(
fn_call_span,
&format!(
);
}
}
- FnSelfUseKind::Normal {
- self_arg,
- implicit_into_iter,
- is_option_or_result,
- } => {
- if implicit_into_iter {
+ CallKind::Normal { self_arg, desugaring, is_option_or_result } => {
+ let self_arg = self_arg.unwrap();
+ if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
err.span_label(
fn_call_span,
&format!(
);
}
}
- // Deref::deref takes &self, which cannot cause a move
- FnSelfUseKind::DerefCoercion { .. } => unreachable!(),
+ // Other desugarings takes &self, which cannot cause a move
+ _ => unreachable!(),
}
} else {
err.span_label(
}
if let UseSpans::FnSelfUse {
- kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty },
+ kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
..
} = use_spans
{
}
}
- if let Some((_, mut old_err)) =
- self.move_error_reported.insert(move_out_indices, (used_place, err))
- {
- // Cancel the old error so it doesn't ICE.
- old_err.cancel();
- }
+ self.buffer_move_error(move_out_indices, (used_place, err));
}
}
Some(borrow_span),
None,
);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
pub(crate) fn report_use_while_mutably_borrowed(
if self.body.local_decls[borrowed_local].is_ref_to_thread_local() {
let err =
self.report_thread_local_value_does_not_live_long_enough(drop_span, borrow_span);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
return;
}
),
};
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
fn report_local_value_does_not_live_long_enough(
None,
);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
fn report_thread_local_value_does_not_live_long_enough(
loan.kind.describe_mutability(),
);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
return;
}
self.explain_deref_coercion(loan, &mut err);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
}
}
err.span_label(span, msg);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
// This is also case 2 from above but for functions, return type is still an
// anonymous reference so we select the first argument.
let argument_span = fn_decl.inputs.first()?.span;
- let argument_ty = sig.inputs().skip_binder().first()?;
+ let argument_ty = *sig.inputs().skip_binder().first()?;
let return_span = fn_decl.output.span();
let return_ty = sig.output().skip_binder();
diag: &mut DiagnosticBuilder<'_>,
) -> String {
match self {
- AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
+ &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
diag.span_label(
- *argument_span,
+ argument_span,
format!("has type `{}`", cx.get_name_for_ty(argument_ty, 0)),
);
cx.get_region_name_for_ty(argument_ty, 0)
}
- AnnotatedBorrowFnSignature::AnonymousFunction {
+ &AnnotatedBorrowFnSignature::AnonymousFunction {
argument_ty,
argument_span,
return_ty,
return_span,
} => {
let argument_ty_name = cx.get_name_for_ty(argument_ty, 0);
- diag.span_label(*argument_span, format!("has type `{}`", argument_ty_name));
+ diag.span_label(argument_span, format!("has type `{}`", argument_ty_name));
let return_ty_name = cx.get_name_for_ty(return_ty, 0);
let types_equal = return_ty_name == argument_ty_name;
diag.span_label(
- *return_span,
+ return_span,
format!(
"{}has type `{}`",
if types_equal { "also " } else { "" },
}
AnnotatedBorrowFnSignature::NamedFunction { arguments, return_ty, return_span } => {
// Region of return type and arguments checked to be the same earlier.
- let region_name = cx.get_region_name_for_ty(return_ty, 0);
+ let region_name = cx.get_region_name_for_ty(*return_ty, 0);
for (_, argument_span) in arguments {
diag.span_label(*argument_span, format!("has lifetime `{}`", region_name));
}
//! Borrow checker diagnostics.
+use rustc_const_eval::util::call_kind;
use rustc_errors::DiagnosticBuilder;
use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
-use rustc_hir::lang_items::LangItemGroup;
use rustc_hir::GeneratorKind;
use rustc_middle::mir::{
AggregateKind, Constant, FakeReadCause, Field, Local, LocalInfo, LocalKind, Location, Operand,
use rustc_middle::ty::print::Print;
use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt};
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
-use rustc_span::{hygiene::DesugaringKind, symbol::sym, Span};
+use rustc_span::{symbol::sym, Span};
use rustc_target::abi::VariantIdx;
use super::borrow_set::BorrowData;
crate use outlives_suggestion::OutlivesSuggestionBuilder;
crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
crate use region_name::{RegionName, RegionNameSource};
-use rustc_span::symbol::Ident;
+crate use rustc_const_eval::util::CallKind;
pub(super) struct IncludingDowncast(pub(super) bool);
match place {
PlaceRef { local, projection: [] } => {
let local = &self.body.local_decls[local];
- self.describe_field_from_ty(&local.ty, field, None)
+ self.describe_field_from_ty(local.ty, field, None)
}
PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
ProjectionElem::Deref => {
}
ProjectionElem::Downcast(_, variant_index) => {
let base_ty = place.ty(self.body, self.infcx.tcx).ty;
- self.describe_field_from_ty(&base_ty, field, Some(*variant_index))
+ self.describe_field_from_ty(base_ty, field, Some(*variant_index))
}
ProjectionElem::Field(_, field_type) => {
- self.describe_field_from_ty(&field_type, field, None)
+ self.describe_field_from_ty(*field_type, field, None)
}
ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
) -> String {
if ty.is_box() {
// If the type is a box, the field is described from the boxed type
- self.describe_field_from_ty(&ty.boxed_ty(), field, variant_index)
+ self.describe_field_from_ty(ty.boxed_ty(), field, variant_index)
} else {
match *ty.kind() {
ty::Adt(def, _) => {
}
ty::Tuple(_) => field.index().to_string(),
ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => {
- self.describe_field_from_ty(&ty, field, variant_index)
+ self.describe_field_from_ty(ty, field, variant_index)
}
ty::Array(ty, _) | ty::Slice(ty) => {
- self.describe_field_from_ty(&ty, field, variant_index)
+ self.describe_field_from_ty(ty, field, variant_index)
}
ty::Closure(def_id, _) | ty::Generator(def_id, _, _) => {
// We won't be borrowck'ing here if the closure came from another crate,
// We need to add synthesized lifetimes where appropriate. We do
// this by hooking into the pretty printer and telling it to label the
// lifetimes without names with the value `'0`.
- match ty.kind() {
- ty::Ref(
- ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. })
- | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }),
- _,
- _,
- ) => printer.region_highlight_mode.highlighting_bound_region(*br, counter),
- _ => {}
+ if let ty::Ref(region, ..) = ty.kind() {
+ match **region {
+ ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
+ | ty::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
+ printer.region_highlight_mode.highlighting_bound_region(br, counter)
+ }
+ _ => {}
+ }
}
let _ = ty.print(printer);
let mut s = String::new();
let mut printer = ty::print::FmtPrinter::new(self.infcx.tcx, &mut s, Namespace::TypeNS);
- let region = match ty.kind() {
- ty::Ref(region, _, _) => {
- match region {
- ty::RegionKind::ReLateBound(_, ty::BoundRegion { kind: br, .. })
- | ty::RegionKind::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
- printer.region_highlight_mode.highlighting_bound_region(*br, counter)
- }
- _ => {}
+ let region = if let ty::Ref(region, ..) = ty.kind() {
+ match **region {
+ ty::ReLateBound(_, ty::BoundRegion { kind: br, .. })
+ | ty::RePlaceholder(ty::PlaceholderRegion { name: br, .. }) => {
+ printer.region_highlight_mode.highlighting_bound_region(br, counter)
}
-
- region
+ _ => {}
}
- _ => bug!("ty for annotation of borrow region is not a reference"),
+ region
+ } else {
+ bug!("ty for annotation of borrow region is not a reference");
};
let _ = region.print(printer);
fn_call_span: Span,
/// The definition span of the method being called
fn_span: Span,
- kind: FnSelfUseKind<'tcx>,
+ kind: CallKind<'tcx>,
},
/// This access is caused by a `match` or `if let` pattern.
PatUse(Span),
OtherUse(Span),
}
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub(super) enum FnSelfUseKind<'tcx> {
- /// A normal method call of the form `receiver.foo(a, b, c)`
- Normal {
- self_arg: Ident,
- implicit_into_iter: bool,
- /// Whether the self type of the method call has an `.as_ref()` method.
- /// Used for better diagnostics.
- is_option_or_result: bool,
- },
- /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)`
- FnOnceCall,
- /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
- Operator { self_arg: Ident },
- DerefCoercion {
- /// The `Span` of the `Target` associated type
- /// in the `Deref` impl we are using.
- deref_target: Span,
- /// The type `T::Deref` we are dereferencing to
- deref_target_ty: Ty<'tcx>,
- },
-}
-
impl UseSpans<'_> {
pub(super) fn args_or_use(self) -> Span {
match self {
UseSpans::ClosureUse { args_span: span, .. }
| UseSpans::PatUse(span)
| UseSpans::OtherUse(span) => span,
- UseSpans::FnSelfUse {
- fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
- } => fn_call_span,
+ UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
+ fn_call_span
+ }
UseSpans::FnSelfUse { var_span, .. } => var_span,
}
}
UseSpans::ClosureUse { path_span: span, .. }
| UseSpans::PatUse(span)
| UseSpans::OtherUse(span) => span,
- UseSpans::FnSelfUse {
- fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
- } => fn_call_span,
+ UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
+ fn_call_span
+ }
UseSpans::FnSelfUse { var_span, .. } => var_span,
}
}
UseSpans::ClosureUse { capture_kind_span: span, .. }
| UseSpans::PatUse(span)
| UseSpans::OtherUse(span) => span,
- UseSpans::FnSelfUse {
- fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, ..
- } => fn_call_span,
+ UseSpans::FnSelfUse { fn_call_span, kind: CallKind::DerefCoercion { .. }, .. } => {
+ fn_call_span
+ }
UseSpans::FnSelfUse { var_span, .. } => var_span,
}
}
kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
}) = &self.body[location.block].terminator
{
- let (method_did, method_substs) = if let Some(info) =
+ let Some((method_did, method_substs)) =
rustc_const_eval::util::find_self_call(
self.infcx.tcx,
&self.body,
target_temp,
location.block,
- ) {
- info
- } else {
+ )
+ else {
return normal_ret;
};
- let tcx = self.infcx.tcx;
- let parent = tcx.parent(method_did);
- let is_fn_once = parent == tcx.lang_items().fn_once_trait();
- let is_operator = !from_hir_call
- && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
- let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
- let fn_call_span = *fn_span;
-
- let self_arg = tcx.fn_arg_names(method_did)[0];
-
- debug!(
- "terminator = {:?} from_hir_call={:?}",
- self.body[location.block].terminator, from_hir_call
+ let kind = call_kind(
+ self.infcx.tcx,
+ self.param_env,
+ method_did,
+ method_substs,
+ *fn_span,
+ *from_hir_call,
+ Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
);
- // Check for a 'special' use of 'self' -
- // an FnOnce call, an operator (e.g. `<<`), or a
- // deref coercion.
- let kind = if is_fn_once {
- Some(FnSelfUseKind::FnOnceCall)
- } else if is_operator {
- Some(FnSelfUseKind::Operator { self_arg })
- } else if is_deref {
- let deref_target =
- tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
- Instance::resolve(tcx, self.param_env, deref_target, method_substs)
- .transpose()
- });
- if let Some(Ok(instance)) = deref_target {
- let deref_target_ty = instance.ty(tcx, self.param_env);
- Some(FnSelfUseKind::DerefCoercion {
- deref_target: tcx.def_span(instance.def_id()),
- deref_target_ty,
- })
- } else {
- None
- }
- } else {
- None
- };
-
- let kind = kind.unwrap_or_else(|| {
- // This isn't a 'special' use of `self`
- debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
- let implicit_into_iter = Some(method_did) == tcx.lang_items().into_iter_fn()
- && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop);
- let parent_self_ty = parent
- .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
- .and_then(|did| match tcx.type_of(did).kind() {
- ty::Adt(def, ..) => Some(def.did),
- _ => None,
- });
- let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
- matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
- });
- FnSelfUseKind::Normal { self_arg, implicit_into_iter, is_option_or_result }
- });
-
return FnSelfUse {
var_span: stmt.source_info.span,
- fn_call_span,
+ fn_call_span: *fn_span,
fn_span: self
.infcx
.tcx
+use rustc_const_eval::util::CallDesugaringKind;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::mir::*;
use rustc_span::{sym, Span, DUMMY_SP};
use rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions;
-use crate::diagnostics::{FnSelfUseKind, UseSpans};
+use crate::diagnostics::{CallKind, UseSpans};
use crate::prefixes::PrefixSet;
use crate::MirBorrowckCtxt;
);
(
match kind {
- IllegalMoveOriginKind::BorrowedContent { target_place } => self
+ &IllegalMoveOriginKind::BorrowedContent { target_place } => self
.report_cannot_move_from_borrowed_content(
original_path,
- *target_place,
+ target_place,
span,
use_spans,
),
- IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
+ &IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
self.cannot_move_out_of_interior_of_drop(span, ty)
}
- IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
- self.cannot_move_out_of_interior_noncopy(span, ty, Some(*is_index))
+ &IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
+ self.cannot_move_out_of_interior_noncopy(span, ty, Some(is_index))
}
},
span,
};
self.add_move_hints(error, &mut err, err_span);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
fn report_cannot_move_from_static(
Applicability::MaybeIncorrect,
);
} else if let Some(UseSpans::FnSelfUse {
- kind: FnSelfUseKind::Normal { implicit_into_iter: true, .. },
+ kind:
+ CallKind::Normal { desugaring: Some((CallDesugaringKind::ForLoopIntoIter, _)), .. },
..
}) = use_spans
{
}
}
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
/// User cannot make signature of a trait mutable without changing the
let hir_map = self.infcx.tcx.hir();
let my_def = self.body.source.def_id();
let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
- let td = if let Some(a) =
+ let Some(td) =
self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
- {
- a
- } else {
+ else {
return (false, None);
};
(
HirId, ImplItem, ImplItemKind, Item, ItemKind,
};
- fn maybe_body_id_of_fn(hir_map: &Map<'_>, id: HirId) -> Option<BodyId> {
+ fn maybe_body_id_of_fn(hir_map: Map<'_>, id: HirId) -> Option<BodyId> {
match hir_map.find(id) {
Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
| Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
}
let hir_map = self.infcx.tcx.hir();
let mir_body_hir_id = self.mir_hir_id();
- if let Some(fn_body_id) = maybe_body_id_of_fn(&hir_map, mir_body_hir_id) {
+ if let Some(fn_body_id) = maybe_body_id_of_fn(hir_map, mir_body_hir_id) {
if let Block(
hir::Block {
expr:
diag.sort_span = mir_span.shrink_to_hi();
// Buffer the diagnostic
- diag.buffer(&mut mbcx.errors_buffer);
+ mbcx.buffer_error(diag);
}
}
/// Returns `true` if a closure is inferred to be an `FnMut` closure.
fn is_closure_fn_mut(&self, fr: RegionVid) -> bool {
- if let Some(ty::ReFree(free_region)) = self.to_error_region(fr) {
+ if let Some(ty::ReFree(free_region)) = self.to_error_region(fr).as_deref() {
if let ty::BoundRegionKind::BrEnv = free_region.bound_region {
if let DefiningTy::Closure(_, substs) =
self.regioncx.universal_regions().defining_ty
let type_test_span = type_test.locations.span(&self.body);
if let Some(lower_bound_region) = lower_bound_region {
- self.infcx
- .construct_generic_bound_failure(
- type_test_span,
- None,
- type_test.generic_kind,
- lower_bound_region,
- )
- .buffer(&mut self.errors_buffer);
+ self.buffer_error(self.infcx.construct_generic_bound_failure(
+ type_test_span,
+ None,
+ type_test.generic_kind,
+ lower_bound_region,
+ ));
} else {
// FIXME. We should handle this case better. It
// indicates that we have e.g., some region variable
// to report it; we could probably handle it by
// iterating over the universal regions and reporting
// an error that multiple bounds are required.
- self.infcx
- .tcx
- .sess
- .struct_span_err(
- type_test_span,
- &format!("`{}` does not live long enough", type_test.generic_kind),
- )
- .buffer(&mut self.errors_buffer);
+ self.buffer_error(self.infcx.tcx.sess.struct_span_err(
+ type_test_span,
+ &format!("`{}` does not live long enough", type_test.generic_kind),
+ ));
}
}
RegionErrorKind::UnexpectedHiddenRegion { span, hidden_ty, member_region } => {
let named_ty = self.regioncx.name_regions(self.infcx.tcx, hidden_ty);
let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
- unexpected_hidden_region_diagnostic(
+ self.buffer_error(unexpected_hidden_region_diagnostic(
self.infcx.tcx,
span,
named_ty,
named_region,
- )
- .buffer(&mut self.errors_buffer);
+ ));
}
RegionErrorKind::BoundUniversalRegionError {
if let (Some(f), Some(o)) = (self.to_error_region(fr), self.to_error_region(outlived_fr)) {
let nice = NiceRegionError::new_from_span(self.infcx, cause.span, o, f);
if let Some(diag) = nice.try_report_from_nll() {
- diag.buffer(&mut self.errors_buffer);
+ self.buffer_error(diag);
return;
}
}
}
}
- diag.buffer(&mut self.errors_buffer);
+ self.buffer_error(diag);
}
/// Report a specialized error when `FnMut` closures return a reference to a captured variable.
fr_name: RegionName,
outlived_fr: RegionVid,
) {
- if let (Some(f), Some(ty::RegionKind::ReStatic)) =
- (self.to_error_region(fr), self.to_error_region(outlived_fr))
+ if let (Some(f), Some(ty::ReStatic)) =
+ (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref())
{
if let Some(&ty::Opaque(did, substs)) = self
.infcx
bound.kind().skip_binder()
{
let r = r.subst(self.infcx.tcx, substs);
- if let ty::RegionKind::ReStatic = r {
+ if r.is_static() {
found = true;
break;
} else {
let tcx = self.infcx.tcx;
debug!("give_region_a_name: error_region = {:?}", error_region);
- match error_region {
+ match *error_region {
ty::ReEarlyBound(ebr) => {
if ebr.has_name() {
let span = tcx.hir().span_if_local(ebr.def_id).unwrap_or(DUMMY_SP);
span: Span,
counter: usize,
) -> RegionNameHighlight {
- let mut highlight = RegionHighlightMode::default();
+ let mut highlight = RegionHighlightMode::new(self.infcx.tcx);
highlight.highlighting_region_vid(needle_fr, counter);
let type_name =
self.infcx.extract_inference_diagnostics_data(ty.into(), Some(highlight)).name;
}
// Otherwise, let's descend into the referent types.
- search_stack.push((referent_ty, &referent_hir_ty.ty));
+ search_stack.push((*referent_ty, &referent_hir_ty.ty));
}
// Match up something like `Foo<'1>`
(ty::Slice(elem_ty), hir::TyKind::Slice(elem_hir_ty))
| (ty::Array(elem_ty, _), hir::TyKind::Array(elem_hir_ty, _)) => {
- search_stack.push((elem_ty, elem_hir_ty));
+ search_stack.push((*elem_ty, elem_hir_ty));
}
(ty::RawPtr(mut_ty), hir::TyKind::Ptr(mut_hir_ty)) => {
return None;
}
- let mut highlight = RegionHighlightMode::default();
+ let mut highlight = RegionHighlightMode::new(tcx);
highlight.highlighting_region_vid(fr, *self.next_region_name.try_borrow().unwrap());
let type_name =
self.infcx.extract_inference_diagnostics_data(yield_ty.into(), Some(highlight)).name;
}
}
+ let mut errors = error::BorrowckErrors::new();
+
// Gather the upvars of a closure, if any.
let tables = tcx.typeck_opt_const_arg(def);
if let Some(ErrorReported) = tables.tainted_by_errors {
infcx.set_tainted_by_errors();
+ errors.set_tainted_by_errors();
}
let upvars: Vec<_> = tables
.closure_min_captures_flattened(def.did.to_def_id())
let location_table_owned = LocationTable::new(body);
let location_table = &location_table_owned;
- let mut errors_buffer = Vec::new();
let (move_data, move_errors): (MoveData<'tcx>, Vec<(Place<'tcx>, MoveError<'tcx>)>) =
match MoveData::gather_moves(&body, tcx, param_env) {
Ok(move_data) => (move_data, Vec::new()),
®ioncx,
&opt_closure_req,
&opaque_type_values,
- &mut errors_buffer,
+ &mut errors,
);
// The various `flow_*` structures can be large. We drop `flow_inits` here
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
reservation_warnings: Default::default(),
- move_error_reported: BTreeMap::new(),
uninitialized_error_reported: Default::default(),
- errors_buffer,
regioncx: regioncx.clone(),
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
polonius_output: None,
+ errors,
};
promoted_mbcx.report_move_errors(move_errors);
- errors_buffer = promoted_mbcx.errors_buffer;
+ errors = promoted_mbcx.errors;
};
}
access_place_error_reported: Default::default(),
reservation_error_reported: Default::default(),
reservation_warnings: Default::default(),
- move_error_reported: BTreeMap::new(),
uninitialized_error_reported: Default::default(),
- errors_buffer,
regioncx: Rc::clone(®ioncx),
used_mut: Default::default(),
used_mut_upvars: SmallVec::new(),
region_names: RefCell::default(),
next_region_name: RefCell::new(1),
polonius_output,
+ errors,
};
// Compute and report region errors, if any.
diag.message = initial_diag.styled_message().clone();
diag.span = initial_diag.span.clone();
- diag.buffer(&mut mbcx.errors_buffer);
+ mbcx.buffer_error(diag);
},
);
initial_diag.cancel();
mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
- let used_mut = mbcx.used_mut;
+ let used_mut = std::mem::take(&mut mbcx.used_mut);
for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) {
let local_decl = &mbcx.body.local_decls[local];
let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data {
})
}
- // Buffer any move errors that we collected and de-duplicated.
- for (_, (_, diag)) in mbcx.move_error_reported {
- diag.buffer(&mut mbcx.errors_buffer);
- }
-
- if !mbcx.errors_buffer.is_empty() {
- mbcx.errors_buffer.sort_by_key(|diag| diag.sort_span);
-
- for diag in mbcx.errors_buffer.drain(..) {
- mbcx.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag);
- }
- }
+ let tainted_by_errors = mbcx.emit_errors();
let result = BorrowCheckResult {
concrete_opaque_types: opaque_type_values,
closure_requirements: opt_closure_req,
used_mut_upvars: mbcx.used_mut_upvars,
+ tainted_by_errors,
};
let body_with_facts = if return_body_with_facts {
/// for the activation of the borrow.
reservation_warnings:
FxHashMap<BorrowIndex, (Place<'tcx>, Span, Location, BorrowKind, BorrowData<'tcx>)>,
- /// This field keeps track of move errors that are to be reported for given move indices.
- ///
- /// There are situations where many errors can be reported for a single move out (see #53807)
- /// and we want only the best of those errors.
- ///
- /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
- /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the
- /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once
- /// all move errors have been reported, any diagnostics in this map are added to the buffer
- /// to be emitted.
- ///
- /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
- /// when errors in the map are being re-added to the error buffer so that errors with the
- /// same primary span come out in a consistent order.
- move_error_reported: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'cx>)>,
/// This field keeps track of errors reported in the checking of uninitialized variables,
/// so that we don't report seemingly duplicate errors.
uninitialized_error_reported: FxHashSet<PlaceRef<'tcx>>,
- /// Errors to be reported buffer
- errors_buffer: Vec<Diagnostic>,
/// This field keeps track of all the local variables that are declared mut and are mutated.
/// Used for the warning issued by an unused mutable local variable.
used_mut: FxHashSet<Local>,
/// Results of Polonius analysis.
polonius_output: Option<Rc<PoloniusOutput>>,
+
+ errors: error::BorrowckErrors<'tcx>,
}
// Check that:
if conflict_error || mutability_error {
debug!("access_place: logging error place_span=`{:?}` kind=`{:?}`", place_span, kind);
-
self.access_place_error_reported.insert((place_span.0, place_span.1));
}
}
error_reported = true;
match kind {
ReadKind::Copy => {
- this.report_use_while_mutably_borrowed(location, place_span, borrow)
- .buffer(&mut this.errors_buffer);
+ let err = this
+ .report_use_while_mutably_borrowed(location, place_span, borrow);
+ this.buffer_error(err);
}
ReadKind::Borrow(bk) => {
- this.report_conflicting_borrow(location, place_span, bk, borrow)
- .buffer(&mut this.errors_buffer);
+ let err =
+ this.report_conflicting_borrow(location, place_span, bk, borrow);
+ this.buffer_error(err);
}
}
Control::Break
error_reported = true;
match kind {
WriteKind::MutableBorrow(bk) => {
- this.report_conflicting_borrow(location, place_span, bk, borrow)
- .buffer(&mut this.errors_buffer);
+ let err =
+ this.report_conflicting_borrow(location, place_span, bk, borrow);
+ this.buffer_error(err);
}
WriteKind::StorageDeadOrDrop => this
.report_borrowed_value_does_not_live_long_enough(
yield_span,
);
- err.buffer(&mut self.errors_buffer);
+ self.buffer_error(err);
}
}
| WriteKind::MutableBorrow(BorrowKind::Shared)
| WriteKind::MutableBorrow(BorrowKind::Shallow),
) => {
- if let (Err(_), true) = (
- self.is_mutable(place.as_ref(), is_local_mutation_allowed),
- self.errors_buffer.is_empty(),
- ) {
+ if self.is_mutable(place.as_ref(), is_local_mutation_allowed).is_err()
+ && !self.has_buffered_errors()
+ {
// rust-lang/rust#46908: In pure NLL mode this code path should be
// unreachable, but we use `delay_span_bug` because we can hit this when
// dereferencing a non-Copy raw pointer *and* have `-Ztreat-err-as-bug`
}
}
+mod error {
+ use super::*;
+
+ pub struct BorrowckErrors<'tcx> {
+ /// This field keeps track of move errors that are to be reported for given move indices.
+ ///
+ /// There are situations where many errors can be reported for a single move out (see #53807)
+ /// and we want only the best of those errors.
+ ///
+ /// The `report_use_of_moved_or_uninitialized` function checks this map and replaces the
+ /// diagnostic (if there is one) if the `Place` of the error being reported is a prefix of the
+ /// `Place` of the previous most diagnostic. This happens instead of buffering the error. Once
+ /// all move errors have been reported, any diagnostics in this map are added to the buffer
+ /// to be emitted.
+ ///
+ /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
+ /// when errors in the map are being re-added to the error buffer so that errors with the
+ /// same primary span come out in a consistent order.
+ buffered_move_errors:
+ BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)>,
+ /// Errors to be reported buffer
+ buffered: Vec<Diagnostic>,
+ /// Set to Some if we emit an error during borrowck
+ tainted_by_errors: Option<ErrorReported>,
+ }
+
+ impl BorrowckErrors<'_> {
+ pub fn new() -> Self {
+ BorrowckErrors {
+ buffered_move_errors: BTreeMap::new(),
+ buffered: Default::default(),
+ tainted_by_errors: None,
+ }
+ }
+
+ pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
+ self.tainted_by_errors = Some(ErrorReported {});
+ t.buffer(&mut self.buffered);
+ }
+
+ pub fn set_tainted_by_errors(&mut self) {
+ self.tainted_by_errors = Some(ErrorReported {});
+ }
+ }
+
+ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
+ pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
+ self.errors.buffer_error(t);
+ }
+
+ pub fn buffer_move_error(
+ &mut self,
+ move_out_indices: Vec<MoveOutIndex>,
+ place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>),
+ ) -> bool {
+ if let Some((_, mut diag)) =
+ self.errors.buffered_move_errors.insert(move_out_indices, place_and_err)
+ {
+ // Cancel the old diagnostic so we don't ICE
+ diag.cancel();
+ false
+ } else {
+ true
+ }
+ }
+
+ pub fn emit_errors(&mut self) -> Option<ErrorReported> {
+ // Buffer any move errors that we collected and de-duplicated.
+ for (_, (_, diag)) in std::mem::take(&mut self.errors.buffered_move_errors) {
+ // We have already set tainted for this error, so just buffer it.
+ diag.buffer(&mut self.errors.buffered);
+ }
+
+ if !self.errors.buffered.is_empty() {
+ self.errors.buffered.sort_by_key(|diag| diag.sort_span);
+
+ for diag in self.errors.buffered.drain(..) {
+ self.infcx.tcx.sess.diagnostic().emit_diagnostic(&diag);
+ }
+ }
+
+ self.errors.tainted_by_errors
+ }
+
+ pub fn has_buffered_errors(&self) -> bool {
+ self.errors.buffered.is_empty()
+ }
+
+ pub fn has_move_error(
+ &self,
+ move_out_indices: &[MoveOutIndex],
+ ) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx>)> {
+ self.errors.buffered_move_errors.get(move_out_indices)
+ }
+ }
+}
+
/// The degree of overlap between 2 places for borrow-checking.
enum Overlap {
/// The places might partially overlap - in this case, we give
//! The entry point of the NLL borrow checker.
use rustc_data_structures::vec_map::VecMap;
-use rustc_errors::Diagnostic;
use rustc_index::vec::IndexVec;
use rustc_infer::infer::InferCtxt;
use rustc_middle::mir::{create_dump_file, dump_enabled, dump_mir, PassWhere};
BasicBlock, Body, ClosureOutlivesSubject, ClosureRegionRequirements, LocalKind, Location,
Promoted,
};
-use rustc_middle::ty::{self, OpaqueTypeKey, RegionKind, RegionVid, Ty};
+use rustc_middle::ty::{self, OpaqueTypeKey, Region, RegionVid, Ty};
use rustc_span::symbol::sym;
use std::env;
use std::fmt::Debug;
regioncx: &RegionInferenceContext<'tcx>,
closure_region_requirements: &Option<ClosureRegionRequirements<'_>>,
opaque_type_values: &VecMap<OpaqueTypeKey<'tcx>, Ty<'tcx>>,
- errors_buffer: &mut Vec<Diagnostic>,
+ errors: &mut crate::error::BorrowckErrors<'tcx>,
) {
let tcx = infcx.tcx;
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
err.note(&format!("Inferred opaque type values:\n{:#?}", opaque_type_values));
}
- err.buffer(errors_buffer);
+ errors.buffer_error(err);
}
fn for_each_region_constraint(
fn to_region_vid(self) -> RegionVid;
}
-impl<'tcx> ToRegionVid for &'tcx RegionKind {
+impl<'tcx> ToRegionVid for Region<'tcx> {
fn to_region_vid(self) -> RegionVid {
- if let ty::ReVar(vid) = self { *vid } else { bug!("region is not an ReVar: {:?}", self) }
+ if let ty::ReVar(vid) = *self { vid } else { bug!("region is not an ReVar: {:?}", self) }
}
}
match verify_bound {
VerifyBound::IfEq(test_ty, verify_bound1) => {
- self.eval_if_eq(tcx, body, generic_ty, lower_bound, test_ty, verify_bound1)
+ self.eval_if_eq(tcx, body, generic_ty, lower_bound, *test_ty, verify_bound1)
}
VerifyBound::IsEmpty => {
}
VerifyBound::OutlivedBy(r) => {
- let r_vid = self.to_region_vid(r);
+ let r_vid = self.to_region_vid(*r);
self.eval_outlives(r_vid, lower_bound)
}
for vid in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) {
match self.definitions[vid].external_name {
None => {}
- Some(&ty::ReStatic) => {}
+ Some(region) if region.is_static() => {}
Some(region) => return region,
}
}
for (i, arg) in opaque_type_key.substs.iter().enumerate() {
let arg_is_param = match arg.unpack() {
GenericArgKind::Type(ty) => matches!(ty.kind(), ty::Param(_)),
- GenericArgKind::Lifetime(ty::ReStatic) => {
+ GenericArgKind::Lifetime(lt) if lt.is_static() => {
tcx.sess
.struct_span_err(span, "non-defining opaque type use in defining scope")
.span_label(
return false;
}
GenericArgKind::Lifetime(lt) => {
- matches!(lt, ty::ReEarlyBound(_) | ty::ReFree(_))
+ matches!(*lt, ty::ReEarlyBound(_) | ty::ReFree(_))
}
- GenericArgKind::Const(ct) => matches!(ct.val, ty::ConstKind::Param(_)),
+ GenericArgKind::Const(ct) => matches!(ct.val(), ty::ConstKind::Param(_)),
};
if arg_is_param {
#[instrument(skip(self), level = "debug")]
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
- *ty = self.renumber_regions(ty);
+ *ty = self.renumber_regions(*ty);
debug!(?ty);
}
#[instrument(skip(self), level = "debug")]
fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
let old_region = *region;
- *region = self.renumber_regions(&old_region);
+ *region = self.renumber_regions(old_region);
debug!(?region);
}
- fn visit_const(&mut self, constant: &mut &'tcx ty::Const<'tcx>, _location: Location) {
- *constant = self.renumber_regions(&*constant);
+ fn visit_const(&mut self, constant: &mut ty::Const<'tcx>, _location: Location) {
+ *constant = self.renumber_regions(*constant);
}
}
// create new region variables, which can't be done later when
// verifying these bounds.
if t1.has_placeholders() {
- t1 = tcx.fold_regions(&t1, &mut false, |r, _| match *r {
- ty::RegionKind::RePlaceholder(placeholder) => {
+ t1 = tcx.fold_regions(t1, &mut false, |r, _| match *r {
+ ty::RePlaceholder(placeholder) => {
self.constraints.placeholder_region(self.infcx, placeholder)
}
_ => r,
}
fn to_region_vid(&mut self, r: ty::Region<'tcx>) -> ty::RegionVid {
- if let ty::RePlaceholder(placeholder) = r {
- self.constraints.placeholder_region(self.infcx, *placeholder).to_region_vid()
+ if let ty::RePlaceholder(placeholder) = *r {
+ self.constraints.placeholder_region(self.infcx, placeholder).to_region_vid()
} else {
self.universal_regions.to_region_vid(r)
}
// `where Type:` is lowered to `where Type: 'empty` so that
// we check `Type` is well formed, but there's no use for
// this bound here.
- if let ty::ReEmpty(_) = r1 {
+ if r1.is_empty() {
return;
}
} else {
let tcx = self.tcx();
let maybe_uneval = match constant.literal {
- ConstantKind::Ty(ct) => match ct.val {
+ ConstantKind::Ty(ct) => match ct.val() {
ty::ConstKind::Unevaluated(uv) => Some(uv),
_ => None,
},
// then remove the outermost reference so we can check the
// type annotation for the remaining type.
if let ty::Ref(_, rty, _) = local_decl.ty.kind() {
- rty
+ *rty
} else {
bug!("{:?} with ref binding has wrong type {}", local, local_decl.ty);
}
PlaceTy::from_ty(match base_ty.kind() {
ty::Array(inner, _) => {
assert!(!from_end, "array subslices should not use from_end");
- tcx.mk_array(inner, to - from)
+ tcx.mk_array(*inner, to - from)
}
ty::Slice(..) => {
assert!(from_end, "slice subslices should use from_end");
ConstraintCategory::Boring
};
if let Err(terr) =
- self.sub_types(op_arg_ty, fn_arg, term_location.to_locations(), category)
+ self.sub_types(op_arg_ty, *fn_arg, term_location.to_locations(), category)
{
span_mirbug!(
self,
fn check_operand(&mut self, op: &Operand<'tcx>, location: Location) {
if let Operand::Constant(constant) = op {
let maybe_uneval = match constant.literal {
- ConstantKind::Ty(ct) => match ct.val {
+ ConstantKind::Ty(ct) => match ct.val() {
ty::ConstKind::Unevaluated(uv) => Some(uv),
_ => None,
},
}
}
- Rvalue::NullaryOp(_, ty) => {
+ &Rvalue::NullaryOp(_, ty) => {
let trait_ref = ty::TraitRef {
def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
substs: tcx.mk_substs_trait(ty, &[]),
let trait_ref = ty::TraitRef {
def_id: tcx.require_lang_item(LangItem::Sized, Some(self.last_span)),
- substs: tcx.mk_substs_trait(ty, &[]),
+ substs: tcx.mk_substs_trait(*ty, &[]),
};
self.prove_trait_ref(
let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig);
if let Err(terr) = self.eq_types(
- ty,
+ *ty,
ty_fn_ptr_from,
location.to_locations(),
ConstraintCategory::Cast,
let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety));
if let Err(terr) = self.eq_types(
- ty,
+ *ty,
ty_fn_ptr_from,
location.to_locations(),
ConstraintCategory::Cast,
let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig);
if let Err(terr) = self.eq_types(
- ty,
+ *ty,
ty_fn_ptr_from,
location.to_locations(),
ConstraintCategory::Cast,
}
};
if let Err(terr) = self.sub_types(
- ty_from,
- ty_to,
+ *ty_from,
+ *ty_to,
location.to_locations(),
ConstraintCategory::Cast,
) {
}
if let Err(terr) = self.sub_types(
- ty_elem,
- ty_to,
+ *ty_elem,
+ *ty_to,
location.to_locations(),
ConstraintCategory::Cast,
) {
CastKind::Misc => {
let ty_from = op.ty(body, tcx);
let cast_ty_from = CastTy::from_ty(ty_from);
- let cast_ty_to = CastTy::from_ty(ty);
+ let cast_ty_to = CastTy::from_ty(*ty);
match (cast_ty_from, cast_ty_to) {
(None, _)
| (_, None | Some(CastTy::FnPtr))
}
Rvalue::Ref(region, _borrow_kind, borrowed_place) => {
- self.add_reborrow_constraint(&body, location, region, borrowed_place);
+ self.add_reborrow_constraint(&body, location, *region, borrowed_place);
}
Rvalue::BinaryOp(
// We don't have to worry about the equality of consts during borrow checking
// as consts always have a static lifetime.
- fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {}
+ fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) {}
fn normalization() -> NormalizationStrategy {
NormalizationStrategy::Eager
/// See `UniversalRegionIndices::to_region_vid`.
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
- if let ty::ReEmpty(ty::UniverseIndex::ROOT) = r {
+ if let ty::ReEmpty(ty::UniverseIndex::ROOT) = *r {
self.root_empty
} else {
self.indices.to_region_vid(r)
first_local_index,
num_universals,
defining_ty,
- unnormalized_output_ty,
+ unnormalized_output_ty: *unnormalized_output_ty,
unnormalized_input_tys,
yield_ty,
}
/// during initialization. Relies on the `indices` map having been
/// fully initialized.
pub fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
- if let ty::ReVar(..) = r {
+ if let ty::ReVar(..) = *r {
r.to_region_vid()
} else {
*self
Some(idx)
}
}
- parse::ArgumentNamed(name) => match args.named_args.get(&name) {
+ parse::ArgumentNamed(name, span) => match args.named_args.get(&name) {
Some(&idx) => Some(idx),
None => {
let msg = format!("there is no argument named `{}`", name);
- ecx.struct_span_err(span, &msg).emit();
+ ecx.struct_span_err(template_span.from_inner(span), &msg).emit();
None
}
},
-use crate::panic::use_panic_2021;
+use crate::edition_panic::use_panic_2021;
use rustc_ast::ptr::P;
use rustc_ast::token;
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
/// Emits errors for literal expressions that are invalid inside and outside of an array.
fn invalid_type_err(cx: &mut base::ExtCtxt<'_>, expr: &P<rustc_ast::Expr>, is_nested: bool) {
- let lit = if let ast::ExprKind::Lit(lit) = &expr.kind {
- lit
- } else {
+ let ast::ExprKind::Lit(lit) = &expr.kind else {
unreachable!();
};
match lit.kind {
"this method must only be called with a variant that has a `#[default]` attribute",
),
[first, rest @ ..] => {
- // FIXME(jhpratt) Do we want to perform this check? It doesn't exist
- // for `#[inline]`, `#[non_exhaustive]`, and presumably others.
-
let suggestion_text =
if rest.len() == 1 { "try removing this" } else { "try removing these" };
// Create the type of `self`.
//
- // in addition, remove defaults from type params (impls cannot have them).
+ // in addition, remove defaults from generic params (impls cannot have them).
let self_params: Vec<_> = generics
.params
.iter_mut()
--- /dev/null
+use rustc_ast::ptr::P;
+use rustc_ast::tokenstream::{DelimSpan, TokenStream};
+use rustc_ast::*;
+use rustc_expand::base::*;
+use rustc_span::edition::Edition;
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+// This expands to either
+// - `$crate::panic::panic_2015!(...)` or
+// - `$crate::panic::panic_2021!(...)`
+// depending on the edition.
+//
+// This is used for both std::panic!() and core::panic!().
+//
+// `$crate` will refer to either the `std` or `core` crate depending on which
+// one we're expanding from.
+pub fn expand_panic<'cx>(
+ cx: &'cx mut ExtCtxt<'_>,
+ sp: Span,
+ tts: TokenStream,
+) -> Box<dyn MacResult + 'cx> {
+ let mac = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
+ expand(mac, cx, sp, tts)
+}
+
+// This expands to either
+// - `$crate::panic::unreachable_2015!(...)` or
+// - `$crate::panic::unreachable_2021!(...)`
+// depending on the edition.
+pub fn expand_unreachable<'cx>(
+ cx: &'cx mut ExtCtxt<'_>,
+ sp: Span,
+ tts: TokenStream,
+) -> Box<dyn MacResult + 'cx> {
+ let mac = if use_panic_2021(sp) { sym::unreachable_2021 } else { sym::unreachable_2015 };
+ expand(mac, cx, sp, tts)
+}
+
+fn expand<'cx>(
+ mac: rustc_span::Symbol,
+ cx: &'cx mut ExtCtxt<'_>,
+ sp: Span,
+ tts: TokenStream,
+) -> Box<dyn MacResult + 'cx> {
+ let sp = cx.with_call_site_ctxt(sp);
+
+ MacEager::expr(
+ cx.expr(
+ sp,
+ ExprKind::MacCall(MacCall {
+ path: Path {
+ span: sp,
+ segments: cx
+ .std_path(&[sym::panic, mac])
+ .into_iter()
+ .map(|ident| PathSegment::from_ident(ident))
+ .collect(),
+ tokens: None,
+ },
+ args: P(MacArgs::Delimited(
+ DelimSpan::from_single(sp),
+ MacDelimiter::Parenthesis,
+ tts,
+ )),
+ prior_type_ascription: None,
+ }),
+ ),
+ )
+}
+
+pub fn use_panic_2021(mut span: Span) -> bool {
+ // To determine the editon, we check the first span up the expansion
+ // stack that does not have #[allow_internal_unstable(edition_panic)].
+ // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.)
+ loop {
+ let expn = span.ctxt().outer_expn_data();
+ if let Some(features) = expn.allow_internal_unstable {
+ if features.iter().any(|&f| f == sym::edition_panic) {
+ span = expn.call_site;
+ continue;
+ }
+ }
+ break expn.edition >= Edition::Edition2021;
+ }
+}
use rustc_expand::base::{self, *};
use rustc_parse_format as parse;
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{MultiSpan, Span};
+use rustc_span::{InnerSpan, MultiSpan, Span};
use smallvec::SmallVec;
use std::borrow::Cow;
enum Position {
Exact(usize),
- Named(Symbol),
+ Capture(usize),
+ Named(Symbol, InnerSpan),
}
struct Context<'a, 'b> {
/// * `arg_unique_types` (in simplified JSON): `[["o", "x"], ["o", "x"], ["o", "x"]]`
/// * `names` (in JSON): `{"foo": 2}`
args: Vec<P<ast::Expr>>,
+ /// The number of arguments that were added by implicit capturing.
+ num_captured_args: usize,
/// Placeholder slot numbers indexed by argument.
arg_types: Vec<Vec<usize>>,
/// Unique format specs seen for each argument.
}
impl<'a, 'b> Context<'a, 'b> {
+ /// The number of arguments that were explicitly given.
+ fn num_args(&self) -> usize {
+ self.args.len() - self.num_captured_args
+ }
+
fn resolve_name_inplace(&self, p: &mut parse::Piece<'_>) {
// NOTE: the `unwrap_or` branch is needed in case of invalid format
// arguments, e.g., `format_args!("{foo}")`.
match *p {
parse::String(_) => {}
parse::NextArgument(ref mut arg) => {
- if let parse::ArgumentNamed(s) = arg.position {
+ if let parse::ArgumentNamed(s, _) = arg.position {
arg.position = parse::ArgumentIs(lookup(s));
}
- if let parse::CountIsName(s) = arg.format.width {
+ if let parse::CountIsName(s, _) = arg.format.width {
arg.format.width = parse::CountIsParam(lookup(s));
}
- if let parse::CountIsName(s) = arg.format.precision {
+ if let parse::CountIsName(s, _) = arg.format.precision {
arg.format.precision = parse::CountIsParam(lookup(s));
}
}
// it's written second, so it should come after width/precision.
let pos = match arg.position {
parse::ArgumentIs(i) | parse::ArgumentImplicitlyIs(i) => Exact(i),
- parse::ArgumentNamed(s) => Named(s),
+ parse::ArgumentNamed(s, span) => Named(s, span),
};
let ty = Placeholder(match arg.format.ty {
parse::CountIsParam(i) => {
self.verify_arg_type(Exact(i), Count);
}
- parse::CountIsName(s) => {
- self.verify_arg_type(Named(s), Count);
+ parse::CountIsName(s, span) => {
+ self.verify_arg_type(Named(s, span), Count);
}
}
}
fn describe_num_args(&self) -> Cow<'_, str> {
- match self.args.len() {
+ match self.num_args() {
0 => "no arguments were given".into(),
1 => "there is 1 argument".into(),
x => format!("there are {} arguments", x).into(),
let count = self.pieces.len()
+ self.arg_with_formatting.iter().filter(|fmt| fmt.precision_span.is_some()).count();
- if self.names.is_empty() && !numbered_position_args && count != self.args.len() {
+ if self.names.is_empty() && !numbered_position_args && count != self.num_args() {
e = self.ecx.struct_span_err(
sp,
&format!(
if let Some(span) = fmt.precision_span {
let span = self.fmtsp.from_inner(span);
match fmt.precision {
- parse::CountIsParam(pos) if pos > self.args.len() => {
+ parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label(
span,
&format!(
if let Some(span) = fmt.width_span {
let span = self.fmtsp.from_inner(span);
match fmt.width {
- parse::CountIsParam(pos) if pos > self.args.len() => {
+ parse::CountIsParam(pos) if pos > self.num_args() => {
e.span_label(
span,
&format!(
/// Actually verifies and tracks a given format placeholder
/// (a.k.a. argument).
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
+ if let Exact(arg) = arg {
+ if arg >= self.num_args() {
+ self.invalid_refs.push((arg, self.curpiece));
+ return;
+ }
+ }
+
match arg {
- Exact(arg) => {
- if self.args.len() <= arg {
- self.invalid_refs.push((arg, self.curpiece));
- return;
- }
+ Exact(arg) | Capture(arg) => {
match ty {
Placeholder(_) => {
// record every (position, type) combination only once
}
}
- Named(name) => {
+ Named(name, span) => {
match self.names.get(&name) {
Some(&idx) => {
// Treat as positional arg.
- self.verify_arg_type(Exact(idx), ty)
+ self.verify_arg_type(Capture(idx), ty)
}
None => {
// For the moment capturing variables from format strings expanded from macros is
self.arg_types.push(Vec::new());
self.arg_unique_types.push(Vec::new());
let span = if self.is_literal {
- *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp)
+ self.fmtsp.from_inner(span)
} else {
self.fmtsp
};
+ self.num_captured_args += 1;
self.args.push(self.ecx.expr_ident(span, Ident::new(name, span)));
self.names.insert(name, idx);
- self.verify_arg_type(Exact(idx), ty)
+ self.verify_arg_type(Capture(idx), ty)
} else {
let msg = format!("there is no argument named `{}`", name);
let sp = if self.is_literal {
- *self.arg_spans.get(self.curpiece).unwrap_or(&self.fmtsp)
+ self.fmtsp.from_inner(span)
} else {
self.fmtsp
};
}
parse::CountImplied => count(sym::Implied, None),
// should never be the case, names are already resolved
- parse::CountIsName(_) => panic!("should never happen"),
+ parse::CountIsName(..) => panic!("should never happen"),
}
}
// should never be the case, because names are already
// resolved.
- parse::ArgumentNamed(_) => panic!("should never happen"),
+ parse::ArgumentNamed(..) => panic!("should never happen"),
}
};
let mut cx = Context {
ecx,
args,
+ num_captured_args: 0,
arg_types,
arg_unique_types,
names,
// Allow using `#[global_allocator]` on an item statement
// FIXME - if we get deref patterns, use them to reduce duplication here
- let (item, is_stmt) = match &item {
+ let (item, is_stmt, ty_span) = match &item {
Annotatable::Item(item) => match item.kind {
- ItemKind::Static(..) => (item, false),
+ ItemKind::Static(ref ty, ..) => (item, false, ecx.with_def_site_ctxt(ty.span)),
_ => return not_static(),
},
Annotatable::Stmt(stmt) => match &stmt.kind {
StmtKind::Item(item_) => match item_.kind {
- ItemKind::Static(..) => (item_, true),
+ ItemKind::Static(ref ty, ..) => (item_, true, ecx.with_def_site_ctxt(ty.span)),
_ => return not_static(),
},
_ => return not_static(),
// Generate a bunch of new items using the AllocFnFactory
let span = ecx.with_def_site_ctxt(item.span);
- let f = AllocFnFactory { span, kind: AllocatorKind::Global, global: item.ident, cx: ecx };
+ let f =
+ AllocFnFactory { span, ty_span, kind: AllocatorKind::Global, global: item.ident, cx: ecx };
// Generate item statements for the allocator methods.
let stmts = ALLOCATOR_METHODS.iter().map(|method| f.allocator_fn(method)).collect();
// Generate anonymous constant serving as container for the allocator methods.
- let const_ty = ecx.ty(span, TyKind::Tup(Vec::new()));
+ let const_ty = ecx.ty(ty_span, TyKind::Tup(Vec::new()));
let const_body = ecx.expr_block(ecx.block(span, stmts));
let const_item = ecx.item_const(span, Ident::new(kw::Underscore, span), const_ty, const_body);
let const_item = if is_stmt {
struct AllocFnFactory<'a, 'b> {
span: Span,
+ ty_span: Span,
kind: AllocatorKind,
global: Ident,
cx: &'b ExtCtxt<'a>,
self.attrs(),
kind,
);
- self.cx.stmt_item(self.span, item)
+ self.cx.stmt_item(self.ty_span, item)
}
fn call_allocator(&self, method: Symbol, mut args: Vec<P<Expr>>) -> P<Expr> {
let method = self.cx.std_path(&[sym::alloc, sym::GlobalAlloc, method]);
- let method = self.cx.expr_path(self.cx.path(self.span, method));
- let allocator = self.cx.path_ident(self.span, self.global);
+ let method = self.cx.expr_path(self.cx.path(self.ty_span, method));
+ let allocator = self.cx.path_ident(self.ty_span, self.global);
let allocator = self.cx.expr_path(allocator);
- let allocator = self.cx.expr_addr_of(self.span, allocator);
+ let allocator = self.cx.expr_addr_of(self.ty_span, allocator);
args.insert(0, allocator);
- self.cx.expr_call(self.span, method, args)
+ self.cx.expr_call(self.ty_span, method, args)
}
fn attrs(&self) -> Vec<Attribute> {
#![feature(decl_macro)]
#![feature(is_sorted)]
#![feature(nll)]
+#![feature(let_else)]
#![feature(proc_macro_internals)]
#![feature(proc_macro_quote)]
#![recursion_limit = "256"]
mod concat_idents;
mod derive;
mod deriving;
+mod edition_panic;
mod env;
mod format;
mod format_foreign;
mod global_allocator;
mod log_syntax;
-mod panic;
mod source_util;
mod test;
mod trace_macros;
log_syntax: log_syntax::expand_log_syntax,
module_path: source_util::expand_mod,
option_env: env::expand_option_env,
- core_panic: panic::expand_panic,
- std_panic: panic::expand_panic,
+ core_panic: edition_panic::expand_panic,
+ std_panic: edition_panic::expand_panic,
+ unreachable: edition_panic::expand_unreachable,
stringify: source_util::expand_stringify,
trace_macros: trace_macros::expand_trace_macros,
}
+++ /dev/null
-use rustc_ast::ptr::P;
-use rustc_ast::tokenstream::{DelimSpan, TokenStream};
-use rustc_ast::*;
-use rustc_expand::base::*;
-use rustc_span::edition::Edition;
-use rustc_span::symbol::sym;
-use rustc_span::Span;
-
-// This expands to either
-// - `$crate::panic::panic_2015!(...)` or
-// - `$crate::panic::panic_2021!(...)`
-// depending on the edition.
-//
-// This is used for both std::panic!() and core::panic!().
-//
-// `$crate` will refer to either the `std` or `core` crate depending on which
-// one we're expanding from.
-pub fn expand_panic<'cx>(
- cx: &'cx mut ExtCtxt<'_>,
- sp: Span,
- tts: TokenStream,
-) -> Box<dyn MacResult + 'cx> {
- let panic = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
-
- let sp = cx.with_call_site_ctxt(sp);
-
- MacEager::expr(
- cx.expr(
- sp,
- ExprKind::MacCall(MacCall {
- path: Path {
- span: sp,
- segments: cx
- .std_path(&[sym::panic, panic])
- .into_iter()
- .map(|ident| PathSegment::from_ident(ident))
- .collect(),
- tokens: None,
- },
- args: P(MacArgs::Delimited(
- DelimSpan::from_single(sp),
- MacDelimiter::Parenthesis,
- tts,
- )),
- prior_type_ascription: None,
- }),
- ),
- )
-}
-
-pub fn use_panic_2021(mut span: Span) -> bool {
- // To determine the editon, we check the first span up the expansion
- // stack that does not have #[allow_internal_unstable(edition_panic)].
- // (To avoid using the edition of e.g. the assert!() or debug_assert!() definition.)
- loop {
- let expn = span.ctxt().outer_expn_data();
- if let Some(features) = expn.allow_internal_unstable {
- if features.iter().any(|&f| f == sym::edition_panic) {
- span = expn.call_site;
- continue;
- }
- }
- break expn.edition >= Edition::Edition2021;
- }
-}
is_proc_macro_crate: bool,
has_proc_macro_decls: bool,
is_test_crate: bool,
- num_crate_types: usize,
handler: &rustc_errors::Handler,
) -> ast::Crate {
let ecfg = ExpansionConfig::default("proc_macro".to_string());
return krate;
}
- if num_crate_types > 1 {
- handler.err("cannot mix `proc-macro` crate type with others");
- }
-
if is_test_crate {
return krate;
}
mut krate: ast::Crate,
resolver: &mut dyn ResolverExpand,
sess: &Session,
- alt_std_name: Option<Symbol>,
) -> ast::Crate {
let edition = sess.parse_sess.edition;
span,
ident,
vec![cx.attribute(cx.meta_word(span, sym::macro_use))],
- ast::ItemKind::ExternCrate(alt_std_name),
+ ast::ItemKind::ExternCrate(None),
),
);
}
use rustc_ast::attr;
use rustc_ast::ptr::P;
use rustc_ast_pretty::pprust;
+use rustc_errors::Applicability;
use rustc_expand::base::*;
use rustc_session::Session;
use rustc_span::symbol::{sym, Ident, Symbol};
}
};
- if let ast::ItemKind::MacCall(_) = item.kind {
- cx.sess.parse_sess.span_diagnostic.span_warn(
- item.span,
- "`#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.",
- );
+ // Note: non-associated fn items are already handled by `expand_test_or_bench`
+ if !matches!(item.kind, ast::ItemKind::Fn(_)) {
+ cx.sess
+ .parse_sess
+ .span_diagnostic
+ .struct_span_err(
+ attr_sp,
+ "the `#[test]` attribute may only be used on a non-associated function",
+ )
+ .note("the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions")
+ .span_label(item.span, format!("expected a non-associated function, found {} {}", item.kind.article(), item.kind.descr()))
+ .span_suggestion(attr_sp, "replace with conditional compilation to make the item only exist when tests are being run", String::from("#[cfg(test)]"), Applicability::MaybeIncorrect)
+ .emit();
+
return vec![Annotatable::Item(item)];
}
"ignore",
cx.expr_bool(sp, should_ignore(&cx.sess, &item)),
),
- // allow_fail: true | false
- field(
- "allow_fail",
- cx.expr_bool(sp, should_fail(&cx.sess, &item)),
- ),
// compile_fail: true | false
field("compile_fail", cx.expr_bool(sp, false)),
// no_run: true | false
sess.contains_name(&i.attrs, sym::ignore)
}
-fn should_fail(sess: &Session, i: &ast::Item) -> bool {
- sess.contains_name(&i.attrs, sym::allow_fail)
-}
-
fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
match cx.sess.find_by_name(&i.attrs, sym::should_panic) {
Some(attr) => {
(false, _) => true,
}
} else {
- sd.span_err(i.span, "only functions may be used as tests");
+ // should be unreachable because `is_test_fn_item` should catch all non-fn items
false
}
}
let arg_value = drop_place.place_ref(
fx,
fx.layout_of(fx.tcx.mk_ref(
- &ty::RegionKind::ReErased,
+ fx.tcx.lifetimes.re_erased,
TypeAndMut { ty, mutbl: crate::rustc_hir::Mutability::Mut },
)),
);
Ok(())
}
- fn update_symbols(&mut self) {}
-
fn build(mut self) {
enum BuilderKind {
Bsd(ar::Builder<File>),
let arg_uninhabited = fx
.mir
.args_iter()
- .any(|arg| fx.layout_of(fx.monomorphize(&fx.mir.local_decls[arg].ty)).abi.is_uninhabited());
+ .any(|arg| fx.layout_of(fx.monomorphize(fx.mir.local_decls[arg].ty)).abi.is_uninhabited());
if !crate::constant::check_constants(&mut fx) {
fx.bcx.append_block_params_for_function_params(fx.block_map[START_BLOCK]);
let times = fx
.monomorphize(times)
.eval(fx.tcx, ParamEnv::reveal_all())
- .val
+ .val()
.try_to_bits(fx.tcx.data_layout.pointer_size)
.unwrap();
if operand.layout().size.bytes() == 0 {
match cplace.layout().ty.kind() {
ty::Array(elem_ty, _len) => {
assert!(!from_end, "array subslices are never `from_end`");
- let elem_layout = fx.layout_of(elem_ty);
+ let elem_layout = fx.layout_of(*elem_ty);
let ptr = cplace.to_ptr();
cplace = CPlace::for_ptr(
ptr.offset_i64(fx, elem_layout.size.bytes() as i64 * (from as i64)),
- fx.layout_of(fx.tcx.mk_array(elem_ty, to - from)),
+ fx.layout_of(fx.tcx.mk_array(*elem_ty, to - from)),
);
}
ty::Slice(elem_ty) => {
assert!(from_end, "slice subslices should be `from_end`");
- let elem_layout = fx.layout_of(elem_ty);
+ let elem_layout = fx.layout_of(*elem_ty);
let (ptr, len) = cplace.to_ptr_maybe_unsized();
let len = len.unwrap();
cplace = CPlace::for_ptr_with_extra(
},
ty::FnPtr(_) => pointer_ty(tcx),
ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => {
- if has_ptr_meta(tcx, pointee_ty) {
+ if has_ptr_meta(tcx, *pointee_ty) {
return None;
} else {
pointer_ty(tcx)
(a, b)
}
ty::RawPtr(TypeAndMut { ty: pointee_ty, mutbl: _ }) | ty::Ref(_, pointee_ty, _) => {
- if has_ptr_meta(tcx, pointee_ty) {
+ if has_ptr_meta(tcx, *pointee_ty) {
(pointer_ty(tcx), pointer_ty(tcx))
} else {
return None;
ConstantKind::Ty(ct) => ct,
ConstantKind::Val(..) => continue,
};
- match const_.val {
+ match const_.val() {
ConstKind::Value(_) => {}
ConstKind::Unevaluated(unevaluated) => {
if let Err(err) =
ConstantKind::Ty(ct) => ct,
ConstantKind::Val(val, ty) => return codegen_const_value(fx, val, ty),
};
- let const_val = match const_.val {
+ let const_val = match const_.val() {
ConstKind::Value(const_val) => const_val,
ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted })
if fx.tcx.is_static(def.did) =>
assert!(substs.is_empty());
assert!(promoted.is_none());
- return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty)).to_cvalue(fx);
+ return codegen_static_ref(fx, def.did, fx.layout_of(const_.ty())).to_cvalue(fx);
}
ConstKind::Unevaluated(unevaluated) => {
match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) {
| ConstKind::Error(_) => unreachable!("{:?}", const_),
};
- codegen_const_value(fx, const_val, const_.ty)
+ codegen_const_value(fx, const_val, const_.ty())
}
pub(crate) fn codegen_const_value<'tcx>(
match operand {
Operand::Constant(const_) => match const_.literal {
ConstantKind::Ty(const_) => {
- fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val.try_to_value()
+ fx.monomorphize(const_).eval(fx.tcx, ParamEnv::reveal_all()).val().try_to_value()
}
ConstantKind::Val(val, _) => Some(val),
},
return None;
}
let const_val = mir_operand_get_const_val(fx, operand)?;
- if fx.layout_of(ty).size
+ if fx.layout_of(*ty).size
!= const_val.try_to_scalar_int()?.size()
{
return None;
}
fn dwarf_ty(&mut self, ty: Ty<'tcx>) -> UnitEntryId {
- if let Some(type_id) = self.types.get(ty) {
+ if let Some(type_id) = self.types.get(&ty) {
return *type_id;
}
// Ensure that type is inserted before recursing to avoid duplicates
self.types.insert(ty, type_id);
- let pointee = self.dwarf_ty(pointee_ty);
+ let pointee = self.dwarf_ty(*pointee_ty);
let type_entry = self.dwarf.unit.get_mut(type_id);
(&ty::Ref(_, a, _), &ty::Ref(_, b, _))
| (&ty::Ref(_, a, _), &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
- (src, unsized_info(fx, a, b, old_info))
+ (src, unsized_info(fx, *a, *b, old_info))
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
let (a, b) = (src_layout.ty.boxed_ty(), dst_layout.ty.boxed_ty());
// Can only happen for vector types
let len =
u16::try_from(len.eval_usize(fx.tcx, ParamEnv::reveal_all())).unwrap();
- let vector_ty = fx.clif_type(element).unwrap().by(len).unwrap();
+ let vector_ty = fx.clif_type(*element).unwrap().by(len).unwrap();
let data = match from.0 {
CValueInner::ByRef(ptr, None) => {
index: Value,
) -> CPlace<'tcx> {
let (elem_layout, ptr) = match self.layout().ty.kind() {
- ty::Array(elem_ty, _) => (fx.layout_of(elem_ty), self.to_ptr()),
- ty::Slice(elem_ty) => (fx.layout_of(elem_ty), self.to_ptr_maybe_unsized().0),
+ ty::Array(elem_ty, _) => (fx.layout_of(*elem_ty), self.to_ptr()),
+ ty::Slice(elem_ty) => (fx.layout_of(*elem_ty), self.to_ptr_maybe_unsized().0),
_ => bug!("place_index({:?})", self.layout().ty),
};
ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }),
ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }),
) => {
- assert_assignable(fx, a, b);
+ assert_assignable(fx, *a, *b);
}
(ty::Ref(_, a, _), ty::RawPtr(TypeAndMut { ty: b, mutbl: _ }))
| (ty::RawPtr(TypeAndMut { ty: a, mutbl: _ }), ty::Ref(_, b, _)) => {
- assert_assignable(fx, a, b);
+ assert_assignable(fx, *a, *b);
}
(ty::FnPtr(_), ty::FnPtr(_)) => {
let from_sig = fx.tcx.normalize_erasing_late_bound_regions(
Ok(())
}
- fn update_symbols(&mut self) {
- }
-
fn build(mut self) {
use std::process::Command;
// TODO(antoyo): set link section.
}
- if attrs.flags.contains(CodegenFnAttrFlags::USED) {
+ if attrs.flags.contains(CodegenFnAttrFlags::USED) || attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
self.add_used_global(global.to_rvalue());
}
}
where
F: FnMut(llvm::Attribute),
{
- for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg)
+ for_each_kind!(self, f, NoAlias, NoCapture, NonNull, ReadOnly, InReg, NoUndef)
}
}
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
}
if tcx.sess.must_emit_unwind_tables() {
- attributes::emit_uwtable(llfn, true);
+ attributes::emit_uwtable(llfn);
}
let callee = kind.fn_name(method.name);
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
}
if tcx.sess.must_emit_unwind_tables() {
- attributes::emit_uwtable(llfn, true);
+ attributes::emit_uwtable(llfn);
}
let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default };
/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
#[inline]
-pub fn emit_uwtable(val: &Value, emit: bool) {
- Attribute::UWTable.toggle_llfn(Function, val, emit);
+pub fn emit_uwtable(val: &Value) {
+ // NOTE: We should determine if we even need async unwind tables, as they
+ // take have more overhead and if we can use sync unwind tables we
+ // probably should.
+ llvm::EmitUWTableAttr(val, true);
}
/// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
// You can also find more info on why Windows always requires uwtables here:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
if cx.sess().must_emit_unwind_tables() {
- attributes::emit_uwtable(llfn, true);
+ attributes::emit_uwtable(llfn);
}
if cx.sess().opts.debugging_opts.profile_sample_use.is_some() {
// The target doesn't care; the subtarget reads our attribute.
apply_tune_cpu_attr(cx, llfn);
- let mut function_features = codegen_fn_attrs
- .target_features
+ let function_features =
+ codegen_fn_attrs.target_features.iter().map(|f| f.as_str()).collect::<Vec<&str>>();
+
+ if let Some(f) = llvm_util::check_tied_features(
+ cx.tcx.sess,
+ &function_features.iter().map(|f| (*f, true)).collect(),
+ ) {
+ let span = cx
+ .tcx
+ .get_attrs(instance.def_id())
+ .iter()
+ .find(|a| a.has_name(rustc_span::sym::target_feature))
+ .map_or_else(|| cx.tcx.def_span(instance.def_id()), |a| a.span);
+ let msg = format!(
+ "the target features {} must all be either enabled or disabled together",
+ f.join(", ")
+ );
+ let mut err = cx.tcx.sess.struct_span_err(span, &msg);
+ err.help("add the missing features in a `target_feature` attribute");
+ err.emit();
+ return;
+ }
+
+ let mut function_features = function_features
.iter()
- .flat_map(|f| {
- let feature = f.as_str();
- llvm_util::to_llvm_feature(cx.tcx.sess, feature)
+ .flat_map(|feat| {
+ llvm_util::to_llvm_feature(cx.tcx.sess, feat)
.into_iter()
.map(|f| format!("+{}", f))
.collect::<Vec<String>>()
config: ArchiveConfig<'a>,
removals: Vec<String>,
additions: Vec<Addition>,
- should_update_symbols: bool,
src_archive: Option<Option<ArchiveRO>>,
}
config,
removals: Vec::new(),
additions: Vec::new(),
- should_update_symbols: false,
src_archive: None,
}
}
.push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() });
}
- /// Indicate that the next call to `build` should update all symbols in
- /// the archive (equivalent to running 'ar s' over it).
- fn update_symbols(&mut self) {
- self.should_update_symbols = true;
- }
-
/// Combine the provided files, rlibs, and native libraries into a single
/// `Archive`.
fn build(mut self) {
match result {
Err(e) => {
- self.config.sess.fatal(&format!("Error calling dlltool: {}", e.to_string()));
+ self.config.sess.fatal(&format!("Error calling dlltool: {}", e));
}
Ok(output) if !output.status.success() => self.config.sess.fatal(&format!(
"Dlltool could not create import library: {}\n{}",
let mut members = Vec::new();
let dst = CString::new(self.config.dst.to_str().unwrap())?;
- let should_update_symbols = self.should_update_symbols;
unsafe {
if let Some(archive) = self.src_archive() {
dst.as_ptr(),
members.len() as libc::size_t,
members.as_ptr() as *const &_,
- should_update_symbols,
+ true,
kind,
);
let ret = if r.into_result().is_err() {
}
if attrs.flags.contains(CodegenFnAttrFlags::USED) {
+ // `USED` and `USED_LINKER` can't be used together.
+ assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER));
+
// The semantics of #[used] in Rust only require the symbol to make it into the
// object file. It is explicitly allowed for the linker to strip the symbol if it
// is dead. As such, use llvm.compiler.used instead of llvm.used.
// in some versions of the gold linker.
self.add_compiler_used_global(g);
}
+ if attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) {
+ // `USED` and `USED_LINKER` can't be used together.
+ assert!(!attrs.flags.contains(CodegenFnAttrFlags::USED));
+
+ self.add_used_global(g);
+ }
}
}
};
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
-use rustc_session::config::{BranchProtection, CFGuard, CrateType, DebugInfo, PAuthKey, PacRet};
+use rustc_session::config::{BranchProtection, CFGuard, CFProtection};
+use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet};
use rustc_session::Session;
use rustc_span::source_map::Span;
use rustc_span::symbol::Symbol;
let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx);
let mut target_data_layout = sess.target.data_layout.clone();
- if llvm_util::get_version() < (13, 0, 0) {
+ let llvm_version = llvm_util::get_version();
+ if llvm_version < (13, 0, 0) {
if sess.target.arch == "powerpc64" {
target_data_layout = target_data_layout.replace("-S128", "");
}
target_data_layout = "e-m:e-p:64:64-i64:64-n32:64-S128".to_string();
}
}
+ if llvm_version < (14, 0, 0) {
+ if sess.target.llvm_target == "i686-pc-windows-msvc"
+ || sess.target.llvm_target == "i586-pc-windows-msvc"
+ {
+ target_data_layout =
+ "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:32-n8:16:32-a:0:32-S32"
+ .to_string();
+ }
+ if sess.target.arch == "wasm32" {
+ target_data_layout = target_data_layout.replace("-p10:8:8-p20:8:8", "");
+ }
+ }
// Ensure the data-layout values hardcoded remain the defaults.
if sess.target.is_builtin {
);
}
+ // Pass on the control-flow protection flags to LLVM (equivalent to `-fcf-protection` in Clang).
+ if let CFProtection::Branch | CFProtection::Full = sess.opts.debugging_opts.cf_protection {
+ llvm::LLVMRustAddModuleFlag(
+ llmod,
+ llvm::LLVMModFlagBehavior::Override,
+ "cf-protection-branch\0".as_ptr().cast(),
+ 1,
+ )
+ }
+ if let CFProtection::Return | CFProtection::Full = sess.opts.debugging_opts.cf_protection {
+ llvm::LLVMRustAddModuleFlag(
+ llmod,
+ llvm::LLVMModFlagBehavior::Override,
+ "cf-protection-return\0".as_ptr().cast(),
+ 1,
+ )
+ }
+
llmod
}
// LLVM 12.
let version = coverageinfo::mapping_version();
if version < 4 {
- tcx.sess.fatal("rustc option `-Z instrument-coverage` requires LLVM 12 or higher.");
+ tcx.sess.fatal("rustc option `-C instrument-coverage` requires LLVM 12 or higher.");
}
debug!("Generating coverage map for CodegenUnit: `{}`", cx.codegen_unit.name());
/// (functions referenced by other "used" or public items). Any other functions considered unused,
/// or "Unreachable", were still parsed and processed through the MIR stage, but were not
/// codegenned. (Note that `-Clink-dead-code` can force some unused code to be codegenned, but
-/// that flag is known to cause other errors, when combined with `-Z instrument-coverage`; and
+/// that flag is known to cause other errors, when combined with `-C instrument-coverage`; and
/// `-Clink-dead-code` will not generate code for unused generic functions.)
///
/// We can find the unused functions (including generic functions) by the set difference of all MIR
use cstr::cstr;
use rustc_codegen_ssa::debuginfo::type_names::cpp_like_debuginfo;
+use rustc_codegen_ssa::debuginfo::type_names::VTableNameKind;
use rustc_codegen_ssa::traits::*;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
///
/// This function is used to remove the temporary metadata
/// mapping after we've computed the actual metadata.
- fn remove_type(&mut self, type_: Ty<'tcx>) {
- if self.type_to_metadata.remove(type_).is_none() {
- bug!("type metadata `Ty` '{}' is not in the `TypeMap`!", type_);
+ fn remove_type(&mut self, ty: Ty<'tcx>) {
+ if self.type_to_metadata.remove(&ty).is_none() {
+ bug!("type metadata `Ty` '{}' is not in the `TypeMap`!", ty);
}
}
) -> String {
format!("{}_variant_part", self.get_unique_type_id_as_string(enum_type_id))
}
+
+ /// Gets the `UniqueTypeId` for the type of a vtable.
+ fn get_unique_type_id_of_vtable_type(&mut self, vtable_type_name: &str) -> UniqueTypeId {
+ let interner_key = self.unique_id_interner.intern(vtable_type_name);
+ interner_key
+ }
}
/// A description of some recursive type. It can either be already finished (as
// ... then create the member descriptions ...
let member_descriptions = member_description_factory.create_member_descriptions(cx);
+ let type_params = compute_type_parameters(cx, unfinished_type);
// ... and attach them to the stub to complete it.
set_members_of_composite_type(
cx,
- unfinished_type,
member_holding_stub,
member_descriptions,
None,
+ type_params,
);
MetadataCreationResult::new(metadata_stub, true)
}
bug!("fixed_size_array_metadata() called with non-ty::Array type `{:?}`", array_type)
};
- let element_type_metadata = type_metadata(cx, element_type);
+ let element_type_metadata = type_metadata(cx, *element_type);
return_if_metadata_created_in_meantime!(cx, unique_type_id);
)
.chain(
// regular arguments
- signature.inputs().iter().map(|argument_type| Some(type_metadata(cx, argument_type))),
+ signature.inputs().iter().map(|&argument_type| Some(type_metadata(cx, argument_type))),
)
.collect();
unique_type_id: UniqueTypeId,
) -> MetadataCreationResult<'ll> {
let element_type = match slice_type.kind() {
- ty::Slice(element_type) => element_type,
+ ty::Slice(element_type) => *element_type,
ty::Str => cx.tcx.types.u8,
_ => {
bug!(
debug!("foreign_type_metadata: {:?}", t);
let name = compute_debuginfo_type_name(cx.tcx, t, false);
- create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA, DIFlags::FlagZero)
+ let (size, align) = cx.size_and_align_of(t);
+ create_struct_stub(
+ cx,
+ size,
+ align,
+ &name,
+ unique_type_id,
+ NO_SCOPE_METADATA,
+ DIFlags::FlagZero,
+ None,
+ )
}
fn param_type_metadata<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
};
let containing_scope = get_namespace_for_item(cx, struct_def_id);
+ let (size, align) = cx.size_and_align_of(struct_type);
let struct_metadata_stub = create_struct_stub(
cx,
- struct_type,
+ size,
+ align,
&struct_name,
unique_type_id,
Some(containing_scope),
DIFlags::FlagZero,
+ None,
);
create_and_register_recursive_type_forward_declaration(
unique_type_id: UniqueTypeId,
containing_scope: Option<&'ll DIScope>,
) -> RecursiveTypeDescription<'ll, 'tcx> {
+ let (size, align) = cx.size_and_align_of(tuple_type);
let tuple_name = compute_debuginfo_type_name(cx.tcx, tuple_type, false);
let struct_stub = create_struct_stub(
cx,
- tuple_type,
+ size,
+ align,
&tuple_name[..],
unique_type_id,
containing_scope,
DIFlags::FlagZero,
+ None,
);
create_and_register_recursive_type_forward_declaration(
describe_enum_variant(cx, self.layout, variant_info, self_metadata);
let member_descriptions = member_description_factory.create_member_descriptions(cx);
+ let type_params = compute_type_parameters(cx, self.enum_type);
set_members_of_composite_type(
cx,
- self.enum_type,
variant_type_metadata,
member_descriptions,
Some(&self.common_members),
+ type_params,
);
vec![MemberDescription {
name: variant_info.variant_name(),
let member_descriptions =
member_desc_factory.create_member_descriptions(cx);
+ let type_params = compute_type_parameters(cx, self.enum_type);
set_members_of_composite_type(
cx,
- self.enum_type,
variant_type_metadata,
member_descriptions,
Some(&self.common_members),
+ type_params,
);
MemberDescription {
);
let member_descriptions = member_desc_factory.create_member_descriptions(cx);
+ let type_params = compute_type_parameters(cx, self.enum_type);
set_members_of_composite_type(
cx,
- self.enum_type,
variant_type_metadata,
member_descriptions,
Some(&self.common_members),
+ type_params,
);
let (size, align) =
let member_descriptions =
member_desc_factory.create_member_descriptions(cx);
+ let type_params = compute_type_parameters(cx, self.enum_type);
set_members_of_composite_type(
cx,
- self.enum_type,
variant_type_metadata,
member_descriptions,
Some(&self.common_members),
+ type_params,
);
let niche_value = calculate_niche_value(i);
.type_map
.borrow_mut()
.get_unique_type_id_of_enum_variant(cx, layout.ty, variant_name);
+
+ let (size, align) = cx.size_and_align_of(layout.ty);
+
create_struct_stub(
cx,
- layout.ty,
+ size,
+ align,
variant_name,
unique_type_id,
Some(containing_scope),
DIFlags::FlagZero,
+ None,
)
});
member_descriptions: Vec<MemberDescription<'ll>>,
containing_scope: Option<&'ll DIScope>,
) -> &'ll DICompositeType {
+ let (size, align) = cx.size_and_align_of(composite_type);
+
// Create the (empty) struct metadata node ...
let composite_type_metadata = create_struct_stub(
cx,
- composite_type,
+ size,
+ align,
composite_type_name,
composite_type_unique_id,
containing_scope,
DIFlags::FlagZero,
+ None,
);
+
// ... and immediately create and add the member descriptions.
set_members_of_composite_type(
cx,
- composite_type,
composite_type_metadata,
member_descriptions,
None,
+ compute_type_parameters(cx, composite_type),
);
composite_type_metadata
fn set_members_of_composite_type<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
- composite_type: Ty<'tcx>,
composite_type_metadata: &'ll DICompositeType,
member_descriptions: Vec<MemberDescription<'ll>>,
common_members: Option<&Vec<Option<&'ll DIType>>>,
+ type_params: &'ll DIArray,
) {
// In some rare cases LLVM metadata uniquing would lead to an existing type
// description being used instead of a new one created in
member_metadata.extend(other_members.iter());
}
- let type_params = compute_type_parameters(cx, composite_type);
unsafe {
- let type_array = create_DIArray(DIB(cx), &member_metadata);
+ let field_array = create_DIArray(DIB(cx), &member_metadata);
llvm::LLVMRustDICompositeTypeReplaceArrays(
DIB(cx),
composite_type_metadata,
- Some(type_array),
+ Some(field_array),
Some(type_params),
);
}
/// with `set_members_of_composite_type()`.
fn create_struct_stub<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
- struct_type: Ty<'tcx>,
- struct_type_name: &str,
+ size: Size,
+ align: Align,
+ type_name: &str,
unique_type_id: UniqueTypeId,
containing_scope: Option<&'ll DIScope>,
flags: DIFlags,
+ vtable_holder: Option<&'ll DIType>,
) -> &'ll DICompositeType {
- let (struct_size, struct_align) = cx.size_and_align_of(struct_type);
-
let type_map = debug_context(cx).type_map.borrow();
let unique_type_id = type_map.get_unique_type_id_as_string(unique_type_id);
llvm::LLVMRustDIBuilderCreateStructType(
DIB(cx),
containing_scope,
- struct_type_name.as_ptr().cast(),
- struct_type_name.len(),
+ type_name.as_ptr().cast(),
+ type_name.len(),
unknown_file_metadata(cx),
UNKNOWN_LINE_NUMBER,
- struct_size.bits(),
- struct_align.bits() as u32,
+ size.bits(),
+ align.bits() as u32,
flags,
None,
empty_array,
0,
- None,
+ vtable_holder,
unique_type_id.as_ptr().cast(),
unique_type_id.len(),
)
}
/// Generates LLVM debuginfo for a vtable.
+///
+/// The vtable type looks like a struct with a field for each function pointer and super-trait
+/// pointer it contains (plus the `size` and `align` fields).
+///
+/// Except for `size`, `align`, and `drop_in_place`, the field names don't try to mirror
+/// the name of the method they implement. This can be implemented in the future once there
+/// is a proper disambiguation scheme for dealing with methods from different traits that have
+/// the same name.
fn vtable_type_metadata<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
ty: Ty<'tcx>,
COMMON_VTABLE_ENTRIES
};
- // FIXME: We describe the vtable as an array of *const () pointers. The length of the array is
- // correct - but we could create a more accurate description, e.g. by describing it
- // as a struct where each field has a name that corresponds to the name of the method
- // it points to.
- // However, this is not entirely straightforward because there might be multiple
- // methods with the same name if the vtable is for multiple traits. So for now we keep
- // things simple instead of adding some ad-hoc disambiguation scheme.
- let vtable_type = tcx.mk_array(tcx.mk_imm_ptr(tcx.types.unit), vtable_entries.len() as u64);
+ // All function pointers are described as opaque pointers. This could be improved in the future
+ // by describing them as actual function pointers.
+ let void_pointer_ty = tcx.mk_imm_ptr(tcx.types.unit);
+ let void_pointer_type_debuginfo = type_metadata(cx, void_pointer_ty);
+ let usize_debuginfo = type_metadata(cx, tcx.types.usize);
+ let (pointer_size, pointer_align) = cx.size_and_align_of(void_pointer_ty);
+ // If `usize` is not pointer-sized and -aligned then the size and alignment computations
+ // for the vtable as a whole would be wrong. Let's make sure this holds even on weird
+ // platforms.
+ assert_eq!(cx.size_and_align_of(tcx.types.usize), (pointer_size, pointer_align));
+
+ let vtable_type_name =
+ compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::Type);
+ let unique_type_id = debug_context(cx)
+ .type_map
+ .borrow_mut()
+ .get_unique_type_id_of_vtable_type(&vtable_type_name);
+ let size = pointer_size * vtable_entries.len() as u64;
+
+ // This gets mapped to a DW_AT_containing_type attribute which allows GDB to correlate
+ // the vtable to the type it is for.
+ let vtable_holder = type_metadata(cx, ty);
+
+ let vtable_type_metadata = create_struct_stub(
+ cx,
+ size,
+ pointer_align,
+ &vtable_type_name,
+ unique_type_id,
+ NO_SCOPE_METADATA,
+ DIFlags::FlagArtificial,
+ Some(vtable_holder),
+ );
+
+ // Create a field for each entry in the vtable.
+ let fields: Vec<_> = vtable_entries
+ .iter()
+ .enumerate()
+ .filter_map(|(index, vtable_entry)| {
+ let (field_name, field_type) = match vtable_entry {
+ ty::VtblEntry::MetadataDropInPlace => {
+ ("drop_in_place".to_string(), void_pointer_type_debuginfo)
+ }
+ ty::VtblEntry::Method(_) => {
+ // Note: This code does not try to give a proper name to each method
+ // because there might be multiple methods with the same name
+ // (coming from different traits).
+ (format!("__method{}", index), void_pointer_type_debuginfo)
+ }
+ ty::VtblEntry::TraitVPtr(_) => {
+ // Note: In the future we could try to set the type of this pointer
+ // to the type that we generate for the corresponding vtable.
+ (format!("__super_trait_ptr{}", index), void_pointer_type_debuginfo)
+ }
+ ty::VtblEntry::MetadataAlign => ("align".to_string(), usize_debuginfo),
+ ty::VtblEntry::MetadataSize => ("size".to_string(), usize_debuginfo),
+ ty::VtblEntry::Vacant => return None,
+ };
+
+ Some(MemberDescription {
+ name: field_name,
+ type_metadata: field_type,
+ offset: pointer_size * index as u64,
+ size: pointer_size,
+ align: pointer_align,
+ flags: DIFlags::FlagZero,
+ discriminant: None,
+ source_info: None,
+ })
+ })
+ .collect();
- type_metadata(cx, vtable_type)
+ let type_params = create_DIArray(DIB(cx), &[]);
+ set_members_of_composite_type(cx, vtable_type_metadata, fields, None, type_params);
+ vtable_type_metadata
}
/// Creates debug information for the given vtable, which is for the
return;
}
- let vtable_name = compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref);
+ let vtable_name =
+ compute_debuginfo_vtable_name(cx.tcx, ty, poly_trait_ref, VTableNameKind::GlobalVariable);
let vtable_type = vtable_type_metadata(cx, ty, poly_trait_ref);
+ let linkage_name = "";
unsafe {
- let linkage_name = "";
llvm::LLVMRustDIBuilderCreateStaticVariable(
DIB(cx),
NO_SCOPE_METADATA,
let t = arg.layout.ty;
let t = match t.kind() {
ty::Array(ct, _)
- if (*ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() =>
+ if (*ct == cx.tcx.types.u8) || cx.layout_of(*ct).is_zst() =>
{
- cx.tcx.mk_imm_ptr(ct)
+ cx.tcx.mk_imm_ptr(*ct)
}
_ => t,
};
fn simd_simple_float_intrinsic<'ll, 'tcx>(
name: Symbol,
- in_elem: &::rustc_middle::ty::TyS<'_>,
- in_ty: &::rustc_middle::ty::TyS<'_>,
+ in_elem: Ty<'_>,
+ in_ty: Ty<'_>,
in_len: u64,
bx: &mut Builder<'_, 'll, 'tcx>,
span: Span,
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
+ NoUndef = 33,
}
/// LLVMIntPredicate
extern "C" {
pub fn LLVMRustInstallFatalErrorHandler();
+ pub fn LLVMRustDisableSystemDialogsOnCrash();
// Create and destroy contexts.
pub fn LLVMRustContextCreate(shouldDiscardNames: bool) -> &'static mut Context;
pub fn LLVMRustAddByValAttr(Fn: &Value, index: c_uint, ty: &Type);
pub fn LLVMRustAddStructRetAttr(Fn: &Value, index: c_uint, ty: &Type);
pub fn LLVMRustAddFunctionAttribute(Fn: &Value, index: c_uint, attr: Attribute);
+ pub fn LLVMRustEmitUWTableAttr(Fn: &Value, async_: bool);
pub fn LLVMRustAddFunctionAttrStringValue(
Fn: &Value,
index: c_uint,
}
}
+pub fn EmitUWTableAttr(llfn: &Value, async_: bool) {
+ unsafe { LLVMRustEmitUWTableAttr(llfn, async_) }
+}
+
pub fn AddFunctionAttrStringValue(llfn: &Value, idx: AttributePlace, attr: &CStr, value: &CStr) {
unsafe {
LLVMRustAddFunctionAttrStringValue(llfn, idx.as_uint(), attr.as_ptr(), value.as_ptr())
use crate::{llvm, llvm_util};
use libc::c_int;
use libloading::Library;
-use rustc_codegen_ssa::target_features::supported_target_features;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_codegen_ssa::target_features::{supported_target_features, tied_target_features};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_fs_util::path_to_c_string;
use rustc_middle::bug;
use rustc_session::config::PrintRequest;
let mut llvm_args = Vec::with_capacity(n_args + 1);
llvm::LLVMRustInstallFatalErrorHandler();
+ // On Windows, an LLVM assertion will open an Abort/Retry/Ignore dialog
+ // box for the purpose of launching a debugger. However, on CI this will
+ // cause it to hang until it times out, which can take several hours.
+ if std::env::var_os("CI").is_some() {
+ llvm::LLVMRustDisableSystemDialogsOnCrash();
+ }
fn llvm_arg_to_arg_name(full_arg: &str) -> &str {
full_arg.trim().split(|c: char| c == '=' || c.is_whitespace()).next().unwrap_or("")
("aarch64", "frintts") => vec!["fptoint"],
("aarch64", "fcma") => vec!["complxnum"],
("aarch64", "pmuv3") => vec!["perfmon"],
+ ("aarch64", "paca") => vec!["pauth"],
+ ("aarch64", "pacg") => vec!["pauth"],
(_, s) => vec![s],
}
}
+// Given a map from target_features to whether they are enabled or disabled,
+// ensure only valid combinations are allowed.
+pub fn check_tied_features(
+ sess: &Session,
+ features: &FxHashMap<&str, bool>,
+) -> Option<&'static [&'static str]> {
+ for tied in tied_target_features(sess) {
+ // Tied features must be set to the same value, or not set at all
+ let mut tied_iter = tied.iter();
+ let enabled = features.get(tied_iter.next().unwrap());
+
+ if tied_iter.any(|f| enabled != features.get(f)) {
+ return Some(tied);
+ }
+ }
+ None
+}
+
pub fn target_features(sess: &Session) -> Vec<Symbol> {
let target_machine = create_informational_target_machine(sess);
- supported_target_features(sess)
- .iter()
- .filter_map(
- |&(feature, gate)| {
+ let mut features: Vec<Symbol> =
+ supported_target_features(sess)
+ .iter()
+ .filter_map(|&(feature, gate)| {
if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
- },
- )
- .filter(|feature| {
- for llvm_feature in to_llvm_feature(sess, feature) {
- let cstr = CString::new(llvm_feature).unwrap();
- if unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } {
- return true;
+ })
+ .filter(|feature| {
+ for llvm_feature in to_llvm_feature(sess, feature) {
+ let cstr = CString::new(llvm_feature).unwrap();
+ if unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } {
+ return true;
+ }
}
- }
- false
- })
- .map(|feature| Symbol::intern(feature))
- .collect()
+ false
+ })
+ .map(|feature| Symbol::intern(feature))
+ .collect();
+
+ // LLVM 14 changed the ABI for i128 arguments to __float/__fix builtins on Win64
+ // (see https://reviews.llvm.org/D110413). This unstable target feature is intended for use
+ // by compiler-builtins, to export the builtins with the expected, LLVM-version-dependent ABI.
+ // The target feature can be dropped once we no longer support older LLVM versions.
+ if sess.is_nightly_build() && get_version() >= (14, 0, 0) {
+ features.push(Symbol::intern("llvm14-builtins-abi"));
+ }
+ features
}
pub fn print_version() {
Some(_) | None => {}
};
+ fn strip(s: &str) -> &str {
+ s.strip_prefix(&['+', '-']).unwrap_or(s)
+ }
+
let filter = |s: &str| {
if s.is_empty() {
return vec![];
}
- let feature = if s.starts_with('+') || s.starts_with('-') {
- &s[1..]
- } else {
+ let feature = strip(s);
+ if feature == s {
return vec![s.to_string()];
- };
+ }
+
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
// are not passed down to LLVM.
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
features.extend(sess.target.features.split(',').flat_map(&filter));
// -Ctarget-features
- features.extend(sess.opts.cg.target_feature.split(',').flat_map(&filter));
-
+ let feats: Vec<&str> = sess.opts.cg.target_feature.split(',').collect();
+ // LLVM enables based on the last occurence of a feature
+ if let Some(f) =
+ check_tied_features(sess, &feats.iter().map(|f| (strip(f), !f.starts_with("-"))).collect())
+ {
+ sess.err(&format!(
+ "Target features {} must all be enabled or disabled together",
+ f.join(", ")
+ ));
+ }
+ features.extend(feats.iter().flat_map(|&f| filter(f)));
features
}
ty::Ref(..) | ty::RawPtr(_) => {
return self.field(cx, index).llvm_type(cx);
}
- ty::Adt(def, _) if def.is_box() => {
+ // only wide pointer boxes are handled as pointers
+ // thin pointer boxes with scalar allocators are handled by the general logic below
+ ty::Adt(def, substs) if def.is_box() && cx.layout_of(substs.type_at(1)).is_zst() => {
let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty());
return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index, immediate);
}
fn add_archive<F>(&mut self, archive: &Path, skip: F) -> io::Result<()>
where
F: FnMut(&str) -> bool + 'static;
- fn update_symbols(&mut self);
fn build(self);
}
let name = &info.crate_name[&cnum];
let used_crate_source = &info.used_crate_source[&cnum];
- let path = if let Some((path, _)) = &used_crate_source.rlib {
- path
- } else if used_crate_source.rmeta.is_some() {
- return Err(format!(
- "could not find rlib for: `{}`, found rmeta (metadata) file",
- name
- ));
+ if let Some((path, _)) = &used_crate_source.rlib {
+ f(cnum, &path);
} else {
- return Err(format!("could not find rlib for: `{}`", name));
- };
- f(cnum, &path);
+ if used_crate_source.rmeta.is_some() {
+ return Err(format!(
+ "could not find rlib for: `{}`, found rmeta (metadata) file",
+ name
+ ));
+ } else {
+ return Err(format!("could not find rlib for: `{}`", name));
+ }
+ }
}
Ok(())
}
ab.inject_dll_import_lib(&raw_dylib_name, &raw_dylib_imports, tmpdir);
}
- // After adding all files to the archive, we need to update the
- // symbol table of the archive.
- ab.update_symbols();
-
// Note that it is important that we add all of our non-object "magical
// files" *after* all of the object files in the archive. The reason for
// this is as follows:
// normal linkers for the platform.
let metadata = create_rmeta_file(sess, codegen_results.metadata.raw_data());
ab.add_file(&emit_metadata(sess, &metadata, tmpdir));
-
- // After adding all files to the archive, we need to update the
- // symbol table of the archive. This currently dies on macOS (see
- // #11162), and isn't necessary there anyway
- if !sess.target.is_like_osx {
- ab.update_symbols();
- }
}
RlibFlavor::StaticlibBase => {
}
}
}
+
return Ok(ab);
}
sess.fatal(&e);
}
- ab.update_symbols();
ab.build();
if !all_native_libs.is_empty() {
sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
let mut archive = <B as ArchiveBuilder>::new(sess, &dst, Some(cratepath));
- archive.update_symbols();
let mut any_objects = false;
for f in archive.src_files() {
// `SHF_EXCLUDE` flag we can set on sections in an object file to get
// automatically removed from the final output.
pub fn create_rmeta_file(sess: &Session, metadata: &[u8]) -> Vec<u8> {
- let mut file = if let Some(file) = create_object_file(sess) {
- file
- } else {
+ let Some(mut file) = create_object_file(sess) else {
// This is used to handle all "other" targets. This includes targets
// in two categories:
//
) -> Vec<u8> {
let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
- let mut file = if let Some(file) = create_object_file(sess) {
- file
- } else {
+ let Some(mut file) = create_object_file(sess) else {
return compressed.to_vec();
};
let section = file.add_section(
codegen_worker_receive,
shared_emitter_main,
future: coordinator_thread,
- output_filenames: tcx.output_filenames(()),
+ output_filenames: tcx.output_filenames(()).clone(),
}
}
cgu_reuse_tracker: sess.cgu_reuse_tracker.clone(),
coordinator_send,
diag_emitter: shared_emitter.clone(),
- output_filenames: tcx.output_filenames(()),
+ output_filenames: tcx.output_filenames(()).clone(),
regular_module_config: regular_config,
metadata_module_config: metadata_config,
allocator_module_config: allocator_config,
used_crate_source: Default::default(),
lang_item_to_crate: Default::default(),
missing_lang_items: Default::default(),
- dependency_formats: tcx.dependency_formats(()),
+ dependency_formats: tcx.dependency_formats(()).clone(),
windows_subsystem,
};
let lang_items = tcx.lang_items();
info.native_libraries
.insert(cnum, tcx.native_libraries(cnum).iter().map(Into::into).collect());
info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string());
- info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum));
+ info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum).clone());
if tcx.is_compiler_builtins(cnum) {
info.compiler_builtins = Some(cnum);
}
if cpp_like_debuginfo {
output.push_str("array$<");
push_debuginfo_type_name(tcx, inner_type, true, output, visited);
- match len.val {
+ match len.val() {
ty::ConstKind::Param(param) => write!(output, ",{}>", param.name).unwrap(),
_ => write!(output, ",{}>", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
.unwrap(),
} else {
output.push('[');
push_debuginfo_type_name(tcx, inner_type, true, output, visited);
- match len.val {
+ match len.val() {
ty::ConstKind::Param(param) => write!(output, "; {}]", param.name).unwrap(),
_ => write!(output, "; {}]", len.eval_usize(tcx, ty::ParamEnv::reveal_all()))
.unwrap(),
// We only care about avoiding recursing
// directly back to the type we're currently
// processing
- visited.remove(t);
+ visited.remove(&t);
}
ty::Closure(def_id, substs) | ty::Generator(def_id, substs, ..) => {
// Name will be "{closure_env#0}<T1, T2, ...>", "{generator_env#0}<T1, T2, ...>", or
}
}
-/// Computes a name for the global variable storing a vtable.
+pub enum VTableNameKind {
+ // Is the name for the const/static holding the vtable?
+ GlobalVariable,
+ // Is the name for the type of the vtable?
+ Type,
+}
+
+/// Computes a name for the global variable storing a vtable (or the type of that global variable).
///
/// The name is of the form:
///
/// or, when generating C++-like names:
///
/// `impl$<path::to::SomeType, path::to::SomeTrait>::vtable$`
+///
+/// If `kind` is `VTableNameKind::Type` then the last component is `{vtable_ty}` instead of just
+/// `{vtable}`, so that the type and the corresponding global variable get assigned different
+/// names.
pub fn compute_debuginfo_vtable_name<'tcx>(
tcx: TyCtxt<'tcx>,
t: Ty<'tcx>,
trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
+ kind: VTableNameKind,
) -> String {
let cpp_like_debuginfo = cpp_like_debuginfo(tcx);
push_close_angle_bracket(cpp_like_debuginfo, &mut vtable_name);
- let suffix = if cpp_like_debuginfo { "::vtable$" } else { "::{vtable}" };
+ let suffix = match (cpp_like_debuginfo, kind) {
+ (true, VTableNameKind::GlobalVariable) => "::vtable$",
+ (false, VTableNameKind::GlobalVariable) => "::{vtable}",
+ (true, VTableNameKind::Type) => "::vtable_type$",
+ (false, VTableNameKind::Type) => "::{vtable_type}",
+ };
vtable_name.reserve_exact(suffix.len());
vtable_name.push_str(suffix);
true
}
-fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: &'tcx ty::Const<'tcx>, output: &mut String) {
- match ct.val {
+fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut String) {
+ match ct.val() {
ty::ConstKind::Param(param) => {
write!(output, "{}", param.name)
}
- _ => match ct.ty.kind() {
+ _ => match ct.ty().kind() {
ty::Int(ity) => {
- let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty);
+ let bits = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
write!(output, "{}", val)
}
ty::Uint(_) => {
- let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty);
+ let val = ct.eval_bits(tcx, ty::ParamEnv::reveal_all(), ct.ty());
write!(output, "{}", val)
}
ty::Bool => {
let mut hasher = StableHasher::new();
hcx.while_hashing_spans(false, |hcx| {
hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
- ct.val.hash_stable(hcx, &mut hasher);
+ ct.val().hash_stable(hcx, &mut hasher);
});
});
// Let's only emit 64 bits of the hash value. That should be plenty for
mir::ConstantKind::Ty(ct) => ct,
mir::ConstantKind::Val(val, _) => return Ok(val),
};
- match ct.val {
+ match ct.val() {
ty::ConstKind::Unevaluated(ct) => self
.cx
.tcx()
let c = ty::Const::from_value(bx.tcx(), val, ty);
let values: Vec<_> = bx
.tcx()
- .destructure_const(ty::ParamEnv::reveal_all().and(&c))
+ .destructure_const(ty::ParamEnv::reveal_all().and(c))
.fields
.iter()
.map(|field| {
- if let Some(prim) = field.val.try_to_scalar() {
+ if let Some(prim) = field.val().try_to_scalar() {
let layout = bx.layout_of(field_ty);
let scalar = match layout.abi {
Abi::Scalar(x) => x,
})
.collect();
let llval = bx.const_struct(&values, false);
- (llval, c.ty)
+ (llval, c.ty())
})
.unwrap_or_else(|_| {
bx.tcx().sess.span_err(span, "could not evaluate shuffle_indices at compile time");
("ssbs", Some(sym::aarch64_target_feature)),
// FEAT_SB
("sb", Some(sym::aarch64_target_feature)),
- // FEAT_PAUTH
- ("pauth", Some(sym::aarch64_target_feature)),
+ // FEAT_PAUTH (address authentication)
+ ("paca", Some(sym::aarch64_target_feature)),
+ // FEAT_PAUTH (generic authentication)
+ ("pacg", Some(sym::aarch64_target_feature)),
// FEAT_DPB
("dpb", Some(sym::aarch64_target_feature)),
// FEAT_DPB2
("v8.7a", Some(sym::aarch64_target_feature)),
];
+const AARCH64_TIED_FEATURES: &[&[&str]] = &[&["paca", "pacg"]];
+
const X86_ALLOWED_FEATURES: &[(&str, Option<Symbol>)] = &[
("adx", Some(sym::adx_target_feature)),
("aes", None),
}
}
+pub fn tied_target_features(sess: &Session) -> &'static [&'static [&'static str]] {
+ match &*sess.target.arch {
+ "aarch64" => AARCH64_TIED_FEATURES,
+ _ => &[],
+ }
+}
+
pub(crate) fn provide(providers: &mut Providers) {
providers.supported_target_features = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes {
/// Returns true if the function source hash was added to the coverage map (even if it had
- /// already been added, for this instance). Returns false *only* if `-Z instrument-coverage` is
+ /// already been added, for this instance). Returns false *only* if `-C instrument-coverage` is
/// not enabled (a coverage map is not being generated).
fn set_function_source_hash(
&mut self,
function_source_hash: u64,
) -> bool;
- /// Returns true if the counter was added to the coverage map; false if `-Z instrument-coverage`
+ /// Returns true if the counter was added to the coverage map; false if `-C instrument-coverage`
/// is not enabled (a coverage map is not being generated).
fn add_coverage_counter(
&mut self,
) -> bool;
/// Returns true if the expression was added to the coverage map; false if
- /// `-Z instrument-coverage` is not enabled (a coverage map is not being generated).
+ /// `-C instrument-coverage` is not enabled (a coverage map is not being generated).
fn add_coverage_counter_expression(
&mut self,
instance: Instance<'tcx>,
region: Option<CodeRegion>,
) -> bool;
- /// Returns true if the region was added to the coverage map; false if `-Z instrument-coverage`
+ /// Returns true if the region was added to the coverage map; false if `-C instrument-coverage`
/// is not enabled (a coverage map is not being generated).
fn add_coverage_unreachable(&mut self, instance: Instance<'tcx>, region: CodeRegion) -> bool;
}
}
// Add spans for the stacktrace. Don't print a single-line backtrace though.
if self.stacktrace.len() > 1 {
+ // Helper closure to print duplicated lines.
+ let mut flush_last_line = |last_frame, times| {
+ if let Some((line, span)) = last_frame {
+ err.span_label(span, &line);
+ // Don't print [... additional calls ...] if the number of lines is small
+ if times < 3 {
+ for _ in 0..times {
+ err.span_label(span, &line);
+ }
+ } else {
+ err.span_label(
+ span,
+ format!("[... {} additional calls {} ...]", times, &line),
+ );
+ }
+ }
+ };
+
+ let mut last_frame = None;
+ let mut times = 0;
for frame_info in &self.stacktrace {
- err.span_label(frame_info.span, frame_info.to_string());
+ let frame = (frame_info.to_string(), frame_info.span);
+ if last_frame.as_ref() == Some(&frame) {
+ times += 1;
+ } else {
+ flush_last_line(last_frame, times);
+ last_frame = Some(frame);
+ times = 0;
+ }
}
+ flush_last_line(last_frame, times);
}
// Let the caller finish the job.
emit(err)
ScalarMaybeUninit, StackPopCleanup,
};
-use rustc_errors::ErrorReported;
use rustc_hir::def::DefKind;
use rustc_middle::mir;
use rustc_middle::mir::interpret::ErrorHandled;
let cid = key.value;
let def = cid.instance.def.with_opt_param();
-
- if let Some(def) = def.as_local() {
- if tcx.has_typeck_results(def.did) {
- if let Some(error_reported) = tcx.typeck_opt_const_arg(def).tainted_by_errors {
- return Err(ErrorHandled::Reported(error_reported));
- }
- }
- if !tcx.is_mir_available(def.did) {
- tcx.sess.delay_span_bug(
- tcx.def_span(def.did),
- &format!("no MIR body is available for {:?}", def.did),
- );
- return Err(ErrorHandled::Reported(ErrorReported {}));
- }
- if let Some(error_reported) = tcx.mir_const_qualif_opt_const_arg(def).error_occured {
- return Err(ErrorHandled::Reported(error_reported));
- }
- }
-
let is_static = tcx.is_static(def.did);
let mut ecx = InterpCx::new(
+use rustc_errors::ErrorReported;
+use rustc_hir::def::DefKind;
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty};
use std::borrow::Borrow;
ty::InstanceDef::Item(def) => {
if ecx.tcx.is_ctfe_mir_available(def.did) {
Ok(ecx.tcx.mir_for_ctfe_opt_const_arg(def))
+ } else if ecx.tcx.def_kind(def.did) == DefKind::AssocConst {
+ ecx.tcx.sess.delay_span_bug(
+ rustc_span::DUMMY_SP,
+ "This is likely a const item that is missing from its impl",
+ );
+ throw_inval!(AlreadyReported(ErrorReported {}));
} else {
let path = ecx.tcx.def_path_str(def.did);
Err(ConstEvalErrKind::NeedsRfc(format!("calling extern function `{}`", path))
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
use crate::interpret::{
- intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, MPlaceTy, MemPlaceMeta, Scalar,
+ intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy,
+ MemPlaceMeta, Scalar,
};
mod error;
}
}
-/// This function uses `unwrap` copiously, because an already validated constant
-/// must have valid fields and can thus never fail outside of compiler bugs. However, it is
-/// invoked from the pretty printer, where it can receive enums with no variants and e.g.
-/// `read_discriminant` needs to be able to handle that.
-pub(crate) fn destructure_const<'tcx>(
+/// This function should never fail for validated constants. However, it is also invoked from the
+/// pretty printer which might attempt to format invalid constants and in that case it might fail.
+pub(crate) fn try_destructure_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- val: &'tcx ty::Const<'tcx>,
-) -> mir::DestructuredConst<'tcx> {
+ val: ty::Const<'tcx>,
+) -> InterpResult<'tcx, mir::DestructuredConst<'tcx>> {
trace!("destructure_const: {:?}", val);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
- let op = ecx.const_to_op(val, None).unwrap();
+ let op = ecx.const_to_op(val, None)?;
// We go to `usize` as we cannot allocate anything bigger anyway.
- let (field_count, variant, down) = match val.ty.kind() {
+ let (field_count, variant, down) = match val.ty().kind() {
ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
- ty::Adt(def, _) if def.variants.is_empty() => {
- return mir::DestructuredConst { variant: None, fields: &[] };
- }
ty::Adt(def, _) => {
- let variant = ecx.read_discriminant(&op).unwrap().1;
- let down = ecx.operand_downcast(&op, variant).unwrap();
+ let variant = ecx.read_discriminant(&op)?.1;
+ let down = ecx.operand_downcast(&op, variant)?;
(def.variants[variant].fields.len(), Some(variant), down)
}
ty::Tuple(substs) => (substs.len(), None, op),
_ => bug!("cannot destructure constant {:?}", val),
};
- let fields_iter = (0..field_count).map(|i| {
- let field_op = ecx.operand_field(&down, i).unwrap();
- let val = op_to_const(&ecx, &field_op);
- ty::Const::from_value(tcx, val, field_op.layout.ty)
- });
- let fields = tcx.arena.alloc_from_iter(fields_iter);
+ let fields = (0..field_count)
+ .map(|i| {
+ let field_op = ecx.operand_field(&down, i)?;
+ let val = op_to_const(&ecx, &field_op);
+ Ok(ty::Const::from_value(tcx, val, field_op.layout.ty))
+ })
+ .collect::<InterpResult<'tcx, Vec<_>>>()?;
+ let fields = tcx.arena.alloc_from_iter(fields);
- mir::DestructuredConst { variant, fields }
+ Ok(mir::DestructuredConst { variant, fields })
}
pub(crate) fn deref_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- val: &'tcx ty::Const<'tcx>,
-) -> &'tcx ty::Const<'tcx> {
+ val: ty::Const<'tcx>,
+) -> ty::Const<'tcx> {
trace!("deref_const: {:?}", val);
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
let op = ecx.const_to_op(val, None).unwrap();
// In case of unsized types, figure out the real type behind.
MemPlaceMeta::Meta(scalar) => match mplace.layout.ty.kind() {
ty::Str => bug!("there's no sized equivalent of a `str`"),
- ty::Slice(elem_ty) => tcx.mk_array(elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
+ ty::Slice(elem_ty) => tcx.mk_array(*elem_ty, scalar.to_machine_usize(&tcx).unwrap()),
_ => bug!(
"type {} should not have metadata, but had {:?}",
mplace.layout.ty,
},
};
- tcx.mk_const(ty::Const { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
+ tcx.mk_const(ty::ConstS { val: ty::ConstKind::Value(op_to_const(&ecx, &mplace.into())), ty })
}
match (&src.layout.ty.kind(), &cast_ty.ty.kind()) {
(&ty::Ref(_, s, _), &ty::Ref(_, c, _) | &ty::RawPtr(TypeAndMut { ty: c, .. }))
| (&ty::RawPtr(TypeAndMut { ty: s, .. }), &ty::RawPtr(TypeAndMut { ty: c, .. })) => {
- self.unsize_into_ptr(src, dest, s, c)
+ self.unsize_into_ptr(src, dest, *s, *c)
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => {
assert_eq!(def_a, def_b);
instance: ty::InstanceDef<'tcx>,
promoted: Option<mir::Promoted>,
) -> InterpResult<'tcx, &'tcx mir::Body<'tcx>> {
- // do not continue if typeck errors occurred (can only occur in local crate)
let def = instance.with_opt_param();
- if let Some(def) = def.as_local() {
- if self.tcx.has_typeck_results(def.did) {
- if let Some(error_reported) = self.tcx.typeck_opt_const_arg(def).tainted_by_errors {
- throw_inval!(AlreadyReported(error_reported))
- }
- }
- }
trace!("load mir(instance={:?}, promoted={:?})", instance, promoted);
- if let Some(promoted) = promoted {
- return Ok(&self.tcx.promoted_mir_opt_const_arg(def)[promoted]);
+ let body = if let Some(promoted) = promoted {
+ &self.tcx.promoted_mir_opt_const_arg(def)[promoted]
+ } else {
+ M::load_mir(self, instance)?
+ };
+ // do not continue if typeck errors occurred (can only occur in local crate)
+ if let Some(err) = body.tainted_by_errors {
+ throw_inval!(AlreadyReported(err));
}
- M::load_mir(self, instance)
+ Ok(body)
}
/// Call this on things you got out of the MIR (so it is as generic as the current
}
}
- fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
self.pretty_print_const(ct, false)
}
/// "universe" (param_env).
pub fn const_to_op(
&self,
- val: &ty::Const<'tcx>,
+ val: ty::Const<'tcx>,
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
- match val.val {
+ match val.val() {
ty::ConstKind::Param(_) | ty::ConstKind::Bound(..) => throw_inval!(TooGeneric),
ty::ConstKind::Error(_) => throw_inval!(AlreadyReported(ErrorReported)),
ty::ConstKind::Unevaluated(uv) => {
ty::ConstKind::Infer(..) | ty::ConstKind::Placeholder(..) => {
span_bug!(self.cur_span(), "const_to_op: Unexpected ConstKind {:?}", val)
}
- ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty, layout),
+ ty::ConstKind::Value(val_val) => self.const_val_to_op(val_val, val.ty(), layout),
}
}
layout: Option<TyAndLayout<'tcx>>,
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
match val {
- mir::ConstantKind::Ty(ct) => self.const_to_op(ct, layout),
- mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, ty, layout),
+ mir::ConstantKind::Ty(ct) => self.const_to_op(*ct, layout),
+ mir::ConstantKind::Val(val, ty) => self.const_val_to_op(*val, *ty, layout),
}
}
let (meta, ty) = match base.layout.ty.kind() {
// It is not nice to match on the type, but that seems to be the only way to
// implement this.
- ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(inner, inner_len)),
+ ty::Array(inner, _) => (MemPlaceMeta::None, self.tcx.mk_array(*inner, inner_len)),
ty::Slice(..) => {
let len = Scalar::from_machine_usize(inner_len, self);
(MemPlaceMeta::Meta(len), base.layout.ty)
assert!(matches!(ty.kind(), ty::Param(_)))
}
ty::subst::GenericArgKind::Const(ct) => {
- assert!(matches!(ct.val, ty::ConstKind::Param(_)))
+ assert!(matches!(ct.val(), ty::ConstKind::Param(_)))
}
ty::subst::GenericArgKind::Lifetime(..) => (),
},
}
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- match c.val {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match c.val() {
ty::ConstKind::Param(..) => ControlFlow::Break(FoundParam),
_ => c.super_visit_with(self),
}
{
// A mutable reference inside a const? That does not seem right (except if it is
// a ZST).
- let layout = self.ecx.layout_of(ty)?;
+ let layout = self.ecx.layout_of(*ty)?;
if !layout.is_zst() {
throw_validation_failure!(self.path, { "mutable reference in a `const`" });
}
// This is the length of the array/slice.
let len = mplace.len(self.ecx)?;
// This is the element type size.
- let layout = self.ecx.layout_of(tys)?;
+ let layout = self.ecx.layout_of(*tys)?;
// This is the size in bytes of the whole array. (This checks for overflow.)
let size = layout.size * len;
// Fast path for arrays and slices of ZSTs. We only need to check a single ZST element
// of an array and not all of them, because there's only a single value of a specific
// ZST type, so either validation fails for all elements or none.
- ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(tys)?.is_zst() => {
+ ty::Array(tys, ..) | ty::Slice(tys) if self.ecx.layout_of(*tys)?.is_zst() => {
// Validate just the first element (if any).
self.walk_aggregate(op, fields.take(1))?
}
providers.eval_to_const_value_raw = const_eval::eval_to_const_value_raw_provider;
providers.eval_to_allocation_raw = const_eval::eval_to_allocation_raw_provider;
providers.const_caller_location = const_eval::const_caller_location;
- providers.destructure_const = |tcx, param_env_and_value| {
+ providers.try_destructure_const = |tcx, param_env_and_value| {
let (param_env, value) = param_env_and_value.into_parts();
- const_eval::destructure_const(tcx, param_env, value)
+ const_eval::try_destructure_const(tcx, param_env, value).ok()
};
providers.const_to_valtree = |tcx, param_env_and_value| {
let (param_env, raw) = param_env_and_value.into_parts();
use rustc_middle::ty::{Binder, TraitPredicate, TraitRef};
use rustc_mir_dataflow::{self, Analysis};
use rustc_span::{sym, Span, Symbol};
+use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
use rustc_trait_selection::traits::SelectionContext;
use std::mem;
fn in_return_place(
&mut self,
ccx: &'mir ConstCx<'mir, 'tcx>,
- error_occured: Option<ErrorReported>,
+ tainted_by_errors: Option<ErrorReported>,
) -> ConstQualifs {
// Find the `Return` terminator if one exists.
//
.map(|(bb, _)| bb);
let return_block = match return_block {
- None => return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty(), error_occured),
+ None => {
+ return qualifs::in_any_value_of_ty(ccx, ccx.body.return_ty(), tainted_by_errors);
+ }
Some(bb) => bb,
};
needs_non_const_drop: self.needs_non_const_drop(ccx, RETURN_PLACE, return_loc),
has_mut_interior: self.has_mut_interior(ccx, RETURN_PLACE, return_loc),
custom_eq,
- error_occured,
+ tainted_by_errors,
}
}
}
}
/// Emits an error if an expression cannot be evaluated in the current context.
- pub fn check_op(&mut self, op: impl NonConstOp) {
+ pub fn check_op(&mut self, op: impl NonConstOp<'tcx>) {
self.check_op_spanned(op, self.span);
}
/// Emits an error at the given `span` if an expression cannot be evaluated in the current
/// context.
- pub fn check_op_spanned<O: NonConstOp>(&mut self, op: O, span: Span) {
+ pub fn check_op_spanned<O: NonConstOp<'tcx>>(&mut self, op: O, span: Span) {
let gate = match op.status_in_item(self.ccx) {
Status::Allowed => return,
self.super_terminator(terminator, location);
match &terminator.kind {
- TerminatorKind::Call { func, args, .. } => {
+ TerminatorKind::Call { func, args, fn_span, from_hir_call, .. } => {
let ConstCx { tcx, body, param_env, .. } = *self.ccx;
let caller = self.def_id().to_def_id();
if let Some(trait_id) = tcx.trait_of_item(callee) {
trace!("attempting to call a trait method");
if !self.tcx.features().const_trait_impl {
- self.check_op(ops::FnCallNonConst(Some((callee, substs))));
+ self.check_op(ops::FnCallNonConst {
+ caller,
+ callee,
+ substs,
+ span: *fn_span,
+ from_hir_call: *from_hir_call,
+ });
return;
}
let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
- let obligation = Obligation::new(
- ObligationCause::dummy(),
- param_env,
- Binder::dummy(TraitPredicate {
- trait_ref,
- constness: ty::BoundConstness::NotConst,
- polarity: ty::ImplPolarity::Positive,
- }),
- );
+ let poly_trait_pred = Binder::dummy(TraitPredicate {
+ trait_ref,
+ constness: ty::BoundConstness::ConstIfConst,
+ polarity: ty::ImplPolarity::Positive,
+ });
+ let obligation =
+ Obligation::new(ObligationCause::dummy(), param_env, poly_trait_pred);
let implsrc = tcx.infer_ctxt().enter(|infcx| {
let mut selcx = SelectionContext::new(&infcx);
return;
}
Ok(Some(ImplSource::UserDefined(data))) => {
- if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) {
- self.check_op(ops::FnCallNonConst(None));
- return;
- }
let callee_name = tcx.item_name(callee);
if let Some(&did) = tcx
.associated_item_def_ids(data.impl_def_id)
substs = InternalSubsts::identity_for_item(tcx, did);
callee = did;
}
+
+ if let hir::Constness::NotConst = tcx.impl_constness(data.impl_def_id) {
+ self.check_op(ops::FnCallNonConst {
+ caller,
+ callee,
+ substs,
+ span: *fn_span,
+ from_hir_call: *from_hir_call,
+ });
+ return;
+ }
}
_ if !tcx.is_const_fn_raw(callee) => {
// At this point, it is only legal when the caller is marked with
// #[default_method_body_is_const], and the callee is in the same
// trait.
let callee_trait = tcx.trait_of_item(callee);
- if callee_trait.is_some() {
- if tcx.has_attr(caller, sym::default_method_body_is_const) {
- if tcx.trait_of_item(caller) == callee_trait {
- nonconst_call_permission = true;
- }
- }
+ if callee_trait.is_some()
+ && tcx.has_attr(caller, sym::default_method_body_is_const)
+ && callee_trait == tcx.trait_of_item(caller)
+ // Can only call methods when it's `<Self as TheTrait>::f`.
+ && tcx.types.self_param == substs.type_at(0)
+ {
+ nonconst_call_permission = true;
}
if !nonconst_call_permission {
- self.check_op(ops::FnCallNonConst(None));
+ let obligation = Obligation::new(
+ ObligationCause::dummy_with_span(*fn_span),
+ param_env,
+ tcx.mk_predicate(
+ poly_trait_pred.map_bound(ty::PredicateKind::Trait),
+ ),
+ );
+
+ // improve diagnostics by showing what failed. Our requirements are stricter this time
+ // as we are going to error again anyways.
+ tcx.infer_ctxt().enter(|infcx| {
+ if let Err(e) = implsrc {
+ infcx.report_selection_error(
+ obligation.clone(),
+ &obligation,
+ &e,
+ false,
+ );
+ }
+ });
+
+ self.check_op(ops::FnCallNonConst {
+ caller,
+ callee,
+ substs,
+ span: *fn_span,
+ from_hir_call: *from_hir_call,
+ });
return;
}
}
}
if !nonconst_call_permission {
- self.check_op(ops::FnCallNonConst(None));
+ self.check_op(ops::FnCallNonConst {
+ caller,
+ callee,
+ substs,
+ span: *fn_span,
+ from_hir_call: *from_hir_call,
+ });
return;
}
}
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::traits::{ImplSource, Obligation, ObligationCause};
+use rustc_middle::mir;
+use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
-use rustc_middle::{mir, ty::AssocKind};
+use rustc_middle::ty::{
+ suggest_constraining_type_param, Adt, Closure, FnDef, FnPtr, Param, TraitPredicate, Ty,
+};
+use rustc_middle::ty::{Binder, BoundConstness, ImplPolarity, TraitRef};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
-use rustc_span::{symbol::Ident, Span, Symbol};
-use rustc_span::{BytePos, Pos};
+use rustc_span::{BytePos, Pos, Span, Symbol};
+use rustc_trait_selection::traits::SelectionContext;
use super::ConstCx;
+use crate::util::{call_kind, CallDesugaringKind, CallKind};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Status {
}
/// An operation that is not *always* allowed in a const context.
-pub trait NonConstOp: std::fmt::Debug {
+pub trait NonConstOp<'tcx>: std::fmt::Debug {
/// Returns an enum indicating whether this operation is allowed within the given item.
- fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
+ fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Forbidden
}
DiagnosticImportance::Primary
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
}
#[derive(Debug)]
pub struct FloatingPointOp;
-impl NonConstOp for FloatingPointOp {
- fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
+ fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() == hir::ConstContext::ConstFn {
Status::Unstable(sym::const_fn_floating_point_arithmetic)
} else {
}
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_floating_point_arithmetic,
/// A function call where the callee is a pointer.
#[derive(Debug)]
pub struct FnCallIndirect;
-impl NonConstOp for FnCallIndirect {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn")
}
}
/// A function call where the callee is not marked as `const`.
-#[derive(Debug)]
-pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>);
-impl<'a> NonConstOp for FnCallNonConst<'a> {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
- let mut err = struct_span_err!(
- ccx.tcx.sess,
- span,
- E0015,
- "calls in {}s are limited to constant functions, \
- tuple structs and tuple variants",
- ccx.const_kind(),
- );
+#[derive(Debug, Clone, Copy)]
+pub struct FnCallNonConst<'tcx> {
+ pub caller: DefId,
+ pub callee: DefId,
+ pub substs: SubstsRef<'tcx>,
+ pub span: Span,
+ pub from_hir_call: bool,
+}
- if let FnCallNonConst(Some((callee, substs))) = *self {
- if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() {
- if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind(
- ccx.tcx,
- Ident::with_dummy_span(sym::eq),
- AssocKind::Fn,
- trait_def_id,
- ) {
- if callee == eq_item.def_id && substs.len() == 2 {
- match (substs[0].unpack(), substs[1].unpack()) {
- (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
- if self_ty == rhs_ty
- && self_ty.is_ref()
- && self_ty.peel_refs().is_primitive() =>
- {
- let mut num_refs = 0;
- let mut tmp_ty = self_ty;
- while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
- num_refs += 1;
- tmp_ty = inner_ty;
- }
- let deref = "*".repeat(num_refs);
-
- if let Ok(call_str) =
- ccx.tcx.sess.source_map().span_to_snippet(span)
- {
- if let Some(eq_idx) = call_str.find("==") {
- if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
- .find(|c: char| !c.is_whitespace())
- {
- let rhs_pos = span.lo()
- + BytePos::from_usize(eq_idx + 2 + rhs_idx);
- let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
- err.multipart_suggestion(
- "consider dereferencing here",
- vec![
- (span.shrink_to_lo(), deref.clone()),
- (rhs_span, deref),
- ],
- Applicability::MachineApplicable,
- );
- }
+impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> {
+ let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self;
+ let ConstCx { tcx, param_env, .. } = *ccx;
+
+ let diag_trait = |mut err, self_ty: Ty<'_>, trait_id| {
+ let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
+
+ match self_ty.kind() {
+ Param(param_ty) => {
+ debug!(?param_ty);
+ if let Some(generics) = caller
+ .as_local()
+ .map(|id| tcx.hir().local_def_id_to_hir_id(id))
+ .map(|id| tcx.hir().get(id))
+ .as_ref()
+ .and_then(|node| node.generics())
+ {
+ let constraint = with_no_trimmed_paths(|| {
+ format!("~const {}", trait_ref.print_only_trait_path())
+ });
+ suggest_constraining_type_param(
+ tcx,
+ generics,
+ &mut err,
+ ¶m_ty.name.as_str(),
+ &constraint,
+ None,
+ );
+ }
+ }
+ Adt(..) => {
+ let obligation = Obligation::new(
+ ObligationCause::dummy(),
+ param_env,
+ Binder::dummy(TraitPredicate {
+ trait_ref,
+ constness: BoundConstness::NotConst,
+ polarity: ImplPolarity::Positive,
+ }),
+ );
+
+ let implsrc = tcx.infer_ctxt().enter(|infcx| {
+ let mut selcx = SelectionContext::new(&infcx);
+ selcx.select(&obligation)
+ });
+
+ if let Ok(Some(ImplSource::UserDefined(data))) = implsrc {
+ let span =
+ tcx.sess.source_map().guess_head_span(tcx.def_span(data.impl_def_id));
+ err.span_note(span, "impl defined here, but it is not `const`");
+ }
+ }
+ _ => {}
+ }
+
+ err
+ };
+
+ let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
+
+ debug!(?call_kind);
+
+ let mut err = match call_kind {
+ CallKind::Normal { desugaring: Some((kind, self_ty)), .. } => {
+ macro_rules! error {
+ ($fmt:literal) => {
+ struct_span_err!(tcx.sess, span, E0015, $fmt, self_ty, ccx.const_kind())
+ };
+ }
+
+ let err = match kind {
+ CallDesugaringKind::ForLoopIntoIter => {
+ error!("cannot convert `{}` into an iterator in {}s")
+ }
+ CallDesugaringKind::QuestionBranch => {
+ error!("`?` cannot determine the branch of `{}` in {}s")
+ }
+ CallDesugaringKind::QuestionFromResidual => {
+ error!("`?` cannot convert from residual of `{}` in {}s")
+ }
+ CallDesugaringKind::TryBlockFromOutput => {
+ error!("`try` block cannot convert `{}` to the result in {}s")
+ }
+ };
+
+ diag_trait(err, self_ty, kind.trait_def_id(tcx))
+ }
+ CallKind::FnCall { fn_trait_id, self_ty } => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0015,
+ "cannot call non-const closure in {}s",
+ ccx.const_kind(),
+ );
+
+ match self_ty.kind() {
+ FnDef(def_id, ..) => {
+ let span = tcx.sess.source_map().guess_head_span(tcx.def_span(*def_id));
+ if ccx.tcx.is_const_fn_raw(*def_id) {
+ span_bug!(span, "calling const FnDef errored when it shouldn't");
+ }
+
+ err.span_note(span, "function defined here, but it is not `const`");
+ }
+ FnPtr(..) => {
+ err.note(&format!(
+ "function pointers need an RFC before allowed to be called in {}s",
+ ccx.const_kind()
+ ));
+ }
+ Closure(..) => {
+ err.note(&format!(
+ "closures need an RFC before allowed to be called in {}s",
+ ccx.const_kind()
+ ));
+ }
+ _ => {}
+ }
+
+ diag_trait(err, self_ty, fn_trait_id)
+ }
+ CallKind::Operator { trait_id, self_ty, .. } => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0015,
+ "cannot call non-const operator in {}s",
+ ccx.const_kind()
+ );
+
+ if Some(trait_id) == ccx.tcx.lang_items().eq_trait() {
+ match (substs[0].unpack(), substs[1].unpack()) {
+ (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+ if self_ty == rhs_ty
+ && self_ty.is_ref()
+ && self_ty.peel_refs().is_primitive() =>
+ {
+ let mut num_refs = 0;
+ let mut tmp_ty = self_ty;
+ while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+ num_refs += 1;
+ tmp_ty = *inner_ty;
+ }
+ let deref = "*".repeat(num_refs);
+
+ if let Ok(call_str) = ccx.tcx.sess.source_map().span_to_snippet(span) {
+ if let Some(eq_idx) = call_str.find("==") {
+ if let Some(rhs_idx) =
+ call_str[(eq_idx + 2)..].find(|c: char| !c.is_whitespace())
+ {
+ let rhs_pos =
+ span.lo() + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+ let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+ err.multipart_suggestion(
+ "consider dereferencing here",
+ vec![
+ (span.shrink_to_lo(), deref.clone()),
+ (rhs_span, deref),
+ ],
+ Applicability::MachineApplicable,
+ );
}
}
}
- _ => {}
}
+ _ => {}
}
}
+
+ diag_trait(err, self_ty, trait_id)
}
- }
+ CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0015,
+ "cannot perform deref coercion on `{}` in {}s",
+ self_ty,
+ ccx.const_kind()
+ );
+
+ err.note(&format!("attempting to deref into `{}`", deref_target_ty));
+
+ // Check first whether the source is accessible (issue #87060)
+ if tcx.sess.source_map().span_to_snippet(deref_target).is_ok() {
+ err.span_note(deref_target, "deref defined here");
+ }
+
+ diag_trait(err, self_ty, tcx.lang_items().deref_trait().unwrap())
+ }
+ _ => struct_span_err!(
+ ccx.tcx.sess,
+ span,
+ E0015,
+ "cannot call non-const fn `{}` in {}s",
+ ccx.tcx.def_path_str_with_substs(callee, substs),
+ ccx.const_kind(),
+ ),
+ };
+
+ err.note(&format!(
+ "calls in {}s are limited to constant functions, \
+ tuple structs and tuple variants",
+ ccx.const_kind(),
+ ));
err
}
#[derive(Debug)]
pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
-impl NonConstOp for FnCallUnstable {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let FnCallUnstable(def_id, feature) = *self;
let mut err = ccx.tcx.sess.struct_span_err(
#[derive(Debug)]
pub struct FnPtrCast;
-impl NonConstOp for FnPtrCast {
- fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for FnPtrCast {
+ fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
}
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_fn_ptr_basics,
#[derive(Debug)]
pub struct Generator(pub hir::GeneratorKind);
-impl NonConstOp for Generator {
- fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for Generator {
+ fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
Status::Unstable(sym::const_async_blocks)
} else {
}
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg)
#[derive(Debug)]
pub struct HeapAllocation;
-impl NonConstOp for HeapAllocation {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_err!(
ccx.tcx.sess,
span,
#[derive(Debug)]
pub struct InlineAsm;
-impl NonConstOp for InlineAsm {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for InlineAsm {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
struct_span_err!(
ccx.tcx.sess,
span,
pub struct LiveDrop {
pub dropped_at: Option<Span>,
}
-impl NonConstOp for LiveDrop {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for LiveDrop {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_err!(
ccx.tcx.sess,
span,
/// A borrow of a type that contains an `UnsafeCell` somewhere. The borrow never escapes to
/// the final value of the constant.
pub struct TransientCellBorrow;
-impl NonConstOp for TransientCellBorrow {
- fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
+ fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_refs_to_cell)
}
fn importance(&self) -> DiagnosticImportance {
// not additionally emit a feature gate error if activating the feature gate won't work.
DiagnosticImportance::Secondary
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_refs_to_cell,
/// the final value of the constant, and thus we cannot allow this (for now). We may allow
/// it in the future for static items.
pub struct CellBorrow;
-impl NonConstOp for CellBorrow {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for CellBorrow {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_err!(
ccx.tcx.sess,
span,
/// static or const items.
pub struct MutBorrow(pub hir::BorrowKind);
-impl NonConstOp for MutBorrow {
- fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for MutBorrow {
+ fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Forbidden
}
DiagnosticImportance::Secondary
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let raw = match self.0 {
hir::BorrowKind::Raw => "raw ",
hir::BorrowKind::Ref => "",
#[derive(Debug)]
pub struct TransientMutBorrow(pub hir::BorrowKind);
-impl NonConstOp for TransientMutBorrow {
- fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
+ fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let raw = match self.0 {
hir::BorrowKind::Raw => "raw ",
hir::BorrowKind::Ref => "",
#[derive(Debug)]
pub struct MutDeref;
-impl NonConstOp for MutDeref {
- fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for MutDeref {
+ fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
DiagnosticImportance::Secondary
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_mut_refs,
/// A call to a `panic()` lang item where the first argument is _not_ a `&str`.
#[derive(Debug)]
pub struct PanicNonStr;
-impl NonConstOp for PanicNonStr {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
ccx.tcx.sess.struct_span_err(
span,
"argument to `panic!()` in a const context must have type `&str`",
/// allocation base addresses that are not known at compile-time.
#[derive(Debug)]
pub struct RawPtrComparison;
-impl NonConstOp for RawPtrComparison {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = ccx
.tcx
.sess
#[derive(Debug)]
pub struct RawMutPtrDeref;
-impl NonConstOp for RawMutPtrDeref {
+impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_mut_refs,
/// allocation base addresses that are not known at compile-time.
#[derive(Debug)]
pub struct RawPtrToIntCast;
-impl NonConstOp for RawPtrToIntCast {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = ccx
.tcx
.sess
/// An access to a (non-thread-local) `static`.
#[derive(Debug)]
pub struct StaticAccess;
-impl NonConstOp for StaticAccess {
- fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+impl<'tcx> NonConstOp<'tcx> for StaticAccess {
+ fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if let hir::ConstContext::Static(_) = ccx.const_kind() {
Status::Allowed
} else {
}
}
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = struct_span_err!(
ccx.tcx.sess,
span,
/// An access to a thread-local `static`.
#[derive(Debug)]
pub struct ThreadLocalAccess;
-impl NonConstOp for ThreadLocalAccess {
- fn build_error<'tcx>(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
struct_span_err!(
ccx.tcx.sess,
span,
#[derive(Debug)]
pub struct MutRef(pub mir::LocalKind);
- impl NonConstOp for MutRef {
- fn status_in_item(&self, _ccx: &ConstCx<'_, '_>) -> Status {
+ impl<'tcx> NonConstOp<'tcx> for MutRef {
+ fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_mut_refs)
}
}
}
- fn build_error<'tcx>(
- &self,
- ccx: &ConstCx<'_, 'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_mut_refs,
#[derive(Debug)]
pub struct FnPtr(pub mir::LocalKind);
- impl NonConstOp for FnPtr {
+ impl<'tcx> NonConstOp<'tcx> for FnPtr {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
}
}
- fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+ fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
}
}
- fn build_error<'tcx>(
- &self,
- ccx: &ConstCx<'_, 'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_fn_ptr_basics,
#[derive(Debug)]
pub struct ImplTrait;
- impl NonConstOp for ImplTrait {
+ impl<'tcx> NonConstOp<'tcx> for ImplTrait {
fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
Status::Unstable(sym::const_impl_trait)
}
- fn build_error<'tcx>(
- &self,
- ccx: &ConstCx<'_, 'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_impl_trait,
#[derive(Debug)]
pub struct TraitBound(pub mir::LocalKind);
- impl NonConstOp for TraitBound {
+ impl<'tcx> NonConstOp<'tcx> for TraitBound {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
}
}
- fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+ fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
}
}
- fn build_error<'tcx>(
- &self,
- ccx: &ConstCx<'_, 'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_trait_bound,
#[derive(Debug)]
pub struct DynTrait(pub mir::LocalKind);
- impl NonConstOp for DynTrait {
+ impl<'tcx> NonConstOp<'tcx> for DynTrait {
fn importance(&self) -> DiagnosticImportance {
match self.0 {
mir::LocalKind::Var | mir::LocalKind::Temp => DiagnosticImportance::Secondary,
}
}
- fn status_in_item(&self, ccx: &ConstCx<'_, '_>) -> Status {
+ fn status_in_item(&self, ccx: &ConstCx<'_, 'tcx>) -> Status {
if ccx.const_kind() != hir::ConstContext::ConstFn {
Status::Allowed
} else {
}
}
- fn build_error<'tcx>(
- &self,
- ccx: &ConstCx<'_, 'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
let mut err = feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_fn_trait_bound,
/// A trait bound with the `?const Trait` opt-out
#[derive(Debug)]
pub struct TraitBoundNotConst;
- impl NonConstOp for TraitBoundNotConst {
- fn status_in_item(&self, _: &ConstCx<'_, '_>) -> Status {
+ impl<'tcx> NonConstOp<'tcx> for TraitBoundNotConst {
+ fn status_in_item(&self, _: &ConstCx<'_, 'tcx>) -> Status {
Status::Unstable(sym::const_trait_bound_opt_out)
}
- fn build_error<'tcx>(
- &self,
- ccx: &ConstCx<'_, 'tcx>,
- span: Span,
- ) -> DiagnosticBuilder<'tcx> {
+ fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
feature_err(
&ccx.tcx.sess.parse_sess,
sym::const_trait_bound_opt_out,
pub fn in_any_value_of_ty<'tcx>(
cx: &ConstCx<'_, 'tcx>,
ty: Ty<'tcx>,
- error_occured: Option<ErrorReported>,
+ tainted_by_errors: Option<ErrorReported>,
) -> ConstQualifs {
ConstQualifs {
has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
needs_non_const_drop: NeedsNonConstDrop::in_any_value_of_ty(cx, ty),
custom_eq: CustomEq::in_any_value_of_ty(cx, ty),
- error_occured,
+ tainted_by_errors,
}
}
// Check the qualifs of the value of `const` items.
if let Some(ct) = constant.literal.const_for_ty() {
- if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) = ct.val {
+ if let ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) = ct.val() {
// Use qualifs of the type for the promoted. Promoteds in MIR body should be possible
// only for `NeedsNonConstDrop` with precise drop checking. This is the only const
// check performed after the promotion. Verify that with an assertion.
if matches!(kind, CastKind::Misc) {
let operand_ty = operand.ty(self.body, self.tcx);
let cast_in = CastTy::from_ty(operand_ty).expect("bad input type for cast");
- let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+ let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast");
if let (CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) = (cast_in, cast_out) {
// ptr-to-int casts are not possible in consts and thus not promotable
return Err(Unpromotable);
span,
user_ty: None,
literal: tcx
- .mk_const(ty::Const {
+ .mk_const(ty::ConstS {
ty,
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def,
vec![],
body.span,
body.generator_kind(),
+ body.tainted_by_errors,
);
let promoter = Promoter {
--- /dev/null
+//! Common logic for borrowck use-after-move errors when moved into a `fn(self)`,
+//! as well as errors when attempting to call a non-const function in a const
+//! context.
+
+use rustc_hir::def_id::DefId;
+use rustc_hir::lang_items::LangItemGroup;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{self, AssocItemContainer, DefIdTree, Instance, ParamEnv, Ty, TyCtxt};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, DesugaringKind, Span};
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum CallDesugaringKind {
+ /// for _ in x {} calls x.into_iter()
+ ForLoopIntoIter,
+ /// x? calls x.branch()
+ QuestionBranch,
+ /// x? calls type_of(x)::from_residual()
+ QuestionFromResidual,
+ /// try { ..; x } calls type_of(x)::from_output(x)
+ TryBlockFromOutput,
+}
+
+impl CallDesugaringKind {
+ pub fn trait_def_id(self, tcx: TyCtxt<'_>) -> DefId {
+ match self {
+ Self::ForLoopIntoIter => tcx.get_diagnostic_item(sym::IntoIterator).unwrap(),
+ Self::QuestionBranch | Self::TryBlockFromOutput => {
+ tcx.lang_items().try_trait().unwrap()
+ }
+ Self::QuestionFromResidual => tcx.get_diagnostic_item(sym::FromResidual).unwrap(),
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum CallKind<'tcx> {
+ /// A normal method call of the form `receiver.foo(a, b, c)`
+ Normal {
+ self_arg: Option<Ident>,
+ desugaring: Option<(CallDesugaringKind, Ty<'tcx>)>,
+ /// Whether the self type of the method call has an `.as_ref()` method.
+ /// Used for better diagnostics.
+ is_option_or_result: bool,
+ },
+ /// A call to `Fn(..)::call(..)`, desugared from `my_closure(a, b, c)`
+ FnCall { fn_trait_id: DefId, self_ty: Ty<'tcx> },
+ /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`)
+ Operator { self_arg: Option<Ident>, trait_id: DefId, self_ty: Ty<'tcx> },
+ DerefCoercion {
+ /// The `Span` of the `Target` associated type
+ /// in the `Deref` impl we are using.
+ deref_target: Span,
+ /// The type `T::Deref` we are dereferencing to
+ deref_target_ty: Ty<'tcx>,
+ self_ty: Ty<'tcx>,
+ },
+}
+
+pub fn call_kind<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ method_did: DefId,
+ method_substs: SubstsRef<'tcx>,
+ fn_call_span: Span,
+ from_hir_call: bool,
+ self_arg: Option<Ident>,
+) -> CallKind<'tcx> {
+ let parent = tcx.opt_associated_item(method_did).and_then(|assoc| match assoc.container {
+ AssocItemContainer::ImplContainer(impl_did) => tcx.trait_id_of_impl(impl_did),
+ AssocItemContainer::TraitContainer(trait_did) => Some(trait_did),
+ });
+
+ let fn_call = parent
+ .and_then(|p| tcx.lang_items().group(LangItemGroup::Fn).iter().find(|did| **did == p));
+
+ let operator = (!from_hir_call)
+ .then(|| parent)
+ .flatten()
+ .and_then(|p| tcx.lang_items().group(LangItemGroup::Op).iter().find(|did| **did == p));
+
+ let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
+
+ // Check for a 'special' use of 'self' -
+ // an FnOnce call, an operator (e.g. `<<`), or a
+ // deref coercion.
+ let kind = if let Some(&trait_id) = fn_call {
+ Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_substs.type_at(0) })
+ } else if let Some(&trait_id) = operator {
+ Some(CallKind::Operator { self_arg, trait_id, self_ty: method_substs.type_at(0) })
+ } else if is_deref {
+ let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
+ Instance::resolve(tcx, param_env, deref_target, method_substs).transpose()
+ });
+ if let Some(Ok(instance)) = deref_target {
+ let deref_target_ty = instance.ty(tcx, param_env);
+ Some(CallKind::DerefCoercion {
+ deref_target: tcx.def_span(instance.def_id()),
+ deref_target_ty,
+ self_ty: method_substs.type_at(0),
+ })
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ kind.unwrap_or_else(|| {
+ // This isn't a 'special' use of `self`
+ debug!(?method_did, ?fn_call_span);
+ let desugaring = if Some(method_did) == tcx.lang_items().into_iter_fn()
+ && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
+ {
+ Some((CallDesugaringKind::ForLoopIntoIter, method_substs.type_at(0)))
+ } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
+ if Some(method_did) == tcx.lang_items().branch_fn() {
+ Some((CallDesugaringKind::QuestionBranch, method_substs.type_at(0)))
+ } else if Some(method_did) == tcx.lang_items().from_residual_fn() {
+ Some((CallDesugaringKind::QuestionFromResidual, method_substs.type_at(0)))
+ } else {
+ None
+ }
+ } else if Some(method_did) == tcx.lang_items().from_output_fn()
+ && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
+ {
+ Some((CallDesugaringKind::TryBlockFromOutput, method_substs.type_at(0)))
+ } else {
+ None
+ };
+ let parent_self_ty = tcx
+ .parent(method_did)
+ .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
+ .and_then(|did| match tcx.type_of(did).kind() {
+ ty::Adt(def, ..) => Some(def.did),
+ _ => None,
+ });
+ let is_option_or_result = parent_self_ty.map_or(false, |def_id| {
+ matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
+ });
+ CallKind::Normal { self_arg, desugaring, is_option_or_result }
+ })
+}
pub mod aggregate;
mod alignment;
+mod call_kind;
pub mod collect_writes;
mod find_self_call;
pub use self::aggregate::expand_aggregate;
pub use self::alignment::is_disaligned;
+pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind};
pub use self::find_self_call::find_self_call;
--- /dev/null
+use std::cmp::Ordering;
+use std::hash::{Hash, Hasher};
+use std::ops::Deref;
+use std::ptr;
+
+mod private {
+ #[derive(Clone, Copy, Debug)]
+ pub struct PrivateZst;
+}
+
+/// A reference to a value that is interned, and is known to be unique.
+///
+/// Note that it is possible to have a `T` and a `Interned<T>` that are (or
+/// refer to) equal but different values. But if you have two different
+/// `Interned<T>`s, they both refer to the same value, at a single location in
+/// memory. This means that equality and hashing can be done on the value's
+/// address rather than the value's contents, which can improve performance.
+///
+/// The `PrivateZst` field means you can pattern match with `Interned(v, _)`
+/// but you can only construct a `Interned` with `new_unchecked`, and not
+/// directly.
+#[derive(Debug)]
+#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
+pub struct Interned<'a, T>(pub &'a T, pub private::PrivateZst);
+
+impl<'a, T> Interned<'a, T> {
+ /// Create a new `Interned` value. The value referred to *must* be interned
+ /// and thus be unique, and it *must* remain unique in the future. This
+ /// function has `_unchecked` in the name but is not `unsafe`, because if
+ /// the uniqueness condition is violated condition it will cause incorrect
+ /// behaviour but will not affect memory safety.
+ #[inline]
+ pub const fn new_unchecked(t: &'a T) -> Self {
+ Interned(t, private::PrivateZst)
+ }
+}
+
+impl<'a, T> Clone for Interned<'a, T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+impl<'a, T> Copy for Interned<'a, T> {}
+
+impl<'a, T> Deref for Interned<'a, T> {
+ type Target = T;
+
+ #[inline]
+ fn deref(&self) -> &T {
+ self.0
+ }
+}
+
+impl<'a, T> PartialEq for Interned<'a, T> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ // Pointer equality implies equality, due to the uniqueness constraint.
+ ptr::eq(self.0, other.0)
+ }
+}
+
+impl<'a, T> Eq for Interned<'a, T> {}
+
+// In practice you can't intern any `T` that doesn't implement `Eq`, because
+// that's needed for hashing. Therefore, we won't be interning any `T` that
+// implements `PartialOrd` without also implementing `Ord`. So we can have the
+// bound `T: Ord` here and avoid duplication with the `Ord` impl below.
+impl<'a, T: Ord> PartialOrd for Interned<'a, T> {
+ fn partial_cmp(&self, other: &Interned<'a, T>) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl<'a, T: Ord> Ord for Interned<'a, T> {
+ fn cmp(&self, other: &Interned<'a, T>) -> Ordering {
+ // Pointer equality implies equality, due to the uniqueness constraint,
+ // but the contents must be compared otherwise.
+ if ptr::eq(self.0, other.0) {
+ Ordering::Equal
+ } else {
+ let res = self.0.cmp(&other.0);
+ debug_assert_ne!(res, Ordering::Equal);
+ res
+ }
+ }
+}
+
+impl<'a, T> Hash for Interned<'a, T> {
+ #[inline]
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ // Pointer hashing is sufficient, due to the uniqueness constraint.
+ ptr::hash(self.0, s)
+ }
+}
+
+#[cfg(test)]
+mod tests;
--- /dev/null
+use super::*;
+use std::cmp::Ordering;
+
+#[derive(Debug)]
+struct S(u32);
+
+impl PartialEq for S {
+ fn eq(&self, _other: &Self) -> bool {
+ panic!("shouldn't be called");
+ }
+}
+
+impl Eq for S {}
+
+impl PartialOrd for S {
+ fn partial_cmp(&self, other: &S) -> Option<Ordering> {
+ // The `==` case should be handled by `Interned`.
+ assert_ne!(self.0, other.0);
+ self.0.partial_cmp(&other.0)
+ }
+}
+
+impl Ord for S {
+ fn cmp(&self, other: &S) -> Ordering {
+ // The `==` case should be handled by `Interned`.
+ assert_ne!(self.0, other.0);
+ self.0.cmp(&other.0)
+ }
+}
+
+#[test]
+fn test_uniq() {
+ let s1 = S(1);
+ let s2 = S(2);
+ let s3 = S(3);
+ let s4 = S(1); // violates uniqueness
+
+ let v1 = Interned::new_unchecked(&s1);
+ let v2 = Interned::new_unchecked(&s2);
+ let v3a = Interned::new_unchecked(&s3);
+ let v3b = Interned::new_unchecked(&s3);
+ let v4 = Interned::new_unchecked(&s4); // violates uniqueness
+
+ assert_ne!(v1, v2);
+ assert_ne!(v2, v3a);
+ assert_eq!(v1, v1);
+ assert_eq!(v3a, v3b);
+ assert_ne!(v1, v4); // same content but different addresses: not equal
+
+ assert_eq!(v1.cmp(&v2), Ordering::Less);
+ assert_eq!(v3a.cmp(&v2), Ordering::Greater);
+ assert_eq!(v1.cmp(&v1), Ordering::Equal); // only uses Interned::eq, not S::cmp
+ assert_eq!(v3a.cmp(&v3b), Ordering::Equal); // only uses Interned::eq, not S::cmp
+
+ assert_eq!(v1.partial_cmp(&v2), Some(Ordering::Less));
+ assert_eq!(v3a.partial_cmp(&v2), Some(Ordering::Greater));
+ assert_eq!(v1.partial_cmp(&v1), Some(Ordering::Equal)); // only uses Interned::eq, not S::cmp
+ assert_eq!(v3a.partial_cmp(&v3b), Some(Ordering::Equal)); // only uses Interned::eq, not S::cmp
+}
#![feature(type_alias_impl_trait)]
#![feature(new_uninit)]
#![feature(once_cell)]
+#![feature(rustc_attrs)]
#![feature(test)]
#![feature(thread_id_value)]
#![feature(vec_into_raw_parts)]
pub mod functor;
pub mod fx;
pub mod graph;
+pub mod intern;
pub mod jobserver;
pub mod macros;
pub mod map_in_place;
pub mod obligation_forest;
pub mod owning_ref;
-pub mod ptr_key;
pub mod sip128;
pub mod small_c_str;
pub mod snapshot_map;
+++ /dev/null
-use std::ops::Deref;
-use std::{hash, ptr};
-
-/// A wrapper around reference that compares and hashes like a pointer.
-/// Can be used as a key in sets/maps indexed by pointers to avoid `unsafe`.
-#[derive(Debug)]
-pub struct PtrKey<'a, T>(pub &'a T);
-
-impl<'a, T> Clone for PtrKey<'a, T> {
- fn clone(&self) -> Self {
- *self
- }
-}
-
-impl<'a, T> Copy for PtrKey<'a, T> {}
-
-impl<'a, T> PartialEq for PtrKey<'a, T> {
- fn eq(&self, rhs: &Self) -> bool {
- ptr::eq(self.0, rhs.0)
- }
-}
-
-impl<'a, T> Eq for PtrKey<'a, T> {}
-
-impl<'a, T> hash::Hash for PtrKey<'a, T> {
- fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
- (self.0 as *const T).hash(hasher)
- }
-}
-
-impl<'a, T> Deref for PtrKey<'a, T> {
- type Target = T;
-
- fn deref(&self) -> &Self::Target {
- self.0
- }
-}
hasher
}
- // A specialized write function for values with size <= 8.
#[inline]
- fn short_write<T>(&mut self, x: T) {
- let size = mem::size_of::<T>();
+ pub fn short_write<const LEN: usize>(&mut self, bytes: [u8; LEN]) {
let nbuf = self.nbuf;
- debug_assert!(size <= 8);
+ debug_assert!(LEN <= 8);
debug_assert!(nbuf < BUFFER_SIZE);
- debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE);
+ debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE);
- if nbuf + size < BUFFER_SIZE {
+ if nbuf + LEN < BUFFER_SIZE {
unsafe {
// The memcpy call is optimized away because the size is known.
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
- ptr::copy_nonoverlapping(&x as *const _ as *const u8, dst, size);
+ ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN);
}
- self.nbuf = nbuf + size;
+ self.nbuf = nbuf + LEN;
return;
}
- unsafe { self.short_write_process_buffer(x) }
+ unsafe { self.short_write_process_buffer(bytes) }
}
// A specialized write function for values with size <= 8 that should only
// `self.nbuf` must cause `self.buf` to become fully initialized (and not
// overflow) if it wasn't already.
#[inline(never)]
- unsafe fn short_write_process_buffer<T>(&mut self, x: T) {
- let size = mem::size_of::<T>();
+ unsafe fn short_write_process_buffer<const LEN: usize>(&mut self, bytes: [u8; LEN]) {
let nbuf = self.nbuf;
- debug_assert!(size <= 8);
+ debug_assert!(LEN <= 8);
debug_assert!(nbuf < BUFFER_SIZE);
- debug_assert!(nbuf + size >= BUFFER_SIZE);
- debug_assert!(nbuf + size < BUFFER_WITH_SPILL_SIZE);
+ debug_assert!(nbuf + LEN >= BUFFER_SIZE);
+ debug_assert!(nbuf + LEN < BUFFER_WITH_SPILL_SIZE);
// Copy first part of input into end of buffer, possibly into spill
// element. The memcpy call is optimized away because the size is known.
let dst = (self.buf.as_mut_ptr() as *mut u8).add(nbuf);
- ptr::copy_nonoverlapping(&x as *const _ as *const u8, dst, size);
+ ptr::copy_nonoverlapping(bytes.as_ptr(), dst, LEN);
// Process buffer.
for i in 0..BUFFER_CAPACITY {
self.state.v0 ^= elem;
}
- // Copy remaining input into start of buffer by copying size - 1
- // elements from spill (at most size - 1 bytes could have overflowed
+ // Copy remaining input into start of buffer by copying LEN - 1
+ // elements from spill (at most LEN - 1 bytes could have overflowed
// into the spill). The memcpy call is optimized away because the size
- // is known. And the whole copy is optimized away for size == 1.
+ // is known. And the whole copy is optimized away for LEN == 1.
let src = self.buf.get_unchecked(BUFFER_SPILL_INDEX) as *const _ as *const u8;
- ptr::copy_nonoverlapping(src, self.buf.as_mut_ptr() as *mut u8, size - 1);
+ ptr::copy_nonoverlapping(src, self.buf.as_mut_ptr() as *mut u8, LEN - 1);
// This function should only be called when the write fills the buffer.
- // Therefore, when size == 1, the new `self.nbuf` must be zero. The size
- // is statically known, so the branch is optimized away.
- self.nbuf = if size == 1 { 0 } else { nbuf + size - BUFFER_SIZE };
+ // Therefore, when LEN == 1, the new `self.nbuf` must be zero.
+ // LEN is statically known, so the branch is optimized away.
+ self.nbuf = if LEN == 1 { 0 } else { nbuf + LEN - BUFFER_SIZE };
self.processed += BUFFER_SIZE;
}
impl Hasher for SipHasher128 {
#[inline]
fn write_u8(&mut self, i: u8) {
- self.short_write(i);
+ self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_u16(&mut self, i: u16) {
- self.short_write(i);
+ self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_u32(&mut self, i: u32) {
- self.short_write(i);
+ self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_u64(&mut self, i: u64) {
- self.short_write(i);
+ self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_usize(&mut self, i: usize) {
- self.short_write(i);
+ self.short_write(i.to_ne_bytes());
}
#[inline]
fn write_i8(&mut self, i: i8) {
- self.short_write(i as u8);
+ self.short_write((i as u8).to_ne_bytes());
}
#[inline]
fn write_i16(&mut self, i: i16) {
- self.short_write(i as u16);
+ self.short_write((i as u16).to_ne_bytes());
}
#[inline]
fn write_i32(&mut self, i: i32) {
- self.short_write(i as u32);
+ self.short_write((i as u32).to_ne_bytes());
}
#[inline]
fn write_i64(&mut self, i: i64) {
- self.short_write(i as u64);
+ self.short_write((i as u64).to_ne_bytes());
}
#[inline]
fn write_isize(&mut self, i: isize) {
- self.short_write(i as usize);
+ self.short_write((i as usize).to_ne_bytes());
}
#[inline]
pub type SnapshotMapStorage<K, V> = SnapshotMap<K, V, FxHashMap<K, V>, ()>;
pub type SnapshotMapRef<'a, K, V, L> = SnapshotMap<K, V, &'a mut FxHashMap<K, V>, &'a mut L>;
+#[derive(Clone)]
pub struct SnapshotMap<K, V, M = FxHashMap<K, V>, L = VecLog<UndoLog<K, V>>> {
map: M,
undo_log: L,
}
}
+#[derive(Clone)]
pub enum UndoLog<K, V> {
Inserted(K),
Overwrite(K, V),
#[inline]
fn write_u16(&mut self, i: u16) {
- self.state.write_u16(i.to_le());
+ self.state.short_write(i.to_le_bytes());
}
#[inline]
fn write_u32(&mut self, i: u32) {
- self.state.write_u32(i.to_le());
+ self.state.short_write(i.to_le_bytes());
}
#[inline]
fn write_u64(&mut self, i: u64) {
- self.state.write_u64(i.to_le());
+ self.state.short_write(i.to_le_bytes());
}
#[inline]
fn write_u128(&mut self, i: u128) {
- self.state.write_u128(i.to_le());
+ self.state.write(&i.to_le_bytes());
}
#[inline]
// Always treat usize as u64 so we get the same results on 32 and 64 bit
// platforms. This is important for symbol hashes when cross compiling,
// for example.
- self.state.write_u64((i as u64).to_le());
+ self.state.short_write((i as u64).to_le_bytes());
}
#[inline]
#[inline]
fn write_i16(&mut self, i: i16) {
- self.state.write_i16(i.to_le());
+ self.state.short_write((i as u16).to_le_bytes());
}
#[inline]
fn write_i32(&mut self, i: i32) {
- self.state.write_i32(i.to_le());
+ self.state.short_write((i as u32).to_le_bytes());
}
#[inline]
fn write_i64(&mut self, i: i64) {
- self.state.write_i64(i.to_le());
+ self.state.short_write((i as u64).to_le_bytes());
}
#[inline]
fn write_i128(&mut self, i: i128) {
- self.state.write_i128(i.to_le());
+ self.state.write(&(i as u128).to_le_bytes());
}
#[inline]
fn write_isize(&mut self, i: isize) {
- // Always treat isize as i64 so we get the same results on 32 and 64 bit
+ // Always treat isize as a 64-bit number so we get the same results on 32 and 64 bit
// platforms. This is important for symbol hashes when cross compiling,
// for example. Sign extending here is preferable as it means that the
// same negative number hashes the same on both 32 and 64 bit platforms.
- let value = (i as i64).to_le() as u64;
+ let value = i as u64;
// Cold path
#[cold]
#[inline(never)]
fn hash_value(state: &mut SipHasher128, value: u64) {
state.write_u8(0xFF);
- state.write_u64(value);
+ state.short_write(value.to_le_bytes());
}
// `isize` values often seem to have a small (positive) numeric value in practice.
check_hash(0xAAAA, 0xAAAAAA);
check_hash(0xAAAAAA, 0xAAAAAAAA);
check_hash(0xFF, 0xFFFFFFFFFFFFFFFF);
+ check_hash(u64::MAX /* -1 */, 1);
}
use rustc_metadata::locator;
use rustc_save_analysis as save;
use rustc_save_analysis::DumpHandler;
-use rustc_serialize::json::{self, ToJson};
+use rustc_serialize::json::ToJson;
use rustc_session::config::{nightly_options, CG_OPTIONS, DB_OPTIONS};
use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
use rustc_session::cstore::MetadataLoader;
const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
-const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["Z", "C", "crate-type"];
+const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];
output_dir: odir,
file_loader,
diagnostic_output,
- stderr: None,
lint_caps: Default::default(),
parse_sess_created: None,
register_lints: None,
describe_lints(compiler.session(), &lint_store, registered_lints);
return;
}
- let should_stop = RustcDefaultCalls::print_crate_info(
+ let should_stop = print_crate_info(
&***compiler.codegen_backend(),
compiler.session(),
None,
interface::run_compiler(config, |compiler| {
let sess = compiler.session();
- let should_stop = RustcDefaultCalls::print_crate_info(
+ let should_stop = print_crate_info(
&***compiler.codegen_backend(),
sess,
Some(compiler.input()),
compiler.temps_dir(),
)
.and_then(|| {
- RustcDefaultCalls::list_metadata(
- sess,
- &*compiler.codegen_backend().metadata_loader(),
- compiler.input(),
- )
+ list_metadata(sess, &*compiler.codegen_backend().metadata_loader(), compiler.input())
})
- .and_then(|| RustcDefaultCalls::try_process_rlink(sess, compiler));
+ .and_then(|| try_process_rlink(sess, compiler));
if should_stop == Compilation::Stop {
return sess.compile_status();
}
}
-/// CompilerCalls instance for a regular rustc build.
-#[derive(Copy, Clone)]
-pub struct RustcDefaultCalls;
-
fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
let upper_cased_code = code.to_ascii_uppercase();
let normalised = if upper_cased_code.starts_with('E') {
}
}
-impl RustcDefaultCalls {
- pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
- if sess.opts.debugging_opts.link_only {
- if let Input::File(file) = compiler.input() {
- // FIXME: #![crate_type] and #![crate_name] support not implemented yet
- sess.init_crate_types(collect_crate_types(sess, &[]));
- let outputs = compiler.build_output_filenames(sess, &[]);
- let rlink_data = fs::read_to_string(file).unwrap_or_else(|err| {
- sess.fatal(&format!("failed to read rlink file: {}", err));
- });
- let codegen_results: CodegenResults = json::decode(&rlink_data);
- let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
- abort_on_err(result, sess);
- } else {
- sess.fatal("rlink must be a file")
- }
- Compilation::Stop
+pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
+ if sess.opts.debugging_opts.link_only {
+ if let Input::File(file) = compiler.input() {
+ // FIXME: #![crate_type] and #![crate_name] support not implemented yet
+ sess.init_crate_types(collect_crate_types(sess, &[]));
+ let outputs = compiler.build_output_filenames(sess, &[]);
+ let rlink_data = fs::read(file).unwrap_or_else(|err| {
+ sess.fatal(&format!("failed to read rlink file: {}", err));
+ });
+ let mut decoder = rustc_serialize::opaque::Decoder::new(&rlink_data, 0);
+ let codegen_results: CodegenResults = rustc_serialize::Decodable::decode(&mut decoder);
+ let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
+ abort_on_err(result, sess);
} else {
- Compilation::Continue
+ sess.fatal("rlink must be a file")
}
+ Compilation::Stop
+ } else {
+ Compilation::Continue
}
+}
- pub fn list_metadata(
- sess: &Session,
- metadata_loader: &dyn MetadataLoader,
- input: &Input,
- ) -> Compilation {
- if sess.opts.debugging_opts.ls {
- match *input {
- Input::File(ref ifile) => {
- let path = &(*ifile);
- let mut v = Vec::new();
- locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v)
- .unwrap();
- println!("{}", String::from_utf8(v).unwrap());
- }
- Input::Str { .. } => {
- early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
- }
+pub fn list_metadata(
+ sess: &Session,
+ metadata_loader: &dyn MetadataLoader,
+ input: &Input,
+) -> Compilation {
+ if sess.opts.debugging_opts.ls {
+ match *input {
+ Input::File(ref ifile) => {
+ let path = &(*ifile);
+ let mut v = Vec::new();
+ locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
+ println!("{}", String::from_utf8(v).unwrap());
+ }
+ Input::Str { .. } => {
+ early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
}
- return Compilation::Stop;
}
-
- Compilation::Continue
+ return Compilation::Stop;
}
- fn print_crate_info(
- codegen_backend: &dyn CodegenBackend,
- sess: &Session,
- input: Option<&Input>,
- odir: &Option<PathBuf>,
- ofile: &Option<PathBuf>,
- temps_dir: &Option<PathBuf>,
- ) -> Compilation {
- use rustc_session::config::PrintRequest::*;
- // NativeStaticLibs and LinkArgs are special - printed during linking
- // (empty iterator returns true)
- if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
- return Compilation::Continue;
- }
+ Compilation::Continue
+}
- let attrs = match input {
- None => None,
- Some(input) => {
- let result = parse_crate_attrs(sess, input);
- match result {
- Ok(attrs) => Some(attrs),
- Err(mut parse_error) => {
- parse_error.emit();
- return Compilation::Stop;
- }
+fn print_crate_info(
+ codegen_backend: &dyn CodegenBackend,
+ sess: &Session,
+ input: Option<&Input>,
+ odir: &Option<PathBuf>,
+ ofile: &Option<PathBuf>,
+ temps_dir: &Option<PathBuf>,
+) -> Compilation {
+ use rustc_session::config::PrintRequest::*;
+ // NativeStaticLibs and LinkArgs are special - printed during linking
+ // (empty iterator returns true)
+ if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
+ return Compilation::Continue;
+ }
+
+ let attrs = match input {
+ None => None,
+ Some(input) => {
+ let result = parse_crate_attrs(sess, input);
+ match result {
+ Ok(attrs) => Some(attrs),
+ Err(mut parse_error) => {
+ parse_error.emit();
+ return Compilation::Stop;
}
}
- };
- for req in &sess.opts.prints {
- match *req {
- TargetList => {
- let mut targets =
- rustc_target::spec::TARGETS.iter().copied().collect::<Vec<_>>();
- targets.sort_unstable();
- println!("{}", targets.join("\n"));
- }
- Sysroot => println!("{}", sess.sysroot.display()),
- TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
- TargetSpec => println!("{}", sess.target.to_json().pretty()),
- FileNames | CrateName => {
- let input = input.unwrap_or_else(|| {
- early_error(ErrorOutputType::default(), "no input file provided")
- });
- let attrs = attrs.as_ref().unwrap();
- let t_outputs = rustc_interface::util::build_output_filenames(
- input, odir, ofile, temps_dir, attrs, sess,
- );
- let id = rustc_session::output::find_crate_name(sess, attrs, input);
- if *req == PrintRequest::CrateName {
- println!("{}", id);
- continue;
- }
- let crate_types = collect_crate_types(sess, attrs);
- for &style in &crate_types {
- let fname =
- rustc_session::output::filename_for_input(sess, style, &id, &t_outputs);
- println!("{}", fname.file_name().unwrap().to_string_lossy());
- }
+ }
+ };
+ for req in &sess.opts.prints {
+ match *req {
+ TargetList => {
+ let mut targets = rustc_target::spec::TARGETS.iter().copied().collect::<Vec<_>>();
+ targets.sort_unstable();
+ println!("{}", targets.join("\n"));
+ }
+ Sysroot => println!("{}", sess.sysroot.display()),
+ TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
+ TargetSpec => println!("{}", sess.target.to_json().pretty()),
+ FileNames | CrateName => {
+ let input = input.unwrap_or_else(|| {
+ early_error(ErrorOutputType::default(), "no input file provided")
+ });
+ let attrs = attrs.as_ref().unwrap();
+ let t_outputs = rustc_interface::util::build_output_filenames(
+ input, odir, ofile, temps_dir, attrs, sess,
+ );
+ let id = rustc_session::output::find_crate_name(sess, attrs, input);
+ if *req == PrintRequest::CrateName {
+ println!("{}", id);
+ continue;
}
- Cfg => {
- let mut cfgs = sess
- .parse_sess
- .config
- .iter()
- .filter_map(|&(name, value)| {
- // Note that crt-static is a specially recognized cfg
- // directive that's printed out here as part of
- // rust-lang/rust#37406, but in general the
- // `target_feature` cfg is gated under
- // rust-lang/rust#29717. For now this is just
- // specifically allowing the crt-static cfg and that's
- // it, this is intended to get into Cargo and then go
- // through to build scripts.
- if (name != sym::target_feature || value != Some(sym::crt_dash_static))
- && !sess.is_nightly_build()
- && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
- {
- return None;
- }
-
- if let Some(value) = value {
- Some(format!("{}=\"{}\"", name, value))
- } else {
- Some(name.to_string())
- }
- })
- .collect::<Vec<String>>();
-
- cfgs.sort();
- for cfg in cfgs {
- println!("{}", cfg);
- }
+ let crate_types = collect_crate_types(sess, attrs);
+ for &style in &crate_types {
+ let fname =
+ rustc_session::output::filename_for_input(sess, style, &id, &t_outputs);
+ println!("{}", fname.file_name().unwrap().to_string_lossy());
}
- RelocationModels
- | CodeModels
- | TlsModels
- | TargetCPUs
- | StackProtectorStrategies
- | TargetFeatures => {
- codegen_backend.print(*req, sess);
+ }
+ Cfg => {
+ let mut cfgs = sess
+ .parse_sess
+ .config
+ .iter()
+ .filter_map(|&(name, value)| {
+ // Note that crt-static is a specially recognized cfg
+ // directive that's printed out here as part of
+ // rust-lang/rust#37406, but in general the
+ // `target_feature` cfg is gated under
+ // rust-lang/rust#29717. For now this is just
+ // specifically allowing the crt-static cfg and that's
+ // it, this is intended to get into Cargo and then go
+ // through to build scripts.
+ if (name != sym::target_feature || value != Some(sym::crt_dash_static))
+ && !sess.is_nightly_build()
+ && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
+ {
+ return None;
+ }
+
+ if let Some(value) = value {
+ Some(format!("{}=\"{}\"", name, value))
+ } else {
+ Some(name.to_string())
+ }
+ })
+ .collect::<Vec<String>>();
+
+ cfgs.sort();
+ for cfg in cfgs {
+ println!("{}", cfg);
}
- // Any output here interferes with Cargo's parsing of other printed output
- NativeStaticLibs => {}
- LinkArgs => {}
}
+ RelocationModels
+ | CodeModels
+ | TlsModels
+ | TargetCPUs
+ | StackProtectorStrategies
+ | TargetFeatures => {
+ codegen_backend.print(*req, sess);
+ }
+ // Any output here interferes with Cargo's parsing of other printed output
+ NativeStaticLibs => {}
+ LinkArgs => {}
}
- Compilation::Stop
}
+ Compilation::Stop
}
/// Prints version information
/// debugging, since some ICEs only happens with non-default compiler flags
/// (and the users don't always report them).
fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
- let args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).collect::<Vec<_>>();
-
- // Avoid printing help because of empty args. This can suggest the compiler
- // itself is not the program root (consider RLS).
- if args.len() < 2 {
- return None;
- }
+ let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
- let matches = handle_options(&args)?;
let mut result = Vec::new();
let mut excluded_cargo_defaults = false;
- for flag in ICE_REPORT_COMPILER_FLAGS {
- let prefix = if flag.len() == 1 { "-" } else { "--" };
-
- for content in &matches.opt_strs(flag) {
- // Split always returns the first element
- let name = if let Some(first) = content.split('=').next() { first } else { &content };
-
- let content =
- if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) { name } else { content };
-
- if !ICE_REPORT_COMPILER_FLAGS_EXCLUDE.contains(&name) {
- result.push(format!("{}{} {}", prefix, flag, content));
+ while let Some(arg) = args.next() {
+ if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
+ let content = if arg.len() == a.len() {
+ match args.next() {
+ Some(arg) => arg.to_string(),
+ None => continue,
+ }
+ } else if arg.get(a.len()..a.len() + 1) == Some("=") {
+ arg[a.len() + 1..].to_string()
} else {
+ arg[a.len()..].to_string()
+ };
+ if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| content.starts_with(exc)) {
excluded_cargo_defaults = true;
+ } else {
+ result.push(a.to_string());
+ match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| content.starts_with(*s))
+ {
+ Some(s) => result.push(s.to_string()),
+ None => result.push(content),
+ }
}
}
}
///
/// A custom rustc driver can skip calling this to set up a custom ICE hook.
pub fn install_ice_hook() {
+ // If the user has not explicitly overriden "RUST_BACKTRACE", then produce
+ // full backtraces. When a compiler ICE happens, we want to gather
+ // as much information as possible to present in the issue opened
+ // by the user. Compiler developers and other rustc users can
+ // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
+ // (e.g. `RUST_BACKTRACE=1`)
+ if std::env::var("RUST_BACKTRACE").is_err() {
+ std::env::set_var("RUST_BACKTRACE", "full");
+ }
SyncLazy::force(&DEFAULT_HOOK);
}
E0185: include_str!("./error_codes/E0185.md"),
E0186: include_str!("./error_codes/E0186.md"),
E0191: include_str!("./error_codes/E0191.md"),
+E0192: include_str!("./error_codes/E0192.md"),
E0193: include_str!("./error_codes/E0193.md"),
E0195: include_str!("./error_codes/E0195.md"),
E0197: include_str!("./error_codes/E0197.md"),
// E0188, // can not cast an immutable reference to a mutable pointer
// E0189, // deprecated: can only cast a boxed pointer to a boxed object
// E0190, // deprecated: can only cast a &-pointer to an &-object
-// E0192, // negative impl only applicable to auto traits
// E0194, // merged into E0403
// E0196, // cannot determine a type for this closure
E0208,
+#### Note: this error code is no longer emitted by the compiler.
+
A negative impl was added on a trait implementation.
Erroneous code example:
-```compile_fail,E0192
+```compile_fail
trait Trait {
type Bar;
}
struct Foo;
-impl !Trait for Foo { } //~ ERROR E0192
+impl !Trait for Foo { } //~ ERROR
fn main() {}
```
0u32 as char; // error: only `u8` can be cast as `char`, not `u32`
```
-As the error message indicates, only `u8` can be cast into `char`. Example:
+`char` is a Unicode Scalar Value, an integer value from 0 to 0xD7FF and
+0xE000 to 0x10FFFF. (The gap is for surrogate pairs.) Only `u8` always fits in
+those ranges so only `u8` may be cast to `char`.
+
+To allow larger values, use `char::from_u32`, which checks the value is valid.
```
-let c = 86u8 as char; // ok!
-assert_eq!(c, 'V');
+assert_eq!(86u8 as char, 'V'); // ok!
+assert_eq!(char::from_u32(0x3B1), Some('α')); // ok!
+assert_eq!(char::from_u32(0xD800), None); // not a USV.
```
For more information about casts, take a look at the Type cast section in
(active, staged_api, "1.0.0", None, None),
/// Added for testing E0705; perma-unstable.
(active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)),
+ /// Allows non-`unsafe` —and thus, unsound— access to `Pin` constructions.
+ /// Marked `incomplete` since perma-unstable and unsound.
+ (incomplete, unsafe_pin_internals, "1.60.0", None, None),
/// Use for stable + negative coherence and strict coherence depending on trait's
/// rustc_strict_coherence value.
(active, with_negative_coherence, "1.60.0", None, None),
(incomplete, adt_const_params, "1.56.0", Some(44580), None),
/// Allows defining an `#[alloc_error_handler]`.
(active, alloc_error_handler, "1.29.0", Some(51540), None),
- /// Allows a test to fail without failing the whole suite.
- (active, allow_fail, "1.19.0", Some(46488), None),
/// Allows explicit discriminants on non-unit enum variants.
(active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None),
/// Allows trait methods with arbitrary self types.
(active, arbitrary_self_types, "1.23.0", Some(44874), None),
/// Allows using `const` operands in inline assembly.
- (active, asm_const, "1.58.0", Some(72016), None),
+ (active, asm_const, "1.58.0", Some(93332), None),
/// Enables experimental inline assembly support for additional architectures.
- (active, asm_experimental_arch, "1.58.0", Some(72016), None),
+ (active, asm_experimental_arch, "1.58.0", Some(93335), None),
/// Allows using `sym` operands in inline assembly.
- (active, asm_sym, "1.58.0", Some(72016), None),
+ (active, asm_sym, "1.58.0", Some(93333), None),
/// Allows the `may_unwind` option in inline assembly.
- (active, asm_unwind, "1.58.0", Some(72016), None),
+ (active, asm_unwind, "1.58.0", Some(93334), None),
/// Allows users to enforce equality of associated constants `TraitImpl<AssocConst=3>`.
(active, associated_const_equality, "1.58.0", Some(92827), None),
/// Allows the user of associated type bounds.
(active, cfg_sanitize, "1.41.0", Some(39699), None),
/// Allows `cfg(target_abi = "...")`.
(active, cfg_target_abi, "1.55.0", Some(80970), None),
- /// Allows `cfg(target_has_atomic = "...")`.
- (active, cfg_target_has_atomic, "1.9.0", Some(32976), None),
+ /// Allows `cfg(target_has_atomic_load_store = "...")`.
+ (active, cfg_target_has_atomic, "1.60.0", Some(94039), None),
+ /// Allows `cfg(target_has_atomic_equal_alignment = "...")`.
+ (active, cfg_target_has_atomic_equal_alignment, "1.60.0", Some(93822), None),
/// Allows `cfg(target_thread_local)`.
(active, cfg_target_thread_local, "1.7.0", Some(29594), None),
/// Allow conditional compilation depending on rust version
/// Allows using and casting function pointers in a `const fn`.
(active, const_fn_fn_ptr_basics, "1.48.0", Some(57563), None),
/// Allows trait bounds in `const fn`.
- (active, const_fn_trait_bound, "1.53.0", Some(57563), None),
+ (active, const_fn_trait_bound, "1.53.0", Some(93706), None),
/// Allows `for _ in _` loops in const contexts.
(active, const_for, "1.56.0", Some(87575), None),
/// Allows argument and return position `impl Trait` in a `const fn`.
///
/// NOTE: A limited form of `union U { ... }` was accepted in 1.19.0.
(active, untagged_unions, "1.13.0", Some(55149), None),
+ /// Allows using the `#[used(linker)]` (or `#[used(compiler)]`) attribute.
+ (active, used_with_arg, "1.60.0", Some(93798), None),
/// Allows `extern "wasm" fn`
(active, wasm_abi, "1.53.0", Some(83788), None),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// (name in cfg, feature, function to check if the feature is enabled)
(sym::target_abi, sym::cfg_target_abi, cfg_fn!(cfg_target_abi)),
(sym::target_thread_local, sym::cfg_target_thread_local, cfg_fn!(cfg_target_thread_local)),
- (sym::target_has_atomic, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
- (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
(
sym::target_has_atomic_equal_alignment,
- sym::cfg_target_has_atomic,
- cfg_fn!(cfg_target_has_atomic),
+ sym::cfg_target_has_atomic_equal_alignment,
+ cfg_fn!(cfg_target_has_atomic_equal_alignment),
),
+ (sym::target_has_atomic_load_store, sym::cfg_target_has_atomic, cfg_fn!(cfg_target_has_atomic)),
(sym::sanitize, sym::cfg_sanitize, cfg_fn!(cfg_sanitize)),
(sym::version, sym::cfg_version, cfg_fn!(cfg_version)),
(sym::panic, sym::cfg_panic, cfg_fn!(cfg_panic)),
ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding),
ungated!(no_mangle, Normal, template!(Word), WarnFollowing),
- ungated!(used, Normal, template!(Word), WarnFollowing),
+ ungated!(used, Normal, template!(Word, List: "compiler|linker"), WarnFollowing),
// Limits:
ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N"), FutureWarnFollowing),
),
// Entry point:
- ungated!(main, Normal, template!(Word), WarnFollowing),
ungated!(start, Normal, template!(Word), WarnFollowing),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
},
// Testing:
- gated!(allow_fail, Normal, template!(Word), WarnFollowing, experimental!(allow_fail)),
gated!(
test_runner, CrateLevel, template!(List: "path"), ErrorFollowing, custom_test_frameworks,
"custom test frameworks are an unstable feature",
(removed, advanced_slice_patterns, "1.0.0", Some(62254), None,
Some("merged into `#![feature(slice_patterns)]`")),
(removed, allocator, "1.0.0", None, None, None),
+ /// Allows a test to fail without failing the whole suite.
+ (removed, allow_fail, "1.19.0", Some(46488), None, Some("removed due to no clear use cases")),
(removed, await_macro, "1.38.0", Some(50547), None,
Some("subsumed by `.await` syntax")),
/// Allows comparing raw pointers during const eval.
///
/// **Belongs to the type namespace.**
PrimTy(hir::PrimTy),
- /// The `Self` type, optionally with the trait it is associated with
- /// and optionally with the [`DefId`] of the impl it is associated with.
+ /// The `Self` type, optionally with the [`DefId`] of the trait it belongs to and
+ /// optionally with the [`DefId`] of the item introducing the `Self` type alias.
///
/// **Belongs to the type namespace.**
///
- /// For example, the `Self` in
- ///
+ /// Examples:
/// ```
+ /// struct Bar(Box<Self>);
+ /// // `Res::SelfTy { trait_: None, alias_of: Some(Bar) }`
+ ///
/// trait Foo {
/// fn foo() -> Box<Self>;
+ /// // `Res::SelfTy { trait_: Some(Foo), alias_of: None }`
/// }
- /// ```
- ///
- /// would have the [`DefId`] of `Foo` associated with it. The `Self` in
- ///
- /// ```
- /// struct Bar;
///
/// impl Bar {
- /// fn new() -> Self { Bar }
+ /// fn blah() {
+ /// let _: Self;
+ /// // `Res::SelfTy { trait_: None, alias_of: Some(::{impl#0}) }`
+ /// }
/// }
- /// ```
- ///
- /// would have the [`DefId`] of the impl associated with it. Finally, the `Self` in
///
- /// ```
/// impl Foo for Bar {
- /// fn foo() -> Box<Self> { Box::new(Bar) }
+ /// fn foo() -> Box<Self> {
+ /// // `Res::SelfTy { trait_: Some(Foo), alias_of: Some(::{impl#1}) }`
+ /// let _: Self;
+ /// // `Res::SelfTy { trait_: Some(Foo), alias_of: Some(::{impl#1}) }`
+ ///
+ /// todo!()
+ /// }
/// }
/// ```
///
- /// would have both the [`DefId`] of `Foo` and the [`DefId`] of the impl
- /// associated with it.
- ///
/// *See also [`Res::SelfCtor`].*
///
/// -----
///
- /// HACK(min_const_generics): impl self types also have an optional requirement to **not** mention
+ /// HACK(min_const_generics): self types also have an optional requirement to **not** mention
/// any generic parameters to allow the following with `min_const_generics`:
/// ```
/// impl Foo { fn test() -> [u8; std::mem::size_of::<Self>()] { todo!() } }
+ ///
+ /// struct Bar([u8; baz::<Self>()]);
+ /// const fn baz<T>() -> usize { 10 }
/// ```
/// We do however allow `Self` in repeat expression even if it is generic to not break code
- /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint.
- ///
- /// FIXME(generic_const_exprs): Remove this bodge once that feature is stable.
- SelfTy(
- /// Optionally, the trait associated with this `Self` type.
- Option<DefId>,
- /// Optionally, the impl associated with this `Self` type.
- Option<(DefId, bool)>,
- ),
+ /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint:
+ /// ```
+ /// fn foo<T>() {
+ /// let _bar = [1_u8; std::mem::size_of::<*mut T>()];
+ /// }
+ /// ```
+ // FIXME(generic_const_exprs): Remove this bodge once that feature is stable.
+ SelfTy {
+ /// The trait this `Self` is a generic arg for.
+ trait_: Option<DefId>,
+ /// The item introducing the `Self` type alias. Can be used in the `type_of` query
+ /// to get the underlying type. Additionally whether the `Self` type is disallowed
+ /// from mentioning generics (i.e. when used in an anonymous constant).
+ alias_to: Option<(DefId, bool)>,
+ },
/// A tool attribute module; e.g., the `rustfmt` in `#[rustfmt::skip]`.
///
/// **Belongs to the type namespace.**
Res::Local(..)
| Res::PrimTy(..)
- | Res::SelfTy(..)
+ | Res::SelfTy { .. }
| Res::SelfCtor(..)
| Res::ToolMod
| Res::NonMacroAttr(..)
Res::SelfCtor(..) => "self constructor",
Res::PrimTy(..) => "builtin type",
Res::Local(..) => "local variable",
- Res::SelfTy(..) => "self type",
+ Res::SelfTy { .. } => "self type",
Res::ToolMod => "tool module",
Res::NonMacroAttr(attr_kind) => attr_kind.descr(),
Res::Err => "unresolved item",
Res::SelfCtor(id) => Res::SelfCtor(id),
Res::PrimTy(id) => Res::PrimTy(id),
Res::Local(id) => Res::Local(map(id)),
- Res::SelfTy(a, b) => Res::SelfTy(a, b),
+ Res::SelfTy { trait_, alias_to } => Res::SelfTy { trait_, alias_to },
Res::ToolMod => Res::ToolMod,
Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
Res::Err => Res::Err,
pub fn ns(&self) -> Option<Namespace> {
match self {
Res::Def(kind, ..) => kind.ns(),
- Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS),
+ Res::PrimTy(..) | Res::SelfTy { .. } | Res::ToolMod => Some(Namespace::TypeNS),
Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
Res::Err => None,
_ => return false,
};
match path.res {
- Res::Def(DefKind::TyParam, def_id) | Res::SelfTy(Some(def_id), None) => {
- def_id == param_def_id
- }
+ Res::Def(DefKind::TyParam, def_id)
+ | Res::SelfTy { trait_: Some(def_id), alias_to: None } => def_id == param_def_id,
_ => false,
}
}
pub ident: Ident,
pub def_id: LocalDefId,
pub vis: Visibility<'hir>,
- pub defaultness: Defaultness,
pub generics: Generics<'hir>,
pub kind: ImplItemKind<'hir>,
pub span: Span,
rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);
- rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 152);
+ rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 144);
rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 136);
}
// Used when no map is actually available, forcing manual implementation of nested visitors.
impl<'hir> Map<'hir> for ! {
fn find(&self, _: HirId) -> Option<Node<'hir>> {
- unreachable!()
+ *self;
}
fn body(&self, _: BodyId) -> &'hir Body<'hir> {
- unreachable!()
+ *self;
}
fn item(&self, _: ItemId) -> &'hir Item<'hir> {
- unreachable!()
+ *self;
}
fn trait_item(&self, _: TraitItemId) -> &'hir TraitItem<'hir> {
- unreachable!()
+ *self;
}
fn impl_item(&self, _: ImplItemId) -> &'hir ImplItem<'hir> {
- unreachable!()
+ *self;
}
fn foreign_item(&self, _: ForeignItemId) -> &'hir ForeignItem<'hir> {
- unreachable!()
+ *self;
}
}
pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) {
// N.B., deliberately force a compilation error if/when new fields are added.
- let ImplItem { def_id: _, ident, ref vis, ref defaultness, ref generics, ref kind, span: _ } =
- *impl_item;
+ let ImplItem { def_id: _, ident, ref vis, ref generics, ref kind, span: _ } = *impl_item;
visitor.visit_ident(ident);
visitor.visit_vis(vis);
- visitor.visit_defaultness(defaultness);
visitor.visit_generics(generics);
match *kind {
ImplItemKind::Const(ref ty, body) => {
///
/// 1. **Shallow visit**: Get a simple callback for every item (or item-like thing) in the HIR.
/// - Example: find all items with a `#[foo]` attribute on them.
-/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().krate().visit_all_item_likes()`.
+/// - How: Implement `ItemLikeVisitor` and call `tcx.hir().visit_all_item_likes()`.
/// - Pro: Efficient; just walks the lists of item-like things, not the nodes themselves.
/// - Con: Don't get information about nesting
/// - Con: Don't have methods for specific bits of HIR, like "on
/// - Example: Examine each expression to look for its type and do some check or other.
/// - How: Implement `intravisit::Visitor` and override the `nested_visit_map()` method
/// to return `NestedVisitorMap::OnlyBodies` and use
-/// `tcx.hir().krate().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within
-/// your `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget
-/// to invoke `intravisit::walk_expr()` to keep walking the subparts).
+/// `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your
+/// `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
+/// `intravisit::walk_expr()` to keep walking the subparts).
/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
/// - Pro: Integrates well into dependency tracking.
/// - Con: Don't get information about nesting between items
pub enum LangItemGroup {
Op,
+ Fn,
}
-const NUM_GROUPS: usize = 1;
+const NUM_GROUPS: usize = 2;
macro_rules! expand_group {
() => {
/// Construct an empty collection of lang items and no missing ones.
pub fn new() -> Self {
fn init_none(_: LangItem) -> Option<DefId> { None }
+ const EMPTY: Vec<DefId> = Vec::new();
Self {
items: vec![$(init_none(LangItem::$variant)),*],
missing: Vec::new(),
- groups: [vec![]; NUM_GROUPS],
+ groups: [EMPTY; NUM_GROUPS],
}
}
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
- Fn, kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
- FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
- FnOnce, sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
+ Fn(Fn), kw::Fn, fn_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnMut(Fn), sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
+ FnOnce(Fn), sym::fn_once, fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;
Unpin, sym::unpin, unpin_trait, Target::Trait, GenericRequirement::None;
Pin, sym::pin, pin_type, Target::Struct, GenericRequirement::None;
- PartialEq, sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
- PartialOrd, sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
+ PartialEq(Op), sym::eq, eq_trait, Target::Trait, GenericRequirement::Exact(1);
+ PartialOrd(Op), sym::partial_ord, partial_ord_trait, Target::Trait, GenericRequirement::Exact(1);
// A number of panic-related lang items. The `panic` item corresponds to divide-by-zero and
// various panic cases with `match`. The `panic_bounds_check` item is for indexing arrays.
Panic, sym::panic, panic_fn, Target::Fn, GenericRequirement::Exact(0);
PanicFmt, sym::panic_fmt, panic_fmt, Target::Fn, GenericRequirement::None;
PanicDisplay, sym::panic_display, panic_display, Target::Fn, GenericRequirement::None;
- PanicStr, sym::panic_str, panic_str, Target::Fn, GenericRequirement::None;
ConstPanicFmt, sym::const_panic_fmt, const_panic_fmt, Target::Fn, GenericRequirement::None;
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn, GenericRequirement::Exact(0);
PanicInfo, sym::panic_info, panic_info, Target::Struct, GenericRequirement::None;
impl<HirCtx: crate::HashStableContext> HashStable<HirCtx> for ImplItem<'_> {
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
- let ImplItem { def_id: _, ident, ref vis, defaultness, ref generics, ref kind, span } =
- *self;
+ let ImplItem { def_id: _, ident, ref vis, ref generics, ref kind, span } = *self;
hcx.hash_hir_item_like(|hcx| {
ident.name.hash_stable(hcx, hasher);
vis.hash_stable(hcx, hasher);
- defaultness.hash_stable(hcx, hasher);
generics.hash_stable(hcx, hasher);
kind.hash_stable(hcx, hasher);
span.hash_stable(hcx, hasher);
fn hash_stable(&self, hcx: &mut HirCtx, hasher: &mut StableHasher) {
// We ignore the `nodes` and `bodies` fields since these refer to information included in
// `hash` which is hashed in the collector and used for the crate hash.
+ // `local_id_to_def_id` is also ignored because is dependent on the body, then just hashing
+ // the body satisfies the condition of two nodes being different have different
+ // `hash_stable` results.
let OwnerNodes {
hash_including_bodies,
hash_without_bodies: _,
}
}
-pub const INDENT_UNIT: usize = 4;
+pub const INDENT_UNIT: isize = 4;
/// Requires you to pass an input filename and reader so that
/// it can scan the input text for comments to copy forward.
self.hardbreak_if_not_bol();
self.maybe_print_comment(ii.span.lo());
self.print_outer_attributes(self.attrs(ii.hir_id()));
- self.print_defaultness(ii.defaultness);
match ii.kind {
hir::ImplItemKind::Const(ref ty, expr) => {
Bound::Excluded(end) => end.index(),
Bound::Unbounded => self.domain_size() - 1,
};
- let len = if let Some(l) = end.checked_sub(start) {
- l
- } else {
- return;
- };
+ let Some(len) = end.checked_sub(start) else { return };
match self {
HybridBitSet::Sparse(sparse) if sparse.len() + len < SPARSE_MAX => {
// The set is sparse and has space for `elems`.
) -> At<'a, 'tcx> {
At { infcx: self, cause, param_env }
}
+
+ /// Forks the inference context, creating a new inference context with the same inference
+ /// variables in the same state. This can be used to "branch off" many tests from the same
+ /// common state. Used in coherence.
+ pub fn fork(&self) -> Self {
+ Self {
+ tcx: self.tcx.clone(),
+ defining_use_anchor: self.defining_use_anchor.clone(),
+ in_progress_typeck_results: self.in_progress_typeck_results.clone(),
+ inner: self.inner.clone(),
+ skip_leak_check: self.skip_leak_check.clone(),
+ lexical_region_resolutions: self.lexical_region_resolutions.clone(),
+ selection_cache: self.selection_cache.clone(),
+ evaluation_cache: self.evaluation_cache.clone(),
+ reported_trait_errors: self.reported_trait_errors.clone(),
+ reported_closure_mismatch: self.reported_closure_mismatch.clone(),
+ tainted_by_errors_flag: self.tainted_by_errors_flag.clone(),
+ err_count_on_creation: self.err_count_on_creation,
+ in_snapshot: self.in_snapshot.clone(),
+ universe: self.universe.clone(),
+ }
+ }
}
pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
a: Self,
b: Self,
) -> TypeTrace<'tcx> {
- TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) }
+ TypeTrace {
+ cause: cause.clone(),
+ values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+ }
}
}
}
}
-impl<'tcx> ToTrace<'tcx> for &'tcx Const<'tcx> {
+impl<'tcx> ToTrace<'tcx> for Const<'tcx> {
fn to_trace(
_: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a: Self,
b: Self,
) -> TypeTrace<'tcx> {
- TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
+ TypeTrace {
+ cause: cause.clone(),
+ values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+ }
}
}
impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> {
fn to_trace(
- tcx: TyCtxt<'tcx>,
+ _: TyCtxt<'tcx>,
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
a: Self,
b: Self,
) -> TypeTrace<'tcx> {
- match (a, b) {
- (ty::Term::Ty(a), ty::Term::Ty(b)) => {
- ToTrace::to_trace(tcx, cause, a_is_expected, a, b)
- }
- (ty::Term::Const(a), ty::Term::Const(b)) => {
- ToTrace::to_trace(tcx, cause, a_is_expected, a, b)
- }
- (_, _) => todo!(),
- }
+ TypeTrace { cause: cause.clone(), values: Terms(ExpectedFound::new(a_is_expected, a, b)) }
}
}
let b_ty = tcx.mk_projection(b.item_def_id, b.substs);
TypeTrace {
cause: cause.clone(),
- values: Types(ExpectedFound::new(a_is_expected, a_ty, b_ty)),
+ values: Terms(ExpectedFound::new(a_is_expected, a_ty.into(), b_ty.into())),
}
}
}
Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state)
}
+ /// Like [Self::canonicalize_query], but preserves distinct universes. For
+ /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and
+ /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1`
+ /// in `U2`.
+ ///
+ /// This is used for Chalk integration.
+ pub fn canonicalize_query_preserving_universes<V>(
+ &self,
+ value: V,
+ query_state: &mut OriginalQueryValues<'tcx>,
+ ) -> Canonicalized<'tcx, V>
+ where
+ V: TypeFoldable<'tcx>,
+ {
+ self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed);
+
+ Canonicalizer::canonicalize(
+ value,
+ self,
+ self.tcx,
+ &CanonicalizeAllFreeRegionsPreservingUniverses,
+ query_state,
+ )
+ }
+
/// Canonicalizes a query *response* `V`. When we canonicalize a
/// query response, we only canonicalize unbound inference
/// variables, and we leave other free regions alone. So,
/// maximally general query. But if we are canonicalizing a *query
/// response*, then we don't typically replace free regions, as they
/// must have been introduced from other parts of the system.
-trait CanonicalizeRegionMode {
+trait CanonicalizeMode {
fn canonicalize_free_region<'tcx>(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
) -> ty::Region<'tcx>;
fn any(&self) -> bool;
+
+ // Do we preserve universe of variables.
+ fn preserve_universes(&self) -> bool;
}
struct CanonicalizeQueryResponse;
-impl CanonicalizeRegionMode for CanonicalizeQueryResponse {
+impl CanonicalizeMode for CanonicalizeQueryResponse {
fn canonicalize_free_region<'tcx>(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
- match r {
+ match *r {
ty::ReFree(_)
| ty::ReErased
| ty::ReStatic
| ty::ReEarlyBound(..) => r,
ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region(
- CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(*placeholder) },
+ CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) },
r,
),
ty::ReVar(vid) => {
- let universe = canonicalizer.region_var_universe(*vid);
+ let universe = canonicalizer.region_var_universe(vid);
canonicalizer.canonical_var_for_region(
CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
r,
fn any(&self) -> bool {
false
}
+
+ fn preserve_universes(&self) -> bool {
+ true
+ }
}
struct CanonicalizeUserTypeAnnotation;
-impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation {
+impl CanonicalizeMode for CanonicalizeUserTypeAnnotation {
fn canonicalize_free_region<'tcx>(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
- match r {
+ match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r,
ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r),
_ => {
fn any(&self) -> bool {
false
}
+
+ fn preserve_universes(&self) -> bool {
+ false
+ }
}
struct CanonicalizeAllFreeRegions;
-impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions {
+impl CanonicalizeMode for CanonicalizeAllFreeRegions {
fn canonicalize_free_region<'tcx>(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
fn any(&self) -> bool {
true
}
+
+ fn preserve_universes(&self) -> bool {
+ false
+ }
+}
+
+struct CanonicalizeAllFreeRegionsPreservingUniverses;
+
+impl CanonicalizeMode for CanonicalizeAllFreeRegionsPreservingUniverses {
+ fn canonicalize_free_region<'tcx>(
+ &self,
+ canonicalizer: &mut Canonicalizer<'_, 'tcx>,
+ r: ty::Region<'tcx>,
+ ) -> ty::Region<'tcx> {
+ let universe = canonicalizer.infcx.universe_of_region(r);
+ canonicalizer.canonical_var_for_region(
+ CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) },
+ r,
+ )
+ }
+
+ fn any(&self) -> bool {
+ true
+ }
+
+ fn preserve_universes(&self) -> bool {
+ true
+ }
}
struct CanonicalizeFreeRegionsOtherThanStatic;
-impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic {
+impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic {
fn canonicalize_free_region<'tcx>(
&self,
canonicalizer: &mut Canonicalizer<'_, 'tcx>,
r: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
- if let ty::ReStatic = r {
- r
- } else {
- canonicalizer.canonical_var_for_region_in_root_universe(r)
- }
+ if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) }
}
fn any(&self) -> bool {
true
}
+
+ fn preserve_universes(&self) -> bool {
+ false
+ }
}
struct Canonicalizer<'cx, 'tcx> {
// Note that indices is only used once `var_values` is big enough to be
// heap-allocated.
indices: FxHashMap<GenericArg<'tcx>, BoundVar>,
- canonicalize_region_mode: &'cx dyn CanonicalizeRegionMode,
+ canonicalize_mode: &'cx dyn CanonicalizeMode,
needs_canonical_flags: TypeFlags,
binder_index: ty::DebruijnIndex,
vid, r
);
let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
- self.canonicalize_region_mode.canonicalize_free_region(self, r)
+ self.canonicalize_mode.canonicalize_free_region(self, r)
}
ty::ReStatic
| ty::ReFree(_)
| ty::ReEmpty(_)
| ty::RePlaceholder(..)
- | ty::ReErased => self.canonicalize_region_mode.canonicalize_free_region(self, r),
+ | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r),
}
}
// `TyVar(vid)` is unresolved, track its universe index in the canonicalized
// result.
Err(mut ui) => {
- // FIXME: perf problem described in #55921.
- ui = ty::UniverseIndex::ROOT;
+ if !self.canonicalize_mode.preserve_universes() {
+ // FIXME: perf problem described in #55921.
+ ui = ty::UniverseIndex::ROOT;
+ }
self.canonicalize_ty_var(
CanonicalVarInfo {
kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)),
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- match ct.val {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ match ct.val() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
debug!("canonical: const var found with vid {:?}", vid);
match self.infcx.probe_const_var(vid) {
// `ConstVar(vid)` is unresolved, track its universe index in the
// canonicalized result
Err(mut ui) => {
- // FIXME: perf problem described in #55921.
- ui = ty::UniverseIndex::ROOT;
+ if !self.canonicalize_mode.preserve_universes() {
+ // FIXME: perf problem described in #55921.
+ ui = ty::UniverseIndex::ROOT;
+ }
return self.canonicalize_const_var(
- CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty) },
+ CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty()) },
ct,
);
}
value: V,
infcx: &InferCtxt<'_, 'tcx>,
tcx: TyCtxt<'tcx>,
- canonicalize_region_mode: &dyn CanonicalizeRegionMode,
+ canonicalize_region_mode: &dyn CanonicalizeMode,
query_state: &mut OriginalQueryValues<'tcx>,
) -> Canonicalized<'tcx, V>
where
let mut canonicalizer = Canonicalizer {
infcx,
tcx,
- canonicalize_region_mode,
+ canonicalize_mode: canonicalize_region_mode,
needs_canonical_flags,
variables: SmallVec::new(),
query_state,
// Once we have canonicalized `out_value`, it should not
// contain anything that ties it to this inference context
- // anymore, so it should live in the global arena.
- debug_assert!(!out_value.needs_infer());
+ // anymore.
+ debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders());
- let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);
+ let canonical_variables =
+ tcx.intern_canonical_var_infos(&canonicalizer.universe_canonicalized_variables());
let max_universe = canonical_variables
.iter()
let var_values = &mut query_state.var_values;
+ let universe = info.universe();
+ if universe != ty::UniverseIndex::ROOT {
+ assert!(self.canonicalize_mode.preserve_universes());
+
+ // Insert universe into the universe map. To preserve the order of the
+ // universes in the value being canonicalized, we don't update the
+ // universe in `info` until we have finished canonicalizing.
+ match query_state.universe_map.binary_search(&universe) {
+ Err(idx) => query_state.universe_map.insert(idx, universe),
+ Ok(_) => {}
+ }
+ }
+
// This code is hot. `variables` and `var_values` are usually small
// (fewer than 8 elements ~95% of the time). They are SmallVec's to
// avoid allocations in those cases. We also don't use `indices` to
}
}
+ /// Replaces the universe indexes used in `var_values` with their index in
+ /// `query_state.universe_map`. This minimizes the maximum universe used in
+ /// the canonicalized value.
+ fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> {
+ if self.query_state.universe_map.len() == 1 {
+ return self.variables;
+ }
+
+ let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self
+ .query_state
+ .universe_map
+ .iter()
+ .enumerate()
+ .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx)))
+ .collect();
+
+ self.variables
+ .iter()
+ .map(|v| CanonicalVarInfo {
+ kind: match v.kind {
+ CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => {
+ return *v;
+ }
+ CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => {
+ CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u]))
+ }
+ CanonicalVarKind::Region(u) => {
+ CanonicalVarKind::Region(reverse_universe_map[&u])
+ }
+ CanonicalVarKind::Const(u, t) => {
+ CanonicalVarKind::Const(reverse_universe_map[&u], t)
+ }
+ CanonicalVarKind::PlaceholderTy(placeholder) => {
+ CanonicalVarKind::PlaceholderTy(ty::Placeholder {
+ universe: reverse_universe_map[&placeholder.universe],
+ ..placeholder
+ })
+ }
+ CanonicalVarKind::PlaceholderRegion(placeholder) => {
+ CanonicalVarKind::PlaceholderRegion(ty::Placeholder {
+ universe: reverse_universe_map[&placeholder.universe],
+ ..placeholder
+ })
+ }
+ CanonicalVarKind::PlaceholderConst(placeholder) => {
+ CanonicalVarKind::PlaceholderConst(ty::Placeholder {
+ universe: reverse_universe_map[&placeholder.universe],
+ ..placeholder
+ })
+ }
+ },
+ })
+ .collect()
+ }
+
/// Shorthand helper that creates a canonical region variable for
/// `r` (always in the root universe). The reason that we always
/// put these variables into the root universe is because this
fn canonicalize_const_var(
&mut self,
info: CanonicalVarInfo<'tcx>,
- const_var: &'tcx ty::Const<'tcx>,
- ) -> &'tcx ty::Const<'tcx> {
+ const_var: ty::Const<'tcx>,
+ ) -> ty::Const<'tcx> {
let infcx = self.infcx;
let bound_to = infcx.shallow_resolve(const_var);
if bound_to != const_var {
self.fold_const(bound_to)
} else {
let var = self.canonical_var(info, const_var.into());
- self.tcx().mk_const(ty::Const {
+ self.tcx().mk_const(ty::ConstS {
val: ty::ConstKind::Bound(self.binder_index, var),
- ty: self.fold_ty(const_var.ty),
+ ty: self.fold_ty(const_var.ty()),
})
}
}
let universe_mapped = universe_map(universe);
let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, name };
self.tcx
- .mk_const(ty::Const {
+ .mk_const(ty::ConstS {
val: ty::ConstKind::Placeholder(placeholder_mapped),
ty: name.ty,
})
v.var_values[BoundVar::new(index)]
});
match (original_value.unpack(), result_value.unpack()) {
- (
- GenericArgKind::Lifetime(ty::ReErased),
- GenericArgKind::Lifetime(ty::ReErased),
- ) => {
+ (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
+ if re1.is_erased() && re2.is_erased() =>
+ {
// No action needed.
}
}
GenericArgKind::Lifetime(result_value) => {
// e.g., here `result_value` might be `'?1` in the example above...
- if let &ty::RegionKind::ReLateBound(debruijn, br) = result_value {
+ if let ty::ReLateBound(debruijn, br) = *result_value {
// ... in which case we would set `canonical_vars[0]` to `Some('static)`.
// We only allow a `ty::INNERMOST` index in substitutions.
}
}
GenericArgKind::Const(result_value) => {
- if let ty::Const { val: ty::ConstKind::Bound(debrujin, b), .. } = result_value {
+ if let ty::ConstKind::Bound(debrujin, b) = result_value.val() {
// ...in which case we would set `canonical_vars[0]` to `Some(const X)`.
// We only allow a `ty::INNERMOST` index in substitutions.
- assert_eq!(*debrujin, ty::INNERMOST);
- opt_values[*b] = Some(*original_value);
+ assert_eq!(debrujin, ty::INNERMOST);
+ opt_values[b] = Some(*original_value);
}
}
}
obligations
.extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations());
}
- (
- GenericArgKind::Lifetime(ty::ReErased),
- GenericArgKind::Lifetime(ty::ReErased),
- ) => {
+ (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
+ if re1.is_erased() && re2.is_erased() =>
+ {
// no action needed
}
(GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
});
}
- fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) {
+ fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) {
span_bug!(
self.cause.span(self.infcx.tcx),
"generic_const_exprs: unreachable `const_equate`"
pub fn super_combine_consts<R>(
&self,
relation: &mut R,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>>
where
R: ConstEquateRelation<'tcx>,
{
let a_is_expected = relation.a_is_expected();
- match (a.val, b.val) {
+ match (a.val(), b.val()) {
(
ty::ConstKind::Infer(InferConst::Var(a_vid)),
ty::ConstKind::Infer(InferConst::Var(b_vid)),
&self,
param_env: ty::ParamEnv<'tcx>,
target_vid: ty::ConstVid<'tcx>,
- ct: &'tcx ty::Const<'tcx>,
+ ct: ty::Const<'tcx>,
vid_is_expected: bool,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
let (for_universe, span) = {
let mut inner = self.inner.borrow_mut();
let variable_table = &mut inner.const_unification_table();
pub fn add_const_equate_obligation(
&mut self,
a_is_expected: bool,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
) {
let predicate = if a_is_expected {
ty::PredicateKind::ConstEquate(a, b)
fn consts(
&mut self,
- c: &'tcx ty::Const<'tcx>,
- c2: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ c: ty::Const<'tcx>,
+ c2: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be ==
- match c.val {
+ match c.val() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
let mut inner = self.infcx.inner.borrow_mut();
let variable_table = &mut inner.const_unification_table();
origin: var_value.origin,
val: ConstVariableValue::Unknown { universe: self.for_universe },
});
- Ok(self.tcx().mk_const_var(new_var_id, c.ty))
+ Ok(self.tcx().mk_const_var(new_var_id, c.ty()))
}
}
}
substs,
substs,
)?;
- Ok(self.tcx().mk_const(ty::Const {
- ty: c.ty,
+ Ok(self.tcx().mk_const(ty::ConstS {
+ ty: c.ty(),
val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
}))
}
/// Register an obligation that both constants must be equal to each other.
///
/// If they aren't equal then the relation doesn't hold.
- fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>);
}
pub trait RelateResultCompare<'tcx, T> {
pub fn const_unification_error<'tcx>(
a_is_expected: bool,
- (a, b): (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>),
+ (a, b): (ty::Const<'tcx>, ty::Const<'tcx>),
) -> TypeError<'tcx> {
TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a, b))
}
debug_assert_eq!(r, _r);
debug!("ConstInferUnifier: r={:?}", r);
- match r {
+ match *r {
// Never make variables for regions bound within the type itself,
// nor for erased regions.
ty::ReLateBound(..) | ty::ReErased => {
#[tracing::instrument(level = "debug", skip(self))]
fn consts(
&mut self,
- c: &'tcx ty::Const<'tcx>,
- _c: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ c: ty::Const<'tcx>,
+ _c: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug_assert_eq!(c, _c);
debug!("ConstInferUnifier: c={:?}", c);
- match c.val {
+ match c.val() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
// Check if the current unification would end up
// unifying `target_vid` with a const which contains
},
},
);
- Ok(self.tcx().mk_const_var(new_var_id, c.ty))
+ Ok(self.tcx().mk_const_var(new_var_id, c.ty()))
}
}
}
substs,
substs,
)?;
- Ok(self.tcx().mk_const(ty::Const {
- ty: c.ty,
+ Ok(self.tcx().mk_const(ty::ConstS {
+ ty: c.ty(),
val: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }),
}))
}
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> {
- fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}
);
// Explain the region we are capturing.
- match hidden_region {
+ match *hidden_region {
ty::ReEmpty(ty::UniverseIndex::ROOT) => {
// All lifetimes shorter than the function body are `empty` in
// lexical region resolution. The default explanation of "an empty
Err(NonTrivialPath)
}
- fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
Err(NonTrivialPath)
}
) -> Option<()> {
for (i, ta) in sub.types().enumerate() {
if ta == other_ty {
- self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
+ self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty);
return Some(());
}
if let ty::Adt(def, _) = ta.kind() {
let path_ = self.tcx.def_path_str(def.did);
if path_ == other_path {
- self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
+ self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty);
return Some(());
}
}
let len2 = sig2.inputs().len();
if len1 == len2 {
for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() {
- let (x1, x2) = self.cmp(l, r);
+ let (x1, x2) = self.cmp(*l, *r);
(values.0).0.extend(x1.0);
(values.1).0.extend(x2.0);
self.push_comma(&mut values.0, &mut values.1, len1, i);
}
fn push_ty_ref<'tcx>(
- region: &ty::Region<'tcx>,
+ region: ty::Region<'tcx>,
ty: Ty<'tcx>,
mutbl: hir::Mutability,
s: &mut DiagnosticStyledString,
path1.clone(),
sub_no_defaults_1,
path2.clone(),
- &t2,
+ t2,
)
.is_some()
{
path2,
sub_no_defaults_2,
path1,
- &t1,
+ t1,
)
.is_some()
{
}
// When finding T != &T, highlight only the borrow
- (&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(&ref_ty1, &t2) => {
+ (&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(ref_ty1, t2) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
- push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0);
+ push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0);
values.1.push_normal(t2.to_string());
values
}
- (_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(&t1, &ref_ty2) => {
+ (_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(t1, ref_ty2) => {
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
values.0.push_normal(t1.to_string());
- push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1);
+ push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1);
values
}
// When encountering &T != &mut T, highlight only the borrow
(&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2))
- if equals(&ref_ty1, &ref_ty2) =>
+ if equals(ref_ty1, ref_ty2) =>
{
let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
- push_ty_ref(&r1, ref_ty1, mutbl1, &mut values.0);
- push_ty_ref(&r2, ref_ty2, mutbl2, &mut values.1);
+ push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0);
+ push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1);
values
}
None => (None, Mismatch::Fixed("type"), false),
Some(values) => {
let (is_simple_error, exp_found) = match values {
- ValuePairs::Types(exp_found) => {
- let is_simple_err =
- exp_found.expected.is_simple_text() && exp_found.found.is_simple_text();
- OpaqueTypesVisitor::visit_expected_found(
- self.tcx,
- exp_found.expected,
- exp_found.found,
- span,
- )
- .report(diag);
+ ValuePairs::Terms(infer::ExpectedFound {
+ expected: ty::Term::Ty(expected),
+ found: ty::Term::Ty(found),
+ }) => {
+ let is_simple_err = expected.is_simple_text() && found.is_simple_text();
+ OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span)
+ .report(diag);
- (is_simple_err, Mismatch::Variable(exp_found))
+ (
+ is_simple_err,
+ Mismatch::Variable(infer::ExpectedFound { expected, found }),
+ )
}
ValuePairs::TraitRefs(_) => (false, Mismatch::Fixed("trait")),
_ => (false, Mismatch::Fixed("type")),
};
if let Some((sp, msg)) = secondary_span {
if swap_secondary_and_primary {
- let terr = if let Some(infer::ValuePairs::Types(infer::ExpectedFound {
+ let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound {
expected,
..
})) = values
.iter()
.filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
.map(|field| (field.name, field.ty(self.tcx, expected_substs)))
- .find(|(_, ty)| same_type_modulo_infer(ty, exp_found.found))
+ .find(|(_, ty)| same_type_modulo_infer(*ty, exp_found.found))
{
if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
}
FailureCode::Error0308(failure_str) => {
let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str);
- if let ValuePairs::Types(ty::error::ExpectedFound { expected, found }) =
- trace.values
- {
+ if let Some((expected, found)) = trace.values.ty() {
match (expected.kind(), found.kind()) {
(ty::Tuple(_), ty::Tuple(_)) => {}
// If a tuple of length one was expected and the found expression has
// parentheses around it, perhaps the user meant to write `(expr,)` to
// build a tuple (issue #86100)
- (ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => {
- if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) {
- if let Some(code) =
- code.strip_prefix('(').and_then(|s| s.strip_suffix(')'))
- {
- err.span_suggestion(
- span,
- "use a trailing comma to create a tuple with one element",
- format!("({},)", code),
- Applicability::MaybeIncorrect,
- );
- }
- }
+ (ty::Tuple(_), _) => {
+ self.emit_tuple_wrap_err(&mut err, span, found, expected)
}
// If a character was expected and the found expression is a string literal
// containing a single character, perhaps the user meant to write `'c'` to
if let Some(code) =
code.strip_prefix('"').and_then(|s| s.strip_suffix('"'))
{
- if code.chars().nth(1).is_none() {
+ if code.chars().count() == 1 {
err.span_suggestion(
span,
"if you meant to write a `char` literal, use single quotes",
diag
}
+ fn emit_tuple_wrap_err(
+ &self,
+ err: &mut DiagnosticBuilder<'tcx>,
+ span: Span,
+ found: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ ) {
+ let [expected_tup_elem] = &expected.tuple_fields().collect::<Vec<_>>()[..]
+ else { return };
+
+ if !same_type_modulo_infer(*expected_tup_elem, found) {
+ return;
+ }
+
+ let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span)
+ else { return };
+
+ let msg = "use a trailing comma to create a tuple with one element";
+ if code.starts_with('(') && code.ends_with(')') {
+ let before_close = span.hi() - BytePos::from_u32(1);
+ err.span_suggestion(
+ span.with_hi(before_close).shrink_to_hi(),
+ msg,
+ ",".into(),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.multipart_suggestion(
+ msg,
+ vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())],
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
fn values_str(
&self,
values: ValuePairs<'tcx>,
) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
match values {
- infer::Types(exp_found) => self.expected_found_str_ty(exp_found),
infer::Regions(exp_found) => self.expected_found_str(exp_found),
- infer::Consts(exp_found) => self.expected_found_str(exp_found),
+ infer::Terms(exp_found) => self.expected_found_str_term(exp_found),
infer::TraitRefs(exp_found) => {
let pretty_exp_found = ty::error::ExpectedFound {
expected: exp_found.expected.print_only_trait_path(),
}
}
- fn expected_found_str_ty(
+ fn expected_found_str_term(
&self,
- exp_found: ty::error::ExpectedFound<Ty<'tcx>>,
+ exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
let exp_found = self.resolve_vars_if_possible(exp_found);
if exp_found.references_error() {
return None;
}
- Some(self.cmp(exp_found.expected, exp_found.found))
+ Some(match (exp_found.expected, exp_found.found) {
+ (ty::Term::Ty(expected), ty::Term::Ty(found)) => self.cmp(expected, found),
+ (expected, found) => (
+ DiagnosticStyledString::highlighted(expected.to_string()),
+ DiagnosticStyledString::highlighted(found.to_string()),
+ ),
+ })
}
/// Returns a string of the form "expected `{}`, found `{}`".
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) -> DiagnosticBuilder<'a> {
- let hir = &self.tcx.hir();
+ let hir = self.tcx.hir();
// Attempt to obtain the span of the parameter so we can
// suggest adding an explicit lifetime bound to it.
let generics = self
pub fn extract_inference_diagnostics_data(
&self,
arg: GenericArg<'tcx>,
- highlight: Option<ty::print::RegionHighlightMode>,
+ highlight: Option<ty::print::RegionHighlightMode<'tcx>>,
) -> InferenceDiagnosticsData {
match arg.unpack() {
GenericArgKind::Type(ty) => {
}
}
GenericArgKind::Const(ct) => {
- match ct.val {
+ match ct.val() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
let origin = self
.inner
}
_ => {}
},
- GenericArgKind::Const(c) => match c.val {
+ GenericArgKind::Const(c) => match c.val() {
ty::ConstKind::Infer(InferConst::Var(_)) => {
return self.extract_inference_diagnostics_data(s, None);
}
let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) {
(_, Some(_)) => String::new(),
(Some(ty), _) if ty.is_closure() => {
- let substs =
- if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() };
+ let ty::Closure(_, substs) = *ty.kind() else { unreachable!() };
let fn_sig = substs.as_closure().sig();
let args = closure_args(&fn_sig);
let ret = fn_sig.output().skip_binder().to_string();
let param_type = arg_data.kind.descr();
let suffix = match local_visitor.found_node_ty {
Some(ty) if ty.is_closure() => {
- let substs =
- if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() };
+ let ty::Closure(_, substs) = *ty.kind() else { unreachable!() };
let fn_sig = substs.as_closure().sig();
let ret = fn_sig.output().skip_binder().to_string();
}
/// Replace not yet inferred const params with their def name.
- fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> {
- match c.val {
- ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty),
+ fn replace_infers(&self, c: Const<'tcx>, index: u32, name: Symbol) -> Const<'tcx> {
+ match c.val() {
+ ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty()),
_ => c,
}
}
.map(|(subst, param)| match &(subst.unpack(), ¶m.kind) {
(_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst,
(crate::infer::GenericArgKind::Const(c), _) => {
- self.replace_infers(c, param.index, param.name).into()
+ self.replace_infers(*c, param.index, param.name).into()
}
_ => subst.super_fold_with(self),
})
}
}
ty::Ref(_, ty, _) => {
- let ty = self.fold_ty(ty);
+ let ty = self.fold_ty(*ty);
match ty.kind() {
// Avoid `&_`, these can be safely presented as `_`.
ty::Error(_) => self.tcx().ty_error(),
| ty::Projection(_)
| ty::Never => t.super_fold_with(self),
ty::Array(ty, c) => {
- self.tcx().mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, sym::N)))
+ self.tcx().mk_ty(ty::Array(self.fold_ty(*ty), self.replace_infers(*c, 0, sym::N)))
}
// We don't want to hide type params that haven't been resolved yet.
// This would be the type that will be written out with the type param
use rustc_errors::{Applicability, ErrorReported};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
-use rustc_middle::ty::{self, TypeVisitor};
+use rustc_middle::ty::TypeVisitor;
use rustc_span::MultiSpan;
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup),
_ => return None,
};
- if *sub != ty::RegionKind::ReStatic {
+ if !sub.is_static() {
return None;
}
let cause = match origin {
.or_else(|| self.try_report_mismatched_static_lifetime())
}
- pub fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
+ pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> {
match (&self.error, self.regions) {
- (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), sub, sup)),
+ (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)),
(Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => {
- Some((origin.span(), sub, sup))
+ Some((origin.span(), *sub, *sup))
}
(None, Some((span, sub, sup))) => Some((span, sub, sup)),
_ => None,
// Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect,
// and can steer users down the wrong path.
- if *named == ty::ReStatic {
+ if named.is_static() {
return None;
}
use crate::infer::ValuePairs;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::{ObligationCause, ObligationCauseCode};
+use rustc_data_structures::intern::Interned;
use rustc_errors::DiagnosticBuilder;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode};
use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt};
use std::fmt::{self, Write};
vid,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
- sub_placeholder @ ty::RePlaceholder(_),
+ sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
- sup_placeholder @ ty::RePlaceholder(_),
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_region(ty::ReVar(*vid))),
+ Some(self.tcx().mk_region(ReVar(*vid))),
cause,
- Some(sub_placeholder),
- Some(sup_placeholder),
+ Some(*sub_placeholder),
+ Some(*sup_placeholder),
values,
),
vid,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
- sub_placeholder @ ty::RePlaceholder(_),
+ sub_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
_,
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_region(ty::ReVar(*vid))),
+ Some(self.tcx().mk_region(ReVar(*vid))),
cause,
- Some(sub_placeholder),
+ Some(*sub_placeholder),
None,
values,
),
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
_,
_,
- sup_placeholder @ ty::RePlaceholder(_),
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_region(ty::ReVar(*vid))),
+ Some(self.tcx().mk_region(ReVar(*vid))),
cause,
None,
Some(*sup_placeholder),
_,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
- sup_placeholder @ ty::RePlaceholder(_),
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
_,
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_region(ty::ReVar(*vid))),
+ Some(self.tcx().mk_region(ReVar(*vid))),
cause,
None,
Some(*sup_placeholder),
_,
_,
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
- sup_placeholder @ ty::RePlaceholder(_),
+ sup_placeholder @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
- Some(self.tcx().mk_region(ty::ReVar(*vid))),
+ Some(self.tcx().mk_region(ReVar(*vid))),
cause,
None,
Some(*sup_placeholder),
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
- sub_region @ ty::RePlaceholder(_),
- sup_region @ ty::RePlaceholder(_),
+ sub_region @ Region(Interned(RePlaceholder(_), _)),
+ sup_region @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
None,
cause,
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
- sub_region @ ty::RePlaceholder(_),
+ sub_region @ Region(Interned(RePlaceholder(_), _)),
sup_region,
)) => self.try_report_trait_placeholder_mismatch(
- (!sup_region.has_name()).then_some(sup_region),
+ (!sup_region.has_name()).then_some(*sup_region),
cause,
- Some(sub_region),
+ Some(*sub_region),
None,
values,
),
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, values }),
sub_region,
- sup_region @ ty::RePlaceholder(_),
+ sup_region @ Region(Interned(RePlaceholder(_), _)),
)) => self.try_report_trait_placeholder_mismatch(
- (!sub_region.has_name()).then_some(sub_region),
+ (!sub_region.has_name()).then_some(*sub_region),
cause,
None,
- Some(sup_region),
+ Some(*sup_region),
values,
),
fn try_report_trait_placeholder_mismatch(
&self,
- vid: Option<ty::Region<'tcx>>,
+ vid: Option<Region<'tcx>>,
cause: &ObligationCause<'tcx>,
- sub_placeholder: Option<ty::Region<'tcx>>,
- sup_placeholder: Option<ty::Region<'tcx>>,
+ sub_placeholder: Option<Region<'tcx>>,
+ sup_placeholder: Option<Region<'tcx>>,
value_pairs: &ValuePairs<'tcx>,
) -> Option<DiagnosticBuilder<'tcx>> {
let (expected_substs, found_substs, trait_def_id) = match value_pairs {
#[instrument(level = "debug", skip(self))]
fn report_trait_placeholder_mismatch(
&self,
- vid: Option<ty::Region<'tcx>>,
+ vid: Option<Region<'tcx>>,
cause: &ObligationCause<'tcx>,
- sub_placeholder: Option<ty::Region<'tcx>>,
- sup_placeholder: Option<ty::Region<'tcx>>,
+ sub_placeholder: Option<Region<'tcx>>,
+ sup_placeholder: Option<Region<'tcx>>,
trait_def_id: DefId,
expected_substs: SubstsRef<'tcx>,
actual_substs: SubstsRef<'tcx>,
fn explain_actual_impl_that_was_found(
&self,
err: &mut DiagnosticBuilder<'_>,
- sub_placeholder: Option<ty::Region<'tcx>>,
- sup_placeholder: Option<ty::Region<'tcx>>,
+ sub_placeholder: Option<Region<'tcx>>,
+ sup_placeholder: Option<Region<'tcx>>,
has_sub: Option<usize>,
has_sup: Option<usize>,
expected_trait_ref: ty::TraitRef<'tcx>,
actual_trait_ref: ty::TraitRef<'tcx>,
- vid: Option<ty::Region<'tcx>>,
+ vid: Option<Region<'tcx>>,
expected_has_vid: Option<usize>,
actual_has_vid: Option<usize>,
any_self_ty_has_vid: bool,
#[derive(Copy, Clone)]
struct Highlighted<'tcx, T> {
tcx: TyCtxt<'tcx>,
- highlight: RegionHighlightMode,
+ highlight: RegionHighlightMode<'tcx>,
value: T,
}
let highlight_trait_ref = |trait_ref| Highlighted {
tcx: self.tcx(),
- highlight: RegionHighlightMode::default(),
+ highlight: RegionHighlightMode::new(self.tcx()),
value: trait_ref,
};
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
use rustc_middle::ty::{
- self, AssocItemContainer, RegionKind, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable,
- TypeVisitor,
+ self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeFoldable, TypeVisitor,
};
use rustc_span::symbol::Ident;
use rustc_span::{MultiSpan, Span};
sup_origin,
sup_r,
spans,
- ) if **sub_r == RegionKind::ReStatic => {
- (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans)
- }
+ ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans),
RegionResolutionError::ConcreteFailure(
SubregionOrigin::Subtype(box TypeTrace { cause, .. }),
sub_r,
sup_r,
- ) if **sub_r == RegionKind::ReStatic => {
+ ) if sub_r.is_static() => {
// This is for an implicit `'static` requirement coming from `impl dyn Trait {}`.
if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() {
// This may have a closure and it would cause ICE
// through `find_param_with_region` (#78262).
- let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
+ let anon_reg_sup = tcx.is_suitable_region(*sup_r)?;
let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id);
if fn_returns.is_empty() {
return None;
}
- let param = self.find_param_with_region(sup_r, sub_r)?;
+ let param = self.find_param_with_region(*sup_r, *sub_r)?;
let lifetime = if sup_r.has_name() {
format!("lifetime `{}`", sup_r)
} else {
"try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})",
var_origin, sub_origin, sub_r, sup_origin, sup_r
);
- let anon_reg_sup = tcx.is_suitable_region(sup_r)?;
+ let anon_reg_sup = tcx.is_suitable_region(*sup_r)?;
debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup);
let sp = var_origin.span();
let return_sp = sub_origin.span();
- let param = self.find_param_with_region(sup_r, sub_r)?;
+ let param = self.find_param_with_region(*sup_r, *sub_r)?;
let (lifetime_name, lifetime) = if sup_r.has_name() {
(sup_r.to_string(), format!("lifetime `{}`", sup_r))
} else {
impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
match t.kind() {
- ty::Dynamic(preds, RegionKind::ReStatic) => {
+ ty::Dynamic(preds, re) if re.is_static() => {
if let Some(def_id) = preds.principal_def_id() {
self.0.insert(def_id);
}
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
-use crate::infer::{SubregionOrigin, Subtype, ValuePairs};
+use crate::infer::{SubregionOrigin, Subtype};
use crate::traits::ObligationCauseCode::CompareImplMethodObligation;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
{
if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = (&sup_origin, &sub_origin) {
if let (
- ValuePairs::Types(sub_expected_found),
- ValuePairs::Types(sup_expected_found),
+ sub_expected_found @ Some((sub_expected, sub_found)),
+ sup_expected_found @ Some(_),
CompareImplMethodObligation { trait_item_def_id, .. },
- ) = (&sub_trace.values, &sup_trace.values, sub_trace.cause.code())
+ ) = (&sub_trace.values.ty(), &sup_trace.values.ty(), sub_trace.cause.code())
{
if sup_expected_found == sub_expected_found {
self.emit_err(
var_origin.span(),
- sub_expected_found.expected,
- sub_expected_found.found,
+ *sub_expected,
+ *sub_found,
*trait_item_def_id,
);
return Some(ErrorReported);
// Mark all unnamed regions in the type with a number.
// This diagnostic is called in response to lifetime errors, so be informative.
- struct HighlightBuilder {
- highlight: RegionHighlightMode,
+ struct HighlightBuilder<'tcx> {
+ highlight: RegionHighlightMode<'tcx>,
counter: usize,
}
- impl HighlightBuilder {
- fn build(ty: Ty<'_>) -> RegionHighlightMode {
+ impl<'tcx> HighlightBuilder<'tcx> {
+ fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> {
let mut builder =
- HighlightBuilder { highlight: RegionHighlightMode::default(), counter: 1 };
+ HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 };
builder.visit_ty(ty);
builder.highlight
}
}
- impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder {
+ impl<'tcx> ty::fold::TypeVisitor<'tcx> for HighlightBuilder<'tcx> {
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
if !r.has_name() && self.counter <= 3 {
self.highlight.highlighting_region(r, self.counter);
}
}
- let expected_highlight = HighlightBuilder::build(expected);
+ let expected_highlight = HighlightBuilder::build(self.tcx(), expected);
let expected = self
.infcx
.extract_inference_diagnostics_data(expected.into(), Some(expected_highlight))
.name;
- let found_highlight = HighlightBuilder::build(found);
+ let found_highlight = HighlightBuilder::build(self.tcx(), found);
let found =
self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name;
.map(|res| {
matches!(
res,
- Res::SelfTy(_, _) | Res::Def(hir::def::DefKind::TyParam, _)
+ Res::SelfTy { trait_: _, alias_to: _ }
+ | Res::Def(hir::def::DefKind::TyParam, _)
)
})
.unwrap_or(false) =>
let ty = fn_sig.inputs()[index];
let mut found_anon_region = false;
let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| {
- if *r == *anon_region {
+ if r == anon_region {
found_anon_region = true;
replace_region
} else {
"...so that the definition in impl matches the definition from the trait",
);
}
+ infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+ self.note_region_origin(err, &parent);
+ }
}
}
infer::Subtype(box trace) => {
let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
let mut err = self.report_and_explain_type_error(trace, &terr);
- match (sub, sup) {
+ match (*sub, *sup) {
(ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
(ty::RePlaceholder(_), _) => {
note_and_explain_region(
trait_item_def_id,
&format!("`{}: {}`", sup, sub),
),
+ infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+ let mut err = self.report_concrete_failure(*parent, sub, sup);
+
+ let trait_item_span = self.tcx.def_span(trait_item_def_id);
+ let item_name = self.tcx.item_name(impl_item_def_id);
+ err.span_label(
+ trait_item_span,
+ format!("definition of `{}` from trait", item_name),
+ );
+
+ let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
+ let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
+
+ let impl_predicates: rustc_data_structures::stable_set::FxHashSet<_> =
+ impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
+ let clauses: Vec<_> = trait_predicates
+ .predicates
+ .into_iter()
+ .filter(|&(pred, _)| !impl_predicates.contains(pred))
+ .map(|(pred, _)| format!("{}", pred))
+ .collect();
+
+ if !clauses.is_empty() {
+ let where_clause_span = self
+ .tcx
+ .hir()
+ .get_generics(impl_item_def_id.expect_local())
+ .unwrap()
+ .where_clause
+ .tail_span_for_suggestion();
+
+ let suggestion = format!(
+ "{} {}",
+ if !impl_predicates.is_empty() { "," } else { " where" },
+ clauses.join(", "),
+ );
+ err.span_suggestion(
+ where_clause_span,
+ &format!(
+ "try copying {} from the trait",
+ if clauses.len() > 1 { "these clauses" } else { "this clause" }
+ ),
+ suggestion,
+ rustc_errors::Applicability::MaybeIncorrect,
+ );
+ }
+
+ err
+ }
}
}
}
impl<'tcx> FreeRegionMap<'tcx> {
- pub fn elements(&self) -> impl Iterator<Item = &Region<'tcx>> {
- self.relation.elements()
+ pub fn elements(&self) -> impl Iterator<Item = Region<'tcx>> + '_ {
+ self.relation.elements().copied()
}
pub fn is_empty(&self) -> bool {
/// True for free regions other than `'static`.
pub fn is_free(&self, r: Region<'_>) -> bool {
- matches!(r, ty::ReEarlyBound(_) | ty::ReFree(_))
+ matches!(*r, ty::ReEarlyBound(_) | ty::ReFree(_))
}
/// True if `r` is a free region or static of the sort that this
ty_freshen_count: u32,
const_freshen_count: u32,
ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>,
- const_freshen_map: FxHashMap<ty::InferConst<'tcx>, &'tcx ty::Const<'tcx>>,
+ const_freshen_map: FxHashMap<ty::InferConst<'tcx>, ty::Const<'tcx>>,
keep_static: bool,
}
fn freshen_const<F>(
&mut self,
- opt_ct: Option<&'tcx ty::Const<'tcx>>,
+ opt_ct: Option<ty::Const<'tcx>>,
key: ty::InferConst<'tcx>,
freshener: F,
ty: Ty<'tcx>,
- ) -> &'tcx ty::Const<'tcx>
+ ) -> ty::Const<'tcx>
where
F: FnOnce(u32) -> ty::InferConst<'tcx>,
{
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- match ct.val {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ match ct.val() {
ty::ConstKind::Infer(ty::InferConst::Var(v)) => {
let opt_ct = self
.infcx
opt_ct,
ty::InferConst::Var(v),
ty::InferConst::Fresh,
- ct.ty,
+ ct.ty(),
);
}
ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => {
r
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- if let ty::Const { val: ty::ConstKind::Infer(ty::InferConst::Var(vid)), ty } = ct {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.val() {
if self.const_vars.0.contains(&vid) {
// This variable was created during the fudging.
// Recreate it with a fresh variable here.
let idx = (vid.index - self.const_vars.0.start.index) as usize;
let origin = self.const_vars.1[idx];
- self.infcx.next_const_var(ty, origin)
+ self.infcx.next_const_var(ct.ty(), origin)
} else {
ct
}
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> {
- fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}
};
let fld_c = |bound_var: ty::BoundVar, ty| {
- self.tcx.mk_const(ty::Const {
+ self.tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Placeholder(ty::PlaceholderConst {
universe: next_universe,
name: ty::BoundConst { var: bound_var, ty },
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
};
+use rustc_data_structures::intern::Interned;
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::{self, Ty, TyCtxt};
/// Contains the result of lexical region resolution. Offers methods
/// to lookup up the final value of a region variable.
+#[derive(Clone)]
pub struct LexicalRegionResolutions<'tcx> {
values: IndexVec<RegionVid, VarValue<'tcx>>,
error_region: ty::Region<'tcx>,
changes.push(b_vid);
}
if let Some(a_vid) = a_vid {
- match *b_data {
- VarValue::Value(ReStatic) | VarValue::ErrorValue => (),
+ match b_data {
+ VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue => (),
_ => {
constraints[a_vid].push((a_vid, b_vid));
constraints[b_vid].push((a_vid, b_vid));
if self.expand_node(a_region, b_vid, b_data) {
changes.push(b_vid);
}
- !matches!(b_data, VarValue::Value(ReStatic) | VarValue::ErrorValue)
+ !matches!(
+ b_data,
+ VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue
+ )
});
}
}
// check below for a common case, here purely as an
// optimization.
let b_universe = self.var_infos[b_vid].universe;
- if let ReEmpty(a_universe) = a_region {
- if *a_universe == b_universe {
+ if let ReEmpty(a_universe) = *a_region {
+ if a_universe == b_universe {
return false;
}
}
// tighter bound than `'static`.
//
// (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.)
- if let ty::RePlaceholder(p) = lub {
+ if let ty::RePlaceholder(p) = *lub {
if b_universe.cannot_name(p.universe) {
lub = self.tcx().lifetimes.re_static;
}
/// term "concrete regions").
#[instrument(level = "trace", skip(self))]
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
- let r = match (a, b) {
- (&ReLateBound(..), _) | (_, &ReLateBound(..)) | (&ReErased, _) | (_, &ReErased) => {
+ let r = match (*a, *b) {
+ (ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => {
bug!("cannot relate region: LUB({:?}, {:?})", a, b);
}
- (&ReVar(v_id), _) | (_, &ReVar(v_id)) => {
+ (ReVar(v_id), _) | (_, ReVar(v_id)) => {
span_bug!(
self.var_infos[v_id].origin.span(),
"lub_concrete_regions invoked with non-concrete \
);
}
- (&ReStatic, _) | (_, &ReStatic) => {
+ (ReStatic, _) | (_, ReStatic) => {
// nothing lives longer than `'static`
self.tcx().lifetimes.re_static
}
- (&ReEmpty(_), r @ (ReEarlyBound(_) | ReFree(_)))
- | (r @ (ReEarlyBound(_) | ReFree(_)), &ReEmpty(_)) => {
+ (ReEmpty(_), ReEarlyBound(_) | ReFree(_)) => {
// All empty regions are less than early-bound, free,
// and scope regions.
- r
+ b
}
- (&ReEmpty(a_ui), &ReEmpty(b_ui)) => {
+ (ReEarlyBound(_) | ReFree(_), ReEmpty(_)) => {
+ // All empty regions are less than early-bound, free,
+ // and scope regions.
+ a
+ }
+
+ (ReEmpty(a_ui), ReEmpty(b_ui)) => {
// Empty regions are ordered according to the universe
// they are associated with.
let ui = a_ui.min(b_ui);
self.tcx().mk_region(ReEmpty(ui))
}
- (&ReEmpty(empty_ui), &RePlaceholder(placeholder))
- | (&RePlaceholder(placeholder), &ReEmpty(empty_ui)) => {
+ (ReEmpty(empty_ui), RePlaceholder(placeholder))
+ | (RePlaceholder(placeholder), ReEmpty(empty_ui)) => {
// If this empty region is from a universe that can
// name the placeholder, then the placeholder is
// larger; otherwise, the only ancestor is `'static`.
}
}
- (&ReEarlyBound(_) | &ReFree(_), &ReEarlyBound(_) | &ReFree(_)) => {
+ (ReEarlyBound(_) | ReFree(_), ReEarlyBound(_) | ReFree(_)) => {
self.region_rels.lub_free_regions(a, b)
}
// For these types, we cannot define any additional
// relationship:
- (&RePlaceholder(..), _) | (_, &RePlaceholder(..)) => {
+ (RePlaceholder(..), _) | (_, RePlaceholder(..)) => {
if a == b {
a
} else {
let node_universe = self.var_infos[node_idx].universe;
for lower_bound in &lower_bounds {
- let effective_lower_bound = if let ty::RePlaceholder(p) = lower_bound.region {
+ let effective_lower_bound = if let ty::RePlaceholder(p) = *lower_bound.region {
if node_universe.cannot_name(p.universe) {
self.tcx().lifetimes.re_static
} else {
.expect("lower_vid_bounds should at least include `node_idx`");
for upper_bound in &upper_bounds {
- if let ty::RePlaceholder(p) = upper_bound.region {
+ if let ty::RePlaceholder(p) = *upper_bound.region {
if min_universe.cannot_name(p.universe) {
let origin = self.var_infos[node_idx].origin;
errors.push(RegionResolutionError::UpperBoundUniverseConflict(
}
VerifyBound::OutlivedBy(r) => {
- self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
+ self.sub_concrete_regions(min, var_values.normalize(self.tcx(), *r))
}
VerifyBound::IsEmpty => {
- matches!(min, ty::ReEmpty(_))
+ matches!(*min, ty::ReEmpty(_))
}
VerifyBound::AnyBound(bs) => {
where
T: TypeFoldable<'tcx>,
{
- tcx.fold_regions(value, &mut false, |r, _db| match r {
- ty::ReVar(rid) => self.resolve_var(*rid),
+ tcx.fold_regions(value, &mut false, |r, _db| match *r {
+ ty::ReVar(rid) => self.resolve_var(rid),
_ => r,
})
}
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> {
- fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}
/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot
/// operations are hot enough that we want only one call to `borrow_mut` per
/// call to `start_snapshot` and `rollback_to`.
+#[derive(Clone)]
pub struct InferCtxtInner<'tcx> {
/// Cache for projections. This cache is snapshotted along with the infcx.
///
/// See the `error_reporting` module for more details.
#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable)]
pub enum ValuePairs<'tcx> {
- Types(ExpectedFound<Ty<'tcx>>),
Regions(ExpectedFound<ty::Region<'tcx>>),
- Consts(ExpectedFound<&'tcx ty::Const<'tcx>>),
+ Terms(ExpectedFound<ty::Term<'tcx>>),
TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>),
PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>),
}
+impl<'tcx> ValuePairs<'tcx> {
+ pub fn ty(&self) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
+ if let ValuePairs::Terms(ExpectedFound {
+ expected: ty::Term::Ty(expected),
+ found: ty::Term::Ty(found),
+ }) = self
+ {
+ Some((*expected, *found))
+ } else {
+ None
+ }
+ }
+}
+
/// The trace designates the path through inference that we took to
/// encounter an error or subtyping constraint.
///
/// Comparing the signature and requirements of an impl associated type
/// against the containing trait
CompareImplTypeObligation { span: Span, impl_item_def_id: DefId, trait_item_def_id: DefId },
+
+ /// Checking that the bounds of a trait's associated type hold for a given impl
+ CheckAssociatedTypeBounds {
+ parent: Box<SubregionOrigin<'tcx>>,
+ impl_item_def_id: DefId,
+ trait_item_def_id: DefId,
+ },
}
// `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger.
self.tcx.mk_ty_var(vid)
}
- pub fn next_const_var(
- &self,
- ty: Ty<'tcx>,
- origin: ConstVariableOrigin,
- ) -> &'tcx ty::Const<'tcx> {
+ pub fn next_const_var(&self, ty: Ty<'tcx>, origin: ConstVariableOrigin) -> ty::Const<'tcx> {
self.tcx.mk_const_var(self.next_const_var_id(origin), ty)
}
ty: Ty<'tcx>,
origin: ConstVariableOrigin,
universe: ty::UniverseIndex,
- ) -> &'tcx ty::Const<'tcx> {
+ ) -> ty::Const<'tcx> {
let vid = self
.inner
.borrow_mut()
pub fn probe_const_var(
&self,
vid: ty::ConstVid<'tcx>,
- ) -> Result<&'tcx ty::Const<'tcx>, ty::UniverseIndex> {
+ ) -> Result<ty::Const<'tcx>, ty::UniverseIndex> {
match self.inner.borrow_mut().const_unification_table().probe_value(vid).val {
ConstVariableValue::Known { value } => Ok(value),
ConstVariableValue::Unknown { universe } => Err(universe),
pub fn report_mismatched_consts(
&self,
cause: &ObligationCause<'tcx>,
- expected: &'tcx ty::Const<'tcx>,
- actual: &'tcx ty::Const<'tcx>,
+ expected: ty::Const<'tcx>,
+ actual: ty::Const<'tcx>,
err: TypeError<'tcx>,
) -> DiagnosticBuilder<'tcx> {
let trace = TypeTrace::consts(cause, true, expected, actual);
/// Tries to extract an inference variable from a constant, returns `None`
/// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`).
- pub fn maybe_from_const(ct: &'tcx ty::Const<'tcx>) -> Option<Self> {
- match ct.val {
+ pub fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> {
+ match ct.val() {
ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)),
_ => None,
}
self.infcx.shallow_resolve_ty(ty)
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val() {
self.infcx
.inner
.borrow_mut()
.const_unification_table()
- .probe_value(*vid)
+ .probe_value(vid)
.val
.known()
.unwrap_or(ct)
a: Ty<'tcx>,
b: Ty<'tcx>,
) -> TypeTrace<'tcx> {
- TypeTrace { cause: cause.clone(), values: Types(ExpectedFound::new(a_is_expected, a, b)) }
+ TypeTrace {
+ cause: cause.clone(),
+ values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+ }
}
pub fn consts(
cause: &ObligationCause<'tcx>,
a_is_expected: bool,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
) -> TypeTrace<'tcx> {
- TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) }
+ TypeTrace {
+ cause: cause.clone(),
+ values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())),
+ }
}
}
ReferenceOutlivesReferent(_, a) => a,
CompareImplMethodObligation { span, .. } => span,
CompareImplTypeObligation { span, .. } => span,
+ CheckAssociatedTypeBounds { ref parent, .. } => parent.span(),
}
}
trait_item_def_id,
},
+ traits::ObligationCauseCode::CheckAssociatedTypeBounds {
+ impl_item_def_id,
+ trait_item_def_id,
+ } => SubregionOrigin::CheckAssociatedTypeBounds {
+ impl_item_def_id,
+ trait_item_def_id,
+ parent: Box::new(default()),
+ },
+
_ => default(),
}
}
info: ty::VarianceDiagInfo<'tcx>,
);
- fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
+ fn const_equate(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>);
/// Creates a new universe index. Used when instantiating placeholders.
fn create_next_universe(&mut self) -> ty::UniverseIndex;
scopes: &[BoundRegionScope<'tcx>],
) -> ty::Region<'tcx> {
debug!("replace_bound_regions(scopes={:?})", scopes);
- if let ty::ReLateBound(debruijn, br) = r {
- Self::lookup_bound_region(*debruijn, br, first_free_index, scopes)
+ if let ty::ReLateBound(debruijn, br) = *r {
+ Self::lookup_bound_region(debruijn, &br, first_free_index, scopes)
} else {
r
}
where
D: TypeRelatingDelegate<'tcx>,
{
- relate.relate(&generalized_ty, &self.value_ty())
+ relate.relate(generalized_ty, self.value_ty())
}
}
where
D: TypeRelatingDelegate<'tcx>,
{
- relate.relate(&self.value_ty(), &generalized_ty)
+ relate.relate(self.value_ty(), generalized_ty)
}
}
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- mut b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ mut b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
let a = self.infcx.shallow_resolve(a);
if !D::forbid_inference_vars() {
b = self.infcx.shallow_resolve(b);
}
- match b.val {
+ match b.val() {
ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
// Forbid inference variables in the RHS.
bug!("unexpected inference var {:?}", b)
where
D: TypeRelatingDelegate<'tcx>,
{
- fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
self.delegate.const_equate(a, b);
}
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
let ScopeInstantiator { bound_region_scope, next_region, .. } = self;
- match r {
- ty::ReLateBound(debruijn, br) if *debruijn == self.target_index => {
- bound_region_scope.map.entry(*br).or_insert_with(|| next_region(*br));
+ match *r {
+ ty::ReLateBound(debruijn, br) if debruijn == self.target_index => {
+ bound_region_scope.map.entry(br).or_insert_with(|| next_region(br));
}
_ => {}
) -> RelateResult<'tcx, ty::Region<'tcx>> {
debug!("TypeGeneralizer::regions(a={:?})", a);
- if let ty::ReLateBound(debruijn, _) = a {
- if *debruijn < self.first_free_index {
+ if let ty::ReLateBound(debruijn, _) = *a {
+ if debruijn < self.first_free_index {
return Ok(a);
}
}
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- _: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
- match a.val {
+ a: ty::Const<'tcx>,
+ _: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+ match a.val() {
ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
bug!("unexpected inference variable encountered in NLL generalization: {:?}", a);
}
origin: var_value.origin,
val: ConstVariableValue::Unknown { universe: self.universe },
});
- Ok(self.tcx().mk_const_var(new_var_id, a.ty))
+ Ok(self.tcx().mk_const_var(new_var_id, a.ty()))
}
}
}
use crate::infer::{GenericKind, InferCtxt};
use crate::traits::query::OutlivesBound;
use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::intern::Interned;
use rustc_hir as hir;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region};
use super::explicit_outlives_bounds;
/// "Region-bound pairs" tracks outlives relations that are known to
/// be true, either because of explicit where-clauses like `T: 'a` or
/// because of implied bounds.
-pub type RegionBoundPairs<'tcx> = Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>;
+pub type RegionBoundPairs<'tcx> = Vec<(Region<'tcx>, GenericKind<'tcx>)>;
impl<'a, 'tcx> OutlivesEnvironment<'tcx> {
pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self {
debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound);
match outlives_bound {
OutlivesBound::RegionSubRegion(
- r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)),
- &ty::ReVar(vid_b),
+ r_a @ (Region(Interned(ReEarlyBound(_), _)) | Region(Interned(ReFree(_), _))),
+ Region(Interned(ReVar(vid_b), _)),
) => {
- infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b);
+ infcx.expect("no infcx provided but region vars found").add_given(r_a, *vid_b);
}
OutlivesBound::RegionSubParam(r_a, param_b) => {
self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b)));
let origin = origin.clone();
match component {
Component::Region(region1) => {
- self.delegate.push_sub_region_constraint(origin, region, region1);
+ self.delegate.push_sub_region_constraint(origin, region, *region1);
}
Component::Param(param_ty) => {
self.param_ty_must_outlive(origin, region, *param_ty);
let scc = self.mini_graph.sccs.scc(*leak_check_node);
// Set the universe of each SCC to be the minimum of its constituent universes
- let universe = self.rcc.universe(region);
+ let universe = self.rcc.universe(*region);
debug!(
"assign_placeholder_values: scc={:?} universe={:?} region={:?}",
scc, universe, region
);
- self.scc_universes[scc].take_min(universe, region);
+ self.scc_universes[scc].take_min(universe, *region);
// Detect those SCCs that directly contain a placeholder
- if let ty::RePlaceholder(placeholder) = region {
+ if let ty::RePlaceholder(placeholder) = **region {
if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) {
- self.assign_scc_value(scc, *placeholder)?;
+ self.assign_scc_value(scc, placeholder)?;
}
}
}
};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::undo_log::UndoLogs;
use rustc_data_structures::unify as ut;
pub use rustc_middle::infer::MemberConstraint;
-#[derive(Default)]
+#[derive(Clone, Default)]
pub struct RegionConstraintStorage<'tcx> {
/// For each `RegionVid`, the corresponding `RegionVariableOrigin`.
var_infos: IndexVec<RegionVid, RegionVariableInfo>,
self.make_subregion(origin, sup, sub);
match (sub, sup) {
- (&ty::ReVar(sub), &ty::ReVar(sup)) => {
+ (Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => {
debug!("make_eqregion: unifying {:?} with {:?}", sub, sup);
- self.unification_table().union(sub, sup);
+ self.unification_table().union(*sub, *sup);
self.any_unifications = true;
}
- (&ty::ReVar(vid), value) | (value, &ty::ReVar(vid)) => {
+ (Region(Interned(ReVar(vid), _)), value)
+ | (value, Region(Interned(ReVar(vid), _))) => {
debug!("make_eqregion: unifying {:?} with {:?}", vid, value);
- self.unification_table().union_value(vid, UnifiedRegion(Some(value)));
+ self.unification_table().union_value(*vid, UnifiedRegion(Some(value)));
self.any_unifications = true;
}
(_, _) => {}
// cannot add constraints once regions are resolved
debug!("origin = {:#?}", origin);
- match (sub, sup) {
- (&ReLateBound(..), _) | (_, &ReLateBound(..)) => {
+ match (*sub, *sup) {
+ (ReLateBound(..), _) | (_, ReLateBound(..)) => {
span_bug!(origin.span(), "cannot relate bound region: {:?} <= {:?}", sub, sup);
}
- (_, &ReStatic) => {
+ (_, ReStatic) => {
// all regions are subregions of static, so we can ignore this
}
- (&ReVar(sub_id), &ReVar(sup_id)) => {
+ (ReVar(sub_id), ReVar(sup_id)) => {
self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin);
}
- (_, &ReVar(sup_id)) => {
+ (_, ReVar(sup_id)) => {
self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin);
}
- (&ReVar(sub_id), _) => {
+ (ReVar(sub_id), _) => {
self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin);
}
_ => {
) -> Region<'tcx> {
// cannot add constraints once regions are resolved
debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b);
- match (a, b) {
- (r @ &ReStatic, _) | (_, r @ &ReStatic) => {
- r // nothing lives longer than static
- }
-
- _ if a == b => {
- a // LUB(a,a) = a
- }
-
- _ => self.combine_vars(tcx, Lub, a, b, origin),
+ if a.is_static() || b.is_static() {
+ a // nothing lives longer than static
+ } else if a == b {
+ a // LUB(a,a) = a
+ } else {
+ self.combine_vars(tcx, Lub, a, b, origin)
}
}
) -> Region<'tcx> {
// cannot add constraints once regions are resolved
debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b);
- match (a, b) {
- (&ReStatic, r) | (r, &ReStatic) => {
- r // static lives longer than everything else
- }
-
- _ if a == b => {
- a // GLB(a,a) = a
- }
-
- _ => self.combine_vars(tcx, Glb, a, b, origin),
+ if a.is_static() {
+ b // static lives longer than everything else
+ } else if b.is_static() {
+ a // static lives longer than everything else
+ } else if a == b {
+ a // GLB(a,a) = a
+ } else {
+ self.combine_vars(tcx, Glb, a, b, origin)
}
}
tcx: TyCtxt<'tcx>,
region: ty::Region<'tcx>,
) -> ty::Region<'tcx> {
- match region {
+ match *region {
ty::ReVar(rid) => {
- let unified_region = self.unification_table().probe_value(*rid);
+ let unified_region = self.unification_table().probe_value(rid);
unified_region.0.unwrap_or_else(|| {
- let root = self.unification_table().find(*rid).vid;
+ let root = self.unification_table().find(rid).vid;
tcx.reuse_or_mk_region(region, ty::ReVar(root))
})
}
pub fn must_hold(&self) -> bool {
match self {
VerifyBound::IfEq(..) => false,
- VerifyBound::OutlivedBy(ty::ReStatic) => true,
- VerifyBound::OutlivedBy(_) => false,
+ VerifyBound::OutlivedBy(re) => re.is_static(),
VerifyBound::IsEmpty => false,
VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()),
VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()),
}
}
- fn fold_const(&mut self, ct: &'tcx Const<'tcx>) -> &'tcx Const<'tcx> {
+ fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> {
if !ct.has_infer_types_or_consts() {
ct // micro-optimize -- if there is nothing in this const that this fold affects...
} else {
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
if !ct.has_infer_regions() {
ct // micro-optimize -- if there is nothing in this const that this fold affects...
} else {
}
}
- fn try_fold_const(
- &mut self,
- c: &'tcx ty::Const<'tcx>,
- ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> {
+ fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
if !c.needs_infer() {
Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects...
} else {
let c = self.infcx.shallow_resolve(c);
- match c.val {
+ match c.val() {
ty::ConstKind::Infer(InferConst::Var(vid)) => {
return Err(FixupError::UnresolvedConst(vid));
}
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
self.fields.infcx.super_combine_consts(self, a, b)
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> {
- fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>) {
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
self.fields.add_const_equate_obligation(self.a_is_expected, a, b);
}
}
use rustc_data_structures::undo_log::{Rollback, UndoLogs};
/// Represents a single undo-able action that affects a type inference variable.
+#[derive(Clone)]
pub(crate) enum UndoLog<'tcx> {
EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>),
SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>),
}
}
+#[derive(Clone)]
pub struct TypeVariableStorage<'tcx> {
values: sv::SnapshotVecStorage<Delegate>,
LatticeVariable,
}
+#[derive(Clone)]
pub(crate) struct TypeVariableData {
origin: TypeVariableOrigin,
}
}
}
+#[derive(Clone)]
pub(crate) struct Instantiate;
pub(crate) struct Delegate;
fn index(&self) -> u32 {
self.vid.as_u32()
}
+ #[inline]
fn from_index(i: u32) -> Self {
TyVidEqKey::from(ty::TyVid::from_u32(i))
}
}
/// Records the "undo" data for a single operation that affects some form of inference variable.
+#[derive(Clone)]
pub(crate) enum UndoLog<'tcx> {
TypeVariables(type_variable::UndoLog<'tcx>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
/// 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)]
pub(crate) struct InferCtxtUndoLogs<'tcx> {
logs: Vec<UndoLog<'tcx>>,
num_open_snapshots: usize,
CodeSelectionError(SelectionError<'tcx>),
CodeProjectionError(MismatchedProjectionTypes<'tcx>),
CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate
- CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>),
+ CodeConstEquateError(ExpectedFound<Const<'tcx>>, TypeError<'tcx>),
CodeAmbiguity,
}
undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
}
-#[derive(Default)]
+#[derive(Clone, Default)]
pub struct ProjectionCacheStorage<'tcx> {
map: SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>,
}
//! `rustc_data_structures::AtomicRef` type, which allows us to setup a global
//! static which can then be set in this file at program startup.
//!
-//! See `SPAN_DEBUG` for an example of how to set things up.
+//! See `SPAN_TRACK` for an example of how to set things up.
//!
//! The functions in this file should fall back to the default set in their
//! origin crate when the `TyCtxt` is not present in TLS.
use rustc_middle::ty::tls;
use std::fmt;
-/// This is a callback from `rustc_ast` as it cannot access the implicit state
-/// in `rustc_middle` otherwise.
-fn span_debug(span: rustc_span::Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- tls::with_opt(|tcx| {
- if let Some(tcx) = tcx {
- rustc_span::debug_with_source_map(span, f, tcx.sess.source_map())
- } else {
- rustc_span::default_span_debug(span, f)
- }
- })
-}
-
fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
tls::with_opt(|tcx| {
if let Some(tcx) = tcx {
/// Sets up the callbacks in prior crates which we want to refer to the
/// TyCtxt in.
pub fn setup_callbacks() {
- rustc_span::SPAN_DEBUG.swap(&(span_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
rustc_span::SPAN_TRACK.swap(&(track_span_parent as fn(_)));
rustc_hir::def_id::DEF_ID_DEBUG.swap(&(def_id_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
TRACK_DIAGNOSTICS.swap(&(track_diagnostic as fn(&_)));
use rustc_span::source_map::{FileLoader, FileName};
use std::path::PathBuf;
use std::result;
-use std::sync::{Arc, Mutex};
pub type Result<T> = result::Result<T, ErrorReported>;
// If the user tried to use a key="value" flag, but is missing the quotes, provide
// a hint about how to resolve this.
- if s.contains("=") && !s.contains("=\"") && !s.ends_with("\"") {
+ if s.contains('=') && !s.contains("=\"") && !s.ends_with('"') {
error!(concat!(
r#"expected `key` or `key="value"`, ensure escaping is appropriate"#,
r#" for your shell, try 'key="value"' or key=\"value\""#
pub file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
pub diagnostic_output: DiagnosticOutput,
- /// Set to capture stderr output during compiler execution
- pub stderr: Option<Arc<Mutex<Vec<u8>>>>,
-
pub lint_caps: FxHashMap<lint::LintId, lint::Level>,
/// This is a callback from the driver that is called when [`ParseSess`] is created.
}
pub fn create_compiler_and_run<R>(config: Config, f: impl FnOnce(&Compiler) -> R) -> R {
+ crate::callbacks::setup_callbacks();
+
let registry = &config.registry;
let (mut sess, codegen_backend) = util::create_session(
config.opts,
})
}
-pub fn run_compiler<R: Send>(mut config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
+pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Send) -> R {
tracing::trace!("run_compiler");
- let stderr = config.stderr.take();
- util::setup_callbacks_and_run_in_thread_pool_with_globals(
+ util::run_in_thread_pool_with_globals(
config.opts.edition,
config.opts.debugging_opts.threads,
- &stderr,
|| create_compiler_and_run(config, f),
)
}
mod queries;
pub mod util;
+pub use callbacks::setup_callbacks;
pub use interface::{run_compiler, Config};
pub use passes::{DEFAULT_EXTERN_QUERY_PROVIDERS, DEFAULT_QUERY_PROVIDERS};
pub use queries::Queries;
rustc_builtin_macros::register_builtin_macros(resolver);
krate = sess.time("crate_injection", || {
- let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
- rustc_builtin_macros::standard_library_imports::inject(krate, resolver, sess, alt_std_name)
+ rustc_builtin_macros::standard_library_imports::inject(krate, resolver, sess)
});
util::check_attr_crate_type(sess, &krate.attrs, &mut resolver.lint_buffer());
});
let crate_types = sess.crate_types();
+ let is_executable_crate = crate_types.contains(&CrateType::Executable);
let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro);
+ if crate_types.len() > 1 {
+ if is_executable_crate {
+ sess.err("cannot mix `bin` crate type with others");
+ }
+ if is_proc_macro_crate {
+ sess.err("cannot mix `proc-macro` crate type with others");
+ }
+ }
+
// For backwards compatibility, we don't try to run proc macro injection
// if rustdoc is run on a proc macro crate without '--crate-type proc-macro' being
// specified. This should only affect users who manually invoke 'rustdoc', as
msg.emit()
} else {
krate = sess.time("maybe_create_a_macro_crate", || {
- let num_crate_types = crate_types.len();
let is_test_crate = sess.opts.test;
rustc_builtin_macros::proc_macro_harness::inject(
sess,
is_proc_macro_crate,
has_proc_macro_decls,
is_test_crate,
- num_crate_types,
sess.diagnostic(),
)
});
boxed_resolver.borrow_mut().access(|resolver| {
for cnum in resolver.cstore().crates_untracked() {
let source = resolver.cstore().crate_source_untracked(cnum);
- if let Some((path, _)) = source.dylib {
+ if let Some((path, _)) = &source.dylib {
files.push(escape_dep_filename(&path.display().to_string()));
}
- if let Some((path, _)) = source.rlib {
+ if let Some((path, _)) = &source.rlib {
files.push(escape_dep_filename(&path.display().to_string()));
}
- if let Some((path, _)) = source.rmeta {
+ if let Some((path, _)) = &source.rmeta {
files.push(escape_dep_filename(&path.display().to_string()));
}
}
use rustc_middle::dep_graph::DepGraph;
use rustc_middle::ty::{GlobalCtxt, TyCtxt};
use rustc_query_impl::Queries as TcxQueries;
-use rustc_serialize::json;
use rustc_session::config::{self, OutputFilenames, OutputType};
use rustc_session::{output::find_crate_name, Session};
use rustc_span::symbol::sym;
}
if sess.opts.debugging_opts.no_link {
- // FIXME: use a binary format to encode the `.rlink` file
- let rlink_data = json::encode(&codegen_results).map_err(|err| {
- sess.fatal(&format!("failed to encode rlink: {}", err));
- })?;
+ let mut encoder = rustc_serialize::opaque::Encoder::new(Vec::new());
+ rustc_serialize::Encodable::encode(&codegen_results, &mut encoder).unwrap();
let rlink_file = self.prepare_outputs.with_extension(config::RLINK_EXT);
- std::fs::write(&rlink_file, rlink_data).map_err(|err| {
+ std::fs::write(&rlink_file, encoder.into_inner()).map_err(|err| {
sess.fatal(&format!("failed to write file {}: {}", rlink_file.display(), err));
})?;
return Ok(());
gcx.enter(rustc_query_impl::alloc_self_profile_query_strings);
}
- if self.session().opts.debugging_opts.query_stats {
- gcx.enter(rustc_query_impl::print_stats);
- }
-
self.session()
.time("serialize_dep_graph", || gcx.enter(rustc_incremental::save_dep_graph));
}
tracked!(force_frame_pointers, Some(false));
tracked!(force_unwind_tables, Some(true));
tracked!(inline_threshold, Some(0xf007ba11));
+ tracked!(instrument_coverage, Some(InstrumentCoverage::All));
tracked!(linker_plugin_lto, LinkerPluginLto::LinkerPluginAuto);
tracked!(link_dead_code, Some(true));
tracked!(llvm_args, vec![String::from("1"), String::from("2")]);
untracked!(print_type_sizes, true);
untracked!(proc_macro_backtrace, true);
untracked!(query_dep_graph, true);
- untracked!(query_stats, true);
untracked!(save_analysis, true);
untracked!(self_profile, SwitchWithOptPath::Enabled(None));
untracked!(self_profile_events, Some(vec![String::new()]));
tracked!(debug_info_for_profiling, true);
tracked!(debug_macros, true);
tracked!(dep_info_omit_d_target, true);
+ tracked!(drop_tracking, true);
tracked!(dual_proc_macros, true);
tracked!(fewer_names, Some(true));
tracked!(force_unstable_if_unmarked, true);
use smallvec::SmallVec;
use std::env;
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
-use std::io;
use std::lazy::SyncOnceCell;
use std::mem;
use std::ops::DerefMut;
use std::panic;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::{Arc, Mutex};
use std::thread;
use tracing::info;
/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
/// for `'static` bounds.
#[cfg(not(parallel_compiler))]
-pub fn scoped_thread<F: FnOnce() -> R + Send, R: Send>(cfg: thread::Builder, f: F) -> R {
+fn scoped_thread<F: FnOnce() -> R + Send, R: Send>(cfg: thread::Builder, f: F) -> R {
// SAFETY: join() is called immediately, so any closure captures are still
// alive.
match unsafe { cfg.spawn_unchecked(f) }.unwrap().join() {
}
#[cfg(not(parallel_compiler))]
-pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
+pub fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
edition: Edition,
_threads: usize,
- stderr: &Option<Arc<Mutex<Vec<u8>>>>,
f: F,
) -> R {
let mut cfg = thread::Builder::new().name("rustc".to_string());
cfg = cfg.stack_size(size);
}
- crate::callbacks::setup_callbacks();
-
- let main_handler = move || {
- rustc_span::create_session_globals_then(edition, || {
- io::set_output_capture(stderr.clone());
- f()
- })
- };
+ let main_handler = move || rustc_span::create_session_globals_then(edition, f);
scoped_thread(cfg, main_handler)
}
}
#[cfg(parallel_compiler)]
-pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
+pub fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
edition: Edition,
threads: usize,
- stderr: &Option<Arc<Mutex<Vec<u8>>>>,
f: F,
) -> R {
- crate::callbacks::setup_callbacks();
-
let mut config = rayon::ThreadPoolBuilder::new()
.thread_name(|_| "rustc".to_string())
.acquire_thread_handler(jobserver::acquire_thread)
// the thread local rustc uses. `session_globals` is captured and set
// on the new threads.
let main_handler = move |thread: rayon::ThreadBuilder| {
- rustc_span::set_session_globals_then(session_globals, || {
- io::set_output_capture(stderr.clone());
- thread.run()
- })
+ rustc_span::set_session_globals_then(session_globals, || thread.run())
};
config.build_scoped(main_handler, with_pool).unwrap()
#[cfg(windows)]
fn current_dll_path() -> Option<PathBuf> {
use std::ffi::OsString;
+ use std::io;
use std::os::windows::prelude::*;
use std::ptr;
}
}
-pub fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> MakeBackendFn {
+fn get_codegen_sysroot(maybe_sysroot: &Option<PathBuf>, backend_name: &str) -> MakeBackendFn {
// For now we only allow this function to be called once as it'll dlopen a
// few things, which seems to work best if we only do that once. In
// general this assertion never trips due to the once guard in `get_codegen_backend`,
inferred_outlives
.iter()
.filter_map(|(pred, _)| match pred.kind().skip_binder() {
- ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match a {
+ ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a {
ty::ReEarlyBound(ebr) if ebr.index == index => Some(b),
_ => None,
},
if let hir::GenericBound::Outlives(lifetime) = bound {
let is_inferred = match tcx.named_region(lifetime.hir_id) {
Some(Region::Static) if infer_static => {
- inferred_outlives.iter().any(|r| matches!(r, ty::ReStatic))
+ inferred_outlives.iter().any(|r| matches!(**r, ty::ReStatic))
}
Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| {
- if let ty::ReEarlyBound(ebr) = r { ebr.index == index } else { false }
+ if let ty::ReEarlyBound(ebr) = **r { ebr.index == index } else { false }
}),
_ => false,
};
}
(Array(a_ty, a_const), Array(b_ty, b_const)) => {
// For arrays, we also check the constness of the type.
- a_const.val == b_const.val
- && structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
+ a_const.val() == b_const.val()
+ && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
}
(Slice(a_ty), Slice(b_ty)) => {
- structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
+ structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
}
(RawPtr(a_tymut), RawPtr(b_tymut)) => {
a_tymut.mutbl == b_tymut.mutbl
&& structurally_same_type_impl(
- seen_types,
- cx,
- &a_tymut.ty,
- &b_tymut.ty,
- ckind,
+ seen_types, cx, a_tymut.ty, b_tymut.ty, ckind,
)
}
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
// For structural sameness, we don't need the region to be same.
a_mut == b_mut
- && structurally_same_type_impl(seen_types, cx, a_ty, b_ty, ckind)
+ && structurally_same_type_impl(seen_types, cx, *a_ty, *b_ty, ckind)
}
(FnDef(..), FnDef(..)) => {
let a_poly_sig = a.fn_sig(tcx);
(a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
== (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
&& a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
- structurally_same_type_impl(seen_types, cx, a, b, ckind)
+ structurally_same_type_impl(seen_types, cx, *a, *b, ckind)
})
&& structurally_same_type_impl(
seen_types,
Ok(())
}
- fn print_const(self, _ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
Ok(())
}
}
}
// Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
- Res::SelfTy(None, Some((did, _))) => {
+ Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => {
if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
if let Some(name @ (sym::Ty | sym::TyCtxt)) =
cx.tcx.get_diagnostic_name(adt.did)
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
+ let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
+
if Some(def_id) == cx.tcx.lang_items().begin_panic_fn()
|| Some(def_id) == cx.tcx.lang_items().panic_fn()
- || Some(def_id) == cx.tcx.lang_items().panic_str()
+ || f_diagnostic_name == Some(sym::panic_str)
{
if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
if matches!(
check_panic(cx, f, arg);
}
}
+ } else if f_diagnostic_name == Some(sym::unreachable_display) {
+ if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
+ if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) {
+ check_panic(
+ cx,
+ f,
+ // This is safe because we checked above that the callee is indeed
+ // unreachable_display
+ match &arg.kind {
+ // Get the borrowed arg not the borrow
+ hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
+ _ => bug!("call to unreachable_display without borrow"),
+ },
+ );
+ }
+ }
}
}
}
return;
}
- // Find the span of the argument to `panic!()`, before expansion in the
- // case of `panic!(some_macro!())`.
+ // Find the span of the argument to `panic!()` or `unreachable!`, before expansion in the
+ // case of `panic!(some_macro!())` or `unreachable!(some_macro!())`.
// We don't use source_callsite(), because this `panic!(..)` might itself
// be expanded from another macro, in which case we want to stop at that
// expansion.
| sym::std_panic_macro
| sym::assert_macro
| sym::debug_assert_macro
+ | sym::unreachable_macro
) {
break;
}
use rustc_span::symbol::sym;
declare_tool_lint! {
- /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to always be passed by value.
- /// This is usually used for types that are thin wrappers around references, so there is no benefit to an extra
- /// layer of indirection. (Example: `Ty` which is a reference to a `TyS`)
+ /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to
+ /// always be passed by value. This is usually used for types that are thin wrappers around
+ /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which
+ /// is a reference to an `Interned<TyS>`)
pub rustc::PASS_BY_VALUE,
Warn,
"pass by reference of a type flagged as `#[rustc_pass_by_value]`",
let path_segment = path.segments.last().unwrap();
return Some(format!("{}{}", name, gen_args(cx, path_segment)));
}
- Res::SelfTy(None, Some((did, _))) => {
+ Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => {
if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
if cx.tcx.has_attr(adt.did, sym::rustc_pass_by_value) {
return Some(cx.tcx.def_path_str_with_substs(adt.did, substs));
));
}
if let Some(sugg_ty) =
- get_type_suggestion(&cx.typeck_results().node_type(expr.hir_id), val, negative)
+ get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
{
if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
let (sans_suffix, _) = repr_str.split_at(pos);
max,
));
if let Some(sugg_ty) =
- get_type_suggestion(&cx.typeck_results().node_type(e.hir_id), v, negative)
+ get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
{
err.help(&format!("consider using the type `{}` instead", sugg_ty));
}
}
}
for arg in sig.inputs() {
- let r = self.check_type_for_ffi(cache, arg);
+ let r = self.check_type_for_ffi(cache, *arg);
match r {
FfiSafe => {}
_ => {
let sig = self.cx.tcx.erase_late_bound_regions(sig);
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
- self.check_type_for_ffi_and_report_errors(input_hir.span, input_ty, false, false);
+ self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
}
if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
StackProtectReq = 30,
StackProtectStrong = 31,
StackProtect = 32,
+ NoUndef = 33,
};
typedef struct OpaqueRustString *RustStringRef;
install_fatal_error_handler(FatalErrorHandler);
}
+extern "C" void LLVMRustDisableSystemDialogsOnCrash() {
+ sys::DisableSystemDialogsOnCrash();
+}
+
extern "C" char *LLVMRustGetLastError(void) {
char *Ret = LastError;
LastError = nullptr;
return Attribute::StackProtectStrong;
case StackProtect:
return Attribute::StackProtect;
+ case NoUndef:
+ return Attribute::NoUndef;
}
report_fatal_error("bad AttributeKind");
}
AddAttribute(F, Index, Attr);
}
+extern "C" void LLVMRustEmitUWTableAttr(LLVMValueRef Fn, bool Async) {
+ Function *F = unwrap<Function>(Fn);
+#if LLVM_VERSION_LT(15, 0)
+ Attribute Attr = Attribute::get(F->getContext(), Attribute::UWTable);
+#else
+ Attribute Attr = Attribute::getWithUWTableKind(
+ F->getContext(), Async ? UWTableKind::Async : UWTableKind::Sync);
+#endif
+ AddAttribute(F, AttributeList::AttrIndex::FunctionIndex, Attr);
+}
+
extern "C" void LLVMRustAddFunctionAttrStringValue(LLVMValueRef Fn,
unsigned Index,
const char *Name,
use rustc_errors::{struct_span_err, FatalError};
use rustc_session::config::{self, CrateType};
use rustc_session::cstore::{CrateSource, MetadataLoader};
-use rustc_session::filesearch::{FileDoesntMatch, FileMatches, FileSearch};
+use rustc_session::filesearch::FileSearch;
use rustc_session::search_paths::PathKind;
use rustc_session::utils::CanonicalizedPath;
use rustc_session::Session;
extra_prefix: &str,
seen_paths: &mut FxHashSet<PathBuf>,
) -> Result<Option<Library>, CrateError> {
- // want: crate_name.dir_part() + prefix + crate_name.file_part + "-"
- let dylib_prefix = format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix);
- let rlib_prefix = format!("lib{}{}", self.crate_name, extra_prefix);
+ let rmeta_prefix = &format!("lib{}{}", self.crate_name, extra_prefix);
+ let rlib_prefix = rmeta_prefix;
+ let dylib_prefix =
+ &format!("{}{}{}", self.target.dll_prefix, self.crate_name, extra_prefix);
let staticlib_prefix =
- format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix);
+ &format!("{}{}{}", self.target.staticlib_prefix, self.crate_name, extra_prefix);
+
+ let rmeta_suffix = ".rmeta";
+ let rlib_suffix = ".rlib";
+ let dylib_suffix = &self.target.dll_suffix;
+ let staticlib_suffix = &self.target.staticlib_suffix;
let mut candidates: FxHashMap<_, (FxHashMap<_, _>, FxHashMap<_, _>, FxHashMap<_, _>)> =
Default::default();
- let mut staticlibs = vec![];
// First, find all possible candidate rlibs and dylibs purely based on
// the name of the files themselves. We're trying to match against an
// of the crate id (path/name/id).
//
// The goal of this step is to look at as little metadata as possible.
- self.filesearch.search(|spf, kind| {
- let file = match &spf.file_name_str {
- None => return FileDoesntMatch,
- Some(file) => file,
- };
- let (hash, found_kind) = if file.starts_with(&rlib_prefix) && file.ends_with(".rlib") {
- (&file[(rlib_prefix.len())..(file.len() - ".rlib".len())], CrateFlavor::Rlib)
- } else if file.starts_with(&rlib_prefix) && file.ends_with(".rmeta") {
- (&file[(rlib_prefix.len())..(file.len() - ".rmeta".len())], CrateFlavor::Rmeta)
- } else if file.starts_with(&dylib_prefix) && file.ends_with(&self.target.dll_suffix) {
- (
- &file[(dylib_prefix.len())..(file.len() - self.target.dll_suffix.len())],
- CrateFlavor::Dylib,
- )
- } else {
- if file.starts_with(&staticlib_prefix)
- && file.ends_with(&self.target.staticlib_suffix)
- {
- staticlibs
- .push(CrateMismatch { path: spf.path.clone(), got: "static".to_string() });
- }
- return FileDoesntMatch;
- };
+ // Unfortunately, the prefix-based matching sometimes is over-eager.
+ // E.g. if `rlib_suffix` is `libstd` it'll match the file
+ // `libstd_detect-8d6701fb958915ad.rlib` (incorrect) as well as
+ // `libstd-f3ab5b1dea981f17.rlib` (correct). But this is hard to avoid
+ // given that `extra_filename` comes from the `-C extra-filename`
+ // option and thus can be anything, and the incorrect match will be
+ // handled safely in `extract_one`.
+ for search_path in self.filesearch.search_paths() {
+ debug!("searching {}", search_path.dir.display());
+ for spf in search_path.files.iter() {
+ debug!("testing {}", spf.path.display());
+
+ let f = &spf.file_name_str;
+ let (hash, kind) = if f.starts_with(rlib_prefix) && f.ends_with(rlib_suffix) {
+ (&f[rlib_prefix.len()..(f.len() - rlib_suffix.len())], CrateFlavor::Rlib)
+ } else if f.starts_with(rmeta_prefix) && f.ends_with(rmeta_suffix) {
+ (&f[rmeta_prefix.len()..(f.len() - rmeta_suffix.len())], CrateFlavor::Rmeta)
+ } else if f.starts_with(dylib_prefix) && f.ends_with(dylib_suffix) {
+ (&f[dylib_prefix.len()..(f.len() - dylib_suffix.len())], CrateFlavor::Dylib)
+ } else {
+ if f.starts_with(staticlib_prefix) && f.ends_with(staticlib_suffix) {
+ self.crate_rejections.via_kind.push(CrateMismatch {
+ path: spf.path.clone(),
+ got: "static".to_string(),
+ });
+ }
+ continue;
+ };
- info!("lib candidate: {}", spf.path.display());
+ info!("lib candidate: {}", spf.path.display());
- let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default();
- let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone());
- if seen_paths.contains(&path) {
- return FileDoesntMatch;
- };
- seen_paths.insert(path.clone());
- match found_kind {
- CrateFlavor::Rlib => rlibs.insert(path, kind),
- CrateFlavor::Rmeta => rmetas.insert(path, kind),
- CrateFlavor::Dylib => dylibs.insert(path, kind),
- };
- FileMatches
- });
- self.crate_rejections.via_kind.extend(staticlibs);
+ let (rlibs, rmetas, dylibs) = candidates.entry(hash.to_string()).or_default();
+ let path = fs::canonicalize(&spf.path).unwrap_or_else(|_| spf.path.clone());
+ if seen_paths.contains(&path) {
+ continue;
+ };
+ seen_paths.insert(path.clone());
+ match kind {
+ CrateFlavor::Rlib => rlibs.insert(path, search_path.kind),
+ CrateFlavor::Rmeta => rmetas.insert(path, search_path.kind),
+ CrateFlavor::Dylib => dylibs.insert(path, search_path.kind),
+ };
+ }
+ }
// We have now collected all known libraries into a set of candidates
// keyed of the filename hash listed. For each filename, we also have a
fn build_dll_import(&self, abi: Abi, item: &hir::ForeignItemRef) -> DllImport {
let calling_convention = if self.tcx.sess.target.arch == "x86" {
match abi {
- Abi::C { .. } | Abi::Cdecl => DllCallingConvention::C,
+ Abi::C { .. } | Abi::Cdecl { .. } => DllCallingConvention::C,
Abi::Stdcall { .. } | Abi::System { .. } => {
DllCallingConvention::Stdcall(self.i686_arg_list_size(item))
}
- Abi::Fastcall => DllCallingConvention::Fastcall(self.i686_arg_list_size(item)),
+ Abi::Fastcall { .. } => {
+ DllCallingConvention::Fastcall(self.i686_arg_list_size(item))
+ }
// Vectorcall is intentionally not supported at this time.
_ => {
self.tcx.sess.span_fatal(
}
} else {
match abi {
- Abi::C { .. } | Abi::Win64 | Abi::System { .. } => DllCallingConvention::C,
+ Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
_ => {
self.tcx.sess.span_fatal(
item.span,
/// How to link (or not link) this crate to the currently compiled crate.
dep_kind: Lock<CrateDepKind>,
/// Filesystem location of this crate.
- source: CrateSource,
+ source: Lrc<CrateSource>,
/// Whether or not this crate should be consider a private dependency
/// for purposes of the 'exported_private_dependencies' lint
private_dep: bool,
}
/// Iterates over the language items in the given crate.
- fn get_lang_items(self, tcx: TyCtxt<'tcx>) -> &'tcx [(DefId, usize)] {
- tcx.arena.alloc_from_iter(
- self.root
- .lang_items
- .decode(self)
- .map(|(def_index, index)| (self.local_def_id(def_index), index)),
- )
+ fn get_lang_items(self) -> impl Iterator<Item = (DefId, usize)> + 'a {
+ self.root
+ .lang_items
+ .decode(self)
+ .map(move |(def_index, index)| (self.local_def_id(def_index), index))
}
/// Iterates over the diagnostic items in the given crate.
)
}
+ /// Decodes all inherent impls in the crate (for rustdoc).
+ fn get_inherent_impls(self) -> impl Iterator<Item = (DefId, DefId)> + 'a {
+ (0..self.root.tables.inherent_impls.size()).flat_map(move |i| {
+ let ty_index = DefIndex::from_usize(i);
+ let ty_def_id = self.local_def_id(ty_index);
+ self.root
+ .tables
+ .inherent_impls
+ .get(self, ty_index)
+ .unwrap_or_else(Lazy::empty)
+ .decode(self)
+ .map(move |impl_index| (ty_def_id, self.local_def_id(impl_index)))
+ })
+ }
+
+ /// Decodes all traits in the crate (for rustdoc and rustc diagnostics).
fn get_traits(self) -> impl Iterator<Item = DefId> + 'a {
self.root.traits.decode(self).map(move |index| self.local_def_id(index))
}
+ /// Decodes all trait impls in the crate (for rustdoc).
fn get_trait_impls(self) -> impl Iterator<Item = (DefId, DefId, Option<SimplifiedType>)> + 'a {
self.cdata.trait_impls.iter().flat_map(move |((trait_cnum_raw, trait_index), impls)| {
let trait_def_id = DefId {
cnum_map,
dependencies,
dep_kind: Lock::new(dep_kind),
- source,
+ source: Lrc::new(source),
private_dep,
host_hash,
extern_crate: Lock::new(None),
}
crate fn source(&self) -> &CrateSource {
- &self.source
+ &*self.source
}
crate fn dep_kind(&self) -> CrateDepKind {
use crate::native_libs;
use rustc_ast as ast;
-use rustc_data_structures::stable_map::FxHashMap;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::definitions::{DefKey, DefPath, DefPathHash};
use rustc_middle::ty::fast_reject::SimplifiedType;
use rustc_middle::ty::query::{ExternProviders, Providers};
use rustc_middle::ty::{self, TyCtxt, Visibility};
-use rustc_session::cstore::{CrateSource, CrateStore, ForeignModule};
+use rustc_session::cstore::{CrateSource, CrateStore};
use rustc_session::utils::NativeLibKind;
use rustc_session::{Session, StableCrateId};
use rustc_span::hygiene::{ExpnHash, ExpnId};
promoted_mir => { tcx.arena.alloc(cdata.get_promoted_mir(tcx, def_id.index)) }
thir_abstract_const => { cdata.get_thir_abstract_const(tcx, def_id.index) }
unused_generic_params => { cdata.get_unused_generic_params(def_id.index) }
- const_param_default => { tcx.mk_const(cdata.get_const_param_default(tcx, def_id.index)) }
+ const_param_default => { cdata.get_const_param_default(tcx, def_id.index) }
mir_const_qualif => { cdata.mir_const_qualif(def_id.index) }
fn_sig => { cdata.fn_sig(def_id.index, tcx) }
inherent_impls => { cdata.get_inherent_implementations_for_type(tcx, def_id.index) }
reachable_non_generics
}
- native_libraries => { Lrc::new(cdata.get_native_libraries(tcx.sess).collect()) }
- foreign_modules => {
- Lrc::new(cdata.get_foreign_modules(tcx.sess).map(|m| (m.def_id, m)).collect())
- }
+ native_libraries => { cdata.get_native_libraries(tcx.sess).collect() }
+ foreign_modules => { cdata.get_foreign_modules(tcx.sess).map(|m| (m.def_id, m)).collect() }
crate_hash => { cdata.root.hash }
crate_host_hash => { cdata.host_hash }
crate_name => { cdata.root.name }
tcx.arena.alloc_slice(&result)
}
defined_lib_features => { cdata.get_lib_features(tcx) }
- defined_lang_items => { cdata.get_lang_items(tcx) }
+ defined_lang_items => { tcx.arena.alloc_from_iter(cdata.get_lang_items()) }
diagnostic_items => { cdata.get_diagnostic_items() }
missing_lang_items => { cdata.get_missing_lang_items(tcx) }
r
}
- used_crate_source => { Lrc::new(cdata.source.clone()) }
+ used_crate_source => { Lrc::clone(&cdata.source) }
exported_symbols => {
let syms = cdata.exported_symbols(tcx);
},
native_libraries: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- Lrc::new(native_libs::collect(tcx))
+ native_libs::collect(tcx)
},
foreign_modules: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
- let modules: FxHashMap<DefId, ForeignModule> =
- foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect();
- Lrc::new(modules)
+ foreign_modules::collect(tcx).into_iter().map(|m| (m.def_id, m)).collect()
},
// Returns a map from a sufficiently visible external item (i.e., an
visible_parent_map.entry(child).or_insert(parent);
}
- Lrc::new(visible_parent_map)
+ visible_parent_map
},
dependency_formats: |tcx, ()| Lrc::new(crate::dependency_format::calculate(tcx)),
self.get_crate_data(def.krate).get_fn_has_self_parameter(def.index)
}
- pub fn crate_source_untracked(&self, cnum: CrateNum) -> CrateSource {
+ pub fn crate_source_untracked(&self, cnum: CrateNum) -> Lrc<CrateSource> {
self.get_crate_data(cnum).source.clone()
}
self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
}
+ /// Decodes all traits in the crate (for rustdoc).
pub fn traits_in_crate_untracked(&self, cnum: CrateNum) -> impl Iterator<Item = DefId> + '_ {
self.get_crate_data(cnum).get_traits()
}
+ /// Decodes all trait impls in the crate (for rustdoc).
pub fn trait_impls_in_crate_untracked(
&self,
cnum: CrateNum,
) -> impl Iterator<Item = (DefId, DefId, Option<SimplifiedType>)> + '_ {
self.get_crate_data(cnum).get_trait_impls()
}
+
+ /// Decodes all inherent impls in the crate (for rustdoc).
+ pub fn inherent_impls_in_crate_untracked(
+ &self,
+ cnum: CrateNum,
+ ) -> impl Iterator<Item = (DefId, DefId)> + '_ {
+ self.get_crate_data(cnum).get_inherent_impls()
+ }
+
+ /// Decodes all lang items in the crate (for rustdoc).
+ pub fn lang_items_untracked(&self, cnum: CrateNum) -> impl Iterator<Item = DefId> + '_ {
+ self.get_crate_data(cnum).get_lang_items().map(|(def_id, _)| def_id)
+ }
}
impl CrateStore for CStore {
use rustc_middle::thir;
use rustc_middle::traits::specialization_graph;
use rustc_middle::ty::codec::TyEncoder;
-use rustc_middle::ty::fast_reject::{self, SimplifiedType, SimplifyParams, StripReferences};
+use rustc_middle::ty::fast_reject::{self, SimplifiedType, SimplifyParams};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, SymbolName, Ty, TyCtxt};
use rustc_serialize::{opaque, Encodable, Encoder};
for local_id in hir.iter_local_def_id() {
let def_id = local_id.to_def_id();
let def_kind = tcx.opt_def_kind(local_id);
- let def_kind = if let Some(def_kind) = def_kind { def_kind } else { continue };
+ let Some(def_kind) = def_kind else { continue };
record!(self.tables.def_kind[def_id] <- match def_kind {
// Replace Ctor by the enclosing object to avoid leaking details in children crates.
DefKind::Ctor(CtorOf::Struct, _) => DefKind::Struct,
hash: self.tcx.crate_hash(cnum),
host_hash: self.tcx.crate_host_hash(cnum),
kind: self.tcx.dep_kind(cnum),
- extra_filename: self.tcx.extra_filename(cnum),
+ extra_filename: self.tcx.extra_filename(cnum).clone(),
};
(cnum, dep)
})
self.tcx,
trait_ref.self_ty(),
SimplifyParams::No,
- StripReferences::No,
);
self.impls
Vec<rustc_middle::traits::query::OutlivesBound<'tcx>>
>
>,
+ [] dtorck_constraint: rustc_middle::traits::query::DtorckConstraint<'tcx>,
+ [] candidate_step: rustc_middle::traits::query::CandidateStep<'tcx>,
+ [] autoderef_bad_ty: rustc_middle::traits::query::MethodAutoderefBadTy<'tcx>,
[] type_op_subtype:
rustc_middle::infer::canonical::Canonical<'tcx,
rustc_middle::infer::canonical::QueryResponse<'tcx, ()>
// Interned types
[] tys: rustc_middle::ty::TyS<'tcx>,
- [] predicates: rustc_middle::ty::PredicateInner<'tcx>,
+ [] predicates: rustc_middle::ty::PredicateS<'tcx>,
+ [] consts: rustc_middle::ty::ConstS<'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
// This is used to decode the &'tcx [Span] for InlineAsm's line_spans.
[decode] span: rustc_span::Span,
[decode] used_trait_imports: rustc_data_structures::fx::FxHashSet<rustc_hir::def_id::LocalDefId>,
+ [decode] impl_source: rustc_middle::traits::ImplSource<'tcx, ()>,
[] dep_kind: rustc_middle::dep_graph::DepKindStruct,
]);
OP: for<'a> FnOnce(TaskDepsRef<'a>),
{
ty::tls::with_context_opt(|icx| {
- let icx = if let Some(icx) = icx { icx } else { return };
+ let Some(icx) = icx else { return };
op(icx.task_deps)
})
}
}
impl<'hir> Map<'hir> {
- pub fn krate(&self) -> &'hir Crate<'hir> {
+ pub fn krate(self) -> &'hir Crate<'hir> {
self.tcx.hir_crate(())
}
- pub fn root_module(&self) -> &'hir Mod<'hir> {
+ pub fn root_module(self) -> &'hir Mod<'hir> {
match self.tcx.hir_owner(CRATE_DEF_ID).map(|o| o.node) {
Some(OwnerNode::Crate(item)) => item,
_ => bug!(),
}
}
- pub fn items(&self) -> impl Iterator<Item = &'hir Item<'hir>> + 'hir {
+ pub fn items(self) -> impl Iterator<Item = &'hir Item<'hir>> + 'hir {
let krate = self.krate();
krate.owners.iter().filter_map(|owner| match owner.as_owner()?.node() {
OwnerNode::Item(item) => Some(item),
})
}
- pub fn def_key(&self, def_id: LocalDefId) -> DefKey {
+ pub fn def_key(self, def_id: LocalDefId) -> DefKey {
// Accessing the DefKey is ok, since it is part of DefPathHash.
self.tcx.untracked_resolutions.definitions.def_key(def_id)
}
- pub fn def_path_from_hir_id(&self, id: HirId) -> Option<DefPath> {
+ pub fn def_path_from_hir_id(self, id: HirId) -> Option<DefPath> {
self.opt_local_def_id(id).map(|def_id| self.def_path(def_id))
}
- pub fn def_path(&self, def_id: LocalDefId) -> DefPath {
+ pub fn def_path(self, def_id: LocalDefId) -> DefPath {
// Accessing the DefPath is ok, since it is part of DefPathHash.
self.tcx.untracked_resolutions.definitions.def_path(def_id)
}
}
#[inline]
- pub fn local_def_id(&self, hir_id: HirId) -> LocalDefId {
+ pub fn local_def_id(self, hir_id: HirId) -> LocalDefId {
self.opt_local_def_id(hir_id).unwrap_or_else(|| {
bug!(
"local_def_id: no entry for `{:?}`, which has a map of `{:?}`",
}
#[inline]
- pub fn opt_local_def_id(&self, hir_id: HirId) -> Option<LocalDefId> {
+ pub fn opt_local_def_id(self, hir_id: HirId) -> Option<LocalDefId> {
if hir_id.local_id == ItemLocalId::new(0) {
Some(hir_id.owner)
} else {
}
#[inline]
- pub fn local_def_id_to_hir_id(&self, def_id: LocalDefId) -> HirId {
+ pub fn local_def_id_to_hir_id(self, def_id: LocalDefId) -> HirId {
self.tcx.local_def_id_to_hir_id(def_id)
}
- pub fn iter_local_def_id(&self) -> impl Iterator<Item = LocalDefId> + '_ {
+ pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'hir {
// Create a dependency to the crate to be sure we reexcute this when the amount of
// definitions change.
self.tcx.ensure().hir_crate(());
self.tcx.untracked_resolutions.definitions.iter_local_def_id()
}
- pub fn opt_def_kind(&self, local_def_id: LocalDefId) -> Option<DefKind> {
+ pub fn opt_def_kind(self, local_def_id: LocalDefId) -> Option<DefKind> {
let hir_id = self.local_def_id_to_hir_id(local_def_id);
let def_kind = match self.find(hir_id)? {
Node::Item(item) => match item.kind {
Some(def_kind)
}
- pub fn def_kind(&self, local_def_id: LocalDefId) -> DefKind {
+ pub fn def_kind(self, local_def_id: LocalDefId) -> DefKind {
self.opt_def_kind(local_def_id)
.unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", local_def_id))
}
- pub fn find_parent_node(&self, id: HirId) -> Option<HirId> {
+ pub fn find_parent_node(self, id: HirId) -> Option<HirId> {
if id.local_id == ItemLocalId::from_u32(0) {
Some(self.tcx.hir_owner_parent(id.owner))
} else {
}
}
- pub fn get_parent_node(&self, hir_id: HirId) -> HirId {
+ pub fn get_parent_node(self, hir_id: HirId) -> HirId {
self.find_parent_node(hir_id).unwrap()
}
/// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found.
- pub fn find(&self, id: HirId) -> Option<Node<'hir>> {
+ pub fn find(self, id: HirId) -> Option<Node<'hir>> {
if id.local_id == ItemLocalId::from_u32(0) {
let owner = self.tcx.hir_owner(id.owner)?;
Some(owner.node.into())
/// Retrieves the `Node` corresponding to `id`, returning `None` if cannot be found.
#[inline]
- pub fn find_by_def_id(&self, id: LocalDefId) -> Option<Node<'hir>> {
+ pub fn find_by_def_id(self, id: LocalDefId) -> Option<Node<'hir>> {
self.find(self.local_def_id_to_hir_id(id))
}
/// Retrieves the `Node` corresponding to `id`, panicking if it cannot be found.
- pub fn get(&self, id: HirId) -> Node<'hir> {
+ 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]
- pub fn get_by_def_id(&self, id: LocalDefId) -> Node<'hir> {
+ 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))
}
- pub fn get_if_local(&self, id: DefId) -> Option<Node<'hir>> {
+ pub fn get_if_local(self, id: DefId) -> Option<Node<'hir>> {
id.as_local().and_then(|id| self.find(self.local_def_id_to_hir_id(id)))
}
- pub fn get_generics(&self, id: LocalDefId) -> Option<&'hir Generics<'hir>> {
+ pub fn get_generics(self, id: LocalDefId) -> Option<&'hir Generics<'hir>> {
let node = self.tcx.hir_owner(id)?;
match node.node {
OwnerNode::ImplItem(impl_item) => Some(&impl_item.generics),
}
}
- pub fn item(&self, id: ItemId) -> &'hir Item<'hir> {
+ pub fn item(self, id: ItemId) -> &'hir Item<'hir> {
self.tcx.hir_owner(id.def_id).unwrap().node.expect_item()
}
- pub fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> {
+ pub fn trait_item(self, id: TraitItemId) -> &'hir TraitItem<'hir> {
self.tcx.hir_owner(id.def_id).unwrap().node.expect_trait_item()
}
- pub fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> {
+ pub fn impl_item(self, id: ImplItemId) -> &'hir ImplItem<'hir> {
self.tcx.hir_owner(id.def_id).unwrap().node.expect_impl_item()
}
- pub fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> {
+ pub fn foreign_item(self, id: ForeignItemId) -> &'hir ForeignItem<'hir> {
self.tcx.hir_owner(id.def_id).unwrap().node.expect_foreign_item()
}
- pub fn body(&self, id: BodyId) -> &'hir Body<'hir> {
+ pub fn body(self, id: BodyId) -> &'hir Body<'hir> {
self.tcx.hir_owner_nodes(id.hir_id.owner).unwrap().bodies[&id.hir_id.local_id]
}
- pub fn fn_decl_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> {
+ pub fn fn_decl_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnDecl<'hir>> {
if let Some(node) = self.find(hir_id) {
fn_decl(node)
} else {
}
}
- pub fn fn_sig_by_hir_id(&self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> {
+ pub fn fn_sig_by_hir_id(self, hir_id: HirId) -> Option<&'hir FnSig<'hir>> {
if let Some(node) = self.find(hir_id) {
fn_sig(node)
} else {
}
}
- pub fn enclosing_body_owner(&self, hir_id: HirId) -> HirId {
+ pub fn enclosing_body_owner(self, hir_id: HirId) -> HirId {
for (parent, _) in self.parent_iter(hir_id) {
if let Some(body) = self.maybe_body_owned_by(parent) {
return self.body_owner(body);
/// Returns the `HirId` that corresponds to the definition of
/// which this is the body of, i.e., a `fn`, `const` or `static`
/// item (possibly associated), a closure, or a `hir::AnonConst`.
- pub fn body_owner(&self, BodyId { hir_id }: BodyId) -> HirId {
+ 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)));
parent
}
- pub fn body_owner_def_id(&self, id: BodyId) -> LocalDefId {
+ pub fn body_owner_def_id(self, id: BodyId) -> LocalDefId {
self.local_def_id(self.body_owner(id))
}
/// Given a `HirId`, returns the `BodyId` associated with it,
/// if the node is a body owner, otherwise returns `None`.
- pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option<BodyId> {
+ pub fn maybe_body_owned_by(self, hir_id: HirId) -> Option<BodyId> {
self.find(hir_id).map(associated_body).flatten()
}
/// Given a body owner's id, returns the `BodyId` associated with it.
- pub fn body_owned_by(&self, id: HirId) -> BodyId {
+ pub fn body_owned_by(self, id: HirId) -> BodyId {
self.maybe_body_owned_by(id).unwrap_or_else(|| {
span_bug!(
self.span(id),
})
}
- pub fn body_param_names(&self, id: BodyId) -> impl Iterator<Item = Ident> + 'hir {
+ pub fn body_param_names(self, id: BodyId) -> impl Iterator<Item = Ident> + 'hir {
self.body(id).params.iter().map(|arg| match arg.pat.kind {
PatKind::Binding(_, _, ident, _) => ident,
_ => Ident::empty(),
/// Returns the `BodyOwnerKind` of this `LocalDefId`.
///
/// Panics if `LocalDefId` does not have an associated body.
- pub fn body_owner_kind(&self, id: HirId) -> BodyOwnerKind {
+ pub fn body_owner_kind(self, id: HirId) -> BodyOwnerKind {
match self.get(id) {
Node::Item(&Item { kind: ItemKind::Const(..), .. })
| Node::TraitItem(&TraitItem { kind: TraitItemKind::Const(..), .. })
/// This should only be used for determining the context of a body, a return
/// value of `Some` does not always suggest that the owner of the body is `const`,
/// just that it has to be checked as if it were.
- pub fn body_const_context(&self, did: LocalDefId) -> Option<ConstContext> {
+ pub fn body_const_context(self, did: LocalDefId) -> Option<ConstContext> {
let hir_id = self.local_def_id_to_hir_id(did);
let ccx = match self.body_owner_kind(hir_id) {
BodyOwnerKind::Const => ConstContext::Const,
});
}
- pub fn ty_param_owner(&self, id: HirId) -> LocalDefId {
+ pub fn ty_param_owner(self, id: HirId) -> LocalDefId {
match self.get(id) {
Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => {
id.expect_owner()
}
}
- pub fn ty_param_name(&self, id: HirId) -> Symbol {
+ pub fn ty_param_name(self, id: HirId) -> Symbol {
match self.get(id) {
Node::Item(&Item { kind: ItemKind::Trait(..) | ItemKind::TraitAlias(..), .. }) => {
kw::SelfUpper
}
}
- pub fn trait_impls(&self, trait_did: DefId) -> &'hir [LocalDefId] {
+ pub fn trait_impls(self, trait_did: DefId) -> &'hir [LocalDefId] {
self.tcx.all_local_trait_impls(()).get(&trait_did).map_or(&[], |xs| &xs[..])
}
/// Gets the attributes on the crate. This is preferable to
/// invoking `krate.attrs` because it registers a tighter
/// dep-graph access.
- pub fn krate_attrs(&self) -> &'hir [ast::Attribute] {
+ pub fn krate_attrs(self) -> &'hir [ast::Attribute] {
self.attrs(CRATE_HIR_ID)
}
- pub fn get_module(&self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) {
+ pub fn get_module(self, module: LocalDefId) -> (&'hir Mod<'hir>, Span, HirId) {
let hir_id = HirId::make_owner(module);
match self.tcx.hir_owner(module).map(|o| o.node) {
Some(OwnerNode::Item(&Item { span, kind: ItemKind::Mod(ref m), .. })) => {
/// follows lexical scoping rules -- then you want a different
/// approach. You should override `visit_nested_item` in your
/// visitor and then call `intravisit::walk_crate` instead.
- pub fn visit_all_item_likes<V>(&self, visitor: &mut V)
+ pub fn visit_all_item_likes<V>(self, visitor: &mut V)
where
V: itemlikevisit::ItemLikeVisitor<'hir>,
{
}
/// A parallel version of `visit_all_item_likes`.
- pub fn par_visit_all_item_likes<V>(&self, visitor: &V)
+ pub fn par_visit_all_item_likes<V>(self, visitor: &V)
where
V: itemlikevisit::ParItemLikeVisitor<'hir> + Sync + Send,
{
})
}
- pub fn visit_item_likes_in_module<V>(&self, module: LocalDefId, visitor: &mut V)
+ pub fn visit_item_likes_in_module<V>(self, module: LocalDefId, visitor: &mut V)
where
V: ItemLikeVisitor<'hir>,
{
}
}
- pub fn for_each_module(&self, f: impl Fn(LocalDefId)) {
+ pub fn for_each_module(self, f: impl Fn(LocalDefId)) {
let mut queue = VecDeque::new();
queue.push_back(CRATE_DEF_ID);
#[cfg(not(parallel_compiler))]
#[inline]
- pub fn par_for_each_module(&self, f: impl Fn(LocalDefId)) {
+ pub fn par_for_each_module(self, f: impl Fn(LocalDefId)) {
self.for_each_module(f)
}
#[cfg(parallel_compiler)]
- pub fn par_for_each_module(&self, f: impl Fn(LocalDefId) + Sync) {
+ pub fn par_for_each_module(self, f: impl Fn(LocalDefId) + Sync) {
use rustc_data_structures::sync::{par_iter, ParallelIterator};
par_iter_submodules(self.tcx, CRATE_DEF_ID, &f);
}
/// Checks if the node is left-hand side of an assignment.
- pub fn is_lhs(&self, id: HirId) -> bool {
+ pub fn is_lhs(self, id: HirId) -> bool {
match self.find(self.get_parent_node(id)) {
Some(Node::Expr(expr)) => match expr.kind {
ExprKind::Assign(lhs, _rhs, _span) => lhs.hir_id == id,
/// Whether the expression pointed at by `hir_id` belongs to a `const` evaluation context.
/// Used exclusively for diagnostics, to avoid suggestion function calls.
- pub fn is_inside_const_context(&self, hir_id: HirId) -> bool {
+ pub fn is_inside_const_context(self, hir_id: HirId) -> bool {
self.body_const_context(self.local_def_id(self.enclosing_body_owner(hir_id))).is_some()
}
/// false
/// }
/// ```
- pub fn get_return_block(&self, id: HirId) -> Option<HirId> {
+ pub fn get_return_block(self, id: HirId) -> Option<HirId> {
let mut iter = self.parent_iter(id).peekable();
let mut ignore_tail = false;
if let Some(node) = self.find(id) {
/// parent item is in this map. The "parent item" is the closest parent node
/// in the HIR which is recorded by the map and is an item, either an item
/// in a module, trait, or impl.
- pub fn get_parent_item(&self, hir_id: HirId) -> LocalDefId {
+ pub fn get_parent_item(self, hir_id: HirId) -> LocalDefId {
if let Some((def_id, _node)) = self.parent_owner_iter(hir_id).next() {
def_id
} else {
/// Returns the `HirId` of `id`'s nearest module parent, or `id` itself if no
/// module parent is in this map.
- pub(super) fn get_module_parent_node(&self, hir_id: HirId) -> LocalDefId {
+ pub(super) fn get_module_parent_node(self, hir_id: HirId) -> LocalDefId {
for (def_id, node) in self.parent_owner_iter(hir_id) {
if let OwnerNode::Item(&Item { kind: ItemKind::Mod(_), .. }) = node {
return def_id;
///
/// Used by error reporting when there's a type error in an if or match arm caused by the
/// expression needing to be unit.
- pub fn get_if_cause(&self, hir_id: HirId) -> Option<&'hir Expr<'hir>> {
+ pub fn get_if_cause(self, hir_id: HirId) -> Option<&'hir Expr<'hir>> {
for (_, node) in self.parent_iter(hir_id) {
match node {
Node::Item(_)
}
/// Returns the nearest enclosing scope. A scope is roughly an item or block.
- pub fn get_enclosing_scope(&self, hir_id: HirId) -> Option<HirId> {
+ pub fn get_enclosing_scope(self, hir_id: HirId) -> Option<HirId> {
for (hir_id, node) in self.parent_iter(hir_id) {
if let Node::Item(Item {
kind:
}
/// Returns the defining scope for an opaque type definition.
- pub fn get_defining_scope(&self, id: HirId) -> HirId {
+ pub fn get_defining_scope(self, id: HirId) -> HirId {
let mut scope = id;
loop {
scope = self.get_enclosing_scope(scope).unwrap_or(CRATE_HIR_ID);
}
}
- pub fn get_foreign_abi(&self, hir_id: HirId) -> Abi {
+ pub fn get_foreign_abi(self, hir_id: HirId) -> Abi {
let parent = self.get_parent_item(hir_id);
if let Some(node) = self.tcx.hir_owner(parent) {
if let OwnerNode::Item(Item { kind: ItemKind::ForeignMod { abi, .. }, .. }) = node.node
)
}
- pub fn expect_item(&self, id: LocalDefId) -> &'hir Item<'hir> {
+ pub fn expect_item(self, id: LocalDefId) -> &'hir Item<'hir> {
match self.tcx.hir_owner(id) {
Some(Owner { node: OwnerNode::Item(item), .. }) => item,
_ => bug!("expected item, found {}", self.node_to_string(HirId::make_owner(id))),
}
}
- pub fn expect_impl_item(&self, id: LocalDefId) -> &'hir ImplItem<'hir> {
+ pub fn expect_impl_item(self, id: LocalDefId) -> &'hir ImplItem<'hir> {
match self.tcx.hir_owner(id) {
Some(Owner { node: OwnerNode::ImplItem(item), .. }) => item,
_ => bug!("expected impl item, found {}", self.node_to_string(HirId::make_owner(id))),
}
}
- pub fn expect_trait_item(&self, id: LocalDefId) -> &'hir TraitItem<'hir> {
+ pub fn expect_trait_item(self, id: LocalDefId) -> &'hir TraitItem<'hir> {
match self.tcx.hir_owner(id) {
Some(Owner { node: OwnerNode::TraitItem(item), .. }) => item,
_ => bug!("expected trait item, found {}", self.node_to_string(HirId::make_owner(id))),
}
}
- pub fn expect_variant(&self, id: HirId) -> &'hir Variant<'hir> {
+ pub fn expect_variant(self, id: HirId) -> &'hir Variant<'hir> {
match self.find(id) {
Some(Node::Variant(variant)) => variant,
_ => bug!("expected variant, found {}", self.node_to_string(id)),
}
}
- pub fn expect_foreign_item(&self, id: LocalDefId) -> &'hir ForeignItem<'hir> {
+ pub fn expect_foreign_item(self, id: LocalDefId) -> &'hir ForeignItem<'hir> {
match self.tcx.hir_owner(id) {
Some(Owner { node: OwnerNode::ForeignItem(item), .. }) => item,
_ => {
}
}
- pub fn expect_expr(&self, id: HirId) -> &'hir Expr<'hir> {
+ pub fn expect_expr(self, id: HirId) -> &'hir Expr<'hir> {
match self.find(id) {
Some(Node::Expr(expr)) => expr,
_ => bug!("expected expr, found {}", self.node_to_string(id)),
}
}
- pub fn opt_name(&self, id: HirId) -> Option<Symbol> {
+ pub fn opt_name(self, id: HirId) -> Option<Symbol> {
Some(match self.get(id) {
Node::Item(i) => i.ident.name,
Node::ForeignItem(fi) => fi.ident.name,
})
}
- pub fn name(&self, id: HirId) -> Symbol {
+ pub fn name(self, id: HirId) -> Symbol {
match self.opt_name(id) {
Some(name) => name,
None => bug!("no name for {}", self.node_to_string(id)),
/// Given a node ID, gets a list of attributes associated with the AST
/// corresponding to the node-ID.
- pub fn attrs(&self, id: HirId) -> &'hir [ast::Attribute] {
+ pub fn attrs(self, id: HirId) -> &'hir [ast::Attribute] {
self.tcx.hir_attrs(id.owner).get(id.local_id)
}
/// Gets the span of the definition of the specified HIR node.
/// This is used by `tcx.get_span`
- pub fn span(&self, hir_id: HirId) -> Span {
+ pub fn span(self, hir_id: HirId) -> Span {
self.opt_span(hir_id)
.unwrap_or_else(|| bug!("hir::map::Map::span: id not in map: {:?}", hir_id))
}
- pub fn opt_span(&self, hir_id: HirId) -> Option<Span> {
+ pub fn opt_span(self, hir_id: HirId) -> Option<Span> {
let span = match self.find(hir_id)? {
Node::Param(param) => param.span,
Node::Item(item) => match &item.kind {
/// Like `hir.span()`, but includes the body of function items
/// (instead of just the function header)
- pub fn span_with_body(&self, hir_id: HirId) -> Span {
+ pub fn span_with_body(self, hir_id: HirId) -> Span {
match self.find(hir_id) {
Some(Node::TraitItem(item)) => item.span,
Some(Node::ImplItem(impl_item)) => impl_item.span,
}
}
- pub fn span_if_local(&self, id: DefId) -> Option<Span> {
+ pub fn span_if_local(self, id: DefId) -> Option<Span> {
id.as_local().and_then(|id| self.opt_span(self.local_def_id_to_hir_id(id)))
}
- pub fn res_span(&self, res: Res) -> Option<Span> {
+ pub fn res_span(self, res: Res) -> Option<Span> {
match res {
Res::Err => None,
Res::Local(id) => Some(self.span(id)),
/// Get a representation of this `id` for debugging purposes.
/// NOTE: Do NOT use this in diagnostics!
- pub fn node_to_string(&self, id: HirId) -> String {
+ pub fn node_to_string(self, id: HirId) -> String {
hir_id_to_string(self, id)
}
/// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
/// called with the HirId for the `{ ... }` anon const
- pub fn opt_const_param_default_param_hir_id(&self, anon_const: HirId) -> Option<HirId> {
+ pub fn opt_const_param_default_param_hir_id(self, anon_const: HirId) -> Option<HirId> {
match self.get(self.get_parent_node(anon_const)) {
Node::GenericParam(GenericParam {
hir_id: param_id,
impl<'hir> intravisit::Map<'hir> for Map<'hir> {
fn find(&self, hir_id: HirId) -> Option<Node<'hir>> {
- self.find(hir_id)
+ (*self).find(hir_id)
}
fn body(&self, id: BodyId) -> &'hir Body<'hir> {
- self.body(id)
+ (*self).body(id)
}
fn item(&self, id: ItemId) -> &'hir Item<'hir> {
- self.item(id)
+ (*self).item(id)
}
fn trait_item(&self, id: TraitItemId) -> &'hir TraitItem<'hir> {
- self.trait_item(id)
+ (*self).trait_item(id)
}
fn impl_item(&self, id: ImplItemId) -> &'hir ImplItem<'hir> {
- self.impl_item(id)
+ (*self).impl_item(id)
}
fn foreign_item(&self, id: ForeignItemId) -> &'hir ForeignItem<'hir> {
- self.foreign_item(id)
+ (*self).foreign_item(id)
}
}
upstream_crates
}
-fn hir_id_to_string(map: &Map<'_>, id: HirId) -> String {
+fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
let id_str = format!(" (hir_id={})", id);
let path_str = || {
/// result.
#[derive(Clone, Debug)]
pub struct OriginalQueryValues<'tcx> {
- /// Map from the universes that appear in the query to the
- /// universes in the caller context. For the time being, we only
- /// ever put ROOT values into the query, so this map is very
+ /// Map from the universes that appear in the query to the universes in the
+ /// caller context. For all queries except `evaluate_goal` (used by Chalk),
+ /// we only ever put ROOT values into the query, so this map is very
/// simple.
pub universe_map: SmallVec<[ty::UniverseIndex; 4]>,
tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
}
GenericArgKind::Const(ct) => tcx
- .mk_const(ty::Const {
- ty: ct.ty,
+ .mk_const(ty::ConstS {
+ ty: ct.ty(),
val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from_u32(i)),
})
.into(),
impl<'tcx> UnifyKey for RegionVidKey<'tcx> {
type Value = UnifiedRegion<'tcx>;
+ #[inline]
fn index(&self) -> u32 {
self.vid.as_u32()
}
+ #[inline]
fn from_index(i: u32) -> Self {
RegionVidKey::from(ty::RegionVid::from_u32(i))
}
#[derive(Copy, Clone, Debug)]
pub enum ConstVariableValue<'tcx> {
- Known { value: &'tcx ty::Const<'tcx> },
+ Known { value: ty::Const<'tcx> },
Unknown { universe: ty::UniverseIndex },
}
impl<'tcx> ConstVariableValue<'tcx> {
/// If this value is known, returns the const it is known to be.
/// Otherwise, `None`.
- pub fn known(&self) -> Option<&'tcx ty::Const<'tcx>> {
+ pub fn known(&self) -> Option<ty::Const<'tcx>> {
match *self {
ConstVariableValue::Unknown { .. } => None,
ConstVariableValue::Known { value } => Some(value),
impl<'tcx> UnifyKey for ty::ConstVid<'tcx> {
type Value = ConstVarValue<'tcx>;
+ #[inline]
fn index(&self) -> u32 {
self.index
}
+ #[inline]
fn from_index(i: u32) -> Self {
ty::ConstVid { index: i, phantom: PhantomData }
}
}
impl<'tcx> UnifyValue for ConstVarValue<'tcx> {
- type Error = (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>);
+ type Error = (ty::Const<'tcx>, ty::Const<'tcx>);
fn unify_values(&value1: &Self, &value2: &Self) -> Result<Self, Self::Error> {
Ok(match (value1.val, value2.val) {
}
}
-impl<'tcx> EqUnifyValue for &'tcx ty::Const<'tcx> {}
+impl<'tcx> EqUnifyValue for ty::Const<'tcx> {}
pub fn replace_if_possible<'tcx, V, L>(
table: &mut UnificationTable<InPlace<ty::ConstVid<'tcx>, V, L>>,
- c: &'tcx ty::Const<'tcx>,
-) -> &'tcx ty::Const<'tcx>
+ c: ty::Const<'tcx>,
+) -> ty::Const<'tcx>
where
V: snapshot_vec::VecLike<unify::Delegate<ty::ConstVid<'tcx>>>,
L: UndoLogs<snapshot_vec::UndoLog<unify::Delegate<ty::ConstVid<'tcx>>>>,
{
- if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = c {
- match table.probe_value(*vid).val.known() {
+ if let ty::ConstKind::Infer(InferConst::Var(vid)) = c.val() {
+ match table.probe_value(vid).val.known() {
Some(c) => c,
None => c,
}
/// the MIR `InstrumentCoverage` pass and not added to the coverage map
/// during codegen.
const NO_COVERAGE = 1 << 15;
+ /// `#[used(linker)]`: indicates that LLVM nor the linker can eliminate this function.
+ const USED_LINKER = 1 << 16;
}
}
}
/// An entry in the `depr_map`.
-#[derive(Clone, HashStable, Debug)]
+#[derive(Copy, Clone, HashStable, Debug)]
pub struct DeprecationEntry {
/// The metadata of the attribute associated with this entry.
pub attr: Deprecation,
let raw_const = self.eval_to_allocation_raw(param_env.and(gid))?;
Ok(self.global_alloc(raw_const.alloc_id).unwrap_memory())
}
+
+ /// Destructure a constant ADT or array into its variant index and its field values.
+ pub fn destructure_const(
+ self,
+ param_env_and_val: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>,
+ ) -> mir::DestructuredConst<'tcx> {
+ self.try_destructure_const(param_env_and_val).unwrap()
+ }
}
use crate::ty::{self, List, Ty, TyCtxt};
use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
+use rustc_errors::ErrorReported;
use rustc_hir::def::{CtorKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_hir::{self, GeneratorKind};
predecessor_cache: PredecessorCache,
is_cyclic: GraphIsCyclicCache,
+
+ pub tainted_by_errors: Option<ErrorReported>,
}
impl<'tcx> Body<'tcx> {
var_debug_info: Vec<VarDebugInfo<'tcx>>,
span: Span,
generator_kind: Option<GeneratorKind>,
+ tainted_by_errors: Option<ErrorReported>,
) -> Self {
// We need `arg_count` locals, and one for the return place.
assert!(
is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
+ tainted_by_errors,
};
body.is_polymorphic = body.has_param_types_or_consts();
body
is_polymorphic: false,
predecessor_cache: PredecessorCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
+ tainted_by_errors: None,
};
body.is_polymorphic = body.has_param_types_or_consts();
body
/// - `Bivariant` -- no effect
AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance),
- /// Marks the start of a "coverage region", injected with '-Zinstrument-coverage'. A
+ /// Marks the start of a "coverage region", injected with '-Cinstrument-coverage'. A
/// `Coverage` statement carries metadata about the coverage region, used to inject a coverage
/// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates
/// executable code, to increment a counter variable at runtime, each time the code region is
Use(Operand<'tcx>),
/// [x; 32]
- Repeat(Operand<'tcx>, &'tcx ty::Const<'tcx>),
+ Repeat(Operand<'tcx>, ty::Const<'tcx>),
/// &x or &mut x
Ref(Region<'tcx>, BorrowKind, Place<'tcx>),
Mul,
/// The `/` operator (division)
///
- /// Division by zero is UB.
+ /// Division by zero is UB, because the compiler should have inserted checks
+ /// prior to this.
Div,
/// The `%` operator (modulus)
///
- /// Using zero as the modulus (second operand) is UB.
+ /// Using zero as the modulus (second operand) is UB, because the compiler
+ /// should have inserted checks prior to this.
Rem,
/// The `^` operator (bitwise xor)
BitXor,
match *self {
Use(ref place) => write!(fmt, "{:?}", place),
- Repeat(ref a, ref b) => {
+ Repeat(ref a, b) => {
write!(fmt, "[{:?}; ", a)?;
pretty_print_const(b, fmt, false)?;
write!(fmt, "]")
#[derive(Lift)]
pub enum ConstantKind<'tcx> {
/// This constant came from the type system
- Ty(&'tcx ty::Const<'tcx>),
+ Ty(ty::Const<'tcx>),
/// This constant cannot go back into the type system, as it represents
/// something the type system cannot handle (e.g. pointers).
Val(interpret::ConstValue<'tcx>, Ty<'tcx>),
impl<'tcx> Constant<'tcx> {
pub fn check_static_ptr(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
- match self.literal.const_for_ty()?.val.try_to_scalar() {
+ match self.literal.try_to_scalar() {
Some(Scalar::Ptr(ptr, _size)) => match tcx.global_alloc(ptr.provenance) {
GlobalAlloc::Static(def_id) => {
assert!(!tcx.is_thread_local_static(def_id));
}
}
-impl<'tcx> From<&'tcx ty::Const<'tcx>> for ConstantKind<'tcx> {
+impl<'tcx> From<ty::Const<'tcx>> for ConstantKind<'tcx> {
#[inline]
- fn from(ct: &'tcx ty::Const<'tcx>) -> Self {
+ fn from(ct: ty::Const<'tcx>) -> Self {
Self::Ty(ct)
}
}
impl<'tcx> ConstantKind<'tcx> {
/// Returns `None` if the constant is not trivially safe for use in the type system.
- pub fn const_for_ty(&self) -> Option<&'tcx ty::Const<'tcx>> {
+ pub fn const_for_ty(&self) -> Option<ty::Const<'tcx>> {
match self {
- ConstantKind::Ty(c) => Some(c),
+ ConstantKind::Ty(c) => Some(*c),
ConstantKind::Val(..) => None,
}
}
pub fn ty(&self) -> Ty<'tcx> {
match self {
- ConstantKind::Ty(c) => c.ty,
- ConstantKind::Val(_, ty) => ty,
+ ConstantKind::Ty(c) => c.ty(),
+ ConstantKind::Val(_, ty) => *ty,
}
}
#[inline]
pub fn try_to_value(self) -> Option<interpret::ConstValue<'tcx>> {
match self {
- ConstantKind::Ty(c) => c.val.try_to_value(),
+ ConstantKind::Ty(c) => c.val().try_to_value(),
ConstantKind::Val(val, _) => Some(val),
}
}
}
fn pretty_print_const<'tcx>(
- c: &ty::Const<'tcx>,
+ c: ty::Const<'tcx>,
fmt: &mut Formatter<'_>,
print_types: bool,
) -> fmt::Result {
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::MirSource;
use rustc_middle::mir::*;
-use rustc_middle::ty::{self, TyCtxt, TyS, TypeFoldable, TypeVisitor};
+use rustc_middle::ty::{self, TyCtxt};
use rustc_target::abi::Size;
-use std::ops::ControlFlow;
const INDENT: &str = " ";
/// Alignment for lining up comments following MIR statements
}
}
-fn use_verbose<'tcx>(ty: &&TyS<'tcx>, fn_def: bool) -> bool {
- match ty.kind() {
+fn use_verbose<'tcx>(ty: Ty<'tcx>, fn_def: bool) -> bool {
+ match *ty.kind() {
ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
// Unit type
ty::Tuple(g_args) if g_args.is_empty() => false,
- ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty(), fn_def)),
+ ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(g_arg.expect_ty(), fn_def)),
ty::Array(ty, _) => use_verbose(ty, fn_def),
ty::FnDef(..) => fn_def,
_ => true,
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
self.super_constant(constant, location);
let Constant { span, user_ty, literal } = constant;
- if use_verbose(&literal.ty(), true) {
+ if use_verbose(literal.ty(), true) {
self.push("mir::Constant");
self.push(&format!(
"+ span: {}",
}
}
- fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
+ fn visit_const(&mut self, constant: ty::Const<'tcx>, _: Location) {
self.super_const(constant);
- let ty::Const { ty, val, .. } = constant;
+ let ty = constant.ty();
+ let val = constant.val();
if use_verbose(ty, false) {
self.push("ty::Const");
self.push(&format!("+ ty: {:?}", ty));
fn alloc_ids_from_alloc(alloc: &Allocation) -> impl DoubleEndedIterator<Item = AllocId> + '_ {
alloc.relocations().values().map(|id| *id)
}
+
fn alloc_ids_from_const(val: ConstValue<'_>) -> impl Iterator<Item = AllocId> + '_ {
match val {
ConstValue::Scalar(interpret::Scalar::Ptr(ptr, _size)) => {
}
}
}
+
struct CollectAllocIds(BTreeSet<AllocId>);
- impl<'tcx> TypeVisitor<'tcx> for CollectAllocIds {
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::ConstKind::Value(val) = c.val {
+
+ impl<'tcx> Visitor<'tcx> for CollectAllocIds {
+ fn visit_const(&mut self, c: ty::Const<'tcx>, _loc: Location) {
+ if let ty::ConstKind::Value(val) = c.val() {
self.0.extend(alloc_ids_from_const(val));
}
- c.super_visit_with(self)
+ }
+
+ fn visit_constant(&mut self, c: &Constant<'tcx>, loc: Location) {
+ match c.literal {
+ ConstantKind::Ty(c) => self.visit_const(c, loc),
+ ConstantKind::Val(val, _) => {
+ self.0.extend(alloc_ids_from_const(val));
+ }
+ }
}
}
+
let mut visitor = CollectAllocIds(Default::default());
- body.visit_with(&mut visitor);
+ visitor.visit_body(body);
+
// `seen` contains all seen allocations, including the ones we have *not* printed yet.
// The protocol is to first `insert` into `seen`, and only if that returns `true`
// then push to `todo`.
pub concrete_opaque_types: VecMap<OpaqueTypeKey<'tcx>, Ty<'tcx>>,
pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
pub used_mut_upvars: SmallVec<[Field; 8]>,
+ pub tainted_by_errors: Option<ErrorReported>,
}
/// The result of the `mir_const_qualif` query.
pub needs_drop: bool,
pub needs_non_const_drop: bool,
pub custom_eq: bool,
- pub error_occured: Option<ErrorReported>,
+ pub tainted_by_errors: Option<ErrorReported>,
}
/// After we borrow check a closure, we are left with various
#[derive(Copy, Clone, Debug, HashStable)]
pub struct DestructuredConst<'tcx> {
pub variant: Option<VariantIdx>,
- pub fields: &'tcx [&'tcx ty::Const<'tcx>],
+ pub fields: &'tcx [ty::Const<'tcx>],
}
/// Coverage information summarized from a MIR if instrumented for source code coverage (see
-/// compiler option `-Zinstrument-coverage`). This information is generated by the
+/// compiler option `-Cinstrument-coverage`). This information is generated by the
/// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query.
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
pub struct CoverageInfo {
/// `PlaceElem`, where we can just use the `Ty` that is already
/// stored inline on field projection elems.
pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
- self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, ty| ty)
+ self.projection_ty_core(tcx, ty::ParamEnv::empty(), &elem, |_, _, &ty| ty)
}
/// `place_ty.projection_ty_core(tcx, elem, |...| { ... })`
ProjectionElem::Subslice { from, to, from_end } => {
PlaceTy::from_ty(match self.ty.kind() {
ty::Slice(..) => self.ty,
- ty::Array(inner, _) if !from_end => tcx.mk_array(inner, (to - from) as u64),
+ ty::Array(inner, _) if !from_end => tcx.mk_array(*inner, (to - from) as u64),
ty::Array(inner, size) if from_end => {
let size = size.eval_usize(tcx, param_env);
let len = size - (from as u64) - (to as u64);
- tcx.mk_array(inner, len)
+ tcx.mk_array(*inner, len)
}
_ => bug!("cannot subslice non-array type: `{:?}`", self),
})
pub fn as_switch(&self) -> Option<(&Operand<'tcx>, Ty<'tcx>, &SwitchTargets)> {
match self {
TerminatorKind::SwitchInt { discr, switch_ty, targets } => {
- Some((discr, switch_ty, targets))
+ Some((discr, *switch_ty, targets))
}
_ => None,
}
/// Preorder traversal of a graph.
///
-/// Preorder traversal is when each node is visited before any of its
-/// successors
+/// Preorder traversal is when each node is visited after at least one of its predecessors. If you
+/// are familar with some basic graph theory, then this performs a depth first search and returns
+/// nodes in order of discovery time.
///
/// ```text
///
/// Postorder traversal of a graph.
///
-/// Postorder traversal is when each node is visited after all of its
-/// successors, except when the successor is only reachable by a back-edge
+/// Postorder traversal is when each node is visited after all of its successors, except when the
+/// successor is only reachable by a back-edge. If you are familiar with some basic graph theory,
+/// then this performs a depth first search and returns nodes in order of completion time.
///
///
/// ```text
}
fn visit_region(&mut self,
- region: & $($mutability)? ty::Region<'tcx>,
+ region: $(& $mutability)? ty::Region<'tcx>,
_: Location) {
self.super_region(region);
}
fn visit_const(&mut self,
- constant: & $($mutability)? &'tcx ty::Const<'tcx>,
+ constant: $(& $mutability)? ty::Const<'tcx>,
_: Location) {
self.super_const(constant);
}
) {
let span = body.span;
if let Some(gen) = &$($mutability)? body.generator {
- if let Some(yield_ty) = &$($mutability)? gen.yield_ty {
+ if let Some(yield_ty) = $(& $mutability)? gen.yield_ty {
self.visit_ty(
yield_ty,
TyContext::YieldTy(SourceInfo::outermost(span))
}
self.visit_ty(
- &$($mutability)? body.return_ty(),
+ $(& $mutability)? body.return_ty(),
TyContext::ReturnTy(SourceInfo::outermost(body.span))
);
ty::InstanceDef::DropGlue(_def_id, Some(ty)) |
ty::InstanceDef::CloneShim(_def_id, ty) => {
// FIXME(eddyb) use a better `TyContext` here.
- self.visit_ty(ty, TyContext::Location(location));
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
}
self.visit_substs(callee_substs, location);
targets: _
} => {
self.visit_operand(discr, location);
- self.visit_ty(switch_ty, TyContext::Location(location));
+ self.visit_ty($(& $mutability)? *switch_ty, TyContext::Location(location));
}
TerminatorKind::Drop {
Rvalue::ThreadLocalRef(_) => {}
Rvalue::Ref(r, bk, path) => {
- self.visit_region(r, location);
+ self.visit_region($(& $mutability)? *r, location);
let ctx = match bk {
BorrowKind::Shared => PlaceContext::NonMutatingUse(
NonMutatingUseContext::SharedBorrow
Rvalue::Cast(_cast_kind, operand, ty) => {
self.visit_operand(operand, location);
- self.visit_ty(ty, TyContext::Location(location));
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
Rvalue::BinaryOp(_bin_op, box(lhs, rhs))
}
Rvalue::NullaryOp(_op, ty) => {
- self.visit_ty(ty, TyContext::Location(location));
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
Rvalue::Aggregate(kind, operands) => {
let kind = &$($mutability)? **kind;
match kind {
AggregateKind::Array(ty) => {
- self.visit_ty(ty, TyContext::Location(location));
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
AggregateKind::Tuple => {
}
Rvalue::ShallowInitBox(operand, ty) => {
self.visit_operand(operand, location);
- self.visit_ty(ty, TyContext::Location(location));
+ self.visit_ty($(& $mutability)? *ty, TyContext::Location(location));
}
}
}
is_block_tail: _,
} = local_decl;
- self.visit_ty(ty, TyContext::LocalDecl {
+ self.visit_ty($(& $mutability)? *ty, TyContext::LocalDecl {
local,
source_info: *source_info,
});
self.visit_span(span);
drop(user_ty); // no visit method for this
match literal {
- ConstantKind::Ty(ct) => self.visit_const(ct, location),
- ConstantKind::Val(_, t) => self.visit_ty(t, TyContext::Location(location)),
+ ConstantKind::Ty(ct) => self.visit_const($(& $mutability)? *ct, location),
+ ConstantKind::Val(_, ty) => self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)),
}
}
ty: & $($mutability)? CanonicalUserTypeAnnotation<'tcx>,
) {
self.visit_span(& $($mutability)? ty.span);
- self.visit_ty(& $($mutability)? ty.inferred_ty, TyContext::UserTy(ty.span));
+ self.visit_ty($(& $mutability)? ty.inferred_ty, TyContext::UserTy(ty.span));
}
fn super_ty(&mut self, _ty: $(& $mutability)? Ty<'tcx>) {
}
- fn super_region(&mut self, _region: & $($mutability)? ty::Region<'tcx>) {
+ fn super_region(&mut self, _region: $(& $mutability)? ty::Region<'tcx>) {
}
- fn super_const(&mut self, _const: & $($mutability)? &'tcx ty::Const<'tcx>) {
+ fn super_const(&mut self, _const: $(& $mutability)? ty::Const<'tcx>) {
}
fn super_substs(&mut self, _substs: & $($mutability)? SubstsRef<'tcx>) {
/// Given the def_id of a const-generic parameter, computes the associated default const
/// parameter. e.g. `fn example<const N: usize=3>` called on `N` would return `3`.
- query const_param_default(param: DefId) -> &'tcx ty::Const<'tcx> {
+ query const_param_default(param: DefId) -> ty::Const<'tcx> {
desc { |tcx| "compute const default for a given parameter `{}`", tcx.def_path_str(param) }
separate_provide_extern
}
desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
}
- query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
+ query native_libraries(_: CrateNum) -> Vec<NativeLib> {
+ storage(ArenaCacheSelector<'tcx>)
desc { "looking up the native libraries of a linked crate" }
separate_provide_extern
}
/// Create a THIR tree for debugging.
query thir_tree(key: ty::WithOptConstParam<LocalDefId>) -> String {
no_hash
+ storage(ArenaCacheSelector<'tcx>)
desc { |tcx| "constructing THIR tree for `{}`", tcx.def_path_str(key.did.to_def_id()) }
}
query symbols_for_closure_captures(
key: (LocalDefId, DefId)
) -> Vec<rustc_span::Symbol> {
+ storage(ArenaCacheSelector<'tcx>)
desc {
|tcx| "symbols for captures of closure `{}` in `{}`",
tcx.def_path_str(key.1),
}
/// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
- /// MIR pass (assuming the -Zinstrument-coverage option is enabled).
+ /// MIR pass (assuming the -Cinstrument-coverage option is enabled).
query coverageinfo(key: ty::InstanceDef<'tcx>) -> mir::CoverageInfo {
desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
storage(ArenaCacheSelector<'tcx>)
query adt_dtorck_constraint(
key: DefId
- ) -> Result<DtorckConstraint<'tcx>, NoSolution> {
+ ) -> Result<&'tcx DtorckConstraint<'tcx>, NoSolution> {
desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) }
}
/// The map returned for `tcx.impl_item_implementor_ids(impl_id)` would be
///`{ trait_f: impl_f, trait_g: impl_g }`
query impl_item_implementor_ids(impl_id: DefId) -> FxHashMap<DefId, DefId> {
- desc { |tcx| "comparing impl items against trait for {}", tcx.def_path_str(impl_id) }
storage(ArenaCacheSelector<'tcx>)
+ desc { |tcx| "comparing impl items against trait for {}", tcx.def_path_str(impl_id) }
}
/// Given an `impl_id`, return the trait it implements.
}
/// Destructure a constant ADT or array into its variant index and its
- /// field values.
- query destructure_const(
- key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>>
- ) -> mir::DestructuredConst<'tcx> {
+ /// field values or return `None` if constant is invalid.
+ ///
+ /// Use infallible `TyCtxt::destructure_const` when you know that constant is valid.
+ query try_destructure_const(
+ key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>
+ ) -> Option<mir::DestructuredConst<'tcx>> {
desc { "destructure constant" }
remap_env_constness
}
/// Dereference a constant reference or raw pointer and turn the result into a constant
/// again.
query deref_const(
- key: ty::ParamEnvAnd<'tcx, &'tcx ty::Const<'tcx>>
- ) -> &'tcx ty::Const<'tcx> {
+ key: ty::ParamEnvAnd<'tcx, ty::Const<'tcx>>
+ ) -> ty::Const<'tcx> {
desc { "deref constant" }
remap_env_constness
}
query lit_to_const(
key: LitToConstInput<'tcx>
- ) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
+ ) -> Result<ty::Const<'tcx>, LitToConstError> {
desc { "converting literal to const" }
}
/// Gets the rendered value of the specified constant or associated constant.
/// Used by rustdoc.
query rendered_const(def_id: DefId) -> String {
+ storage(ArenaCacheSelector<'tcx>)
desc { |tcx| "rendering constant intializer of `{}`", tcx.def_path_str(def_id) }
separate_provide_extern
}
query codegen_fulfill_obligation(
key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)
- ) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
+ ) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorReported> {
cache_on_disk_if { true }
desc { |tcx|
"checking if `{}` fulfills its obligations",
desc { "computing whether `{}` is `Copy`", env.value }
remap_env_constness
}
- /// Query backing `TyS::is_sized`.
+ /// Query backing `Ty::is_sized`.
query is_sized_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Sized`", env.value }
remap_env_constness
}
- /// Query backing `TyS::is_freeze`.
+ /// Query backing `Ty::is_freeze`.
query is_freeze_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is freeze", env.value }
remap_env_constness
}
- /// Query backing `TyS::is_unpin`.
+ /// Query backing `Ty::is_unpin`.
query is_unpin_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` is `Unpin`", env.value }
remap_env_constness
}
- /// Query backing `TyS::needs_drop`.
+ /// Query backing `Ty::needs_drop`.
query needs_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` needs drop", env.value }
remap_env_constness
}
- /// Query backing `TyS::has_significant_drop_raw`.
+ /// Query backing `Ty::has_significant_drop_raw`.
query has_significant_drop_raw(env: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
desc { "computing whether `{}` has a significant drop", env.value }
remap_env_constness
}
- /// Query backing `TyS::is_structural_eq_shallow`.
+ /// Query backing `Ty::is_structural_eq_shallow`.
///
/// This is only correct for ADTs. Call `is_structural_eq_shallow` to handle all types
/// correctly.
}
query dependency_formats(_: ()) -> Lrc<crate::middle::dependency_format::Dependencies> {
+ storage(ArenaCacheSelector<'tcx>)
desc { "get the linkage format of all dependencies" }
}
/// You likely want to call `Instance::upstream_monomorphization()`
/// instead of invoking this query directly.
query upstream_monomorphizations_for(def_id: DefId)
- -> Option<&'tcx FxHashMap<SubstsRef<'tcx>, CrateNum>> {
- desc { |tcx|
- "collecting available upstream monomorphizations for `{}`",
- tcx.def_path_str(def_id),
- }
- separate_provide_extern
+ -> Option<&'tcx FxHashMap<SubstsRef<'tcx>, CrateNum>>
+ {
+ storage(ArenaCacheSelector<'tcx>)
+ desc { |tcx|
+ "collecting available upstream monomorphizations for `{}`",
+ tcx.def_path_str(def_id),
}
+ separate_provide_extern
+ }
/// Returns the upstream crate that exports drop-glue for the given
/// type (`substs` is expected to be a single-item list containing the
desc { "available upstream drop-glue for `{:?}`", substs }
}
- query foreign_modules(_: CrateNum) -> Lrc<FxHashMap<DefId, ForeignModule>> {
+ query foreign_modules(_: CrateNum) -> FxHashMap<DefId, ForeignModule> {
+ storage(ArenaCacheSelector<'tcx>)
desc { "looking up the foreign modules of a linked crate" }
separate_provide_extern
}
separate_provide_extern
}
query extra_filename(_: CrateNum) -> String {
+ storage(ArenaCacheSelector<'tcx>)
eval_always
desc { "looking up the extra filename for a crate" }
separate_provide_extern
}
query crate_extern_paths(_: CrateNum) -> Vec<PathBuf> {
+ storage(ArenaCacheSelector<'tcx>)
eval_always
desc { "looking up the paths for extern crates" }
separate_provide_extern
/// for each parameter if a trait object were to be passed for that parameter.
/// For example, for `struct Foo<'a, T, U>`, this would be `['static, 'static]`.
/// For `struct Foo<'a, T: 'a, U>`, this would instead be `['a, 'static]`.
- query object_lifetime_defaults_map(_: LocalDefId)
- -> Option<Vec<ObjectLifetimeDefault>> {
+ query object_lifetime_defaults(_: LocalDefId) -> Option<&'tcx [ObjectLifetimeDefault]> {
desc { "looking up lifetime defaults for a region on an item" }
}
query late_bound_vars_map(_: LocalDefId)
}
query lifetime_scope_map(_: LocalDefId) -> Option<FxHashMap<ItemLocalId, LifetimeScopeForPath>> {
+ storage(ArenaCacheSelector<'tcx>)
desc { "finds the lifetime scope for an HirId of a PathSegment" }
}
/// check whether the forest is empty.
query type_uninhabited_from(
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>
- ) -> ty::inhabitedness::DefIdForest {
+ ) -> ty::inhabitedness::DefIdForest<'tcx> {
desc { "computing the inhabitedness of `{:?}`", key }
remap_env_constness
}
desc { "calculating the missing lang items in a crate" }
separate_provide_extern
}
- query visible_parent_map(_: ()) -> Lrc<DefIdMap<DefId>> {
+ query visible_parent_map(_: ()) -> DefIdMap<DefId> {
+ storage(ArenaCacheSelector<'tcx>)
desc { "calculating the visible parent map" }
}
query trimmed_def_paths(_: ()) -> FxHashMap<DefId, Symbol> {
separate_provide_extern
}
query used_crate_source(_: CrateNum) -> Lrc<CrateSource> {
+ storage(ArenaCacheSelector<'tcx>)
eval_always
desc { "looking at the source for a crate" }
separate_provide_extern
desc { "optimization level used by backend" }
}
- query output_filenames(_: ()) -> Arc<OutputFilenames> {
+ /// Return the filenames where output artefacts shall be stored.
+ ///
+ /// This query returns an `&Arc` because codegen backends need the value even after the `TyCtxt`
+ /// has been destroyed.
+ query output_filenames(_: ()) -> &'tcx Arc<OutputFilenames> {
eval_always
desc { "output_filenames" }
}
/// all of the cases that the normal `ty::Ty`-based wfcheck does. This is fine,
/// because the `ty::Ty`-based wfcheck is always run.
query diagnostic_hir_wf_check(key: (ty::Predicate<'tcx>, traits::WellFormedLoc)) -> Option<traits::ObligationCause<'tcx>> {
+ storage(ArenaCacheSelector<'tcx>)
eval_always
no_hash
desc { "performing HIR wf-checking for predicate {:?} at item {:?}", key.0, key.1 }
use rustc_index::vec::IndexVec;
use rustc_middle::infer::canonical::Canonical;
use rustc_middle::middle::region;
+use rustc_middle::mir::interpret::AllocId;
use rustc_middle::mir::{
BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp, UserTypeProjection,
};
},
/// An inline `const` block, e.g. `const {}`.
ConstBlock {
- value: &'tcx Const<'tcx>,
+ value: Const<'tcx>,
},
/// An array literal constructed from one repeated element, e.g. `[1; 5]`.
Repeat {
value: ExprId,
- count: &'tcx Const<'tcx>,
+ count: Const<'tcx>,
},
/// An array, e.g. `[a, b, c, d]`.
Array {
},
/// A literal.
Literal {
- literal: &'tcx Const<'tcx>,
+ literal: Const<'tcx>,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
/// The `DefId` of the `const` item this literal
/// was produced from, if this is not a user-written
/// This is only distinguished from `Literal` so that we can register some
/// info for diagnostics.
StaticRef {
- literal: &'tcx Const<'tcx>,
+ alloc_id: AllocId,
+ ty: Ty<'tcx>,
def_id: DefId,
},
/// Inline assembly, i.e. `asm!()`.
out_expr: Option<ExprId>,
},
Const {
- value: &'tcx Const<'tcx>,
+ value: Const<'tcx>,
span: Span,
},
SymFn {
/// * Opaque constants, that must not be matched structurally. So anything that does not derive
/// `PartialEq` and `Eq`.
Constant {
- value: &'tcx ty::Const<'tcx>,
+ value: ty::Const<'tcx>,
},
Range(PatRange<'tcx>),
#[derive(Copy, Clone, Debug, PartialEq, HashStable)]
pub struct PatRange<'tcx> {
- pub lo: &'tcx ty::Const<'tcx>,
- pub hi: &'tcx ty::Const<'tcx>,
+ pub lo: ty::Const<'tcx>,
+ pub hi: ty::Const<'tcx>,
pub end: RangeEnd,
}
/// A node of an `AbstractConst`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
pub enum Node<'tcx> {
- Leaf(&'tcx ty::Const<'tcx>),
+ Leaf(ty::Const<'tcx>),
Binop(mir::BinOp, NodeId, NodeId),
UnaryOp(mir::UnOp, NodeId),
FunctionCall(NodeId, &'tcx [NodeId]),
walk_pat(self, pat);
}
- fn visit_const(&mut self, _cnst: &'tcx Const<'tcx>) {}
+ fn visit_const(&mut self, _cnst: Const<'tcx>) {}
}
pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Expr<'tcx>) {
}
Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {}
Literal { literal, user_ty: _, const_id: _ } => visitor.visit_const(literal),
- StaticRef { literal, def_id: _ } => visitor.visit_const(literal),
+ StaticRef { .. } => {}
InlineAsm { ref operands, template: _, options: _, line_spans: _ } => {
for op in &**operands {
use InlineAsmOperand::*;
visitor.visit_pat(&subpattern.pattern);
}
}
- Constant { value } => visitor.visit_const(value),
+ Constant { value } => visitor.visit_const(*value),
Range(range) => {
visitor.visit_const(range.lo);
visitor.visit_const(range.hi);
trait_item_def_id: DefId,
},
+ /// Checking that the bounds of a trait's associated type hold for a given impl
+ CheckAssociatedTypeBounds {
+ impl_item_def_id: DefId,
+ trait_item_def_id: DefId,
+ },
+
/// Checking that this expression can be assigned where it needs to be
// FIXME(eddyb) #11161 is the original Expr required?
ExprAssignable,
use crate::ty::{self, Ty, TyCtxt};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_data_structures::sync::Lrc;
use rustc_errors::struct_span_err;
use rustc_query_system::ich::StableHashingContext;
use rustc_span::source_map::Span;
pub type CanonicalTypeOpNormalizeGoal<'tcx, T> =
Canonical<'tcx, ty::ParamEnvAnd<'tcx, type_op::Normalize<T>>>;
-#[derive(Clone, Debug, HashStable)]
+#[derive(Copy, Clone, Debug, HashStable)]
pub struct NoSolution;
pub type Fallible<T> = Result<T, NoSolution>;
pub unsize: bool,
}
-#[derive(Clone, Debug, HashStable)]
+#[derive(Copy, Clone, Debug, HashStable)]
pub struct MethodAutoderefStepsResult<'tcx> {
/// The valid autoderef steps that could be find.
- pub steps: Lrc<Vec<CandidateStep<'tcx>>>,
+ pub steps: &'tcx [CandidateStep<'tcx>],
/// If Some(T), a type autoderef reported an error on.
- pub opt_bad_ty: Option<Lrc<MethodAutoderefBadTy<'tcx>>>,
+ pub opt_bad_ty: Option<&'tcx MethodAutoderefBadTy<'tcx>>,
/// If `true`, `steps` has been truncated due to reaching the
/// recursion limit.
pub reached_recursion_limit: bool,
) => Ok(a),
(&ty::Infer(_), _) | (_, &ty::Infer(_)) => {
- Err(TypeError::Sorts(relate::expected_found(self, &a, &b)))
+ Err(TypeError::Sorts(relate::expected_found(self, a, b)))
}
(&ty::Error(_), _) | (_, &ty::Error(_)) => Ok(self.tcx().ty_error()),
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("{}.consts({:?}, {:?})", self.tag(), a, b);
if a == b {
return Ok(a);
}
- match (a.val, b.val) {
+ match (a.val(), b.val()) {
(_, ty::ConstKind::Infer(InferConst::Fresh(_))) => {
return Ok(a);
}
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
- return Err(TypeError::ConstMismatch(relate::expected_found(self, &a, &b)));
+ return Err(TypeError::ConstMismatch(relate::expected_found(self, a, b)));
}
_ => {}
Destructor, FieldDef, GenericPredicates, ReprOptions, Ty, TyCtxt, VariantDef, VariantDiscr,
};
-#[derive(Clone, HashStable, Debug)]
+#[derive(Copy, Clone, HashStable, Debug)]
pub struct AdtSizedConstraint<'tcx>(pub &'tcx [Ty<'tcx>]);
bitflags! {
| Res::Def(DefKind::Union, _)
| Res::Def(DefKind::TyAlias, _)
| Res::Def(DefKind::AssocTy, _)
- | Res::SelfTy(..)
+ | Res::SelfTy { .. }
| Res::SelfCtor(..) => self.non_enum_variant(),
_ => bug!("unexpected res {:?} in variant_of_res", res),
}
&self,
tcx: TyCtxt<'_>,
ident: Ident,
+ // Sorted in order of what kinds to look at
kinds: &[AssocKind],
parent_def_id: DefId,
) -> Option<&ty::AssocItem> {
- self.filter_by_name_unhygienic(ident.name)
- .filter(|item| kinds.contains(&item.kind))
- .find(|item| tcx.hygienic_eq(ident, item.ident(tcx), parent_def_id))
+ kinds.iter().find_map(|kind| self.find_by_name_and_kind(tcx, ident, *kind, parent_def_id))
}
/// Returns the associated item with the given name in the given `Namespace`, if one exists.
}
/// Returns the representative scalar type for this closure kind.
- /// See `TyS::to_opt_closure_kind` for more details.
+ /// See `Ty::to_opt_closure_kind` for more details.
pub fn to_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self {
ty::ClosureKind::Fn => tcx.types.i8,
interpret::{AllocId, Allocation},
};
use crate::thir;
+use crate::traits;
use crate::ty::subst::SubstsRef;
use crate::ty::{self, Ty, TyCtxt};
use rustc_data_structures::fx::FxHashMap;
}
}
+impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ty::Region<'tcx> {
+ fn encode(&self, e: &mut E) -> Result<(), E::Error> {
+ self.kind().encode(e)
+ }
+}
+
+impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for ty::Const<'tcx> {
+ fn encode(&self, e: &mut E) -> Result<(), E::Error> {
+ self.0.0.encode(e)
+ }
+}
+
impl<'tcx, E: TyEncoder<'tcx>> Encodable<E> for AllocId {
fn encode(&self, e: &mut E) -> Result<(), E::Error> {
e.encode_alloc_id(self)
encodable_via_deref! {
&'tcx ty::TypeckResults<'tcx>,
- ty::Region<'tcx>,
+ &'tcx traits::ImplSource<'tcx, ()>,
&'tcx mir::Body<'tcx>,
&'tcx mir::UnsafetyCheckResult,
&'tcx mir::BorrowCheckResult<'tcx>,
}
}
-impl<'tcx, D: TyDecoder<'tcx>> RefDecodable<'tcx, D> for ty::Const<'tcx> {
- fn decode(decoder: &mut D) -> &'tcx Self {
+impl<'tcx, D: TyDecoder<'tcx>> Decodable<D> for ty::Const<'tcx> {
+ fn decode(decoder: &mut D) -> Self {
decoder.tcx().mk_const(Decodable::decode(decoder))
}
}
&'tcx ty::TypeckResults<'tcx>,
&'tcx ty::List<Ty<'tcx>>,
&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
+ &'tcx traits::ImplSource<'tcx, ()>,
&'tcx Allocation,
&'tcx mir::Body<'tcx>,
&'tcx mir::UnsafetyCheckResult,
self, InlineConstSubsts, InlineConstSubstsParts, InternalSubsts, ParamEnv, ParamEnvAnd, Ty,
TyCtxt, TypeFoldable,
};
+use rustc_data_structures::intern::Interned;
use rustc_errors::ErrorReported;
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_macros::HashStable;
+use std::fmt;
mod int;
mod kind;
pub use kind::*;
pub use valtree::*;
+/// Use this rather than `ConstS`, whenever possible.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
+#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
+pub struct Const<'tcx>(pub Interned<'tcx, ConstS<'tcx>>);
+
+impl<'tcx> fmt::Debug for Const<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ // This reflects what `Const` looked liked before `Interned` was
+ // introduced. We print it like this to avoid having to update expected
+ // output in a lot of tests.
+ write!(f, "Const {{ ty: {:?}, val: {:?} }}", self.ty(), self.val())
+ }
+}
+
/// Typed constant value.
-#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
-#[derive(HashStable)]
-pub struct Const<'tcx> {
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, HashStable, TyEncodable, TyDecodable)]
+pub struct ConstS<'tcx> {
pub ty: Ty<'tcx>,
-
pub val: ConstKind<'tcx>,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(Const<'_>, 48);
+static_assert_size!(ConstS<'_>, 48);
impl<'tcx> Const<'tcx> {
+ pub fn ty(self) -> Ty<'tcx> {
+ self.0.ty
+ }
+
+ pub fn val(self) -> ConstKind<'tcx> {
+ self.0.val
+ }
+
/// Literals and const generic parameters are eagerly converted to a constant, everything else
/// becomes `Unevaluated`.
- pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self {
+ pub fn from_anon_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
Self::from_opt_const_arg_anon_const(tcx, ty::WithOptConstParam::unknown(def_id))
}
pub fn from_opt_const_arg_anon_const(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
- ) -> &'tcx Self {
+ ) -> Self {
debug!("Const::from_anon_const(def={:?})", def);
let body_id = match tcx.hir().get_by_def_id(def.did) {
match Self::try_eval_lit_or_param(tcx, ty, expr) {
Some(v) => v,
- None => tcx.mk_const(ty::Const {
+ None => tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: def.to_global(),
substs: InternalSubsts::identity_for_item(tcx, def.did.to_def_id()),
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
- ) -> Option<&'tcx Self> {
+ ) -> Option<Self> {
// Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments
// currently have to be wrapped in curly brackets, so it's necessary to special-case.
let expr = match &expr.kind {
let generics = tcx.generics_of(item_def_id.to_def_id());
let index = generics.param_def_id_to_index[&def_id];
let name = tcx.hir().name(hir_id);
- Some(tcx.mk_const(ty::Const {
+ Some(tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Param(ty::ParamConst::new(index, name)),
ty,
}))
}
}
- pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &'tcx Self {
+ pub fn from_inline_const(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
debug!("Const::from_inline_const(def_id={:?})", def_id);
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let substs =
InlineConstSubsts::new(tcx, InlineConstSubstsParts { parent_substs, ty })
.substs;
- tcx.mk_const(ty::Const {
+ tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: ty::WithOptConstParam::unknown(def_id).to_global(),
substs,
/// Interns the given value as a constant.
#[inline]
- pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
- tcx.mk_const(Self { val: ConstKind::Value(val), ty })
+ pub fn from_value(tcx: TyCtxt<'tcx>, val: ConstValue<'tcx>, ty: Ty<'tcx>) -> Self {
+ tcx.mk_const(ConstS { val: ConstKind::Value(val), ty })
}
#[inline]
/// Interns the given scalar as a constant.
- pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> &'tcx Self {
+ pub fn from_scalar(tcx: TyCtxt<'tcx>, val: Scalar, ty: Ty<'tcx>) -> Self {
Self::from_value(tcx, ConstValue::Scalar(val), ty)
}
#[inline]
/// Creates a constant with the given integer value and interns it.
- pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> &'tcx Self {
+ pub fn from_bits(tcx: TyCtxt<'tcx>, bits: u128, ty: ParamEnvAnd<'tcx, Ty<'tcx>>) -> Self {
let size = tcx
.layout_of(ty)
.unwrap_or_else(|e| panic!("could not compute layout for {:?}: {:?}", ty, e))
#[inline]
/// Creates an interned zst constant.
- pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> &'tcx Self {
+ pub fn zero_sized(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Self {
Self::from_scalar(tcx, Scalar::ZST, ty)
}
#[inline]
/// Creates an interned bool constant.
- pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> &'tcx Self {
+ pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self {
Self::from_bits(tcx, v as u128, ParamEnv::empty().and(tcx.types.bool))
}
#[inline]
/// Creates an interned usize constant.
- pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> &'tcx Self {
+ pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self {
Self::from_bits(tcx, n as u128, ParamEnv::empty().and(tcx.types.usize))
}
/// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
/// contains const generic parameters or pointers).
pub fn try_eval_bits(
- &self,
+ self,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<u128> {
- assert_eq!(self.ty, ty);
+ assert_eq!(self.ty(), ty);
let size = tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(ty)).ok()?.size;
// if `ty` does not depend on generic parameters, use an empty param_env
- self.val.eval(tcx, param_env).try_to_bits(size)
+ self.val().eval(tcx, param_env).try_to_bits(size)
}
#[inline]
- pub fn try_eval_bool(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
- self.val.eval(tcx, param_env).try_to_bool()
+ pub fn try_eval_bool(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<bool> {
+ self.val().eval(tcx, param_env).try_to_bool()
}
#[inline]
- pub fn try_eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> {
- self.val.eval(tcx, param_env).try_to_machine_usize(tcx)
+ pub fn try_eval_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Option<u64> {
+ self.val().eval(tcx, param_env).try_to_machine_usize(tcx)
}
#[inline]
/// Tries to evaluate the constant if it is `Unevaluated`. If that doesn't succeed, return the
/// unevaluated constant.
- pub fn eval(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> &Const<'tcx> {
- if let Some(val) = self.val.try_eval(tcx, param_env) {
+ pub fn eval(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Const<'tcx> {
+ if let Some(val) = self.val().try_eval(tcx, param_env) {
match val {
- Ok(val) => Const::from_value(tcx, val, self.ty),
- Err(ErrorReported) => tcx.const_error(self.ty),
+ Ok(val) => Const::from_value(tcx, val, self.ty()),
+ Err(ErrorReported) => tcx.const_error(self.ty()),
}
} else {
self
#[inline]
/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.
- pub fn eval_bits(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
+ pub fn eval_bits(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> u128 {
self.try_eval_bits(tcx, param_env, ty)
.unwrap_or_else(|| bug!("expected bits of {:#?}, got {:#?}", ty, self))
}
#[inline]
/// Panics if the value cannot be evaluated or doesn't contain a valid `usize`.
- pub fn eval_usize(&self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 {
+ pub fn eval_usize(self, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> u64 {
self.try_eval_usize(tcx, param_env)
.unwrap_or_else(|| bug!("expected usize, got {:#?}", self))
}
}
-pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx Const<'tcx> {
+pub fn const_param_default<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Const<'tcx> {
let default_def_id = match tcx.hir().get_by_def_id(def_id.expect_local()) {
hir::Node::GenericParam(hir::GenericParam {
kind: hir::GenericParamKind::Const { ty: _, default: Some(ac) },
use crate::hir::place::Place as HirPlace;
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource};
-use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath, ObjectLifetimeDefault};
+use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath};
use crate::middle::stability;
use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
-use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
+use crate::mir::{
+ Body, BorrowCheckResult, Field, Local, Place, PlaceElem, ProjectionKind, Promoted,
+};
use crate::thir::Thir;
use crate::traits;
use crate::ty::query::{self, TyCtxtAt};
use crate::ty::TyKind::*;
use crate::ty::{
self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig,
- ClosureSizeProfileData, Const, ConstVid, DefIdTree, ExistentialPredicate, FloatTy, FloatVar,
- FloatVid, GenericParamDefKind, InferConst, InferTy, IntTy, IntVar, IntVid, List, ParamConst,
- ParamTy, PolyFnSig, Predicate, PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind,
- ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy,
+ ClosureSizeProfileData, Const, ConstS, ConstVid, DefIdTree, ExistentialPredicate, FloatTy,
+ FloatVar, FloatVid, GenericParamDefKind, InferConst, InferTy, IntTy, IntVar, IntVid, List,
+ ParamConst, ParamTy, PolyFnSig, Predicate, PredicateKind, PredicateS, ProjectionTy, Region,
+ RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy,
};
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::memmap::Mmap;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
#[derive(TyEncodable, TyDecodable, HashStable)]
pub struct DelaySpanBugEmitted(());
-type InternedSet<'tcx, T> = ShardedHashMap<Interned<'tcx, T>, ()>;
+type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
pub struct CtxtInterners<'tcx> {
/// The arena that types, regions, etc. are allocated from.
region: InternedSet<'tcx, RegionKind>,
poly_existential_predicates:
InternedSet<'tcx, List<ty::Binder<'tcx, ExistentialPredicate<'tcx>>>>,
- predicate: InternedSet<'tcx, PredicateInner<'tcx>>,
+ predicate: InternedSet<'tcx, PredicateS<'tcx>>,
predicates: InternedSet<'tcx, List<Predicate<'tcx>>>,
projs: InternedSet<'tcx, List<ProjectionKind>>,
place_elems: InternedSet<'tcx, List<PlaceElem<'tcx>>>,
- const_: InternedSet<'tcx, Const<'tcx>>,
+ const_: InternedSet<'tcx, ConstS<'tcx>>,
const_allocation: InternedSet<'tcx, Allocation>,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
layout: InternedSet<'tcx, Layout>,
#[allow(rustc::usage_of_ty_tykind)]
#[inline(never)]
fn intern_ty(&self, kind: TyKind<'tcx>) -> Ty<'tcx> {
- self.type_
- .intern(kind, |kind| {
- let flags = super::flags::FlagComputation::for_kind(&kind);
-
- let ty_struct = TyS {
- kind,
- flags: flags.flags,
- outer_exclusive_binder: flags.outer_exclusive_binder,
- };
+ Ty(Interned::new_unchecked(
+ self.type_
+ .intern(kind, |kind| {
+ let flags = super::flags::FlagComputation::for_kind(&kind);
+
+ let ty_struct = TyS {
+ kind,
+ flags: flags.flags,
+ outer_exclusive_binder: flags.outer_exclusive_binder,
+ };
- Interned(self.arena.alloc(ty_struct))
- })
- .0
+ InternedInSet(self.arena.alloc(ty_struct))
+ })
+ .0,
+ ))
}
#[inline(never)]
- fn intern_predicate(
- &self,
- kind: Binder<'tcx, PredicateKind<'tcx>>,
- ) -> &'tcx PredicateInner<'tcx> {
- self.predicate
- .intern(kind, |kind| {
- let flags = super::flags::FlagComputation::for_predicate(kind);
-
- let predicate_struct = PredicateInner {
- kind,
- flags: flags.flags,
- outer_exclusive_binder: flags.outer_exclusive_binder,
- };
+ fn intern_predicate(&self, kind: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> {
+ Predicate(Interned::new_unchecked(
+ self.predicate
+ .intern(kind, |kind| {
+ let flags = super::flags::FlagComputation::for_predicate(kind);
+
+ let predicate_struct = PredicateS {
+ kind,
+ flags: flags.flags,
+ outer_exclusive_binder: flags.outer_exclusive_binder,
+ };
- Interned(self.arena.alloc(predicate_struct))
- })
- .0
+ InternedInSet(self.arena.alloc(predicate_struct))
+ })
+ .0,
+ ))
}
}
}
pub struct CommonConsts<'tcx> {
- pub unit: &'tcx Const<'tcx>,
+ pub unit: Const<'tcx>,
}
pub struct LocalTableInContext<'a, V> {
_ => false,
},
- GenericArgKind::Lifetime(r) => match r {
+ GenericArgKind::Lifetime(r) => match *r {
ty::ReLateBound(debruijn, br) => {
// We only allow a `ty::INNERMOST` index in substitutions.
- assert_eq!(*debruijn, ty::INNERMOST);
+ assert_eq!(debruijn, ty::INNERMOST);
cvar == br.var
}
_ => false,
},
- GenericArgKind::Const(ct) => match ct.val {
+ GenericArgKind::Const(ct) => match ct.val() {
ty::ConstKind::Bound(debruijn, b) => {
// We only allow a `ty::INNERMOST` index in substitutions.
assert_eq!(debruijn, ty::INNERMOST);
impl<'tcx> CommonLifetimes<'tcx> {
fn new(interners: &CtxtInterners<'tcx>) -> CommonLifetimes<'tcx> {
- let mk = |r| interners.region.intern(r, |r| Interned(interners.arena.alloc(r))).0;
+ let mk = |r| {
+ Region(Interned::new_unchecked(
+ interners.region.intern(r, |r| InternedInSet(interners.arena.alloc(r))).0,
+ ))
+ };
CommonLifetimes {
- re_root_empty: mk(RegionKind::ReEmpty(ty::UniverseIndex::ROOT)),
- re_static: mk(RegionKind::ReStatic),
- re_erased: mk(RegionKind::ReErased),
+ re_root_empty: mk(ty::ReEmpty(ty::UniverseIndex::ROOT)),
+ re_static: mk(ty::ReStatic),
+ re_erased: mk(ty::ReErased),
}
}
}
impl<'tcx> CommonConsts<'tcx> {
fn new(interners: &CtxtInterners<'tcx>, types: &CommonTypes<'tcx>) -> CommonConsts<'tcx> {
- let mk_const = |c| interners.const_.intern(c, |c| Interned(interners.arena.alloc(c))).0;
+ let mk_const = |c| {
+ Const(Interned::new_unchecked(
+ interners.const_.intern(c, |c| InternedInSet(interners.arena.alloc(c))).0,
+ ))
+ };
CommonConsts {
- unit: mk_const(ty::Const {
+ unit: mk_const(ty::ConstS {
val: ty::ConstKind::Value(ConstValue::Scalar(Scalar::ZST)),
ty: types.unit,
}),
}
}
+ pub fn mir_borrowck_opt_const_arg(
+ self,
+ def: ty::WithOptConstParam<LocalDefId>,
+ ) -> &'tcx BorrowCheckResult<'tcx> {
+ if let Some(param_did) = def.const_param_did {
+ self.mir_borrowck_const_arg((def.did, param_did))
+ } else {
+ self.mir_borrowck(def.did)
+ }
+ }
+
pub fn alloc_steal_thir(self, thir: Thir<'tcx>) -> &'tcx Steal<Thir<'tcx>> {
self.arena.alloc(Steal::new(thir))
}
/// Like [TyCtxt::ty_error] but for constants.
#[track_caller]
- pub fn const_error(self, ty: Ty<'tcx>) -> &'tcx Const<'tcx> {
+ pub fn const_error(self, ty: Ty<'tcx>) -> Const<'tcx> {
self.const_error_with_message(
ty,
DUMMY_SP,
ty: Ty<'tcx>,
span: S,
msg: &str,
- ) -> &'tcx Const<'tcx> {
+ ) -> Const<'tcx> {
self.sess.delay_span_bug(span, msg);
- self.mk_const(ty::Const { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty })
+ self.mk_const(ty::ConstS { val: ty::ConstKind::Error(DelaySpanBugEmitted(())), ty })
}
pub fn consider_optimizing<T: Fn() -> String>(self, msg: T) -> bool {
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted>;
}
+// Deprecated: we are in the process of converting all uses to `nop_lift`.
+macro_rules! nop_lift_old {
+ ($set:ident; $ty:ty => $lifted:ty) => {
+ impl<'a, 'tcx> Lift<'tcx> for $ty {
+ type Lifted = $lifted;
+ fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
+ if tcx.interners.$set.contains_pointer_to(&InternedInSet(self)) {
+ Some(unsafe { mem::transmute(self) })
+ } else {
+ None
+ }
+ }
+ }
+ };
+}
+
macro_rules! nop_lift {
($set:ident; $ty:ty => $lifted:ty) => {
impl<'a, 'tcx> Lift<'tcx> for $ty {
type Lifted = $lifted;
fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<Self::Lifted> {
- if tcx.interners.$set.contains_pointer_to(&Interned(self)) {
+ if tcx.interners.$set.contains_pointer_to(&InternedInSet(self.0.0)) {
Some(unsafe { mem::transmute(self) })
} else {
None
if self.is_empty() {
return Some(List::empty());
}
- if tcx.interners.$set.contains_pointer_to(&Interned(self)) {
+ if tcx.interners.$set.contains_pointer_to(&InternedInSet(self)) {
Some(unsafe { mem::transmute(self) })
} else {
None
nop_lift! {type_; Ty<'a> => Ty<'tcx>}
nop_lift! {region; Region<'a> => Region<'tcx>}
-nop_lift! {const_; &'a Const<'a> => &'tcx Const<'tcx>}
-nop_lift! {const_allocation; &'a Allocation => &'tcx Allocation}
-nop_lift! {predicate; &'a PredicateInner<'a> => &'tcx PredicateInner<'tcx>}
+nop_lift! {const_; Const<'a> => Const<'tcx>}
+nop_lift_old! {const_allocation; &'a Allocation => &'tcx Allocation}
+nop_lift! {predicate; Predicate<'a> => Predicate<'tcx>}
nop_list_lift! {type_list; Ty<'a> => Ty<'tcx>}
nop_list_lift! {poly_existential_predicates; ty::Binder<'a, ExistentialPredicate<'a>> => ty::Binder<'tcx, ExistentialPredicate<'tcx>>}
pub mod tls {
use super::{ptr_eq, GlobalCtxt, TyCtxt};
- use crate::dep_graph::{DepKind, TaskDepsRef};
+ use crate::dep_graph::TaskDepsRef;
use crate::ty::query;
use rustc_data_structures::sync::{self, Lock};
use rustc_data_structures::thin_vec::ThinVec;
/// The current query job, if any. This is updated by `JobOwner::start` in
/// `ty::query::plumbing` when executing a query.
- pub query: Option<query::QueryJobId<DepKind>>,
+ pub query: Option<query::QueryJobId>,
/// Where to store diagnostics for the current query job, if any.
/// This is updated by `JobOwner::start` in `ty::query::plumbing` when executing a query.
#[allow(non_snake_case)]
mod inner {
use crate::ty::{self, TyCtxt};
- use crate::ty::context::Interned;
+ use crate::ty::context::InternedInSet;
#[derive(Copy, Clone)]
struct DebugStat {
let shards = tcx.interners.type_.lock_shards();
let types = shards.iter().flat_map(|shard| shard.keys());
- for &Interned(t) in types {
- let variant = match t.kind() {
+ for &InternedInSet(t) in types {
+ let variant = match t.kind {
ty::Bool | ty::Char | ty::Int(..) | ty::Uint(..) |
ty::Float(..) | ty::Str | ty::Never => continue,
ty::Error(_) => /* unimportant */ continue,
$(ty::$variant(..) => &mut $variant,)*
};
- let lt = t.flags().intersects(ty::TypeFlags::HAS_RE_INFER);
- let ty = t.flags().intersects(ty::TypeFlags::HAS_TY_INFER);
- let ct = t.flags().intersects(ty::TypeFlags::HAS_CT_INFER);
+ let lt = t.flags.intersects(ty::TypeFlags::HAS_RE_INFER);
+ let ty = t.flags.intersects(ty::TypeFlags::HAS_TY_INFER);
+ let ct = t.flags.intersects(ty::TypeFlags::HAS_CT_INFER);
variant.total += 1;
total.total += 1;
// this type just holds a pointer to it, but it still effectively owns it. It
// impls `Borrow` so that it can be looked up using the original
// (non-arena-memory-owning) types.
-struct Interned<'tcx, T: ?Sized>(&'tcx T);
+struct InternedInSet<'tcx, T: ?Sized>(&'tcx T);
-impl<'tcx, T: 'tcx + ?Sized> Clone for Interned<'tcx, T> {
+impl<'tcx, T: 'tcx + ?Sized> Clone for InternedInSet<'tcx, T> {
fn clone(&self) -> Self {
- Interned(self.0)
+ InternedInSet(self.0)
}
}
-impl<'tcx, T: 'tcx + ?Sized> Copy for Interned<'tcx, T> {}
+impl<'tcx, T: 'tcx + ?Sized> Copy for InternedInSet<'tcx, T> {}
-impl<'tcx, T: 'tcx + ?Sized> IntoPointer for Interned<'tcx, T> {
+impl<'tcx, T: 'tcx + ?Sized> IntoPointer for InternedInSet<'tcx, T> {
fn into_pointer(&self) -> *const () {
self.0 as *const _ as *const ()
}
}
#[allow(rustc::usage_of_ty_tykind)]
-impl<'tcx> Borrow<TyKind<'tcx>> for Interned<'tcx, TyS<'tcx>> {
+impl<'tcx> Borrow<TyKind<'tcx>> for InternedInSet<'tcx, TyS<'tcx>> {
fn borrow<'a>(&'a self) -> &'a TyKind<'tcx> {
- &self.0.kind()
+ &self.0.kind
}
}
-impl<'tcx> PartialEq for Interned<'tcx, TyS<'tcx>> {
- fn eq(&self, other: &Interned<'tcx, TyS<'tcx>>) -> bool {
+impl<'tcx> PartialEq for InternedInSet<'tcx, TyS<'tcx>> {
+ fn eq(&self, other: &InternedInSet<'tcx, TyS<'tcx>>) -> bool {
// The `Borrow` trait requires that `x.borrow() == y.borrow()` equals
// `x == y`.
- self.0.kind() == other.0.kind()
+ self.0.kind == other.0.kind
}
}
-impl<'tcx> Eq for Interned<'tcx, TyS<'tcx>> {}
+impl<'tcx> Eq for InternedInSet<'tcx, TyS<'tcx>> {}
-impl<'tcx> Hash for Interned<'tcx, TyS<'tcx>> {
+impl<'tcx> Hash for InternedInSet<'tcx, TyS<'tcx>> {
fn hash<H: Hasher>(&self, s: &mut H) {
// The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`.
- self.0.kind().hash(s)
+ self.0.kind.hash(s)
}
}
-impl<'tcx> Borrow<Binder<'tcx, PredicateKind<'tcx>>> for Interned<'tcx, PredicateInner<'tcx>> {
+impl<'tcx> Borrow<Binder<'tcx, PredicateKind<'tcx>>> for InternedInSet<'tcx, PredicateS<'tcx>> {
fn borrow<'a>(&'a self) -> &'a Binder<'tcx, PredicateKind<'tcx>> {
&self.0.kind
}
}
-impl<'tcx> PartialEq for Interned<'tcx, PredicateInner<'tcx>> {
- fn eq(&self, other: &Interned<'tcx, PredicateInner<'tcx>>) -> bool {
+impl<'tcx> PartialEq for InternedInSet<'tcx, PredicateS<'tcx>> {
+ fn eq(&self, other: &InternedInSet<'tcx, PredicateS<'tcx>>) -> bool {
// The `Borrow` trait requires that `x.borrow() == y.borrow()` equals
// `x == y`.
self.0.kind == other.0.kind
}
}
-impl<'tcx> Eq for Interned<'tcx, PredicateInner<'tcx>> {}
+impl<'tcx> Eq for InternedInSet<'tcx, PredicateS<'tcx>> {}
-impl<'tcx> Hash for Interned<'tcx, PredicateInner<'tcx>> {
+impl<'tcx> Hash for InternedInSet<'tcx, PredicateS<'tcx>> {
fn hash<H: Hasher>(&self, s: &mut H) {
// The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`.
self.0.kind.hash(s)
}
}
-impl<'tcx, T> Borrow<[T]> for Interned<'tcx, List<T>> {
+impl<'tcx, T> Borrow<[T]> for InternedInSet<'tcx, List<T>> {
fn borrow<'a>(&'a self) -> &'a [T] {
&self.0[..]
}
}
-impl<'tcx, T: PartialEq> PartialEq for Interned<'tcx, List<T>> {
- fn eq(&self, other: &Interned<'tcx, List<T>>) -> bool {
+impl<'tcx, T: PartialEq> PartialEq for InternedInSet<'tcx, List<T>> {
+ fn eq(&self, other: &InternedInSet<'tcx, List<T>>) -> bool {
// The `Borrow` trait requires that `x.borrow() == y.borrow()` equals
// `x == y`.
self.0[..] == other.0[..]
}
}
-impl<'tcx, T: Eq> Eq for Interned<'tcx, List<T>> {}
+impl<'tcx, T: Eq> Eq for InternedInSet<'tcx, List<T>> {}
-impl<'tcx, T: Hash> Hash for Interned<'tcx, List<T>> {
+impl<'tcx, T: Hash> Hash for InternedInSet<'tcx, List<T>> {
fn hash<H: Hasher>(&self, s: &mut H) {
// The `Borrow` trait requires that `x.borrow().hash(s) == x.hash(s)`.
self.0[..].hash(s)
}
macro_rules! direct_interners {
+ ($($name:ident: $method:ident($ty:ty): $ret_ctor:ident -> $ret_ty:ty,)+) => {
+ $(impl<'tcx> Borrow<$ty> for InternedInSet<'tcx, $ty> {
+ fn borrow<'a>(&'a self) -> &'a $ty {
+ &self.0
+ }
+ }
+
+ impl<'tcx> PartialEq for InternedInSet<'tcx, $ty> {
+ fn eq(&self, other: &Self) -> bool {
+ // The `Borrow` trait requires that `x.borrow() == y.borrow()`
+ // equals `x == y`.
+ self.0 == other.0
+ }
+ }
+
+ impl<'tcx> Eq for InternedInSet<'tcx, $ty> {}
+
+ impl<'tcx> Hash for InternedInSet<'tcx, $ty> {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ // The `Borrow` trait requires that `x.borrow().hash(s) ==
+ // x.hash(s)`.
+ self.0.hash(s)
+ }
+ }
+
+ impl<'tcx> TyCtxt<'tcx> {
+ pub fn $method(self, v: $ty) -> $ret_ty {
+ $ret_ctor(Interned::new_unchecked(self.interners.$name.intern(v, |v| {
+ InternedInSet(self.interners.arena.alloc(v))
+ }).0))
+ }
+ })+
+ }
+}
+
+direct_interners! {
+ region: mk_region(RegionKind): Region -> Region<'tcx>,
+ const_: mk_const(ConstS<'tcx>): Const -> Const<'tcx>,
+}
+
+macro_rules! direct_interners_old {
($($name:ident: $method:ident($ty:ty),)+) => {
- $(impl<'tcx> Borrow<$ty> for Interned<'tcx, $ty> {
+ $(impl<'tcx> Borrow<$ty> for InternedInSet<'tcx, $ty> {
fn borrow<'a>(&'a self) -> &'a $ty {
&self.0
}
}
- impl<'tcx> PartialEq for Interned<'tcx, $ty> {
+ impl<'tcx> PartialEq for InternedInSet<'tcx, $ty> {
fn eq(&self, other: &Self) -> bool {
// The `Borrow` trait requires that `x.borrow() == y.borrow()`
// equals `x == y`.
}
}
- impl<'tcx> Eq for Interned<'tcx, $ty> {}
+ impl<'tcx> Eq for InternedInSet<'tcx, $ty> {}
- impl<'tcx> Hash for Interned<'tcx, $ty> {
+ impl<'tcx> Hash for InternedInSet<'tcx, $ty> {
fn hash<H: Hasher>(&self, s: &mut H) {
// The `Borrow` trait requires that `x.borrow().hash(s) ==
// x.hash(s)`.
impl<'tcx> TyCtxt<'tcx> {
pub fn $method(self, v: $ty) -> &'tcx $ty {
self.interners.$name.intern(v, |v| {
- Interned(self.interners.arena.alloc(v))
+ InternedInSet(self.interners.arena.alloc(v))
}).0
}
})+
}
}
-direct_interners! {
- region: mk_region(RegionKind),
- const_: mk_const(Const<'tcx>),
+// FIXME: eventually these should all be converted to `direct_interners`.
+direct_interners_old! {
const_allocation: intern_const_alloc(Allocation),
layout: intern_layout(Layout),
adt_def: intern_adt_def(AdtDef),
impl<'tcx> TyCtxt<'tcx> {
$(pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
self.interners.$field.intern_ref(v, || {
- Interned(List::from_arena(&*self.arena, v))
+ InternedInSet(List::from_arena(&*self.arena, v))
}).0
})+
}
#[inline]
pub fn mk_predicate(self, binder: Binder<'tcx, PredicateKind<'tcx>>) -> Predicate<'tcx> {
- let inner = self.interners.intern_predicate(binder);
- Predicate { inner }
+ self.interners.intern_predicate(binder)
}
#[inline]
}
#[inline]
- pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> &'tcx Const<'tcx> {
- self.mk_const(ty::Const { val: ty::ConstKind::Infer(InferConst::Var(v)), ty })
+ pub fn mk_const_var(self, v: ConstVid<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
+ self.mk_const(ty::ConstS { val: ty::ConstKind::Infer(InferConst::Var(v)), ty })
}
#[inline]
}
#[inline]
- pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> &'tcx ty::Const<'tcx> {
- self.mk_const(ty::Const { val: ty::ConstKind::Infer(ic), ty })
+ pub fn mk_const_infer(self, ic: InferConst<'tcx>, ty: Ty<'tcx>) -> ty::Const<'tcx> {
+ self.mk_const(ty::ConstS { val: ty::ConstKind::Infer(ic), ty })
}
#[inline]
}
#[inline]
- pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> &'tcx Const<'tcx> {
- self.mk_const(ty::Const { val: ty::ConstKind::Param(ParamConst { index, name }), ty })
+ pub fn mk_const_param(self, index: u32, name: Symbol, ty: Ty<'tcx>) -> Const<'tcx> {
+ self.mk_const(ty::ConstS { val: ty::ConstKind::Param(ParamConst { index, name }), ty })
}
pub fn mk_param_from_def(self, param: &ty::GenericParamDef) -> GenericArg<'tcx> {
.map_or(false, |(owner, set)| owner == id.owner && set.contains(&id.local_id))
}
- pub fn object_lifetime_defaults(self, id: HirId) -> Option<Vec<ObjectLifetimeDefault>> {
- self.object_lifetime_defaults_map(id.owner)
- }
-
pub fn late_bound_vars(self, id: HirId) -> &'tcx List<ty::BoundVariableKind> {
self.mk_bound_variable_kinds(
self.late_bound_vars_map(id.owner)
)
}
- pub fn lifetime_scope(self, id: HirId) -> Option<LifetimeScopeForPath> {
- self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id))
+ pub fn lifetime_scope(self, id: HirId) -> Option<&'tcx LifetimeScopeForPath> {
+ self.lifetime_scope_map(id.owner).as_ref().and_then(|map| map.get(&id.local_id))
}
/// Whether the `def_id` counts as const fn in the current crate, considering all active
|tcx, id| tcx.stability().local_deprecation_entry(id.expect_local());
providers.extern_mod_stmt_cnum =
|tcx, id| tcx.resolutions(()).extern_crate_map.get(&id).cloned();
- providers.output_filenames = |tcx, ()| tcx.output_filenames.clone();
+ providers.output_filenames = |tcx, ()| &tcx.output_filenames;
providers.features_query = |tcx, ()| tcx.sess.features_untracked();
providers.is_panic_runtime = |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
-//! Diagnostics related methods for `TyS`.
+//! Diagnostics related methods for `Ty`.
use crate::ty::subst::{GenericArg, GenericArgKind};
use crate::ty::TyKind::*;
use crate::ty::{
ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
- ProjectionTy, Term, TyCtxt, TyS, TypeAndMut,
+ ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
use rustc_span::Span;
-impl<'tcx> TyS<'tcx> {
- /// Similar to `TyS::is_primitive`, but also considers inferred numeric values to be primitive.
- pub fn is_primitive_ty(&self) -> bool {
+impl<'tcx> Ty<'tcx> {
+ /// Similar to `Ty::is_primitive`, but also considers inferred numeric values to be primitive.
+ pub fn is_primitive_ty(self) -> bool {
matches!(
self.kind(),
Bool | Char
/// Whether the type is succinctly representable as a type instead of just referred to with a
/// description in error messages. This is used in the main error message.
- pub fn is_simple_ty(&self) -> bool {
+ pub fn is_simple_ty(self) -> bool {
match self.kind() {
Bool
| Char
/// description in error messages. This is used in the primary span label. Beyond what
/// `is_simple_ty` includes, it also accepts ADTs with no type arguments and references to
/// ADTs with no type arguments.
- pub fn is_simple_text(&self) -> bool {
+ pub fn is_simple_text(self) -> bool {
match self.kind() {
Adt(_, substs) => substs.non_erasable_generics().next().is_none(),
Ref(_, ty, _) => ty.is_simple_text(),
}
/// Whether the type can be safely suggested during error recovery.
- pub fn is_suggestable(&self) -> bool {
+ pub fn is_suggestable(self) -> bool {
fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
match arg.unpack() {
GenericArgKind::Type(ty) => ty.is_suggestable(),
- GenericArgKind::Const(c) => const_is_suggestable(c.val),
+ GenericArgKind::Const(c) => const_is_suggestable(c.val()),
_ => true,
}
}
}) => {
let term_is_suggestable = match term {
Term::Ty(ty) => ty.is_suggestable(),
- Term::Const(c) => const_is_suggestable(c.val),
+ Term::Const(c) => const_is_suggestable(c.val()),
};
term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
}
args.iter().all(generic_arg_is_suggestible)
}
Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
- Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val),
+ Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
_ => true,
}
}
/// created a cycle (because it appears somewhere within that
/// type).
CyclicTy(Ty<'tcx>),
- CyclicConst(&'tcx ty::Const<'tcx>),
+ CyclicConst(ty::Const<'tcx>),
ProjectionMismatched(ExpectedFound<DefId>),
ExistentialMismatch(
ExpectedFound<&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>>,
),
ObjectUnsafeCoercion(DefId),
- ConstMismatch(ExpectedFound<&'tcx ty::Const<'tcx>>),
+ ConstMismatch(ExpectedFound<ty::Const<'tcx>>),
IntrinsicCast,
/// Safe `#[target_feature]` functions are not assignable to safe function pointers.
}
}
-impl<'tcx> ty::TyS<'tcx> {
- pub fn sort_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
+impl<'tcx> Ty<'tcx> {
+ pub fn sort_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
match *self.kind() {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str | ty::Never => {
format!("`{}`", self).into()
}
let n = tcx.lift(n).unwrap();
- if let ty::ConstKind::Value(v) = n.val {
+ if let ty::ConstKind::Value(v) = n.val() {
if let Some(n) = v.try_to_machine_usize(tcx) {
return format!("array of {} element{}", n, pluralize!(n)).into();
}
}
}
- pub fn prefix_string(&self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
+ pub fn prefix_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
match *self.kind() {
ty::Infer(_)
| ty::Error(_)
No,
}
-#[derive(PartialEq, Eq, Debug, Clone, Copy)]
-pub enum StripReferences {
- Yes,
- No,
-}
-
/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists.
///
/// The idea is to get something simple that we can use to quickly decide if two types could unify,
/// When using `SimplifyParams::Yes`, we still return a simplified type for params and projections²,
/// the reasoning for this can be seen at the places doing this.
///
-/// For diagnostics we strip references with `StripReferences::Yes`. This is currently the best
-/// way to skip some unhelpful suggestions.
///
/// ¹ meaning that if two outermost layers are different, then the whole types are also different.
/// ² FIXME(@lcnr): this seems like it can actually end up being unsound with the way it's used during
tcx: TyCtxt<'_>,
ty: Ty<'_>,
can_simplify_params: SimplifyParams,
- strip_references: StripReferences,
) -> Option<SimplifiedType> {
match *ty.kind() {
ty::Bool => Some(BoolSimplifiedType),
}
_ => Some(MarkerTraitObjectSimplifiedType),
},
- ty::Ref(_, ty, mutbl) => {
- if strip_references == StripReferences::Yes {
- // For diagnostics, when recommending similar impls we want to
- // recommend impls even when there is a reference mismatch,
- // so we treat &T and T equivalently in that case.
- simplify_type(tcx, ty, can_simplify_params, strip_references)
- } else {
- Some(RefSimplifiedType(mutbl))
- }
- }
+ ty::Ref(_, _, mutbl) => Some(RefSimplifiedType(mutbl)),
ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(ClosureSimplifiedType(def_id)),
ty::Generator(def_id, _, _) => Some(GeneratorSimplifiedType(def_id)),
ty::GeneratorWitness(ref tys) => {
pub struct FlagComputation {
pub flags: TypeFlags,
- // see `TyS::outer_exclusive_binder` for details
+ // see `Ty::outer_exclusive_binder` for details
pub outer_exclusive_binder: ty::DebruijnIndex,
}
result
}
- pub fn for_const(c: &ty::Const<'_>) -> TypeFlags {
+ pub fn for_const(c: ty::Const<'_>) -> TypeFlags {
let mut result = FlagComputation::new();
result.add_const(c);
result.flags
fn add_ty(&mut self, ty: Ty<'_>) {
self.add_flags(ty.flags());
- self.add_exclusive_binder(ty.outer_exclusive_binder);
+ self.add_exclusive_binder(ty.outer_exclusive_binder());
}
fn add_tys(&mut self, tys: &[Ty<'_>]) {
}
}
- fn add_const(&mut self, c: &ty::Const<'_>) {
- self.add_ty(c.ty);
- match c.val {
+ fn add_const(&mut self, c: ty::Const<'_>) {
+ self.add_ty(c.ty());
+ match c.val() {
ty::ConstKind::Unevaluated(unevaluated) => self.add_unevaluated_const(unevaluated),
ty::ConstKind::Infer(infer) => {
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
-//! Generalized type folding mechanism. The setup is a bit convoluted
-//! but allows for convenient usage. Let T be an instance of some
-//! "foldable type" (one which implements `TypeFoldable`) and F be an
-//! instance of a "folder" (a type which implements `TypeFolder`). Then
-//! the setup is intended to be:
+//! A generalized traversal mechanism for complex data structures that contain
+//! type information.
//!
-//! T.fold_with(F) --calls--> F.fold_T(T) --calls--> T.super_fold_with(F)
+//! There are two types of traversal.
+//! - Folding. This is a modifying traversal. It consumes the data structure,
+//! producing a (possibly) modified version of it. Both fallible and
+//! infallible versions are available. The name is potentially
+//! confusing, because this traversal is more like `Iterator::map` than
+//! `Iterator::fold`.
+//! - Visiting. This is a read-only traversal of the data structure.
//!
-//! This way, when you define a new folder F, you can override
-//! `fold_T()` to customize the behavior, and invoke `T.super_fold_with()`
-//! to get the original behavior. Meanwhile, to actually fold
-//! something, you can just write `T.fold_with(F)`, which is
-//! convenient. (Note that `fold_with` will also transparently handle
-//! things like a `Vec<T>` where T is foldable and so on.)
+//! These traversals have limited flexibility. Only a small number of "types of
+//! interest" within the complex data structures can receive custom
+//! modification (when folding) or custom visitation (when visiting). These are
+//! the ones containing the most important type-related information, such as
+//! `Ty`, `Predicate`, `Region`, and `Const`.
//!
-//! In this ideal setup, the only function that actually *does*
-//! anything is `T.super_fold_with()`, which traverses the type `T`.
-//! Moreover, `T.super_fold_with()` should only ever call `T.fold_with()`.
+//! There are two traits involved in each traversal type.
+//! - The first trait is `TypeFoldable`, which is implemented once for many
+//! types. This includes both (a) types of interest, and (b) all other
+//! relevant types, including generic containers like `Vec` and `Option`. It
+//! defines a "skeleton" of how they should be traversed, for both folding
+//! and visiting.
+//! - The second trait is `TypeFolder`/`FallibleTypeFolder` (for
+//! infallible/fallible folding traversals) or `TypeVisitor` (for visiting
+//! traversals). One of these is implemented for each folder/visitor. This
+//! defines how types of interest are handled.
//!
-//! In some cases, we follow a degenerate pattern where we do not have
-//! a `fold_T` method. Instead, `T.fold_with` traverses the structure directly.
-//! This is suboptimal because the behavior cannot be overridden, but it's
-//! much less work to implement. If you ever *do* need an override that
-//! doesn't exist, it's not hard to convert the degenerate pattern into the
-//! proper thing.
+//! This means each traversal is a mixture of (a) generic traversal operations,
+//! and (b) custom fold/visit operations that are specific to the
+//! folder/visitor.
+//! - The `TypeFoldable` impls handle most of the traversal, and call into
+//! `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` when they encounter a
+//! type of interest.
+//! - A `TypeFolder`/`FallibleTypeFolder`/`TypeVisitor` may also call back into
+//! a `TypeFoldable` impl, because (a) the types of interest are recursive
+//! and can contain other types of interest, and (b) each folder/visitor
+//! might provide custom handling only for some types of interest, or only
+//! for some variants of each type of interest, and then use default
+//! traversal for the remaining cases.
//!
-//! A `TypeFoldable` T can also be visited by a `TypeVisitor` V using similar setup:
-//!
-//! T.visit_with(V) --calls--> V.visit_T(T) --calls--> T.super_visit_with(V).
-//!
-//! These methods return true to indicate that the visitor has found what it is
-//! looking for, and does not need to visit anything else.
+//! For example, if you have `struct S(Ty, U)` where `S: TypeFoldable` and `U:
+//! TypeFoldable`, and an instance `S(ty, u)`, it would be visited like so:
+//! ```
+//! s.visit_with(visitor) calls
+//! - s.super_visit_with(visitor) calls
+//! - ty.visit_with(visitor) calls
+//! - visitor.visit_ty(ty) may call
+//! - ty.super_visit_with(visitor)
+//! - u.visit_with(visitor)
+//! ```
use crate::mir;
use crate::ty::{self, flags::FlagComputation, Binder, Ty, TyCtxt, TypeFlags};
-use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_data_structures::fx::FxHashSet;
use std::fmt;
use std::ops::ControlFlow;
-/// This trait is implemented for every type that can be folded.
-/// Basically, every type that has a corresponding method in `TypeFolder`.
+/// This trait is implemented for every type that can be folded/visited,
+/// providing the skeleton of the traversal.
///
-/// To implement this conveniently, use the derive macro located in `rustc_macros`.
+/// To implement this conveniently, use the derive macro located in
+/// `rustc_macros`.
pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
- /// Consumers may find this more convenient to use with infallible folders than
- /// [`try_super_fold_with`][`TypeFoldable::try_super_fold_with`], to which the
- /// provided default definition delegates. Implementors **should not** override
- /// this provided default definition, to ensure that the two methods are coherent
- /// (provide a definition of `try_super_fold_with` instead).
- fn super_fold_with<F: TypeFolder<'tcx, Error = !>>(self, folder: &mut F) -> Self {
- self.try_super_fold_with(folder).into_ok()
+ /// The main entry point for folding. To fold a value `t` with a folder `f`
+ /// call: `t.try_fold_with(f)`.
+ ///
+ /// For types of interest (such as `Ty`), this default is overridden with a
+ /// method that calls a folder method specifically for that type (such as
+ /// `F::try_fold_ty`). This is where control transfers from `TypeFoldable`
+ /// to `TypeFolder`.
+ ///
+ /// For other types, this default is used.
+ fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
+ self.try_super_fold_with(folder)
}
- /// Consumers may find this more convenient to use with infallible folders than
- /// [`try_fold_with`][`TypeFoldable::try_fold_with`], to which the provided
- /// default definition delegates. Implementors **should not** override this
- /// provided default definition, to ensure that the two methods are coherent
- /// (provide a definition of `try_fold_with` instead).
+
+ /// A convenient alternative to `try_fold_with` for use with infallible
+ /// folders. Do not override this method, to ensure coherence with
+ /// `try_fold_with`.
fn fold_with<F: TypeFolder<'tcx, Error = !>>(self, folder: &mut F) -> Self {
self.try_fold_with(folder).into_ok()
}
+ /// Traverses the type in question, typically by calling `try_fold_with` on
+ /// each field/element. This is true even for types of interest such as
+ /// `Ty`. This should only be called within `TypeFolder` methods, when
+ /// non-custom traversals are desired for types of interest.
fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(
self,
folder: &mut F,
) -> Result<Self, F::Error>;
- fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
- self.try_super_fold_with(folder)
+ /// A convenient alternative to `try_super_fold_with` for use with
+ /// infallible folders. Do not override this method, to ensure coherence
+ /// with `try_super_fold_with`.
+ fn super_fold_with<F: TypeFolder<'tcx, Error = !>>(self, folder: &mut F) -> Self {
+ self.try_super_fold_with(folder).into_ok()
}
- fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>;
+ /// The entry point for visiting. To visit a value `t` with a visitor `v`
+ /// call: `t.visit_with(v)`.
+ ///
+ /// For types of interest (such as `Ty`), this default is overridden with a
+ /// method that calls a visitor method specifically for that type (such as
+ /// `V::visit_ty`). This is where control transfers from `TypeFoldable` to
+ /// `TypeVisitor`.
+ ///
+ /// For other types, this default is used.
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.super_visit_with(visitor)
}
+ /// Traverses the type in question, typically by calling `visit_with` on
+ /// each field/element. This is true even for types of interest such as
+ /// `Ty`. This should only be called within `TypeVisitor` methods, when
+ /// non-custom traversals are desired for types of interest.
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy>;
+
/// Returns `true` if `self` has any late-bound regions that are either
/// bound by `binder` or bound by some binder outside of `binder`.
/// If `binder` is `ty::INNERMOST`, this indicates whether
}
}
-impl<'tcx> TypeFoldable<'tcx> for hir::Constness {
- fn try_super_fold_with<F: TypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> {
- Ok(self)
- }
- fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> {
- ControlFlow::CONTINUE
- }
-}
-
-/// The `TypeFolder` trait defines the actual *folding*. There is a
-/// method defined for every foldable type. Each of these has a
-/// default implementation that does an "identity" fold. Within each
-/// identity fold, it should invoke `foo.fold_with(self)` to fold each
-/// sub-item.
+/// This trait is implemented for every folding traversal. There is a fold
+/// method defined for every type of interest. Each such method has a default
+/// that does an "identity" fold.
///
/// If this folder is fallible (and therefore its [`Error`][`TypeFolder::Error`]
-/// associated type is something other than the default, never),
-/// [`FallibleTypeFolder`] should be implemented manually; otherwise,
+/// associated type is something other than the default `!`) then
+/// [`FallibleTypeFolder`] should be implemented manually. Otherwise,
/// a blanket implementation of [`FallibleTypeFolder`] will defer to
/// the infallible methods of this trait to ensure that the two APIs
/// are coherent.
r.super_fold_with(self)
}
- fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx>
where
Self: TypeFolder<'tcx, Error = !>,
{
}
}
-/// The `FallibleTypeFolder` trait defines the actual *folding*. There is a
-/// method defined for every foldable type. Each of these has a
-/// default implementation that does an "identity" fold. Within each
-/// identity fold, it should invoke `foo.try_fold_with(self)` to fold each
-/// sub-item.
+/// This trait is implemented for every folding traversal. There is a fold
+/// method defined for every type of interest. Each such method has a default
+/// that does an "identity" fold.
///
/// A blanket implementation of this trait (that defers to the relevant
/// method of [`TypeFolder`]) is provided for all infallible folders in
r.try_super_fold_with(self)
}
- fn try_fold_const(
- &mut self,
- c: &'tcx ty::Const<'tcx>,
- ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> {
+ fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
c.try_super_fold_with(self)
}
}
}
-// Blanket implementation of fallible trait for infallible folders
-// delegates to infallible methods to prevent incoherence
+// This blanket implementation of the fallible trait for infallible folders
+// delegates to infallible methods to ensure coherence.
impl<'tcx, F> FallibleTypeFolder<'tcx> for F
where
F: TypeFolder<'tcx, Error = !>,
Ok(self.fold_region(r))
}
- fn try_fold_const(
- &mut self,
- c: &'tcx ty::Const<'tcx>,
- ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> {
+ fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
Ok(self.fold_const(c))
}
}
}
+/// This trait is implemented for every visiting traversal. There is a visit
+/// method defined for every type of interest. Each such method has a default
+/// that recurses into the type's fields in a non-custom fashion.
pub trait TypeVisitor<'tcx>: Sized {
type BreakTy = !;
r.super_visit_with(self)
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
c.super_visit_with(self)
}
where
F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
- H: FnMut(&'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>,
+ H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
{
pub tcx: TyCtxt<'tcx>,
pub ty_op: F,
where
F: FnMut(Ty<'tcx>) -> Ty<'tcx>,
G: FnMut(ty::Region<'tcx>) -> ty::Region<'tcx>,
- H: FnMut(&'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx>,
+ H: FnMut(ty::Const<'tcx>) -> ty::Const<'tcx>,
{
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
self.tcx
(self.lt_op)(r)
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
let ct = ct.super_fold_with(self);
(self.ct_op)(ct)
}
fld_r: Option<&'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a)>,
fld_t: Option<&'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a)>,
- fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a)>,
+ fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a)>,
}
impl<'a, 'tcx> BoundVarReplacer<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
fld_r: Option<&'a mut (dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx> + 'a)>,
fld_t: Option<&'a mut (dyn FnMut(ty::BoundTy) -> Ty<'tcx> + 'a)>,
- fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx> + 'a)>,
+ fld_c: Option<&'a mut (dyn FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx> + 'a)>,
) -> Self {
BoundVarReplacer { tcx, current_index: ty::INNERMOST, fld_r, fld_t, fld_c }
}
ty::Bound(debruijn, bound_ty) if debruijn == self.current_index => {
if let Some(fld_t) = self.fld_t.as_mut() {
let ty = fld_t(bound_ty);
- return ty::fold::shift_vars(self.tcx, &ty, self.current_index.as_u32());
+ return ty::fold::shift_vars(self.tcx, ty, self.current_index.as_u32());
}
}
_ if t.has_vars_bound_at_or_above(self.current_index) => {
r
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- match *ct {
- ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty }
- if debruijn == self.current_index =>
- {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ match ct.val() {
+ ty::ConstKind::Bound(debruijn, bound_const) if debruijn == self.current_index => {
if let Some(fld_c) = self.fld_c.as_mut() {
- let ct = fld_c(bound_const, ty);
- return ty::fold::shift_vars(self.tcx, &ct, self.current_index.as_u32());
+ let ct = fld_c(bound_const, ct.ty());
+ return ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32());
}
}
_ if ct.has_vars_bound_at_or_above(self.current_index) => {
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
G: FnMut(ty::BoundTy) -> Ty<'tcx>,
- H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>,
+ H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>,
T: TypeFoldable<'tcx>,
{
if !value.has_escaping_bound_vars() {
where
F: FnMut(ty::BoundRegion) -> ty::Region<'tcx>,
G: FnMut(ty::BoundTy) -> Ty<'tcx>,
- H: FnMut(ty::BoundVar, Ty<'tcx>) -> &'tcx ty::Const<'tcx>,
+ H: FnMut(ty::BoundVar, Ty<'tcx>) -> ty::Const<'tcx>,
T: TypeFoldable<'tcx>,
{
let mut region_map = BTreeMap::new();
))
},
|c, ty| {
- self.mk_const(ty::Const {
+ self.mk_const(ty::ConstS {
val: ty::ConstKind::Bound(
ty::INNERMOST,
ty::BoundVar::from_usize(c.as_usize() + bound_vars),
}
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
- if t.outer_exclusive_binder < self.binder_index
+ if t.outer_exclusive_binder() < self.binder_index
|| !self.visited.insert((self.binder_index, t))
{
return ControlFlow::BREAK;
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match r {
- ty::ReLateBound(index, br) if *index == self.binder_index => {
+ match *r {
+ ty::ReLateBound(index, br) if index == self.binder_index => {
if self.bound_vars.len() <= br.var.as_usize() {
- bug!("Not enough bound vars: {:?} not found in {:?}", *br, self.bound_vars);
+ bug!("Not enough bound vars: {:?} not found in {:?}", br, self.bound_vars);
}
let list_var = self.bound_vars[br.var.as_usize()];
match list_var {
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- if let ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), ty } = *ct {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ if let ty::ConstKind::Bound(debruijn, bound_ct) = ct.val() {
if self.amount == 0 || debruijn < self.current_index {
ct
} else {
let debruijn = debruijn.shifted_in(self.amount);
- self.tcx.mk_const(ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), ty })
+ self.tcx.mk_const(ty::ConstS {
+ val: ty::ConstKind::Bound(debruijn, bound_ct),
+ ty: ct.ty(),
+ })
}
} else {
ct.super_fold_with(self)
region: ty::Region<'tcx>,
amount: u32,
) -> ty::Region<'tcx> {
- match region {
+ match *region {
ty::ReLateBound(debruijn, br) if amount > 0 => {
- tcx.mk_region(ty::ReLateBound(debruijn.shifted_in(amount), *br))
+ tcx.mk_region(ty::ReLateBound(debruijn.shifted_in(amount), br))
}
_ => region,
}
// bound at `outer_index` or above (because
// `outer_exclusive_binder` is always 1 higher than the
// content in `t`). Therefore, `t` has some escaping vars.
- if t.outer_exclusive_binder > self.outer_index {
+ if t.outer_exclusive_binder() > self.outer_index {
ControlFlow::Break(FoundEscapingVars)
} else {
ControlFlow::CONTINUE
}
}
- fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
// we don't have a `visit_infer_const` callback, so we have to
// hook in here to catch this case (annoying...), but
// otherwise we do want to remember to visit the rest of the
// const, as it has types/regions embedded in a lot of other
// places.
- match ct.val {
+ match ct.val() {
ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => {
ControlFlow::Break(FoundEscapingVars)
}
#[inline]
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
- if predicate.inner.outer_exclusive_binder > self.outer_index {
+ if predicate.outer_exclusive_binder() > self.outer_index {
ControlFlow::Break(FoundEscapingVars)
} else {
ControlFlow::CONTINUE
#[inline]
#[instrument(level = "trace")]
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
let flags = FlagComputation::for_const(c);
trace!(r.flags=?flags);
if flags.intersects(self.flags) {
fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!(
"HasTypeFlagsVisitor: predicate={:?} predicate.flags={:?} self.flags={:?}",
- predicate, predicate.inner.flags, self.flags
+ predicate,
+ predicate.flags(),
+ self.flags
);
- if predicate.inner.flags.intersects(self.flags) {
+ if predicate.flags().intersects(self.flags) {
ControlFlow::Break(FoundFlags)
} else {
ControlFlow::CONTINUE
t.super_visit_with(self)
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
// if we are only looking for "constrained" region, we have to
// ignore the inputs of an unevaluated const, as they may not appear
// in the normalized form
if self.just_constrained {
- if let ty::ConstKind::Unevaluated(..) = c.val {
+ if let ty::ConstKind::Unevaluated(..) = c.val() {
return ControlFlow::CONTINUE;
}
}
use rustc_span::def_id::CRATE_DEF_ID;
use smallvec::SmallVec;
use std::mem;
-use std::sync::Arc;
use DefIdForest::*;
/// We store the minimal set of `DefId`s required to represent the whole set. If A and B are
/// `DefId`s in the `DefIdForest`, and A is a parent of B, then only A will be stored. When this is
/// used with `type_uninhabited_from`, there will very rarely be more than one `DefId` stored.
-#[derive(Clone, HashStable, Debug)]
-pub enum DefIdForest {
+#[derive(Copy, Clone, HashStable, Debug)]
+pub enum DefIdForest<'a> {
Empty,
Single(DefId),
/// This variant is very rare.
/// Invariant: >1 elements
- /// We use `Arc` because this is used in the output of a query.
- Multiple(Arc<[DefId]>),
+ Multiple(&'a [DefId]),
}
/// Tests whether a slice of roots contains a given DefId.
slice.iter().any(|root_id| tcx.is_descendant_of(id, *root_id))
}
-impl<'tcx> DefIdForest {
+impl<'tcx> DefIdForest<'tcx> {
/// Creates an empty forest.
- pub fn empty() -> DefIdForest {
+ pub fn empty() -> DefIdForest<'tcx> {
DefIdForest::Empty
}
/// Creates a forest consisting of a single tree representing the entire
/// crate.
#[inline]
- pub fn full() -> DefIdForest {
+ pub fn full() -> DefIdForest<'tcx> {
DefIdForest::from_id(CRATE_DEF_ID.to_def_id())
}
/// Creates a forest containing a `DefId` and all its descendants.
- pub fn from_id(id: DefId) -> DefIdForest {
+ pub fn from_id(id: DefId) -> DefIdForest<'tcx> {
DefIdForest::Single(id)
}
}
// Only allocates in the rare `Multiple` case.
- fn from_slice(root_ids: &[DefId]) -> DefIdForest {
- match root_ids {
+ fn from_vec(tcx: TyCtxt<'tcx>, root_ids: SmallVec<[DefId; 1]>) -> DefIdForest<'tcx> {
+ match &root_ids[..] {
[] => Empty,
[id] => Single(*id),
- _ => DefIdForest::Multiple(root_ids.into()),
+ _ => DefIdForest::Multiple(tcx.arena.alloc_from_iter(root_ids)),
}
}
}
/// Calculate the intersection of a collection of forests.
- pub fn intersection<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest
+ pub fn intersection<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest<'tcx>
where
- I: IntoIterator<Item = DefIdForest>,
+ I: IntoIterator<Item = DefIdForest<'tcx>>,
{
let mut iter = iter.into_iter();
let mut ret: SmallVec<[_; 1]> = if let Some(first) = iter.next() {
mem::swap(&mut next_ret, &mut ret);
next_ret.clear();
}
- DefIdForest::from_slice(&ret)
+ DefIdForest::from_vec(tcx, ret)
}
/// Calculate the union of a collection of forests.
- pub fn union<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest
+ pub fn union<I>(tcx: TyCtxt<'tcx>, iter: I) -> DefIdForest<'tcx>
where
- I: IntoIterator<Item = DefIdForest>,
+ I: IntoIterator<Item = DefIdForest<'tcx>>,
{
let mut ret: SmallVec<[_; 1]> = SmallVec::new();
let mut next_ret: SmallVec<[_; 1]> = SmallVec::new();
mem::swap(&mut next_ret, &mut ret);
next_ret.clear();
}
- DefIdForest::from_slice(&ret)
+ DefIdForest::from_vec(tcx, ret)
}
}
use crate::ty;
use crate::ty::context::TyCtxt;
use crate::ty::TyKind::*;
-use crate::ty::{AdtDef, FieldDef, Ty, TyS, VariantDef};
+use crate::ty::{AdtDef, FieldDef, Ty, VariantDef};
use crate::ty::{AdtKind, Visibility};
use crate::ty::{DefId, SubstsRef};
tcx: TyCtxt<'tcx>,
substs: SubstsRef<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- ) -> DefIdForest {
+ ) -> DefIdForest<'tcx> {
// Non-exhaustive ADTs from other crates are always considered inhabited.
if self.is_variant_list_non_exhaustive() && !self.did.is_local() {
DefIdForest::empty()
substs: SubstsRef<'tcx>,
adt_kind: AdtKind,
param_env: ty::ParamEnv<'tcx>,
- ) -> DefIdForest {
+ ) -> DefIdForest<'tcx> {
let is_enum = match adt_kind {
// For now, `union`s are never considered uninhabited.
// The precise semantics of inhabitedness with respect to unions is currently undecided.
substs: SubstsRef<'tcx>,
is_enum: bool,
param_env: ty::ParamEnv<'tcx>,
- ) -> DefIdForest {
+ ) -> DefIdForest<'tcx> {
let data_uninhabitedness = move || self.ty(tcx, substs).uninhabited_from(tcx, param_env);
// FIXME(canndrew): Currently enum fields are (incorrectly) stored with
// `Visibility::Invisible` so we need to override `self.vis` if we're
}
}
-impl<'tcx> TyS<'tcx> {
+impl<'tcx> Ty<'tcx> {
/// Calculates the forest of `DefId`s from which this type is visibly uninhabited.
fn uninhabited_from(
- &'tcx self,
+ self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- ) -> DefIdForest {
- tcx.type_uninhabited_from(param_env.and(self))
+ ) -> DefIdForest<'tcx> {
+ tcx.type_uninhabited_from(param_env.and(self)).clone()
}
}
pub(crate) fn type_uninhabited_from<'tcx>(
tcx: TyCtxt<'tcx>,
key: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
-) -> DefIdForest {
+) -> DefIdForest<'tcx> {
let ty = key.value;
let param_env = key.param_env;
match *ty.kind() {
/// lifetimes erased, allowing a `ParamEnv` to be specified for use during normalization.
pub fn ty(&self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Ty<'tcx> {
let ty = tcx.type_of(self.def.def_id());
- tcx.subst_and_normalize_erasing_regions(self.substs, param_env, &ty)
+ tcx.subst_and_normalize_erasing_regions(self.substs, param_env, ty)
}
/// Finds a crate that contains a monomorphization of this instance that
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
debug!("fold_ty: ty={:?}", ty);
- match ty.kind {
+ match *ty.kind() {
ty::Closure(def_id, substs) => {
let polymorphized_substs = polymorphize(
self.tcx,
},
};
let mut abi = Abi::Aggregate { sized: true };
- if tag.value.size(dl) == size {
+
+ // Without latter check aligned enums with custom discriminant values
+ // Would result in ICE see the issue #92464 for more info
+ if tag.value.size(dl) == size || variants.iter().all(|layout| layout.is_empty()) {
abi = Abi::Scalar(tag);
} else {
// Try to use a ScalarPair for all tagged enums.
// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
use SpecAbi::*;
match abi {
- C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
+ C { unwind }
+ | System { unwind }
+ | Cdecl { unwind }
+ | Stdcall { unwind }
+ | Fastcall { unwind }
+ | Vectorcall { unwind }
+ | Thiscall { unwind }
+ | Aapcs { unwind }
+ | Win64 { unwind }
+ | SysV64 { unwind } => {
unwind
|| (!tcx.features().c_unwind && tcx.sess.panic_strategy() == PanicStrategy::Unwind)
}
- Cdecl
- | Fastcall
- | Vectorcall
- | Aapcs
- | Win64
- | SysV64
- | PtxKernel
+ PtxKernel
| Msp430Interrupt
| X86Interrupt
| AmdGpuKernel
EfiApi => bug!("eficall abi should be selected elsewhere"),
Stdcall { .. } => Conv::X86Stdcall,
- Fastcall => Conv::X86Fastcall,
- Vectorcall => Conv::X86VectorCall,
+ Fastcall { .. } => Conv::X86Fastcall,
+ Vectorcall { .. } => Conv::X86VectorCall,
Thiscall { .. } => Conv::X86ThisCall,
C { .. } => Conv::C,
Unadjusted => Conv::C,
- Win64 => Conv::X86_64Win64,
- SysV64 => Conv::X86_64SysV,
- Aapcs => Conv::ArmAapcs,
+ Win64 { .. } => Conv::X86_64Win64,
+ SysV64 { .. } => Conv::X86_64SysV,
+ Aapcs { .. } => Conv::ArmAapcs,
CCmseNonSecureCall => Conv::CCmseNonSecureCall,
PtxKernel => Conv::PtxKernel,
Msp430Interrupt => Conv::Msp430Intr,
Wasm => Conv::C,
// These API constants ought to be more specific...
- Cdecl => Conv::C,
+ Cdecl { .. } => Conv::C,
}
}
/// Error produced by attempting to compute or adjust a `FnAbi`.
-#[derive(Clone, Debug, HashStable)]
+#[derive(Copy, Clone, Debug, HashStable)]
pub enum FnAbiError<'tcx> {
/// Error produced by a `layout_of` call, while computing `FnAbi` initially.
Layout(LayoutError<'tcx>),
layout: TyAndLayout<'tcx>,
offset: Size,
is_return: bool| {
- // Booleans are always an i1 that needs to be zero-extended.
+ // Booleans are always a noundef i1 that needs to be zero-extended.
if scalar.is_bool() {
attrs.ext(ArgExtension::Zext);
+ attrs.set(ArgAttribute::NoUndef);
return;
}
_ => pointee.size,
};
+ // `Box`, `&T`, and `&mut T` cannot be undef.
+ // Note that this only applies to the value of the pointer itself;
+ // this attribute doesn't make it UB for the pointed-to data to be undef.
+ attrs.set(ArgAttribute::NoUndef);
+
// `Box` pointer parameters never alias because ownership is transferred
// `&mut` pointer parameters never alias other parameters,
// or mutable global data
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
use rustc_hir as hir;
use rustc_span::{sym, Span};
use rustc_target::abi::Align;
-use std::cmp::Ordering;
-use std::hash::{Hash, Hasher};
+use std::hash::Hash;
use std::ops::ControlFlow;
-use std::{fmt, ptr, str};
+use std::{fmt, str};
pub use crate::ty::diagnostics::*;
pub use rustc_type_ir::InferTy::*;
RootVariableMinCaptureList, UpvarCapture, UpvarCaptureMap, UpvarId, UpvarListMap, UpvarPath,
CAPTURE_STRUCT_LOCAL,
};
-pub use self::consts::{Const, ConstInt, ConstKind, InferConst, ScalarInt, Unevaluated, ValTree};
+pub use self::consts::{
+ Const, ConstInt, ConstKind, ConstS, InferConst, ScalarInt, Unevaluated, ValTree,
+};
pub use self::context::{
tls, CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations,
CtxtInterners, DelaySpanBugEmitted, FreeRegionInfo, GeneratorInteriorTypeCause, GlobalCtxt,
/// Represents a type.
///
-/// IMPORTANT: Every `TyS` is *required* to have unique contents. The type's
-/// correctness relies on this, *but it does not enforce it*. Therefore, any
-/// code that creates a `TyS` must ensure uniqueness itself. In practice this
-/// is achieved by interning.
+/// IMPORTANT:
+/// - This is a very "dumb" struct (with no derives and no `impls`).
+/// - Values of this type are always interned and thus unique, and are stored
+/// as an `Interned<TyS>`.
+/// - `Ty` (which contains a reference to a `Interned<TyS>`) or `Interned<TyS>`
+/// should be used everywhere instead of `TyS`. In particular, `Ty` has most
+/// of the relevant methods.
+#[derive(PartialEq, Eq, PartialOrd, Ord)]
#[allow(rustc::usage_of_ty_tykind)]
-pub struct TyS<'tcx> {
+crate struct TyS<'tcx> {
/// This field shouldn't be used directly and may be removed in the future.
- /// Use `TyS::kind()` instead.
+ /// Use `Ty::kind()` instead.
kind: TyKind<'tcx>,
/// This field provides fast access to information that is also contained
/// in `kind`.
///
/// This field shouldn't be used directly and may be removed in the future.
- /// Use `TyS::flags()` instead.
+ /// Use `Ty::flags()` instead.
flags: TypeFlags,
/// This field provides fast access to information that is also contained
outer_exclusive_binder: ty::DebruijnIndex,
}
-impl<'tcx> TyS<'tcx> {
- /// A constructor used only for internal testing.
- #[allow(rustc::usage_of_ty_tykind)]
- pub fn make_for_test(
- kind: TyKind<'tcx>,
- flags: TypeFlags,
- outer_exclusive_binder: ty::DebruijnIndex,
- ) -> TyS<'tcx> {
- TyS { kind, flags, outer_exclusive_binder }
- }
-}
-
// `TyS` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
static_assert_size!(TyS<'_>, 40);
-impl<'tcx> Ord for TyS<'tcx> {
- fn cmp(&self, other: &TyS<'tcx>) -> Ordering {
- self.kind().cmp(other.kind())
- }
-}
-
-impl<'tcx> PartialOrd for TyS<'tcx> {
- fn partial_cmp(&self, other: &TyS<'tcx>) -> Option<Ordering> {
- Some(self.kind().cmp(other.kind()))
- }
-}
-
-impl<'tcx> PartialEq for TyS<'tcx> {
- #[inline]
- fn eq(&self, other: &TyS<'tcx>) -> bool {
- // Pointer equality implies equality (due to the unique contents
- // assumption).
- ptr::eq(self, other)
- }
-}
-impl<'tcx> Eq for TyS<'tcx> {}
-
-impl<'tcx> Hash for TyS<'tcx> {
- fn hash<H: Hasher>(&self, s: &mut H) {
- // Pointer hashing is sufficient (due to the unique contents
- // assumption).
- (self as *const TyS<'_>).hash(s)
- }
-}
+/// Use this rather than `TyS`, whenever possible.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[rustc_diagnostic_item = "Ty"]
+#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
+pub struct Ty<'tcx>(Interned<'tcx, TyS<'tcx>>);
+
+// Statics only used for internal testing.
+pub static BOOL_TY: Ty<'static> = Ty(Interned::new_unchecked(&BOOL_TYS));
+static BOOL_TYS: TyS<'static> = TyS {
+ kind: ty::Bool,
+ flags: TypeFlags::empty(),
+ outer_exclusive_binder: DebruijnIndex::from_usize(0),
+};
-impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for TyS<'tcx> {
+impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Ty<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
- let ty::TyS {
+ let TyS {
ref kind,
// The other fields just provide fast access to information that is
flags: _,
outer_exclusive_binder: _,
- } = *self;
+ } = self.0.0;
kind.hash_stable(hcx, hasher);
}
}
-#[rustc_diagnostic_item = "Ty"]
-#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
-pub type Ty<'tcx> = &'tcx TyS<'tcx>;
-
impl ty::EarlyBoundRegion {
/// Does this early bound region have a name? Early bound regions normally
/// always have names except when using anonymous lifetimes (`'_`).
}
}
+/// Represents a predicate.
+///
+/// See comments on `TyS`, which apply here too (albeit for
+/// `PredicateS`/`Predicate` rather than `TyS`/`Ty`).
#[derive(Debug)]
-crate struct PredicateInner<'tcx> {
+crate struct PredicateS<'tcx> {
kind: Binder<'tcx, PredicateKind<'tcx>>,
flags: TypeFlags,
/// See the comment for the corresponding field of [TyS].
outer_exclusive_binder: ty::DebruijnIndex,
}
+// This type is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(PredicateInner<'_>, 56);
-
-#[derive(Clone, Copy, Lift)]
-pub struct Predicate<'tcx> {
- inner: &'tcx PredicateInner<'tcx>,
-}
+static_assert_size!(PredicateS<'_>, 56);
-impl<'tcx> PartialEq for Predicate<'tcx> {
- fn eq(&self, other: &Self) -> bool {
- // `self.kind` is always interned.
- ptr::eq(self.inner, other.inner)
- }
-}
-
-impl Hash for Predicate<'_> {
- fn hash<H: Hasher>(&self, s: &mut H) {
- (self.inner as *const PredicateInner<'_>).hash(s)
- }
-}
-
-impl<'tcx> Eq for Predicate<'tcx> {}
+/// Use this rather than `PredicateS`, whenever possible.
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
+pub struct Predicate<'tcx>(Interned<'tcx, PredicateS<'tcx>>);
impl<'tcx> Predicate<'tcx> {
/// Gets the inner `Binder<'tcx, PredicateKind<'tcx>>`.
#[inline]
pub fn kind(self) -> Binder<'tcx, PredicateKind<'tcx>> {
- self.inner.kind
+ self.0.kind
+ }
+
+ #[inline(always)]
+ pub fn flags(self) -> TypeFlags {
+ self.0.flags
+ }
+
+ #[inline(always)]
+ pub fn outer_exclusive_binder(self) -> DebruijnIndex {
+ self.0.outer_exclusive_binder
}
/// Flips the polarity of a Predicate.
///
/// Given `T: Trait` predicate it returns `T: !Trait` and given `T: !Trait` returns `T: Trait`.
- pub fn flip_polarity(&self, tcx: TyCtxt<'tcx>) -> Option<Predicate<'tcx>> {
+ pub fn flip_polarity(self, tcx: TyCtxt<'tcx>) -> Option<Predicate<'tcx>> {
let kind = self
- .inner
- .kind
+ .kind()
.map_bound(|kind| match kind {
PredicateKind::Trait(TraitPredicate { trait_ref, constness, polarity }) => {
Some(PredicateKind::Trait(TraitPredicate {
impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for Predicate<'tcx> {
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
- let PredicateInner {
+ let PredicateS {
ref kind,
// The other fields just provide fast access to information that is
// also contained in `kind`, so no need to hash them.
flags: _,
outer_exclusive_binder: _,
- } = self.inner;
+ } = self.0.0;
kind.hash_stable(hcx, hasher);
}
ConstEvaluatable(ty::Unevaluated<'tcx, ()>),
/// Constants must be equal. The first component is the const that is expected.
- ConstEquate(&'tcx Const<'tcx>, &'tcx Const<'tcx>),
+ ConstEquate(Const<'tcx>, Const<'tcx>),
/// Represents a type found in the environment that we can use for implied bounds.
///
#[derive(HashStable, TypeFoldable)]
pub enum Term<'tcx> {
Ty(Ty<'tcx>),
- Const(&'tcx Const<'tcx>),
+ Const(Const<'tcx>),
}
impl<'tcx> From<Ty<'tcx>> for Term<'tcx> {
}
}
-impl<'tcx> From<&'tcx Const<'tcx>> for Term<'tcx> {
- fn from(c: &'tcx Const<'tcx>) -> Self {
+impl<'tcx> From<Const<'tcx>> for Term<'tcx> {
+ fn from(c: Const<'tcx>) -> Self {
Term::Const(c)
}
}
impl<'tcx> Term<'tcx> {
pub fn ty(&self) -> Option<Ty<'tcx>> {
- if let Term::Ty(ty) = self { Some(ty) } else { None }
+ if let Term::Ty(ty) = self { Some(*ty) } else { None }
}
- pub fn ct(&self) -> Option<&'tcx Const<'tcx>> {
- if let Term::Const(c) = self { Some(c) } else { None }
+ pub fn ct(&self) -> Option<Const<'tcx>> {
+ if let Term::Const(c) = self { Some(*c) } else { None }
}
}
self.normalize_generic_arg_after_erasing_regions(ty.into()).expect_ty()
}
- fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
self.normalize_generic_arg_after_erasing_regions(c.into()).expect_const()
}
}
}
- fn try_fold_const(
- &mut self,
- c: &'tcx ty::Const<'tcx>,
- ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> {
+ fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
match self.try_normalize_generic_arg_after_erasing_regions(c.into()) {
Ok(t) => Ok(t.expect_const()),
- Err(_) => Err(NormalizationError::Const(*c)),
+ Err(_) => Err(NormalizationError::Const(c)),
}
}
predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
) -> Result<Self::DynExistential, Self::Error>;
- fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error>;
+ fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error>;
fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error>;
own_params.start = 1;
}
- // If we're in verbose mode, then print default-equal args too
- if self.tcx().sess.verbose() {
- return &substs[own_params];
- }
-
// Don't print args that are the defaults of their respective parameters.
own_params.end -= generics
.params
characteristic_def_id_of_type_cached(ty, &mut SsoHashSet::new())
}
-impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::RegionKind {
- type Output = P::Region;
- type Error = P::Error;
- fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
- cx.print_region(self)
- }
-}
-
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'_> {
type Output = P::Region;
type Error = P::Error;
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
- cx.print_region(self)
+ cx.print_region(*self)
}
}
type Output = P::Type;
type Error = P::Error;
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
- cx.print_type(self)
+ cx.print_type(*self)
}
}
}
}
-impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::Const<'tcx> {
+impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> {
type Output = P::Const;
type Error = P::Error;
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
- cx.print_const(self)
+ cx.print_const(*self)
}
}
use crate::ty::{self, ConstInt, DefIdTree, ParamConst, ScalarInt, Term, Ty, TyCtxt, TypeFoldable};
use rustc_apfloat::ieee::{Double, Single};
use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::sso::SsoHashSet;
use rustc_hir as hir;
use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
///
/// Regions not selected by the region highlight mode are presently
/// unaffected.
-#[derive(Copy, Clone, Default)]
-pub struct RegionHighlightMode {
+#[derive(Copy, Clone)]
+pub struct RegionHighlightMode<'tcx> {
+ tcx: TyCtxt<'tcx>,
+
/// If enabled, when we see the selected region, use "`'N`"
/// instead of the ordinary behavior.
- highlight_regions: [Option<(ty::RegionKind, usize)>; 3],
+ highlight_regions: [Option<(ty::Region<'tcx>, usize)>; 3],
/// If enabled, when printing a "free region" that originated from
/// the given `ty::BoundRegionKind`, print it as "`'1`". Free regions that would ordinarily
highlight_bound_region: Option<(ty::BoundRegionKind, usize)>,
}
-impl RegionHighlightMode {
+impl<'tcx> RegionHighlightMode<'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>) -> Self {
+ Self {
+ tcx,
+ highlight_regions: Default::default(),
+ highlight_bound_region: Default::default(),
+ }
+ }
+
/// If `region` and `number` are both `Some`, invokes
/// `highlighting_region`.
pub fn maybe_highlighting_region(
&mut self,
- region: Option<ty::Region<'_>>,
+ region: Option<ty::Region<'tcx>>,
number: Option<usize>,
) {
if let Some(k) = region {
}
/// Highlights the region inference variable `vid` as `'N`.
- pub fn highlighting_region(&mut self, region: ty::Region<'_>, number: usize) {
+ pub fn highlighting_region(&mut self, region: ty::Region<'tcx>, number: usize) {
let num_slots = self.highlight_regions.len();
let first_avail_slot =
self.highlight_regions.iter_mut().find(|s| s.is_none()).unwrap_or_else(|| {
bug!("can only highlight {} placeholders at a time", num_slots,)
});
- *first_avail_slot = Some((*region, number));
+ *first_avail_slot = Some((region, number));
}
/// Convenience wrapper for `highlighting_region`.
pub fn highlighting_region_vid(&mut self, vid: ty::RegionVid, number: usize) {
- self.highlighting_region(&ty::ReVar(vid), number)
+ self.highlighting_region(self.tcx.mk_region(ty::ReVar(vid)), number)
}
/// Returns `Some(n)` with the number to use for the given region, if any.
fn region_highlighted(&self, region: ty::Region<'_>) -> Option<usize> {
self.highlight_regions.iter().find_map(|h| match h {
- Some((r, n)) if r == region => Some(*n),
+ Some((r, n)) if *r == region => Some(*n),
_ => None,
})
}
p!("[", print(ty), "; ");
if self.tcx().sess.verbose() {
p!(write("{:?}", sz));
- } else if let ty::ConstKind::Unevaluated(..) = sz.val {
+ } else if let ty::ConstKind::Unevaluated(..) = sz.val() {
// Do not try to evaluate unevaluated constants. If we are const evaluating an
// array length anon const, rustc will (with debug assertions) print the
// constant's path. Which will end up here again.
p!("_");
- } else if let Some(n) = sz.val.try_to_bits(self.tcx().data_layout.pointer_size) {
+ } else if let Some(n) = sz.val().try_to_bits(self.tcx().data_layout.pointer_size) {
p!(write("{}", n));
- } else if let ty::ConstKind::Param(param) = sz.val {
+ } else if let ty::ConstKind::Param(param) = sz.val() {
p!(write("{}", param));
} else {
p!("_");
// Don't print `'_` if there's no unerased regions.
let print_regions = args.iter().any(|arg| match arg.unpack() {
- GenericArgKind::Lifetime(r) => *r != ty::ReErased,
+ GenericArgKind::Lifetime(r) => !r.is_erased(),
_ => false,
});
let mut args = args.iter().cloned().filter(|arg| match arg.unpack() {
fn pretty_print_const(
mut self,
- ct: &'tcx ty::Const<'tcx>,
+ ct: ty::Const<'tcx>,
print_ty: bool,
) -> Result<Self::Const, Self::Error> {
define_scoped_cx!(self);
if self.tcx().sess.verbose() {
- p!(write("Const({:?}: {:?})", ct.val, ct.ty));
+ p!(write("Const({:?}: {:?})", ct.val(), ct.ty()));
return Ok(self);
}
write!(this, "_")?;
Ok(this)
},
- |this| this.print_type(ct.ty),
+ |this| this.print_type(ct.ty()),
": ",
)?;
} else {
}};
}
- match ct.val {
+ match ct.val() {
ty::ConstKind::Unevaluated(ty::Unevaluated {
def,
substs,
ty::ConstKind::Infer(..) => print_underscore!(),
ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
ty::ConstKind::Value(value) => {
- return self.pretty_print_const_value(value, ct.ty, print_ty);
+ return self.pretty_print_const_value(value, ct.ty(), print_ty);
}
ty::ConstKind::Bound(debruijn, bound_var) => {
// Byte strings (&[u8; N])
ty::Ref(
_,
- ty::TyS {
- kind:
- ty::Array(
- ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. },
- ty::Const {
- val: ty::ConstKind::Value(ConstValue::Scalar(int)), ..
- },
- ),
- ..
- },
+ Ty(Interned(
+ ty::TyS {
+ kind:
+ ty::Array(
+ Ty(Interned(ty::TyS { kind: ty::Uint(ty::UintTy::U8), .. }, _)),
+ ty::Const(Interned(
+ ty::ConstS {
+ val: ty::ConstKind::Value(ConstValue::Scalar(int)),
+ ..
+ },
+ _,
+ )),
+ ),
+ ..
+ },
+ _,
+ )),
_,
) => match self.tcx().get_global_alloc(alloc_id) {
Some(GlobalAlloc::Memory(alloc)) => {
// Byte/string slices, printed as (byte) string literals.
(
ConstValue::Slice { data, start, end },
- ty::Ref(_, ty::TyS { kind: ty::Slice(t), .. }, _),
+ ty::Ref(_, Ty(Interned(ty::TyS { kind: ty::Slice(t), .. }, _)), _),
) if *t == u8_type => {
// The `inspect` here is okay since we checked the bounds, and there are
// no relocations (we have an active slice reference here). We don't use
}
(
ConstValue::Slice { data, start, end },
- ty::Ref(_, ty::TyS { kind: ty::Str, .. }, _),
+ ty::Ref(_, Ty(Interned(ty::TyS { kind: ty::Str, .. }, _)), _),
) => {
// The `inspect` here is okay since we checked the bounds, and there are no
// relocations (we have an active `str` reference here). We don't use this
Ok(self)
}
(ConstValue::ByRef { alloc, offset }, ty::Array(t, n)) if *t == u8_type => {
- let n = n.val.try_to_bits(self.tcx().data_layout.pointer_size).unwrap();
+ let n = n.val().try_to_bits(self.tcx().data_layout.pointer_size).unwrap();
// cast is ok because we already checked for pointer size (32 or 64 bit) above
let range = AllocRange { start: offset, size: Size::from_bytes(n) };
// FIXME(eddyb) for `--emit=mir`/`-Z dump-mir`, we should provide the
// correct `ty::ParamEnv` to allow printing *all* constant values.
(_, ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) if !ty.has_param_types_or_consts() => {
- let contents = self.tcx().destructure_const(
+ let Some(contents) = self.tcx().try_destructure_const(
ty::ParamEnv::reveal_all()
- .and(self.tcx().mk_const(ty::Const { val: ty::ConstKind::Value(ct), ty })),
- );
+ .and(self.tcx().mk_const(ty::ConstS { val: ty::ConstKind::Value(ct), ty })),
+ ) else {
+ // Fall back to debug pretty printing for invalid constants.
+ p!(write("{:?}", ct));
+ if print_ty {
+ p!(": ", print(ty));
+ }
+ return Ok(self);
+ };
+
let fields = contents.fields.iter().copied();
match *ty.kind() {
binder_depth: usize,
printed_type_count: usize,
- pub region_highlight_mode: RegionHighlightMode,
+ pub region_highlight_mode: RegionHighlightMode<'tcx>,
pub name_resolver: Option<Box<&'a dyn Fn(ty::TyVid) -> Option<String>>>,
}
region_index: 0,
binder_depth: 0,
printed_type_count: 0,
- region_highlight_mode: RegionHighlightMode::default(),
+ region_highlight_mode: RegionHighlightMode::new(tcx),
name_resolver: None,
}))
}
self.pretty_print_dyn_existential(predicates)
}
- fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
self.pretty_print_const(ct, true)
}
// Don't print `'_` if there's no unerased regions.
let print_regions = self.tcx.sess.verbose()
|| args.iter().any(|arg| match arg.unpack() {
- GenericArgKind::Lifetime(r) => *r != ty::ReErased,
+ GenericArgKind::Lifetime(r) => !r.is_erased(),
_ => false,
});
let args = args.iter().cloned().filter(|arg| match arg.unpack() {
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
let name = &mut self.name;
let region = match *r {
- ty::ReLateBound(_, br) => self.region_map.entry(br).or_insert_with(|| name(br)),
+ ty::ReLateBound(_, br) => *self.region_map.entry(br).or_insert_with(|| name(br)),
ty::RePlaceholder(ty::PlaceholderRegion { name: kind, .. }) => {
// If this is an anonymous placeholder, don't rename. Otherwise, in some
// async fns, we get a `for<'r> Send` bound
_ => {
// Index doesn't matter, since this is just for naming and these never get bound
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind };
- self.region_map.entry(br).or_insert_with(|| name(br))
+ *self.region_map.entry(br).or_insert_with(|| name(br))
}
}
}
#[instrument(skip(self), level = "trace")]
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- trace!("address: {:p}", r);
+ trace!("address: {:p}", r.0.0);
if let ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) = *r {
self.used_region_names.insert(name);
} else if let ty::RePlaceholder(ty::PlaceholderRegion {
}
// HACK(eddyb) this is separate because `ty::RegionKind` doesn't need lifting.
-impl fmt::Display for ty::RegionKind {
+impl<'tcx> fmt::Display for ty::Region<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(|tcx| {
self.print(FmtPrinter::new(tcx, f, Namespace::TypeNS))?;
forward_display_to_print! {
Ty<'tcx>,
&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
- &'tcx ty::Const<'tcx>,
+ ty::Const<'tcx>,
// HACK(eddyb) these are exhaustive instead of generic,
// because `for<'tcx>` isn't possible yet.
#[inline(always)]
fn noop<T>(_: &T) {}
+/// Helper to ensure that queries only return `Copy` types.
+#[inline(always)]
+fn copy<T: Copy>(x: &T) -> T {
+ *x
+}
+
macro_rules! query_helper_param_ty {
(DefId) => { impl IntoQueryParam<DefId> };
($K:ty) => { $K };
let key = key.into_query_param();
opt_remap_env_constness!([$($modifiers)*][key]);
- let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, Clone::clone);
+ let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, copy);
let lookup = match cached {
Ok(value) => return value,
}
}
+ impl<'a, P: Copy> IntoQueryParam<P> for &'a P {
+ #[inline(always)]
+ fn into_query_param(self) -> P {
+ *self
+ }
+ }
+
impl IntoQueryParam<DefId> for LocalDefId {
#[inline(always)]
fn into_query_param(self) -> DefId {
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>>;
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>>;
fn binders<T>(
&mut self,
Some((ty_def_id, variances)) => {
let variance = variances[i];
let variance_info = if variance == ty::Invariant {
- let ty =
- cached_ty.get_or_insert_with(|| tcx.type_of(ty_def_id).subst(tcx, a_subst));
+ let ty = *cached_ty
+ .get_or_insert_with(|| tcx.type_of(ty_def_id).subst(tcx, a_subst));
ty::VarianceDiagInfo::Invariant { ty, param_index: i.try_into().unwrap() }
} else {
ty::VarianceDiagInfo::default()
/// it.
pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>(
relation: &mut R,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
-) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("{}.super_relate_consts(a = {:?}, b = {:?})", relation.tag(), a, b);
let tcx = relation.tcx();
// FIXME(oli-obk): once const generics can have generic types, this assertion
// will likely get triggered. Move to `normalize_erasing_regions` at that point.
- let a_ty = tcx.erase_regions(a.ty);
- let b_ty = tcx.erase_regions(b.ty);
+ let a_ty = tcx.erase_regions(a.ty());
+ let b_ty = tcx.erase_regions(b.ty());
if a_ty != b_ty {
relation.tcx().sess.delay_span_bug(
DUMMY_SP,
);
}
- let eagerly_eval = |x: &'tcx ty::Const<'tcx>| x.eval(tcx, relation.param_env());
+ let eagerly_eval = |x: ty::Const<'tcx>| x.eval(tcx, relation.param_env());
let a = eagerly_eval(a);
let b = eagerly_eval(b);
// Currently, the values that can be unified are primitive types,
// and those that derive both `PartialEq` and `Eq`, corresponding
// to structural-match types.
- let is_match = match (a.val, b.val) {
+ let is_match = match (a.val(), b.val()) {
(ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {
// The caller should handle these cases!
bug!("var types encountered in super_relate_consts: {:?} {:?}", a, b)
au.substs,
bu.substs,
)?;
- return Ok(tcx.mk_const(ty::Const {
+ return Ok(tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Unevaluated(ty::Unevaluated {
def: au.def,
substs,
promoted: au.promoted,
}),
- ty: a.ty,
+ ty: a.ty(),
}));
}
_ => false,
a_val: ConstValue<'tcx>,
b_val: ConstValue<'tcx>,
// FIXME(oli-obk): these arguments should go away with valtrees
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
// FIXME(oli-obk): this should just be `bool` with valtrees
) -> RelateResult<'tcx, bool> {
let tcx = relation.tcx();
}
(ConstValue::ByRef { alloc: alloc_a, .. }, ConstValue::ByRef { alloc: alloc_b, .. })
- if a.ty.is_ref() || b.ty.is_ref() =>
+ if a.ty().is_ref() || b.ty().is_ref() =>
{
- if a.ty.is_ref() && b.ty.is_ref() {
+ if a.ty().is_ref() && b.ty().is_ref() {
alloc_a == alloc_b
} else {
false
// Both the variant and each field have to be equal.
if a_destructured.variant == b_destructured.variant {
for (a_field, b_field) in iter::zip(a_destructured.fields, b_destructured.fields) {
- relation.consts(a_field, b_field)?;
+ relation.consts(*a_field, *b_field)?;
}
true
}
}
-impl<'tcx> Relate<'tcx> for &'tcx ty::Const<'tcx> {
+impl<'tcx> Relate<'tcx> for ty::Const<'tcx> {
fn relate<R: TypeRelation<'tcx>>(
relation: &mut R,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
relation.consts(a, b)
}
}
use crate::ty::print::{with_no_trimmed_paths, FmtPrinter, Printer};
use crate::ty::{self, InferConst, Lift, Term, Ty, TyCtxt};
use rustc_data_structures::functor::IdFunctor;
+use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::CRATE_DEF_INDEX;
use rustc_index::vec::{Idx, IndexVec};
crate::ty::UniverseIndex,
crate::ty::Variance,
::rustc_span::Span,
+ ::rustc_errors::ErrorReported,
}
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// TypeFoldable implementations.
-//
-// Ideally, each type should invoke `folder.fold_foo(self)` and
-// nothing else. In some cases, though, we haven't gotten around to
-// adding methods on the `folder` yet, and thus the folding is
-// hard-coded here. This is less-flexible, because folders cannot
-// override the behavior, but there are a lot of random types and one
-// can easily refactor the folding into the TypeFolder trait as
-// needed.
/// AdtDefs are basically the same as a DefId.
impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::AdtDef {
}
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
- visitor.visit_ty(self)
+ visitor.visit_ty(*self)
}
}
self,
folder: &mut F,
) -> Result<Self, F::Error> {
- let new = self.inner.kind.try_fold_with(folder)?;
+ let new = self.kind().try_fold_with(folder)?;
Ok(folder.tcx().reuse_or_mk_predicate(self, new))
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
- self.inner.kind.visit_with(visitor)
+ self.kind().visit_with(visitor)
}
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
}
fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
- self.inner.outer_exclusive_binder > binder
+ self.outer_exclusive_binder() > binder
}
fn has_type_flags(&self, flags: ty::TypeFlags) -> bool {
- self.inner.flags.intersects(flags)
+ self.flags().intersects(flags)
}
}
}
}
-impl<'tcx> TypeFoldable<'tcx> for &'tcx ty::Const<'tcx> {
+impl<'tcx> TypeFoldable<'tcx> for ty::Const<'tcx> {
fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(
self,
folder: &mut F,
) -> Result<Self, F::Error> {
- let ty = self.ty.try_fold_with(folder)?;
- let val = self.val.try_fold_with(folder)?;
- if ty != self.ty || val != self.val {
- Ok(folder.tcx().mk_const(ty::Const { ty, val }))
+ let ty = self.ty().try_fold_with(folder)?;
+ let val = self.val().try_fold_with(folder)?;
+ if ty != self.ty() || val != self.val() {
+ Ok(folder.tcx().mk_const(ty::ConstS { ty, val }))
} else {
Ok(self)
}
}
fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
- self.ty.visit_with(visitor)?;
- self.val.visit_with(visitor)
+ self.ty().visit_with(visitor)?;
+ self.val().visit_with(visitor)
}
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
- visitor.visit_const(self)
+ visitor.visit_const(*self)
}
}
self.substs.visit_with(visitor)
}
}
+
+impl<'tcx> TypeFoldable<'tcx> for hir::Constness {
+ fn try_super_fold_with<F: FallibleTypeFolder<'tcx>>(self, _: &mut F) -> Result<Self, F::Error> {
+ Ok(self)
+ }
+
+ fn super_visit_with<V: TypeVisitor<'tcx>>(&self, _: &mut V) -> ControlFlow<V::BreakTy> {
+ ControlFlow::CONTINUE
+ }
+}
use crate::ty::subst::{GenericArg, InternalSubsts, Subst, SubstsRef};
use crate::ty::InferTy::{self, *};
use crate::ty::{self, AdtDef, DefIdTree, Discr, Term, Ty, TyCtxt, TypeFlags, TypeFoldable};
-use crate::ty::{DelaySpanBugEmitted, List, ParamEnv, TyS};
+use crate::ty::{DelaySpanBugEmitted, List, ParamEnv};
use polonius_engine::Atom;
use rustc_data_structures::captures::Captures;
+use rustc_data_structures::intern::Interned;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_index::vec::Idx;
use rustc_target::spec::abi;
use std::borrow::Cow;
use std::cmp::Ordering;
+use std::fmt;
use std::marker::PhantomData;
-use std::ops::Range;
+use std::ops::{Deref, Range};
use ty::util::IntTypeExt;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
}
}
-/// Defines the kinds of types.
+/// Defines the kinds of types used by the type system.
///
-/// N.B., if you change this, you'll probably want to change the corresponding
-/// AST structure in `rustc_ast/src/ast.rs` as well.
+/// Types written by the user start out as [hir::TyKind](rustc_hir::TyKind) and get
+/// converted to this representation using `AstConv::ast_ty_to_ty`.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, TyDecodable, Debug)]
#[derive(HashStable)]
#[rustc_diagnostic_item = "TyKind"]
/// Algebraic data types (ADT). For example: structures, enumerations and unions.
///
- /// InternalSubsts here, possibly against intuition, *may* contain `Param`s.
- /// That is, even after substitution it is possible that there are type
- /// variables. This happens when the `Adt` corresponds to an ADT
- /// definition and not a concrete use of it.
+ /// For example, the type `List<i32>` would be represented using the `AdtDef`
+ /// for `struct List<T>` and the substs `[i32]`.
+ ///
+ /// Note that generic parameters in fields only get lazily substituted
+ /// by using something like `adt_def.all_fields().map(|field| field.ty(tcx, substs))`.
Adt(&'tcx AdtDef, SubstsRef<'tcx>),
/// An unsized FFI type that is opaque to Rust. Written as `extern type T`.
/// The pointee of a string slice. Written as `str`.
Str,
- /// An array with the given length. Written as `[T; n]`.
- Array(Ty<'tcx>, &'tcx ty::Const<'tcx>),
+ /// An array with the given length. Written as `[T; N]`.
+ Array(Ty<'tcx>, ty::Const<'tcx>),
/// The pointee of an array slice. Written as `[T]`.
Slice(Ty<'tcx>),
Ref(Region<'tcx>, Ty<'tcx>, hir::Mutability),
/// The anonymous type of a function declaration/definition. Each
- /// function has a unique type, which is output (for a function
- /// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
+ /// function has a unique type.
///
- /// For example the type of `bar` here:
+ /// For the function `fn foo() -> i32 { 3 }` this type would be
+ /// shown to the user as `fn() -> i32 {foo}`.
///
+ /// For example the type of `bar` here:
/// ```rust
/// fn foo() -> i32 { 1 }
/// let bar = foo; // bar: fn() -> i32 {foo}
/// A pointer to a function. Written as `fn() -> i32`.
///
+ /// Note that both functions and closures start out as either
+ /// [FnDef] or [Closure] which can be then be coerced to this variant.
+ ///
/// For example the type of `bar` here:
///
/// ```rust
/// A trait object. Written as `dyn for<'b> Trait<'b, Assoc = u32> + Send + 'a`.
Dynamic(&'tcx List<Binder<'tcx, ExistentialPredicate<'tcx>>>, ty::Region<'tcx>),
- /// The anonymous type of a closure. Used to represent the type of
- /// `|a| a`.
+ /// The anonymous type of a closure. Used to represent the type of `|a| a`.
+ ///
+ /// Closure substs contain both the - potentially substituted - generic parameters
+ /// of its parent and some synthetic parameters. See the documentation for
+ /// [ClosureSubsts] for more details.
Closure(DefId, SubstsRef<'tcx>),
/// The anonymous type of a generator. Used to represent the type of
/// `|a| yield a`.
+ ///
+ /// For more info about generator substs, visit the documentation for
+ /// [GeneratorSubsts].
Generator(DefId, SubstsRef<'tcx>, hir::Movability),
/// A type representing the types stored inside a generator.
- /// This should only appear in GeneratorInteriors.
+ /// This should only appear as part of the [GeneratorSubsts].
+ ///
+ /// Note that the captured variables for generators are stored separately
+ /// using a tuple in the same way as for closures.
+ ///
+ /// Unlike upvars, the witness can reference lifetimes from
+ /// inside of the generator itself. To deal with them in
+ /// the type of the generator, we convert them to higher ranked
+ /// lifetimes bound by the witness itself.
+ ///
+ /// Looking at the following example, the witness for this generator
+ /// may end up as something like `for<'a> [Vec<i32>, &'a Vec<i32>]`:
+ ///
+ /// ```rust
+ /// |a| {
+ /// let x = &vec![3];
+ /// yield a;
+ /// yield x[0];
+ /// }
+ /// ```
GeneratorWitness(Binder<'tcx, &'tcx List<Ty<'tcx>>>),
/// The never type `!`.
Never,
/// A tuple type. For example, `(i32, bool)`.
- /// Use `TyS::tuple_fields` to iterate over the field types.
+ /// Use `Ty::tuple_fields` to iterate over the field types.
Tuple(SubstsRef<'tcx>),
/// The projection of an associated type. For example,
Projection(ProjectionTy<'tcx>),
/// Opaque (`impl Trait`) type found in a return type.
+ ///
/// The `DefId` comes either from
/// * the `impl Trait` ast::Ty node,
/// * or the `type Foo = impl Trait` declaration
- /// The substitutions are for the generics of the function in question.
- /// After typeck, the concrete type can be found in the `types` map.
+ ///
+ /// For RPIT the substitutions are for the generics of the function,
+ /// while for TAIT it is used for the generic parameters of the alias.
+ ///
+ /// During codegen, `tcx.type_of(def_id)` can be used to get the underlying type.
Opaque(DefId, SubstsRef<'tcx>),
/// A type parameter; for example, `T` in `fn f<T>(x: T) {}`.
Param(ParamTy),
- /// Bound type variable, used only when preparing a trait query.
+ /// Bound type variable, used to represent the `'a` in `for<'a> fn(&'a ())`.
+ ///
+ /// For canonical queries, we replace inference variables with bound variables,
+ /// so e.g. when checking whether `&'_ (): Trait<_>` holds, we canonicalize that to
+ /// `for<'a, T> &'a (): Trait<T>` and then convert the introduced bound variables
+ /// back to inference variables in a new inference context when inside of the query.
+ ///
+ /// See the `rustc-dev-guide` for more details about
+ /// [higher-ranked trait bounds][1] and [canonical queries][2].
+ ///
+ /// [1]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html
+ /// [2]: https://rustc-dev-guide.rust-lang.org/traits/canonical-queries.html
Bound(ty::DebruijnIndex, BoundTy),
- /// A placeholder type - universally quantified higher-ranked type.
+ /// A placeholder type, used during higher ranked subtyping to instantiate
+ /// bound variables.
Placeholder(ty::PlaceholderType),
/// A type variable used during type checking.
+ ///
+ /// Similar to placeholders, inference variables also live in a universe to
+ /// correctly deal with higher ranked types. Though unlike placeholders,
+ /// that universe is stored in the `InferCtxt` instead of directly
+ /// inside of the type.
Infer(InferTy),
/// A placeholder for a type which could not be computed; this is
/// in scope on the function that defined the closure,
/// - CK represents the *closure kind* (Fn vs FnMut vs FnOnce). This
/// is rather hackily encoded via a scalar type. See
-/// `TyS::to_opt_closure_kind` for details.
+/// `Ty::to_opt_closure_kind` for details.
/// - CS represents the *closure signature*, representing as a `fn()`
/// type. For example, `fn(u32, u32) -> u32` would mean that the closure
/// implements `CK<(u32, u32), Output = u32>`, where `CK` is the trait
}
}
-pub type Region<'tcx> = &'tcx RegionKind;
+/// Use this rather than `TyKind`, whenever possible.
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, HashStable)]
+#[cfg_attr(not(bootstrap), rustc_pass_by_value)]
+pub struct Region<'tcx>(pub Interned<'tcx, RegionKind>);
+
+impl<'tcx> Deref for Region<'tcx> {
+ type Target = RegionKind;
+
+ fn deref(&self) -> &RegionKind {
+ &self.0.0
+ }
+}
+
+impl<'tcx> fmt::Debug for Region<'tcx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{:?}", self.kind())
+ }
+}
/// Representation of regions. Note that the NLL checker uses a distinct
/// representation of regions. For this reason, it internally replaces all the
/// to index into internal NLL data structures. See `rustc_const_eval::borrow_check`
/// module for more information.
///
+/// Note: operations are on the wrapper `Region` type, which is interned,
+/// rather than this type.
+///
/// ## The Region lattice within a given function
///
/// In general, the region lattice looks like
}
/// Region utilities
-impl RegionKind {
+impl<'tcx> Region<'tcx> {
+ pub fn kind(self) -> RegionKind {
+ *self.0.0
+ }
+
/// Is this region named by the user?
- pub fn has_name(&self) -> bool {
+ pub fn has_name(self) -> bool {
match *self {
- RegionKind::ReEarlyBound(ebr) => ebr.has_name(),
- RegionKind::ReLateBound(_, br) => br.kind.is_named(),
- RegionKind::ReFree(fr) => fr.bound_region.is_named(),
- RegionKind::ReStatic => true,
- RegionKind::ReVar(..) => false,
- RegionKind::RePlaceholder(placeholder) => placeholder.name.is_named(),
- RegionKind::ReEmpty(_) => false,
- RegionKind::ReErased => false,
+ ty::ReEarlyBound(ebr) => ebr.has_name(),
+ ty::ReLateBound(_, br) => br.kind.is_named(),
+ ty::ReFree(fr) => fr.bound_region.is_named(),
+ ty::ReStatic => true,
+ ty::ReVar(..) => false,
+ ty::RePlaceholder(placeholder) => placeholder.name.is_named(),
+ ty::ReEmpty(_) => false,
+ ty::ReErased => false,
}
}
#[inline]
- pub fn is_late_bound(&self) -> bool {
+ pub fn is_static(self) -> bool {
+ matches!(*self, ty::ReStatic)
+ }
+
+ #[inline]
+ pub fn is_erased(self) -> bool {
+ matches!(*self, ty::ReErased)
+ }
+
+ #[inline]
+ pub fn is_late_bound(self) -> bool {
matches!(*self, ty::ReLateBound(..))
}
#[inline]
- pub fn is_placeholder(&self) -> bool {
+ pub fn is_placeholder(self) -> bool {
matches!(*self, ty::RePlaceholder(..))
}
#[inline]
- pub fn bound_at_or_above_binder(&self, index: ty::DebruijnIndex) -> bool {
+ pub fn is_empty(self) -> bool {
+ matches!(*self, ty::ReEmpty(..))
+ }
+
+ #[inline]
+ pub fn bound_at_or_above_binder(self, index: ty::DebruijnIndex) -> bool {
match *self {
ty::ReLateBound(debruijn, _) => debruijn >= index,
_ => false,
}
}
- pub fn type_flags(&self) -> TypeFlags {
+ pub fn type_flags(self) -> TypeFlags {
let mut flags = TypeFlags::empty();
match *self {
/// of the impl, and for all the other highlighted regions, it
/// would return the `DefId` of the function. In other cases (not shown), this
/// function might return the `DefId` of a closure.
- pub fn free_region_binding_scope(&self, tcx: TyCtxt<'_>) -> DefId {
- match self {
+ pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId {
+ match *self {
ty::ReEarlyBound(br) => tcx.parent(br.def_id).unwrap(),
ty::ReFree(fr) => fr.scope,
_ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self),
}
/// Type utilities
-impl<'tcx> TyS<'tcx> {
+impl<'tcx> Ty<'tcx> {
#[inline(always)]
- pub fn kind(&self) -> &TyKind<'tcx> {
- &self.kind
+ pub fn kind(self) -> &'tcx TyKind<'tcx> {
+ &self.0.0.kind
}
#[inline(always)]
- pub fn flags(&self) -> TypeFlags {
- self.flags
+ pub fn flags(self) -> TypeFlags {
+ self.0.0.flags
}
#[inline]
- pub fn is_unit(&self) -> bool {
+ pub fn is_unit(self) -> bool {
match self.kind() {
Tuple(ref tys) => tys.is_empty(),
_ => false,
}
#[inline]
- pub fn is_never(&self) -> bool {
+ pub fn is_never(self) -> bool {
matches!(self.kind(), Never)
}
#[inline]
- pub fn is_primitive(&self) -> bool {
+ pub fn is_primitive(self) -> bool {
self.kind().is_primitive()
}
#[inline]
- pub fn is_adt(&self) -> bool {
+ pub fn is_adt(self) -> bool {
matches!(self.kind(), Adt(..))
}
#[inline]
- pub fn is_ref(&self) -> bool {
+ pub fn is_ref(self) -> bool {
matches!(self.kind(), Ref(..))
}
#[inline]
- pub fn is_ty_var(&self) -> bool {
+ pub fn is_ty_var(self) -> bool {
matches!(self.kind(), Infer(TyVar(_)))
}
#[inline]
- pub fn ty_vid(&self) -> Option<ty::TyVid> {
+ pub fn ty_vid(self) -> Option<ty::TyVid> {
match self.kind() {
&Infer(TyVar(vid)) => Some(vid),
_ => None,
}
#[inline]
- pub fn is_ty_infer(&self) -> bool {
+ pub fn is_ty_infer(self) -> bool {
matches!(self.kind(), Infer(_))
}
#[inline]
- pub fn is_phantom_data(&self) -> bool {
+ pub fn is_phantom_data(self) -> bool {
if let Adt(def, _) = self.kind() { def.is_phantom_data() } else { false }
}
#[inline]
- pub fn is_bool(&self) -> bool {
+ pub fn is_bool(self) -> bool {
*self.kind() == Bool
}
/// Returns `true` if this type is a `str`.
#[inline]
- pub fn is_str(&self) -> bool {
+ pub fn is_str(self) -> bool {
*self.kind() == Str
}
#[inline]
- pub fn is_param(&self, index: u32) -> bool {
+ pub fn is_param(self, index: u32) -> bool {
match self.kind() {
ty::Param(ref data) => data.index == index,
_ => false,
}
#[inline]
- pub fn is_slice(&self) -> bool {
+ pub fn is_slice(self) -> bool {
match self.kind() {
RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => matches!(ty.kind(), Slice(_) | Str),
_ => false,
}
#[inline]
- pub fn is_array(&self) -> bool {
+ pub fn is_array(self) -> bool {
matches!(self.kind(), Array(..))
}
#[inline]
- pub fn is_simd(&self) -> bool {
+ pub fn is_simd(self) -> bool {
match self.kind() {
Adt(def, _) => def.repr.simd(),
_ => false,
}
}
- pub fn sequence_element_type(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ pub fn sequence_element_type(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self.kind() {
- Array(ty, _) | Slice(ty) => ty,
+ Array(ty, _) | Slice(ty) => *ty,
Str => tcx.types.u8,
_ => bug!("`sequence_element_type` called on non-sequence value: {}", self),
}
}
- pub fn simd_size_and_type(&self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
+ pub fn simd_size_and_type(self, tcx: TyCtxt<'tcx>) -> (u64, Ty<'tcx>) {
match self.kind() {
Adt(def, substs) => {
assert!(def.repr.simd(), "`simd_size_and_type` called on non-SIMD type");
// The way we evaluate the `N` in `[T; N]` here only works since we use
// `simd_size_and_type` post-monomorphization. It will probably start to ICE
// if we use it in generic code. See the `simd-array-trait` ui test.
- (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, f0_elem_ty)
+ (f0_len.eval_usize(tcx, ParamEnv::empty()) as u64, *f0_elem_ty)
}
// Otherwise, the fields of this Adt are the SIMD components (and we assume they
// all have the same type).
}
#[inline]
- pub fn is_region_ptr(&self) -> bool {
+ pub fn is_region_ptr(self) -> bool {
matches!(self.kind(), Ref(..))
}
#[inline]
- pub fn is_mutable_ptr(&self) -> bool {
+ pub fn is_mutable_ptr(self) -> bool {
matches!(
self.kind(),
RawPtr(TypeAndMut { mutbl: hir::Mutability::Mut, .. })
/// Get the mutability of the reference or `None` when not a reference
#[inline]
- pub fn ref_mutability(&self) -> Option<hir::Mutability> {
+ pub fn ref_mutability(self) -> Option<hir::Mutability> {
match self.kind() {
Ref(_, _, mutability) => Some(*mutability),
_ => None,
}
#[inline]
- pub fn is_unsafe_ptr(&self) -> bool {
+ pub fn is_unsafe_ptr(self) -> bool {
matches!(self.kind(), RawPtr(_))
}
/// Tests if this is any kind of primitive pointer type (reference, raw pointer, fn pointer).
#[inline]
- pub fn is_any_ptr(&self) -> bool {
+ pub fn is_any_ptr(self) -> bool {
self.is_region_ptr() || self.is_unsafe_ptr() || self.is_fn_ptr()
}
#[inline]
- pub fn is_box(&self) -> bool {
+ pub fn is_box(self) -> bool {
match self.kind() {
Adt(def, _) => def.is_box(),
_ => false,
}
/// Panics if called on any type other than `Box<T>`.
- pub fn boxed_ty(&self) -> Ty<'tcx> {
+ pub fn boxed_ty(self) -> Ty<'tcx> {
match self.kind() {
Adt(def, substs) if def.is_box() => substs.type_at(0),
_ => bug!("`boxed_ty` is called on non-box type {:?}", self),
/// (A RawPtr is scalar because it represents a non-managed pointer, so its
/// contents are abstract to rustc.)
#[inline]
- pub fn is_scalar(&self) -> bool {
+ pub fn is_scalar(self) -> bool {
matches!(
self.kind(),
Bool | Char
/// Returns `true` if this type is a floating point type.
#[inline]
- pub fn is_floating_point(&self) -> bool {
+ pub fn is_floating_point(self) -> bool {
matches!(self.kind(), Float(_) | Infer(FloatVar(_)))
}
#[inline]
- pub fn is_trait(&self) -> bool {
+ pub fn is_trait(self) -> bool {
matches!(self.kind(), Dynamic(..))
}
#[inline]
- pub fn is_enum(&self) -> bool {
+ pub fn is_enum(self) -> bool {
matches!(self.kind(), Adt(adt_def, _) if adt_def.is_enum())
}
#[inline]
- pub fn is_union(&self) -> bool {
+ pub fn is_union(self) -> bool {
matches!(self.kind(), Adt(adt_def, _) if adt_def.is_union())
}
#[inline]
- pub fn is_closure(&self) -> bool {
+ pub fn is_closure(self) -> bool {
matches!(self.kind(), Closure(..))
}
#[inline]
- pub fn is_generator(&self) -> bool {
+ pub fn is_generator(self) -> bool {
matches!(self.kind(), Generator(..))
}
#[inline]
- pub fn is_integral(&self) -> bool {
+ pub fn is_integral(self) -> bool {
matches!(self.kind(), Infer(IntVar(_)) | Int(_) | Uint(_))
}
#[inline]
- pub fn is_fresh_ty(&self) -> bool {
+ pub fn is_fresh_ty(self) -> bool {
matches!(self.kind(), Infer(FreshTy(_)))
}
#[inline]
- pub fn is_fresh(&self) -> bool {
+ pub fn is_fresh(self) -> bool {
matches!(self.kind(), Infer(FreshTy(_) | FreshIntTy(_) | FreshFloatTy(_)))
}
#[inline]
- pub fn is_char(&self) -> bool {
+ pub fn is_char(self) -> bool {
matches!(self.kind(), Char)
}
#[inline]
- pub fn is_numeric(&self) -> bool {
+ pub fn is_numeric(self) -> bool {
self.is_integral() || self.is_floating_point()
}
#[inline]
- pub fn is_signed(&self) -> bool {
+ pub fn is_signed(self) -> bool {
matches!(self.kind(), Int(_))
}
#[inline]
- pub fn is_ptr_sized_integral(&self) -> bool {
+ pub fn is_ptr_sized_integral(self) -> bool {
matches!(self.kind(), Int(ty::IntTy::Isize) | Uint(ty::UintTy::Usize))
}
#[inline]
- pub fn has_concrete_skeleton(&self) -> bool {
+ pub fn has_concrete_skeleton(self) -> bool {
!matches!(self.kind(), Param(_) | Infer(_) | Error(_))
}
///
/// The parameter `explicit` indicates if this is an *explicit* dereference.
/// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly.
- pub fn builtin_deref(&self, explicit: bool) -> Option<TypeAndMut<'tcx>> {
+ pub fn builtin_deref(self, explicit: bool) -> Option<TypeAndMut<'tcx>> {
match self.kind() {
Adt(def, _) if def.is_box() => {
Some(TypeAndMut { ty: self.boxed_ty(), mutbl: hir::Mutability::Not })
}
- Ref(_, ty, mutbl) => Some(TypeAndMut { ty, mutbl: *mutbl }),
+ Ref(_, ty, mutbl) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
RawPtr(mt) if explicit => Some(*mt),
_ => None,
}
}
/// Returns the type of `ty[i]`.
- pub fn builtin_index(&self) -> Option<Ty<'tcx>> {
+ pub fn builtin_index(self) -> Option<Ty<'tcx>> {
match self.kind() {
- Array(ty, _) | Slice(ty) => Some(ty),
+ Array(ty, _) | Slice(ty) => Some(*ty),
_ => None,
}
}
- pub fn fn_sig(&self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
+ pub fn fn_sig(self, tcx: TyCtxt<'tcx>) -> PolyFnSig<'tcx> {
match self.kind() {
FnDef(def_id, substs) => tcx.fn_sig(*def_id).subst(tcx, substs),
FnPtr(f) => *f,
}
#[inline]
- pub fn is_fn(&self) -> bool {
+ pub fn is_fn(self) -> bool {
matches!(self.kind(), FnDef(..) | FnPtr(_))
}
#[inline]
- pub fn is_fn_ptr(&self) -> bool {
+ pub fn is_fn_ptr(self) -> bool {
matches!(self.kind(), FnPtr(_))
}
#[inline]
- pub fn is_impl_trait(&self) -> bool {
+ pub fn is_impl_trait(self) -> bool {
matches!(self.kind(), Opaque(..))
}
#[inline]
- pub fn ty_adt_def(&self) -> Option<&'tcx AdtDef> {
+ pub fn ty_adt_def(self) -> Option<&'tcx AdtDef> {
match self.kind() {
Adt(adt, _) => Some(adt),
_ => None,
/// Iterates over tuple fields.
/// Panics when called on anything but a tuple.
- pub fn tuple_fields(&self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> {
+ pub fn tuple_fields(self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> {
match self.kind() {
Tuple(substs) => substs.iter().map(|field| field.expect_ty()),
_ => bug!("tuple_fields called on non-tuple"),
/// Get the `i`-th element of a tuple.
/// Panics when called on anything but a tuple.
- pub fn tuple_element_ty(&self, i: usize) -> Option<Ty<'tcx>> {
+ pub fn tuple_element_ty(self, i: usize) -> Option<Ty<'tcx>> {
match self.kind() {
Tuple(substs) => substs.iter().nth(i).map(|field| field.expect_ty()),
_ => bug!("tuple_fields called on non-tuple"),
//
// FIXME: This requires the optimized MIR in the case of generators.
#[inline]
- pub fn variant_range(&self, tcx: TyCtxt<'tcx>) -> Option<Range<VariantIdx>> {
+ pub fn variant_range(self, tcx: TyCtxt<'tcx>) -> Option<Range<VariantIdx>> {
match self.kind() {
TyKind::Adt(adt, _) => Some(adt.variant_range()),
TyKind::Generator(def_id, substs, _) => {
// FIXME: This requires the optimized MIR in the case of generators.
#[inline]
pub fn discriminant_for_variant(
- &self,
+ self,
tcx: TyCtxt<'tcx>,
variant_index: VariantIdx,
) -> Option<Discr<'tcx>> {
}
/// Returns the type of the discriminant of this type.
- pub fn discriminant_ty(&'tcx self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
+ pub fn discriminant_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
match self.kind() {
ty::Adt(adt, _) if adt.is_enum() => adt.repr.discr_type().to_ty(tcx),
ty::Generator(_, substs, _) => substs.as_generator().discr_ty(tcx),
/// Returns the type of metadata for (potentially fat) pointers to this type.
pub fn ptr_metadata_ty(
- &'tcx self,
+ self,
tcx: TyCtxt<'tcx>,
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
) -> Ty<'tcx> {
/// to represent the closure kind, because it has not yet been
/// inferred. Once upvar inference (in `rustc_typeck/src/check/upvar.rs`)
/// is complete, that type variable will be unified.
- pub fn to_opt_closure_kind(&self) -> Option<ty::ClosureKind> {
+ pub fn to_opt_closure_kind(self) -> Option<ty::ClosureKind> {
match self.kind() {
Int(int_ty) => match int_ty {
ty::IntTy::I8 => Some(ty::ClosureKind::Fn),
/// bound such as `[_]: Copy`. A function with such a bound obviously never
/// can be called, but that doesn't mean it shouldn't typecheck. This is why
/// this method doesn't return `Option<bool>`.
- pub fn is_trivially_sized(&self, tcx: TyCtxt<'tcx>) -> bool {
+ pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
match self.kind() {
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
| ty::Uint(_)
use crate::ty::sty::{ClosureSubsts, GeneratorSubsts, InlineConstSubsts};
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
+use rustc_data_structures::intern::Interned;
use rustc_hir::def_id::DefId;
use rustc_macros::HashStable;
use rustc_serialize::{self, Decodable, Encodable};
/// To reduce memory usage, a `GenericArg` is an interned pointer,
/// with the lowest 2 bits being reserved for a tag to
/// indicate the type (`Ty`, `Region`, or `Const`) it points to.
+///
+/// Note: the `PartialEq`, `Eq` and `Hash` derives are only valid because `Ty`,
+/// `Region` and `Const` are all interned.
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct GenericArg<'tcx> {
ptr: NonZeroUsize,
- marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, &'tcx ty::Const<'tcx>)>,
+ marker: PhantomData<(Ty<'tcx>, ty::Region<'tcx>, ty::Const<'tcx>)>,
}
const TAG_MASK: usize = 0b11;
pub enum GenericArgKind<'tcx> {
Lifetime(ty::Region<'tcx>),
Type(Ty<'tcx>),
- Const(&'tcx ty::Const<'tcx>),
+ Const(ty::Const<'tcx>),
}
impl<'tcx> GenericArgKind<'tcx> {
+ #[inline]
fn pack(self) -> GenericArg<'tcx> {
let (tag, ptr) = match self {
GenericArgKind::Lifetime(lt) => {
// Ensure we can use the tag bits.
- assert_eq!(mem::align_of_val(lt) & TAG_MASK, 0);
- (REGION_TAG, lt as *const _ as usize)
+ assert_eq!(mem::align_of_val(lt.0.0) & TAG_MASK, 0);
+ (REGION_TAG, lt.0.0 as *const ty::RegionKind as usize)
}
GenericArgKind::Type(ty) => {
// Ensure we can use the tag bits.
- assert_eq!(mem::align_of_val(ty) & TAG_MASK, 0);
- (TYPE_TAG, ty as *const _ as usize)
+ assert_eq!(mem::align_of_val(ty.0.0) & TAG_MASK, 0);
+ (TYPE_TAG, ty.0.0 as *const ty::TyS<'tcx> as usize)
}
GenericArgKind::Const(ct) => {
// Ensure we can use the tag bits.
- assert_eq!(mem::align_of_val(ct) & TAG_MASK, 0);
- (CONST_TAG, ct as *const _ as usize)
+ assert_eq!(mem::align_of_val(ct.0.0) & TAG_MASK, 0);
+ (CONST_TAG, ct.0.0 as *const ty::ConstS<'tcx> as usize)
}
};
}
impl<'tcx> From<ty::Region<'tcx>> for GenericArg<'tcx> {
+ #[inline]
fn from(r: ty::Region<'tcx>) -> GenericArg<'tcx> {
GenericArgKind::Lifetime(r).pack()
}
}
impl<'tcx> From<Ty<'tcx>> for GenericArg<'tcx> {
+ #[inline]
fn from(ty: Ty<'tcx>) -> GenericArg<'tcx> {
GenericArgKind::Type(ty).pack()
}
}
-impl<'tcx> From<&'tcx ty::Const<'tcx>> for GenericArg<'tcx> {
- fn from(c: &'tcx ty::Const<'tcx>) -> GenericArg<'tcx> {
+impl<'tcx> From<ty::Const<'tcx>> for GenericArg<'tcx> {
+ #[inline]
+ fn from(c: ty::Const<'tcx>) -> GenericArg<'tcx> {
GenericArgKind::Const(c).pack()
}
}
#[inline]
pub fn unpack(self) -> GenericArgKind<'tcx> {
let ptr = self.ptr.get();
+ // SAFETY: use of `Interned::new_unchecked` here is ok because these
+ // pointers were originally created from `Interned` types in `pack()`,
+ // and this is just going in the other direction.
unsafe {
match ptr & TAG_MASK {
- REGION_TAG => GenericArgKind::Lifetime(&*((ptr & !TAG_MASK) as *const _)),
- TYPE_TAG => GenericArgKind::Type(&*((ptr & !TAG_MASK) as *const _)),
- CONST_TAG => GenericArgKind::Const(&*((ptr & !TAG_MASK) as *const _)),
+ REGION_TAG => GenericArgKind::Lifetime(ty::Region(Interned::new_unchecked(
+ &*((ptr & !TAG_MASK) as *const ty::RegionKind),
+ ))),
+ TYPE_TAG => GenericArgKind::Type(Ty(Interned::new_unchecked(
+ &*((ptr & !TAG_MASK) as *const ty::TyS<'tcx>),
+ ))),
+ CONST_TAG => GenericArgKind::Const(ty::Const(Interned::new_unchecked(
+ &*((ptr & !TAG_MASK) as *const ty::ConstS<'tcx>),
+ ))),
_ => intrinsics::unreachable(),
}
}
}
/// Unpack the `GenericArg` as a const when it is known certainly to be a const.
- pub fn expect_const(self) -> &'tcx ty::Const<'tcx> {
+ pub fn expect_const(self) -> ty::Const<'tcx> {
match self.unpack() {
GenericArgKind::Const(c) => c,
_ => bug!("expected a const, but found another kind"),
}
#[inline]
- pub fn consts(&'a self) -> impl DoubleEndedIterator<Item = &'tcx ty::Const<'tcx>> + 'a {
+ pub fn consts(&'a self) -> impl DoubleEndedIterator<Item = ty::Const<'tcx>> + 'a {
self.iter().filter_map(|k| {
if let GenericArgKind::Const(ct) = k.unpack() { Some(ct) } else { None }
})
}
#[inline]
- pub fn const_at(&self, i: usize) -> &'tcx ty::Const<'tcx> {
+ pub fn const_at(&self, i: usize) -> ty::Const<'tcx> {
if let GenericArgKind::Const(ct) = self[i].unpack() {
ct
} else {
}
}
0 => Ok(self),
- _ => {
- let params: SmallVec<[_; 8]> =
- self.iter().map(|k| k.try_fold_with(folder)).collect::<Result<_, _>>()?;
- if params[..] == self[..] {
- Ok(self)
- } else {
- Ok(folder.tcx().intern_substs(¶ms))
- }
- }
+ _ => ty::util::fold_list(self, folder, |tcx, v| tcx.intern_substs(v)),
}
}
}
}
- fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- if let ty::ConstKind::Param(p) = c.val {
+ fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ if let ty::ConstKind::Param(p) = c.val() {
self.const_for_param(p, c)
} else {
c.super_fold_with(self)
self.shift_vars_through_binders(ty)
}
- fn const_for_param(
- &self,
- p: ParamConst,
- source_ct: &'tcx ty::Const<'tcx>,
- ) -> &'tcx ty::Const<'tcx> {
+ fn const_for_param(&self, p: ParamConst, source_ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
// Look up the const in the substitutions. It really should be in there.
let opt_ct = self.substs.get(p.index as usize).map(|k| k.unpack());
let ct = match opt_ct {
use crate::traits::specialization_graph;
-use crate::ty::fast_reject::{self, SimplifiedType, SimplifyParams, StripReferences};
+use crate::ty::fast_reject::{self, SimplifiedType, SimplifyParams};
use crate::ty::fold::TypeFoldable;
use crate::ty::{Ident, Ty, TyCtxt};
use rustc_hir as hir;
self_ty: Ty<'tcx>,
) -> impl Iterator<Item = DefId> + 'tcx {
let impls = self.trait_impls_of(def_id);
- if let Some(simp) =
- fast_reject::simplify_type(self, self_ty, SimplifyParams::No, StripReferences::No)
- {
+ if let Some(simp) = fast_reject::simplify_type(self, self_ty, SimplifyParams::No) {
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
return impls.iter().copied();
}
// whose outer level is not a parameter or projection. Especially for things like
// `T: Clone` this is incredibly useful as we would otherwise look at all the impls
// of `Clone` for `Option<T>`, `Vec<T>`, `ConcreteType` and so on.
- if let Some(simp) =
- fast_reject::simplify_type(self, self_ty, SimplifyParams::Yes, StripReferences::No)
- {
+ if let Some(simp) = fast_reject::simplify_type(self, self_ty, SimplifyParams::Yes) {
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
for &impl_def_id in impls {
if let result @ Some(_) = f(impl_def_id) {
}
if let Some(simplified_self_ty) =
- fast_reject::simplify_type(tcx, impl_self_ty, SimplifyParams::No, StripReferences::No)
+ fast_reject::simplify_type(tcx, impl_self_ty, SimplifyParams::No)
{
impls.non_blanket_impls.entry(simplified_self_ty).or_default().push(impl_def_id);
} else {
use crate::ty::layout::IntegerExt;
use crate::ty::query::TyCtxtAt;
use crate::ty::subst::{GenericArgKind, Subst, SubstsRef};
-use crate::ty::TyKind::*;
-use crate::ty::{self, DebruijnIndex, DefIdTree, List, Ty, TyCtxt, TypeFoldable};
+use crate::ty::{
+ self, Const, DebruijnIndex, DefIdTree, List, ReEarlyBound, Region, Ty, TyCtxt, TyKind::*,
+ TypeFoldable,
+};
use rustc_apfloat::Float as _;
use rustc_ast as ast;
use rustc_attr::{self as attr, SignedInt, UnsignedInt};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::ErrorReported;
use rustc_hir as hir;
let result = iter::zip(item_substs, impl_substs)
.filter(|&(_, k)| {
match k.unpack() {
- GenericArgKind::Lifetime(&ty::RegionKind::ReEarlyBound(ref ebr)) => {
+ GenericArgKind::Lifetime(Region(Interned(ReEarlyBound(ref ebr), _))) => {
!impl_generics.region_param(ebr, self).pure_wrt_drop
}
- GenericArgKind::Type(&ty::TyS { kind: ty::Param(ref pt), .. }) => {
- !impl_generics.type_param(pt, self).pure_wrt_drop
- }
- GenericArgKind::Const(&ty::Const {
- val: ty::ConstKind::Param(ref pc), ..
- }) => !impl_generics.const_param(pc, self).pure_wrt_drop,
+ GenericArgKind::Type(Ty(Interned(
+ ty::TyS { kind: ty::Param(ref pt), .. },
+ _,
+ ))) => !impl_generics.type_param(pt, self).pure_wrt_drop,
+ GenericArgKind::Const(Const(Interned(
+ ty::ConstS { val: ty::ConstKind::Param(ref pc), .. },
+ _,
+ ))) => !impl_generics.const_param(pc, self).pure_wrt_drop,
GenericArgKind::Lifetime(_)
| GenericArgKind::Type(_)
| GenericArgKind::Const(_) => {
let substs = substs.fold_with(self);
if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) {
- Some(expanded_ty) => expanded_ty,
+ Some(expanded_ty) => *expanded_ty,
None => {
let generic_ty = self.tcx.type_of(def_id);
let concrete_ty = generic_ty.subst(self.tcx, substs);
}
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
- if let ty::Opaque(def_id, substs) = t.kind {
+ if let ty::Opaque(def_id, substs) = *t.kind() {
self.expand_opaque_ty(def_id, substs).unwrap_or(t)
} else if t.has_opaque_types() {
t.super_fold_with(self)
}
}
-impl<'tcx> ty::TyS<'tcx> {
+impl<'tcx> Ty<'tcx> {
/// Returns the maximum value for the given numeric type (including `char`s)
/// or returns `None` if the type is not numeric.
- pub fn numeric_max_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
+ pub fn numeric_max_val(self, tcx: TyCtxt<'tcx>) -> Option<Const<'tcx>> {
let val = match self.kind() {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = int_size_and_signed(tcx, self);
}),
_ => None,
};
- val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
+ val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
}
/// Returns the minimum value for the given numeric type (including `char`s)
/// or returns `None` if the type is not numeric.
- pub fn numeric_min_val(&'tcx self, tcx: TyCtxt<'tcx>) -> Option<&'tcx ty::Const<'tcx>> {
+ pub fn numeric_min_val(self, tcx: TyCtxt<'tcx>) -> Option<Const<'tcx>> {
let val = match self.kind() {
ty::Int(_) | ty::Uint(_) => {
let (size, signed) = int_size_and_signed(tcx, self);
}),
_ => None,
};
- val.map(|v| ty::Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
+ val.map(|v| Const::from_bits(tcx, v, ty::ParamEnv::empty().and(self)))
}
/// Checks whether values of this type `T` are *moved* or *copied*
/// full requirements for the `Copy` trait (cc #29149) -- this
/// winds up being reported as an error during NLL borrow check.
pub fn is_copy_modulo_regions(
- &'tcx self,
+ self,
tcx_at: TyCtxtAt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
/// over-approximation in generic contexts, where one can have
/// strange rules like `<T as Foo<'static>>::Bar: Sized` that
/// actually carry lifetime requirements.
- pub fn is_sized(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+ pub fn is_sized(self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
self.is_trivially_sized(tcx_at.tcx) || tcx_at.is_sized_raw(param_env.and(self))
}
/// optimization as well as the rules around static values. Note
/// that the `Freeze` trait is not exposed to end users and is
/// effectively an implementation detail.
- pub fn is_freeze(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+ pub fn is_freeze(self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
self.is_trivially_freeze() || tcx_at.is_freeze_raw(param_env.and(self))
}
///
/// Returning true means the type is known to be `Freeze`. Returning
/// `false` means nothing -- could be `Freeze`, might not be.
- fn is_trivially_freeze(&self) -> bool {
+ fn is_trivially_freeze(self) -> bool {
match self.kind() {
ty::Int(_)
| ty::Uint(_)
| ty::FnDef(..)
| ty::Error(_)
| ty::FnPtr(_) => true,
- ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_freeze),
+ ty::Tuple(_) => self.tuple_fields().all(|f| Self::is_trivially_freeze(f)),
ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_freeze(),
ty::Adt(..)
| ty::Bound(..)
}
/// Checks whether values of this type `T` implement the `Unpin` trait.
- pub fn is_unpin(&'tcx self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+ pub fn is_unpin(self, tcx_at: TyCtxtAt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
self.is_trivially_unpin() || tcx_at.is_unpin_raw(param_env.and(self))
}
///
/// Returning true means the type is known to be `Unpin`. Returning
/// `false` means nothing -- could be `Unpin`, might not be.
- fn is_trivially_unpin(&self) -> bool {
+ fn is_trivially_unpin(self) -> bool {
match self.kind() {
ty::Int(_)
| ty::Uint(_)
| ty::FnDef(..)
| ty::Error(_)
| ty::FnPtr(_) => true,
- ty::Tuple(_) => self.tuple_fields().all(Self::is_trivially_unpin),
+ ty::Tuple(_) => self.tuple_fields().all(|f| Self::is_trivially_unpin(f)),
ty::Slice(elem_ty) | ty::Array(elem_ty, _) => elem_ty.is_trivially_unpin(),
ty::Adt(..)
| ty::Bound(..)
///
/// Note that this method is used to check eligible types in unions.
#[inline]
- pub fn needs_drop(&'tcx self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
+ pub fn needs_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
// Avoid querying in simple cases.
match needs_drop_components(self, &tcx.data_layout) {
Err(AlwaysRequiresDrop) => true,
/// Note that this method is used to check for change in drop order for
/// 2229 drop reorder migration analysis.
#[inline]
- pub fn has_significant_drop(
- &'tcx self,
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> bool {
+ pub fn has_significant_drop(self, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> bool {
// Avoid querying in simple cases.
match needs_drop_components(self, &tcx.data_layout) {
Err(AlwaysRequiresDrop) => true,
/// want to know whether a given call to `PartialEq::eq` will proceed structurally all the way
/// down, you will need to use a type visitor.
#[inline]
- pub fn is_structural_eq_shallow(&'tcx self, tcx: TyCtxt<'tcx>) -> bool {
+ pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool {
match self.kind() {
// Look for an impl of both `PartialStructuralEq` and `StructuralEq`.
Adt(..) => tcx.has_structural_eq_impls(self),
/// - `&'a mut u8` -> `u8`
/// - `&'a &'b u8` -> `u8`
/// - `&'a *const &'b u8 -> *const &'b u8`
- pub fn peel_refs(&'tcx self) -> Ty<'tcx> {
+ pub fn peel_refs(self) -> Ty<'tcx> {
let mut ty = self;
while let Ref(_, inner_ty, _) = ty.kind() {
- ty = inner_ty;
+ ty = *inner_ty;
}
ty
}
- pub fn outer_exclusive_binder(&'tcx self) -> DebruijnIndex {
- self.outer_exclusive_binder
+ pub fn outer_exclusive_binder(self) -> DebruijnIndex {
+ self.0.outer_exclusive_binder
}
}
ty::Dynamic(..) | ty::Error(_) => Err(AlwaysRequiresDrop),
- ty::Slice(ty) => needs_drop_components(ty, target_layout),
+ ty::Slice(ty) => needs_drop_components(*ty, target_layout),
ty::Array(elem_ty, size) => {
- match needs_drop_components(elem_ty, target_layout) {
+ match needs_drop_components(*elem_ty, target_layout) {
Ok(v) if v.is_empty() => Ok(v),
- res => match size.val.try_to_bits(target_layout.pointer_size) {
+ res => match size.val().try_to_bits(target_layout.pointer_size) {
// Arrays of size zero don't need drop, even if their element
// type does.
Some(0) => Ok(SmallVec::new()),
//! An iterator over the type substructure.
//! WARNING: this does not keep track of the region depth.
-use crate::ty;
use crate::ty::subst::{GenericArg, GenericArgKind};
+use crate::ty::{self, Ty};
use rustc_data_structures::sso::SsoHashSet;
use smallvec::{self, SmallVec};
}
}
-impl<'tcx> super::TyS<'tcx> {
+impl<'tcx> Ty<'tcx> {
/// Iterator that walks `self` and any types reachable from
/// `self`, in depth-first order. Note that just walks the types
/// that appear in `self`, it does not descend into the fields of
/// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize }
/// [isize] => { [isize], isize }
/// ```
- pub fn walk(&'tcx self) -> TypeWalker<'tcx> {
+ pub fn walk(self) -> TypeWalker<'tcx> {
TypeWalker::new(self.into())
}
}
},
GenericArgKind::Lifetime(_) => {}
GenericArgKind::Const(parent_ct) => {
- stack.push(parent_ct.ty.into());
- match parent_ct.val {
+ stack.push(parent_ct.ty().into());
+ match parent_ct.val() {
ty::ConstKind::Infer(_)
| ty::ConstKind::Param(_)
| ty::ConstKind::Placeholder(_)
//! See docs in build/expr/mod.rs
use crate::build::Builder;
+use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::CanonicalUserTypeAnnotation;
inferred_ty: ty,
})
});
- assert_eq!(literal.ty, ty);
+ assert_eq!(literal.ty(), ty);
Constant { span, user_ty, literal: literal.into() }
}
- ExprKind::StaticRef { literal, .. } => {
- Constant { span, user_ty: None, literal: literal.into() }
+ ExprKind::StaticRef { alloc_id, ty, .. } => {
+ let const_val =
+ ConstValue::Scalar(Scalar::from_pointer(alloc_id.into(), &this.tcx));
+ let literal = ConstantKind::Val(const_val, ty);
+
+ Constant { span, user_ty: None, literal }
}
ExprKind::ConstBlock { value } => {
Constant { span: span, user_ty: None, literal: value.into() }
let place_builder = place_builder.clone();
this.consume_by_copy_or_move(
place_builder
- .field(n, ty)
+ .field(n, *ty)
.into_place(this.tcx, this.typeck_results),
)
}
// The set of places that we are creating fake borrows of. If there are
// no match guards then we don't need any fake borrows, so don't track
// them.
- let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None };
+ let mut fake_borrows = match_has_guard.then(FxHashSet::default);
let mut otherwise = None;
///
/// For `bool` we always generate two edges, one for `true` and one for
/// `false`.
- options: FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
+ options: FxIndexMap<ty::Const<'tcx>, u128>,
},
/// Test for equality with value, possibly after an unsizing coercion to
/// `ty`,
Eq {
- value: &'tcx ty::Const<'tcx>,
+ value: ty::Const<'tcx>,
// Integer types are handled by `SwitchInt`, and constants with ADT
// types are converted back into patterns, so this can only be `&str`,
// `&[T]`, `f32` or `f64`.
}
PatKind::Range(PatRange { lo, hi, end }) => {
- let (range, bias) = match *lo.ty.kind() {
+ let (range, bias) = match *lo.ty().kind() {
ty::Char => {
(Some(('\u{0000}' as u128, '\u{10FFFF}' as u128, Size::from_bits(32))), 0)
}
_ => (None, 0),
};
if let Some((min, max, sz)) = range {
- if let (Some(lo), Some(hi)) = (lo.val.try_to_bits(sz), hi.val.try_to_bits(sz)) {
+ if let (Some(lo), Some(hi)) =
+ (lo.val().try_to_bits(sz), hi.val().try_to_bits(sz))
+ {
// We want to compare ranges numerically, but the order of the bitwise
// representation of signed integers does not match their numeric order.
// Thus, to correct the ordering, we need to shift the range of signed
},
PatKind::Range(range) => {
- assert_eq!(range.lo.ty, match_pair.pattern.ty);
- assert_eq!(range.hi.ty, match_pair.pattern.ty);
+ assert_eq!(range.lo.ty(), match_pair.pattern.ty);
+ assert_eq!(range.hi.ty(), match_pair.pattern.ty);
Test { span: match_pair.pattern.span, kind: TestKind::Range(range) }
}
test_place: &PlaceBuilder<'tcx>,
candidate: &Candidate<'pat, 'tcx>,
switch_ty: Ty<'tcx>,
- options: &mut FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
+ options: &mut FxIndexMap<ty::Const<'tcx>, u128>,
) -> bool {
let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place) else {
return false;
ty,
);
} else if let [success, fail] = *make_target_blocks(self) {
- assert_eq!(value.ty, ty);
+ assert_eq!(value.ty(), ty);
let expect = self.literal_operand(test.span, value);
let val = Operand::Copy(place);
self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
}
}
- TestKind::Range(PatRange { ref lo, ref hi, ref end }) => {
+ TestKind::Range(PatRange { lo, hi, ref end }) => {
let lower_bound_success = self.cfg.start_new_block();
let target_blocks = make_target_blocks(self);
block: BasicBlock,
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
source_info: SourceInfo,
- value: &'tcx ty::Const<'tcx>,
+ value: ty::Const<'tcx>,
place: Place<'tcx>,
mut ty: Ty<'tcx>,
) {
_ => None,
};
let opt_ref_ty = unsize(ty);
- let opt_ref_test_ty = unsize(value.ty);
+ let opt_ref_test_ty = unsize(value.ty());
match (opt_ref_ty, opt_ref_test_ty) {
// nothing to do, neither is an array
(None, None) => {}
(Some((region, elem_ty, _)), _) | (None, Some((region, elem_ty, _))) => {
let tcx = self.tcx;
// make both a slice
- ty = tcx.mk_imm_ref(region, tcx.mk_slice(elem_ty));
+ ty = tcx.mk_imm_ref(*region, tcx.mk_slice(*elem_ty));
if opt_ref_ty.is_some() {
let temp = self.temp(ty, source_info.span);
self.cfg.push_assign(
let tcx = self.tcx;
- let test_ty = test.lo.ty;
+ let test_ty = test.lo.ty();
let lo = compare_const_vals(tcx, test.lo, pat.hi, self.param_env, test_ty)?;
let hi = compare_const_vals(tcx, test.hi, pat.lo, self.param_env, test_ty)?;
span_bug!(match_pair.pattern.span, "simplifyable pattern found: {:?}", match_pair.pattern)
}
- fn const_range_contains(
- &self,
- range: PatRange<'tcx>,
- value: &'tcx ty::Const<'tcx>,
- ) -> Option<bool> {
+ fn const_range_contains(&self, range: PatRange<'tcx>, value: ty::Const<'tcx>) -> Option<bool> {
use std::cmp::Ordering::*;
let tcx = self.tcx;
- let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty)?;
- let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty)?;
+ let a = compare_const_vals(tcx, range.lo, value, self.param_env, range.lo.ty())?;
+ let b = compare_const_vals(tcx, value, range.hi, self.param_env, range.lo.ty())?;
match (b, range.end) {
(Less, _) | (Equal, RangeEnd::Included) if a != Greater => Some(true),
fn values_not_contained_in_range(
&self,
range: PatRange<'tcx>,
- options: &FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
+ options: &FxIndexMap<ty::Const<'tcx>, u128>,
) -> Option<bool> {
for &val in options.keys() {
if self.const_range_contains(range, val)? {
method_name: Symbol,
self_ty: Ty<'tcx>,
params: &[GenericArg<'tcx>],
-) -> &'tcx ty::Const<'tcx> {
+) -> ty::Const<'tcx> {
let substs = tcx.mk_substs_trait(self_ty, params);
// The unhygienic comparison here is acceptable because this is only
/// Convenience function for creating a literal operand, one
/// without any user type annotation.
- crate fn literal_operand(
- &mut self,
- span: Span,
- literal: &'tcx ty::Const<'tcx>,
- ) -> Operand<'tcx> {
+ crate fn literal_operand(&mut self, span: Span, literal: ty::Const<'tcx>) -> Operand<'tcx> {
let literal = literal.into();
let constant = Box::new(Constant { span, user_ty: None, literal });
Operand::Constant(constant)
let span_with_body = span_with_body.unwrap_or_else(|| tcx.hir().span(id));
tcx.infer_ctxt().enter(|infcx| {
- let body = if let Some(ErrorReported) = typeck_results.tainted_by_errors {
- build::construct_error(&infcx, def, id, body_id, body_owner_kind)
+ let body = if let Some(error_reported) = typeck_results.tainted_by_errors {
+ build::construct_error(&infcx, def, id, body_id, body_owner_kind, error_reported)
} else if body_owner_kind.is_fn_or_closure() {
// fetch the fully liberated fn signature (that is, all bound
// types/lifetimes replaced)
hir_id: hir::HirId,
body_id: hir::BodyId,
body_owner_kind: hir::BodyOwnerKind,
+ err: ErrorReported,
) -> Body<'tcx> {
let tcx = infcx.tcx;
let span = tcx.hir().span(hir_id);
vec![],
span,
generator_kind,
+ Some(err),
);
body.generator.as_mut().map(|gen| gen.yield_ty = Some(ty));
body
self.var_debug_info,
self.fn_span,
self.generator_kind,
+ self.typeck_results.tainted_by_errors,
)
}
let mut closure_ty = self.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
if let ty::Ref(_, ty, _) = closure_ty.kind() {
closure_env_projs.push(ProjectionElem::Deref);
- closure_ty = ty;
+ closure_ty = *ty;
}
let upvar_substs = match closure_ty.kind() {
ty::Closure(_, substs) => ty::UpvarSubsts::Closure(substs),
};
self.var_debug_info.push(VarDebugInfo {
- name: sym,
+ name: *sym,
source_info: SourceInfo::outermost(tcx_hir.span(var_id)),
value: VarDebugInfoContents::Place(Place {
local: ty::CAPTURE_STRUCT_LOCAL,
crate fn lit_to_const<'tcx>(
tcx: TyCtxt<'tcx>,
lit_input: LitToConstInput<'tcx>,
-) -> Result<&'tcx ty::Const<'tcx>, LitToConstError> {
+) -> Result<ty::Const<'tcx>, LitToConstError> {
let LitToConstInput { lit, ty, neg } = lit_input;
let trunc = |n| {
use rustc_middle::hir::place::PlaceBase as HirPlaceBase;
use rustc_middle::hir::place::ProjectionKind as HirProjectionKind;
use rustc_middle::middle::region;
-use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::{BinOp, BorrowKind, Field, UnOp};
use rustc_middle::thir::*;
use rustc_middle::ty::adjustment::{
_ => span_bug!(expr.span, "unexpected repeat expr ty: {:?}", ty),
};
- ExprKind::Repeat { value: self.mirror_expr(v), count }
+ ExprKind::Repeat { value: self.mirror_expr(v), count: *count }
}
hir::ExprKind::Ret(ref v) => {
ExprKind::Return { value: v.as_ref().map(|v| self.mirror_expr(v)) }
// in case we are offsetting from a computed discriminant
// and not the beginning of discriminants (which is always `0`)
let substs = InternalSubsts::identity_for_item(self.tcx(), did);
- let lhs = ty::Const {
+ let lhs = ty::ConstS {
val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(
ty::WithOptConstParam::unknown(did),
substs,
let name = self.tcx.hir().name(hir_id);
let val = ty::ConstKind::Param(ty::ParamConst::new(index, name));
ExprKind::Literal {
- literal: self.tcx.mk_const(ty::Const {
+ literal: self.tcx.mk_const(ty::ConstS {
val,
ty: self.typeck_results().node_type(expr.hir_id),
}),
let user_ty = self.user_substs_applied_to_res(expr.hir_id, res);
debug!("convert_path_expr: (const) user_ty={:?}", user_ty);
ExprKind::Literal {
- literal: self.tcx.mk_const(ty::Const {
+ literal: self.tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Unevaluated(ty::Unevaluated::new(
ty::WithOptConstParam::unknown(def_id),
substs,
let kind = if self.tcx.is_thread_local_static(id) {
ExprKind::ThreadLocalRef(id)
} else {
- let ptr = self.tcx.create_static_alloc(id);
- ExprKind::StaticRef {
- literal: ty::Const::from_scalar(
- self.tcx,
- Scalar::from_pointer(ptr.into(), &self.tcx),
- ty,
- ),
- def_id: id,
- }
+ let alloc_id = self.tcx.create_static_alloc(id);
+ ExprKind::StaticRef { alloc_id, ty, def_id: id }
};
ExprKind::Deref {
arg: self.thir.exprs.push(Expr { ty, temp_lifetime, span: expr.span, kind }),
ty: Ty<'tcx>,
sp: Span,
neg: bool,
- ) -> &'tcx ty::Const<'tcx> {
+ ) -> ty::Const<'tcx> {
trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg);
match self.tcx.at(sp).lit_to_const(LitToConstInput { lit, ty, neg }) {
}
}
if let ty::Ref(_, sub_ty, _) = scrut_ty.kind() {
- if cx.tcx.is_ty_uninhabited_from(cx.module, sub_ty, cx.param_env) {
+ if cx.tcx.is_ty_uninhabited_from(cx.module, *sub_ty, cx.param_env) {
err.note("references are always considered inhabited");
}
}
#[instrument(level = "debug", skip(self))]
pub(super) fn const_to_pat(
&self,
- cv: &'tcx ty::Const<'tcx>,
+ cv: ty::Const<'tcx>,
id: hir::HirId,
span: Span,
mir_structural_match_violation: bool,
ty.is_structural_eq_shallow(self.infcx.tcx)
}
- fn to_pat(
- &mut self,
- cv: &'tcx ty::Const<'tcx>,
- mir_structural_match_violation: bool,
- ) -> Pat<'tcx> {
+ fn to_pat(&mut self, cv: ty::Const<'tcx>, mir_structural_match_violation: bool) -> Pat<'tcx> {
trace!(self.treat_byte_string_as_slice);
// This method is just a wrapper handling a validity check; the heavy lifting is
// performed by the recursive `recur` method, which is not meant to be
// If we were able to successfully convert the const to some pat,
// double-check that all types in the const implement `Structural`.
- let structural = self.search_for_structural_match_violation(cv.ty);
+ let structural = self.search_for_structural_match_violation(cv.ty());
debug!(
"search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
- cv.ty, structural
+ cv.ty(),
+ structural
);
// This can occur because const qualification treats all associated constants as
}
if let Some(msg) = structural {
- if !self.type_may_have_partial_eq_impl(cv.ty) {
+ if !self.type_may_have_partial_eq_impl(cv.ty()) {
// span_fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx().sess.span_fatal(self.span, &msg);
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
fn field_pats(
&self,
- vals: impl Iterator<Item = &'tcx ty::Const<'tcx>>,
+ vals: impl Iterator<Item = ty::Const<'tcx>>,
) -> Result<Vec<FieldPat<'tcx>>, FallbackToConstRef> {
vals.enumerate()
.map(|(idx, val)| {
// Recursive helper for `to_pat`; invoke that (instead of calling this directly).
fn recur(
&self,
- cv: &'tcx ty::Const<'tcx>,
+ cv: ty::Const<'tcx>,
mir_structural_match_violation: bool,
) -> Result<Pat<'tcx>, FallbackToConstRef> {
let id = self.id;
let tcx = self.tcx();
let param_env = self.param_env;
- let kind = match cv.ty.kind() {
+ let kind = match cv.ty().kind() {
ty::Float(_) => {
if self.include_lint_checks {
tcx.struct_span_lint_hir(
PatKind::Wild
}
ty::Adt(..)
- if !self.type_may_have_partial_eq_impl(cv.ty)
+ if !self.type_may_have_partial_eq_impl(cv.ty())
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
- && self.search_for_structural_match_violation(cv.ty).is_some() =>
+ && self.search_for_structural_match_violation(cv.ty()).is_some() =>
{
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
- let msg = self.search_for_structural_match_violation(cv.ty).unwrap();
+ let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
self.saw_const_match_error.set(true);
if self.include_lint_checks {
tcx.sess.span_err(self.span, &msg);
// details.
// Backwards compatibility hack because we can't cause hard errors on these
// types, so we compare them via `PartialEq::eq` at runtime.
- ty::Adt(..) if !self.type_marked_structural(cv.ty) && self.behind_reference.get() => {
+ ty::Adt(..) if !self.type_marked_structural(cv.ty()) && self.behind_reference.get() => {
if self.include_lint_checks
&& !self.saw_const_match_error.get()
&& !self.saw_const_match_lint.get()
let msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
- cv.ty, cv.ty,
+ cv.ty(),
+ cv.ty(),
);
lint.build(&msg).emit()
},
// `PartialEq::eq` on it.
return Err(fallback_to_const_ref(self));
}
- ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty) => {
- debug!("adt_def {:?} has !type_marked_structural for cv.ty: {:?}", adt_def, cv.ty);
+ ty::Adt(adt_def, _) if !self.type_marked_structural(cv.ty()) => {
+ debug!(
+ "adt_def {:?} has !type_marked_structural for cv.ty: {:?}",
+ adt_def,
+ cv.ty()
+ );
let path = tcx.def_path_str(adt_def.did);
let msg = format!(
"to use a constant of type `{}` in a pattern, \
.destructure_const(param_env.and(cv))
.fields
.iter()
- .map(|val| self.recur(val, false))
+ .map(|val| self.recur(*val, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: Vec::new(),
// These are not allowed and will error elsewhere anyway.
ty::Dynamic(..) => {
self.saw_const_match_error.set(true);
- let msg = format!("`{}` cannot be used in patterns", cv.ty);
+ let msg = format!("`{}` cannot be used in patterns", cv.ty());
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
.destructure_const(param_env.and(array))
.fields
.iter()
- .map(|val| self.recur(val, false))
+ .map(|val| self.recur(*val, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: vec![],
}),
span,
- ty: pointee_ty,
+ ty: *pointee_ty,
},
};
self.behind_reference.set(old);
.destructure_const(param_env.and(array))
.fields
.iter()
- .map(|val| self.recur(val, false))
+ .map(|val| self.recur(*val, false))
.collect::<Result<_, _>>()?,
slice: None,
suffix: vec![],
// this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
// reference. This makes the rest of the matching logic simpler as it doesn't have
// to figure out how to get a reference again.
- ty::Adt(adt_def, _) if !self.type_marked_structural(pointee_ty) => {
+ ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
if self.behind_reference.get() {
if self.include_lint_checks
&& !self.saw_const_match_error.get()
}
_ => {
self.saw_const_match_error.set(true);
- let msg = format!("`{}` cannot be used in patterns", cv.ty);
+ let msg = format!("`{}` cannot be used in patterns", cv.ty());
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
&& mir_structural_match_violation
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
- && self.search_for_structural_match_violation(cv.ty).is_some()
+ && self.search_for_structural_match_violation(cv.ty()).is_some()
{
self.saw_const_match_lint.set(true);
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
- let msg = self.search_for_structural_match_violation(cv.ty).unwrap().replace(
+ let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
"in a pattern,",
"in a pattern, the constant's initializer must be trivial or",
);
);
}
- Ok(Pat { span, ty: cv.ty, kind: Box::new(kind) })
+ Ok(Pat { span, ty: cv.ty(), kind: Box::new(kind) })
}
}
fn from_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
- value: &Const<'tcx>,
+ value: Const<'tcx>,
) -> Option<IntRange> {
- if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, value.ty) {
- let ty = value.ty;
+ let ty = value.ty();
+ if let Some((target_size, bias)) = Self::integral_size_and_signed_bias(tcx, ty) {
let val = (|| {
- if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val {
+ if let ty::ConstKind::Value(ConstValue::Scalar(scalar)) = value.val() {
// For this specific pattern we can skip a lot of effort and go
// straight to the result, after doing a bit of checking. (We
// could remove this branch and just fall through, which
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
IntRange(IntRange),
/// Ranges of floating-point literal values (`2.0..=5.2`).
- FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
+ FloatRange(ty::Const<'tcx>, ty::Const<'tcx>, RangeEnd),
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
- Str(&'tcx ty::Const<'tcx>),
+ Str(ty::Const<'tcx>),
/// Array and slice patterns.
Slice(Slice),
/// Constants that must not be matched structurally. They are treated as black
FloatRange(other_from, other_to, other_end),
) => {
match (
- compare_const_vals(pcx.cx.tcx, self_to, other_to, pcx.cx.param_env, pcx.ty),
- compare_const_vals(pcx.cx.tcx, self_from, other_from, pcx.cx.param_env, pcx.ty),
+ compare_const_vals(pcx.cx.tcx, *self_to, *other_to, pcx.cx.param_env, pcx.ty),
+ compare_const_vals(
+ pcx.cx.tcx,
+ *self_from,
+ *other_from,
+ pcx.cx.param_env,
+ pcx.ty,
+ ),
) {
(Some(to), Some(from)) => {
(from == Ordering::Greater || from == Ordering::Equal)
}
(Str(self_val), Str(other_val)) => {
// FIXME: there's probably a more direct way of comparing for equality
- match compare_const_vals(pcx.cx.tcx, self_val, other_val, pcx.cx.param_env, pcx.ty)
- {
+ match compare_const_vals(
+ pcx.cx.tcx,
+ *self_val,
+ *other_val,
+ pcx.cx.param_env,
+ pcx.ty,
+ ) {
Some(comparison) => comparison == Ordering::Equal,
None => false,
}
ty::Bool => smallvec![make_range(0, 1)],
ty::Array(sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
let len = len.eval_usize(cx.tcx, cx.param_env) as usize;
- if len != 0 && cx.is_uninhabited(sub_ty) {
+ if len != 0 && cx.is_uninhabited(*sub_ty) {
smallvec![]
} else {
smallvec![Slice(Slice::new(Some(len), VarLen(0, 0)))]
}
// Treat arrays of a constant but unknown length like slices.
ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
- let kind = if cx.is_uninhabited(sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
+ let kind = if cx.is_uninhabited(*sub_ty) { FixedLen(0) } else { VarLen(0, 0) };
smallvec![Slice(Slice::new(None, kind))]
}
ty::Adt(def, substs) if def.is_enum() => {
}
}
PatKind::Constant { value } => {
- if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, value) {
+ if let Some(int_range) = IntRange::from_const(cx.tcx, cx.param_env, *value) {
ctor = IntRange(int_range);
fields = Fields::empty();
} else {
match pat.ty.kind() {
ty::Float(_) => {
- ctor = FloatRange(value, value, RangeEnd::Included);
+ ctor = FloatRange(*value, *value, RangeEnd::Included);
fields = Fields::empty();
}
ty::Ref(_, t, _) if t.is_str() => {
// fields.
// Note: `t` is `str`, not `&str`.
let subpattern =
- DeconstructedPat::new(Str(value), Fields::empty(), t, pat.span);
+ DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span);
ctor = Single;
fields = Fields::singleton(cx, subpattern)
}
}
}
&PatKind::Range(PatRange { lo, hi, end }) => {
- let ty = lo.ty;
+ let ty = lo.ty();
ctor = if let Some(int_range) = IntRange::from_range(
cx.tcx,
- lo.eval_bits(cx.tcx, cx.param_env, lo.ty),
- hi.eval_bits(cx.tcx, cx.param_env, hi.ty),
+ lo.eval_bits(cx.tcx, cx.param_env, lo.ty()),
+ hi.eval_bits(cx.tcx, cx.param_env, hi.ty()),
ty,
&end,
) {
debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
Pat {
span: pat.span,
- ty: ref_ty,
+ ty: *ref_ty,
kind: Box::new(PatKind::Deref { subpattern: pat }),
}
},
fn lower_pattern_range(
&mut self,
ty: Ty<'tcx>,
- lo: &'tcx ty::Const<'tcx>,
- hi: &'tcx ty::Const<'tcx>,
+ lo: ty::Const<'tcx>,
+ hi: ty::Const<'tcx>,
end: RangeEnd,
span: Span,
) -> PatKind<'tcx> {
- assert_eq!(lo.ty, ty);
- assert_eq!(hi.ty, ty);
+ assert_eq!(lo.ty(), ty);
+ assert_eq!(hi.ty(), ty);
let cmp = compare_const_vals(self.tcx, lo, hi, self.param_env, ty);
match (end, cmp) {
// `x..y` where `x < y`.
ty: Ty<'tcx>,
lo: Option<&PatKind<'tcx>>,
hi: Option<&PatKind<'tcx>>,
- ) -> Option<(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>)> {
+ ) -> Option<(ty::Const<'tcx>, ty::Const<'tcx>)> {
match (lo, hi) {
(Some(PatKind::Constant { value: lo }), Some(PatKind::Constant { value: hi })) => {
- Some((lo, hi))
+ Some((*lo, *hi))
}
(Some(PatKind::Constant { value: lo }), None) => {
- Some((lo, ty.numeric_max_val(self.tcx)?))
+ Some((*lo, ty.numeric_max_val(self.tcx)?))
}
(None, Some(PatKind::Constant { value: hi })) => {
- Some((ty.numeric_min_val(self.tcx)?, hi))
+ Some((ty.numeric_min_val(self.tcx)?, *hi))
}
_ => None,
}
let var_ty = ty;
if let ty::BindByReference(_) = bm {
if let ty::Ref(_, rty, _) = ty.kind() {
- ty = rty;
+ ty = *rty;
} else {
bug!("`ref {}` has wrong type {}", ident, ty);
}
| DefKind::AssocTy,
_,
)
- | Res::SelfTy(..)
+ | Res::SelfTy { .. }
| Res::SelfCtor(..) => PatKind::Leaf { subpatterns },
_ => {
let pattern_error = match res {
let const_ =
ty::Const::from_value(self.tcx, value, self.typeck_results.node_type(id));
- let pattern = self.const_to_pat(&const_, id, span, mir_structural_match_violation);
+ let pattern = self.const_to_pat(const_, id, span, mir_structural_match_violation);
if !is_associated_const {
return pattern;
user_ty_span: span,
},
}),
- ty: const_.ty,
+ ty: const_.ty(),
}
} else {
pattern
// Evaluate early like we do in `lower_path`.
let value = value.eval(self.tcx, self.param_env);
- match value.val {
+ match value.val() {
ConstKind::Param(_) => {
self.errors.push(PatternError::ConstParamInPattern(span));
return PatKind::Wild;
crate fn compare_const_vals<'tcx>(
tcx: TyCtxt<'tcx>,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
) -> Option<Ordering> {
let fallback = || from_bool(a == b);
// Use the fallback if any type differs
- if a.ty != b.ty || a.ty != ty {
+ if a.ty() != b.ty() || a.ty() != ty {
return fallback();
}
// Early return for equal constants (so e.g. references to ZSTs can be compared, even if they
// are just integer addresses).
- if a.val == b.val {
+ if a.val() == b.val() {
return from_bool(true);
}
if let (
ty::ConstKind::Value(a_val @ ConstValue::Slice { .. }),
ty::ConstKind::Value(b_val @ ConstValue::Slice { .. }),
- ) = (a.val, b.val)
+ ) = (a.val(), b.val())
{
let a_bytes = get_slice_bytes(&tcx, a_val);
let b_bytes = get_slice_bytes(&tcx, b_val);
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_target::abi::VariantIdx;
-use std::fmt;
+use std::{fmt, iter};
/// The value of an inserted drop flag.
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
mut succ: BasicBlock,
fields: &[(Place<'tcx>, Option<D::Path>)],
) -> Vec<BasicBlock> {
- Some(succ)
- .into_iter()
+ iter::once(succ)
.chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| {
succ = self.drop_subpath(place, path, succ, unwind_succ);
succ
ty::Dynamic(..) => self.complete_drop(self.succ, self.unwind),
ty::Array(ety, size) => {
let size = size.try_eval_usize(self.tcx(), self.elaborator.param_env());
- self.open_drop_for_array(ety, size)
+ self.open_drop_for_array(*ety, size)
}
- ty::Slice(ety) => self.open_drop_for_array(ety, None),
+ ty::Slice(ety) => self.open_drop_for_array(*ety, None),
_ => bug!("open drop from non-ADT `{:?}`", ty),
}
Default::default(),
body.span,
body.generator_kind(),
+ body.tainted_by_errors,
);
// FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
let err = ConstEvalErr::new(&self.ecx, error, Some(c.span));
if let Some(lint_root) = self.lint_root(source_info) {
let lint_only = match c.literal {
- ConstantKind::Ty(ct) => match ct.val {
+ ConstantKind::Ty(ct) => match ct.val() {
// Promoteds must lint and not error as the user didn't ask for them
ConstKind::Unevaluated(ty::Unevaluated {
def: _,
) {
if let Rvalue::Use(Operand::Constant(c)) = rval {
match c.literal {
- ConstantKind::Ty(c) if matches!(c.val, ConstKind::Unevaluated(..)) => {}
+ ConstantKind::Ty(c) if matches!(c.val(), ConstKind::Unevaluated(..)) => {}
_ => {
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
return;
// Found a value represented as a pair. For now only do const-prop if the type
// of `rvalue` is also a tuple with two scalars.
// FIXME: enable the general case stated above ^.
- let ty = &value.layout.ty;
+ let ty = value.layout.ty;
// Only do it for tuples
if let ty::Tuple(substs) = ty.kind() {
// Only do it if tuple is also a pair with two scalars
literal: self
.ecx
.tcx
- .mk_const(ty::Const {
+ .mk_const(ty::ConstS {
ty,
val: ty::ConstKind::Value(ConstValue::ByRef {
alloc,
//!
//! To enable coverage, include the rustc command line option:
//!
-//! * `-Z instrument-coverage`
+//! * `-C instrument-coverage`
//!
//! MIR Dump Files, with additional `CoverageGraph` graphviz and `CoverageSpan` spanview
//! ------------------------------------------------------------------------------------
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::mir::*;
-use rustc_middle::ty::{self, DebruijnIndex, TyS, TypeFlags};
+use rustc_middle::ty::{self, BOOL_TY};
use rustc_span::{self, BytePos, Pos, Span, DUMMY_SP};
// All `TEMP_BLOCK` targets should be replaced before calling `to_body() -> mir::Body`.
const TEMP_BLOCK: BasicBlock = BasicBlock::MAX;
-fn dummy_ty() -> &'static TyS<'static> {
- thread_local! {
- static DUMMY_TYS: &'static TyS<'static> = Box::leak(Box::new(TyS::make_for_test(
- ty::Bool,
- TypeFlags::empty(),
- DebruijnIndex::from_usize(0),
- )));
- }
-
- &DUMMY_TYS.with(|tys| *tys)
-}
-
struct MockBlocks<'tcx> {
blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>>,
dummy_place: Place<'tcx>,
fn switchint(&mut self, some_from_block: Option<BasicBlock>) -> BasicBlock {
let switchint_kind = TerminatorKind::SwitchInt {
discr: Operand::Move(Place::from(self.new_temp())),
- switch_ty: dummy_ty(),
+ switch_ty: BOOL_TY, // just a dummy value
targets: SwitchTargets::static_if(0, TEMP_BLOCK, TEMP_BLOCK),
};
self.add_block_from(some_from_block, switchint_kind)
impl UnifyKey for UnifyLocal {
type Value = ();
+ #[inline]
fn index(&self) -> u32 {
self.0.as_u32()
}
+ #[inline]
fn from_index(u: u32) -> Self {
Self(Local::from_u32(u))
}
// create temp to store inequality comparison between the two discriminants, `_t` in
// example above
let nequal = BinOp::Ne;
- let comp_res_type = nequal.ty(tcx, parent_ty, opt_data.child_ty);
+ let comp_res_type = nequal.ty(tcx, *parent_ty, opt_data.child_ty);
let comp_temp = patch.new_temp(comp_res_type, opt_data.child_source.span);
patch.add_statement(parent_end, StatementKind::StorageLive(comp_temp));
Some(OptimizationData {
destination,
child_place: *child_place,
- child_ty,
+ child_ty: *child_ty,
child_source: child_terminator.source_info,
})
}
}
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let yield_ty = if let Some(yield_ty) = body.yield_ty() {
- yield_ty
- } else {
+ let Some(yield_ty) = body.yield_ty() else {
// This only applies to generators
return;
};
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
+use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, ConstKind, Instance, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_span::{hygiene::ExpnKind, ExpnData, Span};
return false;
}
+ let param_env = tcx.param_env_reveal_all_normalized(def_id);
+ let param_env = rustc_trait_selection::traits::normalize_param_env_or_error(
+ tcx,
+ def_id,
+ param_env,
+ ObligationCause::misc(body.span, hir_id),
+ );
+
let mut this = Inliner {
tcx,
- param_env: tcx.param_env_reveal_all_normalized(body.source.def_id()),
- codegen_fn_attrs: tcx.codegen_fn_attrs(body.source.def_id()),
+ param_env,
+ codegen_fn_attrs: tcx.codegen_fn_attrs(def_id),
hir_id,
history: Vec::new(),
changed: false,
caller_body.required_consts.extend(
callee_body.required_consts.iter().copied().filter(|&ct| {
match ct.literal.const_for_ty() {
- Some(ct) => matches!(ct.val, ConstKind::Unevaluated(_)),
+ Some(ct) => matches!(ct.val(), ConstKind::Unevaluated(_)),
None => true,
}
}),
mod check_const_item_mutation;
mod check_packed_ref;
pub mod check_unsafety;
-mod cleanup_post_borrowck;
+// This pass is public to allow external drivers to perform MIR cleanup
+pub mod cleanup_post_borrowck;
mod const_debuginfo;
mod const_goto;
mod const_prop;
mod multiple_return_terminators;
mod normalize_array_len;
mod nrvo;
-mod remove_false_edges;
+// 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;
mod reveal_all;
mod separate_const_switch;
mod shim;
-mod simplify;
+// This pass is public to allow external drivers to perform MIR cleanup
+pub mod simplify;
mod simplify_branches;
mod simplify_comparison_integral;
mod simplify_try;
// Ensure that we compute the `mir_const_qualif` for constants at
// this point, before we steal the mir-const result.
// Also this means promotion can rely on all const checks having been done.
- let _ = tcx.mir_const_qualif_opt_const_arg(def);
+ let const_qualifs = tcx.mir_const_qualif_opt_const_arg(def);
let mut body = tcx.mir_const(def).steal();
+ if let Some(error_reported) = const_qualifs.tainted_by_errors {
+ body.tainted_by_errors = Some(error_reported);
+ }
let mut required_consts = Vec::new();
let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
return tcx.mir_drops_elaborated_and_const_checked(def);
}
- // (Mir-)Borrowck uses `mir_promoted`, so we have to force it to
- // execute before we can steal.
- if let Some(param_did) = def.const_param_did {
- tcx.ensure().mir_borrowck_const_arg((def.did, param_did));
- } else {
- tcx.ensure().mir_borrowck(def.did);
- }
+ let mir_borrowck = tcx.mir_borrowck_opt_const_arg(def);
let is_fn_like = tcx.hir().get_by_def_id(def.did).fn_kind().is_some();
if is_fn_like {
let (body, _) = tcx.mir_promoted(def);
let mut body = body.steal();
+ if let Some(error_reported) = mir_borrowck.tainted_by_errors {
+ body.tainted_by_errors = Some(error_reported);
+ }
// IMPORTANT
pm::run_passes(tcx, &mut body, &[&remove_false_edges::RemoveFalseEdges]);
return tcx.arena.alloc(IndexVec::new());
}
- if let Some(param_did) = def.const_param_did {
- tcx.ensure().mir_borrowck_const_arg((def.did, param_did));
- } else {
- tcx.ensure().mir_borrowck(def.did);
- }
- let (_, promoted) = tcx.mir_promoted(def);
- let mut promoted = promoted.steal();
+ let tainted_by_errors = tcx.mir_borrowck_opt_const_arg(def).tainted_by_errors;
+ let mut promoted = tcx.mir_promoted(def).1.steal();
for body in &mut promoted {
+ if let Some(error_reported) = tainted_by_errors {
+ body.tainted_by_errors = Some(error_reported);
+ }
run_post_borrowck_cleanup_passes(tcx, body);
}
use crate::MirPass;
use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::intern::Interned;
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::*;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, ReErased, Region, TyCtxt};
const MAX_NUM_BLOCKS: usize = 800;
const MAX_NUM_LOCALS: usize = 3000;
let Some(local) = place.as_local() else { return };
match operand {
Operand::Copy(place) | Operand::Move(place) => {
- let operand_local =
- if let Some(local) = place.local_or_deref_local() {
- local
- } else {
- return;
- };
+ let Some(operand_local) = place.local_or_deref_local() else { return; };
if !interesting_locals.contains(operand_local) {
return;
}
// current way of patching doesn't allow to work with `mut`
(
ty::Ref(
- ty::RegionKind::ReErased,
+ Region(Interned(ReErased, _)),
operand_ty,
Mutability::Not,
),
- ty::Ref(ty::RegionKind::ReErased, cast_ty, Mutability::Not),
+ ty::Ref(
+ Region(Interned(ReErased, _)),
+ cast_ty,
+ Mutability::Not,
+ ),
) => {
match (operand_ty.kind(), cast_ty.kind()) {
// current way of patching doesn't allow to work with `mut`
impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
if let Some(ct) = constant.literal.const_for_ty() {
- if let ConstKind::Unevaluated(_) = ct.val {
+ if let ConstKind::Unevaluated(_) = ct.val() {
self.required_consts.push(*constant);
}
}
// We have to use `try_normalize_erasing_regions` here, since it's
// possible that we visit impossible-to-satisfy where clauses here,
// see #91745
- *ty = self.tcx.try_normalize_erasing_regions(self.param_env, *ty).unwrap_or(ty);
+ *ty = self.tcx.try_normalize_erasing_regions(self.param_env, *ty).unwrap_or(*ty);
}
}
ty::InstanceDef::DropGlue(def_id, ty) => {
// FIXME(#91576): Drop shims for generators aren't subject to the MIR passes at the end
// of this function. Is this intentional?
- if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(ty::TyS::kind) {
+ if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(Ty::kind) {
let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
let body = body.clone().subst(tcx, substs);
debug!("make_shim({:?}) = {:?}", instance, body);
span: Span,
) -> IndexVec<Local, LocalDecl<'tcx>> {
iter::once(LocalDecl::new(sig.output(), span))
- .chain(sig.inputs().iter().map(|ity| LocalDecl::new(ity, span).immutable()))
+ .chain(sig.inputs().iter().map(|ity| LocalDecl::new(*ity, span).immutable()))
.collect()
}
vec![],
span,
None,
+ // FIXME(compiler-errors): is this correct?
+ None,
)
}
/// evaluation: `if false { ... }`.
///
/// Those statements are bypassed by redirecting paths in the CFG around the
-/// `dead blocks`; but with `-Z instrument-coverage`, the dead blocks usually
+/// `dead blocks`; but with `-C instrument-coverage`, the dead blocks usually
/// include `Coverage` statements representing the Rust source code regions to
/// be counted at runtime. Without these `Coverage` statements, the regions are
/// lost, and the Rust source code will show no coverage information.
let literal = self.monomorphize(constant.literal);
let val = match literal {
mir::ConstantKind::Val(val, _) => val,
- mir::ConstantKind::Ty(ct) => match ct.val {
+ mir::ConstantKind::Ty(ct) => match ct.val() {
ty::ConstKind::Value(val) => val,
ty::ConstKind::Unevaluated(ct) => {
let param_env = ty::ParamEnv::reveal_all();
self.visit_ty(literal.ty(), TyContext::Location(location));
}
- fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, location: Location) {
- debug!("visiting const {:?} @ {:?}", *constant, location);
+ fn visit_const(&mut self, constant: ty::Const<'tcx>, location: Location) {
+ debug!("visiting const {:?} @ {:?}", constant, location);
- let substituted_constant = self.monomorphize(*constant);
+ let substituted_constant = self.monomorphize(constant);
let param_env = ty::ParamEnv::reveal_all();
- match substituted_constant.val {
+ match substituted_constant.val() {
ty::ConstKind::Value(val) => collect_const_value(self.tcx, val, self.output),
ty::ConstKind::Unevaluated(unevaluated) => {
match self.tcx.const_eval_resolve(param_env, unevaluated, None) {
/// Returns `true` if we should codegen an instance in the local crate, or returns `false` if we
/// can just link to the upstream crate and therefore don't need a mono item.
fn should_codegen_locally<'tcx>(tcx: TyCtxt<'tcx>, instance: &Instance<'tcx>) -> bool {
- let def_id = if let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() {
- def_id
- } else {
+ let Some(def_id) = instance.def.def_id_if_not_guaranteed_local_codegen() else {
return true;
};
match (&source_ty.kind(), &target_ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(ty::TypeAndMut { ty: b, .. }))
| (&ty::RawPtr(ty::TypeAndMut { ty: a, .. }), &ty::RawPtr(ty::TypeAndMut { ty: b, .. })) => {
- ptr_vtable(a, b)
+ ptr_vtable(*a, *b)
}
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty())
self.super_local_decl(local, local_decl);
}
- fn visit_const(&mut self, c: &&'tcx Const<'tcx>, _: Location) {
+ fn visit_const(&mut self, c: Const<'tcx>, _: Location) {
c.visit_with(self);
}
impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
#[instrument(level = "debug", skip(self))]
- fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if !c.has_param_types_or_consts() {
return ControlFlow::CONTINUE;
}
- match c.val {
+ match c.val() {
ty::ConstKind::Param(param) => {
debug!(?param);
self.unused_parameters.clear(param.index);
type BreakTy = ();
#[instrument(level = "debug", skip(self))]
- fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
if !c.has_param_types_or_consts() {
return ControlFlow::CONTINUE;
}
- match c.val {
+ match c.val() {
ty::ConstKind::Param(param) => {
if self.unused_parameters.contains(param.index).unwrap_or(false) {
ControlFlow::CONTINUE
/// During the same compile all closures dump the information in the same file
/// "closure_profile_XXXXX.csv", which is created in the directory where the compiler is invoked.
crate fn dump_closure_profile<'tcx>(tcx: TyCtxt<'tcx>, closure_instance: Instance<'tcx>) {
- let mut file = if let Ok(file) = OpenOptions::new()
+ let Ok(mut file) = OpenOptions::new()
.create(true)
.append(true)
.open(&format!("closure_profile_{}.csv", std::process::id()))
- {
- file
- } else {
+ else {
eprintln!("Cound't open file for writing closure profile");
return;
};
Some(match token {
rustc_lexer::TokenKind::LineComment { doc_style } => {
// Skip non-doc comments
- let doc_style = if let Some(doc_style) = doc_style {
- doc_style
- } else {
+ let Some(doc_style) = doc_style else {
self.lint_unicode_text_flow(start);
return None;
};
}
// Skip non-doc comments
- let doc_style = if let Some(doc_style) = doc_style {
- doc_style
- } else {
+ let Some(doc_style) = doc_style else {
self.lint_unicode_text_flow(start);
return None;
};
version control settings",
);
} else {
+ if !mode.is_bytes() {
+ diag.span_suggestion(
+ span_with_quotes,
+ "if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal",
+ format!("r\"{}\"", lit),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
diag.help(
"for more information, visit \
<https://static.rust-lang.org/doc/master/reference.html#literals>",
#![feature(crate_visibility_modifier)]
#![feature(if_let_guard)]
#![feature(box_patterns)]
+#![feature(let_else)]
#![recursion_limit = "256"]
#[macro_use]
} else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
} else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) {
- // We're probably inside of a `Path<'a>` that needs a turbofish, so suppress the
- // "must be followed by a colon" error, and the "expected one of" error.
- self.diagnostic().delay_span_bug(lo, "this label wasn't parsed correctly");
+ // We're probably inside of a `Path<'a>` that needs a turbofish
+ let msg = "expected `while`, `for`, `loop` or `{` after a label";
+ self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
consume_colon = false;
Ok(self.mk_expr_err(lo))
} else {
// Try to lowercase the prefix if it's a valid base prefix.
fn fix_base_capitalisation(s: &str) -> Option<String> {
- if let Some(stripped) = s.strip_prefix("B") {
+ if let Some(stripped) = s.strip_prefix('B') {
Some(format!("0b{stripped}"))
- } else if let Some(stripped) = s.strip_prefix("O") {
+ } else if let Some(stripped) = s.strip_prefix('O') {
Some(format!("0o{stripped}"))
- } else if let Some(stripped) = s.strip_prefix("X") {
+ } else if let Some(stripped) = s.strip_prefix('X') {
Some(format!("0x{stripped}"))
} else {
None
/// The argument is located at a specific index given in the format
ArgumentIs(usize),
/// The argument has a name.
- ArgumentNamed(Symbol),
+ ArgumentNamed(Symbol, InnerSpan),
}
impl Position {
/// The count is specified explicitly.
CountIs(usize),
/// The count is specified by the argument with the given name.
- CountIsName(Symbol),
+ CountIsName(Symbol, InnerSpan),
/// The count is specified by the argument at the given index.
CountIsParam(usize),
/// The count is implied and cannot be explicitly specified.
Some(ArgumentIs(i))
} else {
match self.cur.peek() {
- Some(&(_, c)) if rustc_lexer::is_id_start(c) => {
- Some(ArgumentNamed(Symbol::intern(self.word())))
+ Some(&(start, c)) if rustc_lexer::is_id_start(c) => {
+ let word = self.word();
+ let end = start + word.len();
+ let span = self.to_span_index(start).to(self.to_span_index(end));
+ Some(ArgumentNamed(Symbol::intern(word), span))
}
// This is an `ArgumentNext`.
if word.is_empty() {
self.cur = tmp;
(CountImplied, None)
- } else if self.consume('$') {
- (CountIsName(Symbol::intern(word)), None)
+ } else if let Some(end) = self.consume_pos('$') {
+ let span = self.to_span_index(start + 1).to(self.to_span_index(end));
+ (CountIsName(Symbol::intern(word), span), None)
} else {
self.cur = tmp;
(CountImplied, None)
fill: None,
align: AlignUnknown,
flags: 0,
- precision: CountIsName(Symbol::intern("b")),
- width: CountIsName(Symbol::intern("a")),
+ precision: CountIsName(Symbol::intern("b"), InnerSpan::new(6, 7)),
+ width: CountIsName(Symbol::intern("a"), InnerSpan::new(4, 4)),
precision_span: None,
width_span: None,
ty: "?",
}
fn check_used(&self, attrs: &[Attribute], target: Target) {
+ let mut used_linker_span = None;
+ let mut used_compiler_span = None;
for attr in attrs {
if attr.has_name(sym::used) && target != Target::Static {
self.tcx
.sess
.span_err(attr.span, "attribute must be applied to a `static` variable");
}
+ let inner = attr.meta_item_list();
+ match inner.as_deref() {
+ Some([item]) if item.has_name(sym::linker) => {
+ if used_linker_span.is_none() {
+ used_linker_span = Some(attr.span);
+ }
+ }
+ Some([item]) if item.has_name(sym::compiler) => {
+ if used_compiler_span.is_none() {
+ used_compiler_span = Some(attr.span);
+ }
+ }
+ Some(_) => {
+ // This error case is handled in rustc_typeck::collect.
+ }
+ None => {
+ // Default case (compiler) when arg isn't defined.
+ if used_compiler_span.is_none() {
+ used_compiler_span = Some(attr.span);
+ }
+ }
+ }
+ }
+ if let (Some(linker_span), Some(compiler_span)) = (used_linker_span, used_compiler_span) {
+ let spans = vec![linker_span, compiler_span];
+ self.tcx
+ .sess
+ .struct_span_err(
+ spans,
+ "`used(compiler)` and `used(linker)` can't be used together",
+ )
+ .emit();
}
}
self.check_def_id(variant_id);
}
}
- Res::SelfTy(t, i) => {
+ Res::SelfTy { trait_: t, alias_to: i } => {
if let Some(t) = t {
self.check_def_id(t);
}
// #[used], #[no_mangle], #[export_name], etc also keeps the item alive
// forcefully, e.g., for placing it in a specific section.
- if cg_attrs.contains_extern_indicator() || cg_attrs.flags.contains(CodegenFnAttrFlags::USED) {
+ if cg_attrs.contains_extern_indicator()
+ || cg_attrs.flags.contains(CodegenFnAttrFlags::USED)
+ || cg_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
+ {
return true;
}
}
fn check_missing_const_stability(&self, def_id: LocalDefId, span: Span) {
- let stab_map = self.tcx.stability();
- let stab = stab_map.local_stability(def_id);
- if stab.map_or(false, |stab| stab.level.is_stable()) {
- let const_stab = stab_map.local_const_stability(def_id);
- if const_stab.is_none() {
- self.tcx.sess.span_err(
- span,
- "`#[stable]` const functions must also be either \
- `#[rustc_const_stable]` or `#[rustc_const_unstable]`",
- );
- }
+ if !self.tcx.features().staged_api {
+ return;
+ }
+
+ let is_const = self.tcx.is_const_fn(def_id.to_def_id());
+ let is_stable = self
+ .tcx
+ .lookup_stability(def_id)
+ .map_or(false, |stability| stability.level.is_stable());
+ let missing_const_stability_attribute = self.tcx.lookup_const_stability(def_id).is_none();
+ let is_reachable = self.access_levels.is_reachable(def_id);
+
+ if is_const && is_stable && missing_const_stability_attribute && is_reachable {
+ let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
+ self.tcx.sess.span_err(span, &format!("{descr} has missing const stability attribute"));
}
}
}
self.check_missing_stability(i.def_id, i.span);
}
- // Ensure `const fn` that are `stable` have one of `rustc_const_unstable` or
- // `rustc_const_stable`.
- if self.tcx.features().staged_api
- && matches!(&i.kind, hir::ItemKind::Fn(sig, ..) if sig.header.is_const())
- {
- self.check_missing_const_stability(i.def_id, i.span);
- }
+ // Ensure stable `const fn` have a const stability attribute.
+ self.check_missing_const_stability(i.def_id, i.span);
intravisit::walk_item(self, i)
}
let impl_def_id = self.tcx.hir().get_parent_item(ii.hir_id());
if self.tcx.impl_trait_ref(impl_def_id).is_none() {
self.check_missing_stability(ii.def_id, ii.span);
+ self.check_missing_const_stability(ii.def_id, ii.span);
}
intravisit::walk_impl_item(self, ii);
}
}
}
- fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- self.visit_ty(c.ty)?;
+ fn visit_const(&mut self, c: Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ self.visit_ty(c.ty())?;
let tcx = self.def_id_visitor.tcx();
if let Ok(Some(ct)) = AbstractConst::from_const(tcx, c) {
self.visit_abstract_const_expr(tcx, ct)?;
impl<'a, 'tcx> ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
fn path_is_private_type(&self, path: &hir::Path<'_>) -> bool {
let did = match path.res {
- Res::PrimTy(..) | Res::SelfTy(..) | Res::Err => return false,
+ Res::PrimTy(..) | Res::SelfTy { .. } | Res::Err => return false,
res => res.def_id(),
};
}
}
-impl<'tcx> Key for (&'tcx ty::Const<'tcx>, mir::Field) {
+impl<'tcx> Key for (ty::Const<'tcx>, mir::Field) {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
}
-impl<'tcx> Key for &'tcx ty::Const<'tcx> {
+impl<'tcx> Key for ty::Const<'tcx> {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
extern crate rustc_middle;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::AtomicU64;
use rustc_middle::arena::Arena;
use rustc_middle::dep_graph::{self, DepKindStruct, SerializedDepNodeIndex};
use rustc_middle::ty::query::{query_keys, query_storage, query_stored, query_values};
pub use plumbing::QueryCtxt;
use rustc_query_system::query::*;
-mod stats;
-pub use self::stats::print_stats;
-
mod keys;
use keys::Key;
//! manage the caches, and so forth.
use crate::{on_disk_cache, Queries};
-use rustc_middle::dep_graph::{DepKind, DepNodeIndex, SerializedDepNodeIndex};
+use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex};
use rustc_middle::ty::tls::{self, ImplicitCtxt};
use rustc_middle::ty::TyCtxt;
use rustc_query_system::dep_graph::HasDepContext;
use rustc_serialize::opaque;
use std::any::Any;
+use std::num::NonZeroU64;
#[derive(Copy, Clone)]
pub struct QueryCtxt<'tcx> {
}
impl QueryContext for QueryCtxt<'_> {
- fn current_query_job(&self) -> Option<QueryJobId<Self::DepKind>> {
+ fn next_job_id(&self) -> QueryJobId {
+ QueryJobId(
+ NonZeroU64::new(
+ self.queries.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed),
+ )
+ .unwrap(),
+ )
+ }
+
+ fn current_query_job(&self) -> Option<QueryJobId> {
tls::with_related_context(**self, |icx| icx.query)
}
- fn try_collect_active_jobs(&self) -> Option<QueryMap<Self::DepKind>> {
+ fn try_collect_active_jobs(&self) -> Option<QueryMap> {
self.queries.try_collect_active_jobs(**self)
}
#[inline(always)]
fn start_query<R>(
&self,
- token: QueryJobId<Self::DepKind>,
+ token: QueryJobId,
diagnostics: Option<&Lock<ThinVec<Diagnostic>>>,
compute: impl FnOnce() -> R,
) -> R {
pub fn try_print_query_stack(
self,
- query: Option<QueryJobId<DepKind>>,
+ query: Option<QueryJobId>,
handler: &Handler,
num_frames: Option<usize>,
) -> usize {
}
#[allow(nonstandard_style)]
- pub mod queries {
+ mod queries {
use std::marker::PhantomData;
$(pub struct $name<$tcx> {
type Cache = query_storage::$name<$tcx>;
#[inline(always)]
- fn query_state<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryState<crate::dep_graph::DepKind, Self::Key>
+ fn query_state<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryState<Self::Key>
where QueryCtxt<$tcx>: 'a
{
&tcx.queries.$name
})*
#[allow(nonstandard_style)]
- pub mod query_callbacks {
+ mod query_callbacks {
use super::*;
use rustc_middle::dep_graph::DepNode;
use rustc_middle::ty::query::query_keys;
}
}
- $(pub fn $name()-> DepKindStruct {
+ $(pub(crate) fn $name()-> DepKindStruct {
let is_anon = is_anon!([$($modifiers)*]);
let is_eval_always = is_eval_always!([$($modifiers)*]);
pub on_disk_cache: Option<OnDiskCache<$tcx>>,
- $($(#[$attr])* $name: QueryState<
- crate::dep_graph::DepKind,
- query_keys::$name<$tcx>,
- >,)*
+ jobs: AtomicU64,
+
+ $($(#[$attr])* $name: QueryState<query_keys::$name<$tcx>>,)*
}
impl<$tcx> Queries<$tcx> {
local_providers: Box::new(local_providers),
extern_providers: Box::new(extern_providers),
on_disk_cache,
+ jobs: AtomicU64::new(1),
$($name: Default::default()),*
}
}
pub(crate) fn try_collect_active_jobs(
&$tcx self,
tcx: TyCtxt<$tcx>,
- ) -> Option<QueryMap<crate::dep_graph::DepKind>> {
+ ) -> Option<QueryMap> {
let tcx = QueryCtxt { tcx, queries: self };
let mut jobs = QueryMap::default();
$(
self.$name.try_collect_active_jobs(
tcx,
- dep_graph::DepKind::$name,
make_query::$name,
&mut jobs,
)?;
+++ /dev/null
-use rustc_hir::def_id::{DefId, LOCAL_CRATE};
-use rustc_middle::ty::query::query_storage;
-use rustc_middle::ty::TyCtxt;
-use rustc_query_system::query::{QueryCache, QueryCacheStore};
-
-use std::any::type_name;
-use std::mem;
-
-trait KeyStats {
- fn key_stats(&self, stats: &mut QueryStats);
-}
-
-impl<T> KeyStats for T {
- default fn key_stats(&self, _: &mut QueryStats) {}
-}
-
-impl KeyStats for DefId {
- fn key_stats(&self, stats: &mut QueryStats) {
- if self.krate == LOCAL_CRATE {
- stats.local_def_id_keys = Some(stats.local_def_id_keys.unwrap_or(0) + 1);
- }
- }
-}
-
-#[derive(Clone)]
-struct QueryStats {
- name: &'static str,
- key_size: usize,
- key_type: &'static str,
- value_size: usize,
- value_type: &'static str,
- entry_count: usize,
- local_def_id_keys: Option<usize>,
-}
-
-fn stats<C>(name: &'static str, map: &QueryCacheStore<C>) -> QueryStats
-where
- C: QueryCache,
-{
- let mut stats = QueryStats {
- name,
- key_size: mem::size_of::<C::Key>(),
- key_type: type_name::<C::Key>(),
- value_size: mem::size_of::<C::Value>(),
- value_type: type_name::<C::Value>(),
- entry_count: 0,
- local_def_id_keys: None,
- };
- map.iter_results(&mut |key, _, _| {
- stats.entry_count += 1;
- key.key_stats(&mut stats)
- });
- stats
-}
-
-pub fn print_stats(tcx: TyCtxt<'_>) {
- let queries = query_stats(tcx);
-
- let mut query_key_sizes = queries.clone();
- query_key_sizes.sort_by_key(|q| q.key_size);
- eprintln!("\nLarge query keys:");
- for q in query_key_sizes.iter().rev().filter(|q| q.key_size > 8) {
- eprintln!(" {} - {} x {} - {}", q.name, q.key_size, q.entry_count, q.key_type);
- }
-
- let mut query_value_sizes = queries.clone();
- query_value_sizes.sort_by_key(|q| q.value_size);
- eprintln!("\nLarge query values:");
- for q in query_value_sizes.iter().rev().filter(|q| q.value_size > 8) {
- eprintln!(" {} - {} x {} - {}", q.name, q.value_size, q.entry_count, q.value_type);
- }
-
- let mut query_value_count = queries.clone();
- query_value_count.sort_by_key(|q| q.entry_count);
- eprintln!("\nQuery value count:");
- for q in query_value_count.iter().rev() {
- eprintln!(" {} - {}", q.name, q.entry_count);
- }
-
- let mut def_id_density: Vec<_> =
- queries.iter().filter(|q| q.local_def_id_keys.is_some()).collect();
- def_id_density.sort_by_key(|q| q.local_def_id_keys.unwrap());
- eprintln!("\nLocal DefId density:");
- let total = tcx.resolutions(()).definitions.def_index_count() as f64;
- for q in def_id_density.iter().rev() {
- let local = q.local_def_id_keys.unwrap();
- eprintln!(" {} - {} = ({}%)", q.name, local, (local as f64 * 100.0) / total);
- }
-}
-
-macro_rules! print_stats {
- (<$tcx:tt>
- $($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,)*
- ) => {
- fn query_stats(tcx: TyCtxt<'_>) -> Vec<QueryStats> {
- let mut queries = Vec::new();
-
- $(
- queries.push(stats::<
- query_storage::$name<'_>,
- >(
- stringify!($name),
- &tcx.query_caches.$name,
- ));
- )*
-
- queries
- }
- }
-}
-
-rustc_query_append! { [print_stats!][<'tcx>] }
use super::QueryCtxt;
-use rustc_middle::ty::{self, AdtSizedConstraint, Ty, TyS};
+use rustc_middle::ty::{self, AdtSizedConstraint, Ty};
pub(super) trait Value<'tcx>: Sized {
fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self;
}
}
-impl<'tcx> Value<'tcx> for &'_ TyS<'_> {
+impl<'tcx> Value<'tcx> for Ty<'_> {
fn from_cycle_error(tcx: QueryCtxt<'tcx>) -> Self {
// SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
// FIXME: Represent the above fact in the trait system somehow.
total_edge_count: 0,
total_node_count: 0,
result: Ok(()),
- stats: if record_stats { Some(FxHashMap::default()) } else { None },
+ stats: record_stats.then(FxHashMap::default),
}
}
fn describe(tcx: CTX, key: Self::Key) -> String;
// Don't use this method to access query results, instead use the methods on TyCtxt
- fn query_state<'a>(tcx: CTX) -> &'a QueryState<CTX::DepKind, Self::Key>
+ fn query_state<'a>(tcx: CTX) -> &'a QueryState<Self::Key>
where
CTX: 'a;
use rustc_session::Session;
use rustc_span::Span;
-use std::convert::TryFrom;
use std::hash::Hash;
-use std::num::NonZeroU32;
+use std::num::NonZeroU64;
#[cfg(parallel_compiler)]
use {
- crate::dep_graph::DepKind,
parking_lot::{Condvar, Mutex},
rustc_data_structures::fx::FxHashSet,
rustc_data_structures::sync::Lock,
pub query: QueryStackFrame,
}
-pub type QueryMap<D> = FxHashMap<QueryJobId<D>, QueryJobInfo<D>>;
-
-/// A value uniquely identifying an active query job within a shard in the query cache.
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct QueryShardJobId(pub NonZeroU32);
+pub type QueryMap = FxHashMap<QueryJobId, QueryJobInfo>;
/// A value uniquely identifying an active query job.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct QueryJobId<D> {
- /// Which job within a shard is this
- pub job: QueryShardJobId,
-
- /// In which shard is this job
- pub shard: u16,
+pub struct QueryJobId(pub NonZeroU64);
- /// What kind of query this job is.
- pub kind: D,
-}
-
-impl<D> QueryJobId<D>
-where
- D: Copy + Clone + Eq + Hash,
-{
- pub fn new(job: QueryShardJobId, shard: usize, kind: D) -> Self {
- QueryJobId { job, shard: u16::try_from(shard).unwrap(), kind }
- }
-
- fn query(self, map: &QueryMap<D>) -> QueryStackFrame {
+impl QueryJobId {
+ fn query(self, map: &QueryMap) -> QueryStackFrame {
map.get(&self).unwrap().query.clone()
}
#[cfg(parallel_compiler)]
- fn span(self, map: &QueryMap<D>) -> Span {
+ fn span(self, map: &QueryMap) -> Span {
map.get(&self).unwrap().job.span
}
#[cfg(parallel_compiler)]
- fn parent(self, map: &QueryMap<D>) -> Option<QueryJobId<D>> {
+ fn parent(self, map: &QueryMap) -> Option<QueryJobId> {
map.get(&self).unwrap().job.parent
}
#[cfg(parallel_compiler)]
- fn latch<'a>(self, map: &'a QueryMap<D>) -> Option<&'a QueryLatch<D>> {
+ fn latch<'a>(self, map: &'a QueryMap) -> Option<&'a QueryLatch> {
map.get(&self).unwrap().job.latch.as_ref()
}
}
-pub struct QueryJobInfo<D> {
+pub struct QueryJobInfo {
pub query: QueryStackFrame,
- pub job: QueryJob<D>,
+ pub job: QueryJob,
}
/// Represents an active query job.
#[derive(Clone)]
-pub struct QueryJob<D> {
- pub id: QueryShardJobId,
+pub struct QueryJob {
+ pub id: QueryJobId,
/// The span corresponding to the reason for which this query was required.
pub span: Span,
/// The parent query job which created this job and is implicitly waiting on it.
- pub parent: Option<QueryJobId<D>>,
+ pub parent: Option<QueryJobId>,
/// The latch that is used to wait on this job.
#[cfg(parallel_compiler)]
- latch: Option<QueryLatch<D>>,
+ latch: Option<QueryLatch>,
}
-impl<D> QueryJob<D>
-where
- D: Copy + Clone + Eq + Hash,
-{
+impl QueryJob {
/// Creates a new query job.
- pub fn new(id: QueryShardJobId, span: Span, parent: Option<QueryJobId<D>>) -> Self {
+ pub fn new(id: QueryJobId, span: Span, parent: Option<QueryJobId>) -> Self {
QueryJob {
id,
span,
}
#[cfg(parallel_compiler)]
- pub(super) fn latch(&mut self) -> QueryLatch<D> {
+ pub(super) fn latch(&mut self) -> QueryLatch {
if self.latch.is_none() {
self.latch = Some(QueryLatch::new());
}
}
#[cfg(not(parallel_compiler))]
-impl<D> QueryJobId<D>
-where
- D: Copy + Clone + Eq + Hash,
-{
+impl QueryJobId {
#[cold]
#[inline(never)]
pub(super) fn find_cycle_in_stack(
&self,
- query_map: QueryMap<D>,
- current_job: &Option<QueryJobId<D>>,
+ query_map: QueryMap,
+ current_job: &Option<QueryJobId>,
span: Span,
) -> CycleError {
// Find the waitee amongst `current_job` parents
}
#[cfg(parallel_compiler)]
-struct QueryWaiter<D> {
- query: Option<QueryJobId<D>>,
+struct QueryWaiter {
+ query: Option<QueryJobId>,
condvar: Condvar,
span: Span,
cycle: Lock<Option<CycleError>>,
}
#[cfg(parallel_compiler)]
-impl<D> QueryWaiter<D> {
+impl QueryWaiter {
fn notify(&self, registry: &rayon_core::Registry) {
rayon_core::mark_unblocked(registry);
self.condvar.notify_one();
}
#[cfg(parallel_compiler)]
-struct QueryLatchInfo<D> {
+struct QueryLatchInfo {
complete: bool,
- waiters: Vec<Lrc<QueryWaiter<D>>>,
+ waiters: Vec<Lrc<QueryWaiter>>,
}
#[cfg(parallel_compiler)]
#[derive(Clone)]
-pub(super) struct QueryLatch<D> {
- info: Lrc<Mutex<QueryLatchInfo<D>>>,
+pub(super) struct QueryLatch {
+ info: Lrc<Mutex<QueryLatchInfo>>,
}
#[cfg(parallel_compiler)]
-impl<D: Eq + Hash> QueryLatch<D> {
+impl QueryLatch {
fn new() -> Self {
QueryLatch {
info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })),
}
}
-}
-#[cfg(parallel_compiler)]
-impl<D> QueryLatch<D> {
/// Awaits for the query job to complete.
- pub(super) fn wait_on(
- &self,
- query: Option<QueryJobId<D>>,
- span: Span,
- ) -> Result<(), CycleError> {
+ pub(super) fn wait_on(&self, query: Option<QueryJobId>, span: Span) -> Result<(), CycleError> {
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<D>>) {
+ fn wait_on_inner(&self, waiter: &Lrc<QueryWaiter>) {
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<D>> {
+ fn extract_waiter(&self, waiter: usize) -> Lrc<QueryWaiter> {
let mut info = self.info.lock();
debug_assert!(!info.complete);
// Remove the waiter from the list of waiters
/// A resumable waiter of a query. The usize is the index into waiters in the query's latch
#[cfg(parallel_compiler)]
-type Waiter<D> = (QueryJobId<D>, usize);
+type Waiter = (QueryJobId, usize);
/// Visits all the non-resumable and resumable waiters of a query.
/// Only waiters in a query are visited.
/// required information to resume the waiter.
/// If all `visit` calls returns None, this function also returns None.
#[cfg(parallel_compiler)]
-fn visit_waiters<D, F>(
- query_map: &QueryMap<D>,
- query: QueryJobId<D>,
- mut visit: F,
-) -> Option<Option<Waiter<D>>>
+fn visit_waiters<F>(query_map: &QueryMap, query: QueryJobId, mut visit: F) -> Option<Option<Waiter>>
where
- D: Copy + Clone + Eq + Hash,
- F: FnMut(Span, QueryJobId<D>) -> Option<Option<Waiter<D>>>,
+ F: FnMut(Span, QueryJobId) -> Option<Option<Waiter>>,
{
// 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<D>(
- query_map: &QueryMap<D>,
- query: QueryJobId<D>,
+fn cycle_check(
+ query_map: &QueryMap,
+ query: QueryJobId,
span: Span,
- stack: &mut Vec<(Span, QueryJobId<D>)>,
- visited: &mut FxHashSet<QueryJobId<D>>,
-) -> Option<Option<Waiter<D>>>
-where
- D: Copy + Clone + Eq + Hash,
-{
+ stack: &mut Vec<(Span, QueryJobId)>,
+ visited: &mut FxHashSet<QueryJobId>,
+) -> Option<Option<Waiter>> {
if !visited.insert(query) {
return if let Some(p) = stack.iter().position(|q| q.1 == query) {
// We detected a query cycle, fix up the initial span and return Some
/// 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<D>(
- query_map: &QueryMap<D>,
- query: QueryJobId<D>,
- visited: &mut FxHashSet<QueryJobId<D>>,
-) -> bool
-where
- D: Copy + Clone + Eq + Hash,
-{
+fn connected_to_root(
+ query_map: &QueryMap,
+ query: QueryJobId,
+ visited: &mut FxHashSet<QueryJobId>,
+) -> bool {
// We already visited this or we're deliberately ignoring it
if !visited.insert(query) {
return false;
// Deterministically pick an query from a list
#[cfg(parallel_compiler)]
-fn pick_query<'a, D, T, F>(query_map: &QueryMap<D>, queries: &'a [T], f: F) -> &'a T
+fn pick_query<'a, T, F>(query_map: &QueryMap, queries: &'a [T], f: F) -> &'a T
where
- D: Copy + Clone + Eq + Hash,
- F: Fn(&T) -> (Span, QueryJobId<D>),
+ F: Fn(&T) -> (Span, QueryJobId),
{
// 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<D: DepKind>(
- query_map: &QueryMap<D>,
- jobs: &mut Vec<QueryJobId<D>>,
- wakelist: &mut Vec<Lrc<QueryWaiter<D>>>,
+fn remove_cycle(
+ query_map: &QueryMap,
+ jobs: &mut Vec<QueryJobId>,
+ wakelist: &mut Vec<Lrc<QueryWaiter>>,
) -> bool {
let mut visited = FxHashSet::default();
let mut stack = Vec::new();
}
}
})
- .collect::<Vec<(Span, QueryJobId<D>, Option<(Span, QueryJobId<D>)>)>>();
+ .collect::<Vec<(Span, QueryJobId, Option<(Span, QueryJobId)>)>>();
// Deterministically pick an entry point
let (_, entry_point, usage) = pick_query(query_map, &entry_points, |e| (e.0, e.1));
let mut wakelist = Vec::new();
let query_map = tcx.try_collect_active_jobs().unwrap();
- let mut jobs: Vec<QueryJobId<CTX::DepKind>> = query_map.keys().cloned().collect();
+ let mut jobs: Vec<QueryJobId> = query_map.keys().cloned().collect();
let mut found_cycle = false;
pub fn print_query_stack<CTX: QueryContext>(
tcx: CTX,
- mut current_query: Option<QueryJobId<CTX::DepKind>>,
+ mut current_query: Option<QueryJobId>,
handler: &Handler,
num_frames: Option<usize>,
) -> usize {
}
pub trait QueryContext: HasDepContext {
+ fn next_job_id(&self) -> QueryJobId;
+
/// Get the query information from the TLS context.
- fn current_query_job(&self) -> Option<QueryJobId<Self::DepKind>>;
+ fn current_query_job(&self) -> Option<QueryJobId>;
- fn try_collect_active_jobs(&self) -> Option<QueryMap<Self::DepKind>>;
+ fn try_collect_active_jobs(&self) -> Option<QueryMap>;
/// Load side effects associated to the node in the previous session.
fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects;
/// captured during execution and the actual result.
fn start_query<R>(
&self,
- token: QueryJobId<Self::DepKind>,
+ token: QueryJobId,
diagnostics: Option<&Lock<ThinVec<Diagnostic>>>,
compute: impl FnOnce() -> R,
) -> R;
use crate::dep_graph::{DepContext, DepNode, DepNodeIndex, DepNodeParams};
use crate::query::caches::QueryCache;
use crate::query::config::{QueryDescription, QueryVtable};
-use crate::query::job::{
- report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryShardJobId,
-};
+use crate::query::job::{report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo};
use crate::query::{QueryContext, QueryMap, QuerySideEffects, QueryStackFrame};
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::{FxHashMap, FxHasher};
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::mem;
-use std::num::NonZeroU32;
use std::ptr;
pub struct QueryCacheStore<C: QueryCache> {
}
}
-struct QueryStateShard<D, K> {
- active: FxHashMap<K, QueryResult<D>>,
-
- /// Used to generate unique ids for active jobs.
- jobs: u32,
+struct QueryStateShard<K> {
+ active: FxHashMap<K, QueryResult>,
}
-impl<D, K> Default for QueryStateShard<D, K> {
- fn default() -> QueryStateShard<D, K> {
- QueryStateShard { active: Default::default(), jobs: 0 }
+impl<K> Default for QueryStateShard<K> {
+ fn default() -> QueryStateShard<K> {
+ QueryStateShard { active: Default::default() }
}
}
-pub struct QueryState<D, K> {
- shards: Sharded<QueryStateShard<D, K>>,
+pub struct QueryState<K> {
+ shards: Sharded<QueryStateShard<K>>,
}
/// Indicates the state of a query for a given key in a query map.
-enum QueryResult<D> {
+enum QueryResult {
/// An already executing query. The query job can be used to await for its completion.
- Started(QueryJob<D>),
+ Started(QueryJob),
/// The query panicked. Queries trying to wait on this will raise a fatal error which will
/// silently panic.
Poisoned,
}
-impl<D, K> QueryState<D, K>
+impl<K> QueryState<K>
where
- D: Copy + Clone + Eq + Hash,
K: Eq + Hash + Clone + Debug,
{
pub fn all_inactive(&self) -> bool {
pub fn try_collect_active_jobs<CTX: Copy>(
&self,
tcx: CTX,
- kind: D,
make_query: fn(CTX, K) -> QueryStackFrame,
- jobs: &mut QueryMap<D>,
+ jobs: &mut QueryMap,
) -> Option<()> {
// We use try_lock_shards here since we are called from the
// deadlock handler, and this shouldn't be locked.
let shards = self.shards.try_lock_shards()?;
- for (shard_id, shard) in shards.iter().enumerate() {
+ for shard in shards.iter() {
for (k, v) in shard.active.iter() {
if let QueryResult::Started(ref job) = *v {
- let id = QueryJobId::new(job.id, shard_id, kind);
let query = make_query(tcx, k.clone());
- jobs.insert(id, QueryJobInfo { query, job: job.clone() });
+ jobs.insert(job.id, QueryJobInfo { query, job: job.clone() });
}
}
}
}
}
-impl<D, K> Default for QueryState<D, K> {
- fn default() -> QueryState<D, K> {
+impl<K> Default for QueryState<K> {
+ fn default() -> QueryState<K> {
QueryState { shards: 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, D, K>
+struct JobOwner<'tcx, K>
where
- D: Copy + Clone + Eq + Hash,
K: Eq + Hash + Clone,
{
- state: &'tcx QueryState<D, K>,
+ state: &'tcx QueryState<K>,
key: K,
- id: QueryJobId<D>,
+ id: QueryJobId,
}
#[cold]
cache.store_nocache(value)
}
-impl<'tcx, D, K> JobOwner<'tcx, D, K>
+impl<'tcx, K> JobOwner<'tcx, K>
where
- D: Copy + Clone + Eq + Hash,
K: Eq + Hash + Clone,
{
/// Either gets a `JobOwner` corresponding the query, allowing us to
#[inline(always)]
fn try_start<'b, CTX>(
tcx: &'b CTX,
- state: &'b QueryState<CTX::DepKind, K>,
+ state: &'b QueryState<K>,
span: Span,
key: K,
lookup: QueryLookup,
- dep_kind: CTX::DepKind,
- ) -> TryGetJob<'b, CTX::DepKind, K>
+ ) -> TryGetJob<'b, K>
where
CTX: QueryContext,
{
match lock.active.entry(key) {
Entry::Vacant(entry) => {
- // Generate an id unique within this shard.
- let id = lock.jobs.checked_add(1).unwrap();
- lock.jobs = id;
- let id = QueryShardJobId(NonZeroU32::new(id).unwrap());
-
+ let id = tcx.next_job_id();
let job = tcx.current_query_job();
let job = QueryJob::new(id, span, job);
let key = entry.key().clone();
entry.insert(QueryResult::Started(job));
- let global_id = QueryJobId::new(id, shard, dep_kind);
- let owner = JobOwner { state, id: global_id, key };
+ let owner = JobOwner { state, id, key };
return TryGetJob::NotYetStarted(owner);
}
Entry::Occupied(mut entry) => {
match entry.get_mut() {
#[cfg(not(parallel_compiler))]
QueryResult::Started(job) => {
- let id = QueryJobId::new(job.id, shard, dep_kind);
-
+ let id = job.id;
drop(state_lock);
// If we are single-threaded we know that we have cycle error,
}
}
-impl<'tcx, D, K> Drop for JobOwner<'tcx, D, K>
+impl<'tcx, K> Drop for JobOwner<'tcx, K>
where
- D: Copy + Clone + Eq + Hash,
K: Eq + Hash + Clone,
{
#[inline(never)]
}
/// The result of `try_start`.
-enum TryGetJob<'tcx, D, K>
+enum TryGetJob<'tcx, K>
where
- D: Copy + Clone + Eq + Hash,
K: Eq + Hash + Clone,
{
/// The query is not yet started. Contains a guard to the cache eventually used to start it.
- NotYetStarted(JobOwner<'tcx, D, K>),
+ NotYetStarted(JobOwner<'tcx, K>),
/// The query was already completed.
/// Returns the result of the query and its dep-node index
fn try_execute_query<CTX, C>(
tcx: CTX,
- state: &QueryState<CTX::DepKind, C::Key>,
+ state: &QueryState<C::Key>,
cache: &QueryCacheStore<C>,
span: Span,
key: C::Key,
C::Key: Clone + DepNodeParams<CTX::DepContext>,
CTX: QueryContext,
{
- match JobOwner::<'_, CTX::DepKind, C::Key>::try_start(
- &tcx,
- state,
- span,
- key.clone(),
- lookup,
- query.dep_kind,
- ) {
+ match JobOwner::<'_, C::Key>::try_start(&tcx, state, span, key.clone(), lookup) {
TryGetJob::NotYetStarted(job) => {
let (result, dep_node_index) = execute_job(tcx, key, dep_node, query, job.id);
let result = job.complete(cache, result, dep_node_index);
key: K,
mut dep_node_opt: Option<DepNode<CTX::DepKind>>,
query: &QueryVtable<CTX, K, V>,
- job_id: QueryJobId<CTX::DepKind>,
+ job_id: QueryJobId,
) -> (V, DepNodeIndex)
where
K: Clone + DepNodeParams<CTX::DepContext>,
_,
)
| Res::Local(..)
- | Res::SelfTy(..)
+ | Res::SelfTy { .. }
| Res::SelfCtor(..)
| Res::Err => bug!("unexpected resolution: {:?}", res),
}
let sm = self.session.source_map();
match outer_res {
- Res::SelfTy(maybe_trait_defid, maybe_impl_defid) => {
+ Res::SelfTy { trait_: maybe_trait_defid, alias_to: maybe_impl_defid } => {
if let Some(impl_span) =
maybe_impl_defid.and_then(|(def_id, _)| self.opt_span(def_id))
{
// edit:
// only do this if the const and usage of the non-constant value are on the same line
// the further the two are apart, the higher the chance of the suggestion being wrong
- // also make sure that the pos for the suggestion is not 0 (ICE #90878)
- let sp =
- self.session.source_map().span_extend_to_prev_str(ident.span, current, true);
-
- let pos_for_suggestion = sp.lo().0.saturating_sub(current.len() as u32);
+ let sp = self
+ .session
+ .source_map()
+ .span_extend_to_prev_str(ident.span, current, true, false);
- if sp.lo().0 == 0
- || pos_for_suggestion == 0
- || self.session.source_map().is_multiline(sp)
- {
- err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
- } else {
- let sp = sp.with_lo(BytePos(pos_for_suggestion));
- err.span_suggestion(
- sp,
- &format!("consider using `{}` instead of `{}`", sugg, current),
- format!("{} {}", sugg, ident),
- Applicability::MaybeIncorrect,
- );
- err.span_label(span, "non-constant value");
+ match sp {
+ Some(sp) if !self.session.source_map().is_multiline(sp) => {
+ let sp = sp.with_lo(BytePos(sp.lo().0 - (current.len() as u32)));
+ err.span_suggestion(
+ sp,
+ &format!("consider using `{}` instead of `{}`", sugg, current),
+ format!("{} {}", sugg, ident),
+ Applicability::MaybeIncorrect,
+ );
+ err.span_label(span, "non-constant value");
+ }
+ _ => {
+ err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
+ }
}
+
err
}
ResolutionError::BindingShadowsSomethingUnacceptable {
use rustc_ast::NodeId;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::ptr_key::PtrKey;
+use rustc_data_structures::intern::Interned;
use rustc_errors::{pluralize, struct_span_err, Applicability};
use rustc_hir::def::{self, PartialRes};
use rustc_hir::def_id::DefId;
pub struct NameResolution<'a> {
/// Single imports that may define the name in the namespace.
/// Imports are arena-allocated, so it's ok to use pointers as keys.
- single_imports: FxHashSet<PtrKey<'a, Import<'a>>>,
+ single_imports: FxHashSet<Interned<'a, Import<'a>>>,
/// The least shadowable known binding for this name, or None if there are no known bindings.
pub binding: Option<&'a NameBinding<'a>>,
shadowed_glob: Option<&'a NameBinding<'a>>,
}
crate fn add_single_import(&mut self, import: &'a Import<'a>) {
- self.single_imports.insert(PtrKey(import));
+ self.single_imports.insert(Interned::new_unchecked(import));
}
}
Err(Determined) => {
let key = this.new_key(target, ns);
this.update_resolution(parent, key, |_, resolution| {
- resolution.single_imports.remove(&PtrKey(import));
+ resolution.single_imports.remove(&Interned::new_unchecked(import));
});
}
Ok(binding) if !binding.is_importable() => {
| DefKind::ForeignTy,
_,
) | Res::PrimTy(..)
- | Res::SelfTy(..)
+ | Res::SelfTy { .. }
),
PathSource::Trait(AliasPossibility::No) => matches!(res, Res::Def(DefKind::Trait, _)),
PathSource::Trait(AliasPossibility::Maybe) => {
| DefKind::TyAlias
| DefKind::AssocTy,
_,
- ) | Res::SelfTy(..)
+ ) | Res::SelfTy { .. }
),
PathSource::TraitItem(ns) => match res {
Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) if ns == ValueNS => true,
self.with_current_self_item(item, |this| {
this.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
let item_def_id = this.r.local_def_id(item.id).to_def_id();
- this.with_self_rib(Res::SelfTy(None, Some((item_def_id, false))), |this| {
- visit::walk_item(this, item);
- });
+ this.with_self_rib(
+ Res::SelfTy { trait_: None, alias_to: Some((item_def_id, false)) },
+ |this| {
+ visit::walk_item(this, item);
+ },
+ );
});
});
}
self.compute_num_lifetime_params(item.id, generics);
// Create a new rib for the trait-wide type parameters.
self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
- let local_def_id = this.r.local_def_id(item.id).to_def_id();
- this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| {
+ let def = this.r.local_def_id(item.id).to_def_id();
+ this.with_self_rib(Res::SelfTy { trait_: Some(def), alias_to: None }, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds);
self.compute_num_lifetime_params(item.id, generics);
// Create a new rib for the trait-wide type parameters.
self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
- let local_def_id = this.r.local_def_id(item.id).to_def_id();
- this.with_self_rib(Res::SelfTy(Some(local_def_id), None), |this| {
+ let def = this.r.local_def_id(item.id).to_def_id();
+ this.with_self_rib(Res::SelfTy { trait_: Some(def), alias_to: None }, |this| {
this.visit_generics(generics);
walk_list!(this, visit_param_bound, bounds);
});
// If applicable, create a rib for the type parameters.
self.with_generic_param_rib(generics, ItemRibKind(HasGenericParams::Yes), |this| {
// Dummy self type for better errors if `Self` is used in the trait path.
- this.with_self_rib(Res::SelfTy(None, None), |this| {
+ this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
// Resolve the trait reference, if necessary.
this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
let item_def_id = this.r.local_def_id(item_id);
}
let item_def_id = item_def_id.to_def_id();
- this.with_self_rib(Res::SelfTy(trait_id, Some((item_def_id, false))), |this| {
+ let res =
+ Res::SelfTy { trait_: trait_id, alias_to: Some((item_def_id, false)) };
+ this.with_self_rib(res, |this| {
if let Some(trait_ref) = opt_trait_reference.as_ref() {
// Resolve type arguments in the trait path.
visit::walk_trait_ref(this, trait_ref);
) = &bounded_ty.kind
{
// use this to verify that ident is a type param.
- let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
+ let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
bounded_ty.id,
None,
&Segment::from_path(path),
span,
true,
CrateLint::No,
- ) {
- partial_res
- } else {
+ ) else {
return false;
};
if !(matches!(
if let ast::TyKind::Path(None, type_param_path) = &ty.peel_refs().kind {
// Confirm that the `SelfTy` is a type parameter.
- let partial_res = if let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
+ let Ok(Some(partial_res)) = self.resolve_qpath_anywhere(
bounded_ty.id,
None,
&Segment::from_path(type_param_path),
span,
true,
CrateLint::No,
- ) {
- partial_res
- } else {
+ ) else {
return false;
};
if !(matches!(
Applicability::HasPlaceholders,
);
}
- (Res::SelfTy(..), _) if ns == ValueNS => {
+ (Res::SelfTy { .. }, _) if ns == ValueNS => {
err.span_label(span, fallback_label);
err.note("can't use `Self` as a constructor, you must use the implemented struct");
}
}
trait RegionExt {
- fn early(hir_map: &Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region);
+ fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region);
- fn late(index: u32, hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region);
+ fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region);
fn late_anon(named_late_bound_vars: u32, index: &Cell<u32>) -> Region;
}
impl RegionExt for Region {
- fn early(hir_map: &Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region) {
+ fn early(hir_map: Map<'_>, index: &mut u32, param: &GenericParam<'_>) -> (ParamName, Region) {
let i = *index;
*index += 1;
let def_id = hir_map.local_def_id(param.hir_id);
(param.name.normalize_to_macros_2_0(), Region::EarlyBound(i, def_id.to_def_id(), origin))
}
- fn late(idx: u32, hir_map: &Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) {
+ fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (ParamName, Region) {
let depth = ty::INNERMOST;
let def_id = hir_map.local_def_id(param.hir_id);
let origin = LifetimeDefOrigin::from_param(param);
named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id),
is_late_bound_map,
- object_lifetime_defaults_map: |tcx, id| match tcx.hir().find_by_def_id(id) {
+ object_lifetime_defaults: |tcx, id| match tcx.hir().find_by_def_id(id) {
Some(Node::Item(item)) => compute_object_lifetime_defaults(tcx, item),
_ => None,
},
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
- Some(Region::early(&self.tcx.hir(), &mut index, param))
+ Some(Region::early(self.tcx.hir(), &mut index, param))
}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
non_lifetime_count += 1;
.filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
.enumerate()
.map(|(late_bound_idx, param)| {
- let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param);
+ let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
let r = late_region_as_bound_region(self.tcx, &pair.1);
(pair, r)
})
for param in generics.params {
match param.kind {
GenericParamKind::Lifetime { .. } => {
- let (name, reg) = Region::early(&self.tcx.hir(), &mut index, ¶m);
+ let (name, reg) = Region::early(self.tcx.hir(), &mut index, ¶m);
let Region::EarlyBound(_, def_id, _) = reg else {
bug!();
};
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
- Some(Region::early(&self.tcx.hir(), &mut index, param))
+ Some(Region::early(self.tcx.hir(), &mut index, param))
}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
non_lifetime_count += 1;
.iter()
.filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => {
- Some(Region::early(&self.tcx.hir(), &mut index, param))
+ Some(Region::early(self.tcx.hir(), &mut index, param))
}
GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => {
non_lifetime_count += 1;
this.visit_ty(&ty);
}
}
- GenericParamKind::Const { ref ty, .. } => {
+ GenericParamKind::Const { ref ty, default } => {
let was_in_const_generic = this.is_in_const_generic;
this.is_in_const_generic = true;
walk_list!(this, visit_param_bound, param.bounds);
this.visit_ty(&ty);
+ if let Some(default) = default {
+ this.visit_body(this.tcx.hir().body(default.body));
+ }
this.is_in_const_generic = was_in_const_generic;
}
}
.enumerate()
.map(|(late_bound_idx, param)| {
let pair =
- Region::late(late_bound_idx as u32, &this.tcx.hir(), param);
+ Region::late(late_bound_idx as u32, this.tcx.hir(), param);
let r = late_region_as_bound_region(this.tcx, &pair.1);
(pair, r)
})
.filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
.enumerate()
.map(|(late_bound_idx, param)| {
- let pair = Region::late(
- initial_bound_vars + late_bound_idx as u32,
- &self.tcx.hir(),
- param,
- );
+ let pair =
+ Region::late(initial_bound_vars + late_bound_idx as u32, self.tcx.hir(), param);
let r = late_region_as_bound_region(self.tcx, &pair.1);
lifetimes.insert(pair.0, pair.1);
r
}
}
-fn compute_object_lifetime_defaults(
- tcx: TyCtxt<'_>,
+fn compute_object_lifetime_defaults<'tcx>(
+ tcx: TyCtxt<'tcx>,
item: &hir::Item<'_>,
-) -> Option<Vec<ObjectLifetimeDefault>> {
+) -> Option<&'tcx [ObjectLifetimeDefault]> {
match item.kind {
hir::ItemKind::Struct(_, ref generics)
| hir::ItemKind::Union(_, ref generics)
/// Scan the bounds and where-clauses on parameters to extract bounds
/// of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
/// for each type parameter.
-fn object_lifetime_defaults_for_item(
- tcx: TyCtxt<'_>,
+fn object_lifetime_defaults_for_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
generics: &hir::Generics<'_>,
-) -> Vec<ObjectLifetimeDefault> {
+) -> &'tcx [ObjectLifetimeDefault] {
fn add_bounds(set: &mut Set1<hir::LifetimeName>, bounds: &[hir::GenericBound<'_>]) {
for bound in bounds {
if let hir::GenericBound::Outlives(ref lifetime) = *bound {
}
}
- generics
- .params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => None,
- GenericParamKind::Type { .. } => {
- let mut set = Set1::Empty;
-
- add_bounds(&mut set, ¶m.bounds);
-
- let param_def_id = tcx.hir().local_def_id(param.hir_id);
- for predicate in generics.where_clause.predicates {
- // Look for `type: ...` where clauses.
- let data = match *predicate {
- hir::WherePredicate::BoundPredicate(ref data) => data,
- _ => continue,
- };
+ let process_param = |param: &hir::GenericParam<'_>| match param.kind {
+ GenericParamKind::Lifetime { .. } => None,
+ GenericParamKind::Type { .. } => {
+ let mut set = Set1::Empty;
- // Ignore `for<'a> type: ...` as they can change what
- // lifetimes mean (although we could "just" handle it).
- if !data.bound_generic_params.is_empty() {
- continue;
- }
+ add_bounds(&mut set, ¶m.bounds);
- let res = match data.bounded_ty.kind {
- hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.res,
- _ => continue,
- };
+ let param_def_id = tcx.hir().local_def_id(param.hir_id);
+ for predicate in generics.where_clause.predicates {
+ // Look for `type: ...` where clauses.
+ let data = match *predicate {
+ hir::WherePredicate::BoundPredicate(ref data) => data,
+ _ => continue,
+ };
- if res == Res::Def(DefKind::TyParam, param_def_id.to_def_id()) {
- add_bounds(&mut set, &data.bounds);
- }
+ // Ignore `for<'a> type: ...` as they can change what
+ // lifetimes mean (although we could "just" handle it).
+ if !data.bound_generic_params.is_empty() {
+ continue;
}
- Some(match set {
- Set1::Empty => Set1::Empty,
- Set1::One(name) => {
- if name == hir::LifetimeName::Static {
- Set1::One(Region::Static)
- } else {
- generics
- .params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => Some((
- param.hir_id,
- hir::LifetimeName::Param(param.name),
- LifetimeDefOrigin::from_param(param),
- )),
- _ => None,
- })
- .enumerate()
- .find(|&(_, (_, lt_name, _))| lt_name == name)
- .map_or(Set1::Many, |(i, (id, _, origin))| {
- let def_id = tcx.hir().local_def_id(id);
- Set1::One(Region::EarlyBound(
- i as u32,
- def_id.to_def_id(),
- origin,
- ))
- })
- }
- }
- Set1::Many => Set1::Many,
- })
- }
- GenericParamKind::Const { .. } => {
- // Generic consts don't impose any constraints.
- //
- // We still store a dummy value here to allow generic parameters
- // in an arbitrary order.
- Some(Set1::Empty)
+ let res = match data.bounded_ty.kind {
+ hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => path.res,
+ _ => continue,
+ };
+
+ if res == Res::Def(DefKind::TyParam, param_def_id.to_def_id()) {
+ add_bounds(&mut set, &data.bounds);
+ }
}
- })
- .collect()
+
+ Some(match set {
+ Set1::Empty => Set1::Empty,
+ Set1::One(name) => {
+ if name == hir::LifetimeName::Static {
+ Set1::One(Region::Static)
+ } else {
+ generics
+ .params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => Some((
+ param.hir_id,
+ hir::LifetimeName::Param(param.name),
+ LifetimeDefOrigin::from_param(param),
+ )),
+ _ => None,
+ })
+ .enumerate()
+ .find(|&(_, (_, lt_name, _))| lt_name == name)
+ .map_or(Set1::Many, |(i, (id, _, origin))| {
+ let def_id = tcx.hir().local_def_id(id);
+ Set1::One(Region::EarlyBound(i as u32, def_id.to_def_id(), origin))
+ })
+ }
+ }
+ Set1::Many => Set1::Many,
+ })
+ }
+ GenericParamKind::Const { .. } => {
+ // Generic consts don't impose any constraints.
+ //
+ // We still store a dummy value here to allow generic parameters
+ // in an arbitrary order.
+ Some(Set1::Empty)
+ }
+ };
+
+ tcx.arena.alloc_from_iter(generics.params.iter().filter_map(process_param))
}
impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
if self.map.late_bound.contains(¶m.hir_id) {
let late_bound_idx = named_late_bound_vars;
named_late_bound_vars += 1;
- Some(Region::late(late_bound_idx, &self.tcx.hir(), param))
+ Some(Region::late(late_bound_idx, self.tcx.hir(), param))
} else {
- Some(Region::early(&self.tcx.hir(), &mut next_early_index, param))
+ Some(Region::early(self.tcx.hir(), &mut next_early_index, param))
}
}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
})
.enumerate()
.map(|(late_bound_idx, param)| {
- let pair = Region::late(late_bound_idx as u32, &self.tcx.hir(), param);
+ let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
late_region_as_bound_region(self.tcx, &pair.1)
})
.collect();
};
if let Some(def_id) = def_id.as_local() {
let id = self.tcx.hir().local_def_id_to_hir_id(def_id);
- self.tcx.object_lifetime_defaults(id).unwrap().iter().map(set_to_region).collect()
+ self.tcx
+ .object_lifetime_defaults(id.owner)
+ .unwrap()
+ .iter()
+ .map(set_to_region)
+ .collect()
} else {
let tcx = self.tcx;
self.xcrate_object_lifetime_defaults
// Look for `self: &'a Self` - also desugared from `&'a self`,
// and if that matches, use it for elision and return early.
fn is_self_ty(&self, res: Res) -> bool {
- if let Res::SelfTy(..) = res {
+ if let Res::SelfTy { .. } = res {
return true;
}
use rustc_ast_lowering::ResolverAstLowering;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_data_structures::ptr_key::PtrKey;
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
/// language items.
empty_module: Module<'a>,
module_map: FxHashMap<DefId, Module<'a>>,
- binding_parent_modules: FxHashMap<PtrKey<'a, NameBinding<'a>>, Module<'a>>,
+ binding_parent_modules: FxHashMap<Interned<'a, NameBinding<'a>>, Module<'a>>,
underscore_disambiguator: u32,
/// Maps glob imports to the names of items actually imported.
self.name_resolutions.alloc(Default::default())
}
fn alloc_macro_rules_scope(&'a self, scope: MacroRulesScope<'a>) -> MacroRulesScopeRef<'a> {
- PtrKey(self.dropless.alloc(Cell::new(scope)))
+ Interned::new_unchecked(self.dropless.alloc(Cell::new(scope)))
}
fn alloc_macro_rules_binding(
&'a self,
return Res::Err;
}
}
- Res::Def(DefKind::TyParam, _) | Res::SelfTy(..) => {
+ Res::Def(DefKind::TyParam, _) | Res::SelfTy { .. } => {
for rib in ribs {
let has_generic_params: HasGenericParams = match rib.kind {
NormalRibKind
// HACK(min_const_generics): If we encounter `Self` in an anonymous constant
// we can't easily tell if it's generic at this stage, so we instead remember
// this and then enforce the self type to be concrete later on.
- if let Res::SelfTy(trait_def, Some((impl_def, _))) = res {
- res = Res::SelfTy(trait_def, Some((impl_def, true)));
+ if let Res::SelfTy { trait_, alias_to: Some((def, _)) } = res {
+ res = Res::SelfTy { trait_, alias_to: Some((def, true)) }
} else {
if record_used {
self.report_error(
}
fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) {
- if let Some(old_module) = self.binding_parent_modules.insert(PtrKey(binding), module) {
+ if let Some(old_module) =
+ self.binding_parent_modules.insert(Interned::new_unchecked(binding), module)
+ {
if !ptr::eq(module, old_module) {
span_bug!(binding.span, "parent module is reset for binding");
}
// is disambiguated to mitigate regressions from macro modularization.
// Scoping for `macro_rules` behaves like scoping for `let` at module level, in general.
match (
- self.binding_parent_modules.get(&PtrKey(macro_rules)),
- self.binding_parent_modules.get(&PtrKey(modularized)),
+ self.binding_parent_modules.get(&Interned::new_unchecked(macro_rules)),
+ self.binding_parent_modules.get(&Interned::new_unchecked(modularized)),
) {
(Some(macro_rules), Some(modularized)) => {
macro_rules.nearest_parent_mod() == modularized.nearest_parent_mod()
use rustc_ast_pretty::pprust;
use rustc_attr::StabilityLevel;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::ptr_key::PtrKey;
+use rustc_data_structures::intern::Interned;
use rustc_data_structures::sync::Lrc;
use rustc_errors::struct_span_err;
use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand};
/// This helps to avoid uncontrollable growth of `macro_rules!` scope chains,
/// which usually grow lineraly with the number of macro invocations
/// in a module (including derives) and hurt performance.
-pub(crate) type MacroRulesScopeRef<'a> = PtrKey<'a, Cell<MacroRulesScope<'a>>>;
+pub(crate) type MacroRulesScopeRef<'a> = Interned<'a, Cell<MacroRulesScope<'a>>>;
// Macro namespace is separated into two sub-namespaces, one for bang macros and
// one for attribute-like macros (attributes, derives).
) {
debug!("process_method: {:?}:{}", def_id, ident);
- let map = &self.tcx.hir();
+ let map = self.tcx.hir();
let hir_id = map.local_def_id_to_hir_id(def_id);
self.nest_typeck_results(def_id, |v| {
if let Some(mut method_data) = v.save_ctxt.get_method_data(hir_id, ident, span) {
ty_params: &'tcx hir::Generics<'tcx>,
body: hir::BodyId,
) {
- let map = &self.tcx.hir();
+ let map = self.tcx.hir();
self.nest_typeck_results(item.def_id, |v| {
let body = map.body(body);
if let Some(fn_data) = v.save_ctxt.get_item_data(item) {
}
}
- let map = &self.tcx.hir();
+ let map = self.tcx.hir();
self.nest_typeck_results(item.def_id, |v| {
v.visit_ty(&impl_.self_ty);
if let Some(trait_ref) = &impl_.of_trait {
// walk generics and methods
self.process_generic_params(generics, &qualname, item.hir_id());
for method in methods {
- let map = &self.tcx.hir();
+ let map = self.tcx.hir();
self.process_trait_item(map.trait_item(method.id), item.def_id.to_def_id())
}
}
| HirDefKind::AssocTy,
_,
)
- | Res::SelfTy(..) => {
+ | Res::SelfTy { .. } => {
self.dump_path_segment_ref(id, &hir::PathSegment::from_ident(ident));
}
def => {
_,
)
| Res::PrimTy(..)
- | Res::SelfTy(..)
+ | Res::SelfTy { .. }
| Res::ToolMod
| Res::NonMacroAttr(..)
| Res::SelfCtor(..)
fn lookup_def_id(&self, ref_id: hir::HirId) -> Option<DefId> {
match self.get_path_res(ref_id) {
- Res::PrimTy(_) | Res::SelfTy(..) | Res::Err => None,
+ Res::PrimTy(_) | Res::SelfTy { .. } | Res::Err => None,
def => def.opt_def_id(),
}
}
let res = scx.get_path_res(id.ok_or("Missing id for Path")?);
let (name, start, end) = match res {
- Res::PrimTy(..) | Res::SelfTy(..) | Res::Err => {
+ Res::PrimTy(..) | Res::SelfTy { .. } | Res::Err => {
return Ok(Signature { text: path_to_string(self), defs: vec![], refs: vec![] });
}
Res::Def(DefKind::AssocConst | DefKind::Variant | DefKind::Ctor(..), _) => {
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
-use std::io;
-use std::io::prelude::*;
use std::mem::swap;
use std::num::FpCategory as Fp;
use std::ops::Index;
pub enum ParserError {
/// msg, line, col
SyntaxError(ErrorCode, usize, usize),
- IoError(io::ErrorKind, String),
}
// Builder and Parser have the same errors.
}
}
-fn io_error_to_error(io: io::Error) -> ParserError {
- IoError(io.kind(), io.to_string())
-}
-
impl fmt::Display for ParserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// FIXME this should be a nicer error
}
}
-/// Decodes a json value from an `&mut io::Read`
-pub fn from_reader(rdr: &mut dyn Read) -> Result<Json, BuilderError> {
- let mut contents = Vec::new();
- match rdr.read_to_end(&mut contents) {
- Ok(c) => c,
- Err(e) => return Err(io_error_to_error(e)),
- };
- let s = match str::from_utf8(&contents).ok() {
- Some(s) => s,
- _ => return Err(SyntaxError(NotUtf8, 0, 0)),
- };
- let mut builder = Builder::new(s.chars());
- builder.build()
-}
-
/// Decodes a json value from a string
pub fn from_str(s: &str) -> Result<Json, BuilderError> {
let mut builder = Builder::new(s.chars());
Checks,
}
+/// The different settings that the `-Z cf-protection` flag can have.
+#[derive(Clone, Copy, PartialEq, Hash, Debug)]
+pub enum CFProtection {
+ /// Do not enable control-flow protection
+ None,
+
+ /// Emit control-flow protection for branches (enables indirect branch tracking).
+ Branch,
+
+ /// Emit control-flow protection for returns.
+ Return,
+
+ /// Emit control-flow protection for both branches and returns.
+ Full,
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Hash)]
pub enum OptLevel {
No, // -O0
Block,
}
-/// The different settings that the `-Z instrument-coverage` flag can have.
+/// The different settings that the `-C instrument-coverage` flag can have.
///
-/// Coverage instrumentation now supports combining `-Z instrument-coverage`
+/// Coverage instrumentation now supports combining `-C instrument-coverage`
/// with compiler and linker optimization (enabled with `-O` or `-C opt-level=1`
/// and higher). Nevertheless, there are many variables, depending on options
/// selected, code structure, and enabled attributes. If errors are encountered,
/// either while compiling or when generating `llvm-cov show` reports, consider
/// lowering the optimization level, including or excluding `-C link-dead-code`,
-/// or using `-Z instrument-coverage=except-unused-functions` or `-Z
-/// instrument-coverage=except-unused-generics`.
+/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions`
+/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`.
///
/// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the
/// coverage map, it will not attempt to generate synthetic functions for unused
/// unless the function has type parameters.
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
pub enum InstrumentCoverage {
- /// Default `-Z instrument-coverage` or `-Z instrument-coverage=statement`
+ /// Default `-C instrument-coverage` or `-C instrument-coverage=statement`
All,
- /// `-Z instrument-coverage=except-unused-generics`
+ /// `-Zunstable-options -C instrument-coverage=except-unused-generics`
ExceptUnusedGenerics,
- /// `-Z instrument-coverage=except-unused-functions`
+ /// `-Zunstable-options -C instrument-coverage=except-unused-functions`
ExceptUnusedFunctions,
- /// `-Z instrument-coverage=off` (or `no`, etc.)
+ /// `-C instrument-coverage=off` (or `no`, etc.)
Off,
}
externs: Externs(BTreeMap::new()),
extern_dep_specs: ExternDepSpecs(BTreeMap::new()),
crate_name: None,
- alt_std_name: None,
libs: Vec::new(),
unstable_features: UnstableFeatures::Disallow,
debug_assertions: true,
_ => {}
}
- if debugging_opts.instrument_coverage.is_some()
- && debugging_opts.instrument_coverage != Some(InstrumentCoverage::Off)
- {
+ // Handle both `-Z instrument-coverage` and `-C instrument-coverage`; the latter takes
+ // precedence.
+ match (cg.instrument_coverage, debugging_opts.instrument_coverage) {
+ (Some(ic_c), Some(ic_z)) if ic_c != ic_z => {
+ early_error(
+ error_format,
+ "incompatible values passed for `-C instrument-coverage` \
+ and `-Z instrument-coverage`",
+ );
+ }
+ (Some(InstrumentCoverage::Off | InstrumentCoverage::All), _) => {}
+ (Some(_), _) if !debugging_opts.unstable_options => {
+ early_error(
+ error_format,
+ "`-C instrument-coverage=except-*` requires `-Z unstable-options`",
+ );
+ }
+ (None, None) => {}
+ (None, ic) => {
+ early_warn(
+ error_format,
+ "`-Z instrument-coverage` is deprecated; use `-C instrument-coverage`",
+ );
+ cg.instrument_coverage = ic;
+ }
+ _ => {}
+ }
+
+ if cg.instrument_coverage.is_some() && cg.instrument_coverage != Some(InstrumentCoverage::Off) {
if cg.profile_generate.enabled() || cg.profile_use.is_some() {
early_error(
error_format,
- "option `-Z instrument-coverage` is not compatible with either `-C profile-use` \
+ "option `-C instrument-coverage` is not compatible with either `-C profile-use` \
or `-C profile-generate`",
);
}
- // `-Z instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent
+ // `-C instrument-coverage` implies `-C symbol-mangling-version=v0` - to ensure consistent
// and reversible name mangling. Note, LLVM coverage tools can analyze coverage over
// multiple runs, including some changes to source code; so mangled names must be consistent
// across compilations.
Some(SymbolManglingVersion::Legacy) => {
early_warn(
error_format,
- "-Z instrument-coverage requires symbol mangling version `v0`, \
+ "-C instrument-coverage requires symbol mangling version `v0`, \
but `-C symbol-mangling-version=legacy` was specified",
);
}
unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
extern_dep_specs,
crate_name,
- alt_std_name: None,
libs,
debug_assertions,
actually_rustdoc: false,
/// we have an opt-in scheme here, so one is hopefully forced to think about
/// how the hash should be calculated when adding a new command-line argument.
crate mod dep_tracking {
- use super::LdImpl;
use super::{
- BranchProtection, CFGuard, CrateType, DebugInfo, ErrorOutputType, InstrumentCoverage,
- LinkerPluginLto, LocationDetail, LtoCli, OptLevel, OutputType, OutputTypes, Passes,
- SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion, TrimmedDefPaths,
+ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, ErrorOutputType,
+ InstrumentCoverage, LdImpl, LinkerPluginLto, LocationDetail, LtoCli, OptLevel, OutputType,
+ OutputTypes, Passes, SourceFileHashAlgorithm, SwitchWithOptPath, SymbolManglingVersion,
+ TrimmedDefPaths,
};
use crate::lint;
use crate::options::WasiExecModel;
NativeLibKind,
SanitizerSet,
CFGuard,
+ CFProtection,
TargetTriple,
Edition,
LinkerPluginLto,
//! A module for searching for libraries
-pub use self::FileMatch::*;
-
use std::env;
use std::fs;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
-use crate::search_paths::{PathKind, SearchPath, SearchPathFile};
+use crate::search_paths::{PathKind, SearchPath};
use rustc_fs_util::fix_windows_verbatim_for_gcc;
use tracing::debug;
self.get_lib_path().join("self-contained")
}
- pub fn search<F>(&self, mut pick: F)
- where
- F: FnMut(&SearchPathFile, PathKind) -> FileMatch,
- {
- for search_path in self.search_paths() {
- debug!("searching {}", search_path.dir.display());
- fn is_rlib(spf: &SearchPathFile) -> bool {
- if let Some(f) = &spf.file_name_str { f.ends_with(".rlib") } else { false }
- }
- // Reading metadata out of rlibs is faster, and if we find both
- // an rlib and a dylib we only read one of the files of
- // metadata, so in the name of speed, bring all rlib files to
- // the front of the search list.
- let files1 = search_path.files.iter().filter(|spf| is_rlib(&spf));
- let files2 = search_path.files.iter().filter(|spf| !is_rlib(&spf));
- for spf in files1.chain(files2) {
- debug!("testing {}", spf.path.display());
- let maybe_picked = pick(spf, search_path.kind);
- match maybe_picked {
- FileMatches => {
- debug!("picked {}", spf.path.display());
- }
- FileDoesntMatch => {
- debug!("rejected {}", spf.path.display());
- }
- }
- }
- }
- }
-
pub fn new(
sysroot: &'a Path,
triple: &'a str,
}
pub fn instrument_coverage(&self) -> bool {
- self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
- != InstrumentCoverage::Off
+ self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off) != InstrumentCoverage::Off
}
pub fn instrument_coverage_except_unused_generics(&self) -> bool {
- self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
+ self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
== InstrumentCoverage::ExceptUnusedGenerics
}
pub fn instrument_coverage_except_unused_functions(&self) -> bool {
- self.debugging_opts.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
+ self.cg.instrument_coverage.unwrap_or(InstrumentCoverage::Off)
== InstrumentCoverage::ExceptUnusedFunctions
}
}
externs: Externs [UNTRACKED],
extern_dep_specs: ExternDepSpecs [UNTRACKED],
crate_name: Option<String> [TRACKED],
- /// An optional name to use as the crate for std during std injection,
- /// written `extern crate name as std`. Defaults to `std`. Used by
- /// out-of-tree drivers.
- alt_std_name: Option<String> [TRACKED],
/// Indicates how the compiler should treat unstable features.
unstable_features: UnstableFeatures [TRACKED],
pub const parse_sanitizer_memory_track_origins: &str = "0, 1, or 2";
pub const parse_cfguard: &str =
"either a boolean (`yes`, `no`, `on`, `off`, etc), `checks`, or `nochecks`";
+ pub const parse_cfprotection: &str = "`none`|`no`|`n` (default), `branch`, `return`, or `full`|`yes`|`y` (equivalent to `branch` and `return`)";
pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavor::one_of();
pub const parse_optimization_fuel: &str = "crate=integer";
true
}
+ crate fn parse_cfprotection(slot: &mut CFProtection, v: Option<&str>) -> bool {
+ if v.is_some() {
+ let mut bool_arg = None;
+ if parse_opt_bool(&mut bool_arg, v) {
+ *slot = if bool_arg.unwrap() { CFProtection::Full } else { CFProtection::None };
+ return true;
+ }
+ }
+
+ *slot = match v {
+ None | Some("none") => CFProtection::None,
+ Some("branch") => CFProtection::Branch,
+ Some("return") => CFProtection::Return,
+ Some("full") => CFProtection::Full,
+ Some(_) => return false,
+ };
+ true
+ }
+
crate fn parse_linker_flavor(slot: &mut Option<LinkerFlavor>, v: Option<&str>) -> bool {
match v.and_then(LinkerFlavor::from_str) {
Some(lf) => *slot = Some(lf),
"enable incremental compilation"),
inline_threshold: Option<u32> = (None, parse_opt_number, [TRACKED],
"set the threshold for inlining a function"),
+ instrument_coverage: Option<InstrumentCoverage> = (None, parse_instrument_coverage, [TRACKED],
+ "instrument the generated code to support LLVM source-based code coverage \
+ reports (note, the compiler build config must include `profiler = true`); \
+ implies `-C symbol-mangling-version=v0`. Optional values are:
+ `=all` (implicit value)
+ `=except-unused-generics`
+ `=except-unused-functions`
+ `=off` (default)"),
link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED],
"a single extra argument to append to the linker invocation (can be used several times)"),
link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
"select which borrowck is used (`mir` or `migrate`) (default: `migrate`)"),
branch_protection: BranchProtection = (BranchProtection::default(), parse_branch_protection, [TRACKED],
"set options for branch target identification and pointer authentication on AArch64"),
+ cf_protection: CFProtection = (CFProtection::None, parse_cfprotection, [TRACKED],
+ "instrument control-flow architecture protection"),
cgu_partitioning_strategy: Option<String> = (None, parse_opt_string, [TRACKED],
"the codegen unit partitioning strategy to use"),
chalk: bool = (false, parse_bool, [TRACKED],
dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
"emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
(default: no)"),
+ drop_tracking: bool = (false, parse_bool, [TRACKED],
+ "enables drop tracking in generators (default: no)"),
dual_proc_macros: bool = (false, parse_bool, [TRACKED],
"load proc macros for both target and host, but only link to the target (default: no)"),
dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"use the given `.prof` file for sampled profile-guided optimization (also known as AutoFDO)"),
query_dep_graph: bool = (false, parse_bool, [UNTRACKED],
"enable queries of the dependency graph for regression testing (default: no)"),
- query_stats: bool = (false, parse_bool, [UNTRACKED],
- "print some statistics about the query system (default: no)"),
randomize_layout: bool = (false, parse_bool, [TRACKED],
"randomize the layout of types (default: no)"),
layout_seed: Option<u64> = (None, parse_opt_number, [TRACKED],
/// doable, but very slow, because it involves calls to `file_name` and
/// `extension` that are themselves slow.
///
-/// This type augments the `PathBuf` with an `Option<String>` containing the
+/// This type augments the `PathBuf` with an `String` containing the
/// `PathBuf`'s filename. The prefix and suffix checking is much faster on the
-/// `Option<String>` than the `PathBuf`. (It's an `Option` because
-/// `Path::file_name` can fail; if that happens then all subsequent checking
-/// will also fail, which is fine.)
+/// `String` than the `PathBuf`. (The filename must be valid UTF-8. If it's
+/// not, the entry should be skipped, because all Rust output files are valid
+/// UTF-8, and so a non-UTF-8 filename couldn't be one we're looking for.)
#[derive(Clone, Debug)]
pub struct SearchPathFile {
pub path: PathBuf,
- pub file_name_str: Option<String>,
-}
-
-impl SearchPathFile {
- fn new(path: PathBuf) -> SearchPathFile {
- let file_name_str = path.file_name().and_then(|f| f.to_str()).map(|s| s.to_string());
- SearchPathFile { path, file_name_str }
- }
+ pub file_name_str: String,
}
#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq, Encodable, Decodable)]
// Get the files within the directory.
let files = match std::fs::read_dir(&dir) {
Ok(files) => files
- .filter_map(|e| e.ok().map(|e| SearchPathFile::new(e.path())))
+ .filter_map(|e| {
+ e.ok().and_then(|e| {
+ e.file_name().to_str().map(|s| SearchPathFile {
+ path: e.path(),
+ file_name_str: s.to_string(),
+ })
+ })
+ })
.collect::<Vec<_>>(),
Err(..) => vec![],
};
unicode-width = "0.1.4"
cfg-if = "0.1.2"
tracing = "0.1"
-sha1 = { package = "sha-1", version = "0.9" }
-sha2 = "0.9"
-md5 = { package = "md-5", version = "0.9" }
+sha1 = { package = "sha-1", version = "0.10.0" }
+sha2 = "0.10.1"
+md5 = { package = "md-5", version = "0.10.0" }
/// The ID of the theoretical expansion that generates freshly parsed, unexpanded AST.
pub const ROOT: LocalExpnId = LocalExpnId::from_u32(0);
+ #[inline]
pub fn from_raw(idx: ExpnIndex) -> LocalExpnId {
LocalExpnId::from_u32(idx.as_u32())
}
+ #[inline]
pub fn as_raw(self) -> ExpnIndex {
ExpnIndex::from_u32(self.as_u32())
}
use std::path::{Path, PathBuf};
use std::str::FromStr;
+use md5::Digest;
use md5::Md5;
-use sha1::Digest;
use sha1::Sha1;
use sha2::Sha256;
f()
}
-pub fn debug_with_source_map(
- span: Span,
- f: &mut fmt::Formatter<'_>,
- source_map: &SourceMap,
-) -> fmt::Result {
- write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(span), span.ctxt())
-}
-
-pub fn default_span_debug(span: Span, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- with_session_globals(|session_globals| {
- if let Some(source_map) = &*session_globals.source_map.borrow() {
- debug_with_source_map(span, f, source_map)
- } else {
- f.debug_struct("Span")
- .field("lo", &span.lo())
- .field("hi", &span.hi())
- .field("ctxt", &span.ctxt())
- .finish()
- }
- })
-}
-
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- (*SPAN_DEBUG)(*self, f)
+ with_session_globals(|session_globals| {
+ if let Some(source_map) = &*session_globals.source_map.borrow() {
+ write!(f, "{} ({:?})", source_map.span_to_diagnostic_string(*self), self.ctxt())
+ } else {
+ f.debug_struct("Span")
+ .field("lo", &self.lo())
+ .field("hi", &self.hi())
+ .field("ctxt", &self.ctxt())
+ .finish()
+ }
+ })
}
}
impl fmt::Debug for SpanData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- (*SPAN_DEBUG)(Span::new(self.lo, self.hi, self.ctxt, self.parent), f)
+ fmt::Debug::fmt(&Span::new(self.lo, self.hi, self.ctxt, self.parent), f)
}
}
pub lines: Vec<LineInfo>,
}
-pub static SPAN_DEBUG: AtomicRef<fn(Span, &mut fmt::Formatter<'_>) -> fmt::Result> =
- AtomicRef::new(&(default_span_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
pub static SPAN_TRACK: AtomicRef<fn(LocalDefId)> = AtomicRef::new(&((|_| {}) as fn(_)));
// _____________________________________________________________________________
}
/// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by
- /// whitespace. Returns the same span if no character could be found or if an error occurred
- /// while retrieving the code snippet.
- pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span {
+ /// whitespace. Returns None if the pattern could not be found or if an error occurred while
+ /// retrieving the code snippet.
+ pub fn span_extend_to_prev_str(
+ &self,
+ sp: Span,
+ pat: &str,
+ accept_newlines: bool,
+ include_whitespace: bool,
+ ) -> Option<Span> {
// assure that the pattern is delimited, to avoid the following
// fn my_fn()
// ^^^^ returned span without the check
// ---------- correct span
+ let prev_source = self.span_to_prev_source(sp).ok()?;
for ws in &[" ", "\t", "\n"] {
let pat = pat.to_owned() + ws;
- if let Ok(prev_source) = self.span_to_prev_source(sp) {
- let prev_source = prev_source.rsplit(&pat).next().unwrap_or("").trim_start();
- if prev_source.is_empty() && sp.lo().0 != 0 {
- return sp.with_lo(BytePos(sp.lo().0 - 1));
- } else if accept_newlines || !prev_source.contains('\n') {
- return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
+ if let Some(pat_pos) = prev_source.rfind(&pat) {
+ let just_after_pat_pos = pat_pos + pat.len() - 1;
+ let just_after_pat_plus_ws = if include_whitespace {
+ just_after_pat_pos
+ + prev_source[just_after_pat_pos..]
+ .find(|c: char| !c.is_whitespace())
+ .unwrap_or(0)
+ } else {
+ just_after_pat_pos
+ };
+ let len = prev_source.len() - just_after_pat_plus_ws;
+ let prev_source = &prev_source[just_after_pat_plus_ws..];
+ if accept_newlines || !prev_source.trim_start().contains('\n') {
+ return Some(sp.with_lo(BytePos(sp.lo().0 - len as u32)));
}
}
}
- sp
+ None
}
/// Returns the source snippet as `String` after the given `Span`.
}
pub fn generate_fn_name_span(&self, span: Span) -> Option<Span> {
- let prev_span = self.span_extend_to_prev_str(span, "fn", true);
+ let prev_span = self.span_extend_to_prev_str(span, "fn", true, true).unwrap_or(span);
if let Ok(snippet) = self.span_to_snippet(prev_span) {
debug!(
"generate_fn_name_span: span={:?}, prev_span={:?}, snippet={:?}",
pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> {
// Try to extend the span to the previous "fn" keyword to retrieve the function
// signature.
- let sugg_span = self.span_extend_to_prev_str(span, "fn", false);
- if sugg_span != span {
+ if let Some(sugg_span) = self.span_extend_to_prev_str(span, "fn", false, true) {
if let Ok(snippet) = self.span_to_snippet(sugg_span) {
// Consume the function name.
let mut offset = snippet
Formatter,
From,
FromIterator,
+ FromResidual,
Future,
FxHashMap,
FxHashSet,
cfg_target_abi,
cfg_target_feature,
cfg_target_has_atomic,
+ cfg_target_has_atomic_equal_alignment,
+ cfg_target_has_atomic_load_store,
cfg_target_thread_local,
cfg_target_vendor,
cfg_version,
compare_exchange_weak,
compile_error,
compile_error_macro,
+ compiler,
compiler_builtins,
compiler_fence,
concat,
fill,
finish,
flags,
+ float,
float_to_int_unchecked,
floorf32,
floorf64,
inline_const_pat,
inout,
instruction_set,
+ integer_: "integer",
+ integral,
intel,
into_future,
into_iter,
link_ordinal,
link_section,
linkage,
+ linker,
lint_reasons,
literal,
load,
unmarked_api,
unpin,
unreachable,
+ unreachable_2015,
+ unreachable_2015_macro,
+ unreachable_2021,
+ unreachable_2021_macro,
unreachable_code,
+ unreachable_display,
unreachable_macro,
unrestricted_attribute_tokens,
unsafe_block_in_unsafe_fn,
unsafe_cell,
unsafe_no_drop_flag,
+ unsafe_pin_internals,
unsize,
unsized_fn_params,
unsized_locals,
use_extern_macros,
use_nested_groups,
used,
+ used_with_arg,
usize,
v1,
va_arg,
Ok(self)
}
- fn print_const(self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
// only print integers
- if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int { .. })) = ct.val {
- if ct.ty.is_integral() {
+ if let ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int { .. })) = ct.val() {
+ if ct.ty().is_integral() {
return self.pretty_print_const(ct, true);
}
}
/// The values are start positions in `out`, in bytes.
paths: FxHashMap<(DefId, &'tcx [GenericArg<'tcx>]), usize>,
types: FxHashMap<Ty<'tcx>, usize>,
- consts: FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+ consts: FxHashMap<ty::Const<'tcx>, usize>,
}
impl<'tcx> SymbolMangler<'tcx> {
hir::Mutability::Not => "R",
hir::Mutability::Mut => "Q",
});
- if *r != ty::ReErased {
+ if !r.is_erased() {
self = r.print(self)?;
}
self = ty.print(self)?;
Ok(self)
}
- fn print_const(mut self, ct: &'tcx ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
+ fn print_const(mut self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
// We only mangle a typed value if the const can be evaluated.
let ct = ct.eval(self.tcx, ty::ParamEnv::reveal_all());
- match ct.val {
+ match ct.val() {
ty::ConstKind::Value(_) => {}
// Placeholders (should be demangled as `_`).
}
let start = self.out.len();
- match ct.ty.kind() {
+ match ct.ty().kind() {
ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
- self = ct.ty.print(self)?;
+ self = ct.ty().print(self)?;
- let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty);
+ let mut bits = ct.eval_bits(self.tcx, ty::ParamEnv::reveal_all(), ct.ty());
// Negative integer values are mangled using `n` as a "sign prefix".
- if let ty::Int(ity) = ct.ty.kind() {
+ if let ty::Int(ity) = ct.ty().kind() {
let val =
Integer::from_int_ty(&self.tcx, *ity).size().sign_extend(bits) as i128;
if val < 0 {
// handle `&str` and include both `&` ("R") and `str` ("e") prefixes.
ty::Ref(_, ty, hir::Mutability::Not) if *ty == self.tcx.types.str_ => {
self.push("R");
- match ct.val {
+ match ct.val() {
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
// NOTE(eddyb) the following comment was kept from `ty::print::pretty`:
// The `inspect` here is okay since we checked the bounds, and there are no
Ok(this)
};
- match *ct.ty.kind() {
+ match *ct.ty().kind() {
ty::Array(..) => {
self.push("A");
self = print_field_list(self)?;
}
_ => {
- bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty, ct);
+ bug!("symbol_names: unsupported constant of type `{}` ({:?})", ct.ty(), ct);
}
}
) -> Result<Self::Path, Self::Error> {
// Don't print any regions if they're all erased.
let print_regions = args.iter().any(|arg| match arg.unpack() {
- GenericArgKind::Lifetime(r) => *r != ty::ReErased,
+ GenericArgKind::Lifetime(r) => !r.is_erased(),
_ => false,
});
let args = args.iter().cloned().filter(|arg| match arg.unpack() {
use crate::abi::{self, Abi, Align, FieldsShape, Size};
use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::{self, HasTargetSpec};
+use rustc_span::Symbol;
use std::fmt;
mod aarch64;
// or not to actually emit the attribute. It can also be controlled
// with the `-Zmutable-noalias` debugging option.
const NoAliasMutRef = 1 << 6;
+ const NoUndef = 1 << 7;
}
}
}
// For non-immediate arguments the callee gets its own copy of
// the value on the stack, so there are no aliases. It's also
// program-invisible so can't possibly capture
- attrs.set(ArgAttribute::NoAlias).set(ArgAttribute::NoCapture).set(ArgAttribute::NonNull);
+ attrs
+ .set(ArgAttribute::NoAlias)
+ .set(ArgAttribute::NoCapture)
+ .set(ArgAttribute::NonNull)
+ .set(ArgAttribute::NoUndef);
attrs.pointee_size = layout.size;
// FIXME(eddyb) We should be doing this, but at least on
// i686-pc-windows-msvc, it results in wrong stack offsets.
}
/// Error produced by attempting to adjust a `FnAbi`, for a "foreign" ABI.
-#[derive(Clone, Debug, HashStable_Generic)]
+#[derive(Copy, Clone, Debug, HashStable_Generic)]
pub enum AdjustForForeignAbiError {
/// Target architecture doesn't support "foreign" (i.e. non-Rust) ABIs.
- Unsupported { arch: String, abi: spec::abi::Abi },
+ Unsupported { arch: Symbol, abi: spec::abi::Abi },
}
impl fmt::Display for AdjustForForeignAbiError {
match &cx.target_spec().arch[..] {
"x86" => {
- let flavor = if abi == spec::abi::Abi::Fastcall {
+ let flavor = if let spec::abi::Abi::Fastcall { .. } = abi {
x86::Flavor::Fastcall
} else {
x86::Flavor::General
};
x86::compute_abi_info(cx, self, flavor);
}
- "x86_64" => {
- if abi == spec::abi::Abi::SysV64 {
- x86_64::compute_abi_info(cx, self);
- } else if abi == spec::abi::Abi::Win64 || cx.target_spec().is_like_windows {
- x86_win64::compute_abi_info(self);
- } else {
- x86_64::compute_abi_info(cx, self);
+ "x86_64" => match abi {
+ spec::abi::Abi::SysV64 { .. } => x86_64::compute_abi_info(cx, self),
+ spec::abi::Abi::Win64 { .. } => x86_win64::compute_abi_info(self),
+ _ => {
+ if cx.target_spec().is_like_windows {
+ x86_win64::compute_abi_info(self)
+ } else {
+ x86_64::compute_abi_info(cx, self)
+ }
}
- }
+ },
"aarch64" => aarch64::compute_abi_info(cx, self),
"amdgpu" => amdgpu::compute_abi_info(cx, self),
"arm" => arm::compute_abi_info(cx, self),
"asmjs" => wasm::compute_c_abi_info(cx, self),
"bpf" => bpf::compute_abi_info(self),
arch => {
- return Err(AdjustForForeignAbiError::Unsupported { arch: arch.to_string(), abi });
+ return Err(AdjustForForeignAbiError::Unsupported {
+ arch: Symbol::intern(arch),
+ abi,
+ });
}
}
pub fn target() -> Target {
let mut base = super::hermit_base::opts();
base.max_atomic_width = Some(128);
+ base.features = "+strict-align,+neon,+fp-armv8".to_string();
Target {
llvm_target: "aarch64-unknown-hermit".to_string(),
--- /dev/null
+use crate::spec::Target;
+
+pub fn target() -> Target {
+ let mut base = super::hermit_kernel_base::opts();
+ base.max_atomic_width = Some(128);
+ base.abi = "softfloat".to_string();
+ base.features = "+strict-align,-neon,-fp-armv8".to_string();
+
+ Target {
+ llvm_target: "aarch64-unknown-hermit".to_string(),
+ pointer_width: 64,
+ data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128".to_string(),
+ arch: "aarch64".to_string(),
+ options: base,
+ }
+}
// churn. The specific values are meaningless.
Rust,
C { unwind: bool },
- Cdecl,
+ Cdecl { unwind: bool },
Stdcall { unwind: bool },
- Fastcall,
- Vectorcall,
+ Fastcall { unwind: bool },
+ Vectorcall { unwind: bool },
Thiscall { unwind: bool },
- Aapcs,
- Win64,
- SysV64,
+ Aapcs { unwind: bool },
+ Win64 { unwind: bool },
+ SysV64 { unwind: bool },
PtxKernel,
Msp430Interrupt,
X86Interrupt,
AbiData { abi: Abi::Rust, name: "Rust" },
AbiData { abi: Abi::C { unwind: false }, name: "C" },
AbiData { abi: Abi::C { unwind: true }, name: "C-unwind" },
- AbiData { abi: Abi::Cdecl, name: "cdecl" },
+ AbiData { abi: Abi::Cdecl { unwind: false }, name: "cdecl" },
+ AbiData { abi: Abi::Cdecl { unwind: true }, name: "cdecl-unwind" },
AbiData { abi: Abi::Stdcall { unwind: false }, name: "stdcall" },
AbiData { abi: Abi::Stdcall { unwind: true }, name: "stdcall-unwind" },
- AbiData { abi: Abi::Fastcall, name: "fastcall" },
- AbiData { abi: Abi::Vectorcall, name: "vectorcall" },
+ AbiData { abi: Abi::Fastcall { unwind: false }, name: "fastcall" },
+ AbiData { abi: Abi::Fastcall { unwind: true }, name: "fastcall-unwind" },
+ AbiData { abi: Abi::Vectorcall { unwind: false }, name: "vectorcall" },
+ AbiData { abi: Abi::Vectorcall { unwind: true }, name: "vectorcall-unwind" },
AbiData { abi: Abi::Thiscall { unwind: false }, name: "thiscall" },
AbiData { abi: Abi::Thiscall { unwind: true }, name: "thiscall-unwind" },
- AbiData { abi: Abi::Aapcs, name: "aapcs" },
- AbiData { abi: Abi::Win64, name: "win64" },
- AbiData { abi: Abi::SysV64, name: "sysv64" },
+ AbiData { abi: Abi::Aapcs { unwind: false }, name: "aapcs" },
+ AbiData { abi: Abi::Aapcs { unwind: true }, name: "aapcs-unwind" },
+ AbiData { abi: Abi::Win64 { unwind: false }, name: "win64" },
+ AbiData { abi: Abi::Win64 { unwind: true }, name: "win64-unwind" },
+ AbiData { abi: Abi::SysV64 { unwind: false }, name: "sysv64" },
+ AbiData { abi: Abi::SysV64 { unwind: true }, name: "sysv64-unwind" },
AbiData { abi: Abi::PtxKernel, name: "ptx-kernel" },
AbiData { abi: Abi::Msp430Interrupt, name: "msp430-interrupt" },
AbiData { abi: Abi::X86Interrupt, name: "x86-interrupt" },
C { unwind: false } => 1,
C { unwind: true } => 2,
// Platform-specific ABIs
- Cdecl => 3,
- Stdcall { unwind: false } => 4,
- Stdcall { unwind: true } => 5,
- Fastcall => 6,
- Vectorcall => 7,
- Thiscall { unwind: false } => 8,
- Thiscall { unwind: true } => 9,
- Aapcs => 10,
- Win64 => 11,
- SysV64 => 12,
- PtxKernel => 13,
- Msp430Interrupt => 14,
- X86Interrupt => 15,
- AmdGpuKernel => 16,
- EfiApi => 17,
- AvrInterrupt => 18,
- AvrNonBlockingInterrupt => 19,
- CCmseNonSecureCall => 20,
- Wasm => 21,
+ Cdecl { unwind: false } => 3,
+ Cdecl { unwind: true } => 4,
+ Stdcall { unwind: false } => 5,
+ Stdcall { unwind: true } => 6,
+ Fastcall { unwind: false } => 7,
+ Fastcall { unwind: true } => 8,
+ Vectorcall { unwind: false } => 9,
+ Vectorcall { unwind: true } => 10,
+ Thiscall { unwind: false } => 11,
+ Thiscall { unwind: true } => 12,
+ Aapcs { unwind: false } => 13,
+ Aapcs { unwind: true } => 14,
+ Win64 { unwind: false } => 15,
+ Win64 { unwind: true } => 16,
+ SysV64 { unwind: false } => 17,
+ SysV64 { unwind: true } => 18,
+ PtxKernel => 19,
+ Msp430Interrupt => 20,
+ X86Interrupt => 21,
+ AmdGpuKernel => 22,
+ EfiApi => 23,
+ AvrInterrupt => 24,
+ AvrNonBlockingInterrupt => 25,
+ CCmseNonSecureCall => 26,
+ Wasm => 27,
// Cross-platform ABIs
- System { unwind: false } => 22,
- System { unwind: true } => 23,
- RustIntrinsic => 24,
- RustCall => 25,
- PlatformIntrinsic => 26,
- Unadjusted => 27,
+ System { unwind: false } => 28,
+ System { unwind: true } => 29,
+ RustIntrinsic => 30,
+ RustCall => 31,
+ PlatformIntrinsic => 32,
+ Unadjusted => 33,
};
debug_assert!(
AbiDatas
-use crate::spec::{LinkerFlavor, TargetOptions};
+use crate::spec::TargetOptions;
pub fn opts() -> TargetOptions {
let mut base = super::linux_base::opts();
base.os = "android".to_string();
- // Many of the symbols defined in compiler-rt are also defined in libgcc.
- // Android's linker doesn't like that by default.
- base.pre_link_args
- .entry(LinkerFlavor::Gcc)
- .or_default()
- .push("-Wl,--allow-multiple-definition".to_string());
base.dwarf_version = Some(2);
base.position_independent_executables = true;
base.has_thread_local = false;
--- /dev/null
+use crate::spec::{Target, TargetOptions};
+
+// This target is for uclibc Linux on ARMv7 without NEON,
+// thumb-mode or hardfloat.
+
+pub fn target() -> Target {
+ let base = super::linux_uclibc_base::opts();
+ Target {
+ llvm_target: "armv7-unknown-linux-gnueabi".to_string(),
+ pointer_width: 32,
+ data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".to_string(),
+ arch: "arm".to_string(),
+
+ options: TargetOptions {
+ features: "+v7,+thumb2,+soft-float,-neon".to_string(),
+ cpu: "generic".to_string(),
+ max_atomic_width: Some(64),
+ mcount: "_mcount".to_string(),
+ abi: "eabi".to_string(),
+ ..base
+ },
+ }
+}
llvm_target: "i686-pc-windows-msvc".to_string(),
pointer_width: 32,
data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
- i64:64-f80:32-n8:16:32-a:0:32-S32"
+ i64:64-f80:128-n8:16:32-a:0:32-S32"
.to_string(),
arch: "x86".to_string(),
options: base,
llvm_target: "i686-pc-windows-msvc".to_string(),
pointer_width: 32,
data_layout: "e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-\
- i64:64-f80:32-n8:16:32-a:0:32-S32"
+ i64:64-f80:128-n8:16:32-a:0:32-S32"
.to_string(),
arch: "x86".to_string(),
options: base,
--- /dev/null
+/// A target tuple for OpenWrt MIPS64 targets
+///
+use crate::abi::Endian;
+use crate::spec::{Target, TargetOptions};
+
+pub fn target() -> Target {
+ let mut base = super::linux_musl_base::opts();
+ base.cpu = "mips64r2".to_string();
+ base.features = "+mips64r2".to_string();
+ base.max_atomic_width = Some(64);
+ base.crt_static_default = false;
+
+ Target {
+ // LLVM doesn't recognize "muslabi64" yet.
+ llvm_target: "mips64-unknown-linux-musl".to_string(),
+ pointer_width: 64,
+ data_layout: "E-m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128".to_string(),
+ arch: "mips64".to_string(),
+ options: TargetOptions {
+ abi: "abi64".to_string(),
+ endian: Endian::Big,
+ mcount: "_mcount".to_string(),
+ ..base
+ },
+ }
+}
("aarch64-unknown-hermit", aarch64_unknown_hermit),
("x86_64-unknown-hermit", x86_64_unknown_hermit),
+ ("aarch64-unknown-none-hermitkernel", aarch64_unknown_none_hermitkernel),
("x86_64-unknown-none-hermitkernel", x86_64_unknown_none_hermitkernel),
("riscv32i-unknown-none-elf", riscv32i_unknown_none_elf),
("armv6k-nintendo-3ds", armv6k_nintendo_3ds),
+ ("armv7-unknown-linux-uclibceabi", armv7_unknown_linux_uclibceabi),
("armv7-unknown-linux-uclibceabihf", armv7_unknown_linux_uclibceabihf),
("x86_64-unknown-none", x86_64_unknown_none),
+
+ ("mips64-openwrt-linux-musl", mips64_openwrt_linux_musl),
}
/// Warnings encountered when parsing the target `json`.
impl Deref for Target {
type Target = TargetOptions;
+ #[inline]
fn deref(&self) -> &Self::Target {
&self.options
}
}
impl DerefMut for Target {
+ #[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.options
}
Abi::Stdcall { unwind }
}
Abi::System { unwind } => Abi::C { unwind },
- Abi::EfiApi if self.arch == "x86_64" => Abi::Win64,
+ Abi::EfiApi if self.arch == "x86_64" => Abi::Win64 { unwind: false },
Abi::EfiApi => Abi::C { unwind: false },
// See commentary in `is_abi_supported`.
Abi::Stdcall { .. } | Abi::Thiscall { .. } if self.arch == "x86" => abi,
Abi::Stdcall { unwind } | Abi::Thiscall { unwind } => Abi::C { unwind },
- Abi::Fastcall if self.arch == "x86" => abi,
- Abi::Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => abi,
- Abi::Fastcall | Abi::Vectorcall => Abi::C { unwind: false },
+ Abi::Fastcall { .. } if self.arch == "x86" => abi,
+ Abi::Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => abi,
+ Abi::Fastcall { unwind } | Abi::Vectorcall { unwind } => Abi::C { unwind },
abi => abi,
}
| RustCall
| PlatformIntrinsic
| Unadjusted
- | Cdecl
+ | Cdecl { .. }
| EfiApi => true,
X86Interrupt => ["x86", "x86_64"].contains(&&self.arch[..]),
- Aapcs => "arm" == self.arch,
+ Aapcs { .. } => "arm" == self.arch,
CCmseNonSecureCall => ["arm", "aarch64"].contains(&&self.arch[..]),
- Win64 | SysV64 => self.arch == "x86_64",
+ Win64 { .. } | SysV64 { .. } => self.arch == "x86_64",
PtxKernel => self.arch == "nvptx64",
Msp430Interrupt => self.arch == "msp430",
AmdGpuKernel => self.arch == "amdgcn",
// > convention is used.
//
// -- https://docs.microsoft.com/en-us/cpp/cpp/argument-passing-and-naming-conventions
- Stdcall { .. } | Fastcall | Vectorcall if self.is_like_windows => true,
+ Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } if self.is_like_windows => true,
// Outside of Windows we want to only support these calling conventions for the
// architectures for which these calling conventions are actually well defined.
- Stdcall { .. } | Fastcall if self.arch == "x86" => true,
- Vectorcall if ["x86", "x86_64"].contains(&&self.arch[..]) => true,
+ Stdcall { .. } | Fastcall { .. } if self.arch == "x86" => true,
+ Vectorcall { .. } if ["x86", "x86_64"].contains(&&self.arch[..]) => true,
// Return a `None` for other cases so that we know to emit a future compat lint.
- Stdcall { .. } | Fastcall | Vectorcall => return None,
+ Stdcall { .. } | Fastcall { .. } | Vectorcall { .. } => return None,
})
}
use std::fs;
fn load_file(path: &Path) -> Result<(Target, TargetWarnings), String> {
- let contents = fs::read(path).map_err(|e| e.to_string())?;
- let obj = json::from_reader(&mut &contents[..]).map_err(|e| e.to_string())?;
+ let contents = fs::read_to_string(path).map_err(|e| e.to_string())?;
+ let obj = json::from_str(&contents).map_err(|e| e.to_string())?;
Target::from_json(obj)
}
Target {
llvm_target: "wasm32-unknown-emscripten".to_string(),
pointer_width: 32,
- data_layout: "e-m:e-p:32:32-i64:64-f128:64-n32:64-S128-ni:1:10:20".to_string(),
+ data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-f128:64-n32:64-S128-ni:1:10:20"
+ .to_string(),
arch: "wasm32".to_string(),
options: opts,
}
Target {
llvm_target: "wasm32-unknown-unknown".to_string(),
pointer_width: 32,
- data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20".to_string(),
+ data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".to_string(),
arch: "wasm32".to_string(),
options,
}
Target {
llvm_target: "wasm32-wasi".to_string(),
pointer_width: 32,
- data_layout: "e-m:e-p:32:32-i64:64-n32:64-S128-ni:1:10:20".to_string(),
+ data_layout: "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".to_string(),
arch: "wasm32".to_string(),
options,
}
#[instrument(skip(self), level = "debug")]
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- match r {
+ match *r {
// Ignore bound regions and `'static` regions that appear in the
// type, we only need to remap regions that reference lifetimes
// from the function declaraion.
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
trace!("checking const {:?}", ct);
// Find a const parameter
- match ct.val {
+ match ct.val() {
ty::ConstKind::Param(..) => {
// Look it up in the substitution list.
match self.map.get(&ct.into()).map(|k| k.unpack()) {
)
.emit();
- self.tcx().const_error(ct.ty)
+ self.tcx().const_error(ct.ty())
}
}
}
for (new_region, old_region) in
iter::zip(new_substs.regions(), old_substs.regions())
{
- match (new_region, old_region) {
+ match (*new_region, *old_region) {
// If both predicates have an `ReLateBound` (a HRTB) in the
// same spot, we do nothing.
- (
- ty::RegionKind::ReLateBound(_, _),
- ty::RegionKind::ReLateBound(_, _),
- ) => {}
+ (ty::ReLateBound(_, _), ty::ReLateBound(_, _)) => {}
- (ty::RegionKind::ReLateBound(_, _), _)
- | (_, ty::RegionKind::ReVar(_)) => {
+ (ty::ReLateBound(_, _), _) | (_, ty::ReVar(_)) => {
// One of these is true:
// The new predicate has a HRTB in a spot where the old
// predicate does not (if they both had a HRTB, the previous
// `user_computed_preds`.
return false;
}
- (_, ty::RegionKind::ReLateBound(_, _))
- | (ty::RegionKind::ReVar(_), _) => {
+ (_, ty::ReLateBound(_, _)) | (ty::ReVar(_), _) => {
// This is the opposite situation as the previous arm.
// One of these is true:
//
};
}
ty::PredicateKind::ConstEquate(c1, c2) => {
- let evaluate = |c: &'tcx ty::Const<'tcx>| {
- if let ty::ConstKind::Unevaluated(unevaluated) = c.val {
+ let evaluate = |c: ty::Const<'tcx>| {
+ if let ty::ConstKind::Unevaluated(unevaluated) = c.val() {
match select.infcx().const_eval_resolve(
obligation.param_env,
unevaluated,
Some(obligation.cause.span),
) {
- Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty)),
+ Ok(val) => Ok(ty::Const::from_value(select.tcx(), val, c.ty())),
Err(err) => Err(err),
}
} else {
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- (match r {
- ty::ReVar(vid) => self.vid_to_region.get(vid).cloned(),
+ (match *r {
+ ty::ReVar(vid) => self.vid_to_region.get(&vid).cloned(),
_ => None,
})
.unwrap_or_else(|| r.super_fold_with(self))
PredicateObligation, SelectionError, TraitEngine,
};
use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TypeFoldable};
pub struct FulfillmentContext<'tcx> {
obligations: FxIndexSet<PredicateObligation<'tcx>>,
let environment = obligation.param_env.caller_bounds();
let goal = ChalkEnvironmentAndGoal { environment, goal: obligation.predicate };
let mut orig_values = OriginalQueryValues::default();
- let canonical_goal = infcx.canonicalize_query(goal, &mut orig_values);
+ if goal.references_error() {
+ continue;
+ }
+
+ let canonical_goal =
+ infcx.canonicalize_query_preserving_universes(goal, &mut orig_values);
match infcx.tcx.evaluate_goal(canonical_goal) {
Ok(response) => {
pub fn codegen_fulfill_obligation<'tcx>(
tcx: TyCtxt<'tcx>,
(param_env, trait_ref): (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>),
-) -> Result<ImplSource<'tcx, ()>, ErrorReported> {
+) -> Result<&'tcx ImplSource<'tcx, ()>, ErrorReported> {
// Remove any references to regions; this helps improve caching.
let trait_ref = tcx.erase_regions(trait_ref);
// We expect the input to be fully normalized.
Err(Unimplemented) => {
// This can trigger when we probe for the source of a `'static` lifetime requirement
// on a trait object: `impl Foo for dyn Trait {}` has an implicit `'static` bound.
+ // This can also trigger when we have a global bound that is not actually satisfied,
+ // but was included during typeck due to the trivial_bounds feature.
infcx.tcx.sess.delay_span_bug(
rustc_span::DUMMY_SP,
&format!(
let impl_source = drain_fulfillment_cx_or_panic(&infcx, &mut fulfill_cx, impl_source);
debug!("Cache miss: {:?} => {:?}", trait_ref, impl_source);
- Ok(impl_source)
+ Ok(&*tcx.arena.alloc(impl_source))
})
}
//! [trait-resolution]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html
//! [trait-specialization]: https://rustc-dev-guide.rust-lang.org/traits/specialization.html
-use crate::infer::{CombinedSnapshot, InferOk, TyCtxtInferExt};
-use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::infer::outlives::env::OutlivesEnvironment;
+use crate::infer::{CombinedSnapshot, InferOk, RegionckMode};
use crate::traits::select::IntercrateAmbiguityCause;
use crate::traits::util::impl_trait_ref_and_oblig;
use crate::traits::SkipLeakCheck;
self, FulfillmentContext, Normalized, Obligation, ObligationCause, PredicateObligation,
PredicateObligations, SelectionContext,
};
+//use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
+use rustc_hir::CRATE_HIR_ID;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::traits::TraitEngine;
use rustc_middle::traits::specialization_graph::OverlapMode;
-use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences};
+use rustc_middle::ty::fast_reject::{self, SimplifyParams};
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, Ty, TyCtxt};
impl2_ref.iter().flat_map(|tref| tref.substs.types()),
)
.any(|(ty1, ty2)| {
- let t1 = fast_reject::simplify_type(tcx, ty1, SimplifyParams::No, StripReferences::No);
- let t2 = fast_reject::simplify_type(tcx, ty2, SimplifyParams::No, StripReferences::No);
+ let t1 = fast_reject::simplify_type(tcx, ty1, SimplifyParams::No);
+ let t2 = fast_reject::simplify_type(tcx, ty2, SimplifyParams::No);
if let (Some(t1), Some(t2)) = (t1, t2) {
// Simplified successfully
impl2_def_id: DefId,
overlap_mode: OverlapMode,
) -> Option<OverlapResult<'tcx>> {
- debug!("overlap(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
+ debug!(
+ "overlap(impl1_def_id={:?}, impl2_def_id={:?}, overlap_mode={:?})",
+ impl1_def_id, impl2_def_id, overlap_mode
+ );
selcx.infcx().probe_maybe_skip_leak_check(skip_leak_check.is_yes(), |snapshot| {
overlap_within_probe(
let impl1_header = with_fresh_ty_vars(selcx, param_env, impl1_def_id);
let impl2_header = with_fresh_ty_vars(selcx, param_env, impl2_def_id);
- debug!("overlap: impl1_header={:?}", impl1_header);
- debug!("overlap: impl2_header={:?}", impl2_header);
-
let obligations = equate_impl_headers(selcx, &impl1_header, &impl2_header)?;
debug!("overlap: unification check succeeded");
impl2_header: &ty::ImplHeader<'tcx>,
) -> Option<PredicateObligations<'tcx>> {
// Do `a` and `b` unify? If not, no overlap.
+ debug!("equate_impl_headers(impl1_header={:?}, impl2_header={:?}", impl1_header, impl2_header);
selcx
.infcx()
.at(&ObligationCause::dummy(), ty::ParamEnv::empty())
// If the obligation `&'?a str: Error` holds, it means that there's overlap. If that doesn't
// hold we need to check if `&'?a str: !Error` holds, if doesn't hold there's overlap because
// at some point an impl for `&'?a str: Error` could be added.
+ debug!(
+ "implicit_negative(impl1_header={:?}, impl2_header={:?}, obligations={:?})",
+ impl1_header, impl2_header, obligations
+ );
let infcx = selcx.infcx();
- let tcx = infcx.tcx;
let opt_failing_obligation = impl1_header
.predicates
.iter()
predicate: p,
})
.chain(obligations)
- .find(|o| {
- loose_check(selcx, o) || tcx.features().negative_impls && negative_impl_exists(selcx, o)
- });
- // FIXME: the call to `selcx.predicate_may_hold_fatal` above should be ported
- // to the canonical trait query form, `infcx.predicate_may_hold`, once
- // the new system supports intercrate mode (which coherence needs).
+ .find(|o| !selcx.predicate_may_hold_fatal(o));
if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
impl1_def_id: DefId,
impl2_def_id: DefId,
) -> bool {
+ debug!("negative_impl(impl1_def_id={:?}, impl2_def_id={:?})", impl1_def_id, impl2_def_id);
let tcx = selcx.infcx().tcx;
// create a parameter environment corresponding to a (placeholder) instantiation of impl1
let opt_failing_obligation = obligations
.into_iter()
.chain(more_obligations)
- .find(|o| negative_impl_exists(selcx, o));
+ .find(|o| negative_impl_exists(selcx, impl1_env, impl1_def_id, o));
if let Some(failing_obligation) = opt_failing_obligation {
debug!("overlap: obligation unsatisfiable {:?}", failing_obligation);
})
}
-fn loose_check<'cx, 'tcx>(
- selcx: &mut SelectionContext<'cx, 'tcx>,
- o: &PredicateObligation<'tcx>,
-) -> bool {
- !selcx.predicate_may_hold_fatal(o)
-}
-
fn negative_impl_exists<'cx, 'tcx>(
selcx: &SelectionContext<'cx, 'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ region_context: DefId,
o: &PredicateObligation<'tcx>,
) -> bool {
- let infcx = selcx.infcx();
+ let infcx = &selcx.infcx().fork();
let tcx = infcx.tcx;
o.flip_polarity(tcx)
- .as_ref()
.map(|o| {
- // FIXME This isn't quite correct, regions should be included
- selcx.infcx().predicate_must_hold_modulo_regions(o)
+ let mut fulfillment_cx = FulfillmentContext::new();
+ fulfillment_cx.register_predicate_obligation(infcx, o);
+
+ let errors = fulfillment_cx.select_all_or_error(infcx);
+ if !errors.is_empty() {
+ return false;
+ }
+
+ let mut outlives_env = OutlivesEnvironment::new(param_env);
+ // FIXME -- add "assumed to be well formed" types into the `outlives_env`
+
+ // "Save" the accumulated implied bounds into the outlives environment
+ // (due to the FIXME above, there aren't any, but this step is still needed).
+ // The "body id" is given as `CRATE_HIR_ID`, which is the same body-id used
+ // by the "dummy" causes elsewhere (body-id is only relevant when checking
+ // function bodies with closures).
+ outlives_env.save_implied_bounds(CRATE_HIR_ID);
+
+ infcx.process_registered_region_obligations(
+ outlives_env.region_bound_pairs_map(),
+ Some(tcx.lifetimes.re_root_empty),
+ param_env,
+ );
+
+ let errors =
+ infcx.resolve_regions(region_context, &outlives_env, RegionckMode::default());
+ if !errors.is_empty() {
+ return false;
+ }
+
+ true
})
.unwrap_or(false)
}
.substs
.types()
.flat_map(|ty| uncover_fundamental_ty(tcx, ty, in_crate))
- .find(|ty| ty_is_local_constructor(ty, in_crate));
+ .find(|ty| ty_is_local_constructor(*ty, in_crate));
debug!("orphan_check_trait_ref: uncovered ty local_type: `{:?}`", local_type);
//! In this case we try to build an abstract representation of this constant using
//! `thir_abstract_const` which can then be checked for structural equality with other
//! generic constants mentioned in the `caller_bounds` of the current environment.
+use rustc_data_structures::intern::Interned;
use rustc_errors::ErrorReported;
use rustc_hir::def::DefKind;
use rustc_index::vec::IndexVec;
pub fn from_const(
tcx: TyCtxt<'tcx>,
- ct: &ty::Const<'tcx>,
+ ct: ty::Const<'tcx>,
) -> Result<Option<AbstractConst<'tcx>>, ErrorReported> {
- match ct.val {
+ match ct.val() {
ty::ConstKind::Unevaluated(uv) => AbstractConst::new(tcx, uv.shrink()),
ty::ConstKind::Error(_) => Err(ErrorReported),
_ => Ok(None),
}
}
- fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) {
+ fn visit_const(&mut self, ct: ty::Const<'tcx>) {
self.is_poly |= ct.has_param_types_or_consts();
}
}
self.recurse_build(self.body_id)?;
for n in self.nodes.iter() {
- if let Node::Leaf(ty::Const { val: ty::ConstKind::Unevaluated(ct), ty: _ }) = n {
+ if let Node::Leaf(ty::Const(Interned(
+ ty::ConstS { val: ty::ConstKind::Unevaluated(ct), ty: _ },
+ _,
+ ))) = n
+ {
// `AbstractConst`s should not contain any promoteds as they require references which
// are not allowed.
assert_eq!(ct.promoted, None);
match (a.root(tcx), b.root(tcx)) {
(Node::Leaf(a_ct), Node::Leaf(b_ct)) => {
- if a_ct.ty != b_ct.ty {
+ if a_ct.ty() != b_ct.ty() {
return false;
}
- match (a_ct.val, b_ct.val) {
+ match (a_ct.val(), b_ct.val()) {
// We can just unify errors with everything to reduce the amount of
// emitted errors here.
(ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true,
use rustc_hir::Node;
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::ExpectedFound;
-use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences};
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::{
- self, AdtKind, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable,
+ self, SubtypePredicate, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable,
};
use rustc_session::DiagnosticMessageId;
use rustc_span::symbol::{kw, sym};
pub use rustc_infer::traits::error_reporting::*;
+// When outputting impl candidates, prefer showing those that are more similar.
+//
+// We also compare candidates after skipping lifetimes, which has a lower
+// priority than exact matches.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CandidateSimilarity {
+ Exact { ignoring_lifetimes: bool },
+ Fuzzy { ignoring_lifetimes: bool },
+}
+
+#[derive(Debug, Clone, Copy)]
+pub struct ImplCandidate<'tcx> {
+ pub trait_ref: ty::TraitRef<'tcx>,
+ pub similarity: CandidateSimilarity,
+}
+
pub trait InferCtxtExt<'tcx> {
fn report_fulfillment_errors(
&self,
.tcx
.diagnostic_hir_wf_check((tcx.erase_regions(obligation.predicate), *wf_loc))
{
- obligation.cause = cause;
+ obligation.cause = cause.clone();
span = obligation.cause.span;
}
}
error: &MismatchedProjectionTypes<'tcx>,
);
- fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool;
+ fn fuzzy_match_tys(
+ &self,
+ a: Ty<'tcx>,
+ b: Ty<'tcx>,
+ ignoring_lifetimes: bool,
+ ) -> Option<CandidateSimilarity>;
fn describe_generator(&self, body_id: hir::BodyId) -> Option<&'static str>;
fn find_similar_impl_candidates(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Vec<ty::TraitRef<'tcx>>;
+ ) -> Vec<ImplCandidate<'tcx>>;
fn report_similar_impl_candidates(
&self,
- impl_candidates: Vec<ty::TraitRef<'tcx>>,
+ impl_candidates: Vec<ImplCandidate<'tcx>>,
err: &mut DiagnosticBuilder<'_>,
);
fn is_recursive_obligation(
&self,
- obligated_types: &mut Vec<&ty::TyS<'tcx>>,
+ obligated_types: &mut Vec<Ty<'tcx>>,
cause_code: &ObligationCauseCode<'tcx>,
) -> bool;
}
normalized_ty,
data.term,
) {
- values = Some(match (normalized_ty, data.term) {
- (ty::Term::Ty(normalized_ty), ty::Term::Ty(ty)) => {
- infer::ValuePairs::Types(ExpectedFound::new(
- is_normalized_ty_expected,
- normalized_ty,
- ty,
- ))
- }
- (ty::Term::Const(normalized_ct), ty::Term::Const(ct)) => {
- infer::ValuePairs::Consts(ExpectedFound::new(
- is_normalized_ty_expected,
- normalized_ct,
- ct,
- ))
- }
- (_, _) => span_bug!(
- obligation.cause.span,
- "found const or type where other expected"
- ),
- });
+ values = Some(infer::ValuePairs::Terms(ExpectedFound::new(
+ is_normalized_ty_expected,
+ normalized_ty,
+ data.term,
+ )));
err_buf = error;
err = &err_buf;
}
});
}
- fn fuzzy_match_tys(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+ fn fuzzy_match_tys(
+ &self,
+ mut a: Ty<'tcx>,
+ mut b: Ty<'tcx>,
+ ignoring_lifetimes: bool,
+ ) -> Option<CandidateSimilarity> {
/// returns the fuzzy category of a given type, or None
/// if the type can be equated to any type.
- fn type_category(t: Ty<'_>) -> Option<u32> {
+ fn type_category(tcx: TyCtxt<'_>, t: Ty<'_>) -> Option<u32> {
match t.kind() {
ty::Bool => Some(0),
ty::Char => Some(1),
ty::Str => Some(2),
- ty::Int(..) | ty::Uint(..) | ty::Infer(ty::IntVar(..)) => Some(3),
- ty::Float(..) | ty::Infer(ty::FloatVar(..)) => Some(4),
+ ty::Adt(def, _) if tcx.is_diagnostic_item(sym::String, def.did) => Some(2),
+ ty::Int(..)
+ | ty::Uint(..)
+ | ty::Float(..)
+ | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) => Some(4),
ty::Ref(..) | ty::RawPtr(..) => Some(5),
ty::Array(..) | ty::Slice(..) => Some(6),
ty::FnDef(..) | ty::FnPtr(..) => Some(7),
ty::Dynamic(..) => Some(8),
ty::Closure(..) => Some(9),
ty::Tuple(..) => Some(10),
- ty::Projection(..) => Some(11),
- ty::Param(..) => Some(12),
+ ty::Param(..) => Some(11),
+ ty::Projection(..) => Some(12),
ty::Opaque(..) => Some(13),
ty::Never => Some(14),
- ty::Adt(adt, ..) => match adt.adt_kind() {
- AdtKind::Struct => Some(15),
- AdtKind::Union => Some(16),
- AdtKind::Enum => Some(17),
- },
- ty::Generator(..) => Some(18),
- ty::Foreign(..) => Some(19),
- ty::GeneratorWitness(..) => Some(20),
+ ty::Adt(..) => Some(15),
+ ty::Generator(..) => Some(16),
+ ty::Foreign(..) => Some(17),
+ ty::GeneratorWitness(..) => Some(18),
ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) | ty::Error(_) => None,
}
}
- match (type_category(a), type_category(b)) {
- (Some(cat_a), Some(cat_b)) => match (a.kind(), b.kind()) {
- (&ty::Adt(def_a, _), &ty::Adt(def_b, _)) => def_a == def_b,
- _ => cat_a == cat_b,
- },
- // infer and error can be equated to all types
- _ => true,
+ let strip_references = |mut t: Ty<'tcx>| -> Ty<'tcx> {
+ loop {
+ match t.kind() {
+ ty::Ref(_, inner, _) | ty::RawPtr(ty::TypeAndMut { ty: inner, .. }) => {
+ t = *inner
+ }
+ _ => break t,
+ }
+ }
+ };
+
+ if !ignoring_lifetimes {
+ a = strip_references(a);
+ b = strip_references(b);
+ }
+
+ let cat_a = type_category(self.tcx, a)?;
+ let cat_b = type_category(self.tcx, b)?;
+ if a == b {
+ Some(CandidateSimilarity::Exact { ignoring_lifetimes })
+ } else if cat_a == cat_b {
+ match (a.kind(), b.kind()) {
+ (ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b,
+ // Matching on references results in a lot of unhelpful
+ // suggestions, so let's just not do that for now.
+ //
+ // We still upgrade successful matches to `ignoring_lifetimes: true`
+ // to prioritize that impl.
+ (ty::Ref(..) | ty::RawPtr(..), ty::Ref(..) | ty::RawPtr(..)) => {
+ self.fuzzy_match_tys(a, b, true).is_some()
+ }
+ _ => true,
+ }
+ .then_some(CandidateSimilarity::Fuzzy { ignoring_lifetimes })
+ } else if ignoring_lifetimes {
+ None
+ } else {
+ self.fuzzy_match_tys(a, b, true)
}
}
fn find_similar_impl_candidates(
&self,
trait_ref: ty::PolyTraitRef<'tcx>,
- ) -> Vec<ty::TraitRef<'tcx>> {
- // We simplify params and strip references here.
- //
- // This both removes a lot of unhelpful suggestions, e.g.
- // when searching for `&Foo: Trait` it doesn't suggestion `impl Trait for &Bar`,
- // while also suggesting impls for `&Foo` when we're looking for `Foo: Trait`.
- //
- // The second thing isn't necessarily always a good thing, but
- // any other simple setup results in a far worse output, so 🤷
- let simp = fast_reject::simplify_type(
- self.tcx,
- trait_ref.skip_binder().self_ty(),
- SimplifyParams::Yes,
- StripReferences::Yes,
- );
- let all_impls = self.tcx.all_impls(trait_ref.def_id());
-
- match simp {
- Some(simp) => all_impls
- .filter_map(|def_id| {
- let imp = self.tcx.impl_trait_ref(def_id).unwrap();
- let imp_simp = fast_reject::simplify_type(
- self.tcx,
- imp.self_ty(),
- SimplifyParams::Yes,
- StripReferences::Yes,
- );
- if let Some(imp_simp) = imp_simp {
- if simp != imp_simp {
- return None;
- }
- }
- if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative {
- return None;
- }
- Some(imp)
- })
- .collect(),
- None => all_impls
- .filter_map(|def_id| {
- if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative {
- return None;
- }
- self.tcx.impl_trait_ref(def_id)
- })
- .collect(),
- }
+ ) -> Vec<ImplCandidate<'tcx>> {
+ self.tcx
+ .all_impls(trait_ref.def_id())
+ .filter_map(|def_id| {
+ if self.tcx.impl_polarity(def_id) == ty::ImplPolarity::Negative {
+ return None;
+ }
+
+ let imp = self.tcx.impl_trait_ref(def_id).unwrap();
+
+ self.fuzzy_match_tys(trait_ref.skip_binder().self_ty(), imp.self_ty(), false)
+ .map(|similarity| ImplCandidate { trait_ref: imp, similarity })
+ })
+ .collect()
}
fn report_similar_impl_candidates(
&self,
- impl_candidates: Vec<ty::TraitRef<'tcx>>,
+ impl_candidates: Vec<ImplCandidate<'tcx>>,
err: &mut DiagnosticBuilder<'_>,
) {
if impl_candidates.is_empty() {
};
// Sort impl candidates so that ordering is consistent for UI tests.
- let mut normalized_impl_candidates =
- impl_candidates.iter().copied().map(normalize).collect::<Vec<String>>();
-
- // Sort before taking the `..end` range,
// because the ordering of `impl_candidates` may not be deterministic:
// https://github.com/rust-lang/rust/pull/57475#issuecomment-455519507
- normalized_impl_candidates.sort();
+ //
+ // Prefer more similar candidates first, then sort lexicographically
+ // by their normalized string representation.
+ let mut normalized_impl_candidates_and_similarities = impl_candidates
+ .into_iter()
+ .map(|ImplCandidate { trait_ref, similarity }| {
+ let normalized = normalize(trait_ref);
+ (similarity, normalized)
+ })
+ .collect::<Vec<_>>();
+ normalized_impl_candidates_and_similarities.sort();
+
+ let normalized_impl_candidates = normalized_impl_candidates_and_similarities
+ .into_iter()
+ .map(|(_, normalized)| normalized)
+ .collect::<Vec<_>>();
err.help(&format!(
"the following implementations were found:{}{}",
return;
}
- let impl_candidates = self.find_similar_impl_candidates(trait_ref);
+ let impl_candidates = self
+ .find_similar_impl_candidates(trait_ref)
+ .into_iter()
+ .map(|candidate| candidate.trait_ref)
+ .collect();
let mut err = self.emit_inference_failure_err(
body_id,
span,
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
if let ty::Param(ty::ParamTy { name, .. }) = *ty.kind() {
let infcx = self.infcx;
- self.var_map.entry(ty).or_insert_with(|| {
+ *self.var_map.entry(ty).or_insert_with(|| {
infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeParameterDefinition(name, None),
span: DUMMY_SP,
fn is_recursive_obligation(
&self,
- obligated_types: &mut Vec<&ty::TyS<'tcx>>,
+ obligated_types: &mut Vec<Ty<'tcx>>,
cause_code: &ObligationCauseCode<'tcx>,
) -> bool {
if let ObligationCauseCode::BuiltinDerivedObligation(ref data) = cause_code {
trait_ref.substs.types().skip(1),
impl_trait_ref.substs.types().skip(1),
)
- .all(|(u, v)| self.fuzzy_match_tys(u, v))
+ .all(|(u, v)| self.fuzzy_match_tys(u, v, false).is_some())
{
fuzzy_match_impls.push(def_id);
}
/// Used to set on_unimplemented's `ItemContext`
/// to be the enclosing (async) block/function/closure
fn describe_enclosure(&self, hir_id: hir::HirId) -> Option<&'static str> {
- let hir = &self.tcx.hir();
+ let hir = self.tcx.hir();
let node = hir.find(hir_id)?;
match &node {
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) => {
let type_string = self.tcx.type_of(def.did).to_string();
flags.push((sym::_Self, Some(format!("[{}]", type_string))));
- let len = len.val.try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
+ let len =
+ len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
let string = match len {
Some(n) => format!("[{}; {}]", type_string, n),
None => format!("[{}; _]", type_string),
predicate: &T,
param_env: ty::ParamEnv<'tcx>,
cause_code: &ObligationCauseCode<'tcx>,
- obligated_types: &mut Vec<&ty::TyS<'tcx>>,
+ obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
T: fmt::Display;
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
break;
};
- suggested_ty = inner_ty;
+ suggested_ty = *inner_ty;
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
obligation.param_env,
_ => return false,
};
- let ret_ty = if let hir::FnRetTy::Return(ret_ty) = sig.decl.output {
- ret_ty
- } else {
+ let hir::FnRetTy::Return(ret_ty) = sig.decl.output else {
return false;
};
};
let sm = self.tcx.sess.source_map();
- let snippet = if let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = (
+ let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = (
// Verify that we're dealing with a return `dyn Trait`
ret_ty.span.overlaps(span),
&ret_ty.kind,
// If any of the return types does not conform to the trait, then we can't
// suggest `impl Trait` nor trait objects: it is a type mismatch error.
all_returns_conform_to_trait,
- ) {
- snippet
- } else {
+ ) else {
return false;
};
err.code(error_code!(E0746));
if let Some(cause) =
typeck_results.generator_interior_types.as_ref().skip_binder().iter().find(
|ty::GeneratorInteriorTypeCause { ty, .. }| {
- ty_matches(typeck_results.generator_interior_types.rebind(ty))
+ ty_matches(typeck_results.generator_interior_types.rebind(*ty))
},
)
{
predicate: &T,
param_env: ty::ParamEnv<'tcx>,
cause_code: &ObligationCauseCode<'tcx>,
- obligated_types: &mut Vec<&ty::TyS<'tcx>>,
+ obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
T: fmt::Display,
| ObligationCauseCode::AwaitableExpr(_)
| ObligationCauseCode::ForLoopIterator
| ObligationCauseCode::QuestionMark
- | ObligationCauseCode::LetElse => {}
+ | ObligationCauseCode::LetElse
+ | ObligationCauseCode::CheckAssociatedTypeBounds { .. } => {}
ObligationCauseCode::SliceOrArrayElem => {
err.note("slice and array elements must have `Sized` type");
}
// `T`
substs: self.tcx.mk_substs_trait(
trait_pred.self_ty().skip_binder(),
- self.fresh_substs_for_item(span, item_def_id),
+ &self.fresh_substs_for_item(span, item_def_id)[1..],
),
// `Future::Output`
item_def_id,
//
// Let's just see where this breaks :shrug:
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
- (c1.val, c2.val)
+ (c1.val(), c2.val())
{
if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
return ProcessResult::Changed(vec![]);
let stalled_on = &mut pending_obligation.stalled_on;
- let mut evaluate = |c: &'tcx Const<'tcx>| {
- if let ty::ConstKind::Unevaluated(unevaluated) = c.val {
+ let mut evaluate = |c: Const<'tcx>| {
+ if let ty::ConstKind::Unevaluated(unevaluated) = c.val() {
match self.selcx.infcx().const_eval_resolve(
obligation.param_env,
unevaluated,
Some(obligation.cause.span),
) {
- Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty)),
+ Ok(val) => Ok(Const::from_value(self.selcx.tcx(), val, c.ty())),
Err(ErrorHandled::TooGeneric) => {
stalled_on.extend(
unevaluated
let mut errored = false;
let mut item_iter = items.iter();
+ let parse_value = |value_str| {
+ OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some)
+ };
+
let condition = if is_root {
None
} else {
None,
)
})?;
- attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |_| true);
+ attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |item| {
+ if let Some(symbol) = item.value_str() {
+ if parse_value(symbol).is_err() {
+ errored = true;
+ }
+ }
+ true
+ });
Some(cond.clone())
};
let mut subcommands = vec![];
let mut append_const_msg = None;
- let parse_value = |value_str| {
- OnUnimplementedFormatString::try_parse(tcx, trait_def_id, value_str, span).map(Some)
- };
-
for item in item_iter {
if item.has_name(sym::message) && message.is_none() {
if let Some(message_) = item.value_str() {
let mut append_const_msg = None;
info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
+ let options_map: FxHashMap<Symbol, String> =
+ options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
+
for command in self.subcommands.iter().chain(Some(self)).rev() {
if let Some(ref condition) = command.condition {
if !attr::eval_condition(
Some(tcx.features()),
&mut |c| {
c.ident().map_or(false, |ident| {
- options.contains(&(ident.name, c.value_str().map(|s| s.to_string())))
+ let value = c.value_str().map(|s| {
+ OnUnimplementedFormatString(s).format(tcx, trait_ref, &options_map)
+ });
+
+ options.contains(&(ident.name, value))
})
},
) {
append_const_msg = command.append_const_msg.clone();
}
- let options: FxHashMap<Symbol, String> =
- options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
OnUnimplementedNote {
- label: label.map(|l| l.format(tcx, trait_ref, &options)),
- message: message.map(|m| m.format(tcx, trait_ref, &options)),
- note: note.map(|n| n.format(tcx, trait_ref, &options)),
- enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options)),
+ label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
+ message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
+ note: note.map(|n| n.format(tcx, trait_ref, &options_map)),
+ enclosing_scope: enclosing_scope.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
append_const_msg,
}
}
Piece::String(_) => (), // Normal string, no need to check it
Piece::NextArgument(a) => match a.position {
// `{Self}` is allowed
- Position::ArgumentNamed(s) if s == kw::SelfUpper => (),
+ Position::ArgumentNamed(s, _) if s == kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed
- Position::ArgumentNamed(s) if s == name => (),
+ Position::ArgumentNamed(s, _) if s == name => (),
// `{from_method}` is allowed
- Position::ArgumentNamed(s) if s == sym::from_method => (),
+ Position::ArgumentNamed(s, _) if s == sym::from_method => (),
// `{from_desugaring}` is allowed
- Position::ArgumentNamed(s) if s == sym::from_desugaring => (),
+ Position::ArgumentNamed(s, _) if s == sym::from_desugaring => (),
// `{ItemContext}` is allowed
- Position::ArgumentNamed(s) if s == sym::ItemContext => (),
+ Position::ArgumentNamed(s, _) if s == sym::ItemContext => (),
+ // `{integral}` and `{integer}` and `{float}` are allowed
+ Position::ArgumentNamed(s, _)
+ if s == sym::integral || s == sym::integer_ || s == sym::float =>
+ {
+ ()
+ }
// So is `{A}` if A is a type parameter
- Position::ArgumentNamed(s) => {
+ Position::ArgumentNamed(s, _) => {
match generics.params.iter().find(|param| param.name == s) {
Some(_) => (),
None => {
.map(|p| match p {
Piece::String(s) => s,
Piece::NextArgument(a) => match a.position {
- Position::ArgumentNamed(s) => match generic_map.get(&s) {
+ Position::ArgumentNamed(s, _) => match generic_map.get(&s) {
Some(val) => val,
None if s == name => &trait_str,
None => {
&empty_string
} else if s == sym::ItemContext {
&item_context
+ } else if s == sym::integral {
+ "{integral}"
+ } else if s == sym::integer_ {
+ "{integer}"
+ } else if s == sym::float {
+ "{float}"
} else {
bug!(
"broken on_unimplemented {:?} for {:?}: \
}
}
- fn fold_const(&mut self, constant: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
if self.selcx.tcx().lazy_normalization() {
constant
} else {
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- match *ct {
- ty::Const { val: ty::ConstKind::Bound(debruijn, _), ty: _ }
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ match ct.val() {
+ ty::ConstKind::Bound(debruijn, _)
if debruijn.as_usize() + 1
> self.current_index.as_usize() + self.universe_indices.len() =>
{
bug!("Bound vars outside of `self.universe_indices`");
}
- ty::Const { val: ty::ConstKind::Bound(debruijn, bound_const), ty }
- if debruijn >= self.current_index =>
- {
+ ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
let universe = self.universe_for(debruijn);
let p = ty::PlaceholderConst {
universe,
- name: ty::BoundConst { var: bound_const, ty },
+ name: ty::BoundConst { var: bound_const, ty: ct.ty() },
};
self.mapped_consts.insert(p, bound_const);
- self.infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Placeholder(p), ty })
+ self.infcx
+ .tcx
+ .mk_const(ty::ConstS { val: ty::ConstKind::Placeholder(p), ty: ct.ty() })
}
_ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
_ => ct,
}
fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
- let r1 = match r0 {
+ let r1 = match *r0 {
ty::ReVar(_) => self
.infcx
.inner
}
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
- if let ty::Const { val: ty::ConstKind::Placeholder(p), ty } = *ct {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ if let ty::ConstKind::Placeholder(p) = ct.val() {
let replace_var = self.mapped_consts.get(&p);
match replace_var {
Some(replace_var) => {
let db = ty::DebruijnIndex::from_usize(
self.universe_indices.len() - index + self.current_index.as_usize() - 1,
);
- self.tcx()
- .mk_const(ty::Const { val: ty::ConstKind::Bound(db, *replace_var), ty })
+ self.tcx().mk_const(ty::ConstS {
+ val: ty::ConstKind::Bound(db, *replace_var),
+ ty: ct.ty(),
+ })
}
None => ct,
}
return Ok(Projected::Progress(Progress::error(selcx.tcx())));
}
+ // If the obligation contains any inference types or consts in associated
+ // type substs, then we don't assemble any candidates.
+ // This isn't really correct, but otherwise we can end up in a case where
+ // we constrain inference variables by selecting a single predicate, when
+ // we need to stay general. See issue #91762.
+ let (_, predicate_own_substs) = obligation.predicate.trait_ref_and_own_substs(selcx.tcx());
+ if predicate_own_substs.iter().any(|g| g.has_infer_types_or_consts()) {
+ return Err(ProjectionError::TooManyCandidates);
+ }
+
let mut candidates = ProjectionCandidateSet::None;
// Make sure that the following procedures are kept in order. ParamEnv
crate::traits::InternalSubsts::identity_for_item(tcx, assoc_ty.item.def_id);
let did = ty::WithOptConstParam::unknown(assoc_ty.item.def_id);
let val = ty::ConstKind::Unevaluated(ty::Unevaluated::new(did, identity_substs));
- tcx.mk_const(ty::Const { ty, val }).into()
+ tcx.mk_const(ty::ConstS { ty, val }).into()
} else {
ty.into()
};
| ty::Error(_) => true,
// [T; N] and [T] have same properties as T.
- ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),
+ ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, *ty),
// (T1..Tn) and closures have same properties as T1..Tn --
// check if *any* of those are trivial.
ControlFlow::CONTINUE
}
- fn visit_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- match ct.val {
+ fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match ct.val() {
ty::ConstKind::Bound(debruijn, _) if debruijn >= self.outer_index => {
self.escaping =
self.escaping.max(debruijn.as_usize() - self.outer_index.as_usize());
}
if let Some(ty) = self.cache.get(&ty) {
- return Ok(ty);
+ return Ok(*ty);
}
// See note in `rustc_trait_selection::traits::project` about why we
fn try_fold_const(
&mut self,
- constant: &'tcx ty::Const<'tcx>,
- ) -> Result<&'tcx ty::Const<'tcx>, Self::Error> {
+ constant: ty::Const<'tcx>,
+ ) -> Result<ty::Const<'tcx>, Self::Error> {
let constant = constant.try_super_fold_with(self)?;
Ok(constant.eval(self.infcx.tcx, self.param_env))
}
// Lifetimes aren't allowed to change during unsizing.
GenericArgKind::Lifetime(_) => None,
- GenericArgKind::Const(ct) => match ct.val {
+ GenericArgKind::Const(ct) => match ct.val() {
ty::ConstKind::Param(p) => Some(p.index),
_ => None,
},
use rustc_middle::dep_graph::{DepKind, DepNodeIndex};
use rustc_middle::mir::interpret::ErrorHandled;
use rustc_middle::thir::abstract_const::NotConstEvaluatable;
-use rustc_middle::ty::fast_reject::{self, SimplifyParams, StripReferences};
+use rustc_middle::ty::fast_reject::{self, SimplifyParams};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::subst::{GenericArgKind, Subst, SubstsRef};
//
// Let's just see where this breaks :shrug:
if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) =
- (c1.val, c2.val)
+ (c1.val(), c2.val())
{
if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) {
return Ok(EvaluatedToOk);
}
}
- let evaluate = |c: &'tcx ty::Const<'tcx>| {
- if let ty::ConstKind::Unevaluated(unevaluated) = c.val {
+ let evaluate = |c: ty::Const<'tcx>| {
+ if let ty::ConstKind::Unevaluated(unevaluated) = c.val() {
self.infcx
.const_eval_resolve(
obligation.param_env,
unevaluated,
Some(obligation.cause.span),
)
- .map(|val| ty::Const::from_value(self.tcx(), val, c.ty))
+ .map(|val| ty::Const::from_value(self.tcx(), val, c.ty()))
} else {
Ok(c)
}
.skip_binder() // binder moved -\
.iter()
.flat_map(|ty| {
- let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(ty); // <----/
+ let ty: ty::Binder<'tcx, Ty<'tcx>> = types.rebind(*ty); // <----/
self.infcx.commit_unconditionally(|_| {
let placeholder_ty = self.infcx.replace_bound_vars_with_placeholders(ty);
self.tcx(),
obligation_ty,
SimplifyParams::Yes,
- StripReferences::No,
- );
- let simplified_impl_ty = fast_reject::simplify_type(
- self.tcx(),
- impl_ty,
- SimplifyParams::No,
- StripReferences::No,
);
+ let simplified_impl_ty =
+ fast_reject::simplify_type(self.tcx(), impl_ty, SimplifyParams::No);
simplified_obligation_ty.is_some()
&& simplified_impl_ty.is_some()
for (mut p, _) in predicates {
if let Some(poly_trait_ref) = p.to_opt_poly_trait_pred() {
if Some(poly_trait_ref.def_id()) == sized_trait {
- types_without_default_bounds.remove(poly_trait_ref.self_ty().skip_binder());
+ types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
continue;
}
use crate::traits;
use rustc_hir::def_id::DefId;
-use rustc_middle::ty::fast_reject::{self, SimplifiedType, SimplifyParams, StripReferences};
+use rustc_middle::ty::fast_reject::{self, SimplifiedType, SimplifyParams};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
/// Insert an impl into this set of children without comparing to any existing impls.
fn insert_blindly(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
- if let Some(st) = fast_reject::simplify_type(
- tcx,
- trait_ref.self_ty(),
- SimplifyParams::No,
- StripReferences::No,
- ) {
+ if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), SimplifyParams::No) {
debug!("insert_blindly: impl_def_id={:?} st={:?}", impl_def_id, st);
self.non_blanket_impls.entry(st).or_default().push(impl_def_id)
} else {
fn remove_existing(&mut self, tcx: TyCtxt<'_>, impl_def_id: DefId) {
let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
let vec: &mut Vec<DefId>;
- if let Some(st) = fast_reject::simplify_type(
- tcx,
- trait_ref.self_ty(),
- SimplifyParams::No,
- StripReferences::No,
- ) {
+ if let Some(st) = fast_reject::simplify_type(tcx, trait_ref.self_ty(), SimplifyParams::No) {
debug!("remove_existing: impl_def_id={:?} st={:?}", impl_def_id, st);
vec = self.non_blanket_impls.get_mut(&st).unwrap();
} else {
let mut parent = trait_def_id;
let mut last_lint = None;
- let simplified = fast_reject::simplify_type(
- tcx,
- trait_ref.self_ty(),
- SimplifyParams::No,
- StripReferences::No,
- );
+ let simplified = fast_reject::simplify_type(tcx, trait_ref.self_ty(), SimplifyParams::No);
// Descend the specialization tree, where `parent` is the current parent node.
loop {
.into()
}
GenericArgKind::Const(ct) => {
- match ct.val {
+ match ct.val() {
ty::ConstKind::Infer(infer) => {
let resolved = infcx.shallow_resolve(infer);
if resolved == infer {
return None;
}
- infcx.tcx.mk_const(ty::Const { val: ty::ConstKind::Infer(resolved), ty: ct.ty })
+ infcx
+ .tcx
+ .mk_const(ty::ConstS { val: ty::ConstKind::Infer(resolved), ty: ct.ty() })
}
_ => ct,
}
trait_ref: &ty::TraitRef<'tcx>,
item: Option<&hir::Item<'tcx>>,
cause: &mut traits::ObligationCause<'tcx>,
- pred: &ty::Predicate<'tcx>,
+ pred: ty::Predicate<'tcx>,
) {
debug!(
"extended_cause_with_original_assoc_item_obligation {:?} {:?} {:?} {:?}",
trait_ref,
item,
&mut cause,
- &obligation.predicate,
+ obligation.predicate,
);
traits::Obligation::with_depth(cause, depth, param_env, obligation.predicate)
};
GenericArgKind::Lifetime(_) => continue,
GenericArgKind::Const(constant) => {
- match constant.val {
+ match constant.val() {
ty::ConstKind::Unevaluated(uv) => {
let obligations = self.nominal_obligations(uv.def.did, uv.substs);
self.out.extend(obligations);
if resolved != infer {
let cause = self.cause(traits::MiscObligation);
- let resolved_constant = self.infcx.tcx.mk_const(ty::Const {
+ let resolved_constant = self.infcx.tcx.mk_const(ty::ConstS {
val: ty::ConstKind::Infer(resolved),
- ..*constant
+ ty: constant.ty(),
});
self.out.push(traits::Obligation::with_depth(
cause,
use std::fmt;
use std::sync::Arc;
-use crate::chalk::lowering::{self, LowerInto};
+use crate::chalk::lowering::LowerInto;
pub struct RustIrDatabase<'tcx> {
pub(crate) interner: RustInterner<'tcx>,
- pub(crate) reempty_placeholder: ty::Region<'tcx>,
}
impl fmt::Debug for RustIrDatabase<'_> {
bound_vars: SubstsRef<'tcx>,
) -> Vec<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>> {
let predicates = self.interner.tcx.predicates_defined_on(def_id).predicates;
- let mut regions_substitutor =
- lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder);
predicates
.iter()
.map(|(wc, _)| wc.subst(self.interner.tcx, bound_vars))
- .map(|wc| wc.fold_with(&mut regions_substitutor))
.filter_map(|wc| LowerInto::<
Option<chalk_ir::QuantifiedWhereClause<RustInterner<'tcx>>>
>::lower_into(wc, self.interner)).collect()
let trait_ref = self.interner.tcx.impl_trait_ref(def_id).expect("not an impl");
let trait_ref = trait_ref.subst(self.interner.tcx, bound_vars);
- let mut regions_substitutor =
- lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder);
- let trait_ref = trait_ref.fold_with(&mut regions_substitutor);
let where_clauses = self.where_clauses_for(def_id, bound_vars);
let self_ty = trait_ref.self_ty();
let self_ty = self_ty.subst(self.interner.tcx, bound_vars);
- let mut regions_substitutor =
- lowering::RegionsSubstitutor::new(self.interner.tcx, self.reempty_placeholder);
- let self_ty = self_ty.fold_with(&mut regions_substitutor);
let lowered_ty = self_ty.lower_into(self.interner);
parameters[0].assert_ty_ref(self.interner).could_match(
Fn => lang_items.fn_trait(),
FnMut => lang_items.fn_mut_trait(),
FnOnce => lang_items.fn_once_trait(),
+ Generator => lang_items.gen_trait(),
Unsize => lang_items.unsize_trait(),
Unpin => lang_items.unpin_trait(),
CoerceUnsized => lang_items.coerce_unsized_trait(),
DiscriminantKind => lang_items.discriminant_kind_trait(),
- Generator => lang_items.generator_return(),
};
def_id.map(chalk_ir::TraitId)
}
let variances = self.interner.tcx.variances_of(def_id.0);
chalk_ir::Variances::from_iter(
self.interner,
- variances.iter().map(|v| match v {
- ty::Variance::Invariant => chalk_ir::Variance::Invariant,
- ty::Variance::Covariant => chalk_ir::Variance::Covariant,
- ty::Variance::Contravariant => chalk_ir::Variance::Contravariant,
- ty::Variance::Bivariant => unimplemented!(),
- }),
+ variances.iter().map(|v| v.lower_into(self.interner)),
)
}
fn adt_variance(
&self,
- def_id: chalk_ir::AdtId<RustInterner<'tcx>>,
+ adt_id: chalk_ir::AdtId<RustInterner<'tcx>>,
) -> chalk_ir::Variances<RustInterner<'tcx>> {
- let variances = self.interner.tcx.variances_of(def_id.0.did);
+ let variances = self.interner.tcx.variances_of(adt_id.0.did);
chalk_ir::Variances::from_iter(
self.interner,
- variances.iter().map(|v| match v {
- ty::Variance::Invariant => chalk_ir::Variance::Invariant,
- ty::Variance::Covariant => chalk_ir::Variance::Covariant,
- ty::Variance::Contravariant => chalk_ir::Variance::Contravariant,
- ty::Variance::Bivariant => unimplemented!(),
- }),
+ variances.iter().map(|v| v.lower_into(self.interner)),
)
}
}
var: ty::BoundVar::from_usize(substs.len()),
kind: ty::BrAnon(substs.len() as u32),
};
- tcx.mk_region(ty::RegionKind::ReLateBound(ty::INNERMOST, br)).into()
+ tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)).into()
}
ty::GenericParamDefKind::Const { .. } => tcx
- .mk_const(ty::Const {
+ .mk_const(ty::ConstS {
val: ty::ConstKind::Bound(ty::INNERMOST, ty::BoundVar::from(param.index)),
ty: tcx.type_of(param.def_id),
})
chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General)
}
ty::subst::GenericArgKind::Const(c) => {
- chalk_ir::VariableKind::Const(c.ty.lower_into(interner))
+ chalk_ir::VariableKind::Const(c.ty().lower_into(interner))
}
}),
)
use rustc_middle::traits::{ChalkEnvironmentAndGoal, ChalkRustInterner as RustInterner};
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
-use rustc_middle::ty::{self, Binder, Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeVisitor};
+use rustc_middle::ty::{self, Binder, Region, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_span::def_id::DefId;
use chalk_ir::{FnSig, ForeignDefId};
chalk_ir::DomainGoal::ObjectSafe(chalk_ir::TraitId(t)),
),
+ ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ }) => {
+ chalk_ir::GoalData::SubtypeGoal(chalk_ir::SubtypeGoal {
+ a: a.lower_into(interner),
+ b: b.lower_into(interner),
+ })
+ }
+
// FIXME(chalk): other predicates
//
// We can defer this, but ultimately we'll want to express
// some of these in terms of chalk operations.
ty::PredicateKind::ClosureKind(..)
- | ty::PredicateKind::Subtype(..)
| ty::PredicateKind::Coerce(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..) => {
TyKind::Array(ty, c) => {
let ty = ty.lower_into(interner);
let c = c.lower_into(interner);
- ty::Array(ty, interner.tcx.mk_const(c))
+ ty::Array(ty, c)
}
TyKind::FnDef(id, substitution) => ty::FnDef(id.0, substitution.lower_into(interner)),
TyKind::Closure(closure, substitution) => {
impl<'tcx> LowerInto<'tcx, chalk_ir::Lifetime<RustInterner<'tcx>>> for Region<'tcx> {
fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Lifetime<RustInterner<'tcx>> {
- use rustc_middle::ty::RegionKind::*;
-
- match self {
- ReEarlyBound(_) => {
+ match *self {
+ ty::ReEarlyBound(_) => {
panic!("Should have already been substituted.");
}
- ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new(
+ ty::ReLateBound(db, br) => chalk_ir::LifetimeData::BoundVar(chalk_ir::BoundVar::new(
chalk_ir::DebruijnIndex::new(db.as_u32()),
br.var.as_usize(),
))
.intern(interner),
- ReFree(_) => unimplemented!(),
- ReStatic => chalk_ir::LifetimeData::Static.intern(interner),
- ReVar(_) => unimplemented!(),
- RePlaceholder(placeholder_region) => {
+ ty::ReFree(_) => unimplemented!(),
+ ty::ReStatic => chalk_ir::LifetimeData::Static.intern(interner),
+ ty::ReVar(_) => unimplemented!(),
+ ty::RePlaceholder(placeholder_region) => {
chalk_ir::LifetimeData::Placeholder(chalk_ir::PlaceholderIndex {
ui: chalk_ir::UniverseIndex { counter: placeholder_region.universe.index() },
idx: 0,
})
.intern(interner)
}
- ReEmpty(_) => unimplemented!(),
- // FIXME(chalk): need to handle ReErased
- ReErased => unimplemented!(),
+ ty::ReEmpty(ui) => {
+ chalk_ir::LifetimeData::Empty(chalk_ir::UniverseIndex { counter: ui.index() })
+ .intern(interner)
+ }
+ ty::ReErased => chalk_ir::LifetimeData::Erased.intern(interner),
}
}
}
impl<'tcx> LowerInto<'tcx, Region<'tcx>> for &chalk_ir::Lifetime<RustInterner<'tcx>> {
fn lower_into(self, interner: RustInterner<'tcx>) -> Region<'tcx> {
let kind = match self.data(interner) {
- chalk_ir::LifetimeData::BoundVar(var) => ty::RegionKind::ReLateBound(
+ chalk_ir::LifetimeData::BoundVar(var) => ty::ReLateBound(
ty::DebruijnIndex::from_u32(var.debruijn.depth()),
ty::BoundRegion {
var: ty::BoundVar::from_usize(var.index),
},
),
chalk_ir::LifetimeData::InferenceVar(_var) => unimplemented!(),
- chalk_ir::LifetimeData::Placeholder(p) => {
- ty::RegionKind::RePlaceholder(ty::Placeholder {
- universe: ty::UniverseIndex::from_usize(p.ui.counter),
- name: ty::BoundRegionKind::BrAnon(p.idx as u32),
- })
- }
- chalk_ir::LifetimeData::Static => ty::RegionKind::ReStatic,
- chalk_ir::LifetimeData::Phantom(_, _) => unimplemented!(),
+ chalk_ir::LifetimeData::Placeholder(p) => ty::RePlaceholder(ty::Placeholder {
+ universe: ty::UniverseIndex::from_usize(p.ui.counter),
+ name: ty::BoundRegionKind::BrAnon(p.idx as u32),
+ }),
+ chalk_ir::LifetimeData::Static => return interner.tcx.lifetimes.re_static,
chalk_ir::LifetimeData::Empty(ui) => {
- ty::RegionKind::ReEmpty(ty::UniverseIndex::from_usize(ui.counter))
+ ty::ReEmpty(ty::UniverseIndex::from_usize(ui.counter))
}
- chalk_ir::LifetimeData::Erased => ty::RegionKind::ReErased,
+ chalk_ir::LifetimeData::Erased => return interner.tcx.lifetimes.re_erased,
+ chalk_ir::LifetimeData::Phantom(void, _) => match *void {},
};
interner.tcx.mk_region(kind)
}
impl<'tcx> LowerInto<'tcx, chalk_ir::Const<RustInterner<'tcx>>> for ty::Const<'tcx> {
fn lower_into(self, interner: RustInterner<'tcx>) -> chalk_ir::Const<RustInterner<'tcx>> {
- let ty = self.ty.lower_into(interner);
- let value = match self.val {
+ let ty = self.ty().lower_into(interner);
+ let value = match self.val() {
ty::ConstKind::Value(val) => {
chalk_ir::ConstValue::Concrete(chalk_ir::ConcreteConst { interned: val })
}
chalk_ir::ConstValue::Placeholder(_p) => unimplemented!(),
chalk_ir::ConstValue::Concrete(c) => ty::ConstKind::Value(c.interned),
};
- ty::Const { ty, val }
+ interner.tcx.mk_const(ty::ConstS { ty, val })
}
}
}
chalk_ir::GenericArgData::Const(c) => {
let c: ty::Const<'tcx> = c.lower_into(interner);
- interner.tcx.mk_const(c).into()
+ c.into()
}
}
}
}
}
}
+impl<'tcx> LowerInto<'tcx, chalk_ir::Variance> for ty::Variance {
+ fn lower_into(self, _interner: RustInterner<'tcx>) -> chalk_ir::Variance {
+ match self {
+ ty::Variance::Covariant => chalk_ir::Variance::Covariant,
+ ty::Variance::Invariant => chalk_ir::Variance::Invariant,
+ ty::Variance::Contravariant => chalk_ir::Variance::Contravariant,
+ ty::Variance::Bivariant => unimplemented!(),
+ }
+ }
+}
impl<'tcx> LowerInto<'tcx, chalk_solve::rust_ir::AliasEqBound<RustInterner<'tcx>>>
for ty::ProjectionPredicate<'tcx>
}
fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match r {
- ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind {
+ match *r {
+ ty::ReLateBound(index, br) if index == self.binder_index => match br.kind {
ty::BoundRegionKind::BrNamed(def_id, _name) => {
if !self.named_parameters.iter().any(|d| *d == def_id) {
self.named_parameters.push(def_id);
}
fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
- match r {
- ty::ReLateBound(index, br) if *index == self.binder_index => match br.kind {
+ match *r {
+ ty::ReLateBound(index, br) if index == self.binder_index => match br.kind {
ty::BrNamed(def_id, _name) => match self.named_parameters.get(&def_id) {
Some(idx) => {
let new_br = ty::BoundRegion { var: br.var, kind: ty::BrAnon(*idx) };
- return self.tcx.mk_region(RegionKind::ReLateBound(*index, new_br));
+ return self.tcx.mk_region(ty::ReLateBound(index, new_br));
}
None => panic!("Missing `BrNamed`."),
},
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
match *t.kind() {
- // FIXME(chalk): currently we convert params to placeholders starting at
- // index `0`. To support placeholders, we'll actually need to do a
- // first pass to collect placeholders. Then we can insert params after.
- ty::Placeholder(_) => unimplemented!(),
ty::Param(param) => match self.list.iter().position(|r| r == ¶m) {
Some(idx) => self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType {
universe: ty::UniverseIndex::from_usize(0),
}))
}
},
-
_ => t.super_fold_with(self),
}
}
fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
- match r {
- // FIXME(chalk) - jackh726 - this currently isn't hit in any tests.
- // This covers any region variables in a goal, right?
+ match *r {
+ // FIXME(chalk) - jackh726 - this currently isn't hit in any tests,
+ // since canonicalization will already change these to canonical
+ // variables (ty::ReLateBound).
ty::ReEarlyBound(_re) => match self.named_regions.get(&_re.def_id) {
Some(idx) => {
let br = ty::BoundRegion {
var: ty::BoundVar::from_u32(*idx),
kind: ty::BrAnon(*idx),
};
- self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br))
+ self.tcx.mk_region(ty::ReLateBound(self.binder_index, br))
}
None => {
let idx = self.named_regions.len() as u32;
let br =
ty::BoundRegion { var: ty::BoundVar::from_u32(idx), kind: ty::BrAnon(idx) };
self.named_regions.insert(_re.def_id, idx);
- self.tcx.mk_region(RegionKind::ReLateBound(self.binder_index, br))
+ self.tcx.mk_region(ty::ReLateBound(self.binder_index, br))
}
},
}
}
+crate struct ReverseParamsSubstitutor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>,
+}
+
+impl<'tcx> ReverseParamsSubstitutor<'tcx> {
+ crate fn new(
+ tcx: TyCtxt<'tcx>,
+ params: rustc_data_structures::fx::FxHashMap<usize, rustc_middle::ty::ParamTy>,
+ ) -> Self {
+ Self { tcx, params }
+ }
+}
+
+impl<'tcx> TypeFolder<'tcx> for ReverseParamsSubstitutor<'tcx> {
+ fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ match *t.kind() {
+ ty::Placeholder(ty::PlaceholderType { universe: ty::UniverseIndex::ROOT, name }) => {
+ match self.params.get(&name.as_usize()) {
+ Some(param) => self.tcx.mk_ty(ty::Param(*param)),
+ None => t,
+ }
+ }
+
+ _ => t.super_fold_with(self),
+ }
+ }
+}
+
/// Used to collect `Placeholder`s.
crate struct PlaceholdersCollector {
universe_index: ty::UniverseIndex,
}
fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match r {
+ match *r {
ty::RePlaceholder(p) if p.universe == self.universe_index => {
if let ty::BoundRegionKind::BrAnon(anon) = p.name {
self.next_anon_region_placeholder = self.next_anon_region_placeholder.max(anon);
r.super_visit_with(self)
}
}
-
-/// Used to substitute specific `Regions`s with placeholders.
-crate struct RegionsSubstitutor<'tcx> {
- tcx: TyCtxt<'tcx>,
- reempty_placeholder: ty::Region<'tcx>,
-}
-
-impl<'tcx> RegionsSubstitutor<'tcx> {
- crate fn new(tcx: TyCtxt<'tcx>, reempty_placeholder: ty::Region<'tcx>) -> Self {
- RegionsSubstitutor { tcx, reempty_placeholder }
- }
-}
-
-impl<'tcx> TypeFolder<'tcx> for RegionsSubstitutor<'tcx> {
- fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
- self.tcx
- }
-
- fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
- match r {
- ty::ReEmpty(ui) => {
- assert_eq!(ui.as_usize(), 0);
- self.reempty_placeholder
- }
-
- _ => r.super_fold_with(self),
- }
- }
-}
use rustc_infer::traits::{self, CanonicalChalkEnvironmentAndGoal};
use crate::chalk::db::RustIrDatabase as ChalkRustIrDatabase;
-use crate::chalk::lowering::{
- LowerInto, ParamsSubstitutor, PlaceholdersCollector, RegionsSubstitutor,
-};
+use crate::chalk::lowering::LowerInto;
+use crate::chalk::lowering::{ParamsSubstitutor, PlaceholdersCollector, ReverseParamsSubstitutor};
use chalk_solve::Solution;
let mut placeholders_collector = PlaceholdersCollector::new();
obligation.visit_with(&mut placeholders_collector);
- let reempty_placeholder = tcx.mk_region(ty::RegionKind::RePlaceholder(ty::Placeholder {
- universe: ty::UniverseIndex::ROOT,
- name: ty::BoundRegionKind::BrAnon(placeholders_collector.next_anon_region_placeholder + 1),
- }));
-
let mut params_substitutor =
ParamsSubstitutor::new(tcx, placeholders_collector.next_ty_placeholder);
let obligation = obligation.fold_with(&mut params_substitutor);
- // FIXME(chalk): we really should be substituting these back in the solution
- let _params: FxHashMap<usize, ParamTy> = params_substitutor.params;
-
- let mut regions_substitutor = RegionsSubstitutor::new(tcx, reempty_placeholder);
- let obligation = obligation.fold_with(&mut regions_substitutor);
+ let params: FxHashMap<usize, ParamTy> = params_substitutor.params;
let max_universe = obligation.max_universe.index();
use chalk_solve::Solver;
let mut solver = chalk_engine::solve::SLGSolver::new(32, None);
- let db = ChalkRustIrDatabase { interner, reempty_placeholder };
+ let db = ChalkRustIrDatabase { interner };
+ debug!(?lowered_goal);
let solution = solver.solve(&db, &lowered_goal);
debug!(?obligation, ?solution, "evaluate goal");
use rustc_middle::infer::canonical::CanonicalVarInfo;
let mut var_values: IndexVec<BoundVar, GenericArg<'tcx>> = IndexVec::new();
+ let mut reverse_param_substitutor = ReverseParamsSubstitutor::new(tcx, params);
subst.as_slice(interner).iter().for_each(|p| {
- var_values.push(p.lower_into(interner));
+ var_values.push(p.lower_into(interner).fold_with(&mut reverse_param_substitutor));
});
let variables: Vec<_> = binders
.iter(interner)
ty::Array(ety, _) | ty::Slice(ety) => {
// single-element containers, behave like their element
rustc_data_structures::stack::ensure_sufficient_stack(|| {
- dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, ety, constraints)
+ dtorck_constraint_for_ty(tcx, span, for_ty, depth + 1, *ety, constraints)
})?;
}
tcx.at(span).adt_dtorck_constraint(def.did)?;
// FIXME: we can try to recursively `dtorck_constraint_on_ty`
// there, but that needs some way to handle cycles.
- constraints.dtorck_types.extend(dtorck_types.subst(tcx, substs));
- constraints.outlives.extend(outlives.subst(tcx, substs));
- constraints.overflows.extend(overflows.subst(tcx, substs));
+ constraints.dtorck_types.extend(dtorck_types.iter().map(|t| t.subst(tcx, substs)));
+ constraints.outlives.extend(outlives.iter().map(|t| t.subst(tcx, substs)));
+ constraints.overflows.extend(overflows.iter().map(|t| t.subst(tcx, substs)));
}
// Objects must be alive in order for their destructor
crate fn adt_dtorck_constraint(
tcx: TyCtxt<'_>,
def_id: DefId,
-) -> Result<DtorckConstraint<'_>, NoSolution> {
+) -> Result<&DtorckConstraint<'_>, NoSolution> {
let def = tcx.adt_def(def_id);
let span = tcx.def_span(def_id);
debug!("dtorck_constraint: {:?}", def);
overflows: vec![],
};
debug!("dtorck_constraint: {:?} => {:?}", def, result);
- return Ok(result);
+ return Ok(tcx.arena.alloc(result));
}
let mut result = DtorckConstraint::empty();
debug!("dtorck_constraint: {:?} => {:?}", def, result);
- Ok(result)
+ Ok(tcx.arena.alloc(result))
}
fn dedup_dtorck_constraint(c: &mut DtorckConstraint<'_>) {
// always only region relations, and we are about to
// erase those anyway:
debug_assert_eq!(
- normalized_obligations.iter().find(|p| not_outlives_predicate(&p.predicate)),
+ normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)),
None,
);
})
}
-fn not_outlives_predicate<'tcx>(p: &ty::Predicate<'tcx>) -> bool {
+fn not_outlives_predicate<'tcx>(p: ty::Predicate<'tcx>) -> bool {
match p.kind().skip_binder() {
ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false,
ty::PredicateKind::Trait(..)
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match r {
- ty::ReLateBound(index, br) if *index == self.binder_index => {
+ match *r {
+ ty::ReLateBound(index, br) if index == self.binder_index => {
match self.vars.entry(br.var.as_u32()) {
Entry::Vacant(entry) => {
entry.insert(ty::BoundVariableKind::Region(br.kind));
seen,
shadow_seen,
representable_cache,
- ty,
+ *ty,
force_result,
),
ty::Adt(def, substs) => {
force_result: &mut bool,
) -> Representability {
debug!("is_type_structurally_recursive: {:?} {:?}", ty, sp);
- if let Some(representability) = representable_cache.get(ty) {
+ if let Some(representability) = representable_cache.get(&ty) {
debug!(
"is_type_structurally_recursive: {:?} {:?} - (cached) {:?}",
ty, sp, representability
let self_ty = trait_ref.self_ty();
let self_ty_matches = match self_ty.kind() {
- ty::Dynamic(ref data, ty::ReStatic) => data.principal().is_none(),
+ ty::Dynamic(ref data, re) if re.is_static() => data.principal().is_none(),
_ => false,
};
Some(0) | None => false,
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
- Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(ty)),
+ Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(*ty)),
}
}
ty::Ref(..) => {
/// they carry no values.
impl UnifyKey for TyVid {
type Value = ();
+ #[inline]
fn index(&self) -> u32 {
self.as_u32()
}
+ #[inline]
fn from_index(i: u32) -> TyVid {
TyVid::from_u32(i)
}
fn index(&self) -> u32 {
self.index
}
+ #[inline]
fn from_index(i: u32) -> IntVid {
IntVid { index: i }
}
impl UnifyKey for FloatVid {
type Value = Option<FloatVarValue>;
+ #[inline]
fn index(&self) -> u32 {
self.index
}
+ #[inline]
fn from_index(i: u32) -> FloatVid {
FloatVid { index: i }
}
ty: Ty<'tcx>,
param: Option<&ty::GenericParamDef>,
span: Span,
- ) -> &'tcx Const<'tcx>;
+ ) -> Const<'tcx>;
/// Projecting an associated type from a (potentially)
/// higher-ranked trait reference is more complicated, because of
.find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id)
.is_some()
}
- fn trait_defines_associated_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
+ fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
self.tcx()
.associated_items(trait_def_id)
- .find_by_name_and_kinds(
- self.tcx(),
- assoc_name,
- &[ty::AssocKind::Type, ty::AssocKind::Const],
- trait_def_id,
- )
+ .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id)
.is_some()
}
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
- let assoc_item = tcx
- .associated_items(candidate.def_id())
- .filter_by_name_unhygienic(assoc_ident.name)
- .find(|i| {
- (i.kind == ty::AssocKind::Type || i.kind == ty::AssocKind::Const)
- && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
- })
+ let find_item_of_kind = |kind| {
+ tcx.associated_items(candidate.def_id())
+ .filter_by_name_unhygienic(assoc_ident.name)
+ .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
+ };
+ let assoc_item = find_item_of_kind(ty::AssocKind::Type)
+ .or_else(|| find_item_of_kind(ty::AssocKind::Const))
.expect("missing associated type");
if !assoc_item.vis.is_accessible_from(def_scope, tcx) {
// `trait_object_dummy_self`, so check for that.
let references_self = match pred.skip_binder().term {
ty::Term::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
- ty::Term::Const(c) => c.ty.walk().any(|arg| arg == dummy_self.into()),
+ ty::Term::Const(c) => c.ty().walk().any(|arg| arg == dummy_self.into()),
};
// If the projection output contains `Self`, force the user to
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
{
let mut matching_candidates = all_candidates()
- .filter(|r| self.trait_defines_associated_named(r.def_id(), assoc_name));
-
- let bound = match matching_candidates.next() {
- Some(bound) => bound,
- None => {
+ .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name));
+ let mut const_candidates = all_candidates()
+ .filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name));
+
+ let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) {
+ (Some(bound), _) => (bound, matching_candidates.next()),
+ (None, Some(bound)) => (bound, const_candidates.next()),
+ (None, None) => {
self.complain_about_assoc_type_not_found(
all_candidates,
&ty_param_name(),
return Err(ErrorReported);
}
};
-
debug!("one_bound_for_assoc_type: bound = {:?}", bound);
- if let Some(bound2) = matching_candidates.next() {
+ if let Some(bound2) = next_cand {
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);
let is_equality = is_equality();
return Err(ErrorReported);
}
}
+
Ok(bound)
}
// Find the type of the associated item, and the trait where the associated
// item is declared.
let bound = match (&qself_ty.kind(), qself_res) {
- (_, Res::SelfTy(Some(_), Some((impl_def_id, _)))) => {
+ (_, Res::SelfTy { trait_: Some(_), alias_to: Some((impl_def_id, _)) }) => {
// `Self` in an impl of a trait -- we have a concrete self type and a
// trait reference.
let trait_ref = match tcx.impl_trait_ref(impl_def_id) {
}
(
&ty::Param(_),
- Res::SelfTy(Some(param_did), None) | Res::Def(DefKind::TyParam, param_did),
+ Res::SelfTy { trait_: Some(param_did), alias_to: None }
+ | Res::Def(DefKind::TyParam, param_did),
) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?,
_ => {
if variant_resolution.is_some() {
// We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
// of calling `filter_by_name_and_kind`.
- let item = tcx
- .associated_items(trait_did)
- .in_definition_order()
- .find(|i| {
- i.kind.namespace() == Namespace::TypeNS
- && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
- })
- .expect("missing associated type");
+ let item = tcx.associated_items(trait_did).in_definition_order().find(|i| {
+ i.kind.namespace() == Namespace::TypeNS
+ && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
+ });
+ // Assume that if it's not matched, there must be a const defined with the same name
+ // but it was used in a type position.
+ let Some(item) = item else {
+ let msg = format!("found associated const `{assoc_ident}` when type was expected");
+ tcx.sess.struct_span_err(span, &msg).emit();
+ return Err(ErrorReported);
+ };
let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound);
let ty = self.normalize_ty(span, ty);
let index = generics.param_def_id_to_index[&def_id];
tcx.mk_ty_param(index, tcx.hir().name(hir_id))
}
- Res::SelfTy(Some(_), None) => {
+ Res::SelfTy { trait_: Some(_), alias_to: None } => {
// `Self` in trait or type alias.
assert_eq!(opt_self_ty, None);
self.prohibit_generics(path.segments);
tcx.types.self_param
}
- Res::SelfTy(_, Some((def_id, forbid_generic))) => {
+ Res::SelfTy { trait_: _, alias_to: Some((def_id, forbid_generic)) } => {
// `Self` in impl (we know the concrete type).
assert_eq!(opt_self_ty, None);
self.prohibit_generics(path.segments);
// Try to evaluate any array length constants.
- let normalized_ty = self.normalize_ty(span, tcx.at(span).type_of(def_id));
- if forbid_generic && normalized_ty.needs_subst() {
+ let ty = tcx.at(span).type_of(def_id);
+ // HACK(min_const_generics): Forbid generic `Self` types
+ // here as we can't easily do that during nameres.
+ //
+ // We do this before normalization as we otherwise allow
+ // ```rust
+ // trait AlwaysApplicable { type Assoc; }
+ // impl<T: ?Sized> AlwaysApplicable for T { type Assoc = usize; }
+ //
+ // trait BindsParam<T> {
+ // type ArrayTy;
+ // }
+ // impl<T> BindsParam<T> for <T as AlwaysApplicable>::Assoc {
+ // type ArrayTy = [u8; Self::MAX];
+ // }
+ // ```
+ // Note that the normalization happens in the param env of
+ // the anon const, which is empty. This is why the
+ // `AlwaysApplicable` impl needs a `T: ?Sized` bound for
+ // this to compile if we were to normalize here.
+ if forbid_generic && ty.needs_subst() {
let mut err = tcx.sess.struct_span_err(
path.span,
"generic `Self` types are currently not permitted in anonymous constants",
err.emit();
tcx.ty_error()
} else {
- normalized_ty
+ self.normalize_ty(span, ty)
}
}
Res::Def(DefKind::AssocTy, def_id) => {
// If any of the derived region bounds are 'static, that is always
// the best choice.
- if derived_region_bounds.iter().any(|&r| ty::ReStatic == *r) {
+ if derived_region_bounds.iter().any(|r| r.is_static()) {
return Some(tcx.lifetimes.re_static);
}
use rustc_hir::{self as hir, ExprKind};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::traits::Obligation;
-use rustc_middle::ty::{self, ToPredicate, Ty, TyS};
+use rustc_middle::ty::{self, ToPredicate, Ty};
use rustc_span::{MultiSpan, Span};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
use rustc_trait_selection::traits::{
pub(crate) fn opt_suggest_box_span(
&self,
span: Span,
- outer_ty: &'tcx TyS<'tcx>,
+ outer_ty: Ty<'tcx>,
orig_expected: Expectation<'tcx>,
) -> Option<Span> {
match (orig_expected, self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty))) {
},
};
autoref = Some(Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
target: method.sig.inputs()[0],
});
}
for (method_arg_ty, self_arg_ty) in
iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs())
{
- fcx.demand_eqtype(self.call_expr.span, &self_arg_ty, &method_arg_ty);
+ fcx.demand_eqtype(self.call_expr.span, *self_arg_ty, *method_arg_ty);
}
fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output());
err.emit();
}
CastError::CastToChar => {
- type_error_struct!(
+ let mut err = type_error_struct!(
fcx.tcx.sess,
self.span,
self.expr_ty,
E0604,
"only `u8` can be cast as `char`, not `{}`",
self.expr_ty
- )
- .span_label(self.span, "invalid cast")
- .emit();
+ );
+ err.span_label(self.span, "invalid cast");
+ if self.expr_ty.is_numeric() {
+ err.span_help(
+ self.span,
+ if self.expr_ty == fcx.tcx.types.i8 {
+ "try casting from `u8` instead"
+ } else if self.expr_ty == fcx.tcx.types.u32 {
+ "try `char::from_u32` instead"
+ } else {
+ "try `char::from_u32` instead (via a `u32`)"
+ },
+ );
+ }
+ err.emit();
}
CastError::NonScalar => {
let mut err = type_error_struct!(
.try_coerce(
self.expr,
fcx.tcx.mk_ref(
- &ty::RegionKind::ReErased,
+ fcx.tcx.lifetimes.re_erased,
TypeAndMut { ty: expr_ty, mutbl },
),
self.cast_ty,
.try_coerce(
self.expr,
fcx.tcx.mk_ref(
- &ty::RegionKind::ReErased,
+ fcx.tcx.lifetimes.re_erased,
TypeAndMut { ty: self.expr_ty, mutbl },
),
self.cast_ty,
});
// this will report a type mismatch if needed
- fcx.demand_eqtype(self.span, ety, m_cast.ty);
+ fcx.demand_eqtype(self.span, *ety, m_cast.ty);
return Ok(CastKind::ArrayPtrCast);
}
}
use rustc_middle::ty::layout::MAX_SIMD_LANES;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::util::{Discr, IntTypeExt};
-use rustc_middle::ty::{self, OpaqueTypeKey, ParamEnv, RegionKind, Ty, TyCtxt};
+use rustc_middle::ty::{self, OpaqueTypeKey, ParamEnv, Ty, TyCtxt};
use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
use rustc_span::symbol::sym;
use rustc_span::{self, MultiSpan, Span};
ty::Adt(ref adt, _) => {
adt.did == panic_info_did
&& mutbl == hir::Mutability::Not
- && *region != RegionKind::ReStatic
+ && !region.is_static()
}
_ => false,
},
tcx.sess,
field_span,
E0740,
- "unions may not contain fields that need dropping"
+ "unions cannot contain fields that may need dropping"
+ )
+ .note(
+ "a type is guaranteed not to need dropping \
+ when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type",
)
.multipart_suggestion_verbose(
- "wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped",
+ "when the type does not implement `Copy`, \
+ wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped",
vec![
(ty_span.shrink_to_lo(), format!("std::mem::ManuallyDrop<")),
(ty_span.shrink_to_hi(), ">".into()),
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
debug!("FindParentLifetimeVisitor: r={:?}", r);
- if let RegionKind::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = r {
- if *index < self.0.parent_count as u32 {
+ if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r {
+ if index < self.0.parent_count as u32 {
return ControlFlow::Break(FoundParentLifetime);
} else {
return ControlFlow::CONTINUE;
r.super_visit_with(self)
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::ConstKind::Unevaluated(..) = c.val {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::ConstKind::Unevaluated(..) = c.val() {
// FIXME(#72219) We currently don't detect lifetimes within substs
// which would violate this check. Even though the particular substitution is not used
// within the const, this should still be fixed.
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
match arg.kind {
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
- [PathSegment { res: Some(Res::SelfTy(_, impl_ref)), .. }] => {
+ [
+ PathSegment {
+ res: Some(Res::SelfTy { trait_: _, alias_to: impl_ref }),
+ ..
+ },
+ ] => {
let impl_ty_name =
impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id));
self.selftys.push((path.span, impl_ty_name));
.skip_binder()
.inputs()
.iter()
- .map(|ty| ArgKind::from_expected_ty(ty, None))
+ .map(|ty| ArgKind::from_expected_ty(*ty, None))
.collect();
let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) {
Some((sp, args)) => (Some(sp), args),
where
F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>,
{
- self.unify(&a, &b)
+ self.unify(a, b)
.and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations))
}
}
};
adjustments.push(Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(*r_borrow, mutbl)),
target: ty,
});
&mut diag,
&cause,
trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
- Some(infer::ValuePairs::Types(ExpectedFound {
- expected: trait_fty,
- found: impl_fty,
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_fty.into(),
+ found: impl_fty.into(),
})),
&terr,
false,
&mut diag,
&cause,
trait_c_span.map(|span| (span, "type in trait".to_owned())),
- Some(infer::ValuePairs::Types(ExpectedFound {
- expected: trait_ty,
- found: impl_ty,
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_ty.into(),
+ found: impl_ty.into(),
})),
&terr,
false,
GenericParamDefKind::Const { .. } => {
let bound_var = ty::BoundVariableKind::Const;
bound_vars.push(bound_var);
- tcx.mk_const(ty::Const {
+ tcx.mk_const(ty::ConstS {
ty: tcx.type_of(param.def_id),
val: ty::ConstKind::Bound(
ty::INNERMOST,
let mut selcx = traits::SelectionContext::new(&infcx);
let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
- let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
+ let normalize_cause = ObligationCause::new(
+ impl_ty_span,
+ impl_ty_hir_id,
+ ObligationCauseCode::CheckAssociatedTypeBounds {
+ impl_item_def_id: impl_ty.def_id,
+ trait_item_def_id: trait_ty.def_id,
+ },
+ );
let mk_cause = |span: Span| {
let code = if span.is_dummy() {
traits::MiscObligation
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr),
_,
&ty::Ref(_, checked, _),
- ) if self.infcx.can_sub(self.param_env, checked, &expected).is_ok() => {
+ ) if self.infcx.can_sub(self.param_env, checked, expected).is_ok() => {
// We have `&T`, check if what was expected was `T`. If so,
// we may want to suggest removing a `&`.
if sm.is_imported(expr.span) {
fn consts(
&mut self,
- a: &'tcx ty::Const<'tcx>,
- b: &'tcx ty::Const<'tcx>,
- ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b);
ty::relate::super_relate_consts(self, a, b)
}
// Places may legitimately have unsized types.
// For example, dereferences of a fat pointer and
// the last field of a struct can be unsized.
- ExpectHasType(ty)
+ ExpectHasType(*ty)
} else {
- Expectation::rvalue_hint(self, ty)
+ Expectation::rvalue_hint(self, *ty)
}
}
_ => NoExpectation,
base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>,
) -> Ty<'tcx> {
// Find the relevant variant
- let (variant, adt_ty) = if let Some(variant_ty) = self.check_struct_path(qpath, expr.hir_id)
- {
- variant_ty
- } else {
+ let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else {
self.check_struct_fields_on_error(fields, base_expr);
return self.tcx.ty_error();
};
) {
let len = remaining_fields.len();
- let mut displayable_field_names =
- remaining_fields.keys().map(|ident| ident.as_str()).collect::<Vec<_>>();
-
- displayable_field_names.sort();
+ let mut displayable_field_names: Vec<&str> =
+ remaining_fields.keys().map(|ident| ident.as_str()).collect();
+ // sorting &str primitives here, sort_unstable is ok
+ displayable_field_names.sort_unstable();
let mut truncated_fields_error = String::new();
let remaining_fields_names = match &displayable_field_names[..] {
expr: &hir::Expr<'_>,
base: &hir::Expr<'_>,
field: Ident,
- len: &ty::Const<'tcx>,
+ len: ty::Const<'tcx>,
) {
if let (Some(len), Ok(user_index)) =
(len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>())
fn no_such_field_err(
&self,
field: Ident,
- expr_t: &'tcx ty::TyS<'tcx>,
+ expr_t: Ty<'tcx>,
id: HirId,
) -> DiagnosticBuilder<'_> {
let span = field.span;
);
// try to add a suggestion in case the field is a nested field of a field of the Adt
- if let Some((fields, substs)) = self.get_field_candidates(span, &expr_t) {
+ if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
for candidate_field in fields.iter() {
if let Some(field_path) = self.check_for_nested_field(
span,
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
let field_ty = candidate_field.ty(self.tcx, subst);
- if let Some((nested_fields, subst)) = self.get_field_candidates(span, &field_ty) {
+ if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
for field in nested_fields.iter() {
let accessible = field.vis.is_accessible_from(id, self.tcx);
if accessible {
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
- let ty = self.structurally_resolved_type(expr.span, &ty);
+ let ty = self.structurally_resolved_type(expr.span, ty);
match *ty.kind() {
ty::FnDef(..) => {
let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx));
// unconstrained opaque type variables, in addition to performing
// other kinds of fallback.
for ty in &self.unsolved_variables() {
- fallback_has_occurred |= self.fallback_opaque_type_vars(ty);
+ fallback_has_occurred |= self.fallback_opaque_type_vars(*ty);
}
// See if we can make any more progress.
.type_var_origin(ty)
.map(|origin| origin.span)
.unwrap_or(rustc_span::DUMMY_SP);
- let oty = self.inner.borrow().opaque_types_vars.get(ty).copied();
+ let oty = self.inner.borrow().opaque_types_vars.get(&ty).copied();
if let Some(opaque_ty) = oty {
debug!(
"fallback_opaque_type_vars(ty={:?}): falling back to opaque type {:?}",
ty
}
- pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> &'tcx ty::Const<'tcx> {
+ pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> {
match length {
&hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span),
hir::ArrayLen::Body(anon_const) => self.to_const(anon_const),
}
}
- pub fn to_const(&self, ast_c: &hir::AnonConst) -> &'tcx ty::Const<'tcx> {
+ pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> {
let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id);
let c = ty::Const::from_anon_const(self.tcx, const_def_id);
self.register_wf_obligation(
&self,
ast_c: &hir::AnonConst,
param_def_id: DefId,
- ) -> &'tcx ty::Const<'tcx> {
+ ) -> ty::Const<'tcx> {
let const_def = ty::WithOptConstParam {
did: self.tcx.hir().local_def_id(ast_c.hir_id),
const_param_did: Some(param_def_id),
field: &'tcx ty::FieldDef,
substs: SubstsRef<'tcx>,
) -> Ty<'tcx> {
- self.normalize_associated_types_in(span, &field.ty(self.tcx, substs))
+ self.normalize_associated_types_in(span, field.ty(self.tcx, substs))
}
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
// is polymorphic) and the expected return type.
// No argument expectations are produced if unification fails.
let origin = self.misc(call_span);
- let ures = self.at(&origin, self.param_env).sup(ret_ty, &formal_ret);
+ let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret);
// FIXME(#27336) can't use ? here, Try::from_error doesn't default
// to identity so the resulting type is not constrained.
_ => bug!("unexpected type: {:?}", ty),
},
Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
- | Res::SelfTy(..) => match ty.kind() {
+ | Res::SelfTy { .. } => match ty.kind() {
ty::Adt(adt, substs) if !adt.is_enum() => {
Some((adt.non_enum_variant(), adt.did, substs))
}
ty: Ty<'tcx>,
param: Option<&ty::GenericParamDef>,
span: Span,
- ) -> &'tcx Const<'tcx> {
+ ) -> Const<'tcx> {
if let Some(param) = param {
if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() {
return ct;
if ty.has_escaping_bound_vars() {
ty // FIXME: normalization and escaping regions
} else {
- self.normalize_associated_types_in(span, &ty)
+ self.normalize_associated_types_in(span, ty)
}
}
debug!(
"local variable {:?} is assigned type {}",
decl.pat,
- self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty)
+ self.fcx.ty_to_string(self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty)
);
}
}
debug!(
"pattern binding {} is assigned to {} with type {:?}",
ident,
- self.fcx.ty_to_string(&*self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
+ self.fcx.ty_to_string(self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty),
var_ty
);
}
mod drop_ranges;
-// FIXME(eholk): This flag is here to give a quick way to disable drop tracking in case we find
-// unexpected breakages while it's still new. It should be removed before too long. For example,
-// see #93161.
-const ENABLE_DROP_TRACKING: bool = false;
-
struct InteriorVisitor<'a, 'tcx> {
fcx: &'a FnCtxt<'a, 'tcx>,
types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
yield_data.expr_and_pat_count, self.expr_count, source_span
);
- if ENABLE_DROP_TRACKING
+ if self.fcx.sess().opts.debugging_opts.drop_tracking
&& self
.drop_ranges
.is_dropped_at(hir_id, yield_data.expr_and_pat_count)
self.types.insert(ty::GeneratorInteriorTypeCause {
span: source_span,
- ty: &ty,
+ ty,
scope_span,
yield_span: yield_data.span,
expr: expr.map(|e| e.hir_id),
};
intravisit::walk_body(&mut visitor, body);
- // Check that we visited the same amount of expressions and the RegionResolutionVisitor
+ // Check that we visited the same amount of expressions as the RegionResolutionVisitor
let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
assert_eq!(region_expr_count, visitor.expr_count);
let tcx = self.fcx.tcx;
let ref_ty = tcx.mk_ref(
// Use `ReErased` as `resolve_interior` is going to replace all the regions anyway.
- tcx.mk_region(ty::RegionKind::ReErased),
+ tcx.mk_region(ty::ReErased),
ty::TypeAndMut { ty, mutbl: hir::Mutability::Not },
);
self.record(
def_id: DefId,
body: &'tcx Body<'tcx>,
) -> DropRanges {
- if super::ENABLE_DROP_TRACKING {
+ if fcx.sess().opts.debugging_opts.drop_tracking {
let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body);
let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0);
TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id,
}
}
+
+ fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self {
+ match place_with_id.place.base {
+ PlaceBase::Rvalue | PlaceBase::StaticItem => {
+ TrackedValue::Temporary(place_with_id.hir_id)
+ }
+ PlaceBase::Local(hir_id)
+ | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => {
+ TrackedValue::Variable(hir_id)
+ }
+ }
+ }
}
/// Represents a reason why we might not be able to convert a HirId or Place
return Err(TrackedValueConversionError::PlaceProjectionsNotSupported);
}
- match place_with_id.place.base {
- PlaceBase::Rvalue | PlaceBase::StaticItem => {
- Ok(TrackedValue::Temporary(place_with_id.hir_id))
- }
- PlaceBase::Local(hir_id)
- | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => {
- Ok(TrackedValue::Variable(hir_id))
- }
- }
+ Ok(TrackedValue::from_place_with_projections_allowed(place_with_id))
}
}
};
use hir::{
intravisit::{self, Visitor},
- Body, Expr, ExprKind, Guard, HirId,
+ Body, Expr, ExprKind, Guard, HirId, LoopIdError,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
expr_index: PostOrderId,
tcx: TyCtxt<'tcx>,
typeck_results: &'a TypeckResults<'tcx>,
+ label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>,
}
impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> {
hir,
num_exprs,
);
- Self { hir, places, drop_ranges, expr_index: PostOrderId::from_u32(0), typeck_results, tcx }
+ Self {
+ hir,
+ places,
+ drop_ranges,
+ expr_index: PostOrderId::from_u32(0),
+ typeck_results,
+ tcx,
+ label_stack: vec![],
+ }
}
fn record_drop(&mut self, value: TrackedValue) {
self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1);
}
}
+
+ /// Map a Destination to an equivalent expression node
+ ///
+ /// The destination field of a Break or Continue expression can target either an
+ /// expression or a block. The drop range analysis, however, only deals in
+ /// expression nodes, so blocks that might be the destination of a Break or Continue
+ /// will not have a PostOrderId.
+ ///
+ /// If the destination is an expression, this function will simply return that expression's
+ /// hir_id. If the destination is a block, this function will return the hir_id of last
+ /// expression in the block.
+ fn find_target_expression_from_destination(
+ &self,
+ destination: hir::Destination,
+ ) -> Result<HirId, LoopIdError> {
+ destination.target_id.map(|target| {
+ let node = self.hir.get(target);
+ match node {
+ hir::Node::Expr(_) => target,
+ hir::Node::Block(b) => find_last_block_expression(b),
+ hir::Node::Param(..)
+ | hir::Node::Item(..)
+ | hir::Node::ForeignItem(..)
+ | hir::Node::TraitItem(..)
+ | hir::Node::ImplItem(..)
+ | hir::Node::Variant(..)
+ | hir::Node::Field(..)
+ | hir::Node::AnonConst(..)
+ | hir::Node::Stmt(..)
+ | hir::Node::PathSegment(..)
+ | hir::Node::Ty(..)
+ | hir::Node::TraitRef(..)
+ | hir::Node::Binding(..)
+ | hir::Node::Pat(..)
+ | hir::Node::Arm(..)
+ | hir::Node::Local(..)
+ | hir::Node::Ctor(..)
+ | hir::Node::Lifetime(..)
+ | hir::Node::GenericParam(..)
+ | hir::Node::Visibility(..)
+ | hir::Node::Crate(..)
+ | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
+ }
+ })
+ }
+}
+
+fn find_last_block_expression(block: &hir::Block<'_>) -> HirId {
+ block.expr.map_or_else(
+ // If there is no tail expression, there will be at least one statement in the
+ // block because the block contains a break or continue statement.
+ || block.stmts.last().unwrap().hir_id,
+ |expr| expr.hir_id,
+ )
}
impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> {
});
}
- ExprKind::Loop(body, ..) => {
+ ExprKind::Loop(body, label, ..) => {
let loop_begin = self.expr_index + 1;
+ self.label_stack.push((label, loop_begin));
if body.stmts.is_empty() && body.expr.is_none() {
// For empty loops we won't have updated self.expr_index after visiting the
// body, meaning we'd get an edge from expr_index to expr_index + 1, but
self.visit_block(body);
self.drop_ranges.add_control_edge(self.expr_index, loop_begin);
}
+ self.label_stack.pop();
}
- ExprKind::Break(hir::Destination { target_id: Ok(target), .. }, ..)
- | ExprKind::Continue(hir::Destination { target_id: Ok(target), .. }, ..) => {
- self.drop_ranges.add_control_edge_hir_id(self.expr_index, target);
+ // Find the loop entry by searching through the label stack for either the last entry
+ // (if label is none), or the first entry where the label matches this one. The Loop
+ // case maintains this stack mapping labels to the PostOrderId for the loop entry.
+ ExprKind::Continue(hir::Destination { label, .. }, ..) => self
+ .label_stack
+ .iter()
+ .rev()
+ .find(|(loop_label, _)| label.is_none() || *loop_label == label)
+ .map_or((), |(_, target)| {
+ self.drop_ranges.add_control_edge(self.expr_index, *target)
+ }),
+
+ ExprKind::Break(destination, ..) => {
+ // destination either points to an expression or to a block. We use
+ // find_target_expression_from_destination to use the last expression of the block
+ // if destination points to a block.
+ //
+ // We add an edge to the hir_id of the expression/block we are breaking out of, and
+ // then in process_deferred_edges we will map this hir_id to its PostOrderId, which
+ // will refer to the end of the block due to the post order traversal.
+ self.find_target_expression_from_destination(destination).map_or((), |target| {
+ self.drop_ranges.add_control_edge_hir_id(self.expr_index, target)
+ })
}
ExprKind::Call(f, args) => {
| ExprKind::Binary(..)
| ExprKind::Block(..)
| ExprKind::Box(..)
- | ExprKind::Break(..)
| ExprKind::Cast(..)
| ExprKind::Closure(..)
| ExprKind::ConstBlock(..)
- | ExprKind::Continue(..)
| ExprKind::DropTemps(..)
| ExprKind::Err
| ExprKind::Field(..)
/// Should be called after visiting the HIR but before solving the control flow, otherwise some
/// edges will be missed.
fn process_deferred_edges(&mut self) {
+ trace!("processing deferred edges. post_order_map={:#?}", self.post_order_map);
let mut edges = vec![];
swap(&mut edges, &mut self.deferred_edges);
edges.into_iter().for_each(|(from, to)| {
- let to = *self.post_order_map.get(&to).expect("Expression ID not found");
trace!("Adding deferred edge from {:?} to {:?}", from, to);
+ let to = *self.post_order_map.get(&to).expect("Expression ID not found");
+ trace!("target edge PostOrderId={:?}", to);
self.add_control_edge(from, to)
});
}
_diag_expr_id: HirId,
_bk: rustc_middle::ty::BorrowKind,
) {
- place_with_id
- .try_into()
- .map_or(false, |tracked_value| self.places.borrowed.insert(tracked_value));
+ self.places
+ .borrowed
+ .insert(TrackedValue::from_place_with_projections_allowed(place_with_id));
}
fn mutate(
if unsize {
let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() {
- self.tcx.mk_slice(elem_ty)
+ self.tcx.mk_slice(*elem_ty)
} else {
bug!(
"AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}",
}
Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => {
target = match target.kind() {
- ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
- assert_eq!(*mutbl, hir::Mutability::Mut);
+ &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => {
+ assert_eq!(mutbl, hir::Mutability::Mut);
self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty })
}
other => panic!("Cannot adjust receiver type {:?} to const ptr", other),
if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
let trait_type = self
.tcx
- .mk_ref(region, ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() });
+ .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() });
// We probe again to see if there might be a borrow mutability discrepancy.
match self.lookup_probe(
span,
use crate::hir::def_id::DefId;
use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::sync::Lrc;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Namespace;
/// This is the OriginalQueryValues for the steps queries
/// that are answered in steps.
orig_steps_var_values: OriginalQueryValues<'tcx>,
- steps: Lrc<Vec<CandidateStep<'tcx>>>,
+ steps: &'tcx [CandidateStep<'tcx>],
inherent_candidates: Vec<Candidate<'tcx>>,
extension_candidates: Vec<Candidate<'tcx>>,
param_env_and_self_ty, self_ty
);
MethodAutoderefStepsResult {
- steps: Lrc::new(vec![CandidateStep {
+ steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
self_ty: self.make_query_response_ignoring_pending_obligations(
canonical_inference_vars,
self_ty,
steps.push(CandidateStep {
self_ty: infcx.make_query_response_ignoring_pending_obligations(
inference_vars,
- infcx.tcx.mk_slice(elem_ty),
+ infcx.tcx.mk_slice(*elem_ty),
),
autoderefs: dereferences,
// this could be from an unsafe deref if we had
debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty);
MethodAutoderefStepsResult {
- steps: Lrc::new(steps),
- opt_bad_ty: opt_bad_ty.map(Lrc::new),
+ steps: tcx.arena.alloc_from_iter(steps),
+ opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)),
reached_recursion_limit: autoderef.reached_recursion_limit(),
}
})
method_name: Option<Ident>,
return_type: Option<Ty<'tcx>>,
orig_steps_var_values: OriginalQueryValues<'tcx>,
- steps: Lrc<Vec<CandidateStep<'tcx>>>,
+ steps: &'tcx [CandidateStep<'tcx>],
is_suggestion: IsSuggestion,
scope_expr_id: hir::HirId,
) -> ProbeContext<'a, 'tcx> {
}
fn assemble_inherent_candidates(&mut self) {
- let steps = Lrc::clone(&self.steps);
- for step in steps.iter() {
+ for step in self.steps.iter() {
self.assemble_probe(&step.self_ty);
}
}
}
let ty = match self_ty.kind() {
- ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty,
+ &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) => ty,
_ => return None,
};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_middle::ty::fast_reject::{simplify_type, SimplifyParams, StripReferences};
+use rustc_middle::traits::util::supertraits;
+use rustc_middle::ty::fast_reject::{simplify_type, SimplifyParams};
use rustc_middle::ty::print::with_crate_prefix;
+use rustc_middle::ty::ToPolyTraitRef;
use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable};
use rustc_span::lev_distance;
use rustc_span::symbol::{kw, sym, Ident};
Err(..) => return false,
};
+ // This conditional prevents us from asking to call errors and unresolved types.
+ // It might seem that we can use `predicate_must_hold_modulo_regions`,
+ // but since a Dummy binder is used to fill in the FnOnce trait's arguments,
+ // type resolution always gives a "maybe" here.
+ if self.autoderef(span, ty).any(|(ty, _)| {
+ info!("check deref {:?} error", ty);
+ matches!(ty.kind(), ty::Error(_) | ty::Infer(_))
+ }) {
+ return false;
+ }
+
self.autoderef(span, ty).any(|(ty, _)| {
+ info!("check deref {:?} impl FnOnce", ty);
self.probe(|_| {
let fn_once_substs = tcx.mk_substs_trait(
ty,
let mut bound_spans = vec![];
let mut collect_type_param_suggestions =
- |self_ty: Ty<'tcx>, parent_pred: &ty::Predicate<'tcx>, obligation: &str| {
+ |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| {
// We don't care about regions here, so it's fine to skip the binder here.
if let (ty::Param(_), ty::PredicateKind::Trait(p)) =
(self_ty.kind(), parent_pred.kind().skip_binder())
.filter(|(pred, _, _parent_pred)| !skip_list.contains(&pred))
.filter_map(|(pred, parent_pred, _cause)| {
format_pred(*pred).map(|(p, self_ty)| {
- collect_type_param_suggestions(self_ty, pred, &p);
+ collect_type_param_suggestions(self_ty, *pred, &p);
match parent_pred {
None => format!("`{}`", &p),
Some(parent_pred) => match format_pred(*parent_pred) {
Some((parent_p, _)) => {
collect_type_param_suggestions(
self_ty,
- parent_pred,
+ *parent_pred,
&p,
);
format!("`{}`\nwhich is required by `{}`", p, parent_p)
if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() {
if needs_mut {
let trait_type = self.tcx.mk_ref(
- region,
- ty::TypeAndMut { ty: t_type, mutbl: mutability.invert() },
+ *region,
+ ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() },
);
err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty));
}
Some(adt) if adt.did.is_local() => adt,
_ => continue,
};
- let can_derive = match self.tcx.get_diagnostic_name(trait_pred.def_id()) {
- Some(sym::Default) => !adt.is_enum(),
- Some(
+ if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
+ let can_derive = match diagnostic_name {
+ sym::Default => !adt.is_enum(),
sym::Eq
| sym::PartialEq
| sym::Ord
| sym::Clone
| sym::Copy
| sym::Hash
- | sym::Debug,
- ) => true,
- _ => false,
- };
- if can_derive {
- derives.push((
- format!("{}", trait_pred.self_ty()),
- self.tcx.def_span(adt.did),
- format!("{}", trait_pred.trait_ref.print_only_trait_name()),
- ));
+ | sym::Debug => true,
+ _ => false,
+ };
+ if can_derive {
+ let self_name = trait_pred.self_ty().to_string();
+ let self_span = self.tcx.def_span(adt.did);
+ if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() {
+ for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref())
+ {
+ if let Some(parent_diagnostic_name) =
+ self.tcx.get_diagnostic_name(super_trait.def_id())
+ {
+ derives.push((
+ self_name.clone(),
+ self_span.clone(),
+ parent_diagnostic_name.to_string(),
+ ));
+ }
+ }
+ }
+ derives.push((self_name, self_span, diagnostic_name.to_string()));
+ } else {
+ traits.push(self.tcx.def_span(trait_pred.def_id()));
+ }
} else {
traits.push(self.tcx.def_span(trait_pred.def_id()));
}
// just this list.
for (rcvr_ty, post) in &[
(rcvr_ty, ""),
- (self.tcx.mk_mut_ref(&ty::ReErased, rcvr_ty), "&mut "),
- (self.tcx.mk_imm_ref(&ty::ReErased, rcvr_ty), "&"),
+ (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
+ (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
] {
if let Ok(pick) = self.lookup_probe(
span,
item_name,
- rcvr_ty,
+ *rcvr_ty,
rcvr,
crate::check::method::probe::ProbeScope::AllTraits,
) {
break;
}
for (rcvr_ty, pre) in &[
- (self.tcx.mk_lang_item(rcvr_ty, LangItem::OwnedBox), "Box::new"),
- (self.tcx.mk_lang_item(rcvr_ty, LangItem::Pin), "Pin::new"),
- (self.tcx.mk_diagnostic_item(rcvr_ty, sym::Arc), "Arc::new"),
- (self.tcx.mk_diagnostic_item(rcvr_ty, sym::Rc), "Rc::new"),
+ (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"),
+ (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"),
+ (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"),
+ (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
] {
if let Some(new_rcvr_t) = *rcvr_ty {
if let Ok(pick) = self.lookup_probe(
let table_owner = table.borrow().hir_owner;
let generics = self.tcx.generics_of(table_owner.to_def_id());
let type_param = generics.type_param(param, self.tcx);
- let hir = &self.tcx.hir();
+ let hir = self.tcx.hir();
if let Some(def_id) = type_param.def_id.as_local() {
let id = hir.local_def_id_to_hir_id(def_id);
// Get the `hir::Param` to verify whether it already has any bounds.
// FIXME: Even though negative bounds are not implemented, we could maybe handle
// cases where a positive bound implies a negative impl.
(candidates, Vec::new())
- } else if let Some(simp_rcvr_ty) =
- simplify_type(self.tcx, rcvr_ty, SimplifyParams::Yes, StripReferences::No)
+ } else if let Some(simp_rcvr_ty) = simplify_type(self.tcx, rcvr_ty, SimplifyParams::Yes)
{
let mut potential_candidates = Vec::new();
let mut explicitly_negative = Vec::new();
})
.any(|imp_did| {
let imp = self.tcx.impl_trait_ref(imp_did).unwrap();
- let imp_simp = simplify_type(
- self.tcx,
- imp.self_ty(),
- SimplifyParams::Yes,
- StripReferences::No,
- );
+ let imp_simp =
+ simplify_type(self.tcx, imp.self_ty(), SimplifyParams::Yes);
imp_simp.map_or(false, |s| s == simp_rcvr_ty)
})
{
[] => {}
[trait_info] => {
let msg = format!(
- "the trait `{}` defines an item `{}`, but is explicitely unimplemented",
+ "the trait `{}` defines an item `{}`, but is explicitly unimplemented",
self.tcx.def_path_str(trait_info.def_id),
item_name
);
}
trait_infos => {
let mut msg = format!(
- "the following traits define an item `{}`, but are explicitely unimplemented:",
+ "the following traits define an item `{}`, but are explicitly unimplemented:",
item_name
);
for trait_info in trait_infos {
},
};
let autoref = Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
target: method.sig.inputs()[0],
};
self.apply_adjustments(lhs_expr, vec![autoref]);
},
};
let autoref = Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)),
target: method.sig.inputs()[1],
};
// HACK(eddyb) Bypass checks due to reborrows being in
}
};
if let Ref(_, rty, _) = lhs_ty.kind() {
- if self.infcx.type_is_copy_modulo_regions(self.param_env, rty, lhs_expr.span)
- && self.lookup_op_method(rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok()
+ if self.infcx.type_is_copy_modulo_regions(self.param_env, *rty, lhs_expr.span)
+ && self.lookup_op_method(*rty, &[rhs_ty], Op::Binary(op, is_assign)).is_ok()
{
if let Ok(lstring) = source_map.span_to_snippet(lhs_expr.span) {
let msg = &format!(
self.tcx,
self.body_id,
&mut err,
- ty,
+ *ty,
rhs_ty,
missing_trait,
p,
/// Dereferences a single level of immutable referencing.
fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
match ty.kind() {
- ty::Ref(_, ty, hir::Mutability::Not) => ty,
+ ty::Ref(_, ty, hir::Mutability::Not) => *ty,
_ => ty,
}
}
element_ty: Ty<'tcx>,
arr_ty: Ty<'tcx>,
slice: Option<&'tcx Pat<'tcx>>,
- len: &ty::Const<'tcx>,
+ len: ty::Const<'tcx>,
min_len: u64,
) -> (Option<Ty<'tcx>>, Ty<'tcx>) {
if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) {
err.help("the semantics of slice patterns changed recently; see issue #62254");
}
} else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span)
- .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..)))
+ .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
{
if let (Some(span), true) = (ti.span, ti.origin_expr) {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- let applicability = match self.resolve_vars_if_possible(ti.expected).kind() {
- ty::Adt(adt_def, _)
- if self.tcx.is_diagnostic_item(sym::Option, adt_def.did)
- || self.tcx.is_diagnostic_item(sym::Result, adt_def.did) =>
- {
- // Slicing won't work here, but `.as_deref()` might (issue #91328).
- err.span_suggestion(
- span,
- "consider using `as_deref` here",
- format!("{}.as_deref()", snippet),
- Applicability::MaybeIncorrect,
- );
- None
- }
- // FIXME: instead of checking for Vec only, we could check whether the
- // type implements `Deref<Target=X>`; see
- // https://github.com/rust-lang/rust/pull/91343#discussion_r761466979
- ty::Adt(adt_def, _)
- if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did) =>
- {
- Some(Applicability::MachineApplicable)
+ let applicability = Autoderef::new(
+ &self.infcx,
+ self.param_env,
+ self.body_id,
+ span,
+ self.resolve_vars_if_possible(ti.expected),
+ span,
+ )
+ .find_map(|(ty, _)| {
+ match ty.kind() {
+ ty::Adt(adt_def, _)
+ if self.tcx.is_diagnostic_item(sym::Option, adt_def.did)
+ || self.tcx.is_diagnostic_item(sym::Result, adt_def.did) =>
+ {
+ // Slicing won't work here, but `.as_deref()` might (issue #91328).
+ err.span_suggestion(
+ span,
+ "consider using `as_deref` here",
+ format!("{}.as_deref()", snippet),
+ Applicability::MaybeIncorrect,
+ );
+ Some(None)
+ }
+
+ ty::Slice(..) | ty::Array(..) => {
+ Some(Some(Applicability::MachineApplicable))
+ }
+
+ _ => None,
}
- _ => Some(Applicability::MaybeIncorrect),
- };
+ })
+ .unwrap_or(Some(Applicability::MaybeIncorrect));
if let Some(applicability) = applicability {
err.span_suggestion(
self.apply_adjustments(
oprnd_expr,
vec![Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)),
target: method.sig.inputs()[0],
}],
);
if unsize {
// We only unsize arrays here.
if let ty::Array(element_ty, _) = adjusted_ty.kind() {
- self_ty = self.tcx.mk_slice(element_ty);
+ self_ty = self.tcx.mk_slice(*element_ty);
} else {
continue;
}
let mut adjustments = self.adjust_steps(autoderef);
if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() {
adjustments.push(Adjustment {
- kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
+ kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)),
target: self.tcx.mk_ref(
- region,
+ *region,
ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
),
});
// not the case today.
allow_two_phase_borrow: AllowTwoPhase::No,
};
- adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
- adjustment.target =
- self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
+ adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl));
+ adjustment.target = self
+ .tcx
+ .mk_ref(*region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
}
source = adjustment.target;
}
let rptr_ty = self.resolve_node_type(id);
if let ty::Ref(r, _, _) = rptr_ty.kind() {
debug!("rptr_ty={}", rptr_ty);
- self.link_region(span, r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed);
+ self.link_region(span, *r, ty::BorrowKind::from_mutbl(mutbl), cmt_borrowed);
}
}
self.tcx,
ty,
max_capture_info.capture_kind,
- Some(&ty::ReErased),
+ Some(self.tcx.lifetimes.re_erased),
)
}
};
self.tcx,
capture.place.ty(),
capture.info.capture_kind,
- Some(&ty::ReErased),
+ Some(self.tcx.lifetimes.re_erased),
);
// Checks if a capture implements any of the auto traits
// If the data will be moved out of this place, then the place will be truncated
// at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into
// the closure.
- hir::CaptureBy::Value if !place.deref_tys().any(ty::TyS::is_ref) => {
+ hir::CaptureBy::Value if !place.deref_tys().any(Ty::is_ref) => {
ty::UpvarCapture::ByValue
}
hir::CaptureBy::Value | hir::CaptureBy::Ref => ty::UpvarCapture::ByRef(ty::ImmBorrow),
match p.kind {
ProjectionKind::Field(..) => match ty.kind() {
ty::Adt(def, _) if def.repr.packed() => {
- match tcx.layout_of(param_env.and(p.ty)) {
+ // We erase regions here because they cannot be hashed
+ match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) {
Ok(layout) if layout.align.abi.bytes() == 1 => {
// if the alignment is 1, the type can't be further
// disaligned.
);
// Raw pointers don't inherit mutability
- if place_with_id.place.deref_tys().any(ty::TyS::is_unsafe_ptr) {
+ if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) {
capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow);
}
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId};
.emit();
}
}
-
- check_gat_where_clauses(tcx, trait_item, encl_trait_def_id);
}
/// Require that the user writes where clauses on GATs for the implicit
/// outlives bounds involving trait parameters in trait functions and
/// lifetimes passed as GAT substs. See `self-outlives-lint` test.
///
-/// This trait will be our running example. We are currently WF checking the `Item` item...
-///
-/// ```rust
-/// trait LendingIterator {
-/// type Item<'me>; // <-- WF checking this trait item
-///
-/// fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
+/// We use the following trait as an example throughout this function:
+/// ```rust,ignore (this code fails due to this lint)
+/// trait IntoIter {
+/// type Iter<'a>: Iterator<Item = Self::Item<'a>>;
+/// type Item<'a>;
+/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>;
/// }
/// ```
-fn check_gat_where_clauses(
- tcx: TyCtxt<'_>,
- trait_item: &hir::TraitItem<'_>,
- encl_trait_def_id: DefId,
-) {
- let item = tcx.associated_item(trait_item.def_id);
- // If the current trait item isn't a type, it isn't a GAT
- if !matches!(item.kind, ty::AssocKind::Type) {
- return;
- }
- let generics: &ty::Generics = tcx.generics_of(trait_item.def_id);
- // If the current associated type doesn't have any (own) params, it's not a GAT
- // FIXME(jackh726): we can also warn in the more general case
- if generics.params.len() == 0 {
- return;
- }
- let associated_items: &ty::AssocItems<'_> = tcx.associated_items(encl_trait_def_id);
- let mut clauses: Option<FxHashSet<ty::Predicate<'_>>> = None;
- // For every function in this trait...
- // In our example, this would be the `next` method
- for item in
- associated_items.in_definition_order().filter(|item| matches!(item.kind, ty::AssocKind::Fn))
- {
- // The clauses we that we would require from this function
- let mut function_clauses = FxHashSet::default();
-
- let id = hir::HirId::make_owner(item.def_id.expect_local());
- let param_env = tcx.param_env(item.def_id.expect_local());
-
- let sig = tcx.fn_sig(item.def_id);
- // Get the signature using placeholders. In our example, this would
- // convert the late-bound 'a into a free region.
- let sig = tcx.liberate_late_bound_regions(item.def_id, sig);
- // Collect the arguments that are given to this GAT in the return type
- // of the function signature. In our example, the GAT in the return
- // type is `<Self as LendingIterator>::Item<'a>`, so 'a and Self are arguments.
- let (regions, types) =
- GATSubstCollector::visit(tcx, trait_item.def_id.to_def_id(), sig.output());
-
- // If both regions and types are empty, then this GAT isn't in the
- // return type, and we shouldn't try to do clause analysis
- // (particularly, doing so would end up with an empty set of clauses,
- // since the current method would require none, and we take the
- // intersection of requirements of all methods)
- if types.is_empty() && regions.is_empty() {
- continue;
- }
-
- // The types we can assume to be well-formed. In our example, this
- // would be &'a mut Self, from the first argument.
- let mut wf_tys = FxHashSet::default();
- wf_tys.extend(sig.inputs());
-
- // For each region argument (e.g., 'a in our example), check for a
- // relationship to the type arguments (e.g., Self). If there is an
- // outlives relationship (`Self: 'a`), then we want to ensure that is
- // reflected in a where clause on the GAT itself.
- for (region, region_idx) in ®ions {
- // Ignore `'static` lifetimes for the purpose of this lint: it's
- // because we know it outlives everything and so doesn't give meaninful
- // clues
- if let ty::ReStatic = region {
+fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRef]) {
+ // Associates every GAT's def_id to a list of possibly missing bounds detected by this lint.
+ let mut required_bounds_by_item = FxHashMap::default();
+
+ // Loop over all GATs together, because if this lint suggests adding a where-clause bound
+ // to one GAT, it might then require us to an additional bound on another GAT.
+ // In our `IntoIter` example, we discover a missing `Self: 'a` bound on `Iter<'a>`, which
+ // then in a second loop adds a `Self: 'a` bound to `Item` due to the relationship between
+ // those GATs.
+ loop {
+ let mut should_continue = false;
+ for gat_item in associated_items {
+ let gat_def_id = gat_item.id.def_id;
+ let gat_item = tcx.associated_item(gat_def_id);
+ // If this item is not an assoc ty, or has no substs, then it's not a GAT
+ if gat_item.kind != ty::AssocKind::Type {
continue;
}
- for (ty, ty_idx) in &types {
- // In our example, requires that Self: 'a
- if ty_known_to_outlive(tcx, id, param_env, &wf_tys, *ty, *region) {
- debug!(?ty_idx, ?region_idx);
- debug!("required clause: {} must outlive {}", ty, region);
- // Translate into the generic parameters of the GAT. In
- // our example, the type was Self, which will also be
- // Self in the GAT.
- let ty_param = generics.param_at(*ty_idx, tcx);
- let ty_param = tcx.mk_ty(ty::Param(ty::ParamTy {
- index: ty_param.index,
- name: ty_param.name,
- }));
- // Same for the region. In our example, 'a corresponds
- // to the 'me parameter.
- let region_param = generics.param_at(*region_idx, tcx);
- let region_param =
- tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
- def_id: region_param.def_id,
- index: region_param.index,
- name: region_param.name,
- }));
- // The predicate we expect to see. (In our example,
- // `Self: 'me`.)
- let clause = ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(
- ty_param,
- region_param,
- ));
- let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
- function_clauses.insert(clause);
- }
- }
- }
-
- // For each region argument (e.g., 'a in our example), also check for a
- // relationship to the other region arguments. If there is an
- // outlives relationship, then we want to ensure that is
- // reflected in a where clause on the GAT itself.
- for (region_a, region_a_idx) in ®ions {
- // Ignore `'static` lifetimes for the purpose of this lint: it's
- // because we know it outlives everything and so doesn't give meaninful
- // clues
- if let ty::ReStatic = region_a {
+ let gat_generics = tcx.generics_of(gat_def_id);
+ // FIXME(jackh726): we can also warn in the more general case
+ if gat_generics.params.is_empty() {
continue;
}
- for (region_b, region_b_idx) in ®ions {
- if region_a == region_b {
- continue;
- }
- if let ty::ReStatic = region_b {
+
+ // Gather the bounds with which all other items inside of this trait constrain the GAT.
+ // This is calculated by taking the intersection of the bounds that each item
+ // constrains the GAT with individually.
+ let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
+ for item in associated_items {
+ let item_def_id = item.id.def_id;
+ // Skip our own GAT, since it does not constrain itself at all.
+ if item_def_id == gat_def_id {
continue;
}
- if region_known_to_outlive(tcx, id, param_env, &wf_tys, *region_a, *region_b) {
- debug!(?region_a_idx, ?region_b_idx);
- debug!("required clause: {} must outlive {}", region_a, region_b);
- // Translate into the generic parameters of the GAT.
- let region_a_param = generics.param_at(*region_a_idx, tcx);
- let region_a_param =
- tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
- def_id: region_a_param.def_id,
- index: region_a_param.index,
- name: region_a_param.name,
- }));
- // Same for the region.
- let region_b_param = generics.param_at(*region_b_idx, tcx);
- let region_b_param =
- tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
- def_id: region_b_param.def_id,
- index: region_b_param.index,
- name: region_b_param.name,
- }));
- // The predicate we expect to see.
- let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
- region_a_param,
- region_b_param,
- ));
- let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
- function_clauses.insert(clause);
+ let item_hir_id = item.id.hir_id();
+ let param_env = tcx.param_env(item_def_id);
+
+ let item_required_bounds = match item.kind {
+ // In our example, this corresponds to `into_iter` method
+ hir::AssocItemKind::Fn { .. } => {
+ // For methods, we check the function signature's return type for any GATs
+ // to constrain. In the `into_iter` case, we see that the return type
+ // `Self::Iter<'a>` is a GAT we want to gather any potential missing bounds from.
+ let sig: ty::FnSig<'_> = tcx.liberate_late_bound_regions(
+ item_def_id.to_def_id(),
+ tcx.fn_sig(item_def_id),
+ );
+ gather_gat_bounds(
+ tcx,
+ param_env,
+ item_hir_id,
+ sig.output(),
+ // We also assume that all of the function signature's parameter types
+ // are well formed.
+ &sig.inputs().iter().copied().collect(),
+ gat_def_id,
+ gat_generics,
+ )
+ }
+ // In our example, this corresponds to the `Iter` and `Item` associated types
+ hir::AssocItemKind::Type => {
+ // If our associated item is a GAT with missing bounds, add them to
+ // the param-env here. This allows this GAT to propagate missing bounds
+ // to other GATs.
+ let param_env = augment_param_env(
+ tcx,
+ param_env,
+ required_bounds_by_item.get(&item_def_id),
+ );
+ gather_gat_bounds(
+ tcx,
+ param_env,
+ item_hir_id,
+ tcx.explicit_item_bounds(item_def_id)
+ .iter()
+ .copied()
+ .collect::<Vec<_>>(),
+ &FxHashSet::default(),
+ gat_def_id,
+ gat_generics,
+ )
+ }
+ hir::AssocItemKind::Const => None,
+ };
+
+ if let Some(item_required_bounds) = item_required_bounds {
+ // Take the intersection of the required bounds for this GAT, and
+ // the item_required_bounds which are the ones implied by just
+ // this item alone.
+ // This is why we use an Option<_>, since we need to distinguish
+ // the empty set of bounds from the _uninitialized_ set of bounds.
+ if let Some(new_required_bounds) = &mut new_required_bounds {
+ new_required_bounds.retain(|b| item_required_bounds.contains(b));
+ } else {
+ new_required_bounds = Some(item_required_bounds);
+ }
}
}
- }
- // Imagine we have:
- // ```
- // trait Foo {
- // type Bar<'me>;
- // fn gimme(&self) -> Self::Bar<'_>;
- // fn gimme_default(&self) -> Self::Bar<'static>;
- // }
- // ```
- // We only want to require clauses on `Bar` that we can prove from *all* functions (in this
- // case, `'me` can be `static` from `gimme_default`)
- match clauses.as_mut() {
- Some(clauses) => {
- clauses.drain_filter(|p| !function_clauses.contains(p));
- }
- None => {
- clauses = Some(function_clauses);
+ if let Some(new_required_bounds) = new_required_bounds {
+ let required_bounds = required_bounds_by_item.entry(gat_def_id).or_default();
+ if new_required_bounds.into_iter().any(|p| required_bounds.insert(p)) {
+ // Iterate until our required_bounds no longer change
+ // Since they changed here, we should continue the loop
+ should_continue = true;
+ }
}
}
+ // We know that this loop will eventually halt, since we only set `should_continue` if the
+ // `required_bounds` for this item grows. Since we are not creating any new region or type
+ // variables, the set of all region and type bounds that we could ever insert are limited
+ // by the number of unique types and regions we observe in a given item.
+ if !should_continue {
+ break;
+ }
}
- // If there are any clauses that aren't provable, emit an error
- let clauses = clauses.unwrap_or_default();
- debug!(?clauses);
- if !clauses.is_empty() {
- let param_env = tcx.param_env(trait_item.def_id);
+ for (gat_def_id, required_bounds) in required_bounds_by_item {
+ let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id);
+ debug!(?required_bounds);
+ let param_env = tcx.param_env(gat_def_id);
+ let gat_hir = gat_item_hir.hir_id();
- let mut clauses: Vec<_> = clauses
+ let mut unsatisfied_bounds: Vec<_> = required_bounds
.into_iter()
.filter(|clause| match clause.kind().skip_binder() {
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => {
- !region_known_to_outlive(
- tcx,
- trait_item.hir_id(),
- param_env,
- &FxHashSet::default(),
- a,
- b,
- )
+ !region_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b)
}
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => {
- !ty_known_to_outlive(
- tcx,
- trait_item.hir_id(),
- param_env,
- &FxHashSet::default(),
- a,
- b,
- )
+ !ty_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b)
}
_ => bug!("Unexpected PredicateKind"),
})
- .map(|clause| format!("{}", clause))
+ .map(|clause| clause.to_string())
.collect();
// We sort so that order is predictable
- clauses.sort();
+ unsatisfied_bounds.sort();
- if !clauses.is_empty() {
- let plural = if clauses.len() > 1 { "s" } else { "" };
+ if !unsatisfied_bounds.is_empty() {
+ let plural = if unsatisfied_bounds.len() > 1 { "s" } else { "" };
let mut err = tcx.sess.struct_span_err(
- trait_item.span,
- &format!("missing required bound{} on `{}`", plural, trait_item.ident),
+ gat_item_hir.span,
+ &format!("missing required bound{} on `{}`", plural, gat_item_hir.ident),
);
let suggestion = format!(
"{} {}",
- if !trait_item.generics.where_clause.predicates.is_empty() {
+ if !gat_item_hir.generics.where_clause.predicates.is_empty() {
","
} else {
" where"
},
- clauses.join(", "),
+ unsatisfied_bounds.join(", "),
);
err.span_suggestion(
- trait_item.generics.where_clause.tail_span_for_suggestion(),
+ gat_item_hir.generics.where_clause.tail_span_for_suggestion(),
&format!("add the required where clause{}", plural),
suggestion,
Applicability::MachineApplicable,
);
- let bound = if clauses.len() > 1 { "these bounds are" } else { "this bound is" };
+ let bound =
+ if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" };
err.note(&format!(
"{} currently required to ensure that impls have maximum flexibility",
bound
}
}
+/// Add a new set of predicates to the caller_bounds of an existing param_env.
+fn augment_param_env<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ new_predicates: Option<&FxHashSet<ty::Predicate<'tcx>>>,
+) -> ty::ParamEnv<'tcx> {
+ let Some(new_predicates) = new_predicates else {
+ return param_env;
+ };
+
+ if new_predicates.is_empty() {
+ return param_env;
+ }
+
+ let bounds =
+ tcx.mk_predicates(param_env.caller_bounds().iter().chain(new_predicates.iter().cloned()));
+ // FIXME(compiler-errors): Perhaps there is a case where we need to normalize this
+ // i.e. traits::normalize_param_env_or_error
+ ty::ParamEnv::new(bounds, param_env.reveal(), param_env.constness())
+}
+
+/// We use the following trait as an example throughout this function.
+/// Specifically, let's assume that `to_check` here is the return type
+/// of `into_iter`, and the GAT we are checking this for is `Iter`.
+/// ```rust,ignore (this code fails due to this lint)
+/// trait IntoIter {
+/// type Iter<'a>: Iterator<Item = Self::Item<'a>>;
+/// type Item<'a>;
+/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>;
+/// }
+/// ```
+fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ item_hir: hir::HirId,
+ to_check: T,
+ wf_tys: &FxHashSet<Ty<'tcx>>,
+ gat_def_id: LocalDefId,
+ gat_generics: &'tcx ty::Generics,
+) -> Option<FxHashSet<ty::Predicate<'tcx>>> {
+ // The bounds we that we would require from `to_check`
+ let mut bounds = FxHashSet::default();
+
+ let (regions, types) = GATSubstCollector::visit(tcx, gat_def_id.to_def_id(), to_check);
+
+ // If both regions and types are empty, then this GAT isn't in the
+ // set of types we are checking, and we shouldn't try to do clause analysis
+ // (particularly, doing so would end up with an empty set of clauses,
+ // since the current method would require none, and we take the
+ // intersection of requirements of all methods)
+ if types.is_empty() && regions.is_empty() {
+ return None;
+ }
+
+ for (region_a, region_a_idx) in ®ions {
+ // Ignore `'static` lifetimes for the purpose of this lint: it's
+ // because we know it outlives everything and so doesn't give meaninful
+ // clues
+ if let ty::ReStatic = **region_a {
+ continue;
+ }
+ // For each region argument (e.g., `'a` in our example), check for a
+ // relationship to the type arguments (e.g., `Self`). If there is an
+ // outlives relationship (`Self: 'a`), then we want to ensure that is
+ // reflected in a where clause on the GAT itself.
+ for (ty, ty_idx) in &types {
+ // In our example, requires that `Self: 'a`
+ if ty_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *ty, *region_a) {
+ debug!(?ty_idx, ?region_a_idx);
+ debug!("required clause: {} must outlive {}", ty, region_a);
+ // Translate into the generic parameters of the GAT. In
+ // our example, the type was `Self`, which will also be
+ // `Self` in the GAT.
+ let ty_param = gat_generics.param_at(*ty_idx, tcx);
+ let ty_param = tcx
+ .mk_ty(ty::Param(ty::ParamTy { index: ty_param.index, name: ty_param.name }));
+ // Same for the region. In our example, 'a corresponds
+ // to the 'me parameter.
+ let region_param = gat_generics.param_at(*region_a_idx, tcx);
+ let region_param =
+ tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
+ def_id: region_param.def_id,
+ index: region_param.index,
+ name: region_param.name,
+ }));
+ // The predicate we expect to see. (In our example,
+ // `Self: 'me`.)
+ let clause =
+ ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param));
+ let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
+ bounds.insert(clause);
+ }
+ }
+
+ // For each region argument (e.g., `'a` in our example), also check for a
+ // relationship to the other region arguments. If there is an outlives
+ // relationship, then we want to ensure that is reflected in the where clause
+ // on the GAT itself.
+ for (region_b, region_b_idx) in ®ions {
+ // Again, skip `'static` because it outlives everything. Also, we trivially
+ // know that a region outlives itself.
+ if ty::ReStatic == **region_b || region_a == region_b {
+ continue;
+ }
+ if region_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *region_a, *region_b) {
+ debug!(?region_a_idx, ?region_b_idx);
+ debug!("required clause: {} must outlive {}", region_a, region_b);
+ // Translate into the generic parameters of the GAT.
+ let region_a_param = gat_generics.param_at(*region_a_idx, tcx);
+ let region_a_param =
+ tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
+ def_id: region_a_param.def_id,
+ index: region_a_param.index,
+ name: region_a_param.name,
+ }));
+ // Same for the region.
+ let region_b_param = gat_generics.param_at(*region_b_idx, tcx);
+ let region_b_param =
+ tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion {
+ def_id: region_b_param.def_id,
+ index: region_b_param.index,
+ name: region_b_param.name,
+ }));
+ // The predicate we expect to see.
+ let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(
+ region_a_param,
+ region_b_param,
+ ));
+ let clause = tcx.mk_predicate(ty::Binder::dummy(clause));
+ bounds.insert(clause);
+ }
+ }
+ }
+
+ Some(bounds)
+}
+
/// Given a known `param_env` and a set of well formed types, can we prove that
/// `ty` outlives `region`.
fn ty_known_to_outlive<'tcx>(
wf_tys: &FxHashSet<Ty<'tcx>>,
add_constraints: impl for<'a> FnOnce(
&'a InferCtxt<'a, 'tcx>,
- &'a Vec<(&'tcx ty::RegionKind, GenericKind<'tcx>)>,
+ &'a Vec<(ty::Region<'tcx>, GenericKind<'tcx>)>,
),
) -> bool {
// Unfortunately, we have to use a new `InferCtxt` each call, because
FxHashSet::default()
});
+
+ // Only check traits, don't check trait aliases
+ if let hir::ItemKind::Trait(_, _, _, _, items) = item.kind {
+ check_gat_where_clauses(tcx, items);
+ }
}
/// Checks all associated type defaults of trait `trait_def_id`.
ControlFlow::BREAK
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- if let ty::ConstKind::Param(param) = c.val {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::ConstKind::Param(param) = c.val() {
self.params.insert(param.index);
}
c.super_visit_with(self)
}
}
- fn report_const_error(&self, c: &'tcx ty::Const<'tcx>) {
+ fn report_const_error(&self, c: ty::Const<'tcx>) {
if !self.tcx.sess.has_errors() {
self.infcx
.emit_inference_failure_err(
}
}
fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
- if let ty::ReLateBound(..) = r { r } else { self.tcx.lifetimes.re_erased }
+ if r.is_late_bound() { r } else { self.tcx.lifetimes.re_erased }
}
}
self.tcx.lifetimes.re_erased
}
- fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
match self.infcx.fully_resolve(ct) {
Ok(ct) => self.infcx.tcx.erase_regions(ct),
Err(_) => {
debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct);
self.report_const_error(ct);
self.replaced_with_error = true;
- self.tcx().const_error(ct.ty)
+ self.tcx().const_error(ct.ty())
}
}
}
use ty::TyKind::*;
match (source.kind(), target.kind()) {
(&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
- if infcx.at(&cause, param_env).eq(r_a, r_b).is_ok() && mutbl_a == *mutbl_b => {}
+ if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
(&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
(&Adt(def_a, substs_a), &Adt(def_b, substs_b))
if def_a.is_struct() && def_b.is_struct() =>
}
}
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
- match r {
+ match *r {
ty::ReEarlyBound(p) => {
if self.seen.insert(p.index) {
ControlFlow::CONTINUE
_ => ControlFlow::Break(NotUniqueParam::NotParam(r.into())),
}
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- match c.val {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match c.val() {
ty::ConstKind::Param(p) => {
if self.seen.insert(p.index) {
ControlFlow::CONTINUE
+// ignore-tidy-filelength
//! "Collection" is the process of determining the type and other external
//! details of each item in Rust. Collection is specifically concerned
//! with *inter-procedural* things -- for example, for a function
self.tcx().ty_error_with_message(span, "bad placeholder type")
}
- fn ct_infer(
- &self,
- ty: Ty<'tcx>,
- _: Option<&ty::GenericParamDef>,
- span: Span,
- ) -> &'tcx Const<'tcx> {
- let ty = self.tcx.fold_regions(ty, &mut false, |r, _| match r {
+ fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
+ let ty = self.tcx.fold_regions(ty, &mut false, |r, _| match *r {
ty::ReErased => self.tcx.lifetimes.re_static,
_ => r,
});
kind: ty::GenericParamDefKind::Lifetime,
}));
- let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id);
+ let object_lifetime_defaults = tcx.object_lifetime_defaults(hir_id.owner);
// Now create the real type and const parameters.
let type_start = own_start - has_self as u32 + params.len() as u32;
Some(ty) => {
let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
// Typeck doesn't expect erased regions to be returned from `type_of`.
- let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match r {
+ let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
ty::ReErased => tcx.lifetimes.re_static,
_ => r,
});
fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
let def_id = self.tcx.hir().local_def_id(c.hir_id);
let ct = ty::Const::from_anon_const(self.tcx, def_id);
- if let ty::ConstKind::Unevaluated(uv) = ct.val {
+ if let ty::ConstKind::Unevaluated(uv) = ct.val() {
assert_eq!(uv.promoted, None);
let span = self.tcx.hir().span(c.hir_id);
self.preds.insert((
}
};
for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
- check(input, ty)
+ check(input, *ty)
}
if let hir::FnRetTy::Return(ref ty) = decl.output {
check(ty, fty.output().skip_binder())
}
}
+ // The panic_no_unwind function called by TerminatorKind::Abort will never
+ // unwind. If the panic handler that it invokes unwind then it will simply
+ // call the panic handler again.
+ if Some(id) == tcx.lang_items().panic_no_unwind() {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
+ }
+
let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
let mut inline_span = None;
} else if attr.has_name(sym::rustc_std_internal_symbol) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
} else if attr.has_name(sym::used) {
- codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
+ let inner = attr.meta_item_list();
+ match inner.as_deref() {
+ Some([item]) if item.has_name(sym::linker) => {
+ if !tcx.features().used_with_arg {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::used_with_arg,
+ attr.span,
+ "`#[used(linker)]` is currently unstable",
+ )
+ .emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
+ }
+ Some([item]) if item.has_name(sym::compiler) => {
+ if !tcx.features().used_with_arg {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::used_with_arg,
+ attr.span,
+ "`#[used(compiler)]` is currently unstable",
+ )
+ .emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
+ }
+ Some(_) => {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ "expected `used`, `used(compiler)` or `used(linker)`",
+ )
+ .emit();
+ }
+ None => codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED,
+ }
} else if attr.has_name(sym::cmse_nonsecure_entry) {
if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) {
struct_span_err!(
})
}) =>
{
- // FIXME(associated_const_equality) when does this unwrap fail? I have no idea what case it would.
- let trait_def_id = trait_ref.trait_def_id().unwrap();
+ let Some(trait_def_id) = trait_ref.trait_def_id() else {
+ return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
+ };
let assoc_items = tcx.associated_items(trait_def_id);
let assoc_item = assoc_items.find_by_name_and_kind(
tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(),
err.emit();
}
} else {
- self.found = Some((span, concrete_type));
+ self.found = Some((span, *concrete_type));
}
}
}
}
// Typeck doesn't expect erased regions to be returned from `type_of`.
- tcx.fold_regions(ty, &mut false, |r, _| match r {
+ tcx.fold_regions(ty, &mut false, |r, _| match *r {
ty::ReErased => tcx.lifetimes.re_static,
_ => r,
})
ControlFlow::CONTINUE
}
- fn visit_const(&mut self, c: &'tcx ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
- match c.val {
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match c.val() {
ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => {
// Constant expressions are not injective
- return c.ty.visit_with(self);
+ return c.ty().visit_with(self);
}
ty::ConstKind::Param(data) => {
self.parameters.push(Parameter::from(data));
self.tcx
}
fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> {
- if let ty::ReLateBound(..) = r { &ty::ReErased } else { r }
+ if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r }
}
}
parent_substs: &Vec<GenericArg<'tcx>>,
span: Span,
) {
- if tcx.any_free_region_meets(parent_substs, |r| *r == ty::ReStatic) {
+ if tcx.any_free_region_meets(parent_substs, |r| r.is_static()) {
tcx.sess.struct_span_err(span, "cannot specialize on `'static` lifetime").emit();
}
}
fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
match (decl.c_variadic, abi) {
// The function has the correct calling convention, or isn't a "C-variadic" function.
- (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl) => {}
+ (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
// The function is a "C-variadic" function with an incorrect calling convention.
(true, _) => {
let mut err = struct_span_err!(
if let Some(vec) = self.typeck_results.pat_adjustments().get(pat.hir_id) {
if let Some(first_ty) = vec.first() {
debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty);
- return Ok(first_ty);
+ return Ok(*first_ty);
}
}
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _)
| Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _)
| Res::SelfCtor(..)
- | Res::SelfTy(..) => {
+ | Res::SelfTy { .. } => {
// Structs and Unions have only have one variant.
Ok(VariantIdx::new(0))
}
match kind1.unpack() {
GenericArgKind::Type(ty1) => Some((
ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
- ty::OutlivesPredicate(ty1, region2),
+ ty::OutlivesPredicate(ty1, *region2),
))
.to_predicate(tcx),
span,
)),
GenericArgKind::Lifetime(region1) => Some((
ty::Binder::dummy(ty::PredicateKind::RegionOutlives(
- ty::OutlivesPredicate(region1, region2),
+ ty::OutlivesPredicate(region1, *region2),
))
.to_predicate(tcx),
span,
use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, Region, RegionKind, Ty, TyCtxt};
+use rustc_middle::ty::{self, Region, Ty, TyCtxt};
use rustc_span::Span;
use smallvec::smallvec;
use std::collections::BTreeMap;
fn is_free_region(tcx: TyCtxt<'_>, region: Region<'_>) -> bool {
// First, screen for regions that might appear in a type header.
- match region {
+ match *region {
// These correspond to `T: 'a` relationships:
//
// struct Foo<'a, T> {
// }
//
// We care about these, so fall through.
- RegionKind::ReEarlyBound(_) => true,
+ ty::ReEarlyBound(_) => true,
// These correspond to `T: 'static` relationships which can be
// rather surprising. We are therefore putting this behind a
// struct Foo<'a, T> {
// field: &'static T, // this would generate a ReStatic
// }
- RegionKind::ReStatic => tcx.sess.features_untracked().infer_static_outlives_requirements,
+ ty::ReStatic => tcx.sess.features_untracked().infer_static_outlives_requirements,
// Late-bound regions can appear in `fn` types:
//
//
// The type above might generate a `T: 'b` bound, but we can
// ignore it. We can't put it on the struct header anyway.
- RegionKind::ReLateBound(..) => false,
+ ty::ReLateBound(..) => false,
// This can appear in `where Self: ` bounds (#64855):
//
// struct Bar<T>(<Self as Foo>::Type) where Self: ;
// struct Baz<'a>(&'a Self) where Self: ;
- RegionKind::ReEmpty(_) => false,
+ ty::ReEmpty(_) => false,
// These regions don't appear in types from type declarations:
- RegionKind::ReErased
- | RegionKind::ReVar(..)
- | RegionKind::RePlaceholder(..)
- | RegionKind::ReFree(..) => {
+ ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => {
bug!("unexpected region in outlives inference: {:?}", region);
}
}
fn add_constraints_from_const(
&mut self,
current: &CurrentItem,
- val: &ty::Const<'tcx>,
+ val: ty::Const<'tcx>,
variance: VarianceTermPtr<'a>,
) {
debug!("add_constraints_from_const(val={:?}, variance={:?})", val, variance);
- match &val.val {
+ match &val.val() {
ty::ConstKind::Unevaluated(uv) => {
self.add_constraints_from_invariant_substs(current, uv.substs, variance);
}
# Whether to build the clang compiler.
#clang = false
+# Custom CMake defines to set when building LLVM.
+#build-config = {}
+
# =============================================================================
# General build configuration options
# =============================================================================
#sanitizers = false
# Build the profiler runtime (required when compiling with options that depend
-# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`).
+# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`).
#profiler = false
# Indicates whether the native libraries linked into Cargo will be statically
#sanitizers = build.sanitizers (bool)
# Build the profiler runtime for this target(required when compiling with options that depend
-# on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`).
+# on this runtime, such as `-C profile-generate` or `-C instrument-coverage`).
# This option will override the same option under [build] section.
#profiler = build.profiler (bool)
// This is the magic symbol to call the global alloc error handler. rustc generates
// it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
// default implementations below (`__rdl_oom`) otherwise.
- #[rustc_allocator_nounwind]
fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
}
#[stable(feature = "global_alloc", since = "1.28.0")]
#[rustc_const_unstable(feature = "const_alloc_error", issue = "92523")]
#[cfg(all(not(no_global_oom_handling), not(test)))]
-#[rustc_allocator_nounwind]
#[cold]
pub const fn handle_alloc_error(layout: Layout) -> ! {
const fn ct_error(_: Layout) -> ! {
// if there is no `#[alloc_error_handler]`
#[rustc_std_internal_symbol]
- pub unsafe extern "C" fn __rdl_oom(size: usize, _align: usize) -> ! {
+ pub unsafe extern "C-unwind" fn __rdl_oom(size: usize, _align: usize) -> ! {
panic!("memory allocation of {} bytes failed", size)
}
// if there is an `#[alloc_error_handler]`
#[rustc_std_internal_symbol]
- pub unsafe extern "C" fn __rg_oom(size: usize, align: usize) -> ! {
+ pub unsafe extern "C-unwind" fn __rg_oom(size: usize, align: usize) -> ! {
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
extern "Rust" {
#[lang = "oom"]
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
impl<K: Ord, V, const N: usize> From<[(K, V); N]> for BTreeMap<K, V> {
+ /// Converts a `[(K, V); N]` into a `BTreeMap<(K, V)>`.
+ ///
/// ```
/// use std::collections::BTreeMap;
///
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
+ /// Converts a `[T; N]` into a `BTreeSet<T>`.
+ ///
/// ```
/// use std::collections::BTreeSet;
///
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
impl<T, const N: usize> From<[T; N]> for LinkedList<T> {
+ /// Converts a `[T; N]` into a `LinkedList<T>`.
+ ///
/// ```
/// use std::collections::LinkedList;
///
-//! A double-ended queue implemented with a growable ring buffer.
+//! A double-ended queue (deque) implemented with a growable ring buffer.
//!
//! This queue has *O*(1) amortized inserts and removals from both ends of the
//! container. It also has *O*(1) indexing like a vector. The contained elements
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Default for VecDeque<T> {
- /// Creates an empty `VecDeque<T>`.
+ /// Creates an empty deque.
#[inline]
fn default() -> VecDeque<T> {
VecDeque::new()
}
impl<T> VecDeque<T> {
- /// Creates an empty `VecDeque`.
+ /// Creates an empty deque.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let vector: VecDeque<u32> = VecDeque::new();
+ /// let deque: VecDeque<u32> = VecDeque::new();
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
VecDeque::new_in(Global)
}
- /// Creates an empty `VecDeque` with space for at least `capacity` elements.
+ /// Creates an empty deque with space for at least `capacity` elements.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let vector: VecDeque<u32> = VecDeque::with_capacity(10);
+ /// let deque: VecDeque<u32> = VecDeque::with_capacity(10);
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
}
impl<T, A: Allocator> VecDeque<T, A> {
- /// Creates an empty `VecDeque`.
+ /// Creates an empty deque.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let vector: VecDeque<u32> = VecDeque::new();
+ /// let deque: VecDeque<u32> = VecDeque::new();
/// ```
#[inline]
#[unstable(feature = "allocator_api", issue = "32838")]
VecDeque::with_capacity_in(INITIAL_CAPACITY, alloc)
}
- /// Creates an empty `VecDeque` with space for at least `capacity` elements.
+ /// Creates an empty deque with space for at least `capacity` elements.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let vector: VecDeque<u32> = VecDeque::with_capacity(10);
+ /// let deque: VecDeque<u32> = VecDeque::with_capacity(10);
/// ```
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque<T, A> {
unsafe { ptr::swap(self.ptr().add(ri), self.ptr().add(rj)) }
}
- /// Returns the number of elements the `VecDeque` can hold without
+ /// Returns the number of elements the deque can hold without
/// reallocating.
///
/// # Examples
}
/// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the
- /// given `VecDeque`. Does nothing if the capacity is already sufficient.
+ /// given deque. Does nothing if the capacity is already sufficient.
///
/// Note that the allocator may give the collection more space than it requests. Therefore
/// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future
}
/// Reserves capacity for at least `additional` more elements to be inserted in the given
- /// `VecDeque`. The collection may reserve more space to avoid frequent reallocations.
+ /// deque. The collection may reserve more space to avoid frequent reallocations.
///
/// # Panics
///
}
/// Tries to reserve the minimum capacity for exactly `additional` more elements to
- /// be inserted in the given `VecDeque<T>`. After calling `try_reserve_exact`,
+ /// be inserted in the given deque. After calling `try_reserve_exact`,
/// capacity will be greater than or equal to `self.len() + additional`.
/// Does nothing if the capacity is already sufficient.
///
}
/// Tries to reserve capacity for at least `additional` more elements to be inserted
- /// in the given `VecDeque<T>`. The collection may reserve more space to avoid
+ /// in the given deque. The collection may reserve more space to avoid
/// frequent reallocations. After calling `try_reserve`, capacity will be
/// greater than or equal to `self.len() + additional`. Does nothing if
/// capacity is already sufficient.
Ok(())
}
- /// Shrinks the capacity of the `VecDeque` as much as possible.
+ /// Shrinks the capacity of the deque as much as possible.
///
/// It will drop down as close as possible to the length but the allocator may still inform the
- /// `VecDeque` that there is space for a few more elements.
+ /// deque that there is space for a few more elements.
///
/// # Examples
///
self.shrink_to(0);
}
- /// Shrinks the capacity of the `VecDeque` with a lower bound.
+ /// Shrinks the capacity of the deque with a lower bound.
///
/// The capacity will remain at least as large as both the length
/// and the supplied value.
}
}
- /// Shortens the `VecDeque`, keeping the first `len` elements and dropping
+ /// Shortens the deque, keeping the first `len` elements and dropping
/// the rest.
///
- /// If `len` is greater than the `VecDeque`'s current length, this has no
+ /// If `len` is greater than the deque's current length, this has no
/// effect.
///
/// # Examples
}
/// Returns a pair of slices which contain, in order, the contents of the
- /// `VecDeque`.
+ /// deque.
///
/// If [`make_contiguous`] was previously called, all elements of the
- /// `VecDeque` will be in the first slice and the second slice will be empty.
+ /// deque will be in the first slice and the second slice will be empty.
///
/// [`make_contiguous`]: VecDeque::make_contiguous
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut vector = VecDeque::new();
+ /// let mut deque = VecDeque::new();
///
- /// vector.push_back(0);
- /// vector.push_back(1);
- /// vector.push_back(2);
+ /// deque.push_back(0);
+ /// deque.push_back(1);
+ /// deque.push_back(2);
///
- /// assert_eq!(vector.as_slices(), (&[0, 1, 2][..], &[][..]));
+ /// assert_eq!(deque.as_slices(), (&[0, 1, 2][..], &[][..]));
///
- /// vector.push_front(10);
- /// vector.push_front(9);
+ /// deque.push_front(10);
+ /// deque.push_front(9);
///
- /// assert_eq!(vector.as_slices(), (&[9, 10][..], &[0, 1, 2][..]));
+ /// assert_eq!(deque.as_slices(), (&[9, 10][..], &[0, 1, 2][..]));
/// ```
#[inline]
#[stable(feature = "deque_extras_15", since = "1.5.0")]
}
/// Returns a pair of slices which contain, in order, the contents of the
- /// `VecDeque`.
+ /// deque.
///
/// If [`make_contiguous`] was previously called, all elements of the
- /// `VecDeque` will be in the first slice and the second slice will be empty.
+ /// deque will be in the first slice and the second slice will be empty.
///
/// [`make_contiguous`]: VecDeque::make_contiguous
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut vector = VecDeque::new();
+ /// let mut deque = VecDeque::new();
///
- /// vector.push_back(0);
- /// vector.push_back(1);
+ /// deque.push_back(0);
+ /// deque.push_back(1);
///
- /// vector.push_front(10);
- /// vector.push_front(9);
+ /// deque.push_front(10);
+ /// deque.push_front(9);
///
- /// vector.as_mut_slices().0[0] = 42;
- /// vector.as_mut_slices().1[0] = 24;
- /// assert_eq!(vector.as_slices(), (&[42, 10][..], &[24, 1][..]));
+ /// deque.as_mut_slices().0[0] = 42;
+ /// deque.as_mut_slices().1[0] = 24;
+ /// assert_eq!(deque.as_slices(), (&[42, 10][..], &[24, 1][..]));
/// ```
#[inline]
#[stable(feature = "deque_extras_15", since = "1.5.0")]
}
}
- /// Returns the number of elements in the `VecDeque`.
+ /// Returns the number of elements in the deque.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut v = VecDeque::new();
- /// assert_eq!(v.len(), 0);
- /// v.push_back(1);
- /// assert_eq!(v.len(), 1);
+ /// let mut deque = VecDeque::new();
+ /// assert_eq!(deque.len(), 0);
+ /// deque.push_back(1);
+ /// assert_eq!(deque.len(), 1);
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn len(&self) -> usize {
count(self.tail, self.head, self.cap())
}
- /// Returns `true` if the `VecDeque` is empty.
+ /// Returns `true` if the deque is empty.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut v = VecDeque::new();
- /// assert!(v.is_empty());
- /// v.push_front(1);
- /// assert!(!v.is_empty());
+ /// let mut deque = VecDeque::new();
+ /// assert!(deque.is_empty());
+ /// deque.push_front(1);
+ /// assert!(!deque.is_empty());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn is_empty(&self) -> bool {
(tail, head)
}
- /// Creates an iterator that covers the specified range in the `VecDeque`.
+ /// Creates an iterator that covers the specified range in the deque.
///
/// # Panics
///
/// Panics if the starting point is greater than the end point or if
- /// the end point is greater than the length of the vector.
+ /// the end point is greater than the length of the deque.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let v: VecDeque<_> = [1, 2, 3].into();
- /// let range = v.range(2..).copied().collect::<VecDeque<_>>();
+ /// let deque: VecDeque<_> = [1, 2, 3].into();
+ /// let range = deque.range(2..).copied().collect::<VecDeque<_>>();
/// assert_eq!(range, [3]);
///
/// // A full range covers all contents
- /// let all = v.range(..);
+ /// let all = deque.range(..);
/// assert_eq!(all.len(), 3);
/// ```
#[inline]
}
}
- /// Creates an iterator that covers the specified mutable range in the `VecDeque`.
+ /// Creates an iterator that covers the specified mutable range in the deque.
///
/// # Panics
///
/// Panics if the starting point is greater than the end point or if
- /// the end point is greater than the length of the vector.
+ /// the end point is greater than the length of the deque.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut v: VecDeque<_> = [1, 2, 3].into();
- /// for v in v.range_mut(2..) {
+ /// let mut deque: VecDeque<_> = [1, 2, 3].into();
+ /// for v in deque.range_mut(2..) {
/// *v *= 2;
/// }
- /// assert_eq!(v, [1, 2, 6]);
+ /// assert_eq!(deque, [1, 2, 6]);
///
/// // A full range covers all contents
- /// for v in v.range_mut(..) {
+ /// for v in deque.range_mut(..) {
/// *v *= 2;
/// }
- /// assert_eq!(v, [2, 4, 12]);
+ /// assert_eq!(deque, [2, 4, 12]);
/// ```
#[inline]
#[stable(feature = "deque_range", since = "1.51.0")]
}
/// Creates a draining iterator that removes the specified range in the
- /// `VecDeque` and yields the removed items.
+ /// deque and yields the removed items.
///
/// Note 1: The element range is removed even if the iterator is not
/// consumed until the end.
/// # Panics
///
/// Panics if the starting point is greater than the end point or if
- /// the end point is greater than the length of the vector.
+ /// the end point is greater than the length of the deque.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut v: VecDeque<_> = [1, 2, 3].into();
- /// let drained = v.drain(2..).collect::<VecDeque<_>>();
+ /// let mut deque: VecDeque<_> = [1, 2, 3].into();
+ /// let drained = deque.drain(2..).collect::<VecDeque<_>>();
/// assert_eq!(drained, [3]);
- /// assert_eq!(v, [1, 2]);
+ /// assert_eq!(deque, [1, 2]);
///
/// // A full range clears all contents
- /// v.drain(..);
- /// assert!(v.is_empty());
+ /// deque.drain(..);
+ /// assert!(deque.is_empty());
/// ```
#[inline]
#[stable(feature = "drain", since = "1.6.0")]
unsafe { Drain::new(drain_head, head, iter, deque) }
}
- /// Clears the `VecDeque`, removing all values.
+ /// Clears the deque, removing all values.
///
/// # Examples
///
/// ```
/// use std::collections::VecDeque;
///
- /// let mut v = VecDeque::new();
- /// v.push_back(1);
- /// v.clear();
- /// assert!(v.is_empty());
+ /// let mut deque = VecDeque::new();
+ /// deque.push_back(1);
+ /// deque.clear();
+ /// assert!(deque.is_empty());
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
self.truncate(0);
}
- /// Returns `true` if the `VecDeque` contains an element equal to the
+ /// Returns `true` if the deque contains an element equal to the
/// given value.
///
/// # Examples
/// ```
/// use std::collections::VecDeque;
///
- /// let mut vector: VecDeque<u32> = VecDeque::new();
+ /// let mut deque: VecDeque<u32> = VecDeque::new();
///
- /// vector.push_back(0);
- /// vector.push_back(1);
+ /// deque.push_back(0);
+ /// deque.push_back(1);
///
- /// assert_eq!(vector.contains(&1), true);
- /// assert_eq!(vector.contains(&10), false);
+ /// assert_eq!(deque.contains(&1), true);
+ /// assert_eq!(deque.contains(&10), false);
/// ```
#[stable(feature = "vec_deque_contains", since = "1.12.0")]
pub fn contains(&self, x: &T) -> bool
a.contains(x) || b.contains(x)
}
- /// Provides a reference to the front element, or `None` if the `VecDeque` is
+ /// Provides a reference to the front element, or `None` if the deque is
/// empty.
///
/// # Examples
}
/// Provides a mutable reference to the front element, or `None` if the
- /// `VecDeque` is empty.
+ /// deque is empty.
///
/// # Examples
///
self.get_mut(0)
}
- /// Provides a reference to the back element, or `None` if the `VecDeque` is
+ /// Provides a reference to the back element, or `None` if the deque is
/// empty.
///
/// # Examples
}
/// Provides a mutable reference to the back element, or `None` if the
- /// `VecDeque` is empty.
+ /// deque is empty.
///
/// # Examples
///
self.get_mut(self.len().wrapping_sub(1))
}
- /// Removes the first element and returns it, or `None` if the `VecDeque` is
+ /// Removes the first element and returns it, or `None` if the deque is
/// empty.
///
/// # Examples
}
}
- /// Removes the last element from the `VecDeque` and returns it, or `None` if
+ /// Removes the last element from the deque and returns it, or `None` if
/// it is empty.
///
/// # Examples
}
}
- /// Prepends an element to the `VecDeque`.
+ /// Prepends an element to the deque.
///
/// # Examples
///
}
}
- /// Appends an element to the back of the `VecDeque`.
+ /// Appends an element to the back of the deque.
///
/// # Examples
///
self.tail <= self.head
}
- /// Removes an element from anywhere in the `VecDeque` and returns it,
+ /// Removes an element from anywhere in the deque and returns it,
/// replacing it with the first element.
///
/// This does not preserve ordering, but is *O*(1).
self.pop_front()
}
- /// Removes an element from anywhere in the `VecDeque` and returns it, replacing it with the
- /// last element.
+ /// Removes an element from anywhere in the deque and returns it,
+ /// replacing it with the last element.
///
/// This does not preserve ordering, but is *O*(1).
///
self.pop_back()
}
- /// Inserts an element at `index` within the `VecDeque`, shifting all elements with indices
- /// greater than or equal to `index` towards the back.
+ /// Inserts an element at `index` within the deque, shifting all elements
+ /// with indices greater than or equal to `index` towards the back.
///
/// Element at index 0 is the front of the queue.
///
/// # Panics
///
- /// Panics if `index` is greater than `VecDeque`'s length
+ /// Panics if `index` is greater than deque's length
///
/// # Examples
///
}
}
- /// Removes and returns the element at `index` from the `VecDeque`.
+ /// Removes and returns the element at `index` from the deque.
/// Whichever end is closer to the removal point will be moved to make
/// room, and all the affected elements will be moved to new positions.
/// Returns `None` if `index` is out of bounds.
elem
}
- /// Splits the `VecDeque` into two at the given index.
+ /// Splits the deque into two at the given index.
///
/// Returns a newly allocated `VecDeque`. `self` contains elements `[0, at)`,
- /// and the returned `VecDeque` contains elements `[at, len)`.
+ /// and the returned deque contains elements `[at, len)`.
///
/// Note that the capacity of `self` does not change.
///
debug_assert!(!self.is_full());
}
- /// Modifies the `VecDeque` in-place so that `len()` is equal to `new_len`,
+ /// Modifies the deque in-place so that `len()` is equal to `new_len`,
/// either by removing excess elements from the back or by appending
/// elements generated by calling `generator` to the back.
///
///
/// Once the internal storage is contiguous, the [`as_slices`] and
/// [`as_mut_slices`] methods will return the entire contents of the
- /// `VecDeque` in a single slice.
+ /// deque in a single slice.
///
/// [`as_slices`]: VecDeque::as_slices
/// [`as_mut_slices`]: VecDeque::as_mut_slices
}
}
- /// Binary searches this sorted `VecDeque` for a given element.
+ /// Binary searches the sorted deque for a given element.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
/// assert!(matches!(r, Ok(1..=4)));
/// ```
///
- /// If you want to insert an item to a sorted `VecDeque`, while maintaining
+ /// If you want to insert an item to a sorted deque, while maintaining
/// sort order:
///
/// ```
self.binary_search_by(|e| e.cmp(x))
}
- /// Binary searches this sorted `VecDeque` with a comparator function.
+ /// Binary searches the sorted deque with a comparator function.
///
/// The comparator function should implement an order consistent
- /// with the sort order of the underlying `VecDeque`, returning an
- /// order code that indicates whether its argument is `Less`,
- /// `Equal` or `Greater` than the desired target.
+ /// with the sort order of the deque, returning an order code that
+ /// indicates whether its argument is `Less`, `Equal` or `Greater`
+ /// than the desired target.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
}
}
- /// Binary searches this sorted `VecDeque` with a key extraction function.
+ /// Binary searches the sorted deque with a key extraction function.
///
- /// Assumes that the `VecDeque` is sorted by the key, for instance with
+ /// Assumes that the deque is sorted by the key, for instance with
/// [`make_contiguous().sort_by_key()`] using the same key extraction function.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// For example, [7, 15, 3, 5, 4, 12, 6] is a partitioned under the predicate x % 2 != 0
/// (all odd numbers are at the start, all even at the end).
///
- /// If this deque is not partitioned, the returned result is unspecified and meaningless,
+ /// If the deque is not partitioned, the returned result is unspecified and meaningless,
/// as this method performs a kind of binary search.
///
/// See also [`binary_search`], [`binary_search_by`], and [`binary_search_by_key`].
}
impl<T: Clone, A: Allocator> VecDeque<T, A> {
- /// Modifies the `VecDeque` in-place so that `len()` is equal to new_len,
+ /// Modifies the deque in-place so that `len()` is equal to new_len,
/// either by removing excess elements from the back or by appending clones of `value`
/// to the back.
///
type Item = T;
type IntoIter = IntoIter<T, A>;
- /// Consumes the `VecDeque` into a front-to-back iterator yielding elements by
+ /// Consumes the deque into a front-to-back iterator yielding elements by
/// value.
fn into_iter(self) -> IntoIter<T, A> {
IntoIter::new(self)
#[stable(feature = "std_collections_from_array", since = "1.56.0")]
impl<T, const N: usize> From<[T; N]> for VecDeque<T> {
+ /// Converts a `[T; N]` into a `VecDeque<T>`.
+ ///
/// ```
/// use std::collections::VecDeque;
///
//! identifier '=' expression
//! ```
//!
-//! For example, the following [`format!`] expressions all use named argument:
+//! For example, the following [`format!`] expressions all use named arguments:
//!
//! ```
//! format!("{argument}", argument = "test"); // => "test"
#![feature(extend_one)]
#![feature(fmt_internals)]
#![feature(fn_traits)]
-#![feature(inherent_ascii_escape)]
#![feature(inplace_iteration)]
#![feature(iter_advance_by)]
#![feature(layout_for_ptr)]
#![feature(associated_type_bounds)]
#![feature(box_syntax)]
#![feature(cfg_sanitize)]
-#![feature(cfg_target_has_atomic)]
+#![cfg_attr(bootstrap, feature(cfg_target_has_atomic))]
#![feature(const_deref)]
#![feature(const_fn_trait_bound)]
#![feature(const_mut_refs)]
#![cfg_attr(test, feature(test))]
#![feature(unboxed_closures)]
#![feature(unsized_fn_params)]
+#![feature(c_unwind)]
//
// Rustdoc features:
#![feature(doc_cfg)]
// reference to the allocation.
unsafe { &mut this.ptr.as_mut().value }
}
+
+ /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
+ /// clone.
+ ///
+ /// Assuming `rc_t` is of type `Rc<T>`, this function is functionally equivalent to
+ /// `(*rc_t).clone()`, but will avoid cloning the inner value where possible.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(arc_unwrap_or_clone)]
+ /// # use std::{ptr, rc::Rc};
+ /// let inner = String::from("test");
+ /// let ptr = inner.as_ptr();
+ ///
+ /// let rc = Rc::new(inner);
+ /// let inner = Rc::unwrap_or_clone(rc);
+ /// // The inner value was not cloned
+ /// assert!(ptr::eq(ptr, inner.as_ptr()));
+ ///
+ /// let rc = Rc::new(inner);
+ /// let rc2 = rc.clone();
+ /// let inner = Rc::unwrap_or_clone(rc);
+ /// // Because there were 2 references, we had to clone the inner value.
+ /// assert!(!ptr::eq(ptr, inner.as_ptr()));
+ /// // `rc2` is the last reference, so when we unwrap it we get back
+ /// // the original `String`.
+ /// let inner = Rc::unwrap_or_clone(rc2);
+ /// assert!(ptr::eq(ptr, inner.as_ptr()));
+ /// ```
+ #[inline]
+ #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")]
+ pub fn unwrap_or_clone(this: Self) -> T {
+ Rc::try_unwrap(this).unwrap_or_else(|rc| (*rc).clone())
+ }
}
impl Rc<dyn Any> {
pub use core::slice::ArrayChunksMut;
#[unstable(feature = "array_windows", issue = "75027")]
pub use core::slice::ArrayWindows;
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
pub use core::slice::EscapeAscii;
#[stable(feature = "slice_get_slice", since = "1.28.0")]
pub use core::slice::SliceIndex;
// either unique to begin with, or became one upon cloning the contents.
unsafe { Self::get_mut_unchecked(this) }
}
+
+ /// If we have the only reference to `T` then unwrap it. Otherwise, clone `T` and return the
+ /// clone.
+ ///
+ /// Assuming `arc_t` is of type `Arc<T>`, this function is functionally equivalent to
+ /// `(*arc_t).clone()`, but will avoid cloning the inner value where possible.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(arc_unwrap_or_clone)]
+ /// # use std::{ptr, sync::Arc};
+ /// let inner = String::from("test");
+ /// let ptr = inner.as_ptr();
+ ///
+ /// let arc = Arc::new(inner);
+ /// let inner = Arc::unwrap_or_clone(arc);
+ /// // The inner value was not cloned
+ /// assert!(ptr::eq(ptr, inner.as_ptr()));
+ ///
+ /// let arc = Arc::new(inner);
+ /// let arc2 = arc.clone();
+ /// let inner = Arc::unwrap_or_clone(arc);
+ /// // Because there were 2 references, we had to clone the inner value.
+ /// assert!(!ptr::eq(ptr, inner.as_ptr()));
+ /// // `arc2` is the last reference, so when we unwrap it we get back
+ /// // the original `String`.
+ /// let inner = Arc::unwrap_or_clone(arc2);
+ /// assert!(ptr::eq(ptr, inner.as_ptr()));
+ /// ```
+ #[inline]
+ #[unstable(feature = "arc_unwrap_or_clone", issue = "93610")]
+ pub fn unwrap_or_clone(this: Self) -> T {
+ Arc::try_unwrap(this).unwrap_or_else(|arc| (*arc).clone())
+ }
}
impl<T: ?Sized> Arc<T> {
#[cfg(not(no_global_oom_handling))]
#[stable(feature = "vec_from_array", since = "1.44.0")]
impl<T, const N: usize> From<[T; N]> for Vec<T> {
- #[cfg(not(test))]
- fn from(s: [T; N]) -> Vec<T> {
- <[T]>::into_vec(box s)
- }
/// Allocate a `Vec<T>` and move `s`'s items into it.
///
/// # Examples
/// ```
/// assert_eq!(Vec::from([1, 2, 3]), vec![1, 2, 3]);
/// ```
+ #[cfg(not(test))]
+ fn from(s: [T; N]) -> Vec<T> {
+ <[T]>::into_vec(box s)
+ }
+
#[cfg(test)]
fn from(s: [T; N]) -> Vec<T> {
crate::slice::into_vec(box s)
#![feature(binary_heap_as_slice)]
#![feature(inplace_iteration)]
#![feature(iter_advance_by)]
+#![feature(round_char_boundary)]
#![feature(slice_group_by)]
#![feature(slice_partition_dedup)]
#![feature(string_remove_matches)]
assert!((!from_utf8(&[0xf0, 0xff, 0x10]).is_ok()));
assert!((!from_utf8(&[0xf0, 0xff, 0xff, 0x10]).is_ok()));
}
+
+#[test]
+fn utf8_char_counts() {
+ let strs = [("e", 1), ("é", 1), ("€", 1), ("\u{10000}", 1), ("eé€\u{10000}", 4)];
+ let mut reps =
+ [8, 64, 256, 512, 1024].iter().copied().flat_map(|n| n - 8..=n + 8).collect::<Vec<usize>>();
+ if cfg!(not(miri)) {
+ let big = 1 << 16;
+ reps.extend(big - 8..=big + 8);
+ }
+ let counts = if cfg!(miri) { 0..1 } else { 0..8 };
+ let padding = counts.map(|len| " ".repeat(len)).collect::<Vec<String>>();
+
+ for repeat in reps {
+ for (tmpl_str, tmpl_char_count) in strs {
+ for pad_start in &padding {
+ for pad_end in &padding {
+ // Create a string with padding...
+ let with_padding =
+ format!("{}{}{}", pad_start, tmpl_str.repeat(repeat), pad_end);
+ // ...and then skip past that padding. This should ensure
+ // that we test several different alignments for both head
+ // and tail.
+ let si = pad_start.len();
+ let ei = with_padding.len() - pad_end.len();
+ let target = &with_padding[si..ei];
+
+ assert!(!target.starts_with(" ") && !target.ends_with(" "));
+ let expected_count = tmpl_char_count * repeat;
+ assert_eq!(
+ expected_count,
+ target.chars().count(),
+ "wrong count for `{:?}.repeat({})` (padding: `{:?}`)",
+ tmpl_str,
+ repeat,
+ (pad_start.len(), pad_end.len()),
+ );
+ }
+ }
+ }
+ }
+}
+
+#[test]
+fn floor_char_boundary() {
+ fn check_many(s: &str, arg: impl IntoIterator<Item = usize>, ret: usize) {
+ for idx in arg {
+ assert_eq!(
+ s.floor_char_boundary(idx),
+ ret,
+ "{:?}.floor_char_boundary({:?}) != {:?}",
+ s,
+ idx,
+ ret
+ );
+ }
+ }
+
+ // edge case
+ check_many("", [0, 1, isize::MAX as usize, usize::MAX], 0);
+
+ // basic check
+ check_many("x", [0], 0);
+ check_many("x", [1, isize::MAX as usize, usize::MAX], 1);
+
+ // 1-byte chars
+ check_many("jp", [0], 0);
+ check_many("jp", [1], 1);
+ check_many("jp", 2..4, 2);
+
+ // 2-byte chars
+ check_many("ĵƥ", 0..2, 0);
+ check_many("ĵƥ", 2..4, 2);
+ check_many("ĵƥ", 4..6, 4);
+
+ // 3-byte chars
+ check_many("日本", 0..3, 0);
+ check_many("日本", 3..6, 3);
+ check_many("日本", 6..8, 6);
+
+ // 4-byte chars
+ check_many("🇯🇵", 0..4, 0);
+ check_many("🇯🇵", 4..8, 4);
+ check_many("🇯🇵", 8..10, 8);
+}
+
+#[test]
+fn ceil_char_boundary() {
+ fn check_many(s: &str, arg: impl IntoIterator<Item = usize>, ret: usize) {
+ for idx in arg {
+ assert_eq!(
+ s.ceil_char_boundary(idx),
+ ret,
+ "{:?}.ceil_char_boundary({:?}) != {:?}",
+ s,
+ idx,
+ ret
+ );
+ }
+ }
+
+ // edge case
+ check_many("", [0], 0);
+
+ // basic check
+ check_many("x", [0], 0);
+ check_many("x", [1], 1);
+
+ // 1-byte chars
+ check_many("jp", [0], 0);
+ check_many("jp", [1], 1);
+ check_many("jp", [2], 2);
+
+ // 2-byte chars
+ check_many("ĵƥ", 0..=0, 0);
+ check_many("ĵƥ", 1..=2, 2);
+ check_many("ĵƥ", 3..=4, 4);
+
+ // 3-byte chars
+ check_many("日本", 0..=0, 0);
+ check_many("日本", 1..=3, 3);
+ check_many("日本", 4..=6, 6);
+
+ // 4-byte chars
+ check_many("🇯🇵", 0..=0, 0);
+ check_many("🇯🇵", 1..=4, 4);
+ check_many("🇯🇵", 5..=8, 8);
+}
+
+#[test]
+#[should_panic]
+fn ceil_char_boundary_above_len_panic() {
+ let _ = "x".ceil_char_boundary(2);
+}
description = "The Rust Core Library"
autotests = false
autobenches = false
+# If you update this, be sure to update it in a bunch of other places too!
+# As of 2022, it was the ci/pgo.sh script and the core-no-fp-fmt-parse test.
edition = "2021"
[lib]
use std::str;
use test::{black_box, Bencher};
-const LOREM_SHORT: &str = "Lorem ipsum";
-
-const LOREM: &str = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
-Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
-Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
-Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
-Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis.
-At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur";
-
-const EMOJI: &str = "😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘😗☺😚😙🥲😋😛😜🤪😝🤑🤗🤭🤫🤔🤐🤨😐😑😶😶🌫️😏😒🙄😬😮💨🤥😌😔😪🤤😴😷🤒🤕🤢🤮🤧🥵🥶🥴😵😵💫🤯🤠🥳🥸😎🤓🧐😕😟🙁☹😮😯😲😳🥺😦😧😨😰😥😢😭😱😖😣😞😓😩😫🥱😤😡😠🤬😈👿💀☠💩🤡👹👺👻👽👾🤖😺😸😹😻😼😽🙀😿😾🙈🙉🙊💋💌💘💝💖💗💓💞💕💟❣💔❤️🔥❤️🩹❤🧡💛💚💙💜🤎🖤🤍💯💢💥💫💦💨🕳💣💬👁️🗨️🗨🗯💭💤👋🤚🖐✋🖖👌🤌🤏✌🤞🤟🤘🤙👈👉👆🖕👇☝👍👎✊👊🤛🤜👏🙌👐🤲🤝🙏✍💅🤳💪🦾🦿🦵🦶👂🦻👃🧠🫀🫁🦷🦴👀👁👅👄👶🧒👦👧🧑👱👨🧔🧔♂️🧔♀️👨🦰👨🦱👨🦳👨🦲👩👩🦰🧑🦰👩🦱🧑🦱👩🦳🧑🦳👩🦲🧑🦲👱♀️👱♂️🧓👴👵🙍🙍♂️🙍♀️🙎🙎♂️🙎♀️🙅🙅♂️🙅♀️🙆🙆♂️🙆♀️💁💁♂️💁♀️🙋🙋♂️🙋♀️🧏🧏♂️🧏♀️🙇🙇♂️🙇♀️🤦🤦♂️🤦♀️🤷🤷♂️🤷♀️🧑⚕️👨⚕️👩⚕️🧑🎓👨🎓👩🎓🧑🏫👨🏫👩🏫🧑⚖️👨⚖️👩⚖️🧑🌾👨🌾👩🌾🧑🍳👨🍳👩🍳🧑🔧👨🔧👩🔧🧑🏭👨🏭👩🏭🧑💼👨💼👩💼🧑🔬👨🔬👩🔬🧑💻👨💻👩💻🧑🎤👨🎤👩🎤🧑🎨👨🎨👩🎨🧑✈️👨✈️👩✈️🧑🚀👨🚀👩🚀🧑🚒👨🚒👩🚒👮👮♂️👮♀️🕵🕵️♂️🕵️♀️💂💂♂️💂♀️🥷👷👷♂️👷♀️🤴👸👳👳♂️👳♀️👲🧕🤵🤵♂️🤵♀️👰👰♂️👰♀️🤰🤱👩🍼👨🍼🧑🍼👼🎅🤶🧑🎄🦸🦸♂️🦸♀️🦹🦹♂️🦹♀️🧙🧙♂️🧙♀️🧚🧚♂️🧚♀️🧛🧛♂️🧛♀️🧜🧜♂️🧜♀️🧝🧝♂️🧝♀️🧞🧞♂️🧞♀️🧟🧟♂️🧟♀️💆💆♂️💆♀️💇💇♂️💇♀️🚶🚶♂️🚶♀️🧍🧍♂️🧍♀️🧎🧎♂️🧎♀️🧑🦯👨🦯👩🦯🧑🦼👨🦼👩🦼🧑🦽👨🦽👩🦽🏃🏃♂️🏃♀️💃🕺🕴👯👯♂️👯♀️🧖🧖♂️🧖♀️🧗🧗♂️🧗♀️🤺🏇⛷🏂🏌🏌️♂️🏌️♀️🏄🏄♂️🏄♀️🚣🚣♂️🚣♀️🏊🏊♂️🏊♀️⛹⛹️♂️⛹️♀️🏋🏋️♂️🏋️♀️🚴🚴♂️🚴♀️🚵🚵♂️🚵♀️🤸🤸♂️🤸♀️🤼🤼♂️🤼♀️🤽🤽♂️🤽♀️🤾🤾♂️🤾♀️🤹🤹♂️🤹♀️🧘🧘♂️🧘♀️🛀🛌🧑🤝🧑👭👫👬💏👩❤️💋👨👨❤️💋👨👩❤️💋👩💑👩❤️👨👨❤️👨👩❤️👩👪👨👩👦👨👩👧👨👩👧👦👨👩👦👦👨👩👧👧👨👨👦👨👨👧👨👨👧👦👨👨👦👦👨👨👧👧👩👩👦👩👩👧👩👩👧👦👩👩👦👦👩👩👧👧👨👦👨👦👦👨👧👨👧👦👨👧👧👩👦👩👦👦👩👧👩👧👦👩👧👧🗣👤👥🫂👣🦰🦱🦳🦲🐵🐒🦍🦧🐶🐕🦮🐕🦺🐩🐺🦊🦝🐱🐈🐈⬛🦁🐯🐅🐆🐴🐎🦄🦓🦌🦬🐮🐂🐃🐄🐷🐖🐗🐽🐏🐑🐐🐪🐫🦙🦒🐘🦣🦏🦛🐭🐁🐀🐹🐰🐇🐿🦫🦔🦇🐻🐻❄️🐨🐼🦥🦦🦨🦘🦡🐾🦃🐔🐓🐣🐤🐥🐦🐧🕊🦅🦆🦢🦉🦤🪶🦩🦚🦜🐸🐊🐢🦎🐍🐲🐉🦕🦖🐳🐋🐬🦭🐟🐠🐡🦈🐙🐚🐌🦋🐛🐜🐝🪲🐞🦗🪳🕷🕸🦂🦟🪰🪱🦠💐🌸💮🏵🌹🥀🌺🌻🌼🌷🌱🪴🌲🌳🌴🌵🌾🌿☘🍀🍁🍂🍃🍇🍈🍉🍊🍋🍌🍍🥭🍎🍏🍐🍑🍒🍓🫐🥝🍅🫒🥥🥑🍆🥔🥕🌽🌶🫑🥒🥬🥦🧄🧅🍄🥜🌰🍞🥐🥖🫓🥨🥯🥞🧇🧀🍖🍗🥩🥓🍔🍟🍕🌭🥪🌮🌯🫔🥙🧆🥚🍳🥘🍲🫕🥣🥗🍿🧈🧂🥫🍱🍘🍙🍚🍛🍜🍝🍠🍢🍣🍤🍥🥮🍡🥟🥠🥡🦀🦞🦐🦑🦪🍦🍧🍨🍩🍪🎂🍰🧁🥧🍫🍬🍭🍮🍯🍼🥛☕🫖🍵🍶🍾🍷🍸🍹🍺🍻🥂🥃🥤🧋🧃🧉🧊🥢🍽🍴🥄🔪🏺🌍🌎🌏🌐🗺🗾🧭🏔⛰🌋🗻🏕🏖🏜🏝🏞🏟🏛🏗🧱🪨🪵🛖🏘🏚🏠🏡🏢🏣🏤🏥🏦🏨🏩🏪🏫🏬🏭🏯🏰💒🗼🗽⛪🕌🛕🕍⛩🕋⛲⛺🌁🌃🏙🌄🌅🌆🌇🌉♨🎠🎡🎢💈🎪🚂🚃🚄🚅🚆🚇🚈🚉🚊🚝🚞🚋🚌🚍🚎🚐🚑🚒🚓🚔🚕🚖🚗🚘🚙🛻🚚🚛🚜🏎🏍🛵🦽🦼🛺🚲🛴🛹🛼🚏🛣🛤🛢⛽🚨🚥🚦🛑🚧⚓⛵🛶🚤🛳⛴🛥🚢✈🛩🛫🛬🪂💺🚁🚟🚠🚡🛰🚀🛸🛎🧳⌛⏳⌚⏰⏱⏲🕰🕛🕧🕐🕜🕑🕝🕒🕞🕓🕟🕔🕠🕕🕡🕖🕢🕗🕣🕘🕤🕙🕥🕚🕦🌑🌒🌓🌔🌕🌖🌗🌘🌙🌚🌛🌜🌡☀🌝🌞🪐⭐🌟🌠🌌☁⛅⛈🌤🌥🌦🌧🌨🌩🌪🌫🌬🌀🌈🌂☂☔⛱⚡❄☃⛄☄🔥💧🌊🎃🎄🎆🎇🧨✨🎈🎉🎊🎋🎍🎎🎏🎐🎑🧧🎀🎁🎗🎟🎫🎖🏆🏅🥇🥈🥉⚽⚾🥎🏀🏐🏈🏉🎾🥏🎳🏏🏑🏒🥍🏓🏸🥊🥋🥅⛳⛸🎣🤿🎽🎿🛷🥌🎯🪀🪁🎱🔮🪄🧿🎮🕹🎰🎲🧩🧸🪅🪆♠♥♦♣♟🃏🀄🎴🎭🖼🎨🧵🪡🧶🪢👓🕶🥽🥼🦺👔👕👖🧣🧤🧥🧦👗👘🥻🩱🩲🩳👙👚👛👜👝🛍🎒🩴👞👟🥾🥿👠👡🩰👢👑👒🎩🎓🧢🪖⛑📿💄💍💎🔇🔈🔉🔊📢📣📯🔔🔕🎼🎵🎶🎙🎚🎛🎤🎧📻🎷🪗🎸🎹🎺🎻🪕🥁";
-
-#[bench]
-fn str_char_count_lorem(b: &mut Bencher) {
- b.iter(|| black_box(LOREM).chars().count());
-}
-
-#[bench]
-fn str_char_count_lorem_short(b: &mut Bencher) {
- b.iter(|| black_box(LOREM_SHORT).chars().count());
-}
-
-#[bench]
-fn str_char_count_emoji(b: &mut Bencher) {
- b.iter(|| black_box(EMOJI).chars().count());
-}
+mod char_count;
+mod corpora;
#[bench]
fn str_validate_emoji(b: &mut Bencher) {
- b.iter(|| str::from_utf8(black_box(EMOJI.as_bytes())));
+ b.iter(|| str::from_utf8(black_box(corpora::emoji::LARGE.as_bytes())));
}
--- /dev/null
+use super::corpora::*;
+use test::{black_box, Bencher};
+
+macro_rules! define_benches {
+ ($( fn $name: ident($arg: ident: &str) $body: block )+) => {
+ define_benches!(mod en_tiny, en::TINY, $($name $arg $body)+);
+ define_benches!(mod en_small, en::SMALL, $($name $arg $body)+);
+ define_benches!(mod en_medium, en::MEDIUM, $($name $arg $body)+);
+ define_benches!(mod en_large, en::LARGE, $($name $arg $body)+);
+ define_benches!(mod en_huge, en::HUGE, $($name $arg $body)+);
+
+ define_benches!(mod zh_tiny, zh::TINY, $($name $arg $body)+);
+ define_benches!(mod zh_small, zh::SMALL, $($name $arg $body)+);
+ define_benches!(mod zh_medium, zh::MEDIUM, $($name $arg $body)+);
+ define_benches!(mod zh_large, zh::LARGE, $($name $arg $body)+);
+ define_benches!(mod zh_huge, zh::HUGE, $($name $arg $body)+);
+
+ define_benches!(mod ru_tiny, ru::TINY, $($name $arg $body)+);
+ define_benches!(mod ru_small, ru::SMALL, $($name $arg $body)+);
+ define_benches!(mod ru_medium, ru::MEDIUM, $($name $arg $body)+);
+ define_benches!(mod ru_large, ru::LARGE, $($name $arg $body)+);
+ define_benches!(mod ru_huge, ru::HUGE, $($name $arg $body)+);
+
+ define_benches!(mod emoji_tiny, emoji::TINY, $($name $arg $body)+);
+ define_benches!(mod emoji_small, emoji::SMALL, $($name $arg $body)+);
+ define_benches!(mod emoji_medium, emoji::MEDIUM, $($name $arg $body)+);
+ define_benches!(mod emoji_large, emoji::LARGE, $($name $arg $body)+);
+ define_benches!(mod emoji_huge, emoji::HUGE, $($name $arg $body)+);
+ };
+ (mod $mod_name: ident, $input: expr, $($name: ident $arg: ident $body: block)+) => {
+ mod $mod_name {
+ use super::*;
+ $(
+ #[bench]
+ fn $name(bencher: &mut Bencher) {
+ let input = $input;
+ bencher.bytes = input.len() as u64;
+ let mut input_s = input.to_string();
+ bencher.iter(|| {
+ let $arg: &str = &black_box(&mut input_s);
+ black_box($body)
+ })
+ }
+ )+
+ }
+ };
+}
+
+define_benches! {
+ fn case00_libcore(s: &str) {
+ libcore(s)
+ }
+
+ fn case01_filter_count_cont_bytes(s: &str) {
+ filter_count_cont_bytes(s)
+ }
+
+ fn case02_iter_increment(s: &str) {
+ iterator_increment(s)
+ }
+
+ fn case03_manual_char_len(s: &str) {
+ manual_char_len(s)
+ }
+}
+
+fn libcore(s: &str) -> usize {
+ s.chars().count()
+}
+
+#[inline]
+fn utf8_is_cont_byte(byte: u8) -> bool {
+ (byte as i8) < -64
+}
+
+fn filter_count_cont_bytes(s: &str) -> usize {
+ s.as_bytes().iter().filter(|&&byte| !utf8_is_cont_byte(byte)).count()
+}
+
+fn iterator_increment(s: &str) -> usize {
+ let mut c = 0;
+ for _ in s.chars() {
+ c += 1;
+ }
+ c
+}
+
+fn manual_char_len(s: &str) -> usize {
+ let s = s.as_bytes();
+ let mut c = 0;
+ let mut i = 0;
+ let l = s.len();
+ while i < l {
+ let b = s[i];
+ if b < 0x80 {
+ i += 1;
+ } else if b < 0xe0 {
+ i += 2;
+ } else if b < 0xf0 {
+ i += 3;
+ } else {
+ i += 4;
+ }
+ c += 1;
+ }
+ c
+}
--- /dev/null
+//! Exposes a number of modules with different kinds of strings.
+//!
+//! Each module contains `&str` constants named `TINY`, `SMALL`, `MEDIUM`,
+//! `LARGE`, and `HUGE`.
+//!
+//! - The `TINY` string is generally around 8 bytes.
+//! - The `SMALL` string is generally around 30-40 bytes.
+//! - The `MEDIUM` string is generally around 600-700 bytes.
+//! - The `LARGE` string is the `MEDIUM` string repeated 8x, and is around 5kb.
+//! - The `HUGE` string is the `LARGE` string repeated 8x (or the `MEDIUM`
+//! string repeated 64x), and is around 40kb.
+//!
+//! Except for `mod emoji` (which is just a bunch of emoji), the strings were
+//! pulled from (localizations of) rust-lang.org.
+
+macro_rules! repeat8 {
+ ($s:expr) => {
+ concat!($s, $s, $s, $s, $s, $s, $s, $s)
+ };
+}
+
+macro_rules! define_consts {
+ ($s:literal) => {
+ pub const MEDIUM: &str = $s;
+ pub const LARGE: &str = repeat8!($s);
+ pub const HUGE: &str = repeat8!(repeat8!(repeat8!($s)));
+ };
+}
+
+pub mod en {
+ pub const TINY: &str = "Mary had";
+ pub const SMALL: &str = "Mary had a little lamb, Little lamb";
+ define_consts! {
+ "Rust is blazingly fast and memory-efficient: with no runtime or garbage
+ collector, it can power performance-critical services, run on embedded
+ devices, and easily integrate with other languages. Rust’s rich type system
+ and ownership model guarantee memory-safety and thread-safety — enabling you
+ to eliminate many classes of bugs at compile-time. Rust has great
+ documentation, a friendly compiler with useful error messages, and top-notch
+ tooling — an integrated package manager and build tool, smart multi-editor
+ support with auto-completion and type inspections, an auto-formatter, and
+ more."
+ }
+}
+
+pub mod zh {
+ pub const TINY: &str = "速度惊";
+ pub const SMALL: &str = "速度惊人且内存利用率极高";
+ define_consts! {
+ "Rust 速度惊人且内存利用率极高。由于\
+ 没有运行时和垃圾回收,它能够胜任对性能要\
+ 求特别高的服务,可以在嵌入式设备上运行,\
+ 还能轻松和其他语言集成。Rust 丰富的类型\
+ 系统和所有权模型保证了内存安全和线程安全,\
+ 让您在编译期就能够消除各种各样的错误。\
+ Rust 拥有出色的文档、友好的编译器和清晰\
+ 的错误提示信息, 还集成了一流的工具——\
+ 包管理器和构建工具, 智能地自动补全和类\
+ 型检验的多编辑器支持, 以及自动格式化代\
+ 码等等。"
+ }
+}
+
+pub mod ru {
+ pub const TINY: &str = "Сотни";
+ pub const SMALL: &str = "Сотни компаний по";
+ define_consts! {
+ "Сотни компаний по всему миру используют Rust в реальных\
+ проектах для быстрых кросс-платформенных решений с\
+ ограниченными ресурсами. Такие проекты, как Firefox,\
+ Dropbox и Cloudflare, используют Rust. Rust отлично\
+ подходит как для стартапов, так и для больших компаний,\
+ как для встраиваемых устройств, так и для масштабируемых\
+ web-сервисов. Мой самый большой комплимент Rust."
+ }
+}
+
+pub mod emoji {
+ pub const TINY: &str = "😀😃";
+ pub const SMALL: &str = "😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘";
+ define_consts! {
+ "😀😃😄😁😆😅🤣😂🙂🙃😉😊😇🥰😍🤩😘😗☺😚😙🥲😋😛😜🤪😝🤑🤗🤭🤫🤔🤐🤨😐😑😶😶🌫️😏😒\
+ 🙄😬😮💨🤥😌😔😪🤤😴😷🤒🤕🤢🤮🤧🥵🥶🥴😵😵💫🤯��🥳🥸😎🤓🧐😕😟🙁☹😮😯😲😳🥺😦😧😨\
+ 😰😥😢😭😱😖😣😞😓😩😫🥱😤😡😠🤬😈👿💀☠💩🤡👹👺👻👽👾🤖😺😸😹😻😼😽🙀😿😾🙈🙉🙊\
+ 💋💌💘💝💖💗💓��💕💟❣💔❤️🔥❤️🩹❤🧡💛💚💙💜🤎🖤🤍💯💢💥💫💦💨🕳💬👁️🗨️🗨🗯💭💤👋\
+ 🤚🖐✋🖖👌🤌🤏✌"
+ }
+}
/// Returns a slice containing the entire array. Equivalent to `&s[..]`.
#[stable(feature = "array_as_slice", since = "1.57.0")]
+ #[rustc_const_stable(feature = "array_as_slice", since = "1.57.0")]
pub const fn as_slice(&self) -> &[T] {
self
}
#[stable(feature = "cell_from", since = "1.12.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for Cell<T> {
+ /// Creates a new `Cell<T>` containing the given value.
fn from(t: T) -> Cell<T> {
Cell::new(t)
}
#[stable(feature = "cell_from", since = "1.12.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for RefCell<T> {
+ /// Creates a new `RefCell<T>` containing the given value.
fn from(t: T) -> RefCell<T> {
RefCell::new(t)
}
/// ```
#[inline(always)]
#[stable(feature = "unsafe_cell_raw_get", since = "1.56.0")]
+ #[rustc_const_stable(feature = "unsafe_cell_raw_get", since = "1.56.0")]
pub const fn raw_get(this: *const Self) -> *mut T {
// We can just cast the pointer from `UnsafeCell<T>` to `T` because of
// #[repr(transparent)]. This exploits libstd's special status, there is
#[stable(feature = "cell_from", since = "1.12.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for UnsafeCell<T> {
+ /// Creates a new `UnsafeCell<T>` containing the given value.
fn from(t: T) -> UnsafeCell<T> {
UnsafeCell::new(t)
}
where
U: ~const From<T>,
{
+ /// Calls `U::from(self)`.
+ ///
+ /// That is, this conversion is whatever the implementation of
+ /// <code>[From]<T> for U</code> chooses to do.
fn into(self) -> U {
U::from(self)
}
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for T {
+ /// Returns the argument unchanged.
fn from(t: T) -> T {
t
}
use crate::iter::{InPlaceIterable, Iterator};
-use crate::ops::{ControlFlow, Try};
+use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, NeverShortCircuit, Residual, Try};
mod chain;
mod cloned;
}
/// An iterator adapter that produces output as long as the underlying
-/// iterator produces `Result::Ok` values.
+/// iterator produces values where `Try::branch` says to `ControlFlow::Continue`.
///
-/// If an error is encountered, the iterator stops and the error is
-/// stored.
-pub(crate) struct ResultShunt<'a, I, E> {
+/// If a `ControlFlow::Break` is encountered, the iterator stops and the
+/// residual is stored.
+pub(crate) struct GenericShunt<'a, I, R> {
iter: I,
- error: &'a mut Result<(), E>,
+ residual: &'a mut Option<R>,
}
-/// Process the given iterator as if it yielded a `T` instead of a
-/// `Result<T, _>`. Any errors will stop the inner iterator and
-/// the overall result will be an error.
-pub(crate) fn process_results<I, T, E, F, U>(iter: I, mut f: F) -> Result<U, E>
+/// Process the given iterator as if it yielded a the item's `Try::Output`
+/// type instead. Any `Try::Residual`s encountered will stop the inner iterator
+/// and be propagated back to the overall result.
+pub(crate) fn try_process<I, T, R, F, U>(iter: I, mut f: F) -> ChangeOutputType<I::Item, U>
where
- I: Iterator<Item = Result<T, E>>,
- for<'a> F: FnMut(ResultShunt<'a, I, E>) -> U,
+ I: Iterator<Item: Try<Output = T, Residual = R>>,
+ for<'a> F: FnMut(GenericShunt<'a, I, R>) -> U,
+ R: Residual<U>,
{
- let mut error = Ok(());
- let shunt = ResultShunt { iter, error: &mut error };
+ let mut residual = None;
+ let shunt = GenericShunt { iter, residual: &mut residual };
let value = f(shunt);
- error.map(|()| value)
+ match residual {
+ Some(r) => FromResidual::from_residual(r),
+ None => Try::from_output(value),
+ }
}
-impl<I, T, E> Iterator for ResultShunt<'_, I, E>
+impl<I, R> Iterator for GenericShunt<'_, I, R>
where
- I: Iterator<Item = Result<T, E>>,
+ I: Iterator<Item: Try<Residual = R>>,
{
- type Item = T;
+ type Item = <I::Item as Try>::Output;
fn next(&mut self) -> Option<Self::Item> {
- self.find(|_| true)
+ self.try_for_each(ControlFlow::Break).break_value()
}
fn size_hint(&self) -> (usize, Option<usize>) {
- if self.error.is_err() {
+ if self.residual.is_some() {
(0, Some(0))
} else {
let (_, upper) = self.iter.size_hint();
}
}
- fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
+ fn try_fold<B, F, T>(&mut self, init: B, mut f: F) -> T
where
- F: FnMut(B, Self::Item) -> R,
- R: Try<Output = B>,
+ F: FnMut(B, Self::Item) -> T,
+ T: Try<Output = B>,
{
- let error = &mut *self.error;
self.iter
- .try_fold(init, |acc, x| match x {
- Ok(x) => ControlFlow::from_try(f(acc, x)),
- Err(e) => {
- *error = Err(e);
+ .try_fold(init, |acc, x| match Try::branch(x) {
+ ControlFlow::Continue(x) => ControlFlow::from_try(f(acc, x)),
+ ControlFlow::Break(r) => {
+ *self.residual = Some(r);
ControlFlow::Break(try { acc })
}
})
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
- #[inline]
- fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
- move |acc, x| Ok(f(acc, x))
- }
-
- self.try_fold(init, ok(fold)).unwrap()
+ self.try_fold(init, NeverShortCircuit::wrap_mut_2(fold)).0
}
}
#[unstable(issue = "none", feature = "inplace_iteration")]
-unsafe impl<I, E> SourceIter for ResultShunt<'_, I, E>
+unsafe impl<I, R> SourceIter for GenericShunt<'_, I, R>
where
I: SourceIter,
{
}
}
-// SAFETY: ResultShunt::next calls I::find, which has to advance `iter` in order to
-// return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's guaranteed that
-// at least one item will be moved out from the underlying source.
+// SAFETY: GenericShunt::next calls `I::try_for_each`, which has to advance `iter`
+// in order to return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's
+// guaranteed that at least one item will be moved out from the underlying source.
#[unstable(issue = "none", feature = "inplace_iteration")]
-unsafe impl<I, T, E> InPlaceIterable for ResultShunt<'_, I, E> where
- I: Iterator<Item = Result<T, E>> + InPlaceIterable
+unsafe impl<I, T, R> InPlaceIterable for GenericShunt<'_, I, R> where
+ I: Iterator<Item: Try<Output = T, Residual = R>> + InPlaceIterable
{
}
#[unstable(feature = "iter_intersperse", reason = "recently added", issue = "79524")]
pub use self::adapters::{Intersperse, IntersperseWith};
-pub(crate) use self::adapters::process_results;
+pub(crate) use self::adapters::try_process;
mod adapters;
mod range;
where
I: Iterator<Item = Result<U, E>>,
{
- iter::process_results(iter, |i| i.sum())
+ iter::try_process(iter, |i| i.sum())
}
}
where
I: Iterator<Item = Result<U, E>>,
{
- iter::process_results(iter, |i| i.product())
+ iter::try_process(iter, |i| i.product())
}
}
where
I: Iterator<Item = Option<U>>,
{
- iter.map(|x| x.ok_or(())).sum::<Result<_, _>>().ok()
+ iter::try_process(iter, |i| i.sum())
}
}
where
I: Iterator<Item = Option<U>>,
{
- iter.map(|x| x.ok_or(())).product::<Result<_, _>>().ok()
+ iter::try_process(iter, |i| i.product())
}
}
/// ```
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented(
+ on(
+ _Self = "[{A}]",
+ message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size",
+ label = "try explicitly collecting into a `Vec<{A}>`",
+ ),
+ on(
+ all(
+ A = "{integer}",
+ any(
+ _Self = "[i8]",
+ _Self = "[i16]",
+ _Self = "[i32]",
+ _Self = "[i64]",
+ _Self = "[i128]",
+ _Self = "[isize]",
+ _Self = "[u8]",
+ _Self = "[u16]",
+ _Self = "[u32]",
+ _Self = "[u64]",
+ _Self = "[u128]",
+ _Self = "[usize]"
+ )
+ ),
+ message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size",
+ label = "try explicitly collecting into a `Vec<{A}>`",
+ ),
message = "a value of type `{Self}` cannot be built from an iterator \
over elements of type `{A}`",
label = "value of type `{Self}` cannot be built from `std::iter::Iterator<Item={A}>`"
use crate::cmp::{self, Ordering};
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
+use super::super::try_process;
use super::super::TrustedRandomAccessNoCoerce;
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
use super::super::{FlatMap, Flatten};
FromIterator::from_iter(self)
}
+ /// Fallibly transforms an iterator into a collection, short circuiting if
+ /// a failure is encountered.
+ ///
+ /// `try_collect()` is a variation of [`collect()`][`collect`] that allows fallible
+ /// conversions during collection. Its main use case is simplifying conversions from
+ /// iterators yielding [`Option<T>`][`Option`] into `Option<Collection<T>>`, or similarly for other [`Try`]
+ /// types (e.g. [`Result`]).
+ ///
+ /// Importantly, `try_collect()` doesn't require that the outer [`Try`] type also implements [`FromIterator`];
+ /// only the inner type produced on `Try::Output` must implement it. Concretely,
+ /// this means that collecting into `ControlFlow<_, Vec<i32>>` is valid because `Vec<i32>` implements
+ /// [`FromIterator`], even though [`ControlFlow`] doesn't.
+ ///
+ /// Also, if a failure is encountered during `try_collect()`, the iterator is still valid and
+ /// may continue to be used, in which case it will continue iterating starting after the element that
+ /// triggered the failure. See the last example below for an example of how this works.
+ ///
+ /// # Examples
+ /// Successfully collecting an iterator of `Option<i32>` into `Option<Vec<i32>>`:
+ /// ```
+ /// #![feature(iterator_try_collect)]
+ ///
+ /// let u = vec![Some(1), Some(2), Some(3)];
+ /// let v = u.into_iter().try_collect::<Vec<i32>>();
+ /// assert_eq!(v, Some(vec![1, 2, 3]));
+ /// ```
+ ///
+ /// Failing to collect in the same way:
+ /// ```
+ /// #![feature(iterator_try_collect)]
+ ///
+ /// let u = vec![Some(1), Some(2), None, Some(3)];
+ /// let v = u.into_iter().try_collect::<Vec<i32>>();
+ /// assert_eq!(v, None);
+ /// ```
+ ///
+ /// A similar example, but with `Result`:
+ /// ```
+ /// #![feature(iterator_try_collect)]
+ ///
+ /// let u: Vec<Result<i32, ()>> = vec![Ok(1), Ok(2), Ok(3)];
+ /// let v = u.into_iter().try_collect::<Vec<i32>>();
+ /// assert_eq!(v, Ok(vec![1, 2, 3]));
+ ///
+ /// let u = vec![Ok(1), Ok(2), Err(()), Ok(3)];
+ /// let v = u.into_iter().try_collect::<Vec<i32>>();
+ /// assert_eq!(v, Err(()));
+ /// ```
+ ///
+ /// Finally, even [`ControlFlow`] works, despite the fact that it
+ /// doesn't implement [`FromIterator`]. Note also that the iterator can
+ /// continue to be used, even if a failure is encountered:
+ ///
+ /// ```
+ /// #![feature(iterator_try_collect)]
+ ///
+ /// use core::ops::ControlFlow::{Break, Continue};
+ ///
+ /// let u = [Continue(1), Continue(2), Break(3), Continue(4), Continue(5)];
+ /// let mut it = u.into_iter();
+ ///
+ /// let v = it.try_collect::<Vec<_>>();
+ /// assert_eq!(v, Break(3));
+ ///
+ /// let v = it.try_collect::<Vec<_>>();
+ /// assert_eq!(v, Continue(vec![4, 5]));
+ /// ```
+ ///
+ /// [`collect`]: Iterator::collect
+ #[inline]
+ #[unstable(feature = "iterator_try_collect", issue = "94047")]
+ fn try_collect<B>(&mut self) -> ChangeOutputType<Self::Item, B>
+ where
+ Self: Sized,
+ <Self as Iterator>::Item: Try,
+ <<Self as Iterator>::Item as Try>::Residual: Residual<B>,
+ B: FromIterator<<Self::Item as Try>::Output>,
+ {
+ try_process(self, |i| i.collect())
+ }
+
/// Consumes an iterator, creating two collections from it.
///
/// The predicate passed to `partition()` can return `true`, or `false`.
#[unstable(feature = "once_cell", issue = "74465")]
impl<T> const From<T> for OnceCell<T> {
+ /// Creates a new `OnceCell<T>` which already contains the given `value`.
fn from(value: T) -> Self {
OnceCell { inner: UnsafeCell::new(Some(value)) }
}
#![feature(associated_type_bounds)]
#![feature(auto_traits)]
#![feature(cfg_target_has_atomic)]
+#![cfg_attr(not(bootstrap), feature(cfg_target_has_atomic_equal_alignment))]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_fn_fn_ptr_basics)]
#![feature(const_fn_trait_bound)]
/// unreachable!("The loop should always return");
/// }
/// ```
+#[cfg(not(bootstrap))]
+#[macro_export]
+#[rustc_builtin_macro(unreachable)]
+#[allow_internal_unstable(edition_panic)]
+#[stable(feature = "rust1", since = "1.0.0")]
+#[cfg_attr(not(test), rustc_diagnostic_item = "unreachable_macro")]
+macro_rules! unreachable {
+ // Expands to either `$crate::panic::unreachable_2015` or `$crate::panic::unreachable_2021`
+ // depending on the edition of the caller.
+ ($($arg:tt)*) => {
+ /* compiler built-in */
+ };
+}
+
+/// unreachable!() macro
+#[cfg(bootstrap)]
#[macro_export]
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "unreachable_macro")]
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
}
- /// Same as `format_args`, but can be used in some const contexts.
+ /// Same as [`format_args`], but can be used in some const contexts.
///
/// This macro is used by the panic macros for the `const_panic` feature.
///
($fmt:expr, $($args:tt)*) => {{ /* compiler built-in */ }};
}
- /// Same as `format_args`, but adds a newline in the end.
+ /// Same as [`format_args`], but adds a newline in the end.
#[unstable(
feature = "format_args_nl",
issue = "none",
$reversed:expr, $le_bytes:expr, $be_bytes:expr,
$to_xe_bytes_doc:expr, $from_xe_bytes_doc:expr) => {
/// The smallest value that can be represented by this integer type,
- #[doc = concat!("-2<sup>", $BITS_MINUS_ONE, "</sup>.")]
+ #[doc = concat!("−2<sup>", $BITS_MINUS_ONE, "</sup>.")]
///
/// # Examples
///
pub const MIN: Self = !0 ^ ((!0 as $UnsignedT) >> 1) as Self;
/// The largest value that can be represented by this integer type,
- #[doc = concat!("2<sup>", $BITS_MINUS_ONE, "</sup> - 1.")]
+ #[doc = concat!("2<sup>", $BITS_MINUS_ONE, "</sup> − 1.")]
///
/// # Examples
///
///
/// ```
#[stable(feature = "saturating_div", since = "1.58.0")]
+ #[rustc_const_stable(feature = "saturating_div", since = "1.58.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
/// Basic usage:
///
/// ```
- /// #![feature(int_abs_diff)]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($UnsignedT), ");")]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($UnsignedT), ");")]
#[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").abs_diff(80), 180", stringify!($UnsignedT), ");")]
#[doc = concat!("assert_eq!((-100", stringify!($SelfT), ").abs_diff(-120), 20", stringify!($UnsignedT), ");")]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MIN.abs_diff(", stringify!($SelfT), "::MAX), ", stringify!($UnsignedT), "::MAX);")]
/// ```
- #[unstable(feature = "int_abs_diff", issue = "89492")]
+ #[stable(feature = "int_abs_diff", since = "1.60.0")]
+ #[rustc_const_stable(feature = "int_abs_diff", since = "1.60.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
/// # Examples
///
/// ```
- /// #![feature(inherent_ascii_escape)]
///
/// assert_eq!("0", b'0'.escape_ascii().to_string());
/// assert_eq!("\\t", b'\t'.escape_ascii().to_string());
/// ```
#[must_use = "this returns the escaped byte as an iterator, \
without modifying the original"]
- #[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+ #[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
#[inline]
- pub fn escape_ascii(&self) -> ascii::EscapeDefault {
- ascii::escape_default(*self)
+ pub fn escape_ascii(self) -> ascii::EscapeDefault {
+ ascii::escape_default(self)
+ }
+
+ pub(crate) fn is_utf8_char_boundary(self) -> bool {
+ // This is bit magic equivalent to: b < 128 || b >= 192
+ (self as i8) >= -0x40
}
}
/// ```
#[must_use]
#[stable(feature = "nonzero_is_power_of_two", since = "1.59.0")]
+ #[rustc_const_stable(feature = "nonzero_is_power_of_two", since = "1.59.0")]
#[inline]
pub const fn is_power_of_two(self) -> bool {
// LLVM 11 normalizes `unchecked_sub(x, 1) & x == 0` to the implementation seen here.
pub const MIN: Self = 0;
/// The largest value that can be represented by this integer type,
- #[doc = concat!("2<sup>", $BITS, "</sup> - 1.")]
+ #[doc = concat!("2<sup>", $BITS, "</sup> − 1.")]
///
/// # Examples
///
///
/// ```
#[stable(feature = "saturating_div", since = "1.58.0")]
+ #[rustc_const_stable(feature = "saturating_div", since = "1.58.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
/// Basic usage:
///
/// ```
- /// #![feature(int_abs_diff)]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(80), 20", stringify!($SelfT), ");")]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".abs_diff(110), 10", stringify!($SelfT), ");")]
/// ```
- #[unstable(feature = "int_abs_diff", issue = "89492")]
+ #[stable(feature = "int_abs_diff", since = "1.60.0")]
+ #[rustc_const_stable(feature = "int_abs_diff", since = "1.60.0")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
}
forward_ref_op_assign! { impl const AddAssign, add_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const AddAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn add_assign(&mut self, other: $t) {
+ *self = *self + Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const AddAssign, add_assign for Wrapping<$t>, $t }
+
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Sub for Wrapping<$t> {
}
forward_ref_op_assign! { impl const SubAssign, sub_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const SubAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn sub_assign(&mut self, other: $t) {
+ *self = *self - Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const SubAssign, sub_assign for Wrapping<$t>, $t }
+
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Mul for Wrapping<$t> {
}
forward_ref_op_assign! { impl const MulAssign, mul_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const MulAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn mul_assign(&mut self, other: $t) {
+ *self = *self * Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const MulAssign, mul_assign for Wrapping<$t>, $t }
+
#[stable(feature = "wrapping_div", since = "1.3.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Div for Wrapping<$t> {
}
forward_ref_op_assign! { impl const DivAssign, div_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const DivAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn div_assign(&mut self, other: $t) {
+ *self = *self / Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const DivAssign, div_assign for Wrapping<$t>, $t }
+
#[stable(feature = "wrapping_impls", since = "1.7.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Rem for Wrapping<$t> {
}
forward_ref_op_assign! { impl const RemAssign, rem_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const RemAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn rem_assign(&mut self, other: $t) {
+ *self = *self % Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const RemAssign, rem_assign for Wrapping<$t>, $t }
+
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Not for Wrapping<$t> {
}
forward_ref_op_assign! { impl const BitXorAssign, bitxor_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const BitXorAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn bitxor_assign(&mut self, other: $t) {
+ *self = *self ^ Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const BitXorAssign, bitxor_assign for Wrapping<$t>, $t }
+
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const BitOr for Wrapping<$t> {
}
forward_ref_op_assign! { impl const BitOrAssign, bitor_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const BitOrAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn bitor_assign(&mut self, other: $t) {
+ *self = *self | Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const BitOrAssign, bitor_assign for Wrapping<$t>, $t }
+
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const BitAnd for Wrapping<$t> {
}
forward_ref_op_assign! { impl const BitAndAssign, bitand_assign for Wrapping<$t>, Wrapping<$t> }
+ #[stable(feature = "wrapping_int_assign_impl", since = "1.60.0")]
+ #[rustc_const_unstable(feature = "const_ops", issue = "90080")]
+ impl const BitAndAssign<$t> for Wrapping<$t> {
+ #[inline]
+ fn bitand_assign(&mut self, other: $t) {
+ *self = *self & Wrapping(other);
+ }
+ }
+ forward_ref_op_assign! { impl const BitAndAssign, bitand_assign for Wrapping<$t>, $t }
+
#[stable(feature = "wrapping_neg", since = "1.10.0")]
#[rustc_const_unstable(feature = "const_ops", issue = "90080")]
impl const Neg for Wrapping<$t> {
enclosing_scope = "this function should return `Result` or `Option` to accept `?`"
),
)]
+#[rustc_diagnostic_item = "FromResidual"]
#[unstable(feature = "try_trait_v2", issue = "84277")]
pub trait FromResidual<R = <Self as Try>::Residual> {
/// Constructs the type from a compatible `Residual` type.
#[repr(transparent)]
pub(crate) struct NeverShortCircuit<T>(pub T);
+impl<T> NeverShortCircuit<T> {
+ /// Wrap a binary `FnMut` to return its result wrapped in a `NeverShortCircuit`.
+ #[inline]
+ pub fn wrap_mut_2<A, B>(mut f: impl FnMut(A, B) -> T) -> impl FnMut(A, B) -> Self {
+ move |a, b| NeverShortCircuit(f(a, b))
+ }
+}
+
pub(crate) enum NeverShortCircuitResidual {}
impl<T> Try for NeverShortCircuit<T> {
#![stable(feature = "rust1", since = "1.0.0")]
-use crate::iter::{FromIterator, FusedIterator, TrustedLen};
+use crate::iter::{self, FromIterator, FusedIterator, TrustedLen};
use crate::panicking::{panic, panic_str};
use crate::pin::Pin;
use crate::{
/// # Examples
///
/// ```
- /// fn sq(x: u32) -> Option<u32> { Some(x * x) }
- /// fn nope(_: u32) -> Option<u32> { None }
+ /// fn sq_then_to_string(x: u32) -> Option<String> {
+ /// x.checked_mul(x).map(|sq| sq.to_string())
+ /// }
+ ///
+ /// assert_eq!(Some(2).and_then(sq_then_to_string), Some(4.to_string()));
+ /// assert_eq!(Some(1_000_000).and_then(sq_then_to_string), None); // overflowed!
+ /// assert_eq!(None.and_then(sq_then_to_string), None);
+ /// ```
+ ///
+ /// Often used to chain fallible operations that may return [`None`].
+ ///
+ /// ```
+ /// let arr_2d = [["A0", "A1"], ["B0", "B1"]];
+ ///
+ /// let item_0_1 = arr_2d.get(0).and_then(|row| row.get(1));
+ /// assert_eq!(item_0_1, Some(&"A1"));
///
- /// assert_eq!(Some(2).and_then(sq).and_then(sq), Some(16));
- /// assert_eq!(Some(2).and_then(sq).and_then(nope), None);
- /// assert_eq!(Some(2).and_then(nope).and_then(sq), None);
- /// assert_eq!(None.and_then(sq).and_then(sq), None);
+ /// let item_2_0 = arr_2d.get(2).and_then(|row| row.get(0));
+ /// assert_eq!(item_2_0, None);
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
// FIXME(#11084): This could be replaced with Iterator::scan when this
// performance bug is closed.
- iter.into_iter().map(|x| x.ok_or(())).collect::<Result<_, _>>().ok()
+ iter::try_process(iter.into_iter(), |i| i.collect())
}
}
),
}
+#[doc(hidden)]
+#[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
+#[allow_internal_unstable(core_panic)]
+#[rustc_diagnostic_item = "unreachable_2015_macro"]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro unreachable_2015 {
+ () => (
+ $crate::panicking::panic("internal error: entered unreachable code")
+ ),
+ // Use of `unreachable_display` for non_fmt_panic lint.
+ // NOTE: the message ("internal error ...") is embeded directly in unreachable_display
+ ($msg:expr $(,)?) => (
+ $crate::panicking::unreachable_display(&$msg)
+ ),
+ ($fmt:expr, $($arg:tt)*) => (
+ $crate::panic!($crate::concat!("internal error: entered unreachable code: ", $fmt), $($arg)*)
+ ),
+}
+
+#[doc(hidden)]
+#[unstable(feature = "edition_panic", issue = "none", reason = "use unreachable!() instead")]
+#[allow_internal_unstable(core_panic)]
+#[rustc_diagnostic_item = "unreachable_2021_macro"]
+#[rustc_macro_transparency = "semitransparent"]
+pub macro unreachable_2021 {
+ () => (
+ $crate::panicking::panic("internal error: entered unreachable code")
+ ),
+ ($($t:tt)+) => (
+ $crate::panic!("internal error: entered unreachable code: {}", $crate::format_args!($($t)+))
+ ),
+}
+
/// An internal trait used by libstd to pass data from libstd to `panic_unwind`
/// and other panic runtimes. Not intended to be stabilized any time soon, do
/// not use.
/// This is true for most kinds of panics with the exception of panics
/// caused by trying to unwind out of a `Drop` implementation or a function
/// whose ABI does not support unwinding.
+ ///
+ /// It is safe for a panic handler to unwind even when this function returns
+ /// true, however this will simply cause the panic handler to be called
+ /// again.
#[must_use]
#[unstable(feature = "panic_can_unwind", issue = "92988")]
pub fn can_unwind(&self) -> bool {
#[inline]
#[track_caller]
-#[lang = "panic_str"] // needed for `non-fmt-panics` lint
+#[rustc_diagnostic_item = "panic_str"]
#[rustc_const_unstable(feature = "core_panic", issue = "none")]
pub const fn panic_str(expr: &str) -> ! {
panic_display(&expr);
}
+#[cfg(not(bootstrap))]
+#[inline]
+#[track_caller]
+#[rustc_diagnostic_item = "unreachable_display"] // needed for `non-fmt-panics` lint
+pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! {
+ panic_fmt(format_args!("internal error: entered unreachable code: {}", *x));
+}
+
#[inline]
#[track_caller]
#[lang = "panic_display"] // needed for const-evaluated panics
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct Pin<P> {
- pointer: P,
+ // FIXME(#93176): this field is made `#[unstable] #[doc(hidden)] pub` to:
+ // - deter downstream users from accessing it (which would be unsound!),
+ // - let the `pin!` macro access it (such a macro requires using struct
+ // literal syntax in order to benefit from lifetime extension).
+ // Long-term, `unsafe` fields or macro hygiene are expected to offer more robust alternatives.
+ #[unstable(feature = "unsafe_pin_internals", issue = "none")]
+ #[doc(hidden)]
+ pub pointer: P,
}
// The following implementations aren't derived in order to avoid soundness
#[stable(feature = "pin", since = "1.33.0")]
impl<P, U> DispatchFromDyn<Pin<U>> for Pin<P> where P: DispatchFromDyn<U> {}
+
+/// Constructs a <code>[Pin]<[&mut] T></code>, by pinning[^1] a `value: T` _locally_[^2].
+///
+/// Unlike [`Box::pin`], this does not involve a heap allocation.
+///
+/// [^1]: If the (type `T` of the) given value does not implement [`Unpin`], then this
+/// effectively pins the `value` in memory, where it will be unable to be moved.
+/// Otherwise, <code>[Pin]<[&mut] T></code> behaves like <code>[&mut] T</code>, and operations such
+/// as [`mem::replace()`][crate::mem::replace] will allow extracting that value, and therefore,
+/// moving it.
+/// See [the `Unpin` section of the `pin` module][self#unpin] for more info.
+///
+/// [^2]: This is usually dubbed "stack"-pinning. And whilst local values are almost always located
+/// in the stack (_e.g._, when within the body of a non-`async` function), the truth is that inside
+/// the body of an `async fn` or block —more generally, the body of a generator— any locals crossing
+/// an `.await` point —a `yield` point— end up being part of the state captured by the `Future` —by
+/// the `Generator`—, and thus will be stored wherever that one is.
+///
+/// ## Examples
+///
+/// ### Basic usage
+///
+/// ```rust
+/// #![feature(pin_macro)]
+/// # use core::marker::PhantomPinned as Foo;
+/// use core::pin::{pin, Pin};
+///
+/// fn stuff(foo: Pin<&mut Foo>) {
+/// // …
+/// # let _ = foo;
+/// }
+///
+/// let pinned_foo = pin!(Foo { /* … */ });
+/// stuff(pinned_foo);
+/// // or, directly:
+/// stuff(pin!(Foo { /* … */ }));
+/// ```
+///
+/// ### Manually polling a `Future` (wihout `Unpin` bounds)
+///
+/// ```rust
+/// #![feature(pin_macro)]
+/// use std::{
+/// future::Future,
+/// pin::pin,
+/// task::{Context, Poll},
+/// thread,
+/// };
+/// # use std::{sync::Arc, task::Wake, thread::Thread};
+///
+/// # /// A waker that wakes up the current thread when called.
+/// # struct ThreadWaker(Thread);
+/// #
+/// # impl Wake for ThreadWaker {
+/// # fn wake(self: Arc<Self>) {
+/// # self.0.unpark();
+/// # }
+/// # }
+/// #
+/// /// Runs a future to completion.
+/// fn block_on<Fut: Future>(fut: Fut) -> Fut::Output {
+/// let waker_that_unparks_thread = // …
+/// # Arc::new(ThreadWaker(thread::current())).into();
+/// let mut cx = Context::from_waker(&waker_that_unparks_thread);
+/// // Pin the future so it can be polled.
+/// let mut pinned_fut = pin!(fut);
+/// loop {
+/// match pinned_fut.as_mut().poll(&mut cx) {
+/// Poll::Pending => thread::park(),
+/// Poll::Ready(res) => return res,
+/// }
+/// }
+/// }
+/// #
+/// # assert_eq!(42, block_on(async { 42 }));
+/// ```
+///
+/// ### With `Generator`s
+///
+/// ```rust
+/// #![feature(generators, generator_trait, pin_macro)]
+/// use core::{
+/// ops::{Generator, GeneratorState},
+/// pin::pin,
+/// };
+///
+/// fn generator_fn() -> impl Generator<Yield = usize, Return = ()> /* not Unpin */ {
+/// // Allow generator to be self-referential (not `Unpin`)
+/// // vvvvvv so that locals can cross yield points.
+/// static || {
+/// let foo = String::from("foo"); // --+
+/// yield 0; // | <- crosses yield point!
+/// println!("{}", &foo); // <----------+
+/// yield foo.len();
+/// }
+/// }
+///
+/// fn main() {
+/// let mut generator = pin!(generator_fn());
+/// match generator.as_mut().resume(()) {
+/// GeneratorState::Yielded(0) => {},
+/// _ => unreachable!(),
+/// }
+/// match generator.as_mut().resume(()) {
+/// GeneratorState::Yielded(3) => {},
+/// _ => unreachable!(),
+/// }
+/// match generator.resume(()) {
+/// GeneratorState::Yielded(_) => unreachable!(),
+/// GeneratorState::Complete(()) => {},
+/// }
+/// }
+/// ```
+///
+/// ## Remarks
+///
+/// Precisely because a value is pinned to local storage, the resulting <code>[Pin]<[&mut] T></code>
+/// reference ends up borrowing a local tied to that block: it can't escape it.
+///
+/// The following, for instance, fails to compile:
+///
+/// ```rust,compile_fail
+/// #![feature(pin_macro)]
+/// use core::pin::{pin, Pin};
+/// # use core::{marker::PhantomPinned as Foo, mem::drop as stuff};
+///
+/// let x: Pin<&mut Foo> = {
+/// let x: Pin<&mut Foo> = pin!(Foo { /* … */ });
+/// x
+/// }; // <- Foo is dropped
+/// stuff(x); // Error: use of dropped value
+/// ```
+///
+/// <details><summary>Error message</summary>
+///
+/// ```console
+/// error[E0716]: temporary value dropped while borrowed
+/// --> src/main.rs:9:28
+/// |
+/// 8 | let x: Pin<&mut Foo> = {
+/// | - borrow later stored here
+/// 9 | let x: Pin<&mut Foo> = pin!(Foo { /* … */ });
+/// | ^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
+/// 10 | x
+/// 11 | }; // <- Foo is dropped
+/// | - temporary value is freed at the end of this statement
+/// |
+/// = note: consider using a `let` binding to create a longer lived value
+/// ```
+///
+/// </details>
+///
+/// This makes [`pin!`] **unsuitable to pin values when intending to _return_ them**. Instead, the
+/// value is expected to be passed around _unpinned_ until the point where it is to be consumed,
+/// where it is then useful and even sensible to pin the value locally using [`pin!`].
+///
+/// If you really need to return a pinned value, consider using [`Box::pin`] instead.
+///
+/// On the other hand, pinning to the stack[<sup>2</sup>](#fn2) using [`pin!`] is likely to be
+/// cheaper than pinning into a fresh heap allocation using [`Box::pin`]. Moreover, by virtue of not
+/// even needing an allocator, [`pin!`] is the main non-`unsafe` `#![no_std]`-compatible [`Pin`]
+/// constructor.
+///
+/// [`Box::pin`]: ../../std/boxed/struct.Box.html#method.pin
+#[unstable(feature = "pin_macro", issue = "93178")]
+#[rustc_macro_transparency = "semitransparent"]
+#[allow_internal_unstable(unsafe_pin_internals)]
+pub macro pin($value:expr $(,)?) {
+ // This is `Pin::new_unchecked(&mut { $value })`, so, for starters, let's
+ // review such a hypothetical macro (that any user-code could define):
+ //
+ // ```rust
+ // macro_rules! pin {( $value:expr ) => (
+ // match &mut { $value } { at_value => unsafe { // Do not wrap `$value` in an `unsafe` block.
+ // $crate::pin::Pin::<&mut _>::new_unchecked(at_value)
+ // }}
+ // )}
+ // ```
+ //
+ // Safety:
+ // - `type P = &mut _`. There are thus no pathological `Deref{,Mut}` impls
+ // that would break `Pin`'s invariants.
+ // - `{ $value }` is braced, making it a _block expression_, thus **moving**
+ // the given `$value`, and making it _become an **anonymous** temporary_.
+ // By virtue of being anonynomous, it can no longer be accessed, thus
+ // preventing any attemps to `mem::replace` it or `mem::forget` it, _etc._
+ //
+ // This gives us a `pin!` definition that is sound, and which works, but only
+ // in certain scenarios:
+ // - If the `pin!(value)` expression is _directly_ fed to a function call:
+ // `let poll = pin!(fut).poll(cx);`
+ // - If the `pin!(value)` expression is part of a scrutinee:
+ // ```rust
+ // match pin!(fut) { pinned_fut => {
+ // pinned_fut.as_mut().poll(...);
+ // pinned_fut.as_mut().poll(...);
+ // }} // <- `fut` is dropped here.
+ // ```
+ // Alas, it doesn't work for the more straight-forward use-case: `let` bindings.
+ // ```rust
+ // let pinned_fut = pin!(fut); // <- temporary value is freed at the end of this statement
+ // pinned_fut.poll(...) // error[E0716]: temporary value dropped while borrowed
+ // // note: consider using a `let` binding to create a longer lived value
+ // ```
+ // - Issues such as this one are the ones motivating https://github.com/rust-lang/rfcs/pull/66
+ //
+ // This makes such a macro incredibly unergonomic in practice, and the reason most macros
+ // out there had to take the path of being a statement/binding macro (_e.g._, `pin!(future);`)
+ // instead of featuring the more intuitive ergonomics of an expression macro.
+ //
+ // Luckily, there is a way to avoid the problem. Indeed, the problem stems from the fact that a
+ // temporary is dropped at the end of its enclosing statement when it is part of the parameters
+ // given to function call, which has precisely been the case with our `Pin::new_unchecked()`!
+ // For instance,
+ // ```rust
+ // let p = Pin::new_unchecked(&mut <temporary>);
+ // ```
+ // becomes:
+ // ```rust
+ // let p = { let mut anon = <temporary>; &mut anon };
+ // ```
+ //
+ // However, when using a literal braced struct to construct the value, references to temporaries
+ // can then be taken. This makes Rust change the lifespan of such temporaries so that they are,
+ // instead, dropped _at the end of the enscoping block_.
+ // For instance,
+ // ```rust
+ // let p = Pin { pointer: &mut <temporary> };
+ // ```
+ // becomes:
+ // ```rust
+ // let mut anon = <temporary>;
+ // let p = Pin { pointer: &mut anon };
+ // ```
+ // which is *exactly* what we want.
+ //
+ // See https://doc.rust-lang.org/1.58.1/reference/destructors.html#temporary-lifetime-extension
+ // for more info.
+ $crate::pin::Pin::<&mut _> { pointer: &mut { $value } }
+}
#[stable(feature = "nonnull", since = "1.25.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T: ?Sized> const From<&mut T> for NonNull<T> {
+ /// Converts a `&mut T` to a `NonNull<T>`.
+ ///
+ /// This conversion is safe and infallible since references cannot be null.
#[inline]
fn from(reference: &mut T) -> Self {
// SAFETY: A mutable reference cannot be null.
#[stable(feature = "nonnull", since = "1.25.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T: ?Sized> const From<&T> for NonNull<T> {
+ /// Converts a `&T` to a `NonNull<T>`.
+ ///
+ /// This conversion is safe and infallible since references cannot be null.
#[inline]
fn from(reference: &T) -> Self {
// SAFETY: A reference cannot be null, so the conditions for
#[unstable(feature = "ptr_internals", issue = "none")]
impl<T: ?Sized> const From<&mut T> for Unique<T> {
+ /// Converts a `&mut T` to a `Unique<T>`.
+ ///
+ /// This conversion is infallible since references cannot be null.
#[inline]
fn from(reference: &mut T) -> Self {
// SAFETY: A mutable reference cannot be null
///
/// # Examples
///
- /// Basic usage:
+ /// ```
+ /// fn sq_then_to_string(x: u32) -> Result<String, &'static str> {
+ /// x.checked_mul(x).map(|sq| sq.to_string()).ok_or("overflowed")
+ /// }
///
+ /// assert_eq!(Ok(2).and_then(sq_then_to_string), Ok(4.to_string()));
+ /// assert_eq!(Ok(1_000_000).and_then(sq_then_to_string), Err("overflowed"));
+ /// assert_eq!(Err("not a number").and_then(sq_then_to_string), Err("not a number"));
/// ```
- /// fn sq(x: u32) -> Result<u32, u32> { Ok(x * x) }
- /// fn err(x: u32) -> Result<u32, u32> { Err(x) }
///
- /// assert_eq!(Ok(2).and_then(sq).and_then(sq), Ok(16));
- /// assert_eq!(Ok(2).and_then(sq).and_then(err), Err(4));
- /// assert_eq!(Ok(2).and_then(err).and_then(sq), Err(2));
- /// assert_eq!(Err(3).and_then(sq).and_then(sq), Err(3));
+ /// Often used to chain fallible operations that may return [`Err`].
+ ///
+ /// ```
+ /// use std::{io::ErrorKind, path::Path};
+ ///
+ /// // Note: on Windows "/" maps to "C:\"
+ /// let root_modified_time = Path::new("/").metadata().and_then(|md| md.modified());
+ /// assert!(root_modified_time.is_ok());
+ ///
+ /// let should_fail = Path::new("/bad/path").metadata().and_then(|md| md.modified());
+ /// assert!(should_fail.is_err());
+ /// assert_eq!(should_fail.unwrap_err().kind(), ErrorKind::NotFound);
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
// FIXME(#11084): This could be replaced with Iterator::scan when this
// performance bug is closed.
- iter::process_results(iter.into_iter(), |i| i.collect())
+ iter::try_process(iter.into_iter(), |i| i.collect())
}
}
/// # Examples
///
/// ```
- /// #![feature(inherent_ascii_escape)]
///
/// let s = b"0\t\r\n'\"\\\x9d";
/// let escaped = s.escape_ascii().to_string();
/// ```
#[must_use = "this returns the escaped bytes as an iterator, \
without modifying the original"]
- #[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+ #[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
pub fn escape_ascii(&self) -> EscapeAscii<'_> {
EscapeAscii { inner: self.iter().flat_map(EscapeByte) }
}
///
/// This `struct` is created by the [`slice::escape_ascii`] method. See its
/// documentation for more information.
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
#[derive(Clone)]
pub struct EscapeAscii<'a> {
inner: iter::FlatMap<super::Iter<'a, u8>, ascii::EscapeDefault, EscapeByte>,
}
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
impl<'a> iter::Iterator for EscapeAscii<'a> {
type Item = u8;
#[inline]
}
}
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
impl<'a> iter::DoubleEndedIterator for EscapeAscii<'a> {
fn next_back(&mut self) -> Option<u8> {
self.inner.next_back()
}
}
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
impl<'a> iter::ExactSizeIterator for EscapeAscii<'a> {}
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
impl<'a> iter::FusedIterator for EscapeAscii<'a> {}
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
impl<'a> fmt::Display for EscapeAscii<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.clone().try_for_each(|b| f.write_char(b as char))
}
}
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
impl<'a> fmt::Debug for EscapeAscii<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EscapeAscii").finish_non_exhaustive()
//! Comparison traits for `[T]`.
-use crate::cmp;
-use crate::cmp::Ordering::{self, Greater, Less};
+use crate::cmp::{self, Ordering};
use crate::mem;
use super::from_raw_parts;
impl SliceOrd for u8 {
#[inline]
fn compare(left: &[Self], right: &[Self]) -> Ordering {
- let order =
- // SAFETY: `left` and `right` are references and are thus guaranteed to be valid.
- // We use the minimum of both lengths which guarantees that both regions are
- // valid for reads in that interval.
- unsafe { memcmp(left.as_ptr(), right.as_ptr(), cmp::min(left.len(), right.len())) };
+ // Since the length of a slice is always less than or equal to isize::MAX, this never underflows.
+ let diff = left.len() as isize - right.len() as isize;
+ // This comparison gets optimized away (on x86_64 and ARM) because the subtraction updates flags.
+ let len = if left.len() < right.len() { left.len() } else { right.len() };
+ // SAFETY: `left` and `right` are references and are thus guaranteed to be valid.
+ // We use the minimum of both lengths which guarantees that both regions are
+ // valid for reads in that interval.
+ let mut order = unsafe { memcmp(left.as_ptr(), right.as_ptr(), len) as isize };
if order == 0 {
- left.len().cmp(&right.len())
- } else if order < 0 {
- Less
- } else {
- Greater
+ order = diff;
}
+ order.cmp(&0)
}
}
#[unstable(feature = "slice_range", issue = "76393")]
pub use index::range;
-#[unstable(feature = "inherent_ascii_escape", issue = "77174")]
+#[stable(feature = "inherent_ascii_escape", since = "1.60.0")]
pub use ascii::EscapeAscii;
/// Calculates the direction and split point of a one-sided range.
--- /dev/null
+//! Code for efficiently counting the number of `char`s in a UTF-8 encoded
+//! string.
+//!
+//! Broadly, UTF-8 encodes `char`s as a "leading" byte which begins the `char`,
+//! followed by some number (possibly 0) of continuation bytes.
+//!
+//! The leading byte can have a number of bit-patterns (with the specific
+//! pattern indicating how many continuation bytes follow), but the continuation
+//! bytes are always in the format `0b10XX_XXXX` (where the `X`s can take any
+//! value). That is, the most significant bit is set, and the second most
+//! significant bit is unset.
+//!
+//! To count the number of characters, we can just count the number of bytes in
+//! the string which are not continuation bytes, which can be done many bytes at
+//! a time fairly easily.
+//!
+//! Note: Because the term "leading byte" can sometimes be ambiguous (for
+//! example, it could also refer to the first byte of a slice), we'll often use
+//! the term "non-continuation byte" to refer to these bytes in the code.
+use core::intrinsics::unlikely;
+
+const USIZE_SIZE: usize = core::mem::size_of::<usize>();
+const UNROLL_INNER: usize = 4;
+
+#[inline]
+pub(super) fn count_chars(s: &str) -> usize {
+ if s.len() < USIZE_SIZE * UNROLL_INNER {
+ // Avoid entering the optimized implementation for strings where the
+ // difference is not likely to matter, or where it might even be slower.
+ // That said, a ton of thought was not spent on the particular threshold
+ // here, beyond "this value seems to make sense".
+ char_count_general_case(s.as_bytes())
+ } else {
+ do_count_chars(s)
+ }
+}
+
+fn do_count_chars(s: &str) -> usize {
+ // For correctness, `CHUNK_SIZE` must be:
+ //
+ // - Less than or equal to 255, otherwise we'll overflow bytes in `counts`.
+ // - A multiple of `UNROLL_INNER`, otherwise our `break` inside the
+ // `body.chunks(CHUNK_SIZE)` loop is incorrect.
+ //
+ // For performance, `CHUNK_SIZE` should be:
+ // - Relatively cheap to `/` against (so some simple sum of powers of two).
+ // - Large enough to avoid paying for the cost of the `sum_bytes_in_usize`
+ // too often.
+ const CHUNK_SIZE: usize = 192;
+
+ // Check the properties of `CHUNK_SIZE` and `UNROLL_INNER` that are required
+ // for correctness.
+ const _: () = assert!(CHUNK_SIZE < 256);
+ const _: () = assert!(CHUNK_SIZE % UNROLL_INNER == 0);
+
+ // SAFETY: transmuting `[u8]` to `[usize]` is safe except for size
+ // differences which are handled by `align_to`.
+ let (head, body, tail) = unsafe { s.as_bytes().align_to::<usize>() };
+
+ // This should be quite rare, and basically exists to handle the degenerate
+ // cases where align_to fails (as well as miri under symbolic alignment
+ // mode).
+ //
+ // The `unlikely` helps discourage LLVM from inlining the body, which is
+ // nice, as we would rather not mark the `char_count_general_case` function
+ // as cold.
+ if unlikely(body.is_empty() || head.len() > USIZE_SIZE || tail.len() > USIZE_SIZE) {
+ return char_count_general_case(s.as_bytes());
+ }
+
+ let mut total = char_count_general_case(head) + char_count_general_case(tail);
+ // Split `body` into `CHUNK_SIZE` chunks to reduce the frequency with which
+ // we call `sum_bytes_in_usize`.
+ for chunk in body.chunks(CHUNK_SIZE) {
+ // We accumulate intermediate sums in `counts`, where each byte contains
+ // a subset of the sum of this chunk, like a `[u8; size_of::<usize>()]`.
+ let mut counts = 0;
+
+ let (unrolled_chunks, remainder) = chunk.as_chunks::<UNROLL_INNER>();
+ for unrolled in unrolled_chunks {
+ for &word in unrolled {
+ // Because `CHUNK_SIZE` is < 256, this addition can't cause the
+ // count in any of the bytes to overflow into a subsequent byte.
+ counts += contains_non_continuation_byte(word);
+ }
+ }
+
+ // Sum the values in `counts` (which, again, is conceptually a `[u8;
+ // size_of::<usize>()]`), and accumulate the result into `total`.
+ total += sum_bytes_in_usize(counts);
+
+ // If there's any data in `remainder`, then handle it. This will only
+ // happen for the last `chunk` in `body.chunks()` (because `CHUNK_SIZE`
+ // is divisible by `UNROLL_INNER`), so we explicitly break at the end
+ // (which seems to help LLVM out).
+ if !remainder.is_empty() {
+ // Accumulate all the data in the remainder.
+ let mut counts = 0;
+ for &word in remainder {
+ counts += contains_non_continuation_byte(word);
+ }
+ total += sum_bytes_in_usize(counts);
+ break;
+ }
+ }
+ total
+}
+
+// Checks each byte of `w` to see if it contains the first byte in a UTF-8
+// sequence. Bytes in `w` which are continuation bytes are left as `0x00` (e.g.
+// false), and bytes which are non-continuation bytes are left as `0x01` (e.g.
+// true)
+#[inline]
+fn contains_non_continuation_byte(w: usize) -> usize {
+ const LSB: usize = 0x0101_0101_0101_0101u64 as usize;
+ ((!w >> 7) | (w >> 6)) & LSB
+}
+
+// Morally equivalent to `values.to_ne_bytes().into_iter().sum::<usize>()`, but
+// more efficient.
+#[inline]
+fn sum_bytes_in_usize(values: usize) -> usize {
+ const LSB_SHORTS: usize = 0x0001_0001_0001_0001_u64 as usize;
+ const SKIP_BYTES: usize = 0x00ff_00ff_00ff_00ff_u64 as usize;
+
+ let pair_sum: usize = (values & SKIP_BYTES) + ((values >> 8) & SKIP_BYTES);
+ pair_sum.wrapping_mul(LSB_SHORTS) >> ((USIZE_SIZE - 2) * 8)
+}
+
+// This is the most direct implementation of the concept of "count the number of
+// bytes in the string which are not continuation bytes", and is used for the
+// head and tail of the input string (the first and last item in the tuple
+// returned by `slice::align_to`).
+fn char_count_general_case(s: &[u8]) -> usize {
+ s.iter().filter(|&&byte| !super::validations::utf8_is_cont_byte(byte)).count()
+}
use super::from_utf8_unchecked;
use super::pattern::Pattern;
use super::pattern::{DoubleEndedSearcher, ReverseSearcher, Searcher};
-use super::validations::{next_code_point, next_code_point_reverse, utf8_is_cont_byte};
+use super::validations::{next_code_point, next_code_point_reverse};
use super::LinesAnyMap;
use super::{BytesIsNotEmpty, UnsafeBytesToStr};
use super::{CharEscapeDebugContinue, CharEscapeDefault, CharEscapeUnicode};
#[inline]
fn count(self) -> usize {
- // length in `char` is equal to the number of non-continuation bytes
- self.iter.filter(|&&byte| !utf8_is_cont_byte(byte)).count()
+ super::count::count_chars(self.as_str())
}
#[inline]
#![stable(feature = "rust1", since = "1.0.0")]
mod converts;
+mod count;
mod error;
mod iter;
mod traits;
use iter::SplitInternal;
use iter::{MatchesInternal, SplitNInternal};
-use validations::truncate_to_char_boundary;
-
#[inline(never)]
#[cold]
#[track_caller]
fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! {
const MAX_DISPLAY_LENGTH: usize = 256;
- let (truncated, s_trunc) = truncate_to_char_boundary(s, MAX_DISPLAY_LENGTH);
- let ellipsis = if truncated { "[...]" } else { "" };
+ let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
+ let s_trunc = &s[..trunc_len];
+ let ellipsis = if trunc_len < s.len() { "[...]" } else { "" };
// 1. out of bounds
if begin > s.len() || end > s.len() {
// 3. character boundary
let index = if !s.is_char_boundary(begin) { begin } else { end };
// find the character
- let mut char_start = index;
- while !s.is_char_boundary(char_start) {
- char_start -= 1;
- }
+ let char_start = s.floor_char_boundary(index);
// `char_start` must be less than len and a char boundary
let ch = s[char_start..].chars().next().unwrap();
let char_range = char_start..char_start + ch.len_utf8();
// code on higher opt-levels. See PR #84751 for more details.
None => index == self.len(),
- // This is bit magic equivalent to: b < 128 || b >= 192
- Some(&b) => (b as i8) >= -0x40,
+ Some(&b) => b.is_utf8_char_boundary(),
+ }
+ }
+
+ /// Finds the closest `x` not exceeding `index` where `is_char_boundary(x)` is `true`.
+ ///
+ /// This method can help you truncate a string so that it's still valid UTF-8, but doesn't
+ /// exceed a given number of bytes. Note that this is done purely at the character level
+ /// and can still visually split graphemes, even though the underlying characters aren't
+ /// split. For example, the emoji 🧑🔬 (scientist) could be split so that the string only
+ /// includes 🧑 (person) instead.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(round_char_boundary)]
+ /// let s = "❤️🧡💛💚💙💜";
+ /// assert_eq!(s.len(), 26);
+ /// assert!(!s.is_char_boundary(13));
+ ///
+ /// let closest = s.floor_char_boundary(13);
+ /// assert_eq!(closest, 10);
+ /// assert_eq!(&s[..closest], "❤️🧡");
+ /// ```
+ #[unstable(feature = "round_char_boundary", issue = "93743")]
+ #[inline]
+ pub fn floor_char_boundary(&self, index: usize) -> usize {
+ if index >= self.len() {
+ self.len()
+ } else {
+ let lower_bound = index.saturating_sub(3);
+ let new_index = self.as_bytes()[lower_bound..=index]
+ .iter()
+ .rposition(|b| b.is_utf8_char_boundary());
+
+ // SAFETY: we know that the character boundary will be within four bytes
+ unsafe { lower_bound + new_index.unwrap_unchecked() }
+ }
+ }
+
+ /// Finds the closest `x` not below `index` where `is_char_boundary(x)` is `true`.
+ ///
+ /// This method is the natural complement to [`floor_char_boundary`]. See that method
+ /// for more details.
+ ///
+ /// [`floor_char_boundary`]: str::floor_char_boundary
+ ///
+ /// # Panics
+ ///
+ /// Panics if `index > self.len()`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(round_char_boundary)]
+ /// let s = "❤️🧡💛💚💙💜";
+ /// assert_eq!(s.len(), 26);
+ /// assert!(!s.is_char_boundary(13));
+ ///
+ /// let closest = s.ceil_char_boundary(13);
+ /// assert_eq!(closest, 14);
+ /// assert_eq!(&s[..closest], "❤️🧡💛");
+ /// ```
+ #[unstable(feature = "round_char_boundary", issue = "93743")]
+ #[inline]
+ pub fn ceil_char_boundary(&self, index: usize) -> usize {
+ if index > self.len() {
+ slice_error_fail(self, index, index)
+ } else {
+ let upper_bound = Ord::min(index + 4, self.len());
+ self.as_bytes()[index..upper_bound]
+ .iter()
+ .position(|b| b.is_utf8_char_boundary())
+ .map_or(upper_bound, |pos| pos + index)
}
}
/// Mask of the value bits of a continuation byte.
const CONT_MASK: u8 = 0b0011_1111;
-
-// truncate `&str` to length at most equal to `max`
-// return `true` if it were truncated, and the new str.
-pub(super) fn truncate_to_char_boundary(s: &str, mut max: usize) -> (bool, &str) {
- if max >= s.len() {
- (false, s)
- } else {
- while !s.is_char_boundary(max) {
- max -= 1;
- }
- (true, &s[..max])
- }
-}
#[stable(feature = "atomic_from", since = "1.23.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<*mut T> for AtomicPtr<T> {
+ /// Converts a `*mut T` into an `AtomicPtr<T>`.
#[inline]
fn from(p: *mut T) -> Self {
Self::new(p)
#[stable(feature = "futures_api", since = "1.36.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T> const From<T> for Poll<T> {
- /// Convert to a `Ready` variant.
+ /// Moves the value into a [`Poll::Ready`] to make a `Poll<T>`.
///
/// # Example
///
}
}
}
+
+// just tests by whether or not this compiles
+fn _pending_impl_all_auto_traits<T>() {
+ use std::panic::{RefUnwindSafe, UnwindSafe};
+ fn all_auto_traits<T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe>() {}
+
+ all_auto_traits::<std::future::Pending<T>>();
+}
let _: &dyn BuildHasher<Hasher = DefaultHasher> = &RandomState::new();
}
+
+// just tests by whether or not this compiles
+fn _build_hasher_default_impl_all_auto_traits<T>() {
+ use std::panic::{RefUnwindSafe, UnwindSafe};
+ fn all_auto_traits<T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe>() {}
+
+ all_auto_traits::<std::hash::BuildHasherDefault<T>>();
+}
let b: Vec<isize> = a.iter().cloned().collect();
assert!(a == b);
}
+
+#[test]
+fn test_try_collect() {
+ use core::ops::ControlFlow::{Break, Continue};
+
+ let u = vec![Some(1), Some(2), Some(3)];
+ let v = u.into_iter().try_collect::<Vec<i32>>();
+ assert_eq!(v, Some(vec![1, 2, 3]));
+
+ let u = vec![Some(1), Some(2), None, Some(3)];
+ let mut it = u.into_iter();
+ let v = it.try_collect::<Vec<i32>>();
+ assert_eq!(v, None);
+ let v = it.try_collect::<Vec<i32>>();
+ assert_eq!(v, Some(vec![3]));
+
+ let u: Vec<Result<i32, ()>> = vec![Ok(1), Ok(2), Ok(3)];
+ let v = u.into_iter().try_collect::<Vec<i32>>();
+ assert_eq!(v, Ok(vec![1, 2, 3]));
+
+ let u = vec![Ok(1), Ok(2), Err(()), Ok(3)];
+ let v = u.into_iter().try_collect::<Vec<i32>>();
+ assert_eq!(v, Err(()));
+
+ let numbers = vec![1, 2, 3, 4, 5];
+ let all_positive = numbers
+ .iter()
+ .cloned()
+ .map(|n| if n > 0 { Some(n) } else { None })
+ .try_collect::<Vec<i32>>();
+ assert_eq!(all_positive, Some(numbers));
+
+ let numbers = vec![-2, -1, 0, 1, 2];
+ let all_positive =
+ numbers.into_iter().map(|n| if n > 0 { Some(n) } else { None }).try_collect::<Vec<i32>>();
+ assert_eq!(all_positive, None);
+
+ let u = [Continue(1), Continue(2), Break(3), Continue(4), Continue(5)];
+ let mut it = u.into_iter();
+
+ let v = it.try_collect::<Vec<_>>();
+ assert_eq!(v, Break(3));
+
+ let v = it.try_collect::<Vec<_>>();
+ assert_eq!(v, Continue(vec![4, 5]));
+}
+
+// just tests by whether or not this compiles
+fn _empty_impl_all_auto_traits<T>() {
+ use std::panic::{RefUnwindSafe, UnwindSafe};
+ fn all_auto_traits<T: Send + Sync + Unpin + UnwindSafe + RefUnwindSafe>() {}
+
+ all_auto_traits::<std::iter::Empty<T>>();
+}
#![feature(box_syntax)]
#![feature(cell_update)]
#![feature(cfg_panic)]
-#![feature(cfg_target_has_atomic)]
+#![cfg_attr(bootstrap, feature(cfg_target_has_atomic))]
#![feature(const_assume)]
#![feature(const_black_box)]
#![feature(const_bool_to_option)]
#![feature(inline_const)]
#![feature(is_sorted)]
#![feature(pattern)]
+#![feature(pin_macro)]
#![feature(sort_internals)]
#![feature(slice_take)]
#![feature(maybe_uninit_uninit_array)]
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
#![feature(iter_order_by)]
+#![feature(iterator_try_collect)]
#![feature(iterator_try_reduce)]
#![feature(const_mut_refs)]
#![feature(const_pin)]
mod option;
mod pattern;
mod pin;
+mod pin_macro;
mod ptr;
mod result;
mod simd;
--- /dev/null
+// edition:2021
+use core::{
+ marker::PhantomPinned,
+ mem::{drop as stuff, transmute},
+ pin::{pin, Pin},
+};
+
+#[test]
+fn basic() {
+ let it: Pin<&mut PhantomPinned> = pin!(PhantomPinned);
+ stuff(it);
+}
+
+#[test]
+fn extension_works_through_block() {
+ let it: Pin<&mut PhantomPinned> = { pin!(PhantomPinned) };
+ stuff(it);
+}
+
+#[test]
+fn extension_works_through_unsafe_block() {
+ // "retro-type-inference" works as well.
+ let it: Pin<&mut PhantomPinned> = unsafe { pin!(transmute(())) };
+ stuff(it);
+}
+
+#[test]
+fn unsize_coercion() {
+ let slice: Pin<&mut [PhantomPinned]> = pin!([PhantomPinned; 2]);
+ stuff(slice);
+ let dyn_obj: Pin<&mut dyn Send> = pin!([PhantomPinned; 2]);
+ stuff(dyn_obj);
+}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1
+#[cfg(target_arch = "m68k")]
+const UNWIND_DATA_REG: (i32, i32) = (0, 1); // D0, D1
+
#[cfg(any(target_arch = "mips", target_arch = "mips64"))]
const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1
panic_abort = { path = "../panic_abort" }
core = { path = "../core" }
libc = { version = "0.2.116", default-features = false, features = ['rustc-dep-of-std'] }
-compiler_builtins = { version = "0.1.67" }
+compiler_builtins = { version = "0.1.69" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
hashbrown = { version = "0.12", default-features = false, features = ['rustc-dep-of-std'] }
/// Returns the current working directory as a [`PathBuf`].
///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `getcwd` function on Unix
+/// and the `GetCurrentDirectoryW` function on Windows.
+///
/// # Errors
///
/// Returns an [`Err`] if the current working directory value is invalid.
/// Changes the current working directory to the specified path.
///
+/// # Platform-specific behavior
+///
+/// This function currently corresponds to the `chdir` function on Unix
+/// and the `SetCurrentDirectoryW` function on Windows.
+///
/// Returns an [`Err`] if the operation fails.
///
/// # Examples
#[stable(feature = "cstring_from_cow_cstr", since = "1.28.0")]
impl<'a> From<Cow<'a, CStr>> for CString {
+ /// Converts a `Cow<'a, CStr>` into a `CString`, by copying the contents if they are
+ /// borrowed.
#[inline]
fn from(s: Cow<'a, CStr>) -> Self {
s.into_owned()
#[stable(feature = "box_from_c_str", since = "1.17.0")]
impl From<&CStr> for Box<CStr> {
+ /// Converts a `&CStr` into a `Box<CStr>`,
+ /// by copying the contents into a newly allocated [`Box`].
fn from(s: &CStr) -> Box<CStr> {
let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul());
unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) }
#[stable(feature = "box_from_cow", since = "1.45.0")]
impl From<Cow<'_, CStr>> for Box<CStr> {
+ /// Converts a `Cow<'a, CStr>` into a `Box<CStr>`,
+ /// by copying the contents if they are borrowed.
#[inline]
fn from(cow: Cow<'_, CStr>) -> Box<CStr> {
match cow {
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<&CStr> for Arc<CStr> {
+ /// Converts a `&CStr` into a `Arc<CStr>`,
+ /// by copying the contents into a newly allocated [`Arc`].
#[inline]
fn from(s: &CStr) -> Arc<CStr> {
let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul());
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<&CStr> for Rc<CStr> {
+ /// Converts a `&CStr` into a `Rc<CStr>`,
+ /// by copying the contents into a newly allocated [`Rc`].
#[inline]
fn from(s: &CStr) -> Rc<CStr> {
let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul());
impl From<NulError> for io::Error {
/// Converts a [`NulError`] into a [`io::Error`].
fn from(_: NulError) -> io::Error {
- io::Error::new_const(io::ErrorKind::InvalidInput, &"data provided contains a nul byte")
+ io::const_io_error!(io::ErrorKind::InvalidInput, "data provided contains a nul byte")
}
}
/// assert!(cstr.is_err());
/// ```
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
- pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> {
+ pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&Self, FromBytesWithNulError> {
let nul_pos = memchr::memchr(0, bytes);
- if let Some(nul_pos) = nul_pos {
- if nul_pos + 1 != bytes.len() {
- return Err(FromBytesWithNulError::interior_nul(nul_pos));
+ match nul_pos {
+ Some(nul_pos) if nul_pos + 1 == bytes.len() => {
+ // SAFETY: We know there is only one nul byte, at the end
+ // of the byte slice.
+ Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) })
}
- Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) })
- } else {
- Err(FromBytesWithNulError::not_nul_terminated())
+ Some(nul_pos) => Err(FromBytesWithNulError::interior_nul(nul_pos)),
+ None => Err(FromBytesWithNulError::not_nul_terminated()),
}
}
#[stable(feature = "cstring_asref", since = "1.7.0")]
impl From<&CStr> for CString {
+ /// Copies the contents of the `&CStr` into a newly allocated `CString`.
fn from(s: &CStr) -> CString {
s.to_owned()
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized + AsRef<OsStr>> From<&T> for OsString {
+ /// Copies any value implementing <code>[AsRef]<[OsStr]></code>
+ /// into a newly allocated [`OsString`].
fn from(s: &T) -> OsString {
s.as_ref().to_os_string()
}
#[stable(feature = "box_from_os_str", since = "1.17.0")]
impl From<&OsStr> for Box<OsStr> {
+ /// Copies the string into a newly allocated <code>[Box]<[OsStr]></code>.
#[inline]
fn from(s: &OsStr) -> Box<OsStr> {
let rw = Box::into_raw(s.inner.into_box()) as *mut OsStr;
#[stable(feature = "box_from_cow", since = "1.45.0")]
impl From<Cow<'_, OsStr>> for Box<OsStr> {
+ /// Converts a `Cow<'a, OsStr>` into a <code>[Box]<[OsStr]></code>,
+ /// by copying the contents if they are borrowed.
#[inline]
fn from(cow: Cow<'_, OsStr>) -> Box<OsStr> {
match cow {
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<&OsStr> for Arc<OsStr> {
+ /// Copies the string into a newly allocated <code>[Arc]<[OsStr]></code>.
#[inline]
fn from(s: &OsStr) -> Arc<OsStr> {
let arc = s.inner.into_arc();
#[stable(feature = "shared_from_slice2", since = "1.24.0")]
impl From<&OsStr> for Rc<OsStr> {
+ /// Copies the string into a newly allocated <code>[Rc]<[OsStr]></code>.
#[inline]
fn from(s: &OsStr) -> Rc<OsStr> {
let rc = s.inner.into_rc();
#[stable(feature = "cow_from_osstr", since = "1.28.0")]
impl<'a> From<OsString> for Cow<'a, OsStr> {
+ /// Moves the string into a [`Cow::Owned`].
#[inline]
fn from(s: OsString) -> Cow<'a, OsStr> {
Cow::Owned(s)
#[stable(feature = "cow_from_osstr", since = "1.28.0")]
impl<'a> From<&'a OsStr> for Cow<'a, OsStr> {
+ /// Converts the string reference into a [`Cow::Borrowed`].
#[inline]
fn from(s: &'a OsStr) -> Cow<'a, OsStr> {
Cow::Borrowed(s)
#[stable(feature = "cow_from_osstr", since = "1.28.0")]
impl<'a> From<&'a OsString> for Cow<'a, OsStr> {
+ /// Converts the string reference into a [`Cow::Borrowed`].
#[inline]
fn from(s: &'a OsString) -> Cow<'a, OsStr> {
Cow::Borrowed(s.as_os_str())
#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")]
impl<'a> From<Cow<'a, OsStr>> for OsString {
+ /// Converts a `Cow<'a, OsStr>` into an [`OsString`],
+ /// by copying the contents if they are borrowed.
#[inline]
fn from(s: Cow<'a, OsStr>) -> Self {
s.into_owned()
///
/// This function currently corresponds to `openat`, `fdopendir`, `unlinkat` and `lstat` functions
/// on Unix (except for macOS before version 10.10 and REDOX) and the `CreateFileW`,
-/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtOpenFile` functions on
+/// `GetFileInformationByHandleEx`, `SetFileInformationByHandle`, and `NtCreateFile` functions on
/// Windows. Note that, this [may change in the future][changes].
///
/// [changes]: io#platform-specific-behavior
match path.parent() {
Some(p) => self.create_dir_all(p)?,
None => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"failed to create whole tree",
+ "failed to create whole tree",
));
}
}
/// This function will traverse symbolic links to query information about the
/// destination file. In case of broken symbolic links this will return `Ok(false)`.
///
-/// As opposed to the `exists()` method, this one doesn't silently ignore errors
+/// As opposed to the [`Path::exists`] method, this one doesn't silently ignore errors
/// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission
/// denied on some of the parent directories.)
///
/// assert!(!fs::try_exists("does_not_exist.txt").expect("Can't check existence of file does_not_exist.txt"));
/// assert!(fs::try_exists("/root/secret_file.txt").is_err());
/// ```
+///
+/// [`Path::exists`]: crate::path::Path::exists
// FIXME: stabilization should modify documentation of `exists()` to recommend this method
// instead.
#[unstable(feature = "path_try_exists", issue = "83186")]
let mut bytes = Vec::new();
self.read_to_end(&mut bytes)?;
let string = crate::str::from_utf8(&bytes).map_err(|_| {
- io::Error::new_const(
+ io::const_io_error!(
io::ErrorKind::InvalidData,
- &"stream did not contain valid UTF-8",
+ "stream did not contain valid UTF-8",
)
})?;
*buf += string;
use crate::error;
use crate::fmt;
use crate::io::{
- self, Error, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
+ self, ErrorKind, IntoInnerError, IoSlice, Seek, SeekFrom, Write, DEFAULT_BUF_SIZE,
};
use crate::mem;
use crate::ptr;
match r {
Ok(0) => {
- return Err(Error::new_const(
+ return Err(io::const_io_error!(
ErrorKind::WriteZero,
- &"failed to write the buffered data",
+ "failed to write the buffered data",
));
}
Ok(n) => guard.consume(n),
use crate::io::prelude::*;
use crate::cmp;
-use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom};
use core::convert::TryInto;
self.pos = n;
Ok(self.pos)
}
- None => Err(Error::new_const(
+ None => Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"invalid seek to a negative or overflowing position",
+ "invalid seek to a negative or overflowing position",
)),
}
}
// Resizing write implementation
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8>, buf: &[u8]) -> io::Result<usize> {
let pos: usize = (*pos_mut).try_into().map_err(|_| {
- Error::new_const(
+ io::const_io_error!(
ErrorKind::InvalidInput,
- &"cursor position exceeds maximum possible vector length",
+ "cursor position exceeds maximum possible vector length",
)
})?;
// Make sure the internal buffer is as least as big as where we
#[cfg(test)]
mod tests;
+#[cfg(target_pointer_width = "64")]
+mod repr_bitpacked;
+#[cfg(target_pointer_width = "64")]
+use repr_bitpacked::Repr;
+
+#[cfg(not(target_pointer_width = "64"))]
+mod repr_unpacked;
+#[cfg(not(target_pointer_width = "64"))]
+use repr_unpacked::Repr;
+
use crate::convert::From;
use crate::error;
use crate::fmt;
}
}
-enum Repr {
+// Only derive debug in tests, to make sure it
+// doesn't accidentally get printed.
+#[cfg_attr(test, derive(Debug))]
+enum ErrorData<C> {
Os(i32),
Simple(ErrorKind),
- // &str is a fat pointer, but &&str is a thin pointer.
- SimpleMessage(ErrorKind, &'static &'static str),
- Custom(Box<Custom>),
+ SimpleMessage(&'static SimpleMessage),
+ Custom(C),
}
+// `#[repr(align(4))]` is probably redundant, it should have that value or
+// higher already. We include it just because repr_bitpacked.rs's encoding
+// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the
+// alignment required by the struct, only increase it).
+//
+// If we add more variants to ErrorData, this can be increased to 8, but it
+// should probably be behind `#[cfg_attr(target_pointer_width = "64", ...)]` or
+// whatever cfg we're using to enable the `repr_bitpacked` code, since only the
+// that version needs the alignment, and 8 is higher than the alignment we'll
+// have on 32 bit platforms.
+//
+// (For the sake of being explicit: the alignment requirement here only matters
+// if `error/repr_bitpacked.rs` is in use — for the unpacked repr it doesn't
+// matter at all)
+#[repr(align(4))]
#[derive(Debug)]
+pub(crate) struct SimpleMessage {
+ kind: ErrorKind,
+ message: &'static str,
+}
+
+impl SimpleMessage {
+ pub(crate) const fn new(kind: ErrorKind, message: &'static str) -> Self {
+ Self { kind, message }
+ }
+}
+
+/// Create and return an `io::Error` for a given `ErrorKind` and constant
+/// message. This doesn't allocate.
+pub(crate) macro const_io_error($kind:expr, $message:expr $(,)?) {
+ $crate::io::error::Error::from_static_message({
+ const MESSAGE_DATA: $crate::io::error::SimpleMessage =
+ $crate::io::error::SimpleMessage::new($kind, $message);
+ &MESSAGE_DATA
+ })
+}
+
+// As with `SimpleMessage`: `#[repr(align(4))]` here is just because
+// repr_bitpacked's encoding requires it. In practice it almost certainly be
+// already be this high or higher.
+#[derive(Debug)]
+#[repr(align(4))]
struct Custom {
kind: ErrorKind,
error: Box<dyn error::Error + Send + Sync>,
/// The filesystem does not support making so many hardlinks to the same file.
#[unstable(feature = "io_error_more", issue = "86442")]
TooManyLinks,
- /// Filename too long.
+ /// A filename was invalid.
///
- /// The limit might be from the underlying filesystem or API, or an administratively imposed
- /// resource limit.
+ /// This error can also cause if it exceeded the filename length limit.
#[unstable(feature = "io_error_more", issue = "86442")]
- FilenameTooLong,
+ InvalidFilename,
/// Program argument list too long.
///
/// When trying to run an external program, a system or process limit on the size of the
DirectoryNotEmpty => "directory not empty",
ExecutableFileBusy => "executable file busy",
FileTooLarge => "file too large",
- FilenameTooLong => "filename too long",
FilesystemLoop => "filesystem loop or indirection limit (e.g. symlink loop)",
FilesystemQuotaExceeded => "filesystem quota exceeded",
HostUnreachable => "host unreachable",
Interrupted => "operation interrupted",
InvalidData => "invalid data",
+ InvalidFilename => "invalid filename",
InvalidInput => "invalid input parameter",
IsADirectory => "is a directory",
NetworkDown => "network down",
/// ```
#[inline]
fn from(kind: ErrorKind) -> Error {
- Error { repr: Repr::Simple(kind) }
+ Error { repr: Repr::new_simple(kind) }
}
}
}
fn _new(kind: ErrorKind, error: Box<dyn error::Error + Send + Sync>) -> Error {
- Error { repr: Repr::Custom(Box::new(Custom { kind, error })) }
+ Error { repr: Repr::new_custom(Box::new(Custom { kind, error })) }
}
- /// Creates a new I/O error from a known kind of error as well as a
- /// constant message.
+ /// Creates a new I/O error from a known kind of error as well as a constant
+ /// message.
///
/// This function does not allocate.
///
- /// This function should maybe change to
- /// `new_const<const MSG: &'static str>(kind: ErrorKind)`
- /// in the future, when const generics allow that.
+ /// You should not use this directly, and instead use the `const_io_error!`
+ /// macro: `io::const_io_error!(ErrorKind::Something, "some_message")`.
+ ///
+ /// This function should maybe change to `from_static_message<const MSG: &'static
+ /// str>(kind: ErrorKind)` in the future, when const generics allow that.
#[inline]
- pub(crate) const fn new_const(kind: ErrorKind, message: &'static &'static str) -> Error {
- Self { repr: Repr::SimpleMessage(kind, message) }
+ pub(crate) const fn from_static_message(msg: &'static SimpleMessage) -> Error {
+ Self { repr: Repr::new_simple_message(msg) }
}
/// Returns an error representing the last OS error which occurred.
#[must_use]
#[inline]
pub fn from_raw_os_error(code: i32) -> Error {
- Error { repr: Repr::Os(code) }
+ Error { repr: Repr::new_os(code) }
}
/// Returns the OS error that this error represents (if any).
#[must_use]
#[inline]
pub fn raw_os_error(&self) -> Option<i32> {
- match self.repr {
- Repr::Os(i) => Some(i),
- Repr::Custom(..) => None,
- Repr::Simple(..) => None,
- Repr::SimpleMessage(..) => None,
+ match self.repr.data() {
+ ErrorData::Os(i) => Some(i),
+ ErrorData::Custom(..) => None,
+ ErrorData::Simple(..) => None,
+ ErrorData::SimpleMessage(..) => None,
}
}
#[must_use]
#[inline]
pub fn get_ref(&self) -> Option<&(dyn error::Error + Send + Sync + 'static)> {
- match self.repr {
- Repr::Os(..) => None,
- Repr::Simple(..) => None,
- Repr::SimpleMessage(..) => None,
- Repr::Custom(ref c) => Some(&*c.error),
+ match self.repr.data() {
+ ErrorData::Os(..) => None,
+ ErrorData::Simple(..) => None,
+ ErrorData::SimpleMessage(..) => None,
+ ErrorData::Custom(c) => Some(&*c.error),
}
}
#[must_use]
#[inline]
pub fn get_mut(&mut self) -> Option<&mut (dyn error::Error + Send + Sync + 'static)> {
- match self.repr {
- Repr::Os(..) => None,
- Repr::Simple(..) => None,
- Repr::SimpleMessage(..) => None,
- Repr::Custom(ref mut c) => Some(&mut *c.error),
+ match self.repr.data_mut() {
+ ErrorData::Os(..) => None,
+ ErrorData::Simple(..) => None,
+ ErrorData::SimpleMessage(..) => None,
+ ErrorData::Custom(c) => Some(&mut *c.error),
}
}
#[must_use = "`self` will be dropped if the result is not used"]
#[inline]
pub fn into_inner(self) -> Option<Box<dyn error::Error + Send + Sync>> {
- match self.repr {
- Repr::Os(..) => None,
- Repr::Simple(..) => None,
- Repr::SimpleMessage(..) => None,
- Repr::Custom(c) => Some(c.error),
+ match self.repr.into_data() {
+ ErrorData::Os(..) => None,
+ ErrorData::Simple(..) => None,
+ ErrorData::SimpleMessage(..) => None,
+ ErrorData::Custom(c) => Some(c.error),
}
}
#[must_use]
#[inline]
pub fn kind(&self) -> ErrorKind {
- match self.repr {
- Repr::Os(code) => sys::decode_error_kind(code),
- Repr::Custom(ref c) => c.kind,
- Repr::Simple(kind) => kind,
- Repr::SimpleMessage(kind, _) => kind,
+ match self.repr.data() {
+ ErrorData::Os(code) => sys::decode_error_kind(code),
+ ErrorData::Custom(c) => c.kind,
+ ErrorData::Simple(kind) => kind,
+ ErrorData::SimpleMessage(m) => m.kind,
}
}
}
impl fmt::Debug for Repr {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self {
- Repr::Os(code) => fmt
+ match self.data() {
+ ErrorData::Os(code) => fmt
.debug_struct("Os")
.field("code", &code)
.field("kind", &sys::decode_error_kind(code))
.field("message", &sys::os::error_string(code))
.finish(),
- Repr::Custom(ref c) => fmt::Debug::fmt(&c, fmt),
- Repr::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
- Repr::SimpleMessage(kind, &message) => {
- fmt.debug_struct("Error").field("kind", &kind).field("message", &message).finish()
- }
+ ErrorData::Custom(c) => fmt::Debug::fmt(&c, fmt),
+ ErrorData::Simple(kind) => fmt.debug_tuple("Kind").field(&kind).finish(),
+ ErrorData::SimpleMessage(msg) => fmt
+ .debug_struct("Error")
+ .field("kind", &msg.kind)
+ .field("message", &msg.message)
+ .finish(),
}
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self.repr {
- Repr::Os(code) => {
+ match self.repr.data() {
+ ErrorData::Os(code) => {
let detail = sys::os::error_string(code);
write!(fmt, "{} (os error {})", detail, code)
}
- Repr::Custom(ref c) => c.error.fmt(fmt),
- Repr::Simple(kind) => write!(fmt, "{}", kind.as_str()),
- Repr::SimpleMessage(_, &msg) => msg.fmt(fmt),
+ ErrorData::Custom(ref c) => c.error.fmt(fmt),
+ ErrorData::Simple(kind) => write!(fmt, "{}", kind.as_str()),
+ ErrorData::SimpleMessage(msg) => msg.message.fmt(fmt),
}
}
}
impl error::Error for Error {
#[allow(deprecated, deprecated_in_future)]
fn description(&self) -> &str {
- match self.repr {
- Repr::Os(..) | Repr::Simple(..) => self.kind().as_str(),
- Repr::SimpleMessage(_, &msg) => msg,
- Repr::Custom(ref c) => c.error.description(),
+ match self.repr.data() {
+ ErrorData::Os(..) | ErrorData::Simple(..) => self.kind().as_str(),
+ ErrorData::SimpleMessage(msg) => msg.message,
+ ErrorData::Custom(c) => c.error.description(),
}
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn error::Error> {
- match self.repr {
- Repr::Os(..) => None,
- Repr::Simple(..) => None,
- Repr::SimpleMessage(..) => None,
- Repr::Custom(ref c) => c.error.cause(),
+ match self.repr.data() {
+ ErrorData::Os(..) => None,
+ ErrorData::Simple(..) => None,
+ ErrorData::SimpleMessage(..) => None,
+ ErrorData::Custom(c) => c.error.cause(),
}
}
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match self.repr {
- Repr::Os(..) => None,
- Repr::Simple(..) => None,
- Repr::SimpleMessage(..) => None,
- Repr::Custom(ref c) => c.error.source(),
+ match self.repr.data() {
+ ErrorData::Os(..) => None,
+ ErrorData::Simple(..) => None,
+ ErrorData::SimpleMessage(..) => None,
+ ErrorData::Custom(c) => c.error.source(),
}
}
}
--- /dev/null
+//! This is a densely packed error representation which is used on targets with
+//! 64-bit pointers.
+//!
+//! (Note that `bitpacked` vs `unpacked` here has no relationship to
+//! `#[repr(packed)]`, it just refers to attempting to use any available bits in
+//! a more clever manner than `rustc`'s default layout algorithm would).
+//!
+//! Conceptually, it stores the same data as the "unpacked" equivalent we use on
+//! other targets. Specifically, you can imagine it as an optimized version of
+//! the following enum (which is roughly equivalent to what's stored by
+//! `repr_unpacked::Repr`, e.g. `super::ErrorData<Box<Custom>>`):
+//!
+//! ```ignore (exposition-only)
+//! enum ErrorData {
+//! Os(i32),
+//! Simple(ErrorKind),
+//! SimpleMessage(&'static SimpleMessage),
+//! Custom(Box<Custom>),
+//! }
+//! ```
+//!
+//! However, it packs this data into a 64bit non-zero value.
+//!
+//! This optimization not only allows `io::Error` to occupy a single pointer,
+//! but improves `io::Result` as well, especially for situations like
+//! `io::Result<()>` (which is now 64 bits) or `io::Result<u64>` (which is now
+//! 128 bits), which are quite common.
+//!
+//! # Layout
+//! Tagged values are 64 bits, with the 2 least significant bits used for the
+//! tag. This means there are there are 4 "variants":
+//!
+//! - **Tag 0b00**: The first variant is equivalent to
+//! `ErrorData::SimpleMessage`, and holds a `&'static SimpleMessage` directly.
+//!
+//! `SimpleMessage` has an alignment >= 4 (which is requested with
+//! `#[repr(align)]` and checked statically at the bottom of this file), which
+//! means every `&'static SimpleMessage` should have the both tag bits as 0,
+//! meaning its tagged and untagged representation are equivalent.
+//!
+//! This means we can skip tagging it, which is necessary as this variant can
+//! be constructed from a `const fn`, which probably cannot tag pointers (or
+//! at least it would be difficult).
+//!
+//! - **Tag 0b01**: The other pointer variant holds the data for
+//! `ErrorData::Custom` and the remaining 62 bits are used to store a
+//! `Box<Custom>`. `Custom` also has alignment >= 4, so the bottom two bits
+//! are free to use for the tag.
+//!
+//! The only important thing to note is that `ptr::wrapping_add` and
+//! `ptr::wrapping_sub` are used to tag the pointer, rather than bitwise
+//! operations. This should preserve the pointer's provenance, which would
+//! otherwise be lost.
+//!
+//! - **Tag 0b10**: Holds the data for `ErrorData::Os(i32)`. We store the `i32`
+//! in the pointer's most significant 32 bits, and don't use the bits `2..32`
+//! for anything. Using the top 32 bits is just to let us easily recover the
+//! `i32` code with the correct sign.
+//!
+//! - **Tag 0b11**: Holds the data for `ErrorData::Simple(ErrorKind)`. This
+//! stores the `ErrorKind` in the top 32 bits as well, although it doesn't
+//! occupy nearly that many. Most of the bits are unused here, but it's not
+//! like we need them for anything else yet.
+//!
+//! # Use of `NonNull<()>`
+//!
+//! Everything is stored in a `NonNull<()>`, which is odd, but actually serves a
+//! purpose.
+//!
+//! Conceptually you might think of this more like:
+//!
+//! ```ignore (exposition-only)
+//! union Repr {
+//! // holds integer (Simple/Os) variants, and
+//! // provides access to the tag bits.
+//! bits: NonZeroU64,
+//! // Tag is 0, so this is stored untagged.
+//! msg: &'static SimpleMessage,
+//! // Tagged (offset) `Box<Custom>` pointer.
+//! tagged_custom: NonNull<()>,
+//! }
+//! ```
+//!
+//! But there are a few problems with this:
+//!
+//! 1. Union access is equivalent to a transmute, so this representation would
+//! require we transmute between integers and pointers in at least one
+//! direction, which may be UB (and even if not, it is likely harder for a
+//! compiler to reason about than explicit ptr->int operations).
+//!
+//! 2. Even if all fields of a union have a niche, the union itself doesn't,
+//! although this may change in the future. This would make things like
+//! `io::Result<()>` and `io::Result<usize>` larger, which defeats part of
+//! the motivation of this bitpacking.
+//!
+//! Storing everything in a `NonZeroUsize` (or some other integer) would be a
+//! bit more traditional for pointer tagging, but it would lose provenance
+//! information, couldn't be constructed from a `const fn`, and would probably
+//! run into other issues as well.
+//!
+//! The `NonNull<()>` seems like the only alternative, even if it's fairly odd
+//! to use a pointer type to store something that may hold an integer, some of
+//! the time.
+
+use super::{Custom, ErrorData, ErrorKind, SimpleMessage};
+use alloc::boxed::Box;
+use core::mem::{align_of, size_of};
+use core::ptr::NonNull;
+
+// The 2 least-significant bits are used as tag.
+const TAG_MASK: usize = 0b11;
+const TAG_SIMPLE_MESSAGE: usize = 0b00;
+const TAG_CUSTOM: usize = 0b01;
+const TAG_OS: usize = 0b10;
+const TAG_SIMPLE: usize = 0b11;
+
+#[repr(transparent)]
+pub(super) struct Repr(NonNull<()>);
+
+// All the types `Repr` stores internally are Send + Sync, and so is it.
+unsafe impl Send for Repr {}
+unsafe impl Sync for Repr {}
+
+impl Repr {
+ pub(super) fn new_custom(b: Box<Custom>) -> Self {
+ let p = Box::into_raw(b).cast::<u8>();
+ // Should only be possible if an allocator handed out a pointer with
+ // wrong alignment.
+ debug_assert_eq!((p as usize & TAG_MASK), 0);
+ // Note: We know `TAG_CUSTOM <= size_of::<Custom>()` (static_assert at
+ // end of file), and both the start and end of the expression must be
+ // valid without address space wraparound due to `Box`'s semantics.
+ //
+ // This means it would be correct to implement this using `ptr::add`
+ // (rather than `ptr::wrapping_add`), but it's unclear this would give
+ // any benefit, so we just use `wrapping_add` instead.
+ let tagged = p.wrapping_add(TAG_CUSTOM).cast::<()>();
+ // Safety: `TAG_CUSTOM + p` is the same as `TAG_CUSTOM | p`,
+ // because `p`'s alignment means it isn't allowed to have any of the
+ // `TAG_BITS` set (you can verify that addition and bitwise-or are the
+ // same when the operands have no bits in common using a truth table).
+ //
+ // Then, `TAG_CUSTOM | p` is not zero, as that would require
+ // `TAG_CUSTOM` and `p` both be zero, and neither is (as `p` came from a
+ // box, and `TAG_CUSTOM` just... isn't zero -- it's `0b01`). Therefore,
+ // `TAG_CUSTOM + p` isn't zero and so `tagged` can't be, and the
+ // `new_unchecked` is safe.
+ let res = Self(unsafe { NonNull::new_unchecked(tagged) });
+ // quickly smoke-check we encoded the right thing (This generally will
+ // only run in libstd's tests, unless the user uses -Zbuild-std)
+ debug_assert!(matches!(res.data(), ErrorData::Custom(_)), "repr(custom) encoding failed");
+ res
+ }
+
+ #[inline]
+ pub(super) fn new_os(code: i32) -> Self {
+ let utagged = ((code as usize) << 32) | TAG_OS;
+ // Safety: `TAG_OS` is not zero, so the result of the `|` is not 0.
+ let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) });
+ // quickly smoke-check we encoded the right thing (This generally will
+ // only run in libstd's tests, unless the user uses -Zbuild-std)
+ debug_assert!(
+ matches!(res.data(), ErrorData::Os(c) if c == code),
+ "repr(os) encoding failed for {}",
+ code,
+ );
+ res
+ }
+
+ #[inline]
+ pub(super) fn new_simple(kind: ErrorKind) -> Self {
+ let utagged = ((kind as usize) << 32) | TAG_SIMPLE;
+ // Safety: `TAG_SIMPLE` is not zero, so the result of the `|` is not 0.
+ let res = Self(unsafe { NonNull::new_unchecked(utagged as *mut ()) });
+ // quickly smoke-check we encoded the right thing (This generally will
+ // only run in libstd's tests, unless the user uses -Zbuild-std)
+ debug_assert!(
+ matches!(res.data(), ErrorData::Simple(k) if k == kind),
+ "repr(simple) encoding failed {:?}",
+ kind,
+ );
+ res
+ }
+
+ #[inline]
+ pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
+ // Safety: References are never null.
+ Self(unsafe { NonNull::new_unchecked(m as *const _ as *mut ()) })
+ }
+
+ #[inline]
+ pub(super) fn data(&self) -> ErrorData<&Custom> {
+ // Safety: We're a Repr, decode_repr is fine.
+ unsafe { decode_repr(self.0, |c| &*c) }
+ }
+
+ #[inline]
+ pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
+ // Safety: We're a Repr, decode_repr is fine.
+ unsafe { decode_repr(self.0, |c| &mut *c) }
+ }
+
+ #[inline]
+ pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
+ let this = core::mem::ManuallyDrop::new(self);
+ // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
+ // safe because we prevent double-drop using `ManuallyDrop`.
+ unsafe { decode_repr(this.0, |p| Box::from_raw(p)) }
+ }
+}
+
+impl Drop for Repr {
+ #[inline]
+ fn drop(&mut self) {
+ // Safety: We're a Repr, decode_repr is fine. The `Box::from_raw` is
+ // safe because we're being dropped.
+ unsafe {
+ let _ = decode_repr(self.0, |p| Box::<Custom>::from_raw(p));
+ }
+ }
+}
+
+// Shared helper to decode a `Repr`'s internal pointer into an ErrorData.
+//
+// Safety: `ptr`'s bits should be encoded as described in the document at the
+// top (it should `some_repr.0`)
+#[inline]
+unsafe fn decode_repr<C, F>(ptr: NonNull<()>, make_custom: F) -> ErrorData<C>
+where
+ F: FnOnce(*mut Custom) -> C,
+{
+ let bits = ptr.as_ptr() as usize;
+ match bits & TAG_MASK {
+ TAG_OS => {
+ let code = ((bits as i64) >> 32) as i32;
+ ErrorData::Os(code)
+ }
+ TAG_SIMPLE => {
+ let kind_bits = (bits >> 32) as u32;
+ let kind = kind_from_prim(kind_bits).unwrap_or_else(|| {
+ debug_assert!(false, "Invalid io::error::Repr bits: `Repr({:#018x})`", bits);
+ // This means the `ptr` passed in was not valid, which violates
+ // the unsafe contract of `decode_repr`.
+ //
+ // Using this rather than unwrap meaningfully improves the code
+ // for callers which only care about one variant (usually
+ // `Custom`)
+ core::hint::unreachable_unchecked();
+ });
+ ErrorData::Simple(kind)
+ }
+ TAG_SIMPLE_MESSAGE => ErrorData::SimpleMessage(&*ptr.cast::<SimpleMessage>().as_ptr()),
+ TAG_CUSTOM => {
+ // It would be correct for us to use `ptr::sub` here (see the
+ // comment above the `wrapping_add` call in `new_custom` for why),
+ // but it isn't clear that it makes a difference, so we don't.
+ let custom = ptr.as_ptr().cast::<u8>().wrapping_sub(TAG_CUSTOM).cast::<Custom>();
+ ErrorData::Custom(make_custom(custom))
+ }
+ _ => {
+ // Can't happen, and compiler can tell
+ unreachable!();
+ }
+ }
+}
+
+// This compiles to the same code as the check+transmute, but doesn't require
+// unsafe, or to hard-code max ErrorKind or its size in a way the compiler
+// couldn't verify.
+#[inline]
+fn kind_from_prim(ek: u32) -> Option<ErrorKind> {
+ macro_rules! from_prim {
+ ($prim:expr => $Enum:ident { $($Variant:ident),* $(,)? }) => {{
+ // Force a compile error if the list gets out of date.
+ const _: fn(e: $Enum) = |e: $Enum| match e {
+ $($Enum::$Variant => ()),*
+ };
+ match $prim {
+ $(v if v == ($Enum::$Variant as _) => Some($Enum::$Variant),)*
+ _ => None,
+ }
+ }}
+ }
+ from_prim!(ek => ErrorKind {
+ NotFound,
+ PermissionDenied,
+ ConnectionRefused,
+ ConnectionReset,
+ HostUnreachable,
+ NetworkUnreachable,
+ ConnectionAborted,
+ NotConnected,
+ AddrInUse,
+ AddrNotAvailable,
+ NetworkDown,
+ BrokenPipe,
+ AlreadyExists,
+ WouldBlock,
+ NotADirectory,
+ IsADirectory,
+ DirectoryNotEmpty,
+ ReadOnlyFilesystem,
+ FilesystemLoop,
+ StaleNetworkFileHandle,
+ InvalidInput,
+ InvalidData,
+ TimedOut,
+ WriteZero,
+ StorageFull,
+ NotSeekable,
+ FilesystemQuotaExceeded,
+ FileTooLarge,
+ ResourceBusy,
+ ExecutableFileBusy,
+ Deadlock,
+ CrossesDevices,
+ TooManyLinks,
+ InvalidFilename,
+ ArgumentListTooLong,
+ Interrupted,
+ Other,
+ UnexpectedEof,
+ Unsupported,
+ OutOfMemory,
+ Uncategorized,
+ })
+}
+
+// Some static checking to alert us if a change breaks any of the assumptions
+// that our encoding relies on for correctness and soundness. (Some of these are
+// a bit overly thorough/cautious, admittedly)
+//
+// If any of these are hit on a platform that libstd supports, we should likely
+// just use `repr_unpacked.rs` there instead (unless the fix is easy).
+macro_rules! static_assert {
+ ($condition:expr) => {
+ const _: () = assert!($condition);
+ };
+ (@usize_eq: $lhs:expr, $rhs:expr) => {
+ const _: [(); $lhs] = [(); $rhs];
+ };
+}
+
+// The bitpacking we use requires pointers be exactly 64 bits.
+static_assert!(@usize_eq: size_of::<NonNull<()>>(), 8);
+
+// We also require pointers and usize be the same size.
+static_assert!(@usize_eq: size_of::<NonNull<()>>(), size_of::<usize>());
+
+// `Custom` and `SimpleMessage` need to be thin pointers.
+static_assert!(@usize_eq: size_of::<&'static SimpleMessage>(), 8);
+static_assert!(@usize_eq: size_of::<Box<Custom>>(), 8);
+
+static_assert!((TAG_MASK + 1).is_power_of_two());
+// And they must have sufficient alignment.
+static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
+static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
+
+static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE_MESSAGE), TAG_SIMPLE_MESSAGE);
+static_assert!(@usize_eq: (TAG_MASK & TAG_CUSTOM), TAG_CUSTOM);
+static_assert!(@usize_eq: (TAG_MASK & TAG_OS), TAG_OS);
+static_assert!(@usize_eq: (TAG_MASK & TAG_SIMPLE), TAG_SIMPLE);
+
+// This is obviously true (`TAG_CUSTOM` is `0b01`), but in `Repr::new_custom` we
+// offset a pointer by this value, and expect it to both be within the same
+// object, and to not wrap around the address space. See the comment in that
+// function for further details.
+//
+// Actually, at the moment we use `ptr::wrapping_add`, not `ptr::add`, so this
+// check isn't needed for that one, although the assertion that we don't
+// actually wrap around in that wrapping_add does simplify the safety reasoning
+// elsewhere considerably.
+static_assert!(size_of::<Custom>() >= TAG_CUSTOM);
+
+// These two store a payload which is allowed to be zero, so they must be
+// non-zero to preserve the `NonNull`'s range invariant.
+static_assert!(TAG_OS != 0);
+static_assert!(TAG_SIMPLE != 0);
+// We can't tag `SimpleMessage`s, the tag must be 0.
+static_assert!(@usize_eq: TAG_SIMPLE_MESSAGE, 0);
+
+// Check that the point of all of this still holds.
+//
+// We'd check against `io::Error`, but *technically* it's allowed to vary,
+// as it's not `#[repr(transparent)]`/`#[repr(C)]`. We could add that, but
+// the `#[repr()]` would show up in rustdoc, which might be seen as a stable
+// commitment.
+static_assert!(@usize_eq: size_of::<Repr>(), 8);
+static_assert!(@usize_eq: size_of::<Option<Repr>>(), 8);
+static_assert!(@usize_eq: size_of::<Result<(), Repr>>(), 8);
+static_assert!(@usize_eq: size_of::<Result<usize, Repr>>(), 16);
--- /dev/null
+//! This is a fairly simple unpacked error representation that's used on
+//! non-64bit targets, where the packed 64 bit representation wouldn't work, and
+//! would have no benefit.
+
+use super::{Custom, ErrorData, ErrorKind, SimpleMessage};
+use alloc::boxed::Box;
+
+type Inner = ErrorData<Box<Custom>>;
+
+pub(super) struct Repr(Inner);
+
+impl Repr {
+ pub(super) fn new_custom(b: Box<Custom>) -> Self {
+ Self(Inner::Custom(b))
+ }
+ #[inline]
+ pub(super) fn new_os(code: i32) -> Self {
+ Self(Inner::Os(code))
+ }
+ #[inline]
+ pub(super) fn new_simple(kind: ErrorKind) -> Self {
+ Self(Inner::Simple(kind))
+ }
+ #[inline]
+ pub(super) const fn new_simple_message(m: &'static SimpleMessage) -> Self {
+ Self(Inner::SimpleMessage(m))
+ }
+ #[inline]
+ pub(super) fn into_data(self) -> ErrorData<Box<Custom>> {
+ self.0
+ }
+ #[inline]
+ pub(super) fn data(&self) -> ErrorData<&Custom> {
+ match &self.0 {
+ Inner::Os(c) => ErrorData::Os(*c),
+ Inner::Simple(k) => ErrorData::Simple(*k),
+ Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m),
+ Inner::Custom(m) => ErrorData::Custom(&*m),
+ }
+ }
+ #[inline]
+ pub(super) fn data_mut(&mut self) -> ErrorData<&mut Custom> {
+ match &mut self.0 {
+ Inner::Os(c) => ErrorData::Os(*c),
+ Inner::Simple(k) => ErrorData::Simple(*k),
+ Inner::SimpleMessage(m) => ErrorData::SimpleMessage(*m),
+ Inner::Custom(m) => ErrorData::Custom(&mut *m),
+ }
+ }
+}
-use super::{Custom, Error, ErrorKind, Repr};
+use super::{const_io_error, Custom, Error, ErrorData, ErrorKind, Repr};
+use crate::assert_matches::assert_matches;
use crate::error;
use crate::fmt;
use crate::mem::size_of;
let msg = error_string(code);
let kind = decode_error_kind(code);
let err = Error {
- repr: Repr::Custom(box Custom {
+ repr: Repr::new_custom(box Custom {
kind: ErrorKind::InvalidInput,
- error: box Error { repr: super::Repr::Os(code) },
+ error: box Error { repr: super::Repr::new_os(code) },
}),
};
let expected = format!(
#[test]
fn test_const() {
- const E: Error = Error::new_const(ErrorKind::NotFound, &"hello");
+ const E: Error = const_io_error!(ErrorKind::NotFound, "hello");
assert_eq!(E.kind(), ErrorKind::NotFound);
assert_eq!(E.to_string(), "hello");
assert!(format!("{:?}", E).contains("\"hello\""));
assert!(format!("{:?}", E).contains("NotFound"));
}
+
+#[test]
+fn test_os_packing() {
+ for code in -20i32..20i32 {
+ let e = Error::from_raw_os_error(code);
+ assert_eq!(e.raw_os_error(), Some(code));
+ assert_matches!(
+ e.repr.data(),
+ ErrorData::Os(c) if c == code,
+ );
+ }
+}
+
+#[test]
+fn test_errorkind_packing() {
+ assert_eq!(Error::from(ErrorKind::NotFound).kind(), ErrorKind::NotFound);
+ assert_eq!(Error::from(ErrorKind::PermissionDenied).kind(), ErrorKind::PermissionDenied);
+ assert_eq!(Error::from(ErrorKind::Uncategorized).kind(), ErrorKind::Uncategorized);
+ // Check that the innards look like like what we want.
+ assert_matches!(
+ Error::from(ErrorKind::OutOfMemory).repr.data(),
+ ErrorData::Simple(ErrorKind::OutOfMemory),
+ );
+}
+
+#[test]
+fn test_simple_message_packing() {
+ use super::{ErrorKind::*, SimpleMessage};
+ macro_rules! check_simple_msg {
+ ($err:expr, $kind:ident, $msg:literal) => {{
+ let e = &$err;
+ // Check that the public api is right.
+ assert_eq!(e.kind(), $kind);
+ assert!(format!("{:?}", e).contains($msg));
+ // and we got what we expected
+ assert_matches!(
+ e.repr.data(),
+ ErrorData::SimpleMessage(SimpleMessage { kind: $kind, message: $msg })
+ );
+ }};
+ }
+
+ let not_static = const_io_error!(Uncategorized, "not a constant!");
+ check_simple_msg!(not_static, Uncategorized, "not a constant!");
+
+ const CONST: Error = const_io_error!(NotFound, "definitely a constant!");
+ check_simple_msg!(CONST, NotFound, "definitely a constant!");
+
+ static STATIC: Error = const_io_error!(BrokenPipe, "a constant, sort of!");
+ check_simple_msg!(STATIC, BrokenPipe, "a constant, sort of!");
+}
+
+#[derive(Debug, PartialEq)]
+struct Bojji(bool);
+impl error::Error for Bojji {}
+impl fmt::Display for Bojji {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "ah! {:?}", self)
+ }
+}
+
+#[test]
+fn test_custom_error_packing() {
+ use super::Custom;
+ let test = Error::new(ErrorKind::Uncategorized, Bojji(true));
+ assert_matches!(
+ test.repr.data(),
+ ErrorData::Custom(Custom {
+ kind: ErrorKind::Uncategorized,
+ error,
+ }) if error.downcast_ref::<Bojji>().as_deref() == Some(&Bojji(true)),
+ );
+}
use crate::cmp;
use crate::fmt;
use crate::io::{
- self, BufRead, Error, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write,
+ self, BufRead, ErrorKind, IoSlice, IoSliceMut, Read, ReadBuf, Seek, SeekFrom, Write,
};
use crate::mem;
#[inline]
fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
if buf.len() > self.len() {
- return Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer"));
+ return Err(io::const_io_error!(
+ ErrorKind::UnexpectedEof,
+ "failed to fill whole buffer"
+ ));
}
let (a, b) = self.split_at(buf.len());
if self.write(data)? == data.len() {
Ok(())
} else {
- Err(Error::new_const(ErrorKind::WriteZero, &"failed to write whole buffer"))
+ Err(io::const_io_error!(ErrorKind::WriteZero, "failed to write whole buffer"))
}
}
#[unstable(feature = "read_buf", issue = "78485")]
pub use self::readbuf::ReadBuf;
+pub(crate) use error::const_io_error;
mod buffered;
pub(crate) mod copy;
let ret = f(g.buf);
if str::from_utf8(&g.buf[g.len..]).is_err() {
ret.and_then(|_| {
- Err(Error::new_const(ErrorKind::InvalidData, &"stream did not contain valid UTF-8"))
+ Err(error::const_io_error!(
+ ErrorKind::InvalidData,
+ "stream did not contain valid UTF-8"
+ ))
})
} else {
g.len = g.buf.len();
}
}
if !buf.is_empty() {
- Err(Error::new_const(ErrorKind::UnexpectedEof, &"failed to fill whole buffer"))
+ Err(error::const_io_error!(ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
} else {
Ok(())
}
while !buf.is_empty() {
match self.write(buf) {
Ok(0) => {
- return Err(Error::new_const(
+ return Err(error::const_io_error!(
ErrorKind::WriteZero,
- &"failed to write whole buffer",
+ "failed to write whole buffer",
));
}
Ok(n) => buf = &buf[n..],
while !bufs.is_empty() {
match self.write_vectored(bufs) {
Ok(0) => {
- return Err(Error::new_const(
+ return Err(error::const_io_error!(
ErrorKind::WriteZero,
- &"failed to write whole buffer",
+ "failed to write whole buffer",
));
}
Ok(n) => IoSlice::advance_slices(&mut bufs, n),
if output.error.is_err() {
output.error
} else {
- Err(Error::new_const(ErrorKind::Uncategorized, &"formatter error"))
+ Err(error::const_io_error!(ErrorKind::Uncategorized, "formatter error"))
}
}
}
impl Read for R {
fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
- Err(io::Error::new_const(io::ErrorKind::Other, &""))
+ Err(io::const_io_error!(io::ErrorKind::Other, ""))
}
}
impl BufRead for R {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
- Err(io::Error::new_const(io::ErrorKind::Other, &""))
+ Err(io::const_io_error!(io::ErrorKind::Other, ""))
}
fn consume(&mut self, _amt: usize) {}
}
// std is implemented with unstable features, many of which are internal
// compiler details that will never be stable
// NB: the following list is sorted to minimize merge conflicts.
+#![feature(absolute_path)]
#![feature(alloc_error_handler)]
#![feature(alloc_layout_extra)]
#![feature(allocator_api)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#![feature(cfg_eval)]
-#![feature(cfg_target_has_atomic)]
+#![cfg_attr(bootstrap, feature(cfg_target_has_atomic))]
#![feature(cfg_target_thread_local)]
#![feature(char_error_internals)]
#![feature(char_internals)]
#[doc(no_inline)] // Note (#82861): required for correct documentation
pub use core::arch::*;
+ #[stable(feature = "simd_aarch64", since = "1.60.0")]
+ pub use std_detect::is_aarch64_feature_detected;
#[stable(feature = "simd_x86", since = "1.27.0")]
pub use std_detect::is_x86_feature_detected;
#[unstable(feature = "stdsimd", issue = "48556")]
pub use std_detect::{
- is_aarch64_feature_detected, is_arm_feature_detected, is_mips64_feature_detected,
- is_mips_feature_detected, is_powerpc64_feature_detected, is_powerpc_feature_detected,
- is_riscv_feature_detected,
+ is_arm_feature_detected, is_mips64_feature_detected, is_mips_feature_detected,
+ is_powerpc64_feature_detected, is_powerpc_feature_detected, is_riscv_feature_detected,
};
}
#![stable(feature = "rust1", since = "1.0.0")]
-use crate::io::{self, Error, ErrorKind};
+use crate::io::{self, ErrorKind};
#[stable(feature = "rust1", since = "1.0.0")]
pub use self::addr::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
}
}
Err(last_err.unwrap_or_else(|| {
- Error::new_const(ErrorKind::InvalidInput, &"could not resolve to any addresses")
+ io::const_io_error!(ErrorKind::InvalidInput, "could not resolve to any addresses")
}))
}
mod tests;
use crate::fmt;
-use crate::io::{self, Error, ErrorKind};
+use crate::io::{self, ErrorKind};
use crate::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs};
use crate::sys_common::net as net_imp;
use crate::sys_common::{AsInner, FromInner, IntoInner};
pub fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> io::Result<usize> {
match addr.to_socket_addrs()?.next() {
Some(addr) => self.0.send_to(buf, &addr),
- None => Err(Error::new_const(ErrorKind::InvalidInput, &"no addresses to send data to")),
+ None => {
+ Err(io::const_io_error!(ErrorKind::InvalidInput, "no addresses to send data to"))
+ }
}
}
#[cfg(target_os = "wasi")]
pub fn try_clone(&self) -> crate::io::Result<Self> {
- Err(crate::io::Error::new_const(
+ Err(crate::io::const_io_error!(
crate::io::ErrorKind::Unsupported,
- &"operation not supported on WASI yet",
+ "operation not supported on WASI yet",
))
}
}
fn as_fd(&self) -> BorrowedFd<'_>;
}
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<T: AsFd> AsFd for &T {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ T::as_fd(self)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<T: AsFd> AsFd for &mut T {
+ #[inline]
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ T::as_fd(self)
+ }
+}
+
#[unstable(feature = "io_safety", issue = "87074")]
impl AsFd for BorrowedFd<'_> {
#[inline]
}
}
if !buf.is_empty() {
- Err(io::Error::new_const(io::ErrorKind::UnexpectedEof, &"failed to fill whole buffer"))
+ Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer",))
} else {
Ok(())
}
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(0) => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::WriteZero,
- &"failed to write whole buffer",
+ "failed to write whole buffer",
));
}
Ok(n) => {
///
/// fn main() -> std::io::Result<()> {
/// let f = std::fs::File::open("/file")?;
-/// fs::fchown(f, Some(0), Some(0))?;
+/// fs::fchown(&f, Some(0), Some(0))?;
/// Ok(())
/// }
/// ```
let bytes = path.as_os_str().as_bytes();
if bytes.contains(&0) {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"paths must not contain interior null bytes",
+ "paths must not contain interior null bytes",
));
}
if bytes.len() >= addr.sun_path.len() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"path must be shorter than SUN_LEN",
+ "path must be shorter than SUN_LEN",
));
}
// SAFETY: `bytes` and `addr.sun_path` are not overlapping and
// linux returns zero bytes of address
len = sun_path_offset(&addr) as libc::socklen_t; // i.e., zero-length address
} else if addr.sun_family != libc::AF_UNIX as libc::sa_family_t {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"file descriptor did not correspond to a Unix socket",
+ "file descriptor did not correspond to a Unix socket",
));
}
addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
if namespace.len() + 1 > addr.sun_path.len() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"namespace must be shorter than SUN_LEN",
+ "namespace must be shorter than SUN_LEN",
));
}
}
}
if !buf.is_empty() {
- Err(io::Error::new_const(io::ErrorKind::UnexpectedEof, &"failed to fill whole buffer"))
+ Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
} else {
Ok(())
}
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(0) => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::WriteZero,
- &"failed to write whole buffer",
+ "failed to write whole buffer",
));
}
Ok(n) => {
a if a == wasi::ADVICE_DONTNEED.raw() => wasi::ADVICE_DONTNEED,
a if a == wasi::ADVICE_NOREUSE.raw() => wasi::ADVICE_NOREUSE,
_ => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"invalid parameter 'advice'",
+ "invalid parameter 'advice'",
));
}
};
fn osstr2str(f: &OsStr) -> io::Result<&str> {
f.to_str()
- .ok_or_else(|| io::Error::new_const(io::ErrorKind::Uncategorized, &"input must be utf-8"))
+ .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
}
fn as_handle(&self) -> BorrowedHandle<'_>;
}
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<T: AsHandle> AsHandle for &T {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ T::as_handle(self)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<T: AsHandle> AsHandle for &mut T {
+ #[inline]
+ fn as_handle(&self) -> BorrowedHandle<'_> {
+ T::as_handle(self)
+ }
+}
+
impl AsHandle for BorrowedHandle<'_> {
#[inline]
fn as_handle(&self) -> BorrowedHandle<'_> {
#[cfg(target_vendor = "uwp")]
pub(crate) fn set_no_inherit(&self) -> io::Result<()> {
- Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Unavailable on UWP"))
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "Unavailable on UWP"))
}
}
fn as_socket(&self) -> BorrowedSocket<'_>;
}
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<T: AsSocket> AsSocket for &T {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ T::as_socket(self)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<T: AsSocket> AsSocket for &mut T {
+ #[inline]
+ fn as_socket(&self) -> BorrowedSocket<'_> {
+ T::as_socket(self)
+ }
+}
+
impl AsSocket for BorrowedSocket<'_> {
#[inline]
fn as_socket(&self) -> BorrowedSocket<'_> {
use crate::sync::Arc;
use crate::ffi::{OsStr, OsString};
-
+use crate::sys;
use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR};
////////////////////////////////////////////////////////////////////////////////
#[stable(feature = "rust1", since = "1.0.0")]
pub const MAIN_SEPARATOR: char = crate::sys::path::MAIN_SEP;
+/// The primary separator of path components for the current platform.
+///
+/// For example, `/` on Unix and `\` on Windows.
+#[unstable(feature = "main_separator_str", issue = "94071")]
+pub const MAIN_SEPARATOR_STR: &str = crate::sys::path::MAIN_SEP_STR;
+
////////////////////////////////////////////////////////////////////////////////
// Misc helpers
////////////////////////////////////////////////////////////////////////////////
#[stable(feature = "path_buf_from_box", since = "1.18.0")]
impl From<Box<Path>> for PathBuf {
- /// Converts a `Box<Path>` into a `PathBuf`
+ /// Converts a <code>[Box]<[Path]></code> into a [`PathBuf`].
///
/// This conversion does not allocate or copy memory.
#[inline]
#[stable(feature = "box_from_path_buf", since = "1.20.0")]
impl From<PathBuf> for Box<Path> {
- /// Converts a `PathBuf` into a `Box<Path>`
+ /// Converts a [`PathBuf`] into a <code>[Box]<[Path]></code>.
///
/// This conversion currently should not allocate memory,
/// but this behavior is not guaranteed on all platforms or in all future versions.
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized + AsRef<OsStr>> From<&T> for PathBuf {
- /// Converts a borrowed `OsStr` to a `PathBuf`.
+ /// Converts a borrowed [`OsStr`] to a [`PathBuf`].
///
/// Allocates a [`PathBuf`] and copies the data into it.
#[inline]
/// This function will traverse symbolic links to query information about the
/// destination file. In case of broken symbolic links this will return `Ok(false)`.
///
- /// As opposed to the `exists()` method, this one doesn't silently ignore errors
+ /// As opposed to the [`exists()`] method, this one doesn't silently ignore errors
/// unrelated to the path not existing. (E.g. it will return `Err(_)` in case of permission
/// denied on some of the parent directories.)
///
/// assert!(!Path::new("does_not_exist.txt").try_exists().expect("Can't check existence of file does_not_exist.txt"));
/// assert!(Path::new("/root/secret_file.txt").try_exists().is_err());
/// ```
+ ///
+ /// [`exists()`]: Self::exists
// FIXME: stabilization should modify documentation of `exists()` to recommend this method
// instead.
#[unstable(feature = "path_try_exists", issue = "83186")]
impl Hash for Path {
fn hash<H: Hasher>(&self, h: &mut H) {
let bytes = self.as_u8_slice();
- let prefix_len = match parse_prefix(&self.inner) {
+ let (prefix_len, verbatim) = match parse_prefix(&self.inner) {
Some(prefix) => {
prefix.hash(h);
- prefix.len()
+ (prefix.len(), prefix.is_verbatim())
}
- None => 0,
+ None => (0, false),
};
let bytes = &bytes[prefix_len..];
let mut bytes_hashed = 0;
for i in 0..bytes.len() {
- if is_sep_byte(bytes[i]) {
+ let is_sep = if verbatim { is_verbatim_sep(bytes[i]) } else { is_sep_byte(bytes[i]) };
+ if is_sep {
if i > component_start {
let to_hash = &bytes[component_start..i];
h.write(to_hash);
}
// skip over separator and optionally a following CurDir item
- // since components() would normalize these away
- component_start = i + match bytes[i..] {
- [_, b'.', b'/', ..] | [_, b'.'] => 2,
- _ => 1,
- };
+ // since components() would normalize these away.
+ component_start = i + 1;
+
+ let tail = &bytes[component_start..];
+
+ if !verbatim {
+ component_start += match tail {
+ [b'.'] => 1,
+ [b'.', sep @ _, ..] if is_sep_byte(*sep) => 1,
+ _ => 0,
+ };
+ }
}
}
"prefix not found"
}
}
+
+/// Makes the path absolute without accessing the filesystem.
+///
+/// If the path is relative, the current directory is used as the base directory.
+/// All intermediate components will be resolved according to platforms-specific
+/// rules but unlike [`canonicalize`][crate::fs::canonicalize] this does not
+/// resolve symlinks and may succeed even if the path does not exist.
+///
+/// If the `path` is empty or getting the
+/// [current directory][crate::env::current_dir] fails then an error will be
+/// returned.
+///
+/// # Examples
+///
+/// ## Posix paths
+///
+/// ```
+/// #![feature(absolute_path)]
+/// # #[cfg(unix)]
+/// fn main() -> std::io::Result<()> {
+/// use std::path::{self, Path};
+///
+/// // Relative to absolute
+/// let absolute = path::absolute("foo/./bar")?;
+/// assert!(absolute.ends_with("foo/bar"));
+///
+/// // Absolute to absolute
+/// let absolute = path::absolute("/foo//test/.././bar.rs")?;
+/// assert_eq!(absolute, Path::new("/foo/test/../bar.rs"));
+/// Ok(())
+/// }
+/// # #[cfg(not(unix))]
+/// # fn main() {}
+/// ```
+///
+/// The path is resolved using [POSIX semantics][posix-semantics] except that
+/// it stops short of resolving symlinks. This means it will keep `..`
+/// components and trailing slashes.
+///
+/// ## Windows paths
+///
+/// ```
+/// #![feature(absolute_path)]
+/// # #[cfg(windows)]
+/// fn main() -> std::io::Result<()> {
+/// use std::path::{self, Path};
+///
+/// // Relative to absolute
+/// let absolute = path::absolute("foo/./bar")?;
+/// assert!(absolute.ends_with(r"foo\bar"));
+///
+/// // Absolute to absolute
+/// let absolute = path::absolute(r"C:\foo//test\..\./bar.rs")?;
+///
+/// assert_eq!(absolute, Path::new(r"C:\foo\bar.rs"));
+/// Ok(())
+/// }
+/// # #[cfg(not(windows))]
+/// # fn main() {}
+/// ```
+///
+/// For verbatim paths this will simply return the path as given. For other
+/// paths this is currently equivalent to calling [`GetFullPathNameW`][windows-path]
+/// This may change in the future.
+///
+/// [posix-semantics]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
+/// [windows-path]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
+#[unstable(feature = "absolute_path", issue = "92750")]
+pub fn absolute<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
+ let path = path.as_ref();
+ if path.as_os_str().is_empty() {
+ Err(io::const_io_error!(io::ErrorKind::InvalidInput, "cannot make an empty path absolute",))
+ } else {
+ sys::path::absolute(path)
+ }
+}
relative_from: Some("")
);
+ tc!("foo/.", "foo",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!("foo/./bar", "foo/bar",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
tc!("foo/bar", "foo",
eq: false,
starts_with: true,
ends_with: true,
relative_from: Some("")
);
+
+ tc!(r"C:\foo\.\bar.txt", r"C:\foo\bar.txt",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!(r"C:\foo\.", r"C:\foo",
+ eq: true,
+ starts_with: true,
+ ends_with: true,
+ relative_from: Some("")
+ );
+
+ tc!(r"\\?\C:\foo\.\bar.txt", r"\\?\C:\foo\bar.txt",
+ eq: false,
+ starts_with: false,
+ ends_with: false,
+ relative_from: None
+ );
}
}
ord!(Equal, "foo/bar", "foo/bar//");
}
+#[test]
+#[cfg(unix)]
+fn test_unix_absolute() {
+ use crate::path::absolute;
+
+ assert!(absolute("").is_err());
+
+ let relative = "a/b";
+ let mut expected = crate::env::current_dir().unwrap();
+ expected.push(relative);
+ assert_eq!(absolute(relative).unwrap(), expected);
+
+ // Test how components are collected.
+ assert_eq!(absolute("/a/b/c").unwrap(), Path::new("/a/b/c"));
+ assert_eq!(absolute("/a//b/c").unwrap(), Path::new("/a/b/c"));
+ assert_eq!(absolute("//a/b/c").unwrap(), Path::new("//a/b/c"));
+ assert_eq!(absolute("///a/b/c").unwrap(), Path::new("/a/b/c"));
+ assert_eq!(absolute("/a/b/c/").unwrap(), Path::new("/a/b/c/"));
+ assert_eq!(absolute("/a/./b/../c/.././..").unwrap(), Path::new("/a/b/../c/../.."));
+}
+
+#[test]
+#[cfg(windows)]
+fn test_windows_absolute() {
+ use crate::path::absolute;
+ // An empty path is an error.
+ assert!(absolute("").is_err());
+
+ let relative = r"a\b";
+ let mut expected = crate::env::current_dir().unwrap();
+ expected.push(relative);
+ assert_eq!(absolute(relative).unwrap(), expected);
+
+ macro_rules! unchanged(
+ ($path:expr) => {
+ assert_eq!(absolute($path).unwrap(), Path::new($path));
+ }
+ );
+
+ unchanged!(r"C:\path\to\file");
+ unchanged!(r"C:\path\to\file\");
+ unchanged!(r"\\server\share\to\file");
+ unchanged!(r"\\server.\share.\to\file");
+ unchanged!(r"\\.\PIPE\name");
+ unchanged!(r"\\.\C:\path\to\COM1");
+ unchanged!(r"\\?\C:\path\to\file");
+ unchanged!(r"\\?\UNC\server\share\to\file");
+ unchanged!(r"\\?\PIPE\name");
+ // Verbatim paths are always unchanged, no matter what.
+ unchanged!(r"\\?\path.\to/file..");
+
+ assert_eq!(absolute(r"C:\path..\to.\file.").unwrap(), Path::new(r"C:\path..\to\file"));
+ assert_eq!(absolute(r"C:\path\to\COM1").unwrap(), Path::new(r"\\.\COM1"));
+ assert_eq!(absolute(r"C:\path\to\COM1.txt").unwrap(), Path::new(r"\\.\COM1"));
+ assert_eq!(absolute(r"C:\path\to\COM1 .txt").unwrap(), Path::new(r"\\.\COM1"));
+ assert_eq!(absolute(r"C:\path\to\cOnOuT$").unwrap(), Path::new(r"\\.\cOnOuT$"));
+}
+
#[bench]
fn bench_path_cmp_fast_path_buf_sort(b: &mut test::Bencher) {
let prefix = "my/home";
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<ChildStdin> for Stdio {
- /// Converts a `ChildStdin` into a `Stdio`
+ /// Converts a [`ChildStdin`] into a [`Stdio`].
///
/// # Examples
///
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<ChildStdout> for Stdio {
- /// Converts a `ChildStdout` into a `Stdio`
+ /// Converts a [`ChildStdout`] into a [`Stdio`].
///
/// # Examples
///
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<ChildStderr> for Stdio {
- /// Converts a `ChildStderr` into a `Stdio`
+ /// Converts a [`ChildStderr`] into a [`Stdio`].
///
/// # Examples
///
#[stable(feature = "stdio_from", since = "1.20.0")]
impl From<fs::File> for Stdio {
- /// Converts a `File` into a `Stdio`
+ /// Converts a [`File`](fs::File) into a [`Stdio`].
///
/// # Examples
///
}
}
+#[unstable(feature = "process_exitcode_placeholder", issue = "48711")]
+impl From<u8> for ExitCode {
+ /// Construct an exit code from an arbitrary u8 value.
+ fn from(code: u8) -> Self {
+ ExitCode(imp::ExitCode::from(code))
+ }
+}
+
impl Child {
/// Forces the child process to exit. If the child has already exited, an [`InvalidInput`]
/// error is returned.
(false, _, true) => Ok(O_WRONLY | O_APPEND),
(true, _, true) => Ok(O_RDWR | O_APPEND),
(false, false, false) => {
- Err(io::Error::new_const(ErrorKind::InvalidInput, &"invalid access mode"))
+ Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid access mode"))
}
}
}
(true, false) => {}
(false, false) => {
if self.truncate || self.create || self.create_new {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"invalid creation mode",
+ "invalid creation mode",
));
}
}
(_, true) => {
if self.truncate && !self.create_new {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"invalid creation mode",
+ "invalid creation mode",
));
}
}
}
pub fn unsupported_err() -> crate::io::Error {
- crate::io::Error::new_const(
+ crate::io::const_io_error!(
crate::io::ErrorKind::Unsupported,
- &"operation not supported on HermitCore yet",
+ "operation not supported on HermitCore yet",
)
}
/// if not, starts it.
pub fn init() -> io::Result<()> {
if abi::network_init() < 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
ErrorKind::Uncategorized,
- &"Unable to initialize network interface",
+ "Unable to initialize network interface",
));
}
match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
- _ => Err(io::Error::new_const(
+ _ => Err(io::const_io_error!(
ErrorKind::Uncategorized,
- &"Unable to initiate a connection on a socket",
+ "Unable to initiate a connection on a socket",
)),
}
}
Some(duration.as_millis() as u64),
) {
Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
- _ => Err(io::Error::new_const(
+ _ => Err(io::const_io_error!(
ErrorKind::Uncategorized,
- &"Unable to initiate a connection on a socket",
+ "Unable to initiate a connection on a socket",
)),
}
}
pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
.map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"Unable to set timeout value")
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value")
})
}
*self.0.as_inner(),
duration.map(|d| d.as_millis() as u64),
)
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"Unable to set timeout value"))
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value"))
}
pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"Unable to determine timeout value")
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
})?;
Ok(duration.map(|d| Duration::from_millis(d)))
pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"Unable to determine timeout value")
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
})?;
Ok(duration.map(|d| Duration::from_millis(d)))
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
abi::tcpstream::peek(*self.0.as_inner(), buf)
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"peek failed"))
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peek failed"))
}
pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
for i in ioslice.iter_mut() {
let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"Unable to read on socket")
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to read on socket")
})?;
if ret != 0 {
for i in ioslice.iter() {
size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"Unable to write on socket")
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to write on socket")
})?;
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"peer_addr failed"))?;
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"))?;
let saddr = match ipaddr {
Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
_ => {
- return Err(io::Error::new_const(ErrorKind::Uncategorized, &"peer_addr failed"));
+ return Err(io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"));
}
};
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
- abi::tcpstream::shutdown(*self.0.as_inner(), how as i32).map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"unable to shutdown socket")
- })
+ abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to shutdown socket"))
}
pub fn duplicate(&self) -> io::Result<TcpStream> {
pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"set_nodelay failed"))
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "set_nodelay failed"))
}
pub fn nodelay(&self) -> io::Result<bool> {
abi::tcpstream::nodelay(*self.0.as_inner())
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"nodelay failed"))
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "nodelay failed"))
}
pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
abi::tcpstream::set_tll(*self.0.as_inner(), tll)
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"unable to set TTL"))
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to set TTL"))
}
pub fn ttl(&self) -> io::Result<u32> {
abi::tcpstream::get_tll(*self.0.as_inner())
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"unable to get TTL"))
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to get TTL"))
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| {
- io::Error::new_const(ErrorKind::Uncategorized, &"unable to set blocking mode")
+ io::const_io_error!(ErrorKind::Uncategorized, "unable to set blocking mode")
})
}
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
- .map_err(|_| io::Error::new_const(ErrorKind::Uncategorized, &"accept failed"))?;
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "accept failed"))?;
let saddr = match ipaddr {
Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
_ => {
- return Err(io::Error::new_const(ErrorKind::Uncategorized, &"accept failed"));
+ return Err(io::const_io_error!(ErrorKind::Uncategorized, "accept failed"));
}
};
unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
- Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stdout is not able to print"))
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print"))
} else {
Ok(len as usize)
}
unsafe { len = abi::write(1, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
- Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stdout is not able to print"))
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print"))
} else {
Ok(len as usize)
}
unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
- Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stderr is not able to print"))
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print"))
} else {
Ok(len as usize)
}
unsafe { len = abi::write(2, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
- Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Stderr is not able to print"))
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print"))
} else {
Ok(len as usize)
}
// The thread failed to start and as a result p was not consumed. Therefore, it is
// safe to reconstruct the box so that it gets deallocated.
drop(Box::from_raw(p));
- Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"Unable to create thread!"))
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!"))
} else {
Ok(Thread { tid: tid })
};
Instant { t: time }
}
- pub const fn zero() -> Instant {
- Instant { t: Timespec::zero() }
- }
-
- pub fn actually_monotonic() -> bool {
- true
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}
pub type MovableCondvar = Condvar;
impl Condvar {
+ #[inline]
pub const fn new() -> Condvar {
Condvar { waiters: SpinMutex::new(waiter_queue::WaiterQueue::new()) }
}
+ #[inline]
pub unsafe fn init(&mut self) {}
pub unsafe fn notify_one(&self) {
let insert_after = {
let mut cursor = head.last;
loop {
- if waiter.priority <= cursor.as_ref().priority {
+ if waiter.priority >= cursor.as_ref().priority {
// `cursor` and all previous waiters have the same or higher
// priority than `current_task_priority`. Insert the new
// waiter right after `cursor`.
if let Some(mut insert_after) = insert_after {
// Insert `waiter` after `insert_after`
- let insert_before = insert_after.as_ref().prev;
+ let insert_before = insert_after.as_ref().next;
waiter.prev = Some(insert_after);
insert_after.as_mut().next = Some(waiter_ptr);
waiter.next = insert_before;
if let Some(mut insert_before) = insert_before {
insert_before.as_mut().prev = Some(waiter_ptr);
+ } else {
+ head.last = waiter_ptr;
}
} else {
// Insert `waiter` to the front
match (waiter.prev, waiter.next) {
(Some(mut prev), Some(mut next)) => {
prev.as_mut().next = Some(next);
- next.as_mut().next = Some(prev);
+ next.as_mut().prev = Some(prev);
}
(None, Some(mut next)) => {
head.first = next;
- next.as_mut().next = None;
+ next.as_mut().prev = None;
}
(Some(mut prev), None) => {
prev.as_mut().next = None;
unsafe { waiter.as_ref().task != 0 }
}
+ #[inline]
pub fn pop_front(&mut self) -> Option<abi::ID> {
unsafe {
let head = self.head.as_mut()?;
}
}
- pub const fn zero() -> Instant {
- Instant(0)
- }
-
- pub fn actually_monotonic() -> bool {
- // There are ways to change the system time
- false
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0).map(|ticks| {
// `SYSTIM` is measured in microseconds
}
pub fn unsupported_err() -> crate::io::Error {
- crate::io::Error::new_const(ErrorKind::Unsupported, &"operation not supported on SGX yet")
+ crate::io::const_io_error!(ErrorKind::Unsupported, "operation not supported on SGX yet")
}
/// This function is used to implement various functions that doesn't exist,
pub fn sgx_ineffective<T>(v: T) -> crate::io::Result<T> {
static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false);
if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) {
- Err(crate::io::Error::new_const(
+ Err(crate::io::const_io_error!(
ErrorKind::Uncategorized,
- &"operation can't be trusted to have any effect on SGX",
+ "operation can't be trusted to have any effect on SGX",
))
} else {
Ok(v)
pub fn connect_timeout(addr: &SocketAddr, dur: Duration) -> io::Result<TcpStream> {
if dur == Duration::default() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
Self::connect(Ok(addr)) // FIXME: ignoring timeout
pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
match dur {
Some(dur) if dur == Duration::default() => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
_ => sgx_ineffective(()),
pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
match dur {
Some(dur) if dur == Duration::default() => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
_ => sgx_ineffective(()),
use crate::ffi::OsStr;
-use crate::path::Prefix;
+use crate::io;
+use crate::path::{Path, PathBuf, Prefix};
+use crate::sys::unsupported;
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
pub const MAIN_SEP_STR: &str = "/";
pub const MAIN_SEP: char = '/';
+
+pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub(*other)?))
}
-
- pub fn actually_monotonic() -> bool {
- false
- }
-
- pub const fn zero() -> Instant {
- Instant(Duration::from_secs(0))
- }
}
impl SystemTime {
pub fn unlink(p: &Path) -> io::Result<()> {
if stat(p)?.file_type().is_dir() {
- Err(io::Error::new_const(io::ErrorKind::IsADirectory, &"is a directory"))
+ Err(io::const_io_error!(io::ErrorKind::IsADirectory, "is a directory"))
} else {
error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
.map_err(|e| e.as_io_error())?;
.map_err(|e| e.as_io_error())?;
Ok(())
} else {
- Err(io::Error::new_const(io::ErrorKind::NotADirectory, &"not a directory"))
+ Err(io::const_io_error!(io::ErrorKind::NotADirectory, "not a directory"))
}
}
pub fn readlink(p: &Path) -> io::Result<PathBuf> {
// This target doesn't support symlinks
stat(p)?;
- Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"not a symbolic link"))
+ Err(io::const_io_error!(io::ErrorKind::InvalidInput, "not a symbolic link"))
}
pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
}
pub fn unsupported_err() -> crate::io::Error {
- crate::io::Error::new_const(
+ crate::io::const_io_error!(
crate::io::ErrorKind::Unsupported,
- &"operation not supported on this platform",
+ "operation not supported on this platform",
)
}
}
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
};
match n {
- 0 => Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out")),
+ 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")),
_ => {
let can_write = writefds.num_fds != 0;
if !can_write {
let timeout = match dur {
Some(dur) => {
if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this
/// function just returns a generic error.
fn cvt_env(t: c_int) -> io::Result<c_int> {
- if t == -1 {
- Err(io::Error::new_const(io::ErrorKind::Uncategorized, &"failure"))
- } else {
- Ok(t)
- }
+ if t == -1 { Err(io::const_io_error!(io::ErrorKind::Uncategorized, "failure")) } else { Ok(t) }
}
pub fn temp_dir() -> PathBuf {
use crate::ffi::OsStr;
-use crate::path::Prefix;
+use crate::io;
+use crate::path::{Path, PathBuf, Prefix};
+use crate::sys::unsupported;
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
pub const MAIN_SEP_STR: &str = "\\";
pub const MAIN_SEP: char = '\\';
+
+pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
tm_min: rtc.tm_min,
tm_hour: rtc.tm_hour,
tm_mday: rtc.tm_mday,
- tm_mon: rtc.tm_mon,
+ tm_mon: rtc.tm_mon - 1,
tm_year: rtc.tm_year,
tm_wday: rtc.tm_wday,
tm_yday: 0,
tv_nsec: ext.stx_btime.tv_nsec as _,
}))
} else {
- Err(io::Error::new_const(
+ Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"creation time is not available for the filesystem",
+ "creation time is not available for the filesystem",
))
};
}
}
- Err(io::Error::new_const(
+ Err(io::const_io_error!(
io::ErrorKind::Unsupported,
- &"creation time is not available on this platform \
+ "creation time is not available on this platform \
currently",
))
}
macro_rules! unimpl {
() => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::Unsupported,
- &"No networking available on L4Re.",
+ "No networking available on L4Re.",
));
};
}
libc::ENOSPC => StorageFull,
libc::ENOSYS => Unsupported,
libc::EMLINK => TooManyLinks,
- libc::ENAMETOOLONG => FilenameTooLong,
+ libc::ENAMETOOLONG => InvalidFilename,
libc::ENETDOWN => NetworkDown,
libc::ENETUNREACH => NetworkUnreachable,
libc::ENOTCONN => NotConnected,
}
pub fn unsupported_err() -> io::Error {
- io::Error::new_const(
- io::ErrorKind::Unsupported,
- &"operation not supported on this platform",
- )
+ io::const_io_error!(io::ErrorKind::Unsupported, "operation not supported on this platform",)
}
}
let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 };
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
loop {
let elapsed = start.elapsed();
if elapsed >= timeout {
- return Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out"));
+ return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out"));
}
let timeout = timeout - elapsed;
// for POLLHUP rather than read readiness
if pollfd.revents & libc::POLLHUP != 0 {
let e = self.take_error()?.unwrap_or_else(|| {
- io::Error::new_const(
+ io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"no error set after POLLHUP",
+ "no error set after POLLHUP",
)
});
return Err(e);
let timeout = match dur {
Some(dur) => {
if dur.as_secs() == 0 && dur.subsec_nanos() == 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
0,
))?;
if path_len <= 1 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"KERN_PROC_PATHNAME sysctl returned zero-length string",
+ "KERN_PROC_PATHNAME sysctl returned zero-length string",
));
}
let mut path: Vec<u8> = Vec::with_capacity(path_len);
if curproc_exe.is_file() {
return crate::fs::read_link(curproc_exe);
}
- Err(io::Error::new_const(
+ Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"/proc/curproc/exe doesn't point to regular file.",
+ "/proc/curproc/exe doesn't point to regular file.",
))
}
sysctl().or_else(|_| procfs())
cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, &mut argv_len, ptr::null_mut(), 0))?;
argv.set_len(argv_len as usize);
if argv[0].is_null() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"no current exe available",
+ "no current exe available",
));
}
let argv0 = CStr::from_ptr(argv[0]).to_bytes();
#[cfg(any(target_os = "linux", target_os = "android", target_os = "emscripten"))]
pub fn current_exe() -> io::Result<PathBuf> {
match crate::fs::read_link("/proc/self/exe") {
- Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::Error::new_const(
+ Err(ref e) if e.kind() == io::ErrorKind::NotFound => Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"no /proc/self/exe available. Is /proc mounted?",
+ "no /proc/self/exe available. Is /proc mounted?",
)),
other => other,
}
);
if result != 0 {
use crate::io::ErrorKind;
- Err(io::Error::new_const(ErrorKind::Uncategorized, &"Error getting executable path"))
+ Err(io::const_io_error!(ErrorKind::Uncategorized, "Error getting executable path"))
} else {
let name = CStr::from_ptr((*info.as_ptr()).name.as_ptr()).to_bytes();
Ok(PathBuf::from(OsStr::from_bytes(name)))
#[cfg(any(target_os = "fuchsia", target_os = "l4re"))]
pub fn current_exe() -> io::Result<PathBuf> {
use crate::io::ErrorKind;
- Err(io::Error::new_const(ErrorKind::Unsupported, &"Not yet implemented!"))
+ Err(io::const_io_error!(ErrorKind::Unsupported, "Not yet implemented!"))
}
#[cfg(target_os = "vxworks")]
+use crate::env;
use crate::ffi::OsStr;
-use crate::path::Prefix;
+use crate::io;
+use crate::path::{Path, PathBuf, Prefix};
#[inline]
pub fn is_sep_byte(b: u8) -> bool {
pub const MAIN_SEP_STR: &str = "/";
pub const MAIN_SEP: char = '/';
+
+/// Make a POSIX path absolute without changing its semantics.
+pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
+ // This is mostly a wrapper around collecting `Path::components`, with
+ // exceptions made where this conflicts with the POSIX specification.
+ // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
+ // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
+
+ let mut components = path.components();
+ let path_os = path.as_os_str().bytes();
+
+ let mut normalized = if path.is_absolute() {
+ // "If a pathname begins with two successive <slash> characters, the
+ // first component following the leading <slash> characters may be
+ // interpreted in an implementation-defined manner, although more than
+ // two leading <slash> characters shall be treated as a single <slash>
+ // character."
+ if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
+ components.next();
+ PathBuf::from("//")
+ } else {
+ PathBuf::new()
+ }
+ } else {
+ env::current_dir()?
+ };
+ normalized.extend(components);
+
+ // "Interfaces using pathname resolution may specify additional constraints
+ // when a pathname that does not name an existing directory contains at
+ // least one non- <slash> character and contains one or more trailing
+ // <slash> characters".
+ // A trailing <slash> is also meaningful if "a symbolic link is
+ // encountered during pathname resolution".
+ if path_os.ends_with(b"/") {
+ normalized.push("");
+ }
+
+ Ok(normalized)
+}
}
}
+impl From<u8> for ExitCode {
+ fn from(code: u8) -> Self {
+ Self(code)
+ }
+}
+
pub struct CommandArgs<'a> {
iter: crate::slice::Iter<'a, CString>,
}
let envp = self.capture_env();
if self.saw_nul() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"nul byte found in provided data",
+ "nul byte found in provided data",
));
}
pub fn exec(&mut self, default: Stdio) -> io::Error {
if self.saw_nul() {
- return io::Error::new_const(
+ return io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"nul byte found in provided data",
+ "nul byte found in provided data",
);
}
))?;
}
if actual != 1 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidData,
- &"Failed to get exit status of process",
+ "Failed to get exit status of process",
));
}
Ok(ExitStatus(proc_info.return_code))
))?;
}
if actual != 1 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidData,
- &"Failed to get exit status of process",
+ "Failed to get exit status of process",
));
}
Ok(Some(ExitStatus(proc_info.return_code)))
let envp = self.capture_env();
if self.saw_nul() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"nul byte found in provided data",
+ "nul byte found in provided data",
));
}
let envp = self.capture_env();
if self.saw_nul() {
- return io::Error::new_const(
- ErrorKind::InvalidInput,
- &"nul byte found in provided data",
- );
+ return io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data",);
}
match self.setup_io(default, true) {
// and used for another process, and we probably shouldn't be killing
// random processes, so just return an error.
if self.status.is_some() {
- Err(Error::new_const(
+ Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"invalid argument: can't kill an exited process",
+ "invalid argument: can't kill an exited process",
))
} else {
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
let envp = self.capture_env();
if self.saw_nul() {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"nul byte found in provided data",
+ "nul byte found in provided data",
));
}
let (ours, theirs) = self.setup_io(default, needs_stdin)?;
// and used for another process, and we probably shouldn't be killing
// random processes, so just return an error.
if self.status.is_some() {
- Err(Error::new_const(
+ Err(io::const_io_error!(
ErrorKind::InvalidInput,
- &"invalid argument: can't kill an exited process",
+ "invalid argument: can't kill an exited process",
))
} else {
cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
}
match unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) } {
-1 => Err(io::Error::last_os_error()),
- 0 => Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform")),
+ 0 => Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform")),
cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) }),
}
} else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "netbsd"))] {
if res == -1 {
return Err(io::Error::last_os_error());
} else if cpus == 0 {
- return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform"));
+ return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
}
}
Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
if res == -1 {
return Err(io::Error::last_os_error());
} else if cpus == 0 {
- return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform"));
+ return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
}
Ok(unsafe { NonZeroUsize::new_unchecked(cpus as usize) })
let res = libc::get_system_info(&mut sinfo);
if res != libc::B_OK {
- return Err(io::Error::new_const(io::ErrorKind::NotFound, &"The number of hardware threads is not known for the target platform"));
+ return Err(io::const_io_error!(io::ErrorKind::NotFound, "The number of hardware threads is not known for the target platform"));
}
Ok(NonZeroUsize::new_unchecked(sinfo.cpu_count as usize))
}
} else {
// FIXME: implement on vxWorks, Redox, l4re
- Err(io::Error::new_const(io::ErrorKind::Unsupported, &"Getting the number of hardware threads is not supported on the target platform"))
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "Getting the number of hardware threads is not supported on the target platform"))
}
}
}
Instant { t: unsafe { mach_absolute_time() } }
}
- pub const fn zero() -> Instant {
- Instant { t: 0 }
- }
-
- pub fn actually_monotonic() -> bool {
- true
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
let diff = self.t.checked_sub(other.t)?;
let info = info();
Instant { t: now(libc::CLOCK_MONOTONIC) }
}
- pub const fn zero() -> Instant {
- Instant { t: Timespec::zero() }
- }
-
- pub fn actually_monotonic() -> bool {
- (cfg!(target_os = "linux") && cfg!(target_arch = "x86_64"))
- || (cfg!(target_os = "linux") && cfg!(target_arch = "x86"))
- || (cfg!(target_os = "linux") && cfg!(target_arch = "aarch64"))
- || cfg!(target_os = "fuchsia")
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.t.sub_timespec(&other.t).ok()
}
}
pub fn unsupported_err() -> std_io::Error {
- std_io::Error::new_const(
+ std_io::const_io_error!(
std_io::ErrorKind::Unsupported,
- &"operation not supported on this platform",
+ "operation not supported on this platform",
)
}
}
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
- Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform"))
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot set env vars on this platform"))
}
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
- Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform"))
+ Err(io::const_io_error!(io::ErrorKind::Unsupported, "cannot unset env vars on this platform"))
}
pub fn temp_dir() -> PathBuf {
}
}
+impl From<u8> for ExitCode {
+ fn from(code: u8) -> Self {
+ match code {
+ 0 => Self::SUCCESS,
+ 1..=255 => Self::FAILURE,
+ }
+ }
+}
+
pub struct Process(!);
impl Process {
panic!("time not implemented on this platform")
}
- pub const fn zero() -> Instant {
- Instant(Duration::from_secs(0))
- }
-
- pub fn actually_monotonic() -> bool {
- false
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0)
}
pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
f.to_str()
- .ok_or_else(|| io::Error::new_const(io::ErrorKind::Uncategorized, &"input must be utf-8"))
+ .ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
}
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
for entry in ReadDir::new(fd, dummy_root) {
let entry = entry?;
let path = crate::str::from_utf8(&entry.name).map_err(|_| {
- io::Error::new_const(io::ErrorKind::Uncategorized, &"invalid utf-8 file name found")
+ io::const_io_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found")
})?;
if entry.file_type()?.is_dir() {
Instant(current_time(wasi::CLOCKID_MONOTONIC))
}
- pub const fn zero() -> Instant {
- Instant(Duration::from_secs(0))
- }
-
- pub fn actually_monotonic() -> bool {
- true
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0)
}
pub const FILE_ATTRIBUTE_READONLY: DWORD = 0x1;
pub const FILE_ATTRIBUTE_DIRECTORY: DWORD = 0x10;
pub const FILE_ATTRIBUTE_REPARSE_POINT: DWORD = 0x400;
+pub const INVALID_FILE_ATTRIBUTES: DWORD = DWORD::MAX;
pub const FILE_SHARE_DELETE: DWORD = 0x4;
pub const FILE_SHARE_READ: DWORD = 0x1;
pub const FILE_SHARE_WRITE: DWORD = 0x2;
+pub const FILE_OPEN: ULONG = 0x00000001;
pub const FILE_OPEN_REPARSE_POINT: ULONG = 0x200000;
pub const OBJ_DONT_REPARSE: ULONG = 0x1000;
lpBuffer: LPWSTR,
lpFilePart: *mut LPWSTR,
) -> DWORD;
+ pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD;
}
#[link(name = "ws2_32")]
compat_fn! {
"ntdll":
- pub fn NtOpenFile(
+ pub fn NtCreateFile(
FileHandle: *mut HANDLE,
DesiredAccess: ACCESS_MASK,
ObjectAttributes: *const OBJECT_ATTRIBUTES,
IoStatusBlock: *mut IO_STATUS_BLOCK,
+ AllocationSize: *mut i64,
+ FileAttributes: ULONG,
ShareAccess: ULONG,
- OpenOptions: ULONG
+ CreateDisposition: ULONG,
+ CreateOptions: ULONG,
+ EaBuffer: *mut c_void,
+ EaLength: ULONG
) -> NTSTATUS {
- panic!("`NtOpenFile` not available");
+ panic!("`NtCreateFile` not available");
}
pub fn RtlNtStatusToDosError(
Status: NTSTATUS
)
}
_ => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::Uncategorized,
- &"Unsupported reparse point type",
+ "Unsupported reparse point type",
));
}
};
/// Open a link relative to the parent directory, ensure no symlinks are followed.
fn open_link_no_reparse(parent: &File, name: &[u16], access: u32) -> io::Result<File> {
- // This is implemented using the lower level `NtOpenFile` function as
+ // This is implemented using the lower level `NtCreateFile` function as
// unfortunately opening a file relative to a parent is not supported by
// win32 functions. It is however a fundamental feature of the NT kernel.
//
- // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntopenfile
+ // See https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntcreatefile
unsafe {
let mut handle = ptr::null_mut();
let mut io_status = c::IO_STATUS_BLOCK::default();
Attributes: ATTRIBUTES.load(Ordering::Relaxed),
..c::OBJECT_ATTRIBUTES::default()
};
- let status = c::NtOpenFile(
+ let status = c::NtCreateFile(
&mut handle,
access,
&object,
&mut io_status,
+ crate::ptr::null_mut(),
+ 0,
c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE,
+ c::FILE_OPEN,
// If `name` is a symlink then open the link rather than the target.
c::FILE_OPEN_REPARSE_POINT,
+ crate::ptr::null_mut(),
+ 0,
);
// Convert an NTSTATUS to the more familiar Win32 error codes (aka "DosError")
if c::nt_success(status) {
#[cfg(target_vendor = "uwp")]
pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::Unsupported,
- &"hard link are not supported on UWP",
+ "hard link are not supported on UWP",
));
}
c::ERROR_FILE_NOT_FOUND => return NotFound,
c::ERROR_PATH_NOT_FOUND => return NotFound,
c::ERROR_NO_DATA => return BrokenPipe,
+ c::ERROR_INVALID_NAME => return InvalidFilename,
c::ERROR_INVALID_PARAMETER => return InvalidInput,
c::ERROR_NOT_ENOUGH_MEMORY | c::ERROR_OUTOFMEMORY => return OutOfMemory,
c::ERROR_SEM_TIMEOUT
c::ERROR_POSSIBLE_DEADLOCK => return Deadlock,
c::ERROR_NOT_SAME_DEVICE => return CrossesDevices,
c::ERROR_TOO_MANY_LINKS => return TooManyLinks,
- c::ERROR_FILENAME_EXCED_RANGE => return FilenameTooLong,
+ c::ERROR_FILENAME_EXCED_RANGE => return InvalidFilename,
_ => {}
}
fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
let mut maybe_result: Vec<u16> = s.encode_wide().collect();
if unrolled_find_u16s(0, &maybe_result).is_some() {
- return Err(crate::io::Error::new_const(
+ return Err(crate::io::const_io_error!(
ErrorKind::InvalidInput,
- &"strings passed to WinAPI cannot contain NULs",
+ "strings passed to WinAPI cannot contain NULs",
));
}
maybe_result.push(0);
match result {
Err(ref error) if error.kind() == io::ErrorKind::WouldBlock => {
if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
};
match count {
- 0 => {
- Err(io::Error::new_const(io::ErrorKind::TimedOut, &"connection timed out"))
- }
+ 0 => Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")),
_ => {
if writefds.fd_count != 1 {
if let Some(e) = self.take_error()? {
Some(dur) => {
let timeout = sys::dur2timeout(dur);
if timeout == 0 {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"cannot set a 0 duration timeout",
+ "cannot set a 0 duration timeout",
));
}
timeout
)?;
Ok(path)
}
+
+/// Make a Windows path absolute.
+pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
+ if path.as_os_str().bytes().starts_with(br"\\?\") {
+ return Ok(path.into());
+ }
+ let path = to_u16s(path)?;
+ let lpfilename = path.as_ptr();
+ fill_utf16_buf(
+ // SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
+ // `lpfilename` is a pointer to a null terminated string that is not
+ // invalidated until after `GetFullPathNameW` returns successfully.
+ |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
+ super::os2path,
+ )
+}
fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
if str.as_ref().encode_wide().any(|b| b == 0) {
- Err(io::Error::new_const(ErrorKind::InvalidInput, &"nul byte found in provided data"))
+ Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data"))
} else {
Ok(str)
}
) -> io::Result<PathBuf> {
// Early return if there is no filename.
if exe_path.is_empty() || path::has_trailing_slash(exe_path) {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
- &"program path has no file name",
+ "program path has no file name",
));
}
// Test if the file name has the `exe` extension.
// Append `.exe` if not already there.
path = path::append_suffix(path, EXE_SUFFIX.as_ref());
- if path.try_exists().unwrap_or(false) {
+ if program_exists(&path) {
return Ok(path);
} else {
// It's ok to use `set_extension` here because the intent is to
if !has_extension {
path.set_extension(EXE_EXTENSION);
}
- if let Ok(true) = path.try_exists() { Some(path) } else { None }
+ if program_exists(&path) { Some(path) } else { None }
});
if let Some(path) = result {
return Ok(path);
}
}
// If we get here then the executable cannot be found.
- Err(io::Error::new_const(io::ErrorKind::NotFound, &"program not found"))
+ Err(io::const_io_error!(io::ErrorKind::NotFound, "program not found"))
}
// Calls `f` for every path that should be used to find an executable.
None
}
+/// Check if a file exists without following symlinks.
+fn program_exists(path: &Path) -> bool {
+ unsafe {
+ to_u16s(path)
+ .map(|path| {
+ // Getting attributes using `GetFileAttributesW` does not follow symlinks
+ // and it will almost always be successful if the link exists.
+ // There are some exceptions for special system files (e.g. the pagefile)
+ // but these are not executable.
+ c::GetFileAttributesW(path.as_ptr()) != c::INVALID_FILE_ATTRIBUTES
+ })
+ .unwrap_or(false)
+ }
+}
+
impl Stdio {
fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> {
match *self {
}
}
+impl From<u8> for ExitCode {
+ fn from(code: u8) -> Self {
+ ExitCode(c::DWORD::from(code))
+ }
+}
+
fn zeroed_startupinfo() -> c::STARTUPINFO {
c::STARTUPINFO {
cb: 0,
fn windows_exe_resolver() {
use super::resolve_exe;
use crate::io;
+ use crate::sys::fs::symlink;
+ use crate::sys_common::io::test::tmpdir;
let env_paths = || env::var_os("PATH");
// The application's directory is also searched.
let current_exe = env::current_exe().unwrap();
assert!(resolve_exe(current_exe.file_name().unwrap().as_ref(), empty_paths, None).is_ok());
+
+ // Create a temporary path and add a broken symlink.
+ let temp = tmpdir();
+ let mut exe_path = temp.path().to_owned();
+ exe_path.push("exists.exe");
+ symlink("<DOES NOT EXIST>".as_ref(), &exe_path).unwrap();
+
+ // A broken symlink should still be resolved.
+ assert!(resolve_exe(OsStr::new("exists.exe"), empty_paths, Some(temp.path().as_ref())).is_ok());
}
if data[0] >> 6 != 0b10 {
// not a continuation byte - reject
incomplete_utf8.len = 0;
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidData,
- &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
));
}
incomplete_utf8.bytes[incomplete_utf8.len as usize] = data[0];
return Ok(1);
}
Err(_) => {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidData,
- &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
));
}
}
incomplete_utf8.len = 1;
return Ok(1);
} else {
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidData,
- &"Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
+ "Windows stdio in console mode does not support writing non-UTF-8 byte sequences",
));
}
}
}
Err(_) => {
// We can't really do any better than forget all data and return an error.
- return Err(io::Error::new_const(
+ return Err(io::const_io_error!(
io::ErrorKind::InvalidData,
- &"Windows stdin in console mode does not support non-UTF-16 input; \
+ "Windows stdin in console mode does not support non-UTF-16 input; \
encountered unpaired surrogate",
));
}
sysinfo.dwNumberOfProcessors as usize
};
match res {
- 0 => Err(io::Error::new_const(
+ 0 => Err(io::const_io_error!(
io::ErrorKind::NotFound,
- &"The number of hardware threads is not known for the target platform",
+ "The number of hardware threads is not known for the target platform",
)),
cpus => Ok(unsafe { NonZeroUsize::new_unchecked(cpus) }),
}
perf_counter::PerformanceCounterInstant::now().into()
}
- pub fn actually_monotonic() -> bool {
- false
- }
-
- pub const fn zero() -> Instant {
- Instant { t: Duration::from_secs(0) }
- }
-
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
// On windows there's a threshold below which we consider two timestamps
// equivalent due to measurement error. For more details + doc link,
use crate::io::{self, Error, ErrorKind};
use crate::path::Path;
-pub(crate) const NOT_FILE_ERROR: Error = Error::new_const(
+pub(crate) const NOT_FILE_ERROR: Error = io::const_io_error!(
ErrorKind::InvalidInput,
- &"the source path is neither a regular file nor a symlink to a regular file",
+ "the source path is neither a regular file nor a symlink to a regular file",
);
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
use crate::convert::{TryFrom, TryInto};
use crate::ffi::CString;
use crate::fmt;
-use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut};
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
use crate::mem;
use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
use crate::ptr;
*(storage as *const _ as *const c::sockaddr_in6)
})))
}
- _ => Err(Error::new_const(ErrorKind::InvalidInput, &"invalid argument")),
+ _ => Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid argument")),
}
}
($e:expr, $msg:expr) => {
match $e {
Some(r) => r,
- None => return Err(io::Error::new_const(io::ErrorKind::InvalidInput, &$msg)),
+ None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, $msg)),
}
};
}
#![stable(feature = "time", since = "1.3.0")]
-mod monotonic;
#[cfg(test)]
mod tests;
/// A measurement of a monotonically nondecreasing clock.
/// Opaque and useful only with [`Duration`].
///
-/// Instants are always guaranteed to be no less than any previously measured
-/// instant when created, and are often useful for tasks such as measuring
+/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously
+/// measured instant when created, and are often useful for tasks such as measuring
/// benchmarks or timing how long an operation takes.
///
/// Note, however, that instants are **not** guaranteed to be **steady**. In other
/// }
/// ```
///
+/// [platform bugs]: Instant#monotonicity
+///
/// # OS-specific behaviors
///
/// An `Instant` is a wrapper around system-specific types and it may behave
/// > structure cannot represent the new point in time.
///
/// [`add`]: Instant::add
+///
+/// ## Monotonicity
+///
+/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior
+/// if available, which is the case for all [tier 1] platforms.
+/// In practice such guarantees are – under rare circumstances – broken by hardware, virtualization
+/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks
+/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this
+/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations
+/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order.
+///
+/// This workaround obscures programming errors where earlier and later instants are accidentally
+/// swapped. For this reason future rust versions may reintroduce panics.
+///
+/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html
+/// [`duration_since`]: Instant::duration_since
+/// [`elapsed`]: Instant::elapsed
+/// [`sub`]: Instant::sub
+/// [`checked_duration_since`]: Instant::checked_duration_since
+///
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[stable(feature = "time2", since = "1.8.0")]
pub struct Instant(time::Instant);
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
pub fn now() -> Instant {
- let os_now = time::Instant::now();
-
- // And here we come upon a sad state of affairs. The whole point of
- // `Instant` is that it's monotonically increasing. We've found in the
- // wild, however, that it's not actually monotonically increasing for
- // one reason or another. These appear to be OS and hardware level bugs,
- // and there's not really a whole lot we can do about them. Here's a
- // taste of what we've found:
- //
- // * #48514 - OpenBSD, x86_64
- // * #49281 - linux arm64 and s390x
- // * #51648 - windows, x86
- // * #56560 - windows, x86_64, AWS
- // * #56612 - windows, x86, vm (?)
- // * #56940 - linux, arm64
- // * https://bugzilla.mozilla.org/show_bug.cgi?id=1487778 - a similar
- // Firefox bug
- //
- // It seems that this just happens a lot in the wild.
- // We're seeing panics across various platforms where consecutive calls
- // to `Instant::now`, such as via the `elapsed` function, are panicking
- // as they're going backwards. Placed here is a last-ditch effort to try
- // to fix things up. We keep a global "latest now" instance which is
- // returned instead of what the OS says if the OS goes backwards.
- //
- // To hopefully mitigate the impact of this, a few platforms are
- // excluded as "these at least haven't gone backwards yet".
- //
- // While issues have been seen on arm64 platforms the Arm architecture
- // requires that the counter monotonically increases and that it must
- // provide a uniform view of system time (e.g. it must not be possible
- // for a core to receive a message from another core with a time stamp
- // and observe time going backwards (ARM DDI 0487G.b D11.1.2). While
- // there have been a few 64bit SoCs that have bugs which cause time to
- // not monoticially increase, these have been fixed in the Linux kernel
- // and we shouldn't penalize all Arm SoCs for those who refuse to
- // update their kernels:
- // SUN50I_ERRATUM_UNKNOWN1 - Allwinner A64 / Pine A64 - fixed in 5.1
- // FSL_ERRATUM_A008585 - Freescale LS2080A/LS1043A - fixed in 4.10
- // HISILICON_ERRATUM_161010101 - Hisilicon 1610 - fixed in 4.11
- // ARM64_ERRATUM_858921 - Cortex A73 - fixed in 4.12
- if time::Instant::actually_monotonic() {
- return Instant(os_now);
- }
-
- Instant(monotonic::monotonize(os_now))
+ Instant(time::Instant::now())
}
- /// Returns the amount of time elapsed from another instant to this one.
+ /// Returns the amount of time elapsed from another instant to this one,
+ /// or zero duration if that instant is later than this one.
///
/// # Panics
///
- /// This function will panic if `earlier` is later than `self`.
+ /// Previous rust versions panicked when `earlier` was later than `self`. Currently this
+ /// method saturates. Future versions may reintroduce the panic in some circumstances.
+ /// See [Monotonicity].
+ ///
+ /// [Monotonicity]: Instant#monotonicity
///
/// # Examples
///
/// sleep(Duration::new(1, 0));
/// let new_now = Instant::now();
/// println!("{:?}", new_now.duration_since(now));
+ /// println!("{:?}", now.duration_since(new_now)); // 0ns
/// ```
#[must_use]
#[stable(feature = "time2", since = "1.8.0")]
pub fn duration_since(&self, earlier: Instant) -> Duration {
- self.0.checked_sub_instant(&earlier.0).expect("supplied instant is later than self")
+ self.checked_duration_since(earlier).unwrap_or_default()
}
/// Returns the amount of time elapsed from another instant to this one,
/// or None if that instant is later than this one.
///
+ /// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s,
+ /// this method can return `None`.
+ ///
+ /// [monotonicity bugs]: Instant#monotonicity
+ ///
/// # Examples
///
/// ```no_run
///
/// # Panics
///
- /// This function may panic if the current time is earlier than this
- /// instant, which is something that can happen if an `Instant` is
- /// produced synthetically.
+ /// Previous rust versions panicked when self was earlier than the current time. Currently this
+ /// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
+ /// See [Monotonicity].
+ ///
+ /// [Monotonicity]: Instant#monotonicity
///
/// # Examples
///
impl Sub<Instant> for Instant {
type Output = Duration;
+ /// Returns the amount of time elapsed from another instant to this one,
+ /// or zero duration if that instant is later than this one.
+ ///
+ /// # Panics
+ ///
+ /// Previous rust versions panicked when `other` was later than `self`. Currently this
+ /// method saturates. Future versions may reintroduce the panic in some circumstances.
+ /// See [Monotonicity].
+ ///
+ /// [Monotonicity]: Instant#monotonicity
fn sub(self, other: Instant) -> Duration {
self.duration_since(other)
}
+++ /dev/null
-use crate::sys::time;
-
-#[inline]
-pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
- inner::monotonize(raw)
-}
-
-#[cfg(any(all(target_has_atomic = "64", not(target_has_atomic = "128")), target_arch = "aarch64"))]
-pub mod inner {
- use crate::sync::atomic::AtomicU64;
- use crate::sync::atomic::Ordering::*;
- use crate::sys::time;
- use crate::time::Duration;
-
- pub(in crate::time) const ZERO: time::Instant = time::Instant::zero();
-
- // bits 30 and 31 are never used since the nanoseconds part never exceeds 10^9
- const UNINITIALIZED: u64 = 0b11 << 30;
- static MONO: AtomicU64 = AtomicU64::new(UNINITIALIZED);
-
- #[inline]
- pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
- monotonize_impl(&MONO, raw)
- }
-
- #[inline]
- pub(in crate::time) fn monotonize_impl(mono: &AtomicU64, raw: time::Instant) -> time::Instant {
- let delta = raw.checked_sub_instant(&ZERO).unwrap();
- let secs = delta.as_secs();
- // occupies no more than 30 bits (10^9 seconds)
- let nanos = delta.subsec_nanos() as u64;
-
- // This wraps around every 136 years (2^32 seconds).
- // To detect backsliding we use wrapping arithmetic and declare forward steps smaller
- // than 2^31 seconds as expected and everything else as a backslide which will be
- // monotonized.
- // This could be a problem for programs that call instants at intervals greater
- // than 68 years. Interstellar probes may want to ensure that actually_monotonic() is true.
- let packed = (secs << 32) | nanos;
- let updated = mono.fetch_update(Relaxed, Relaxed, |old| {
- (old == UNINITIALIZED || packed.wrapping_sub(old) < u64::MAX / 2).then_some(packed)
- });
- match updated {
- Ok(_) => raw,
- Err(newer) => {
- // Backslide occurred. We reconstruct monotonized time from the upper 32 bit of the
- // passed in value and the 64bits loaded from the atomic
- let seconds_lower = newer >> 32;
- let mut seconds_upper = secs & 0xffff_ffff_0000_0000;
- if secs & 0xffff_ffff > seconds_lower {
- // Backslide caused the lower 32bit of the seconds part to wrap.
- // This must be the case because the seconds part is larger even though
- // we are in the backslide branch, i.e. the seconds count should be smaller or equal.
- //
- // We assume that backslides are smaller than 2^32 seconds
- // which means we need to add 1 to the upper half to restore it.
- //
- // Example:
- // most recent observed time: 0xA1_0000_0000_0000_0000u128
- // bits stored in AtomicU64: 0x0000_0000_0000_0000u64
- // backslide by 1s
- // caller time is 0xA0_ffff_ffff_0000_0000u128
- // -> we can fix up the upper half time by adding 1 << 32
- seconds_upper = seconds_upper.wrapping_add(0x1_0000_0000);
- }
- let secs = seconds_upper | seconds_lower;
- let nanos = newer as u32;
- ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
- }
- }
- }
-}
-
-#[cfg(all(target_has_atomic = "128", not(target_arch = "aarch64")))]
-pub mod inner {
- use crate::sync::atomic::AtomicU128;
- use crate::sync::atomic::Ordering::*;
- use crate::sys::time;
- use crate::time::Duration;
-
- const ZERO: time::Instant = time::Instant::zero();
- static MONO: AtomicU128 = AtomicU128::new(0);
-
- #[inline]
- pub(super) fn monotonize(raw: time::Instant) -> time::Instant {
- let delta = raw.checked_sub_instant(&ZERO).unwrap();
- // Split into seconds and nanos since Duration doesn't have a
- // constructor that takes a u128
- let secs = delta.as_secs() as u128;
- let nanos = delta.subsec_nanos() as u128;
- let timestamp: u128 = secs << 64 | nanos;
- let timestamp = MONO.fetch_max(timestamp, Relaxed).max(timestamp);
- let secs = (timestamp >> 64) as u64;
- let nanos = timestamp as u32;
- ZERO.checked_add_duration(&Duration::new(secs, nanos)).unwrap()
- }
-}
-
-#[cfg(not(any(target_has_atomic = "64", target_has_atomic = "128")))]
-pub mod inner {
- use crate::cmp;
- use crate::sys::time;
- use crate::sys_common::mutex::StaticMutex;
-
- #[inline]
- pub(super) fn monotonize(os_now: time::Instant) -> time::Instant {
- static LOCK: StaticMutex = StaticMutex::new();
- static mut LAST_NOW: time::Instant = time::Instant::zero();
- unsafe {
- let _lock = LOCK.lock();
- let now = cmp::max(LAST_NOW, os_now);
- LAST_NOW = now;
- now
- }
- }
-}
}
#[test]
-#[should_panic]
-fn instant_duration_since_panic() {
+fn instant_duration_since_saturates() {
let a = Instant::now();
- let _ = (a - Duration::SECOND).duration_since(a);
+ assert_eq!((a - Duration::SECOND).duration_since(a), Duration::ZERO);
}
#[test]
#[test]
fn instant_saturating_duration_since_nopanic() {
let a = Instant::now();
+ #[allow(deprecated, deprecated_in_future)]
let ret = (a - Duration::SECOND).saturating_duration_since(a);
assert_eq!(ret, Duration::ZERO);
}
assert!(a < hundred_twenty_years);
}
-#[cfg(all(target_has_atomic = "64", not(target_has_atomic = "128")))]
-#[test]
-fn monotonizer_wrapping_backslide() {
- use super::monotonic::inner::{monotonize_impl, ZERO};
- use core::sync::atomic::AtomicU64;
-
- let reference = AtomicU64::new(0);
-
- let time = match ZERO.checked_add_duration(&Duration::from_secs(0xffff_ffff)) {
- Some(time) => time,
- None => {
- // platform cannot represent u32::MAX seconds so it won't have to deal with this kind
- // of overflow either
- return;
- }
- };
-
- let monotonized = monotonize_impl(&reference, time);
- let expected = ZERO.checked_add_duration(&Duration::from_secs(1 << 32)).unwrap();
- assert_eq!(
- monotonized, expected,
- "64bit monotonizer should handle overflows in the seconds part"
- );
-}
-
macro_rules! bench_instant_threaded {
($bench_name:ident, $thread_count:expr) => {
#[bench]
#![cfg_attr(
any(
all(target_arch = "arm", any(target_os = "linux", target_os = "android")),
- all(target_arch = "aarch64", any(target_os = "linux", target_os = "android")),
+ all(bootstrap, target_arch = "aarch64", any(target_os = "linux", target_os = "android")),
all(target_arch = "powerpc", target_os = "linux"),
all(target_arch = "powerpc64", target_os = "linux"),
- any(target_arch = "x86", target_arch = "x86_64"),
),
feature(stdsimd)
)]
println!("flagm: {}", is_aarch64_feature_detected!("flagm"));
println!("ssbs: {}", is_aarch64_feature_detected!("ssbs"));
println!("sb: {}", is_aarch64_feature_detected!("sb"));
- println!("pauth: {}", is_aarch64_feature_detected!("pauth"));
+ println!("paca: {}", is_aarch64_feature_detected!("paca"));
+ println!("pacg: {}", is_aarch64_feature_detected!("pacg"));
println!("dpb: {}", is_aarch64_feature_detected!("dpb"));
println!("dpb2: {}", is_aarch64_feature_detected!("dpb2"));
println!("sve2: {}", is_aarch64_feature_detected!("sve2"));
-Subproject commit eaee02ffdf5d820729ccdf2f95fa08b08c7d24d2
+Subproject commit b4a0e07552cf90ef8f1a5b775bf70e4db94b3d63
unstable-options = Allow use of experimental features",
"unstable-options",
)
- .optflagopt(
+ .optflag(
"",
"report-time",
- "Show execution time of each test. Available values:
- plain = do not colorize the execution time (default);
- colored = colorize output according to the `color` parameter value;
+ "Show execution time of each test.
Threshold values for colorized output can be configured via
`RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
is 0.5 seconds, and the critical time is 2 seconds.
Not available for --format=terse",
- "plain|colored",
)
.optflag(
"",
allow_unstable: bool,
) -> OptPartRes<Option<TestTimeOptions>> {
let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
- let colored_opt_str = matches.opt_str("report-time");
- let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
// If `ensure-test-time` option is provided, time output is enforced,
// so user won't be confused if any of tests will silently fail.
let options = if report_time || ensure_test_time {
- if ensure_test_time && !report_time {
- report_time_colored = true;
- }
- Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
+ Some(TestTimeOptions::new_from_env(ensure_test_time))
} else {
None
};
pub passed: usize,
pub failed: usize,
pub ignored: usize,
- pub allowed_fail: usize,
pub filtered_out: usize,
pub measured: usize,
pub exec_time: Option<TestSuiteExecTime>,
passed: 0,
failed: 0,
ignored: 0,
- allowed_fail: 0,
filtered_out: 0,
measured: 0,
exec_time: None,
TestResult::TrFailed => "failed".to_owned(),
TestResult::TrFailedMsg(ref msg) => format!("failed: {}", msg),
TestResult::TrIgnored => "ignored".to_owned(),
- TestResult::TrAllowedFail => "failed (allowed)".to_owned(),
TestResult::TrBench(ref bs) => fmt_bench_samples(bs),
TestResult::TrTimedFail => "failed (time limit exceeded)".to_owned(),
},
}
fn current_test_count(&self) -> usize {
- self.passed + self.failed + self.ignored + self.measured + self.allowed_fail
+ self.passed + self.failed + self.ignored + self.measured
}
}
st.not_failures.push((test, stdout));
}
TestResult::TrIgnored => st.ignored += 1,
- TestResult::TrAllowedFail => st.allowed_fail += 1,
TestResult::TrBench(bs) => {
st.metrics.insert_metric(
test.name.as_slice(),
self.write_event("test", desc.name.as_slice(), "ignored", exec_time, stdout, None)
}
- TestResult::TrAllowedFail => self.write_event(
- "test",
- desc.name.as_slice(),
- "allowed_failure",
- exec_time,
- stdout,
- None,
- ),
-
TestResult::TrBench(ref bs) => {
let median = bs.ns_iter_summ.median as usize;
let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;
\"event\": \"{}\", \
\"passed\": {}, \
\"failed\": {}, \
- \"allowed_fail\": {}, \
\"ignored\": {}, \
\"measured\": {}, \
\"filtered_out\": {}",
if state.failed == 0 { "ok" } else { "failed" },
state.passed,
- state.failed + state.allowed_fail,
- state.allowed_fail,
+ state.failed,
state.ignored,
state.measured,
state.filtered_out,
_shuffle_seed: Option<u64>,
) -> io::Result<()> {
// We write xml header on run start
- self.out.write_all(b"\n")?;
self.write_message("<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
}
))?;
}
- TestResult::TrOk | TestResult::TrAllowedFail => {
+ TestResult::TrOk => {
self.write_message(&*format!(
"<testcase classname=\"{}\" \
name=\"{}\" time=\"{}\"/>",
self.write_message("</testsuite>")?;
self.write_message("</testsuites>")?;
- self.out.write_all(b"\n\n")?;
+ self.out.write_all(b"\n")?;
Ok(state.failed == 0)
}
self.write_short_result("ignored", term::color::YELLOW)
}
- pub fn write_allowed_fail(&mut self) -> io::Result<()> {
- self.write_short_result("FAILED (allowed)", term::color::YELLOW)
- }
-
pub fn write_time_failed(&mut self) -> io::Result<()> {
self.write_short_result("FAILED (time limit exceeded)", term::color::RED)
}
if let (Some(opts), Some(time)) = (self.time_options, exec_time) {
let time_str = format!(" <{}>", time);
- let color = if opts.colored {
+ let color = if self.use_color {
if opts.is_critical(desc, time) {
Some(term::color::RED)
} else if opts.is_warn(desc, time) {
TestResult::TrOk => self.write_ok()?,
TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?,
TestResult::TrIgnored => self.write_ignored()?,
- TestResult::TrAllowedFail => self.write_allowed_fail()?,
TestResult::TrBench(ref bs) => {
self.write_bench()?;
self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?;
self.write_pretty("FAILED", term::color::RED)?;
}
- let s = if state.allowed_fail > 0 {
- format!(
- ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out",
- state.passed,
- state.failed + state.allowed_fail,
- state.allowed_fail,
- state.ignored,
- state.measured,
- state.filtered_out
- )
- } else {
- format!(
- ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
- state.passed, state.failed, state.ignored, state.measured, state.filtered_out
- )
- };
+ let s = format!(
+ ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
+ state.passed, state.failed, state.ignored, state.measured, state.filtered_out
+ );
self.write_plain(&s)?;
self.write_short_result("i", term::color::YELLOW)
}
- pub fn write_allowed_fail(&mut self) -> io::Result<()> {
- self.write_short_result("a", term::color::YELLOW)
- }
-
pub fn write_bench(&mut self) -> io::Result<()> {
self.write_pretty("bench", term::color::CYAN)
}
self.write_failed()
}
TestResult::TrIgnored => self.write_ignored(),
- TestResult::TrAllowedFail => self.write_allowed_fail(),
TestResult::TrBench(ref bs) => {
if self.is_multithreaded {
self.write_test_name(desc)?;
self.write_pretty("FAILED", term::color::RED)?;
}
- let s = if state.allowed_fail > 0 {
- format!(
- ". {} passed; {} failed ({} allowed); {} ignored; {} measured; {} filtered out",
- state.passed,
- state.failed + state.allowed_fail,
- state.allowed_fail,
- state.ignored,
- state.measured,
- state.filtered_out
- )
- } else {
- format!(
- ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
- state.passed, state.failed, state.ignored, state.measured, state.filtered_out
- )
- };
+ let s = format!(
+ ". {} passed; {} failed; {} ignored; {} measured; {} filtered out",
+ state.passed, state.failed, state.ignored, state.measured, state.filtered_out
+ );
self.write_plain(&s)?;
TrFailed,
TrFailedMsg(String),
TrIgnored,
- TrAllowedFail,
TrBench(BenchSamples),
TrTimedFail,
}
if maybe_panic_str.map(|e| e.contains(msg)).unwrap_or(false) {
TestResult::TrOk
- } else if desc.allow_fail {
- TestResult::TrAllowedFail
} else if let Some(panic_str) = maybe_panic_str {
TestResult::TrFailedMsg(format!(
r#"panic did not contain expected string
(&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => {
TestResult::TrFailedMsg("test did not panic as expected".to_string())
}
- _ if desc.allow_fail => TestResult::TrAllowedFail,
_ => TestResult::TrFailed,
};
time_opts: &Option<time::TestTimeOptions>,
exec_time: &Option<time::TestExecTime>,
) -> TestResult {
- let result = match (desc.allow_fail, code) {
- (_, TR_OK) => TestResult::TrOk,
- (true, TR_FAILED) => TestResult::TrAllowedFail,
- (false, TR_FAILED) => TestResult::TrFailed,
- (_, _) => TestResult::TrFailedMsg(format!("got unexpected return code {}", code)),
+ let result = match code {
+ TR_OK => TestResult::TrOk,
+ TR_FAILED => TestResult::TrFailed,
+ _ => TestResult::TrFailedMsg(format!("got unexpected return code {}", code)),
};
// If test is already failed (or allowed to fail), do not change the result.
name: StaticTestName("1"),
ignore: true,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(move || {})),
},
name: StaticTestName("2"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(move || {})),
},
name: StaticTestName("whatever"),
ignore: true,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: true,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::Yes,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::YesWithMessage("error message"),
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::YesWithMessage(expected),
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::YesWithMessage(expected),
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(f)),
};
name: StaticTestName("whatever"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type,
+ #[cfg(bootstrap)]
+ allow_fail: false,
}
}
let options = TestTimeOptions {
error_on_excess: false,
- colored: false,
unit_threshold: unit.clone(),
integration_threshold: integration.clone(),
doctest_threshold: doc.clone(),
name: StaticTestName("3"),
ignore: false,
should_panic: ShouldPanic::Yes,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(move || {})),
});
name: StaticTestName(name),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(move || {})),
})
name: DynTestName((*name).clone()),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: DynTestFn(Box::new(testfn)),
};
name: StaticTestName("f"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
};
crate::bench::benchmark(TestId(0), desc, tx, true, f);
name: StaticTestName("f"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
};
crate::bench::benchmark(TestId(0), desc, tx, true, f);
name: StaticTestName("a"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
};
let test_b = TestDesc {
name: StaticTestName("b"),
ignore: false,
should_panic: ShouldPanic::No,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
};
let mut out = PrettyFormatter::new(OutputLocation::Raw(Vec::new()), false, 10, false, None);
passed: 0,
failed: 0,
ignored: 0,
- allowed_fail: 0,
filtered_out: 0,
measured: 0,
exec_time: None,
/// Denotes if the test critical execution time limit excess should be considered
/// a test failure.
pub error_on_excess: bool,
- pub colored: bool,
pub unit_threshold: TimeThreshold,
pub integration_threshold: TimeThreshold,
pub doctest_threshold: TimeThreshold,
}
impl TestTimeOptions {
- pub fn new_from_env(error_on_excess: bool, colored: bool) -> Self {
+ pub fn new_from_env(error_on_excess: bool) -> Self {
let unit_threshold = TimeThreshold::from_env_var(time_constants::UNIT_ENV_NAME)
.unwrap_or_else(Self::default_unit);
let doctest_threshold = TimeThreshold::from_env_var(time_constants::DOCTEST_ENV_NAME)
.unwrap_or_else(Self::default_doctest);
- Self { error_on_excess, colored, unit_threshold, integration_threshold, doctest_threshold }
+ Self { error_on_excess, unit_threshold, integration_threshold, doctest_threshold }
}
pub fn is_warn(&self, test: &TestDesc, exec_time: &TestExecTime) -> bool {
pub name: TestName,
pub ignore: bool,
pub should_panic: options::ShouldPanic,
- pub allow_fail: bool,
pub compile_fail: bool,
pub no_run: bool,
pub test_type: TestType,
+ #[cfg(bootstrap)]
+ pub allow_fail: bool,
}
impl TestDesc {
}
options::ShouldPanic::No => {}
}
- if self.allow_fail {
- return Some("allow fail");
- }
if self.compile_fail {
return Some("compile fail");
}
#[cfg(all(target_arch = "aarch64", target_pointer_width = "32"))]
pub const unwinder_private_data_size: usize = 5;
+#[cfg(target_arch = "m68k")]
+pub const unwinder_private_data_size: usize = 2;
+
#[cfg(target_arch = "mips")]
pub const unwinder_private_data_size: usize = 2;
serde = { version = "1.0.8", features = ["derive"] }
serde_json = "1.0.2"
toml = "0.5"
-time = "0.1"
ignore = "0.4.10"
opener = "0.5"
once_cell = "1.7.2"
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3"
-features = ["fileapi", "ioapiset", "jobapi2", "handleapi", "winioctl", "psapi", "impl-default"]
+features = [
+ "fileapi",
+ "ioapiset",
+ "jobapi2",
+ "handleapi",
+ "winioctl",
+ "psapi",
+ "impl-default",
+ "timezoneapi",
+]
[dev-dependencies]
-pretty_assertions = "0.6"
+pretty_assertions = "0.7"
println!("{}", suggestion);
}
+ let pre_commit = config.src.join(".git").join("hooks").join("pre-commit");
Build::new(config).build();
if suggest_setup {
println!("{}", suggestion);
}
+ // Give a warning if the pre-commit script is in pre-commit and not pre-push.
+ // HACK: Since the commit script uses hard links, we can't actually tell if it was installed by x.py setup or not.
+ // We could see if it's identical to src/etc/pre-push.sh, but pre-push may have been modified in the meantime.
+ // Instead, look for this comment, which is almost certainly not in any custom hook.
+ if std::fs::read_to_string(pre_commit).map_or(false, |contents| {
+ contents.contains("https://github.com/rust-lang/rust/issues/77620#issuecomment-705144570")
+ }) {
+ println!(
+ "warning: You have the pre-push script installed to .git/hooks/pre-commit. \
+ Consider moving it to .git/hooks/pre-push instead, which runs less often."
+ );
+ }
+
if suggest_setup || changelog_suggestion.is_some() {
println!("note: this message was printed twice to make it more likely to be seen");
}
except tarfile.CompressionError:
return False
-def get(base, url, path, checksums, verbose=False, do_verify=True):
+def get(base, url, path, checksums, verbose=False, do_verify=True, help_on_error=None):
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
temp_path = temp_file.name
print("ignoring already-download file",
path, "due to failed verification")
os.unlink(path)
- download(temp_path, "{}/{}".format(base, url), True, verbose)
+ download(temp_path, "{}/{}".format(base, url), True, verbose, help_on_error=help_on_error)
if do_verify and not verify(temp_path, sha256, verbose):
raise RuntimeError("failed verification")
if verbose:
os.unlink(temp_path)
-def download(path, url, probably_big, verbose):
+def download(path, url, probably_big, verbose, help_on_error=None):
for _ in range(0, 4):
try:
- _download(path, url, probably_big, verbose, True)
+ _download(path, url, probably_big, verbose, True, help_on_error=help_on_error)
return
except RuntimeError:
print("\nspurious failure, trying again")
- _download(path, url, probably_big, verbose, False)
+ _download(path, url, probably_big, verbose, False, help_on_error=help_on_error)
-def _download(path, url, probably_big, verbose, exception):
+def _download(path, url, probably_big, verbose, exception, help_on_error=None):
if probably_big or verbose:
print("downloading {}".format(url))
# see https://serverfault.com/questions/301128/how-to-download
"--connect-timeout", "30", # timeout if cannot connect within 30 seconds
"--retry", "3", "-Sf", "-o", path, url],
verbose=verbose,
- exception=exception)
+ exception=exception,
+ help_on_error=help_on_error)
def verify(path, expected, verbose):
shutil.rmtree(os.path.join(dst, fname))
-def run(args, verbose=False, exception=False, is_bootstrap=False, **kwargs):
+def run(args, verbose=False, exception=False, is_bootstrap=False, help_on_error=None, **kwargs):
"""Run a child program in a new process"""
if verbose:
print("running: " + ' '.join(args))
code = ret.wait()
if code != 0:
err = "failed to run: " + ' '.join(args)
+ if help_on_error is not None:
+ err += "\n" + help_on_error
if verbose or exception:
raise RuntimeError(err)
# For most failures, we definitely do want to print this error, or the user will have no
filename = "rust-dev-nightly-" + self.build + tarball_suffix
tarball = os.path.join(rustc_cache, filename)
if not os.path.exists(tarball):
+ help_on_error = "error: failed to download llvm from ci"
+ help_on_error += "\nhelp: old builds get deleted after a certain time"
+ help_on_error += "\nhelp: if trying to compile an old commit of rustc,"
+ help_on_error += " disable `download-ci-llvm` in config.toml:"
+ help_on_error += "\n"
+ help_on_error += "\n[llvm]"
+ help_on_error += "\ndownload-ci-llvm = false"
+ help_on_error += "\n"
get(
base,
"{}/{}".format(url, filename),
self.checksums_sha256,
verbose=self.verbose,
do_verify=False,
+ help_on_error=help_on_error,
)
unpack(tarball, tarball_suffix, self.llvm_root(),
match="rust-dev",
build.verbose = args.verbose
build.clean = args.clean
- # Read from `RUST_BOOTSTRAP_CONFIG`, then `--config`, then fallback to `config.toml` (if it
+ # Read from `--config`, then `RUST_BOOTSTRAP_CONFIG`, then fallback to `config.toml` (if it
# exists).
- toml_path = os.getenv('RUST_BOOTSTRAP_CONFIG') or args.config
+ toml_path = args.config or os.getenv('RUST_BOOTSTRAP_CONFIG')
if not toml_path and os.path.exists('config.toml'):
toml_path = 'config.toml'
fn main() {
println!("cargo:rerun-if-changed=build.rs");
+ println!("cargo:rerun-if-env-changed=RUSTC");
+ println!("cargo:rerun-if-env-changed=PATH");
println!("cargo:rustc-env=BUILD_TRIPLE={}", env::var("HOST").unwrap());
// This may not be a canonicalized path.
pub llvm_polly: bool,
pub llvm_clang: bool,
pub llvm_from_ci: bool,
+ pub llvm_build_config: HashMap<String, String>,
pub use_lld: bool,
pub lld_enabled: bool,
polly: Option<bool>,
clang: Option<bool>,
download_ci_llvm: Option<StringOrBool>,
+ build_config: Option<HashMap<String, String>>,
}
}
config.llvm_allow_old_toolchain = llvm.allow_old_toolchain.unwrap_or(false);
config.llvm_polly = llvm.polly.unwrap_or(false);
config.llvm_clang = llvm.clang.unwrap_or(false);
+ config.llvm_build_config = llvm.build_config.clone().unwrap_or(Default::default());
config.llvm_from_ci = match llvm.download_ci_llvm {
Some(StringOrBool::String(s)) => {
assert!(s == "if-available", "unknown option `{}` for download-ci-llvm", s);
check_ci_llvm!(llvm.allow_old_toolchain);
check_ci_llvm!(llvm.polly);
check_ci_llvm!(llvm.clang);
+ check_ci_llvm!(llvm.build_config);
check_ci_llvm!(llvm.plugins);
// CI-built LLVM can be either dynamic or static.
use crate::tool::{self, Tool};
use crate::util::{exe, is_dylib, timeit};
use crate::{Compiler, DependencyType, Mode, LLVM_TOOLS};
-use time::{self, Timespec};
pub fn pkgname(builder: &Builder<'_>, component: &str) -> String {
format!("{}-{}", component, builder.rust_package_vers())
let man_src = builder.src.join("src/doc/man");
let man_dst = image.join("share/man/man1");
- // Reproducible builds: If SOURCE_DATE_EPOCH is set, use that as the time.
- let time = env::var("SOURCE_DATE_EPOCH")
- .map(|timestamp| {
- let epoch = timestamp
- .parse()
- .map_err(|err| format!("could not parse SOURCE_DATE_EPOCH: {}", err))
- .unwrap();
-
- time::at(Timespec::new(epoch, 0))
- })
- .unwrap_or_else(|_| time::now());
-
- let month_year = t!(time::strftime("%B %Y", &time));
// don't use our `bootstrap::util::{copy, cp_r}`, because those try
// to hardlink, and we don't want to edit the source templates
for file_entry in builder.read_dir(&man_src) {
let page_src = file_entry.path();
let page_dst = man_dst.join(file_entry.file_name());
+ let src_text = t!(std::fs::read_to_string(&page_src));
+ let new_text = src_text.replace("<INSERT VERSION HERE>", &builder.version);
+ t!(std::fs::write(&page_dst, &new_text));
t!(fs::copy(&page_src, &page_dst));
- // template in month/year and version number
- builder.replace_in_file(
- &page_dst,
- &[
- ("<INSERT DATE HERE>", &month_year),
- ("<INSERT VERSION HERE>", &builder.version),
- ],
- );
}
// Debugger scripts
"llvm-project\\llvm",
"llvm-project/compiler-rt",
"llvm-project\\compiler-rt",
+ "llvm-project/cmake",
+ "llvm-project\\cmake",
];
if spath.contains("llvm-project")
&& !spath.ends_with("llvm-project")
} else {
cmd.env("CFG_MINGW", "0").env("CFG_ABI", "MSVC");
}
-
- if target.contains("x86_64") {
- cmd.env("CFG_PLATFORM", "x64");
- } else {
- cmd.env("CFG_PLATFORM", "x86");
- }
}
/// Maybe add LLVM object files to the given destination lib-dir. Allows either static or dynamic linking.
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, HashSet};
use std::env;
-use std::fs::{self, File, OpenOptions};
-use std::io::{Read, Seek, SeekFrom, Write};
+use std::fs::{self, File};
use std::path::{Path, PathBuf};
use std::process::{self, Command};
use std::str;
// Try passing `--progress` to start, then run git again without if that fails.
let update = |progress: bool| {
let mut git = Command::new("git");
- git.args(&["submodule", "update", "--init", "--recursive"]);
+ git.args(&["submodule", "update", "--init", "--recursive", "--depth=1"]);
if progress {
git.arg("--progress");
}
}
}
- /// Search-and-replaces within a file. (Not maximally efficiently: allocates a
- /// new string for each replacement.)
- pub fn replace_in_file(&self, path: &Path, replacements: &[(&str, &str)]) {
- if self.config.dry_run {
- return;
- }
- let mut contents = String::new();
- let mut file = t!(OpenOptions::new().read(true).write(true).open(path));
- t!(file.read_to_string(&mut contents));
- for &(target, replacement) in replacements {
- contents = contents.replace(target, replacement);
- }
- t!(file.seek(SeekFrom::Start(0)));
- t!(file.set_len(0));
- t!(file.write_all(contents.as_bytes()));
- }
-
/// Copies the `src` directory recursively to `dst`. Both are assumed to exist
/// when this function is called.
pub fn cp_r(&self, src: &Path, dst: &Path) {
root: String,
}
+// Linker flags to pass to LLVM's CMake invocation.
+#[derive(Debug, Clone, Default)]
+struct LdFlags {
+ // CMAKE_EXE_LINKER_FLAGS
+ exe: OsString,
+ // CMAKE_SHARED_LINKER_FLAGS
+ shared: OsString,
+ // CMAKE_MODULE_LINKER_FLAGS
+ module: OsString,
+}
+
+impl LdFlags {
+ fn push_all(&mut self, s: impl AsRef<OsStr>) {
+ let s = s.as_ref();
+ self.exe.push(" ");
+ self.exe.push(s);
+ self.shared.push(" ");
+ self.shared.push(s);
+ self.module.push(" ");
+ self.module.push(s);
+ }
+}
+
// This returns whether we've already previously built LLVM.
//
// It's used to avoid busting caches during x.py check -- if we've already built
// https://llvm.org/docs/CMake.html
let mut cfg = cmake::Config::new(builder.src.join(root));
+ let mut ldflags = LdFlags::default();
let profile = match (builder.config.llvm_optimize, builder.config.llvm_release_debuginfo) {
(false, _) => "Debug",
cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
}
- // For distribution we want the LLVM tools to be *statically* linked to libstdc++
- if builder.config.llvm_tools_enabled {
- if !target.contains("msvc") {
+ // For distribution we want the LLVM tools to be *statically* linked to libstdc++.
+ // We also do this if the user explicitly requested static libstdc++.
+ if builder.config.llvm_tools_enabled || builder.config.llvm_static_stdcpp {
+ if !target.contains("msvc") && !target.contains("netbsd") {
if target.contains("apple") {
- cfg.define("CMAKE_EXE_LINKER_FLAGS", "-static-libstdc++");
+ ldflags.push_all("-static-libstdc++");
} else {
- cfg.define("CMAKE_EXE_LINKER_FLAGS", "-Wl,-Bsymbolic -static-libstdc++");
+ ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++");
}
}
}
- if !target.contains("freebsd") && target.starts_with("riscv") {
+ if target.starts_with("riscv") && !target.contains("freebsd") {
// RISC-V GCC erroneously requires linking against
// `libatomic` when using 1-byte and 2-byte C++
// atomics but the LLVM build system check cannot
// FreeBSD uses Clang as its system compiler and
// provides no libatomic in its base system so does
// not want this.
- if !builder.config.llvm_tools_enabled {
- cfg.define("CMAKE_EXE_LINKER_FLAGS", "-latomic");
- } else {
- cfg.define("CMAKE_EXE_LINKER_FLAGS", "-latomic -static-libstdc++");
- }
- cfg.define("CMAKE_SHARED_LINKER_FLAGS", "-latomic");
+ ldflags.exe.push(" -latomic");
+ ldflags.shared.push(" -latomic");
}
if target.contains("msvc") {
// Workaround for ppc32 lld limitation
if target == "powerpc-unknown-freebsd" {
- cfg.define("CMAKE_EXE_LINKER_FLAGS", "-fuse-ld=bfd");
+ ldflags.exe.push(" -fuse-ld=bfd");
}
// https://llvm.org/docs/HowToCrossCompileLLVM.html
cfg.define("LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN", "YES");
}
- configure_cmake(builder, target, &mut cfg, true);
+ configure_cmake(builder, target, &mut cfg, true, ldflags);
+
+ for (key, val) in &builder.config.llvm_build_config {
+ cfg.define(key, val);
+ }
// FIXME: we don't actually need to build all LLVM tools and all LLVM
// libraries here, e.g., we just want a few components and a few
target: TargetSelection,
cfg: &mut cmake::Config,
use_compiler_launcher: bool,
+ mut ldflags: LdFlags,
) {
// Do not print installation messages for up-to-date files.
// LLVM and LLD builds can produce a lot of those and hit CI limits on log size.
}
cfg.build_arg("-j").build_arg(builder.jobs().to_string());
- let mut cflags = builder.cflags(target, GitRepo::Llvm).join(" ");
+ let mut cflags: OsString = builder.cflags(target, GitRepo::Llvm).join(" ").into();
if let Some(ref s) = builder.config.llvm_cflags {
- cflags.push_str(&format!(" {}", s));
+ cflags.push(" ");
+ cflags.push(s);
}
// Some compiler features used by LLVM (such as thread locals) will not work on a min version below iOS 10.
if target.contains("apple-ios") {
if target.contains("86-") {
- cflags.push_str(" -miphonesimulator-version-min=10.0");
+ cflags.push(" -miphonesimulator-version-min=10.0");
} else {
- cflags.push_str(" -miphoneos-version-min=10.0");
+ cflags.push(" -miphoneos-version-min=10.0");
}
}
if builder.config.llvm_clang_cl.is_some() {
- cflags.push_str(&format!(" --target={}", target))
+ cflags.push(&format!(" --target={}", target));
}
- cfg.define("CMAKE_C_FLAGS", cflags);
- let mut cxxflags = builder.cflags(target, GitRepo::Llvm).join(" ");
- if builder.config.llvm_static_stdcpp && !target.contains("msvc") && !target.contains("netbsd") {
- cxxflags.push_str(" -static-libstdc++");
+ if let Some(flags) = env::var_os("CFLAGS") {
+ cflags.push(" ");
+ cflags.push(flags);
}
+ cfg.define("CMAKE_C_FLAGS", cflags);
+ let mut cxxflags: OsString = builder.cflags(target, GitRepo::Llvm).join(" ").into();
if let Some(ref s) = builder.config.llvm_cxxflags {
- cxxflags.push_str(&format!(" {}", s));
+ cxxflags.push(" ");
+ cxxflags.push(s);
}
if builder.config.llvm_clang_cl.is_some() {
- cxxflags.push_str(&format!(" --target={}", target))
+ cxxflags.push(&format!(" --target={}", target));
+ }
+ if let Some(flags) = env::var_os("CXXFLAGS") {
+ cxxflags.push(" ");
+ cxxflags.push(flags);
}
cfg.define("CMAKE_CXX_FLAGS", cxxflags);
if let Some(ar) = builder.ar(target) {
}
}
- if let Some(ref s) = builder.config.llvm_ldflags {
- cfg.define("CMAKE_SHARED_LINKER_FLAGS", s);
- cfg.define("CMAKE_MODULE_LINKER_FLAGS", s);
- cfg.define("CMAKE_EXE_LINKER_FLAGS", s);
+ if let Some(ref flags) = builder.config.llvm_ldflags {
+ ldflags.push_all(flags);
}
+ if let Some(flags) = env::var_os("LDFLAGS") {
+ ldflags.push_all(&flags);
+ }
+
+ cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared);
+ cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module);
+ cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe);
+
if env::var_os("SCCACHE_ERROR_LOG").is_some() {
cfg.env("RUSTC_LOG", "sccache=warn");
}
t!(fs::create_dir_all(&out_dir));
let mut cfg = cmake::Config::new(builder.src.join("src/llvm-project/lld"));
- configure_cmake(builder, target, &mut cfg, true);
+ configure_cmake(builder, target, &mut cfg, true, LdFlags::default());
// 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
// Unfortunately sccache currently lacks support to build them successfully.
// Disable compiler launcher on Darwin targets to avoid potential issues.
let use_compiler_launcher = !self.target.contains("apple-darwin");
- configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher);
+ configure_cmake(builder, self.target, &mut cfg, use_compiler_launcher, LdFlags::default());
t!(fs::create_dir_all(&out_dir));
cfg.out_dir(out_dir);
use crate::TargetSelection;
use crate::{t, VERSION};
+use std::env::consts::EXE_SUFFIX;
use std::fmt::Write as _;
-use std::path::{Path, PathBuf};
+use std::fs::File;
+use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::process::Command;
use std::str::FromStr;
use std::{
println!("`x.py` will now use the configuration at {}", include_path.display());
let build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
- let stage_path = ["build", build.rustc_target_arg(), "stage1"].join("/");
+ let stage_path =
+ ["build", build.rustc_target_arg(), "stage1"].join(&MAIN_SEPARATOR.to_string());
println!();
return;
}
+ if !ensure_stage1_toolchain_placeholder_exists(stage_path) {
+ println!(
+ "Failed to create a template for stage 1 toolchain or confirm that it already exists"
+ );
+ return;
+ }
+
if try_link_toolchain(&stage_path[..]) {
println!(
"Added `stage1` rustup toolchain; try `cargo +stage1 build` on a separate rust project to run a newly-built toolchain"
.map_or(false, |output| output.status.success())
}
+fn ensure_stage1_toolchain_placeholder_exists(stage_path: &str) -> bool {
+ let pathbuf = PathBuf::from(stage_path);
+
+ if fs::create_dir_all(pathbuf.join("lib")).is_err() {
+ return false;
+ };
+
+ let pathbuf = pathbuf.join("bin");
+ if fs::create_dir_all(&pathbuf).is_err() {
+ return false;
+ };
+
+ let pathbuf = pathbuf.join(format!("rustc{}", EXE_SUFFIX));
+
+ if pathbuf.exists() {
+ return true;
+ }
+
+ // Take care not to overwrite the file
+ let result = File::options().append(true).create(true).open(&pathbuf);
+ if result.is_err() {
+ return false;
+ }
+
+ return true;
+}
+
// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
fn abbrev_all() -> impl Iterator<Item = ((String, String), Profile)> {
let mut input = String::new();
println!(
"Rust's CI will automatically fail if it doesn't pass `tidy`, the internal tool for ensuring code quality.
-If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` on each commit
-to ensure your code is up to par. If you decide later that this behavior is undesirable,
-simply delete the `pre-commit` file from .git/hooks."
+If you'd like, x.py can install a git hook for you that will automatically run `tidy --bless` before
+pushing your code to ensure your code is up to par. If you decide later that this behavior is
+undesirable, simply delete the `pre-push` file from .git/hooks."
);
let should_install = loop {
};
if should_install {
- let src = src_path.join("src").join("etc").join("pre-commit.sh");
+ let src = src_path.join("src").join("etc").join("pre-push.sh");
let git = t!(Command::new("git").args(&["rev-parse", "--git-common-dir"]).output().map(
|output| {
assert!(output.status.success(), "failed to run `git`");
PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
}
));
- let dst = git.join("hooks").join("pre-commit");
+ let dst = git.join("hooks").join("pre-push");
match fs::hard_link(src, &dst) {
Err(e) => println!(
"error: could not create hook {}: do you already have the git hook installed?\n{}",
dst.display(),
e
),
- Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-commit`"),
+ Ok(_) => println!("Linked `src/etc/pre-commit.sh` to `.git/hooks/pre-push`"),
};
} else {
println!("Ok, skipping installation!");
from ctypes import *
libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
- PROESSOR_CPU_LOAD_INFO = c_int(2)
+ class host_cpu_load_info_data_t(Structure):
+ _fields_ = [("cpu_ticks", c_uint * 4)]
+
+ host_statistics = libc.host_statistics
+ host_statistics.argtypes = [
+ c_uint,
+ c_int,
+ POINTER(host_cpu_load_info_data_t),
+ POINTER(c_int)
+ ]
+ host_statistics.restype = c_int
+
CPU_STATE_USER = 0
CPU_STATE_SYSTEM = 1
CPU_STATE_IDLE = 2
CPU_STATE_NICE = 3
- c_int_p = POINTER(c_int)
-
class State:
def __init__(self):
- num_cpus_u = c_uint(0)
- cpu_info = c_int_p()
- cpu_info_cnt = c_int(0)
- err = libc.host_processor_info(
+ stats = host_cpu_load_info_data_t()
+ count = c_int(4) # HOST_CPU_LOAD_INFO_COUNT
+ err = libc.host_statistics(
libc.mach_host_self(),
- PROESSOR_CPU_LOAD_INFO,
- byref(num_cpus_u),
- byref(cpu_info),
- byref(cpu_info_cnt),
+ c_int(3), # HOST_CPU_LOAD_INFO
+ byref(stats),
+ byref(count),
)
assert err == 0
- self.user = 0
- self.system = 0
- self.idle = 0
- self.nice = 0
- cur = 0
- while cur < cpu_info_cnt.value:
- self.user += cpu_info[cur + CPU_STATE_USER]
- self.system += cpu_info[cur + CPU_STATE_SYSTEM]
- self.idle += cpu_info[cur + CPU_STATE_IDLE]
- self.nice += cpu_info[cur + CPU_STATE_NICE]
- cur += num_cpus_u.value
+ self.system = stats.cpu_ticks[CPU_STATE_SYSTEM]
+ self.user = stats.cpu_ticks[CPU_STATE_USER]
+ self.idle = stats.cpu_ticks[CPU_STATE_IDLE]
+ self.nice = stats.cpu_ticks[CPU_STATE_NICE]
def idle_since(self, prev):
user = self.user - prev.user
FROM ubuntu:20.04
-RUN apt-get update && \
- apt-get install -y --no-install-recommends \
- curl \
- ca-certificates
-WORKDIR /tmp
-RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
-
-FROM ubuntu:16.04
-
-# The ca-certificates in ubuntu-16 is too old, so update the certificates
-# with something more recent.
-COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
-ENV CURL_CA_BUNDLE /tmp/cacert.pem
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
FROM ubuntu:20.04
-RUN apt-get update && \
- apt-get install -y --no-install-recommends \
- curl \
- ca-certificates
-WORKDIR /tmp
-RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
-
-FROM ubuntu:16.04
-
-# The ca-certificates in ubuntu-16 is too old, so update the certificates
-# with something more recent.
-COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
-ENV CURL_CA_BUNDLE /tmp/cacert.pem
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
FROM ubuntu:20.04
-RUN apt-get update && \
- apt-get install -y --no-install-recommends \
- curl \
- ca-certificates
-WORKDIR /tmp
-RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
-
-FROM ubuntu:16.04
-
-# The ca-certificates in ubuntu-16 is too old, so update the certificates
-# with something more recent.
-COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
-ENV CURL_CA_BUNDLE /tmp/cacert.pem
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
-FROM ubuntu:16.04
+FROM ubuntu:20.04
-RUN apt-get update && apt-get install -y --no-install-recommends \
+RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
g++ \
make \
ninja-build \
# https://github.com/puppeteer/puppeteer/issues/375
#
# We also specify the version in case we need to update it to go around cache limitations.
-RUN npm install -g browser-ui-test@0.5.8 --unsafe-perm=true
+RUN npm install -g browser-ui-test@0.7.2 --unsafe-perm=true
ENV RUST_CONFIGURE_ARGS \
--build=x86_64-unknown-linux-gnu \
NO_OVERFLOW_CHECKS: 1
<<: *job-macos-xl
- - name: x86_64-apple
- env:
- SCRIPT: ./x.py --stage 2 test
+ - name: x86_64-apple-1
+ env: &env-x86_64-apple-tests
+ SCRIPT: ./x.py --stage 2 test --exclude src/test/ui --exclude src/test/rustdoc --exclude src/test/run-make-fulldeps
RUST_CONFIGURE_ARGS: --build=x86_64-apple-darwin --enable-sanitizers --enable-profiler --set rust.jemalloc --set llvm.ninja=false
RUSTC_RETRY_LINKER_ON_SEGFAULT: 1
MACOSX_DEPLOYMENT_TARGET: 10.8
NO_OVERFLOW_CHECKS: 1
<<: *job-macos-xl
+ - name: x86_64-apple-2
+ env:
+ SCRIPT: ./x.py --stage 2 test src/test/ui src/test/rustdoc src/test/run-make-fulldeps
+ <<: *env-x86_64-apple-tests
+ <<: *job-macos-xl
+
# This target only needs to support 11.0 and up as nothing else supports the hardware
- name: dist-aarch64-apple
env:
--llvm-profile-generate
# Profile libcore compilation in opt-level=0 and opt-level=3
-RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \
- --crate-type=lib ../library/core/src/lib.rs
-RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \
- --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs
+RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
+ --edition=2021 --crate-type=lib ../library/core/src/lib.rs
+RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
+ --edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs
# Merge the profile data we gathered for LLVM
# Note that this uses the profdata from the clang we used to build LLVM,
--rust-profile-generate=/tmp/rustc-pgo
# Profile libcore compilation in opt-level=0 and opt-level=3
-RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \
- --crate-type=lib ../library/core/src/lib.rs
-RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc --edition=2018 \
- --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs
+RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
+ --edition=2021 --crate-type=lib ../library/core/src/lib.rs
+RUSTC_BOOTSTRAP=1 ./build/$PGO_HOST/stage2/bin/rustc \
+ --edition=2021 --crate-type=lib -Copt-level=3 ../library/core/src/lib.rs
cp -r /tmp/rustc-perf ./
chown -R $(whoami): ./rustc-perf
-Subproject commit 98904efaa4fc968db8ff59cf2744d9f7ed158166
+Subproject commit 67b768c0b660a069a45f0e5d8ae2f679df1022ab
-.TH RUSTC "1" "<INSERT DATE HERE>" "rustc <INSERT VERSION HERE>" "User Commands"
+.TH RUSTC "1" "April 2019" "rustc <INSERT VERSION HERE>" "User Commands"
.SH NAME
rustc \- The Rust compiler
.SH SYNOPSIS
-.TH RUSTDOC "1" "<INSERT DATE HERE>" "rustdoc <INSERT VERSION HERE>" "User Commands"
+.TH RUSTDOC "1" "July 2018" "rustdoc <INSERT VERSION HERE>" "User Commands"
.SH NAME
rustdoc \- generate documentation from Rust source code
.SH SYNOPSIS
-Subproject commit 9493715a6280a1f74be759c7e1ef9999b5d13e6f
+Subproject commit 90993eeac93dbf9388992de92965f99cf6f29a03
-Subproject commit 411c2f0d5cebf48453ae2d136ad0c5e611d39aec
+Subproject commit 70fc73a6b908e08e66aa0306856c5211312f6c05
-Subproject commit 8763adb62c712df69b1d39ea3e692b6d696cc4d9
+Subproject commit 62f58394ba7b203f55ac35ddcc4c0b79578f5706
- [Platform Support](platform-support.md)
- [Template for target-specific documentation](platform-support/TEMPLATE.md)
- [aarch64-apple-ios-sim](platform-support/aarch64-apple-ios-sim.md)
+ - [armv7-unknown-linux-uclibceabi](platform-support/armv7-unknown-linux-uclibceabi.md)
- [armv7-unknown-linux-uclibceabihf](platform-support/armv7-unknown-linux-uclibceabihf.md)
+ - [aarch64-unknown-none-hermitkernel](platform-support/aarch64-unknown-none-hermitkernel.md)
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
- [*-unknown-openbsd](platform-support/openbsd.md)
- [x86_64-unknown-none](platform-support/x86_64-unknown-none.md)
- [Custom Targets](targets/custom.md)
- [Known Issues](targets/known-issues.md)
- [Profile-guided Optimization](profile-guided-optimization.md)
+- [Instrumentation-based Code Coverage](instrument-coverage.md)
- [Linker-plugin based LTO](linker-plugin-lto.md)
- [Exploit Mitigations](exploit-mitigations.md)
- [Contributing to `rustc`](contributing.md)
| s | 75 |
| z | 25 |
+## instrument-coverage
+
+This option enables instrumentation-based code coverage support. See the
+chapter on [instrumentation-based code coverage] for more information.
+
+Note that while the `-C instrument-coverage` option is stable, the profile data
+format produced by the resulting instrumentation may change, and may not work
+with coverage tools other than those built and shipped with the compiler.
+
## link-arg
This flag lets you append a single extra argument to the linker invocation.
[option-emit]: ../command-line-arguments.md#option-emit
[option-o-optimize]: ../command-line-arguments.md#option-o-optimize
+[instrumentation-based code coverage]: ../instrument-coverage.md
[profile-guided optimization]: ../profile-guided-optimization.md
[option-g-debug]: ../command-line-arguments.md#option-g-debug
--- /dev/null
+# `instrument-coverage`
+
+## Introduction
+
+The Rust compiler includes two code coverage implementations:
+
+- A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo.
+- A source-based code coverage implementation, enabled with `-C instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data.
+
+This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-C instrument-coverage` compiler flag.
+
+## How it works
+
+When `-C instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:
+
+- Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed.
+- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, if compiling with LLVM 12, or _Version 6_, if compiling with LLVM 13 or higher), to define the code regions (start and end positions in the source code) being counted.
+
+When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats.
+
+[`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic
+[llvm code coverage mapping format]: https://llvm.org/docs/CoverageMappingFormat.html
+
+> **Note**: `-C instrument-coverage` also automatically enables `-C symbol-mangling-version=v0` (tracking issue [#60705]). The `v0` symbol mangler is strongly recommended. The `v0` demangler can be overridden by explicitly adding `-Z unstable-options -C symbol-mangling-version=legacy`.
+
+[#60705]: https://github.com/rust-lang/rust/issues/60705
+
+## Enable coverage profiling in the Rust compiler
+
+Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-C instrument-coverage` generates an error that the profiler runtime is missing.
+
+The Rust `nightly` distribution channel includes the profiler runtime, by default.
+
+> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
+>
+> ```toml
+> # Build the profiler runtime (required when compiling with options that depend
+> # on this runtime, such as `-C profile-generate` or `-C instrument-coverage`).
+> profiler = true
+> ```
+
+### Building the demangler
+
+LLVM coverage reporting tools generate results that can include function names and other symbol references, and the raw coverage results report symbols using the compiler's "mangled" version of the symbol names, which can be difficult to interpret. To work around this issue, LLVM coverage tools also support a user-specified symbol name demangler.
+
+One option for a Rust demangler is [`rustfilt`], which can be installed with:
+
+```shell
+cargo install rustfilt
+```
+
+Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with:
+
+```shell
+$ ./x.py build rust-demangler
+```
+
+[`rustfilt`]: https://crates.io/crates/rustfilt
+
+## Compiling with coverage enabled
+
+Set the `-C instrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling.
+
+The default option generates coverage for all functions, including unused (never called) functions and generics. The compiler flag supports an optional value to tailor this behavior. (See [`-C instrument-coverage=<options>`](#-c-instrument-coverageoptions), below.)
+
+With `cargo`, you can instrument your program binary _and_ dependencies at the same time.
+
+For example (if your project's Cargo.toml builds a binary by default):
+
+```shell
+$ cd your-project
+$ cargo clean
+$ RUSTFLAGS="-C instrument-coverage" cargo build
+```
+
+If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`] crate):
+
+```shell
+$ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \
+ RUSTFLAGS="-C instrument-coverage" \
+ cargo build --example formatjson5
+```
+
+> **Note**: that some compiler options, combined with `-C instrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect.
+
+## Running the instrumented binary to generate raw coverage profiling data
+
+In the previous example, `cargo` generated the coverage-instrumented binary `formatjson5`:
+
+```shell
+$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
+```
+
+```json5
+{
+ some: "thing",
+}
+```
+
+After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`:
+
+```shell
+$ echo "{some: 'thing'}" \
+ | LLVM_PROFILE_FILE="formatjson5.profraw" target/debug/examples/formatjson5 -
+...
+$ ls formatjson5.profraw
+formatjson5.profraw
+```
+
+If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten:
+
+- `%p` - The process ID.
+- `%h` - The hostname of the machine running the program.
+- `%t` - The value of the TMPDIR environment variable.
+- `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`).
+- `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered.
+
+## Installing LLVM coverage tools
+
+LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher, and processing the *raw* data may require exactly the LLVM version used by the compiler. (`llvm-cov --version` typically shows the tool's LLVM version number, and `rustc --verbose --version` shows the version of LLVM used by the Rust compiler.)
+
+- You can install compatible versions of these tools via the `rustup` component `llvm-tools-preview`. This component is the recommended path, though the specific tools available and their interface is not currently subject to Rust's usual stability guarantees. In this case, you may also find `cargo-binutils` useful as a wrapper around these tools.
+- You can install a compatible version of LLVM tools from your operating system distribution, or from your distribution of LLVM.
+- If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`.
+
+The examples in this document show how to use the llvm tools directly.
+
+## Creating coverage reports
+
+Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`], which can combine multiple raw profiles and index them at the same time:
+
+```shell
+$ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata
+```
+
+Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`], for a coverage summaries; and [`llvm-cov show`], to see detailed coverage of lines and regions (character ranges) overlaid on the original source code.
+
+These commands have several display and filtering options. For example:
+
+```shell
+$ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \
+ -instr-profile=formatjson5.profdata \
+ -show-line-counts-or-regions \
+ -show-instantiations \
+ -name=add_quoted_string
+```
+
+<img alt="Screenshot of sample `llvm-cov show` result, for function add_quoted_string" src="img/llvm-cov-show-01.png" class="center"/>
+<br/>
+<br/>
+
+Some of the more notable options in this example include:
+
+- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool)
+- `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map)
+- `--instr-profile=<path-to-file>.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary)
+- `--name=<exact-function-name>` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=<pattern>`)
+
+[`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge
+[`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report
+[`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show
+
+> **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`no_coverage` attribute] (which requires the feature flag `#![feature(no_coverage)]`).
+
+[`no_coverage` attribute]: ../unstable-book/language-features/no-coverage.html
+
+## Interpreting reports
+
+There are four statistics tracked in a coverage summary:
+
+- Function coverage is the percentage of functions that have been executed at least once. A function is considered to be executed if any of its instantiations are executed.
+- Instantiation coverage is the percentage of function instantiations that have been executed at least once. Generic functions and functions generated from macros are two kinds of functions that may have multiple instantiations.
+- Line coverage is the percentage of code lines that have been executed at least once. Only executable lines within function bodies are considered to be code lines.
+- Region coverage is the percentage of code regions that have been executed at least once. A code region may span multiple lines: for example, in a large function body with no control flow. In other cases, a single line can contain multiple code regions: `return x || (y && z)` has countable code regions for `x` (which may resolve the expression, if `x` is `true`), `|| (y && z)` (executed only if `x` was `false`), and `return` (executed in either situation).
+
+Of these four statistics, function coverage is usually the least granular while region coverage is the most granular. The project-wide totals for each statistic are listed in the summary.
+
+## Test coverage
+
+A typical use case for coverage analysis is test coverage. Rust's source-based coverage tools can both measure your tests' code coverage as percentage, and pinpoint functions and branches not tested.
+
+The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate.
+
+Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-C instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.)
+
+```shell
+$ RUSTFLAGS="-C instrument-coverage" \
+ LLVM_PROFILE_FILE="json5format-%m.profraw" \
+ cargo test --tests
+```
+
+Make note of the test binary file paths, displayed after the word "`Running`" in the test output:
+
+```text
+ ...
+ Compiling json5format v0.1.3 ($HOME/json5format)
+ Finished test [unoptimized + debuginfo] target(s) in 14.60s
+
+ Running target/debug/deps/json5format-fececd4653271682
+running 25 tests
+...
+test result: ok. 25 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+
+ Running target/debug/deps/lib-30768f9c53506dc5
+running 31 tests
+...
+test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+```
+
+You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them:
+
+```shell
+$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata
+```
+
+Then run the `cov` tool, with the `profdata` file and all test binaries:
+
+```shell
+$ llvm-cov report \
+ --use-color --ignore-filename-regex='/.cargo/registry' \
+ --instr-profile=json5format.profdata \
+ --object target/debug/deps/lib-30768f9c53506dc5 \
+ --object target/debug/deps/json5format-fececd4653271682
+$ llvm-cov show \
+ --use-color --ignore-filename-regex='/.cargo/registry' \
+ --instr-profile=json5format.profdata \
+ --object target/debug/deps/lib-30768f9c53506dc5 \
+ --object target/debug/deps/json5format-fececd4653271682 \
+ --show-instantiations --show-line-counts-or-regions \
+ --Xdemangler=rustfilt | less -R
+```
+
+> **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_
+
+### Tips for listing the binaries automatically
+
+For `bash` users, one suggested way to automatically complete the `cov` command with the list of binaries is with a command like:
+
+```bash
+$ llvm-cov report \
+ $( \
+ for file in \
+ $( \
+ RUSTFLAGS="-C instrument-coverage" \
+ cargo test --tests --no-run --message-format=json \
+ | jq -r "select(.profile.test == true) | .filenames[]" \
+ | grep -v dSYM - \
+ ); \
+ do \
+ printf "%s %s " -object $file; \
+ done \
+ ) \
+ --instr-profile=json5format.profdata --summary-only # and/or other options
+```
+
+Adding `--no-run --message-format=json` to the _same_ `cargo test` command used to run
+the tests (including the same environment variables and flags) generates output in a JSON
+format that `jq` can easily query.
+
+The `printf` command takes this list and generates the `--object <binary>` arguments
+for each listed test binary.
+
+### Including doc tests
+
+The previous examples run `cargo test` with `--tests`, which excludes doc tests.[^79417]
+
+To include doc tests in the coverage results, drop the `--tests` flag, and apply the
+`-C instrument-coverage` flag, and some doc-test-specific options in the
+`RUSTDOCFLAGS` environment variable. (The `llvm-profdata` command does not change.)
+
+```bash
+$ RUSTFLAGS="-C instrument-coverage" \
+ RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
+ LLVM_PROFILE_FILE="json5format-%m.profraw" \
+ cargo test
+$ llvm-profdata merge -sparse json5format-*.profraw -o json5format.profdata
+```
+
+The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries
+(with their coverage maps) for `llvm-cov`.
+
+```bash
+$ llvm-cov report \
+ $( \
+ for file in \
+ $( \
+ RUSTFLAGS="-C instrument-coverage" \
+ RUSTDOCFLAGS="-C instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
+ cargo test --no-run --message-format=json \
+ | jq -r "select(.profile.test == true) | .filenames[]" \
+ | grep -v dSYM - \
+ ) \
+ target/debug/doctestbins/*/rust_out; \
+ do \
+ [[ -x $file ]] && printf "%s %s " -object $file; \
+ done \
+ ) \
+ --instr-profile=json5format.profdata --summary-only # and/or other options
+```
+
+> **Note**: The differences in this `llvm-cov` invocation, compared with the
+> version without doc tests, include:
+
+- The `cargo test ... --no-run` command is updated with the same environment variables
+ and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE`
+ is only used when _running_ the tests.)
+- The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out`
+ binaries generated for doc tests (note, however, that some `rust_out` files may not
+ be executable binaries).
+- `[[ -x $file ]] &&` filters the files passed on to the `printf`, to include only
+ executable binaries.
+
+[^79417]:
+ There is ongoing work to resolve a known issue
+ [(#79417)](https://github.com/rust-lang/rust/issues/79417) that doc test coverage
+ generates incorrect source line numbers in `llvm-cov show` results.
+
+## `-C instrument-coverage=<options>`
+
+- `-C instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-C instrument-coverage`, with no value.)
+- `-C instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-C instrument-coverage` option.)
+- `-Zunstable-options -C instrument-coverage=except-unused-generics`: Instrument all functions except unused generics.
+- `-Zunstable-options -C instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions.
+
+## Other references
+
+Rust's implementation and workflow for source-based code coverage is based on the same library and tools used to implement [source-based code coverage in Clang]. (This document is partially based on the Clang guide.)
+
+[source-based code coverage in clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
+[`json5format`]: https://crates.io/crates/json5format
`aarch64-apple-tvos` | * | | ARM64 tvOS
[`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3
`aarch64-unknown-freebsd` | ✓ | ✓ | ARM64 FreeBSD
-`aarch64-unknown-hermit` | ? | |
+`aarch64-unknown-hermit` | ✓ | | ARM64 HermitCore
+[`aarch64-unknown-none-hermitkernel`](platform-support/aarch64-unknown-none-hermitkernel.md) | * | | ARM64 HermitCore kernel
`aarch64-unknown-uefi` | * | | ARM64 UEFI
`aarch64-unknown-linux-gnu_ilp32` | ✓ | ✓ | ARM64 Linux (ILP32 ABI)
`aarch64-unknown-netbsd` | ✓ | ✓ |
`armv6-unknown-netbsd-eabihf` | ? | |
`armv6k-nintendo-3ds` | * | | ARMv6K Nintendo 3DS, Horizon (Requires devkitARM toolchain)
`armv7-apple-ios` | ✓ | | ARMv7 iOS, Cortex-a8
-[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | ARMv7 Linux uClibc
+[`armv7-unknown-linux-uclibceabi`](platform-support/armv7-unknown-linux-uclibceabi.md) | ✓ | ✓ | ARMv7 Linux with uClibc, softfloat
+[`armv7-unknown-linux-uclibceabihf`](platform-support/armv7-unknown-linux-uclibceabihf.md) | ✓ | ? | ARMv7 Linux with uClibc, hardfloat
`armv7-unknown-freebsd` | ✓ | ✓ | ARMv7 FreeBSD
`armv7-unknown-netbsd-eabihf` | ✓ | ✓ |
`armv7-wrs-vxworks-eabihf` | ? | |
`i686-wrs-vxworks` | ? | |
`m68k-unknown-linux-gnu` | ? | | Motorola 680x0 Linux
`mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc
+`mips64-openwrt-linux-musl` | ? | | MIPS64 for OpenWrt Linux MUSL
`mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP)
`mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc
`mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat
`x86_64-sun-solaris` | ? | | Deprecated target for 64-bit Solaris 10/11, illumos
`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
-`x86_64-unknown-hermit` | ? | |
+`x86_64-unknown-hermit` | ✓ | | HermitCore
`x86_64-unknown-l4re-uclibc` | ? | |
[`x86_64-unknown-none`](platform-support/x86_64-unknown-none.md) | * | | Freestanding/bare-metal x86_64, softfloat
-`x86_64-unknown-none-hermitkernel` | ? | | HermitCore kernel
+`x86_64-unknown-none-hermitkernel` | * | | HermitCore kernel
`x86_64-unknown-none-linuxkernel` | * | | Linux kernel modules
[`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD
`x86_64-unknown-uefi` | * | | 64-bit UEFI
--- /dev/null
+# `aarch64-unknown-none-hermitkernel`
+
+**Tier: 3**
+
+Required to build the kernel for [HermitCore](https://github.com/hermitcore/hermit-playground)
+or [RustyHermit](https://github.com/hermitcore/rusty-hermit).
+The result is a bare-metal aarch64 binary in ELF format.
+
+## Target maintainers
+
+- Stefan Lankes, https://github.com/stlankes
+
+## Requirements
+
+This target is cross-compiled. There is no support for `std`, but the
+library operating system provides a simple allocator to use `alloc`.
+
+By default, Rust code generated for this target does not use any vector or
+floating-point registers. This allows the generated code to build the library
+operaring system, which may need to avoid the use of such
+registers or which may have special considerations about the use of such
+registers (e.g. saving and restoring them to avoid breaking userspace code
+using the same registers). In contrast to `aarch64-unknown-none-softfloat`,
+the target is completly relocatable, which is a required feature of RustyHermit.
+
+By default, code generated with this target should run on any `aarch64`
+hardware; enabling additional target features may raise this baseline.
+On `aarch64-unknown-none-hermitkernel`, `extern "C"` uses the [standard System V calling
+convention](https://github.com/ARM-software/abi-aa/releases/download/2021Q3/sysvabi64.pdf),
+without red zones.
+
+This target generated binaries in the ELF format.
+
+## Building the target
+
+Typical you should not use the target directly. The target `aarch64-unknown-hermit`
+builds the _user space_ of RustyHermit and supports red zones and floating-point
+operations.
+To build and link the kernel to the application, the crate
+[hermit-sys](https://github.com/hermitcore/rusty-hermit/tree/master/hermit-sys)
+should be used by adding the following lines to the `Cargo.toml` file of
+your application.
+
+```toml
+[target.'cfg(target_os = "hermit")'.dependencies]
+hermit-sys = "0.1.*"
+```
+
+The crate `hermit-sys` uses the target `aarch64-unknown-none-hermitkernel`
+to build the kernel.
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you need to build the crate `hermit-sys` (see
+"Building the target" above).
+
+## Testing
+
+As `aarch64-unknown-none-hermitkernel` does not support `std`
+and does not support running any Rust testsuite.
+
+## Cross-compilation toolchains and C code
+
+If you want to compile C code along with Rust you will need an
+appropriate `aarch64` toolchain.
+
+Rust *may* be able to use an `aarch64-linux-gnu-` toolchain with appropriate
+standalone flags to build for this toolchain (depending on the assumptions of
+that toolchain, see below), or you may wish to use a separate
+`aarch64-unknown-none` (or `aarch64-elf-`) toolchain.
+
+On some `aarch64` hosts that use ELF binaries, you *may* be able to use the host
+C toolchain, if it does not introduce assumptions about the host environment
+that don't match the expectations of a standalone environment. Otherwise, you
+may need a separate toolchain for standalone/freestanding development, just as
+when cross-compiling from a non-`aarch64` platform.
--- /dev/null
+# `armv7-unknown-linux-uclibceabi`
+
+**Tier: 3**
+
+This target supports ARMv7 softfloat CPUs and uses the uclibc-ng standard library. This is a common configuration on many consumer routers (e.g., Netgear R7000, Asus RT-AC68U).
+
+## Target maintainers
+
+* [@lancethepants](https://github.com/lancethepants)
+
+## Requirements
+
+This target is cross compiled, and requires a cross toolchain.
+
+This target supports host tools and std.
+
+## Building the target
+
+You will need to download or build a `'C'` cross toolchain that targets ARMv7 softfloat and that uses the uclibc-ng standard library. If your target hardware is something like a router or an embedded device, keep in mind that manufacturer supplied SDKs for this class of CPU could be outdated and potentially unsuitable for bootstrapping rust.
+
+[Here](https://github.com/lancethepants/tomatoware-toolchain) is a sample toolchain that is built using [buildroot](https://buildroot.org/). It uses modern toolchain components, older thus universal kernel headers (2.6.36.4), and is used for a project called [Tomatoware](https://github.com/lancethepants/tomatoware). This toolchain is patched so that its sysroot is located at /mmc (e.g., /mmc/bin, /mmc/lib, /mmc/include). This is useful in scenarios where the root filesystem is read-only but you are able attach external storage loaded with user applications. Tomatoware is an example of this that even allows you to run various compilers and developer tools natively on the target device.
+
+Utilizing the Tomatoware toolchain this target can be built for cross compilation and native compilation (host tools) with project
+
+[rust-bootstrap-armv7-unknown-linux-uclibceabi](https://github.com/lancethepants/rust-bootstrap-armv7-unknown-linux-uclibceabi).
+
+
+Here is a sample config if using your own toolchain.
+
+```toml
+[build]
+build-stage = 2
+target = ["armv7-unknown-linux-uclibceabi"]
+
+[target.armv7-unknown-linux-uclibceabi]
+cc = "/path/to/arm-unknown-linux-uclibcgnueabi-gcc"
+cxx = "/path/to/arm-unknown-linux-uclibcgnueabi-g++"
+ar = "path/to/arm-unknown-linux-uclibcgnueabi-ar"
+ranlib = "path/to/arm-unknown-linux-uclibcgnueabi-"
+linker = "/path/to/arm-unknown-linux-uclibcgnueabi-"
+```
+
+## Building Rust programs
+
+The following assumes you are using the Tomatoware toolchain and environment. Adapt if you are using your own toolchain.
+
+### Native compilation
+
+Since this target supports host tools, you can natively build rust applications directly on your target device. This can be convenient because it removes the complexities of cross compiling and you can immediately test and deploy your binaries. One downside is that compiling on your ARMv7 CPU will probably be much slower than cross compilation on your x86 machine.
+
+To setup native compilation:
+
+* Download Tomatoware to your device using the latest nightly release found [here](https://files.lancethepants.com/Tomatoware/Nightly/).
+* Extract `tar zxvf arm-soft-mmc.tgz -C /mmc`
+* Add `/mmc/bin:/mmc:sbin/` to your PATH, or `source /mmc/etc/profile`
+* `apt update && apt install rust`
+
+If you bootstrap rust on your own using the project above, it will create a .deb file that you then can install with
+```text
+dpkg -i rust_1.xx.x-x_arm.deb
+```
+
+After completing these steps you can use rust normally in a native environment.
+
+### Cross Compilation
+
+To cross compile, you'll need to:
+
+* Build the rust cross toochain using [rust-bootstrap-armv7-unknown-linux-uclibceabi](https://github.com/lancethepants/rust-bootstrap-armv7-unknown-linux-uclibceabi) or your own built toolchain.
+* Link your built toolchain with
+
+ ```text
+ rustup toolchain link stage2 \
+ ${HOME}/rust-bootstrap-armv7-unknown-linux-uclibceabi/src/rust/rust/build/x86_64-unknown-linux-gnu/stage2
+ ```
+* Build with:
+ ```text
+ CC=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-gcc \
+ CXX=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-g++ \
+ AR=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-ar \
+ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_UCLIBCEABI_LINKER=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-gcc \
+ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_UCLIBCEABI_RUSTFLAGS='-Clink-arg=-s -Clink-arg=-Wl,--dynamic-linker=/mmc/lib/ld-uClibc.so.1 -Clink-arg=-Wl,-rpath,/mmc/lib' \
+ cargo +stage2 build --target armv7-unknown-linux-uclibceabi --release
+ ```
+* Copy the binary to your target device and run.
+
+We specify `CC`, `CXX`, and `AR` because somtimes a project or a subproject requires the use of your `'C'` cross toolchain. Since Tomatoware has a modified sysroot we also pass via RUSTFLAGS the location of the dynamic-linker and rpath.
+
+### Test with QEMU
+
+To test a cross-compiled binary on your build system follow the instructions for `Cross Compilation`, install `qemu-arm-static`, and run with the following.
+```text
+CC=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-gcc \
+CXX=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-g++ \
+AR=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-ar \
+CARGO_TARGET_ARMV7_UNKNOWN_LINUX_UCLIBCEABI_LINKER=/opt/tomatoware/arm-soft-mmc/bin/arm-linux-gcc \
+CARGO_TARGET_ARMV7_UNKNOWN_LINUX_UCLIBCEABI_RUNNER="qemu-arm-static -L /opt/tomatoware/arm-soft-mmc/arm-tomatoware-linux-uclibcgnueabi/sysroot/" \
+cargo +stage2 run --target armv7-unknown-linux-uclibceabi --release
+```
+### Run in a chroot
+
+It's also possible to build in a chroot environment. This is a convenient way to work without needing to access the target hardware.
+
+To build the chroot:
+
+* `sudo debootstrap --arch armel bullseye $HOME/debian`
+* `sudo chroot $HOME/debian/ /bin/bash`
+* `mount proc /proc -t proc`
+* `mount -t sysfs /sys sys/`
+* `export PATH=/mmc/bin:/mmc/sbin:$PATH`
+
+From here you can setup your environment (e.g., add user, install wget).
+
+* Download Tomatoware to the chroot environment using the latest nightly release found [here](https://files.lancethepants.com/Tomatoware/Nightly/).
+* Extract `tar zxvf arm-soft-mmc.tgz -C /mmc`
+* Add `/mmc/bin:/mmc:sbin/` to your PATH, or `source /mmc/etc/profile`
+* `sudo /mmc/bin/apt update && sudo /mmc/bin/apt install rust`
+
+After completing these steps you can use rust normally in a chroot environment.
+
+Remember when using `sudo` the root user's PATH could differ from your user's PATH.
--- /dev/null
+# `mips64-openwrt-linux-musl`
+**Tier: 3**
+
+## Target maintainers
+- Donald Hoskins `grommish@gmail.com`, https://github.com/Itus-Shield
+
+## Requirements
+This target is cross-compiled. There is no support for `std`. There is no
+default allocator, but it's possible to use `alloc` by supplying an allocator.
+
+By default, Rust code generated for this target uses `-msoft-float` and is
+dynamically linked.
+
+This target generated binaries in the ELF format.
+
+## Building the target
+This target is built exclusively within the `OpenWrt` build system via
+the `rust-lang` HOST package
+
+## Building Rust programs
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will either need to build Rust with the target enabled (see
+"Building the target" above).
+
+## Testing
+As `mips64-openwrt-linux-musl` supports a variety of different environments and does
+not support `std`, this target does not support running the Rust testsuite at this
+time.
Writes the results of the tests to the given file.
-#### `--report-time` _FORMAT_
+#### `--report-time`
⚠️ 🚧 This option is [unstable](#unstable-options), and requires the `-Z
unstable-options` flag. See [tracking issue
`--check-theme` flag, it discards all other flags and only performs the CSS rule
comparison operation.
-### `--crate-version`: control the crate version
+## `--crate-version`: control the crate version
Using this flag looks like this:
file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file.
Otherwise, it assumes that the input file is Rust.
+# Unstable command line arguments
+
## `--nocapture`
When this flag is used with `--test`, the output (stdout and stderr) of your tests won't be
captured by rustdoc. Instead, the output will be directed to your terminal,
as if you had run the test executable manually. This is especially useful
for debugging your tests!
+
+## `--check`
+
+When this flag is supplied, rustdoc will type check and lint your code, but will not generate any
+documentation or run your doctests.
+
+Using this flag looks like:
+
+```bash
+rustdoc -Z unstable-options --check src/lib.rs
+```
--- /dev/null
+# `cf-protection`
+
+This option enables control-flow enforcement technology (CET) on x86; a more detailed description of
+CET is available [here]. Similar to `clang`, this flag takes one of the following values:
+
+- `none` - Disable CET completely (this is the default).
+- `branch` - Enable indirect branch tracking (`IBT`).
+- `return` - Enable shadow stack (`SHSTK`).
+- `full` - Enable both `branch` and `return`.
+
+[here]: https://www.intel.com/content/www/us/en/develop/articles/technical-look-control-flow-enforcement-technology.html
+
+This flag only applies to the LLVM backend: it sets the `cf-protection-branch` and
+`cf-protection-return` flags on LLVM modules. Note, however, that all compiled modules linked
+together must have the flags set for the compiled output to be CET-enabled. Currently, Rust's
+standard library does not ship with CET enabled by default, so you may need to rebuild all standard
+modules with a `cargo` command like:
+
+```sh
+$ RUSTFLAGS="-Z cf-protection=full" RUSTC="rustc-custom" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu
+```
+
+### Detection
+
+An ELF binary is CET-enabled if it has the `IBT` and `SHSTK` tags, e.g.:
+
+```sh
+$ readelf -a target/x86_64-unknown-linux-gnu/debug/example | grep feature:
+ Properties: x86 feature: IBT, SHSTK
+```
+
+### Troubleshooting
+
+To display modules that are not CET enabled, examine the linker errors available when `cet-report` is enabled:
+
+```sh
+$ RUSTC_LOG=rustc_codegen_ssa::back::link=info rustc-custom -v -Z cf-protection=full -C link-arg="-Wl,-z,cet-report=warning" -o example example.rs
+...
+/usr/bin/ld: /.../build/x86_64-unknown-linux-gnu/stage1/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-d73f7266be14cb8b.rlib(std-d73f7266be14cb8b.std.f7443020-cgu.12.rcgu.o): warning: missing IBT and SHSTK properties
+```
+++ /dev/null
-# `instrument-coverage`
-
-The tracking issue for this feature is: [#79121].
-
-[#79121]: https://github.com/rust-lang/rust/issues/79121
-
----
-
-## Introduction
-
-The Rust compiler includes two code coverage implementations:
-
-- A GCC-compatible, gcov-based coverage implementation, enabled with `-Z profile`, which derives coverage data based on DebugInfo.
-- A source-based code coverage implementation, enabled with `-Z instrument-coverage`, which uses LLVM's native, efficient coverage instrumentation to generate very precise coverage data.
-
-This document describes how to enable and use the LLVM instrumentation-based coverage, via the `-Z instrument-coverage` compiler flag.
-
-## How it works
-
-When `-Z instrument-coverage` is enabled, the Rust compiler enhances rust-based libraries and binaries by:
-
-- Automatically injecting calls to an LLVM intrinsic ([`llvm.instrprof.increment`]), at functions and branches in compiled code, to increment counters when conditional sections of code are executed.
-- Embedding additional information in the data section of each library and binary (using the [LLVM Code Coverage Mapping Format] _Version 5_, if compiling with LLVM 12, or _Version 6_, if compiling with LLVM 13 or higher), to define the code regions (start and end positions in the source code) being counted.
-
-When running a coverage-instrumented program, the counter values are written to a `profraw` file at program termination. LLVM bundles tools that read the counter results, combine those results with the coverage map (embedded in the program binary), and generate coverage reports in multiple formats.
-
-[`llvm.instrprof.increment`]: https://llvm.org/docs/LangRef.html#llvm-instrprof-increment-intrinsic
-[llvm code coverage mapping format]: https://llvm.org/docs/CoverageMappingFormat.html
-
-> **Note**: `-Z instrument-coverage` also automatically enables `-C symbol-mangling-version=v0` (tracking issue [#60705]). The `v0` symbol mangler is strongly recommended, but be aware that this demangler is also experimental. The `v0` demangler can be overridden by explicitly adding `-Z unstable-options -C symbol-mangling-version=legacy`.
-
-[#60705]: https://github.com/rust-lang/rust/issues/60705
-
-## Enable coverage profiling in the Rust compiler
-
-Rust's source-based code coverage requires the Rust "profiler runtime". Without it, compiling with `-Z instrument-coverage` generates an error that the profiler runtime is missing.
-
-The Rust `nightly` distribution channel includes the profiler runtime, by default.
-
-> **Important**: If you are building the Rust compiler from the source distribution, the profiler runtime is _not_ enabled in the default `config.toml.example`. Edit your `config.toml` file and ensure the `profiler` feature is set it to `true` (either under the `[build]` section, or under the settings for an individual `[target.<triple>]`):
->
-> ```toml
-> # Build the profiler runtime (required when compiling with options that depend
-> # on this runtime, such as `-C profile-generate` or `-Z instrument-coverage`).
-> profiler = true
-> ```
-
-### Building the demangler
-
-LLVM coverage reporting tools generate results that can include function names and other symbol references, and the raw coverage results report symbols using the compiler's "mangled" version of the symbol names, which can be difficult to interpret. To work around this issue, LLVM coverage tools also support a user-specified symbol name demangler.
-
-One option for a Rust demangler is [`rustfilt`], which can be installed with:
-
-```shell
-cargo install rustfilt
-```
-
-Another option, if you are building from the Rust compiler source distribution, is to use the `rust-demangler` tool included in the Rust source distribution, which can be built with:
-
-```shell
-$ ./x.py build rust-demangler
-```
-
-[`rustfilt`]: https://crates.io/crates/rustfilt
-
-## Compiling with coverage enabled
-
-Set the `-Z instrument-coverage` compiler flag in order to enable LLVM source-based code coverage profiling.
-
-The default option generates coverage for all functions, including unused (never called) functions and generics. The compiler flag supports an optional value to tailor this behavior. (See [`-Z instrument-coverage=<options>`](#-z-instrument-coverageoptions), below.)
-
-With `cargo`, you can instrument your program binary _and_ dependencies at the same time.
-
-For example (if your project's Cargo.toml builds a binary by default):
-
-```shell
-$ cd your-project
-$ cargo clean
-$ RUSTFLAGS="-Z instrument-coverage" cargo build
-```
-
-If `cargo` is not configured to use your `profiler`-enabled version of `rustc`, set the path explicitly via the `RUSTC` environment variable. Here is another example, using a `stage1` build of `rustc` to compile an `example` binary (from the [`json5format`] crate):
-
-```shell
-$ RUSTC=$HOME/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc \
- RUSTFLAGS="-Z instrument-coverage" \
- cargo build --example formatjson5
-```
-
-> **Note**: that some compiler options, combined with `-Z instrument-coverage`, can produce LLVM IR and/or linked binaries that are incompatible with LLVM coverage maps. For example, coverage requires references to actual functions in LLVM IR. If any covered function is optimized out, the coverage tools may not be able to process the coverage results. If you need to pass additional options, with coverage enabled, test them early, to confirm you will get the coverage results you expect.
-
-## Running the instrumented binary to generate raw coverage profiling data
-
-In the previous example, `cargo` generated the coverage-instrumented binary `formatjson5`:
-
-```shell
-$ echo "{some: 'thing'}" | target/debug/examples/formatjson5 -
-```
-
-```json5
-{
- some: "thing",
-}
-```
-
-After running this program, a new file, `default.profraw`, should be in the current working directory. It's often preferable to set a specific file name or path. You can change the output file using the environment variable `LLVM_PROFILE_FILE`:
-
-```shell
-$ echo "{some: 'thing'}" \
- | LLVM_PROFILE_FILE="formatjson5.profraw" target/debug/examples/formatjson5 -
-...
-$ ls formatjson5.profraw
-formatjson5.profraw
-```
-
-If `LLVM_PROFILE_FILE` contains a path to a non-existent directory, the missing directory structure will be created. Additionally, the following special pattern strings are rewritten:
-
-- `%p` - The process ID.
-- `%h` - The hostname of the machine running the program.
-- `%t` - The value of the TMPDIR environment variable.
-- `%Nm` - the instrumented binary’s signature: The runtime creates a pool of N raw profiles, used for on-line profile merging. The runtime takes care of selecting a raw profile from the pool, locking it, and updating it before the program exits. `N` must be between `1` and `9`, and defaults to `1` if omitted (with simply `%m`).
-- `%c` - Does not add anything to the filename, but enables a mode (on some platforms, including Darwin) in which profile counter updates are continuously synced to a file. This means that if the instrumented program crashes, or is killed by a signal, perfect coverage information can still be recovered.
-
-## Installing LLVM coverage tools
-
-LLVM's supplies two tools—`llvm-profdata` and `llvm-cov`—that process coverage data and generate reports. There are several ways to find and/or install these tools, but note that the coverage mapping data generated by the Rust compiler requires LLVM version 12 or higher. (`llvm-cov --version` typically shows the tool's LLVM version number.):
-
-- The LLVM tools may be installed (or installable) directly to your OS (such as via `apt-get`, for Linux).
-- If you are building the Rust compiler from source, you can optionally use the bundled LLVM tools, built from source. Those tool binaries can typically be found in your build platform directory at something like: `rust/build/x86_64-unknown-linux-gnu/llvm/bin/llvm-*`.
-- You can install compatible versions of these tools via `rustup`.
-
-The `rustup` option is guaranteed to install a compatible version of the LLVM tools, but they can be hard to find. We recommend [`cargo-binutils`], which installs Rust-specific wrappers around these and other LLVM tools, so you can invoke them via `cargo` commands!
-
-```shell
-$ rustup component add llvm-tools-preview
-$ cargo install cargo-binutils
-$ cargo profdata -- --help # note the additional "--" preceding the tool-specific arguments
-```
-
-[`cargo-binutils`]: https://crates.io/crates/cargo-binutils
-
-## Creating coverage reports
-
-Raw profiles have to be indexed before they can be used to generate coverage reports. This is done using [`llvm-profdata merge`] (or `cargo profdata -- merge`), which can combine multiple raw profiles and index them at the same time:
-
-```shell
-$ llvm-profdata merge -sparse formatjson5.profraw -o formatjson5.profdata
-```
-
-Finally, the `.profdata` file is used, in combination with the coverage map (from the program binary) to generate coverage reports using [`llvm-cov report`] (or `cargo cov -- report`), for a coverage summaries; and [`llvm-cov show`] (or `cargo cov -- show`), to see detailed coverage of lines and regions (character ranges) overlaid on the original source code.
-
-These commands have several display and filtering options. For example:
-
-```shell
-$ llvm-cov show -Xdemangler=rustfilt target/debug/examples/formatjson5 \
- -instr-profile=formatjson5.profdata \
- -show-line-counts-or-regions \
- -show-instantiations \
- -name=add_quoted_string
-```
-
-<img alt="Screenshot of sample `llvm-cov show` result, for function add_quoted_string" src="img/llvm-cov-show-01.png" class="center"/>
-<br/>
-<br/>
-
-Some of the more notable options in this example include:
-
-- `--Xdemangler=rustfilt` - the command name or path used to demangle Rust symbols (`rustfilt` in the example, but this could also be a path to the `rust-demangler` tool)
-- `target/debug/examples/formatjson5` - the instrumented binary (from which to extract the coverage map)
-- `--instr-profile=<path-to-file>.profdata` - the location of the `.profdata` file created by `llvm-profdata merge` (from the `.profraw` file generated by the instrumented binary)
-- `--name=<exact-function-name>` - to show coverage for a specific function (or, consider using another filter option, such as `--name-regex=<pattern>`)
-
-[`llvm-profdata merge`]: https://llvm.org/docs/CommandGuide/llvm-profdata.html#profdata-merge
-[`llvm-cov report`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-report
-[`llvm-cov show`]: https://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-show
-
-> **Note**: Coverage can also be disabled on an individual function by annotating the function with the [`no_coverage` attribute] (which requires the feature flag `#![feature(no_coverage)]`).
-
-[`no_coverage` attribute]: ../language-features/no-coverage.md
-
-## Interpreting reports
-
-There are four statistics tracked in a coverage summary:
-
-- Function coverage is the percentage of functions that have been executed at least once. A function is considered to be executed if any of its instantiations are executed.
-- Instantiation coverage is the percentage of function instantiations that have been executed at least once. Generic functions and functions generated from macros are two kinds of functions that may have multiple instantiations.
-- Line coverage is the percentage of code lines that have been executed at least once. Only executable lines within function bodies are considered to be code lines.
-- Region coverage is the percentage of code regions that have been executed at least once. A code region may span multiple lines: for example, in a large function body with no control flow. In other cases, a single line can contain multiple code regions: `return x || (y && z)` has countable code regions for `x` (which may resolve the expression, if `x` is `true`), `|| (y && z)` (executed only if `x` was `false`), and `return` (executed in either situation).
-
-Of these four statistics, function coverage is usually the least granular while region coverage is the most granular. The project-wide totals for each statistic are listed in the summary.
-
-## Test coverage
-
-A typical use case for coverage analysis is test coverage. Rust's source-based coverage tools can both measure your tests' code coverage as percentage, and pinpoint functions and branches not tested.
-
-The following example (using the [`json5format`] crate, for demonstration purposes) show how to generate and analyze coverage results for all tests in a crate.
-
-Since `cargo test` both builds and runs the tests, we set both the additional `RUSTFLAGS`, to add the `-Z instrument-coverage` flag, and `LLVM_PROFILE_FILE`, to set a custom filename for the raw profiling data generated during the test runs. Since there may be more than one test binary, apply `%m` in the filename pattern. This generates unique names for each test binary. (Otherwise, each executed test binary would overwrite the coverage results from the previous binary.)
-
-```shell
-$ RUSTFLAGS="-Z instrument-coverage" \
- LLVM_PROFILE_FILE="json5format-%m.profraw" \
- cargo test --tests
-```
-
-Make note of the test binary file paths, displayed after the word "`Running`" in the test output:
-
-```text
- ...
- Compiling json5format v0.1.3 ($HOME/json5format)
- Finished test [unoptimized + debuginfo] target(s) in 14.60s
-
- Running target/debug/deps/json5format-fececd4653271682
-running 25 tests
-...
-test result: ok. 25 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
-
- Running target/debug/deps/lib-30768f9c53506dc5
-running 31 tests
-...
-test result: ok. 31 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
-```
-
-You should have one or more `.profraw` files now, one for each test binary. Run the `profdata` tool to merge them:
-
-```shell
-$ cargo profdata -- merge \
- -sparse json5format-*.profraw -o json5format.profdata
-```
-
-Then run the `cov` tool, with the `profdata` file and all test binaries:
-
-```shell
-$ cargo cov -- report \
- --use-color --ignore-filename-regex='/.cargo/registry' \
- --instr-profile=json5format.profdata \
- --object target/debug/deps/lib-30768f9c53506dc5 \
- --object target/debug/deps/json5format-fececd4653271682
-$ cargo cov -- show \
- --use-color --ignore-filename-regex='/.cargo/registry' \
- --instr-profile=json5format.profdata \
- --object target/debug/deps/lib-30768f9c53506dc5 \
- --object target/debug/deps/json5format-fececd4653271682 \
- --show-instantiations --show-line-counts-or-regions \
- --Xdemangler=rustfilt | less -R
-```
-
-> **Note**: The command line option `--ignore-filename-regex=/.cargo/registry`, which excludes the sources for dependencies from the coverage results.\_
-
-### Tips for listing the binaries automatically
-
-For `bash` users, one suggested way to automatically complete the `cov` command with the list of binaries is with a command like:
-
-```bash
-$ cargo cov -- report \
- $( \
- for file in \
- $( \
- RUSTFLAGS="-Z instrument-coverage" \
- cargo test --tests --no-run --message-format=json \
- | jq -r "select(.profile.test == true) | .filenames[]" \
- | grep -v dSYM - \
- ); \
- do \
- printf "%s %s " -object $file; \
- done \
- ) \
- --instr-profile=json5format.profdata --summary-only # and/or other options
-```
-
-Adding `--no-run --message-format=json` to the _same_ `cargo test` command used to run
-the tests (including the same environment variables and flags) generates output in a JSON
-format that `jq` can easily query.
-
-The `printf` command takes this list and generates the `--object <binary>` arguments
-for each listed test binary.
-
-### Including doc tests
-
-The previous examples run `cargo test` with `--tests`, which excludes doc tests.[^79417]
-
-To include doc tests in the coverage results, drop the `--tests` flag, and apply the
-`-Z instrument-coverage` flag, and some doc-test-specific options in the
-`RUSTDOCFLAGS` environment variable. (The `cargo profdata` command does not change.)
-
-```bash
-$ RUSTFLAGS="-Z instrument-coverage" \
- RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
- LLVM_PROFILE_FILE="json5format-%m.profraw" \
- cargo test
-$ cargo profdata -- merge \
- -sparse json5format-*.profraw -o json5format.profdata
-```
-
-The `-Z unstable-options --persist-doctests` flag is required, to save the test binaries
-(with their coverage maps) for `llvm-cov`.
-
-```bash
-$ cargo cov -- report \
- $( \
- for file in \
- $( \
- RUSTFLAGS="-Z instrument-coverage" \
- RUSTDOCFLAGS="-Z instrument-coverage -Z unstable-options --persist-doctests target/debug/doctestbins" \
- cargo test --no-run --message-format=json \
- | jq -r "select(.profile.test == true) | .filenames[]" \
- | grep -v dSYM - \
- ) \
- target/debug/doctestbins/*/rust_out; \
- do \
- [[ -x $file ]] && printf "%s %s " -object $file; \
- done \
- ) \
- --instr-profile=json5format.profdata --summary-only # and/or other options
-```
-
-> **Note**: The differences in this `cargo cov` command, compared with the version without
-> doc tests, include:
-
-- The `cargo test ... --no-run` command is updated with the same environment variables
- and flags used to _build_ the tests, _including_ the doc tests. (`LLVM_PROFILE_FILE`
- is only used when _running_ the tests.)
-- The file glob pattern `target/debug/doctestbins/*/rust_out` adds the `rust_out`
- binaries generated for doc tests (note, however, that some `rust_out` files may not
- be executable binaries).
-- `[[ -x $file ]] &&` filters the files passed on to the `printf`, to include only
- executable binaries.
-
-[^79417]:
- There is ongoing work to resolve a known issue
- [(#79417)](https://github.com/rust-lang/rust/issues/79417) that doc test coverage
- generates incorrect source line numbers in `llvm-cov show` results.
-
-## `-Z instrument-coverage=<options>`
-
-- `-Z instrument-coverage=all`: Instrument all functions, including unused functions and unused generics. (This is the same as `-Z instrument-coverage`, with no value.)
-- `-Z instrument-coverage=except-unused-generics`: Instrument all functions except unused generics.
-- `-Z instrument-coverage=except-unused-functions`: Instrument only used (called) functions and instantiated generic functions.
-- `-Z instrument-coverage=off`: Do not instrument any functions. (This is the same as simply not including the `-Z instrument-coverage` option.)
-
-## Other references
-
-Rust's implementation and workflow for source-based code coverage is based on the same library and tools used to implement [source-based code coverage in Clang]. (This document is partially based on the Clang guide.)
-
-[source-based code coverage in clang]: https://clang.llvm.org/docs/SourceBasedCodeCoverage.html
-[`json5format`]: https://crates.io/crates/json5format
Available options:
```sh
---report-time [plain|colored]
- Show execution time of each test. Available values:
- plain = do not colorize the execution time (default);
- colored = colorize output according to the `color`
- parameter value;
+--report-time
+ Show execution time of each test.
Threshold values for colorized output can be
configured via
`RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION`
+++ /dev/null
-# `source-based-code-coverage`
-
-See compiler flag [`-Z instrument-coverage`].
-
-[`-z instrument-coverage`]: ./instrument-coverage.html
# `asm_const`
-The tracking issue for this feature is: [#72016]
+The tracking issue for this feature is: [#93332]
-[#72016]: https://github.com/rust-lang/rust/issues/72016
+[#93332]: https://github.com/rust-lang/rust/issues/93332
------------------------
# `asm_experimental_arch`
-The tracking issue for this feature is: [#72016]
+The tracking issue for this feature is: [#93335]
-[#72016]: https://github.com/rust-lang/rust/issues/72016
+[#93335]: https://github.com/rust-lang/rust/issues/93335
------------------------
# `asm_sym`
-The tracking issue for this feature is: [#72016]
+The tracking issue for this feature is: [#93333]
-[#72016]: https://github.com/rust-lang/rust/issues/72016
+[#93333]: https://github.com/rust-lang/rust/issues/93333
------------------------
# `asm_unwind`
-The tracking issue for this feature is: [#72016]
+The tracking issue for this feature is: [#93334]
-[#72016]: https://github.com/rust-lang/rust/issues/72016
+[#93334]: https://github.com/rust-lang/rust/issues/93334
------------------------
------------------------
-Introduces four new ABI strings: "C-unwind", "stdcall-unwind",
-"thiscall-unwind", and "system-unwind". These enable unwinding from other
-languages (such as C++) into Rust frames and from Rust into other languages.
+Introduces new ABI strings:
+- "C-unwind"
+- "cdecl-unwind"
+- "stdcall-unwind"
+- "fastcall-unwind"
+- "vectorcall-unwind"
+- "thiscall-unwind"
+- "aapcs-unwind"
+- "win64-unwind"
+- "sysv64-unwind"
+- "system-unwind"
+
+These enable unwinding from other languages (such as C++) into Rust frames and
+from Rust into other languages.
See [RFC 2945] for more information.
<If Condition="(base.table.table.ctrl.pointer[i] & 0x80) == 0">
<!-- Bucket is populated -->
<Exec>n--</Exec>
- <Item Name="{((tuple$<$T1, $T2>*)base.table.table.ctrl.pointer)[-(i + 1)].__0}">((tuple$<$T1, $T2>*)base.table.table.ctrl.pointer)[-(i + 1)].__1</Item>
+ <Item Name="{((tuple$<$T1,$T2>*)base.table.table.ctrl.pointer)[-(i + 1)].__0}">((tuple$<$T1,$T2>*)base.table.table.ctrl.pointer)[-(i + 1)].__1</Item>
</If>
<Exec>i++</Exec>
</Loop>
+++ /dev/null
-#!/usr/bin/env bash
-#
-# Call `tidy --bless` before each commit
-# Copy this script to .git/hooks to activate,
-# and remove it from .git/hooks to deactivate.
-#
-
-set -Eeuo pipefail
-
-# 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 --bless"
-
-if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
- COMMAND="python $COMMAND"
-fi
-
-echo "Running pre-commit script '$COMMAND'"
-
-cd "$ROOT_DIR"
-
-$COMMAND
--- /dev/null
+#!/usr/bin/env bash
+#
+# Call `tidy --bless` before each commit
+# Copy this script to .git/hooks to activate,
+# and remove it from .git/hooks to deactivate.
+#
+
+set -Eeuo pipefail
+
+# 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 --bless"
+
+if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
+ COMMAND="python $COMMAND"
+fi
+
+echo "Running pre-push script '$COMMAND'"
+
+cd "$ROOT_DIR"
+
+$COMMAND
serde_json = "1.0"
smallvec = "1.6.1"
tempfile = "3"
-itertools = "0.9"
+itertools = "0.10"
regex = "1"
rustdoc-json-types = { path = "../rustdoc-json-types" }
tracing = "0.1"
.get(name)
.unwrap_or(&empty)
.iter()
- .map(|region| GenericBound::Outlives(Self::get_lifetime(region, names_map)))
+ .map(|region| GenericBound::Outlives(Self::get_lifetime(*region, names_map)))
.collect();
if bounds.is_empty() {
let hir_id = tcx.hir().local_def_id_to_hir_id(did);
rustc_hir_pretty::id_to_string(&tcx.hir(), hir_id)
} else {
- tcx.rendered_const(did)
+ tcx.rendered_const(did).clone()
}
}
}
}
-impl Clean<Option<Lifetime>> for ty::RegionKind {
+impl Clean<Option<Lifetime>> for ty::Region<'_> {
fn clean(&self, _cx: &mut DocContext<'_>) -> Option<Lifetime> {
- match *self {
+ match **self {
ty::ReStatic => Some(Lifetime::statik()),
ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrNamed(_, name), .. }) => {
Some(Lifetime(name))
fn clean(&self, cx: &mut DocContext<'_>) -> Option<WherePredicate> {
let ty::OutlivesPredicate(a, b) = self;
- if let (ty::ReEmpty(_), ty::ReEmpty(_)) = (a, b) {
+ if a.is_empty() && b.is_empty() {
return None;
}
fn clean(&self, cx: &mut DocContext<'_>) -> Option<WherePredicate> {
let ty::OutlivesPredicate(ty, lt) = self;
- if let ty::ReEmpty(_) = lt {
+ if lt.is_empty() {
return None;
}
{
m.header.constness = hir::Constness::NotConst;
}
- MethodItem(m, Some(self.defaultness))
+ let defaultness = cx.tcx.associated_item(self.def_id).defaultness;
+ MethodItem(m, Some(defaultness))
}
hir::ImplItemKind::TyAlias(ref hir_ty) => {
let type_ = hir_ty.clean(cx);
impl<'tcx> Clean<Type> for Ty<'tcx> {
fn clean(&self, cx: &mut DocContext<'_>) -> Type {
trace!("cleaning type: {:?}", self);
- let ty = normalize(cx, self).unwrap_or(self);
+ let ty = normalize(cx, *self).unwrap_or(*self);
match *ty.kind() {
ty::Never => Primitive(PrimitiveType::Never),
ty::Bool => Primitive(PrimitiveType::Bool),
for pb in obj.projection_bounds() {
bindings.push(TypeBinding {
name: cx.tcx.associated_item(pb.item_def_id()).name,
- kind: TypeBindingKind::Equality {
- term: pb.skip_binder().term.clean(cx).into(),
- },
+ kind: TypeBindingKind::Equality { term: pb.skip_binder().term.clean(cx) },
});
}
fn clean(&self, cx: &mut DocContext<'_>) -> Constant {
// FIXME: instead of storing the stringified expression, store `self` directly instead.
Constant {
- type_: self.ty.clean(cx),
+ type_: self.ty().clean(cx),
kind: ConstantKind::TyConst { expr: self.to_string() },
}
}
// Look for equality predicates on associated types that can be merged into
// general bound predicates
equalities.retain(|&(ref lhs, ref rhs)| {
- let (self_, trait_did, name) = if let Some(p) = lhs.projection() {
- p
- } else {
+ let Some((self_, trait_did, name)) = lhs.projection() else {
return true;
};
let generic = match self_ {
/// A link that has not yet been rendered.
///
/// This link will be turned into a rendered link by [`Item::links`].
-#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+#[derive(Clone, Debug, PartialEq, Eq)]
crate struct ItemLink {
/// The original link written in the markdown
crate link: String,
// Additional documentation should be shown before the original documentation
let other_attrs = additional_attrs
.into_iter()
- .map(|(attrs, id)| attrs.iter().map(move |attr| (attr, Some(id))))
- .flatten()
+ .flat_map(|(attrs, id)| attrs.iter().map(move |attr| (attr, Some(id))))
.chain(attrs.iter().map(|attr| (attr, None)))
.filter_map(clean_attr)
.collect();
/// Checks if this is a `T::Name` path for an associated type.
crate fn is_assoc_ty(&self) -> bool {
match self.res {
- Res::SelfTy(..) if self.segments.len() != 1 => true,
+ Res::SelfTy { .. } if self.segments.len() != 1 => true,
Res::Def(DefKind::TyParam, _) if self.segments.len() != 1 => true,
Res::Def(DefKind::AssocTy, _) => true,
_ => false,
let args: Vec<_> = substs
.iter()
.filter_map(|kind| match kind.unpack() {
- GenericArgKind::Lifetime(lt) => match lt {
+ GenericArgKind::Lifetime(lt) => match *lt {
ty::ReLateBound(_, ty::BoundRegion { kind: ty::BrAnon(_), .. }) => {
Some(GenericArg::Lifetime(Lifetime::elided()))
}
})
}
-crate fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String {
- match n.val {
+crate fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
+ match n.val() {
ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs: _, promoted }) => {
let mut s = if let Some(def) = def.as_local() {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def.did);
.collect()
}
-fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: &ty::Const<'_>) -> String {
+fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: ty::Const<'_>) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
- match (ct.val, ct.ty.kind()) {
+ match (ct.val(), ct.ty().kind()) {
(ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Uint(ui)) => {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
}
(ty::ConstKind::Value(ConstValue::Scalar(int)), ty::Int(i)) => {
- let ty = tcx.lift(ct.ty).unwrap();
+ let ty = tcx.lift(ct.ty()).unwrap();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
match path.res {
Res::PrimTy(p) => Primitive(PrimitiveType::from(p)),
- Res::SelfTy(..) if path.segments.len() == 1 => Generic(kw::SelfUpper),
+ Res::SelfTy { .. } if path.segments.len() == 1 => Generic(kw::SelfUpper),
Res::Def(DefKind::TyParam, _) if path.segments.len() == 1 => Generic(path.segments[0].name),
_ => {
let _ = register_res(cx, path.res);
| Union | Mod | ForeignTy | Const | Static | Macro(..) | TraitAlias),
i,
) => (i, kind.into()),
- // This is part of a trait definition; document the trait.
- Res::SelfTy(Some(trait_def_id), _) => (trait_def_id, ItemType::Trait),
- // This is an inherent impl; it doesn't have its own page.
- Res::SelfTy(None, Some((impl_def_id, _))) => return impl_def_id,
- Res::SelfTy(None, None)
+ // This is part of a trait definition or trait impl; document the trait.
+ Res::SelfTy { trait_: Some(trait_def_id), alias_to: _ } => (trait_def_id, ItemType::Trait),
+ // This is an inherent impl or a type definition; it doesn't have its own page.
+ Res::SelfTy { trait_: None, alias_to: Some((item_def_id, _)) } => return item_def_id,
+ Res::SelfTy { trait_: None, alias_to: None }
| Res::PrimTy(_)
| Res::ToolMod
| Res::SelfCtor(_)
matches
.opt_str("default-theme")
.iter()
- .map(|theme| {
+ .flat_map(|theme| {
vec![
("use-system-theme".to_string(), "false".to_string()),
("theme".to_string(), theme.to_string()),
]
})
- .flatten()
.collect(),
matches
.opt_strs("default-setting")
output_dir: None,
file_loader: None,
diagnostic_output: DiagnosticOutput::Default,
- stderr: None,
lint_caps,
parse_sess_created: None,
register_lints: Some(box crate::lint::register_lints),
output_dir: None,
file_loader: None,
diagnostic_output: DiagnosticOutput::Default,
- stderr: None,
lint_caps,
parse_sess_created: None,
register_lints: Some(box crate::lint::register_lints),
} else {
let returns_result = everything_else.trim_end().ends_with("(())");
// Give each doctest main function a unique name.
- // This is for example needed for the tooling around `-Z instrument-coverage`.
+ // This is for example needed for the tooling around `-C instrument-coverage`.
let inner_fn_name = if let Some(test_id) = test_id {
format!("_doctest_main_{}", test_id)
} else {
};
// Note on newlines: We insert a line/newline *before*, and *after*
// the doctest and adjust the `line_offset` accordingly.
- // In the case of `-Z instrument-coverage`, this means that the generated
+ // In the case of `-C instrument-coverage`, this means that the generated
// inner `main` function spans from the doctest opening codeblock to the
// closing one. For example
// /// ``` <- start of the inner main
},
// compiler failures are test failures
should_panic: test::ShouldPanic::No,
- allow_fail: config.allow_fail,
compile_fail: config.compile_fail,
no_run,
test_type: test::TestType::DocTest,
+ #[cfg(bootstrap)]
+ allow_fail: false,
},
testfn: test::DynTestFn(box move || {
let report_unused_externs = |uext| {
}
#[inline]
- fn write_fmt(self: &mut Self, args: fmt::Arguments<'_>) -> fmt::Result {
+ fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
self.buffer.write_fmt(args)
}
}
let (mut starts, mut ends): (Vec<_>, Vec<_>) = info
.0
.into_iter()
- .map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi)))
- .flatten()
+ .flat_map(|(kind, ranges)| ranges.into_iter().map(move |(lo, hi)| ((lo, kind), hi)))
.unzip();
// Sort the sequences in document order.
let should_panic;
let ignore;
let edition;
- let kind = if let Some(Event::Start(Tag::CodeBlock(kind))) = event {
- kind
- } else {
+ let Some(Event::Start(Tag::CodeBlock(kind))) = event else {
return event;
};
self.buf.push_back((Event::Html(format!("</a></h{}>", level).into()), 0..0));
let start_tags = format!(
- "<h{level} id=\"{id}\" class=\"section-header\">\
+ "<h{level} id=\"{id}\">\
<a href=\"#{id}\">",
id = id,
level = level
crate test_harness: bool,
crate compile_fail: bool,
crate error_codes: Vec<String>,
- crate allow_fail: bool,
crate edition: Option<Edition>,
}
test_harness: false,
compile_fail: false,
error_codes: Vec::new(),
- allow_fail: false,
edition: None,
}
}
seen_rust_tags = !seen_other_tags;
}
}
- "allow_fail" => {
- data.allow_fail = true;
- seen_rust_tags = !seen_other_tags;
- }
"rust" => {
data.rust = true;
seen_rust_tags = true;
"the code block will either not be tested if not marked as a rust one \
or will be run (which you might not want)",
))
- } else if s == "allow-fail" || s == "allow_fail" || s == "allowfail" {
- Some((
- "allow_fail",
- "the code block will either not be tested if not marked as a rust one \
- or will be run (which you might not want)",
- ))
} else if s == "test-harness" || s == "test_harness" || s == "testharness" {
Some((
"test_harness",
compile_fail: true,
..Default::default()
});
- t(LangString { original: "allow_fail".into(), allow_fail: true, ..Default::default() });
t(LangString { original: "no_run,example".into(), no_run: true, ..Default::default() });
t(LangString {
original: "sh,should_panic".into(),
assert_eq!(output, expect, "original: {}", input);
}
- t(
- "# Foo bar",
- "<h2 id=\"foo-bar\" class=\"section-header\"><a href=\"#foo-bar\">Foo bar</a></h2>",
- );
+ t("# Foo bar", "<h2 id=\"foo-bar\"><a href=\"#foo-bar\">Foo bar</a></h2>");
t(
"## Foo-bar_baz qux",
- "<h3 id=\"foo-bar_baz-qux\" class=\"section-header\">\
+ "<h3 id=\"foo-bar_baz-qux\">\
<a href=\"#foo-bar_baz-qux\">Foo-bar_baz qux</a></h3>",
);
t(
"### **Foo** *bar* baz!?!& -_qux_-%",
- "<h4 id=\"foo-bar-baz--qux-\" class=\"section-header\">\
+ "<h4 id=\"foo-bar-baz--qux-\">\
<a href=\"#foo-bar-baz--qux-\"><strong>Foo</strong> \
<em>bar</em> baz!?!& -<em>qux</em>-%</a>\
</h4>",
);
t(
"#### **Foo?** & \\*bar?!* _`baz`_ ❤ #qux",
- "<h5 id=\"foo--bar--baz--qux\" class=\"section-header\">\
+ "<h5 id=\"foo--bar--baz--qux\">\
<a href=\"#foo--bar--baz--qux\"><strong>Foo?</strong> & *bar?!* \
<em><code>baz</code></em> ❤ #qux</a>\
</h5>",
assert_eq!(output, expect, "original: {}", input);
}
- t(
- &mut map,
- "# Example",
- "<h2 id=\"example\" class=\"section-header\"><a href=\"#example\">Example</a></h2>",
- );
- t(
- &mut map,
- "# Panics",
- "<h2 id=\"panics\" class=\"section-header\"><a href=\"#panics\">Panics</a></h2>",
- );
- t(
- &mut map,
- "# Example",
- "<h2 id=\"example-1\" class=\"section-header\"><a href=\"#example-1\">Example</a></h2>",
- );
- t(
- &mut map,
- "# Search",
- "<h2 id=\"search-1\" class=\"section-header\"><a href=\"#search-1\">Search</a></h2>",
- );
- t(
- &mut map,
- "# Example",
- "<h2 id=\"example-2\" class=\"section-header\"><a href=\"#example-2\">Example</a></h2>",
- );
- t(
- &mut map,
- "# Panics",
- "<h2 id=\"panics-1\" class=\"section-header\"><a href=\"#panics-1\">Panics</a></h2>",
- );
+ t(&mut map, "# Example", "<h2 id=\"example\"><a href=\"#example\">Example</a></h2>");
+ t(&mut map, "# Panics", "<h2 id=\"panics\"><a href=\"#panics\">Panics</a></h2>");
+ t(&mut map, "# Example", "<h2 id=\"example-1\"><a href=\"#example-1\">Example</a></h2>");
+ t(&mut map, "# Search", "<h2 id=\"search-1\"><a href=\"#search-1\">Search</a></h2>");
+ t(&mut map, "# Example", "<h2 id=\"example-2\"><a href=\"#example-2\">Example</a></h2>");
+ t(&mut map, "# Panics", "<h2 id=\"panics-1\"><a href=\"#panics-1\">Panics</a></h2>");
}
#[test]
if out.is_empty() {
write!(
&mut out,
- "<div class=\"notable\">Notable traits for {}</div>\
+ "<span class=\"notable\">Notable traits for {}</span>\
<code class=\"content\">",
impl_.for_.print(cx)
);
out.insert_str(
0,
"<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ\
- <div class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
+ <span class=\"notable-traits-tooltiptext\"><span class=\"docblock\">",
);
- out.push_str("</code></span></div></span></span>");
+ out.push_str("</code></span></span></span></span>");
}
out.into_inner()
.map(|item| format!("{}.{}", item.type_(), name));
write!(
w,
- "<div id=\"{}\" class=\"{}{} has-srclink\">",
+ "<section id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class,
);
render_rightside(w, cx, item, containing_item, render_mode);
render_mode,
);
w.write_str("</h4>");
- w.write_str("</div>");
+ w.write_str("</section>");
}
}
clean::TypedefItem(ref tydef, _) => {
let id = cx.derive_id(source_id.clone());
write!(
w,
- "<div id=\"{}\" class=\"{}{} has-srclink\">",
+ "<section id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class
);
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
cx,
);
w.write_str("</h4>");
- w.write_str("</div>");
+ w.write_str("</section>");
}
clean::AssocConstItem(ref ty, _) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
write!(
w,
- "<div id=\"{}\" class=\"{}{} has-srclink\">",
+ "<section id=\"{}\" class=\"{}{} has-srclink\">",
id, item_type, in_trait_class
);
render_rightside(w, cx, item, containing_item, render_mode);
cx,
);
w.write_str("</h4>");
- w.write_str("</div>");
+ w.write_str("</section>");
}
clean::AssocTypeItem(ref bounds, ref default) => {
let source_id = format!("{}.{}", item_type, name);
let id = cx.derive_id(source_id.clone());
- write!(w, "<div id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
+ write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class,);
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
w.write_str("<h4 class=\"code-header\">");
assoc_type(
cx,
);
w.write_str("</h4>");
- w.write_str("</div>");
+ w.write_str("</section>");
}
clean::StrippedItem(..) => return,
_ => panic!("can't make docs for trait item with name {:?}", item.name),
RenderMode::ForDeref { .. } => (None, None),
};
- write!(w, "<div class=\"rightside\">");
+ let mut rightside = Buffer::new();
let has_stability = render_stability_since_raw(
- w,
+ &mut rightside,
item.stable_since(tcx),
const_stability,
containing_item.stable_since(tcx),
const_stable_since,
);
- let mut tmp_buf = Buffer::empty_from(w);
- write_srclink(cx, item, &mut tmp_buf);
- if has_stability && !tmp_buf.is_empty() {
- w.write_str(" · ");
+ let mut srclink = Buffer::empty_from(w);
+ write_srclink(cx, item, &mut srclink);
+ if has_stability && !srclink.is_empty() {
+ rightside.write_str(" · ");
+ }
+ rightside.push_buffer(srclink);
+ if !rightside.is_empty() {
+ write!(w, "<span class=\"rightside\">{}</span>", rightside.into_inner());
}
- w.push_buffer(tmp_buf);
- w.write_str("</div>");
}
pub(crate) fn render_impl_summary(
} else {
format!(" data-aliases=\"{}\"", aliases.join(","))
};
- write!(w, "<div id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
+ write!(w, "<section id=\"{}\" class=\"impl has-srclink\"{}>", id, aliases);
render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
write!(w, "<a href=\"#{}\" class=\"anchor\"></a>", id);
write!(w, "<h3 class=\"code-header in-band\">");
let is_trait = i.inner_impl().trait_.is_some();
if is_trait {
if let Some(portability) = portability(&i.impl_item, Some(parent)) {
- write!(w, "<div class=\"item-info\">{}</div>", portability);
+ write!(w, "<span class=\"item-info\">{}</span>", portability);
}
}
- w.write_str("</div>");
+ w.write_str("</section>");
}
fn print_sidebar(cx: &Context<'_>, it: &clean::Item, buffer: &mut Buffer) {
// to navigate the documentation (though slightly inefficiently).
if !it.is_mod() {
- buffer.write_str("<h2 class=\"location\">In ");
- for (i, name) in cx.current.iter().take(parentlen).enumerate() {
- if i > 0 {
- buffer.write_str("::<wbr>");
- }
- write!(
- buffer,
- "<a href=\"{}index.html\">{}</a>",
- &cx.root_path()[..(cx.current.len() - i - 1) * 3],
- *name
- );
- }
- buffer.write_str("</h2>");
+ let path: String = cx.current.iter().map(|s| s.as_str()).intersperse("::").collect();
+
+ write!(buffer, "<h2 class=\"location\"><a href=\"index.html\">In {}</a></h2>", path);
}
// Sidebar refers to the enclosing module, not this module.
// We want links' order to be reproducible so we don't use unstable sort.
assoc_consts.sort();
- out.push_str(
- "<h3 class=\"sidebar-title\">\
- <a href=\"#implementations\">Associated Constants</a>\
- </h3>\
- <div class=\"sidebar-links\">",
+ print_sidebar_block(
+ out,
+ "implementations",
+ "Associated Constants",
+ assoc_consts.iter(),
);
- for line in assoc_consts {
- write!(out, "{}", line);
- }
- out.push_str("</div>");
}
let mut methods = v
.iter()
// We want links' order to be reproducible so we don't use unstable sort.
methods.sort();
- out.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#implementations\">Methods</a></h3>\
- <div class=\"sidebar-links\">",
- );
- for line in methods {
- write!(out, "{}", line);
- }
- out.push_str("</div>");
+ print_sidebar_block(out, "implementations", "Methods", methods.iter());
}
}
ret
};
- let write_sidebar_links = |out: &mut Buffer, links: Vec<String>| {
- out.push_str("<div class=\"sidebar-links\">");
- for link in links {
- out.push_str(&link);
- }
- out.push_str("</div>");
- };
-
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
let blanket_format = format_impls(blanket_impl);
if !concrete_format.is_empty() {
- out.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#trait-implementations\">\
- Trait Implementations</a></h3>",
+ print_sidebar_block(
+ out,
+ "trait-implementations",
+ "Trait Implementations",
+ concrete_format.iter(),
);
- write_sidebar_links(out, concrete_format);
}
if !synthetic_format.is_empty() {
- out.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#synthetic-implementations\">\
- Auto Trait Implementations</a></h3>",
+ print_sidebar_block(
+ out,
+ "synthetic-implementations",
+ "Auto Trait Implementations",
+ synthetic_format.iter(),
);
- write_sidebar_links(out, synthetic_format);
}
if !blanket_format.is_empty() {
- out.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#blanket-implementations\">\
- Blanket Implementations</a></h3>",
+ print_sidebar_block(
+ out,
+ "blanket-implementations",
+ "Blanket Implementations",
+ blanket_format.iter(),
);
- write_sidebar_links(out, blanket_format);
}
}
}
} else {
"deref-methods"
};
- write!(
- out,
- "<h3 class=\"sidebar-title\"><a href=\"#{}\">Methods from {}<Target={}></a></h3>",
- id,
+ let title = format!(
+ "Methods from {}<Target={}>",
Escape(&format!("{:#}", impl_.inner_impl().trait_.as_ref().unwrap().print(cx))),
Escape(&format!("{:#}", real_target.print(cx))),
);
// We want links' order to be reproducible so we don't use unstable sort.
ret.sort();
- out.push_str("<div class=\"sidebar-links\">");
- for link in ret {
- write!(out, "{}", link);
- }
- out.push_str("</div>");
+ print_sidebar_block(out, id, &title, ret.iter());
}
}
let fields = get_struct_fields_name(&s.fields);
if !fields.is_empty() {
- if let CtorKind::Fictive = s.struct_type {
- sidebar.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#fields\">Fields</a></h3>\
- <div class=\"sidebar-links\">",
- );
-
- for field in fields {
- sidebar.push_str(&field);
+ match s.struct_type {
+ CtorKind::Fictive => {
+ print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
}
-
- sidebar.push_str("</div>");
- } else if let CtorKind::Fn = s.struct_type {
- sidebar
- .push_str("<h3 class=\"sidebar-title\"><a href=\"#fields\">Tuple Fields</a></h3>");
+ CtorKind::Fn => print_sidebar_title(&mut sidebar, "fields", "Tuple Fields"),
+ CtorKind::Const => {}
}
}
sidebar_assoc_items(cx, &mut sidebar, it);
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
+ write!(buf, "<section>{}</section>", sidebar.into_inner());
}
}
}
}
+/// Don't call this function directly!!! Use `print_sidebar_title` or `print_sidebar_block` instead!
+fn print_sidebar_title_inner(buf: &mut Buffer, id: &str, title: &str) {
+ write!(
+ buf,
+ "<h3 class=\"sidebar-title\">\
+ <a href=\"#{}\">{}</a>\
+ </h3>",
+ id, title
+ );
+}
+
+fn print_sidebar_title(buf: &mut Buffer, id: &str, title: &str) {
+ buf.push_str("<div class=\"block\">");
+ print_sidebar_title_inner(buf, id, title);
+ buf.push_str("</div>");
+}
+
+fn print_sidebar_block(
+ buf: &mut Buffer,
+ id: &str,
+ title: &str,
+ items: impl Iterator<Item = impl fmt::Display>,
+) {
+ buf.push_str("<div class=\"block\">");
+ print_sidebar_title_inner(buf, id, title);
+ buf.push_str("<ul>");
+ for item in items {
+ write!(buf, "<li>{}</li>", item);
+ }
+ buf.push_str("</ul></div>");
+}
+
fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean::Trait) {
- buf.write_str("<div class=\"block items\">");
+ buf.write_str("<section>");
fn print_sidebar_section(
out: &mut Buffer,
items: &[clean::Item],
- before: &str,
+ id: &str,
+ title: &str,
filter: impl Fn(&clean::Item) -> bool,
- write: impl Fn(&mut Buffer, &str),
- after: &str,
+ mapper: impl Fn(&str) -> String,
) {
- let mut items = items
+ let mut items: Vec<&str> = items
.iter()
.filter_map(|m| match m.name {
Some(ref name) if filter(m) => Some(name.as_str()),
if !items.is_empty() {
items.sort_unstable();
- out.push_str(before);
- for item in items.into_iter() {
- write(out, &item);
- }
- out.push_str(after);
+ print_sidebar_block(out, id, title, items.into_iter().map(mapper));
}
}
print_sidebar_section(
buf,
&t.items,
- "<h3 class=\"sidebar-title\"><a href=\"#associated-types\">\
- Associated Types</a></h3><div class=\"sidebar-links\">",
+ "associated-types",
+ "Associated Types",
|m| m.is_associated_type(),
- |out, sym| write!(out, "<a href=\"#associatedtype.{0}\">{0}</a>", sym),
- "</div>",
+ |sym| format!("<a href=\"#associatedtype.{0}\">{0}</a>", sym),
);
print_sidebar_section(
buf,
&t.items,
- "<h3 class=\"sidebar-title\"><a href=\"#associated-const\">\
- Associated Constants</a></h3><div class=\"sidebar-links\">",
+ "associated-const",
+ "Associated Constants",
|m| m.is_associated_const(),
- |out, sym| write!(out, "<a href=\"#associatedconstant.{0}\">{0}</a>", sym),
- "</div>",
+ |sym| format!("<a href=\"#associatedconstant.{0}\">{0}</a>", sym),
);
print_sidebar_section(
buf,
&t.items,
- "<h3 class=\"sidebar-title\"><a href=\"#required-methods\">\
- Required Methods</a></h3><div class=\"sidebar-links\">",
+ "required-methods",
+ "Required Methods",
|m| m.is_ty_method(),
- |out, sym| write!(out, "<a href=\"#tymethod.{0}\">{0}</a>", sym),
- "</div>",
+ |sym| format!("<a href=\"#tymethod.{0}\">{0}</a>", sym),
);
print_sidebar_section(
buf,
&t.items,
- "<h3 class=\"sidebar-title\"><a href=\"#provided-methods\">\
- Provided Methods</a></h3><div class=\"sidebar-links\">",
+ "provided-methods",
+ "Provided Methods",
|m| m.is_method(),
- |out, sym| write!(out, "<a href=\"#method.{0}\">{0}</a>", sym),
- "</div>",
+ |sym| format!("<a href=\"#method.{0}\">{0}</a>", sym),
);
let cache = cx.cache();
if !res.is_empty() {
res.sort();
- buf.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#foreign-impls\">\
- Implementations on Foreign Types</a></h3>\
- <div class=\"sidebar-links\">",
+ print_sidebar_block(
+ buf,
+ "foreign-impls",
+ "Implementations on Foreign Types",
+ res.iter().map(|(name, id)| format!("<a href=\"#{}\">{}</a>", id, Escape(&name))),
);
- for (name, id) in res.into_iter() {
- write!(buf, "<a href=\"#{}\">{}</a>", id, Escape(&name));
- }
- buf.push_str("</div>");
}
}
sidebar_assoc_items(cx, buf, it);
- buf.push_str("<h3 class=\"sidebar-title\"><a href=\"#implementors\">Implementors</a></h3>");
+ print_sidebar_title(buf, "implementors", "Implementors");
if t.is_auto {
- buf.push_str(
- "<h3 class=\"sidebar-title\"><a \
- href=\"#synthetic-implementors\">Auto Implementors</a></h3>",
- );
+ print_sidebar_title(buf, "synthetic-implementors", "Auto Implementors");
}
- buf.push_str("</div>")
+ buf.push_str("</section>")
}
fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
sidebar_assoc_items(cx, &mut sidebar, it);
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
+ write!(buf, "<section>{}</section>", sidebar.into_inner());
}
}
sidebar_assoc_items(cx, &mut sidebar, it);
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
+ write!(buf, "<section>{}</section>", sidebar.into_inner());
}
}
let fields = get_struct_fields_name(&u.fields);
if !fields.is_empty() {
- sidebar.push_str(
- "<h3 class=\"sidebar-title\"><a href=\"#fields\">Fields</a></h3>\
- <div class=\"sidebar-links\">",
- );
-
- for field in fields {
- sidebar.push_str(&field);
- }
-
- sidebar.push_str("</div>");
+ print_sidebar_block(&mut sidebar, "fields", "Fields", fields.iter());
}
sidebar_assoc_items(cx, &mut sidebar, it);
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
+ write!(buf, "<section>{}</section>", sidebar.into_inner());
}
}
.collect::<Vec<_>>();
if !variants.is_empty() {
variants.sort_unstable();
- sidebar.push_str(&format!(
- "<h3 class=\"sidebar-title\"><a href=\"#variants\">Variants</a></h3>\
- <div class=\"sidebar-links\">{}</div>",
- variants.join(""),
- ));
+ print_sidebar_block(&mut sidebar, "variants", "Variants", variants.iter());
}
sidebar_assoc_items(cx, &mut sidebar, it);
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
- }
-}
+ write!(buf, "<section>{}</section>", sidebar.into_inner());
+ }
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+enum ItemSection {
+ Reexports,
+ PrimitiveTypes,
+ Modules,
+ Macros,
+ Structs,
+ Enums,
+ Constants,
+ Statics,
+ Traits,
+ Functions,
+ TypeDefinitions,
+ Unions,
+ Implementations,
+ TypeMethods,
+ Methods,
+ StructFields,
+ Variants,
+ AssociatedTypes,
+ AssociatedConstants,
+ ForeignTypes,
+ Keywords,
+ OpaqueTypes,
+ AttributeMacros,
+ DeriveMacros,
+ TraitAliases,
+}
+
+impl ItemSection {
+ const ALL: &'static [Self] = {
+ use ItemSection::*;
+ // NOTE: The order here affects the order in the UI.
+ &[
+ Reexports,
+ PrimitiveTypes,
+ Modules,
+ Macros,
+ Structs,
+ Enums,
+ Constants,
+ Statics,
+ Traits,
+ Functions,
+ TypeDefinitions,
+ Unions,
+ Implementations,
+ TypeMethods,
+ Methods,
+ StructFields,
+ Variants,
+ AssociatedTypes,
+ AssociatedConstants,
+ ForeignTypes,
+ Keywords,
+ OpaqueTypes,
+ AttributeMacros,
+ DeriveMacros,
+ TraitAliases,
+ ]
+ };
-fn item_ty_to_strs(ty: ItemType) -> (&'static str, &'static str) {
+ fn id(self) -> &'static str {
+ match self {
+ Self::Reexports => "reexports",
+ Self::Modules => "modules",
+ Self::Structs => "structs",
+ Self::Unions => "unions",
+ Self::Enums => "enums",
+ Self::Functions => "functions",
+ Self::TypeDefinitions => "types",
+ Self::Statics => "statics",
+ Self::Constants => "constants",
+ Self::Traits => "traits",
+ Self::Implementations => "impls",
+ Self::TypeMethods => "tymethods",
+ Self::Methods => "methods",
+ Self::StructFields => "fields",
+ Self::Variants => "variants",
+ Self::Macros => "macros",
+ Self::PrimitiveTypes => "primitives",
+ Self::AssociatedTypes => "associated-types",
+ Self::AssociatedConstants => "associated-consts",
+ Self::ForeignTypes => "foreign-types",
+ Self::Keywords => "keywords",
+ Self::OpaqueTypes => "opaque-types",
+ Self::AttributeMacros => "attributes",
+ Self::DeriveMacros => "derives",
+ Self::TraitAliases => "trait-aliases",
+ }
+ }
+
+ fn name(self) -> &'static str {
+ match self {
+ Self::Reexports => "Re-exports",
+ Self::Modules => "Modules",
+ Self::Structs => "Structs",
+ Self::Unions => "Unions",
+ Self::Enums => "Enums",
+ Self::Functions => "Functions",
+ Self::TypeDefinitions => "Type Definitions",
+ Self::Statics => "Statics",
+ Self::Constants => "Constants",
+ Self::Traits => "Traits",
+ Self::Implementations => "Implementations",
+ Self::TypeMethods => "Type Methods",
+ Self::Methods => "Methods",
+ Self::StructFields => "Struct Fields",
+ Self::Variants => "Variants",
+ Self::Macros => "Macros",
+ Self::PrimitiveTypes => "Primitive Types",
+ Self::AssociatedTypes => "Associated Types",
+ Self::AssociatedConstants => "Associated Constants",
+ Self::ForeignTypes => "Foreign Types",
+ Self::Keywords => "Keywords",
+ Self::OpaqueTypes => "Opaque Types",
+ Self::AttributeMacros => "Attribute Macros",
+ Self::DeriveMacros => "Derive Macros",
+ Self::TraitAliases => "Trait Aliases",
+ }
+ }
+}
+
+fn item_ty_to_section(ty: ItemType) -> ItemSection {
match ty {
- ItemType::ExternCrate | ItemType::Import => ("reexports", "Re-exports"),
- ItemType::Module => ("modules", "Modules"),
- ItemType::Struct => ("structs", "Structs"),
- ItemType::Union => ("unions", "Unions"),
- ItemType::Enum => ("enums", "Enums"),
- ItemType::Function => ("functions", "Functions"),
- ItemType::Typedef => ("types", "Type Definitions"),
- ItemType::Static => ("statics", "Statics"),
- ItemType::Constant => ("constants", "Constants"),
- ItemType::Trait => ("traits", "Traits"),
- ItemType::Impl => ("impls", "Implementations"),
- ItemType::TyMethod => ("tymethods", "Type Methods"),
- ItemType::Method => ("methods", "Methods"),
- ItemType::StructField => ("fields", "Struct Fields"),
- ItemType::Variant => ("variants", "Variants"),
- ItemType::Macro => ("macros", "Macros"),
- ItemType::Primitive => ("primitives", "Primitive Types"),
- ItemType::AssocType => ("associated-types", "Associated Types"),
- ItemType::AssocConst => ("associated-consts", "Associated Constants"),
- ItemType::ForeignType => ("foreign-types", "Foreign Types"),
- ItemType::Keyword => ("keywords", "Keywords"),
- ItemType::OpaqueTy => ("opaque-types", "Opaque Types"),
- ItemType::ProcAttribute => ("attributes", "Attribute Macros"),
- ItemType::ProcDerive => ("derives", "Derive Macros"),
- ItemType::TraitAlias => ("trait-aliases", "Trait aliases"),
+ ItemType::ExternCrate | ItemType::Import => ItemSection::Reexports,
+ ItemType::Module => ItemSection::Modules,
+ ItemType::Struct => ItemSection::Structs,
+ ItemType::Union => ItemSection::Unions,
+ ItemType::Enum => ItemSection::Enums,
+ ItemType::Function => ItemSection::Functions,
+ ItemType::Typedef => ItemSection::TypeDefinitions,
+ ItemType::Static => ItemSection::Statics,
+ ItemType::Constant => ItemSection::Constants,
+ ItemType::Trait => ItemSection::Traits,
+ ItemType::Impl => ItemSection::Implementations,
+ ItemType::TyMethod => ItemSection::TypeMethods,
+ ItemType::Method => ItemSection::Methods,
+ ItemType::StructField => ItemSection::StructFields,
+ ItemType::Variant => ItemSection::Variants,
+ ItemType::Macro => ItemSection::Macros,
+ ItemType::Primitive => ItemSection::PrimitiveTypes,
+ ItemType::AssocType => ItemSection::AssociatedTypes,
+ ItemType::AssocConst => ItemSection::AssociatedConstants,
+ ItemType::ForeignType => ItemSection::ForeignTypes,
+ ItemType::Keyword => ItemSection::Keywords,
+ ItemType::OpaqueTy => ItemSection::OpaqueTypes,
+ ItemType::ProcAttribute => ItemSection::AttributeMacros,
+ ItemType::ProcDerive => ItemSection::DeriveMacros,
+ ItemType::TraitAlias => ItemSection::TraitAliases,
ItemType::Generic => unreachable!(),
}
}
fn sidebar_module(buf: &mut Buffer, items: &[clean::Item]) {
let mut sidebar = String::new();
- // Re-exports are handled a bit differently because they can be extern crates or imports.
- if items.iter().any(|it| {
- it.name.is_some()
- && (it.type_() == ItemType::ExternCrate
- || (it.type_() == ItemType::Import && !it.is_stripped()))
- }) {
- let (id, name) = item_ty_to_strs(ItemType::Import);
- sidebar.push_str(&format!("<li><a href=\"#{}\">{}</a></li>", id, name));
- }
-
- // ordering taken from item_module, reorder, where it prioritized elements in a certain order
- // to print its headings
- for &myty in &[
- ItemType::Primitive,
- ItemType::Module,
- ItemType::Macro,
- ItemType::Struct,
- ItemType::Enum,
- ItemType::Constant,
- ItemType::Static,
- ItemType::Trait,
- ItemType::Function,
- ItemType::Typedef,
- ItemType::Union,
- ItemType::Impl,
- ItemType::TyMethod,
- ItemType::Method,
- ItemType::StructField,
- ItemType::Variant,
- ItemType::AssocType,
- ItemType::AssocConst,
- ItemType::ForeignType,
- ItemType::Keyword,
- ] {
- if items.iter().any(|it| !it.is_stripped() && it.type_() == myty && it.name.is_some()) {
- let (id, name) = item_ty_to_strs(myty);
- sidebar.push_str(&format!("<li><a href=\"#{}\">{}</a></li>", id, name));
- }
+ let item_sections_in_use: FxHashSet<_> = items
+ .iter()
+ .filter(|it| !it.is_stripped() && it.name.is_some())
+ .map(|it| item_ty_to_section(it.type_()))
+ .collect();
+ for &sec in ItemSection::ALL.iter().filter(|sec| item_sections_in_use.contains(sec)) {
+ sidebar.push_str(&format!("<li><a href=\"#{}\">{}</a></li>", sec.id(), sec.name()));
}
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\"><ul>{}</ul></div>", sidebar);
+ write!(
+ buf,
+ "<section>\
+ <div class=\"block\">\
+ <ul>{}</ul>\
+ </div>\
+ </section>",
+ sidebar
+ );
}
}
sidebar_assoc_items(cx, &mut sidebar, it);
if !sidebar.is_empty() {
- write!(buf, "<div class=\"block items\">{}</div>", sidebar.into_inner());
+ write!(buf, "<section>{}</section>", sidebar.into_inner());
}
}
w,
"<div class=\"docblock scraped-example-list\">\
<span></span>\
- <h5 id=\"{id}\" class=\"section-header\">\
+ <h5 id=\"{id}\">\
<a href=\"#{id}\">Examples found in repository</a>\
</h5>",
id = id
use rustc_target::abi::{Layout, Primitive, TagEncoding, Variants};
use super::{
- collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_strs, notable_traits_decl,
- render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
- render_impl, render_stability_since_raw, write_srclink, AssocItemLink, Context,
- ImplRenderingParameters,
+ collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section,
+ notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code,
+ render_attributes_in_pre, render_impl, render_stability_since_raw, write_srclink,
+ AssocItemLink, Context, ImplRenderingParameters,
};
use crate::clean;
use crate::formats::item_type::ItemType;
) -> Ordering {
let ty1 = i1.type_();
let ty2 = i2.type_();
- if ty1 != ty2 {
+ if item_ty_to_section(ty1) != item_ty_to_section(ty2)
+ || (ty1 != ty2 && (ty1 == ItemType::ExternCrate || ty2 == ItemType::ExternCrate))
+ {
return (reorder(ty1), idx1).cmp(&(reorder(ty2), idx2));
}
let s1 = i1.stability(tcx).as_ref().map(|s| s.level);
});
debug!("{:?}", indices);
- let mut curty = None;
+ let mut last_section = None;
for &idx in &indices {
let myitem = &items[idx];
continue;
}
- let myty = Some(myitem.type_());
- if curty == Some(ItemType::ExternCrate) && myty == Some(ItemType::Import) {
- // Put `extern crate` and `use` re-exports in the same section.
- curty = myty;
- } else if myty != curty {
- if curty.is_some() {
+ let my_section = item_ty_to_section(myitem.type_());
+ if Some(my_section) != last_section {
+ if last_section.is_some() {
w.write_str(ITEM_TABLE_CLOSE);
}
- curty = myty;
- let (short, name) = item_ty_to_strs(myty.unwrap());
+ last_section = Some(my_section);
write!(
w,
"<h2 id=\"{id}\" class=\"small-section-header\">\
<a href=\"#{id}\">{name}</a>\
</h2>\n{}",
ITEM_TABLE_OPEN,
- id = cx.derive_id(short.to_owned()),
- name = name
+ id = cx.derive_id(my_section.id().to_owned()),
+ name = my_section.name(),
);
}
}
}
- if curty.is_some() {
+ if last_section.is_some() {
w.write_str(ITEM_TABLE_CLOSE);
}
}
<ul>",
);
- let adt = if let Adt(adt, _) = ty_layout.ty.kind() {
- adt
- } else {
+ let Adt(adt, _) = ty_layout.ty.kind() else {
span_bug!(tcx.def_span(ty_def_id), "not an adt")
};
lastpathid += 1;
if let Some(&(ref fqp, short)) = paths.get(&defid) {
- crate_paths.push((short, fqp.last().unwrap().clone()));
+ crate_paths.push((short, *fqp.last().unwrap()));
Some(pathid)
} else {
None
/* General structure and fonts */
body {
- font: 1rem/1.4 "Source Serif 4", NanumBarunGothic, serif;
+ /* Line spacing at least 1.5 per Web Content Accessibility Guidelines
+ https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */
+ font: 1rem/1.5 "Source Serif 4", NanumBarunGothic, serif;
margin: 0;
position: relative;
/* We use overflow-wrap: break-word for Safari, which doesn't recognize
}
h1 {
- font-size: 1.5rem;
+ font-size: 1.5rem; /* 24px */
}
h2 {
- font-size: 1.4rem;
+ font-size: 1.375rem; /* 22px */
}
h3 {
- font-size: 1.3rem;
+ font-size: 1.25rem; /* 20px */
}
h1, h2, h3, h4, h5, h6 {
font-weight: 500;
border-bottom: 1px solid;
}
h3.code-header {
- font-size: 1.1rem;
+ font-size: 1.125rem; /* 18px */
}
h4.code-header {
font-size: 1rem;
}
-h3.code-header, h4.code-header {
+.code-header {
font-weight: 600;
border-bottom-style: none;
- padding: 0;
margin: 0;
+ padding: 0;
+ margin-top: 0.6em;
+ margin-bottom: 0.4em;
}
.impl,
.impl-items .method,
.methods .associatedtype {
flex-basis: 100%;
font-weight: 600;
- margin-top: 16px;
- margin-bottom: 10px;
position: relative;
}
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
}
-.content ul.crate a.crate {
- font-size: 1rem/1.6;
-}
-
ol, ul {
- padding-left: 25px;
+ padding-left: 24px;
}
ul ul, ol ul, ul ol, ol ol {
- margin-bottom: .6em;
+ margin-bottom: .625em;
}
p {
- margin: 0 0 .6em 0;
+ /* Paragraph spacing at least 1.5 times line spacing per Web Content Accessibility Guidelines.
+ Line-height is 1.5rem, so line spacing is .5rem; .75em is 1.5 times that.
+ https://www.w3.org/WAI/WCAG21/Understanding/visual-presentation.html */
+ margin: 0 0 .75em 0;
}
summary {
}
.docblock code, .docblock-short code {
border-radius: 3px;
- padding: 0 0.1em;
+ padding: 0 0.125em;
}
.docblock pre code, .docblock-short pre code {
padding: 0;
}
.sidebar {
- font-size: 0.9rem;
+ font-size: 0.875rem;
width: 250px;
min-width: 200px;
overflow-y: scroll;
.location a:first-of-type {
font-weight: 500;
}
-.location a:hover {
- text-decoration: underline;
-}
.block {
padding: 0;
}
.block ul, .block li {
padding: 0;
+ margin: 0;
list-style: none;
}
-.block a {
+.block a,
+h2.location a {
display: block;
- padding: 0.3em;
- margin-left: -0.3em;
+ padding: 0.25rem;
+ margin-left: -0.25rem;
text-overflow: ellipsis;
overflow: hidden;
}
-.sidebar-links a {
- white-space: nowrap;
-}
-
.sidebar h2 {
border-bottom: none;
font-weight: 500;
padding: 0;
margin: 0;
- margin-top: 1rem;
- margin-bottom: 1rem;
+ margin-top: 0.7rem;
+ margin-bottom: 0.7rem;
}
.sidebar h3 {
- font-size: 1.1rem;
+ font-size: 1.125rem; /* 18px */
font-weight: 500;
padding: 0;
margin: 0;
- margin-top: 0.5rem;
- margin-bottom: 0.25rem;
}
-.sidebar-links,
-.block {
+.sidebar-elems .block {
margin-bottom: 2em;
}
+.sidebar-elems .block li a {
+ white-space: nowrap;
+}
+
.mobile-topbar {
display: none;
}
white-space: pre-wrap;
}
-.top-doc .docblock h2 { font-size: 1.3rem; }
-.top-doc .docblock h3 { font-size: 1.15rem; }
+.top-doc .docblock h2 { font-size: 1.375rem; }
+.top-doc .docblock h3 { font-size: 1.25; }
.top-doc .docblock h4,
.top-doc .docblock h5 {
- font-size: 1.1rem;
+ font-size: 1.125rem;
}
.top-doc .docblock h6 {
font-size: 1rem;
}
.docblock h5 { font-size: 1rem; }
-.docblock h6 { font-size: 0.95rem; }
+.docblock h6 { font-size: 0.875rem; }
.docblock {
margin-left: 24px;
.content .out-of-band {
flex-grow: 0;
- font-size: 1.15rem;
+ font-size: 1.125rem;
font-weight: normal;
float: right;
}
-.method > .code-header, .trait-impl > .code-header, .invisible > .code-header {
+.method > .code-header, .trait-impl > .code-header {
max-width: calc(100% - 41px);
display: block;
}
.content td { vertical-align: top; }
.content td:first-child { padding-right: 20px; }
.content td p:first-child { margin-top: 0; }
-.content td h1, .content td h2 { margin-left: 0; font-size: 1.1rem; }
+.content td h1, .content td h2 { margin-left: 0; font-size: 1.125rem; }
.content tr:first-child td { border-top: 0; }
.docblock table {
.content .fn .where,
.content .where.fmt-newline {
display: block;
- font-size: 0.8rem;
+ font-size: 0.875rem;
}
.content .methods > div:not(.notable-traits):not(.method) {
}
.content .item-info code {
- font-size: 0.81rem;
+ font-size: 0.875rem;
}
.content .item-info {
position: relative;
- margin-left: 33px;
+ margin-left: 24px;
}
.sub-variant > div > .item-info {
margin-top: initial;
}
-.content .item-info::before {
- content: '⬑';
- font-size: 1.5625rem;
- position: absolute;
- top: -6px;
- left: -19px;
-}
-
.content .impl-items .docblock, .content .impl-items .item-info {
margin-bottom: .6em;
}
#main-content > .item-info {
margin-top: 0;
+ margin-left: 0;
}
nav.sub {
text-decoration: underline;
}
-.invisible > .srclink,
-.method > .code-header + .srclink {
- position: absolute;
- top: 0;
- right: 0;
- font-size: 1.0625rem;
- font-weight: normal;
-}
-
.block a.current.crate { font-weight: 500; }
/* In most contexts we use `overflow-wrap: anywhere` to ensure that we can wrap
display: table-cell;
}
.item-left {
- padding-right: 1.2rem;
+ padding-right: 1.25rem;
}
.search-container {
#crate-search {
min-width: 115px;
margin-top: 5px;
- margin-left: 0.2em;
- padding-left: 0.3em;
+ margin-left: 0.25em;
+ padding-left: 0.3125em;
padding-right: 23px;
border: 0;
border-radius: 4px;
outline: none;
border: 1px solid;
border-radius: 2px;
- padding: 5px 8px;
- font-size: 1.0625rem;
+ padding: 8px;
+ font-size: 1rem;
transition: border-color 300ms ease;
width: 100%;
}
}
.item-info .stab {
- display: table;
+ display: inline-block;
}
.stab {
padding: 3px;
margin-bottom: 5px;
- font-size: 0.9rem;
+ font-size: 0.875rem;
font-weight: normal;
}
.stab p {
}
.stab .emoji {
- font-size: 1.2rem;
+ font-size: 1.25rem;
}
/* Black one-pixel outline around emoji shapes */
.import-item .stab {
border-radius: 3px;
display: inline-block;
- font-size: 0.8rem;
+ font-size: 0.875rem;
line-height: 1.2;
margin-bottom: 0;
- margin-left: .3em;
+ margin-left: 0.3125em;
padding: 2px;
vertical-align: text-bottom;
}
font-weight: normal;
font-size: 1rem;
}
-.impl .srclink {
- font-size: 1.0625rem;
-}
.rightside {
float: right;
}
-.has-srclink {
- font-size: 1rem;
- margin-bottom: 12px;
- /* Push the src link out to the right edge consistently */
- justify-content: space-between;
-}
-
.variants_table {
width: 100%;
}
position: absolute;
padding: 5px 10px 5px 10px;
border-radius: 5px;
- font-size: 1.3rem;
+ font-size: 1.375rem;
top: 5px;
right: 5px;
z-index: 1;
a.test-arrow:hover{
text-decoration: none;
}
-.section-header:hover a:before {
- position: absolute;
- left: -25px;
- padding-right: 10px; /* avoid gap that causes hover to disappear */
- content: '\2002\00a7\2002';
-}
-
-.section-header:hover a {
- text-decoration: none;
-}
.code-attribute {
font-weight: 300;
h3.variant {
font-weight: 600;
- font-size: 1.1rem;
+ font-size: 1.125rem;
margin-bottom: 10px;
border-bottom: none;
}
margin-top: 3px;
}
-.top-doc .docblock > .section-header:first-child {
- margin-left: 15px;
-}
-.top-doc .docblock > .section-header:first-child:hover > a:before {
- left: -10px;
-}
-
-.docblock > .section-header:first-child {
- margin-top: 0;
-}
-
:target > code, :target > .code-header {
opacity: 1;
}
margin-bottom: 13px;
font-size: 1.1875rem;
font-weight: 600;
+ display: block;
}
.notable-traits .docblock code.content{
left: 0;
cursor: pointer;
font-weight: bold;
- font-size: 1.2rem;
+ font-size: 1.25rem;
border-bottom: 1px solid;
display: flex;
height: 40px;
}
.table-display .out-of-band {
position: relative;
- font-size: 1.1875rem;
+ font-size: 1.125rem;
display: block;
}
-#implementors-list > .impl-items .table-display .out-of-band {
- font-size: 1.0625rem;
-}
.table-display td:hover .anchor {
display: block;
div.name::before {
content: "\25B6";
padding-left: 4px;
- font-size: 0.7rem;
+ font-size: 0.625rem;
position: absolute;
left: -16px;
top: 4px;
details.rustdoc-toggle > summary::before {
content: "";
cursor: pointer;
- width: 17px;
- height: max(17px, 1.1em);
+ width: 16px;
+ height: 16px;
background-repeat: no-repeat;
background-position: top left;
display: inline-block;
details.rustdoc-toggle > summary:not(.hideme)::before {
position: absolute;
left: -24px;
- top: 3px;
+ top: 4px;
}
.impl-items > details.rustdoc-toggle > summary:not(.hideme)::before {
details.rustdoc-toggle[open] > summary::before,
details.rustdoc-toggle[open] > summary.hideme::before {
- width: 17px;
- height: max(17px, 1.1em);
+ width: 16px;
+ height: 16px;
background-repeat: no-repeat;
background-position: top left;
display: inline-block;
width: 0;
}
+ .mobile-topbar .location a {
+ padding: 0;
+ margin: 0;
+ }
+
.mobile-topbar .location {
border: none;
- margin: 0;
- margin-left: auto;
- padding: 0.3em;
- padding-right: 0.6em;
+ padding: 0;
+ margin: auto 0.5em auto auto;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
height is specified in pixels, this also has to be specified in
pixels to avoid overflowing the topbar when the user sets a bigger
font size. */
- font-size: 22.4px;
+ font-size: 24px;
}
.mobile-topbar .logo-container {
}
}
+.method-toggle summary,
+.implementors-toggle summary,
+.impl {
+ margin-bottom: 0.75em;
+}
+
+.method-toggle[open] {
+ margin-bottom: 2em;
+}
+
+.implementors-toggle[open] {
+ margin-bottom: 2em;
+}
+
+#trait-implementations-list .method-toggle,
+#synthetic-implementations-list .method-toggle,
+#blanket-implementations-list .method-toggle {
+ margin-bottom: 1em;
+}
/* Begin: styles for --scrape-examples feature */
background-color: #5c6773;
}
-.sidebar .current {
+.sidebar .current,
+.sidebar a:hover {
background-color: transparent;
color: #ffb44c;
}
color: #ff7733;
}
-.sidebar-elems .location a {
- color: #fff;
-}
-
-.block a:hover {
- background: transparent;
- color: #ffb44c;
-}
-
.line-numbers span { color: #5c6773; }
.line-numbers .line-highlighted {
color: #708090;
.in-band a {
color: #c5c5c5;
}
+.sidebar h2 a,
+.sidebar h3 a {
+ color: white;
+}
.search-results a {
color: #0096cf;
}
background-color: rgba(32, 34, 37, .6);
}
-.sidebar .current {
- background-color: #333;
+.sidebar .current,
+.sidebar a:hover {
+ background: #444;
}
.source .sidebar {
background-color: #565656;
}
-.block a:hover {
- background: #444;
-}
-
.line-numbers span { color: #3B91E2; }
.line-numbers .line-highlighted {
background-color: #0a042f !important;
background-color: rgba(36, 37, 39, 0.6);
}
-.sidebar .current {
+.sidebar .current,
+.sidebar a:hover {
background-color: #fff;
}
background-color: #f1f1f1;
}
-.block a:hover {
- background: #F5F5F5;
-}
-
.line-numbers span { color: #c67e2d; }
.line-numbers .line-highlighted {
background-color: #FDFFD3 !important;
return getVar("root-path") + basename + getVar("resource-suffix") + extension;
}
-
(function () {
window.rootPath = getVar("root-path");
window.currentCrate = getVar("current-crate");
document.title = searchState.titleBeforeSearch;
// We also remove the query parameter from the URL.
if (searchState.browserSupportsHistoryApi()) {
- history.replaceState("", window.currentCrate + " - Rust",
+ history.replaceState(null, window.currentCrate + " - Rust",
getNakedUrl() + window.location.hash);
}
},
});
return params;
},
- putBackSearch: function(search_input) {
- var search = searchState.outputElement();
- if (search_input.value !== "" && hasClass(search, "hidden")) {
- searchState.showResults(search);
- if (searchState.browserSupportsHistoryApi()) {
- var extra = "?search=" + encodeURIComponent(search_input.value);
- history.replaceState(search_input.value, "",
- getNakedUrl() + extra + window.location.hash);
- }
- document.title = searchState.title;
- }
- },
browserSupportsHistoryApi: function() {
return window.history && typeof window.history.pushState === "function";
},
}
search_input.addEventListener("focus", function() {
- searchState.putBackSearch(this);
- search_input.origPlaceholder = searchState.input.placeholder;
+ search_input.origPlaceholder = search_input.placeholder;
search_input.placeholder = "Type your search here.";
loadSearch();
});
- search_input.addEventListener("blur", function() {
- search_input.placeholder = searchState.input.origPlaceholder;
- });
if (search_input.value != '') {
loadSearch();
var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
if (searchState.browserSupportsHistoryApi()) {
// `window.location.search`` contains all the query parameters, not just `search`.
- history.replaceState(hash, "",
+ history.replaceState(null, "",
getNakedUrl() + window.location.search + "#" + hash);
}
elem = document.getElementById(hash);
others.appendChild(div);
}
- function block(shortty, longty) {
+ /**
+ * Append to the sidebar a "block" of links - a heading along with a list (`<ul>`) of items.
+ *
+ * @param {string} shortty - A short type name, like "primitive", "mod", or "macro"
+ * @param {string} id - The HTML id of the corresponding section on the module page.
+ * @param {string} longty - A long, capitalized, plural name, like "Primitive Types",
+ * "Modules", or "Macros".
+ */
+ function block(shortty, id, longty) {
var filtered = items[shortty];
if (!filtered) {
return;
var div = document.createElement("div");
div.className = "block " + shortty;
var h3 = document.createElement("h3");
- h3.textContent = longty;
+ h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
div.appendChild(h3);
var ul = document.createElement("ul");
var isModule = hasClass(document.body, "mod");
if (!isModule) {
- block("primitive", "Primitive Types");
- block("mod", "Modules");
- block("macro", "Macros");
- block("struct", "Structs");
- block("enum", "Enums");
- block("union", "Unions");
- block("constant", "Constants");
- block("static", "Statics");
- block("trait", "Traits");
- block("fn", "Functions");
- block("type", "Type Definitions");
- block("foreigntype", "Foreign Types");
- block("keyword", "Keywords");
- block("traitalias", "Trait Aliases");
+ block("primitive", "primitives", "Primitive Types");
+ block("mod", "modules", "Modules");
+ block("macro", "macros", "Macros");
+ block("struct", "structs", "Structs");
+ block("enum", "enums", "Enums");
+ block("union", "unions", "Unions");
+ block("constant", "constants", "Constants");
+ block("static", "static", "Statics");
+ block("trait", "traits", "Traits");
+ block("fn", "functions", "Functions");
+ block("type", "types", "Type Definitions");
+ block("foreigntype", "foreign-types", "Foreign Types");
+ block("keyword", "keywords", "Keywords");
+ block("traitalias", "trait-aliases", "Trait Aliases");
}
// `crates{version}.js` should always be loaded before this script, so we can use
/* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
-/* global onEachLazy, removeClass, searchState, updateLocalStorage */
+/* global onEachLazy, removeClass, searchState, hasClass */
(function() {
// This mapping table should match the discriminants of
searchState.input.value = params.search || "";
}
+ /**
+ * Build an URL with search parameters.
+ *
+ * @param {string} search - The current search being performed.
+ * @param {string|null} filterCrates - The current filtering crate (if any).
+ * @return {string}
+ */
+ function buildUrl(search, filterCrates) {
+ var extra = "?search=" + encodeURIComponent(search);
+
+ if (filterCrates !== null) {
+ extra += "&filter-crate=" + encodeURIComponent(filterCrates);
+ }
+ return getNakedUrl() + extra + window.location.hash;
+ }
+
+ /**
+ * Return the filtering crate or `null` if there is none.
+ *
+ * @return {string|null}
+ */
+ function getFilterCrates() {
+ var elem = document.getElementById("crate-search");
+
+ if (elem &&
+ elem.value !== "All crates" &&
+ hasOwnPropertyRustdoc(rawSearchIndex, elem.value))
+ {
+ return elem.value;
+ }
+ return null;
+ }
+
/**
* Executes the query and returns a list of results for each results tab.
* @param {Object} query - The user query
}
function typePassesFilter(filter, type) {
- // No filter
- if (filter <= NO_TYPE_FILTER) return true;
-
- // Exact match
- if (filter === type) return true;
+ // No filter or Exact mach
+ if (filter <= NO_TYPE_FILTER || filter === type) return true;
// Match related items
var name = itemTypes[type];
// aliases to be before the others in the displayed results.
var aliases = [];
var crateAliases = [];
- if (filterCrates !== undefined) {
+ if (filterCrates !== null) {
if (ALIASES[filterCrates] && ALIASES[filterCrates][query.search]) {
var query_aliases = ALIASES[filterCrates][query.search];
var len = query_aliases.length;
{
val = extractGenerics(val.substr(1, val.length - 2));
for (i = 0; i < nSearchWords; ++i) {
- if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+ if (filterCrates !== null && searchIndex[i].crate !== filterCrates) {
continue;
}
in_args = findArg(searchIndex[i], val, true, typeFilter);
var output = extractGenerics(parts[1]);
for (i = 0; i < nSearchWords; ++i) {
- if (filterCrates !== undefined && searchIndex[i].crate !== filterCrates) {
+ if (filterCrates !== null && searchIndex[i].crate !== filterCrates) {
continue;
}
var type = searchIndex[i].type;
var lev, j;
for (j = 0; j < nSearchWords; ++j) {
ty = searchIndex[j];
- if (!ty || (filterCrates !== undefined && ty.crate !== filterCrates)) {
+ if (!ty || (filterCrates !== null && ty.crate !== filterCrates)) {
continue;
}
var lev_add = 0;
}
function execSearch(query, searchWords, filterCrates) {
- function getSmallest(arrays, positions, notDuplicates) {
- var start = null;
-
- for (var it = 0, len = positions.length; it < len; ++it) {
- if (arrays[it].length > positions[it] &&
- (start === null || start > arrays[it][positions[it]].lev) &&
- !notDuplicates[arrays[it][positions[it]].fullPath]) {
- start = arrays[it][positions[it]].lev;
- }
- }
- return start;
- }
-
- function mergeArrays(arrays) {
- var ret = [];
- var positions = [];
- var notDuplicates = {};
-
- for (var x = 0, arrays_len = arrays.length; x < arrays_len; ++x) {
- positions.push(0);
- }
- while (ret.length < MAX_RESULTS) {
- var smallest = getSmallest(arrays, positions, notDuplicates);
-
- if (smallest === null) {
- break;
- }
- for (x = 0; x < arrays_len && ret.length < MAX_RESULTS; ++x) {
- if (arrays[x].length > positions[x] &&
- arrays[x][positions[x]].lev === smallest &&
- !notDuplicates[arrays[x][positions[x]].fullPath]) {
- ret.push(arrays[x][positions[x]]);
- notDuplicates[arrays[x][positions[x]].fullPath] = true;
- positions[x] += 1;
- }
- }
- }
- return ret;
- }
-
- // Split search query by ",", while respecting angle bracket nesting.
- // Since "<" is an alias for the Ord family of traits, it also uses
- // lookahead to distinguish "<"-as-less-than from "<"-as-angle-bracket.
- //
- // tokenizeQuery("A<B, C>, D") == ["A<B, C>", "D"]
- // tokenizeQuery("A<B, C, D") == ["A<B", "C", "D"]
- function tokenizeQuery(raw) {
- var i, matched;
- var l = raw.length;
- var depth = 0;
- var nextAngle = /(<|>)/g;
- var ret = [];
- var start = 0;
- for (i = 0; i < l; ++i) {
- switch (raw[i]) {
- case "<":
- nextAngle.lastIndex = i + 1;
- matched = nextAngle.exec(raw);
- if (matched && matched[1] === '>') {
- depth += 1;
- }
- break;
- case ">":
- if (depth > 0) {
- depth -= 1;
- }
- break;
- case ",":
- if (depth === 0) {
- ret.push(raw.substring(start, i));
- start = i + 1;
- }
- break;
- }
- }
- if (start !== i) {
- ret.push(raw.substring(start, i));
- }
- return ret;
- }
-
- var queries = tokenizeQuery(query.raw);
+ query = query.raw.trim();
var results = {
"in_args": [],
"returned": [],
"others": [],
};
- for (var i = 0, len = queries.length; i < len; ++i) {
- query = queries[i].trim();
- if (query.length !== 0) {
- var tmp = execQuery(getQuery(query), searchWords, filterCrates);
+ if (query.length !== 0) {
+ var tmp = execQuery(getQuery(query), searchWords, filterCrates);
- results.in_args.push(tmp.in_args);
- results.returned.push(tmp.returned);
- results.others.push(tmp.others);
- }
- }
- if (queries.length > 1) {
- return {
- "in_args": mergeArrays(results.in_args),
- "returned": mergeArrays(results.returned),
- "others": mergeArrays(results.others),
- };
+ results.in_args.push(tmp.in_args);
+ results.returned.push(tmp.returned);
+ results.others.push(tmp.others);
}
return {
"in_args": results.in_args[0],
};
}
- function getFilterCrates() {
- var elem = document.getElementById("crate-search");
-
- if (elem && elem.value !== "All crates" &&
- hasOwnPropertyRustdoc(rawSearchIndex, elem.value))
- {
- return elem.value;
- }
- return undefined;
- }
-
/**
* Perform a search based on the current state of the search input element
* and display the results.
}
if (!forced && query.id === currentResults) {
if (query.query.length > 0) {
- searchState.putBackSearch(searchState.input);
+ putBackSearch();
}
return;
}
+ var filterCrates = getFilterCrates();
+
+ // In case we have no information about the saved crate and there is a URL query parameter,
+ // we override it with the URL query parameter.
+ if (filterCrates === null && params["filter-crate"] !== undefined) {
+ filterCrates = params["filter-crate"];
+ }
+
// Update document title to maintain a meaningful browser history
searchState.title = "Results for " + query.query + " - Rust";
// Because searching is incremental by character, only the most
// recent search query is added to the browser history.
if (searchState.browserSupportsHistoryApi()) {
- var newURL = getNakedUrl() + "?search=" + encodeURIComponent(query.raw) +
- window.location.hash;
+ var newURL = buildUrl(query.raw, filterCrates);
+
if (!history.state && !params.search) {
- history.pushState(query, "", newURL);
+ history.pushState(null, "", newURL);
} else {
- history.replaceState(query, "", newURL);
+ history.replaceState(null, "", newURL);
}
}
- var filterCrates = getFilterCrates();
showResults(execSearch(query, searchWords, filterCrates),
params["go_to_first"], filterCrates);
}
search();
}
+ function putBackSearch() {
+ var search_input = searchState.input;
+ if (!searchState.input) {
+ return;
+ }
+ var search = searchState.outputElement();
+ if (search_input.value !== "" && hasClass(search, "hidden")) {
+ searchState.showResults(search);
+ if (searchState.browserSupportsHistoryApi()) {
+ history.replaceState(null, "",
+ buildUrl(search_input.value, getFilterCrates()));
+ }
+ document.title = searchState.title;
+ }
+ }
+
function registerSearchEvents() {
var searchAfter500ms = function() {
searchState.clearInputTimeout();
if (searchState.input.value.length === 0) {
if (searchState.browserSupportsHistoryApi()) {
- history.replaceState("", window.currentCrate + " - Rust",
+ history.replaceState(null, window.currentCrate + " - Rust",
getNakedUrl() + window.location.hash);
}
searchState.hideResults();
}
});
+ searchState.input.addEventListener("focus", function() {
+ putBackSearch();
+ });
+
+ searchState.input.addEventListener("blur", function() {
+ searchState.input.placeholder = searchState.input.origPlaceholder;
+ });
+
// Push and pop states are used to add search results to the browser
// history.
if (searchState.browserSupportsHistoryApi()) {
}
function updateCrate(ev) {
- updateLocalStorage("rustdoc-saved-filter-crate", ev.target.value);
+ if (ev.target.value === "All crates") {
+ // If we don't remove it from the URL, it'll be picked up again by the search.
+ var params = searchState.getQueryStringParams();
+ var query = searchState.input.value.trim();
+ if (!history.state && !params.search) {
+ history.pushState(null, "", buildUrl(query, null));
+ } else {
+ history.replaceState(null, "", buildUrl(query, null));
+ }
+ }
// In case you "cut" the entry from the search input, then change the crate filter
// before paste back the previous search, you get the old search results without
// the filter. To prevent this, we need to remove the previous results.
searchWords = buildIndex(rawSearchIndex);
registerSearchEvents();
- // If there's a search term in the URL, execute the search now.
- if (searchState.getQueryStringParams().search) {
- search();
+
+ function runSearchIfNeeded() {
+ // If there's a search term in the URL, execute the search now.
+ if (searchState.getQueryStringParams().search) {
+ search();
+ }
}
+
+ runSearchIfNeeded();
};
if (window.searchIndex !== undefined) {
(function () {
function changeSetting(settingName, value) {
- updateLocalStorage("rustdoc-" + settingName, value);
+ updateLocalStorage(settingName, value);
switch (settingName) {
case "theme":
if (child.innerText === ">") {
sidebar.classList.add("expanded");
child.innerText = "<";
- updateLocalStorage("rustdoc-source-sidebar-show", "true");
+ updateLocalStorage("source-sidebar-show", "true");
} else {
sidebar.classList.remove("expanded");
child.innerText = ">";
- updateLocalStorage("rustdoc-source-sidebar-show", "false");
+ updateLocalStorage("source-sidebar-show", "false");
}
}
var inner = document.createElement("div");
- if (getCurrentValue("rustdoc-source-sidebar-show") === "true") {
+ if (getCurrentValue("source-sidebar-show") === "true") {
inner.innerText = "<";
} else {
inner.innerText = ">";
var sidebar = document.createElement("div");
sidebar.id = "source-sidebar";
- if (getCurrentValue("rustdoc-source-sidebar-show") !== "true") {
+ if (getCurrentValue("source-sidebar-show") !== "true") {
container.classList.remove("expanded");
} else {
container.classList.add("expanded");
})();
function getSettingValue(settingName) {
- var current = getCurrentValue('rustdoc-' + settingName);
+ var current = getCurrentValue(settingName);
if (current !== null) {
return current;
}
function updateLocalStorage(name, value) {
try {
- window.localStorage.setItem(name, value);
+ window.localStorage.setItem("rustdoc-" + name, value);
} catch(e) {
// localStorage is not accessible, do nothing
}
function getCurrentValue(name) {
try {
- return window.localStorage.getItem(name);
+ return window.localStorage.getItem("rustdoc-" + name);
} catch(e) {
return null;
}
// If this new value comes from a system setting or from the previously
// saved theme, no need to save it.
if (saveTheme) {
- updateLocalStorage("rustdoc-theme", newTheme);
+ updateLocalStorage("theme", newTheme);
}
if (styleElem.href === newHref) {
value = true;
}
- updateLocalStorage("rustdoc-use-system-theme", value);
+ updateLocalStorage("use-system-theme", value);
// update the toggle if we're on the settings page
var toggle = document.getElementById("use-system-theme");
if (getSettingValue("use-system-theme") === null
&& getSettingValue("preferred-dark-theme") === null
&& darkThemes.indexOf(localStoredTheme) >= 0) {
- updateLocalStorage("rustdoc-preferred-dark-theme", localStoredTheme);
+ updateLocalStorage("preferred-dark-theme", localStoredTheme);
}
// call the function to initialize the theme at least once!
#![feature(rustc_private)]
#![feature(array_methods)]
#![feature(assert_matches)]
+#![feature(bool_to_option)]
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(box_syntax)]
Ok(opts) => opts,
Err(code) => return if code == 0 { Ok(()) } else { Err(ErrorReported) },
};
- rustc_interface::util::setup_callbacks_and_run_in_thread_pool_with_globals(
+ rustc_interface::util::run_in_thread_pool_with_globals(
options.edition,
1, // this runs single-threaded, even in a parallel compiler
- &None,
move || main_options(options),
)
}
//!
//! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
-use rustc_ast as ast;
use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
-use rustc_expand::base::SyntaxExtensionKind;
use rustc_hir::def::{
DefKind,
Namespace::{self, *},
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_ID};
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
use rustc_middle::{bug, span_bug, ty};
-use rustc_resolve::ParentScope;
use rustc_session::lint::Lint;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{sym, Ident, Symbol};
path_str: &'a str,
module_id: DefId,
) -> Result<Res, ResolutionFailure<'a>> {
- let path = ast::Path::from_ident(Ident::from_str(path_str));
self.cx.enter_resolver(|resolver| {
- // FIXME(jynelson): does this really need 3 separate lookups?
- if let Ok((Some(ext), res)) = resolver.resolve_macro_path(
- &path,
- None,
- &ParentScope::module(resolver.graph_root(), resolver),
- false,
- false,
- ) {
- if let SyntaxExtensionKind::LegacyBang { .. } = ext.kind {
- return Ok(res.try_into().unwrap());
- }
- }
- if let Some(&res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
- return Ok(res.try_into().unwrap());
- }
+ // NOTE: this needs 2 separate lookups because `resolve_str_path_error` doesn't take
+ // lexical scope into account (it ignores all macros not defined at the mod-level)
debug!("resolving {} as a macro in the module {:?}", path_str, module_id);
if let Ok((_, res)) =
resolver.resolve_str_path_error(DUMMY_SP, path_str, MacroNS, module_id)
return Ok(res);
}
}
+ if let Some(&res) = resolver.all_macros().get(&Symbol::intern(path_str)) {
+ return Ok(res.try_into().unwrap());
+ }
Err(ResolutionFailure::NotResolved {
module_id,
partial_res: None,
let base_node =
if item.is_mod() && inner_docs { self.mod_ids.last().copied() } else { parent_node };
- let mut module_id = if let Some(id) = base_node {
- id
- } else {
+ let Some(mut module_id) = base_node else {
// This is a bug.
debug!("attempting to resolve item without parent module: {}", path_str);
resolution_failure(
// If so, report it and say the first which failed; if not, say the first path segment didn't resolve.
let mut name = path_str;
'outer: loop {
- let (start, end) = if let Some(x) = split(name) {
- x
- } else {
+ let Some((start, end)) = split(name) else {
// avoid bug that marked [Quux::Z] as missing Z, not Quux
if partial_res.is_none() {
*unresolved = name.into();
let all_traits = Vec::from_iter(self.resolver.cstore().traits_in_crate_untracked(cnum));
let all_trait_impls =
Vec::from_iter(self.resolver.cstore().trait_impls_in_crate_untracked(cnum));
+ let all_inherent_impls =
+ Vec::from_iter(self.resolver.cstore().inherent_impls_in_crate_untracked(cnum));
+ let all_lang_items = Vec::from_iter(self.resolver.cstore().lang_items_untracked(cnum));
// Querying traits in scope is expensive so we try to prune the impl and traits lists
// using privacy, private traits and impls from other crates are never documented in
self.add_traits_in_parent_scope(impl_def_id);
}
}
+ for (ty_def_id, impl_def_id) in all_inherent_impls {
+ if self.resolver.cstore().visibility_untracked(ty_def_id) == Visibility::Public {
+ self.add_traits_in_parent_scope(impl_def_id);
+ }
+ }
+ for def_id in all_lang_items {
+ self.add_traits_in_parent_scope(def_id);
+ }
self.all_traits.extend(all_traits);
self.all_trait_impls.extend(all_trait_impls.into_iter().map(|(_, def_id, _)| def_id));
tags: &mut Vec<(String, Range<usize>)>,
tag_name: String,
range: Range<usize>,
- f: &impl Fn(&str, &Range<usize>),
+ f: &impl Fn(&str, &Range<usize>, bool),
) {
let tag_name_low = tag_name.to_lowercase();
if let Some(pos) = tags.iter().rposition(|(t, _)| t.to_lowercase() == tag_name_low) {
// `tags` is used as a queue, meaning that everything after `pos` is included inside it.
// So `<h2><h3></h2>` will look like `["h2", "h3"]`. So when closing `h2`, we will still
// have `h3`, meaning the tag wasn't closed as it should have.
- f(&format!("unclosed HTML tag `{}`", last_tag_name), &last_tag_span);
+ f(&format!("unclosed HTML tag `{}`", last_tag_name), &last_tag_span, true);
}
// Remove the `tag_name` that was originally closed
tags.pop();
} else {
// It can happen for example in this case: `<h2></script></h2>` (the `h2` tag isn't required
// but it helps for the visualization).
- f(&format!("unopened HTML tag `{}`", tag_name), &range);
+ f(&format!("unopened HTML tag `{}`", tag_name), &range, false);
+ }
+}
+
+fn extract_path_backwards(text: &str, end_pos: usize) -> Option<usize> {
+ use rustc_lexer::{is_id_continue, is_id_start};
+ let mut current_pos = end_pos;
+ loop {
+ if current_pos >= 2 && text[..current_pos].ends_with("::") {
+ current_pos -= 2;
+ }
+ let new_pos = text[..current_pos]
+ .char_indices()
+ .rev()
+ .take_while(|(_, c)| is_id_start(*c) || is_id_continue(*c))
+ .reduce(|_accum, item| item)
+ .and_then(|(new_pos, c)| is_id_start(c).then_some(new_pos));
+ if let Some(new_pos) = new_pos {
+ if current_pos != new_pos {
+ current_pos = new_pos;
+ continue;
+ }
+ }
+ break;
+ }
+ if current_pos == end_pos {
+ return None;
+ } else {
+ return Some(current_pos);
}
}
range: &Range<usize>,
start_pos: usize,
iter: &mut Peekable<CharIndices<'_>>,
- f: &impl Fn(&str, &Range<usize>),
+ f: &impl Fn(&str, &Range<usize>, bool),
) {
let mut tag_name = String::new();
let mut is_closing = false;
text: &str,
range: Range<usize>,
is_in_comment: &mut Option<Range<usize>>,
- f: &impl Fn(&str, &Range<usize>),
+ f: &impl Fn(&str, &Range<usize>, bool),
) {
let mut iter = text.char_indices().peekable();
};
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
if !dox.is_empty() {
- let report_diag = |msg: &str, range: &Range<usize>| {
+ let report_diag = |msg: &str, range: &Range<usize>, is_open_tag: bool| {
let sp = match super::source_span_for_markdown_range(tcx, &dox, range, &item.attrs)
{
Some(sp) => sp,
None => item.attr_span(tcx),
};
tcx.struct_span_lint_hir(crate::lint::INVALID_HTML_TAGS, hir_id, sp, |lint| {
- lint.build(msg).emit()
+ use rustc_lint_defs::Applicability;
+ let mut diag = lint.build(msg);
+ // If a tag looks like `<this>`, it might actually be a generic.
+ // We don't try to detect stuff `<like, this>` because that's not valid HTML,
+ // and we don't try to detect stuff `<like this>` because that's not valid Rust.
+ if let Some(Some(generics_start)) = (is_open_tag
+ && dox[..range.end].ends_with(">"))
+ .then(|| extract_path_backwards(&dox, range.start))
+ {
+ let generics_sp = match super::source_span_for_markdown_range(
+ tcx,
+ &dox,
+ &(generics_start..range.end),
+ &item.attrs,
+ ) {
+ Some(sp) => sp,
+ None => item.attr_span(tcx),
+ };
+ // multipart form is chosen here because ``Vec<i32>`` would be confusing.
+ diag.multipart_suggestion(
+ "try marking as source code",
+ vec![
+ (generics_sp.shrink_to_lo(), String::from("`")),
+ (generics_sp.shrink_to_hi(), String::from("`")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ diag.emit()
});
};
let t = t.to_lowercase();
!ALLOWED_UNCLOSED.contains(&t.as_str())
}) {
- report_diag(&format!("unclosed HTML tag `{}`", tag), range);
+ report_diag(&format!("unclosed HTML tag `{}`", tag), range, true);
}
if let Some(range) = is_in_comment {
- report_diag("Unclosed HTML comment", &range);
+ report_diag("Unclosed HTML comment", &range, false);
}
}
}
hir::ExprKind::MethodCall(_, _, span) => {
let types = tcx.typeck(ex.hir_id.owner);
- let def_id = if let Some(def_id) = types.type_dependent_def_id(ex.hir_id) {
- def_id
- } else {
+ let Some(def_id) = types.type_dependent_def_id(ex.hir_id) else {
trace!("type_dependent_def_id({}) = None", ex.hir_id);
return;
};
return;
}
- let file = tcx.sess.source_map().lookup_char_pos(span.lo()).file;
+ let source_map = tcx.sess.source_map();
+ let file = source_map.lookup_char_pos(span.lo()).file;
let file_path = match file.name.clone() {
FileName::Real(real_filename) => real_filename.into_local_path(),
_ => None,
let fn_entries = self.calls.entry(fn_key).or_default();
trace!("Including expr: {:?}", span);
+ let enclosing_item_span =
+ source_map.span_extend_to_prev_char(enclosing_item_span, '\n', false);
let location = CallLocation::new(span, enclosing_item_span, &file);
fn_entries.entry(abs_path).or_insert_with(mk_call_data).locations.push(location);
}
let target_crates = options
.target_crates
.into_iter()
- .map(|target| all_crates.iter().filter(move |(_, name)| name.as_str() == target))
- .flatten()
+ .flat_map(|target| all_crates.iter().filter(move |(_, name)| name.as_str() == target))
.map(|(crate_num, _)| **crate_num)
.collect::<Vec<_>>();
debug!("maybe_inline_local res: {:?}", res);
let tcx = self.cx.tcx;
- let res_did = if let Some(did) = res.opt_def_id() {
- did
- } else {
+ let Some(res_did) = res.opt_def_id() else {
return false;
};
-Subproject commit b6b46f596a7d2523ee1acd1c00e699615849da60
+Subproject commit e29ac13bc97e26f886c3bfe72f9135e994c3cd0a
--- /dev/null
+// Test that the correct module flags are emitted with different control-flow protection flags.
+
+// revisions: undefined none branch return full
+// needs-llvm-components: x86
+// [undefined] compile-flags:
+// [none] compile-flags: -Z cf-protection=none
+// [branch] compile-flags: -Z cf-protection=branch
+// [return] compile-flags: -Z cf-protection=return
+// [full] compile-flags: -Z cf-protection=full
+// compile-flags: --target x86_64-unknown-linux-gnu
+
+#![crate_type = "lib"]
+#![feature(no_core, lang_items)]
+#![no_core]
+
+#[lang="sized"]
+trait Sized { }
+
+// A basic test function.
+pub fn test() {
+}
+
+// undefined-NOT: !"cf-protection-branch"
+// undefined-NOT: !"cf-protection-return"
+
+// none-NOT: !"cf-protection-branch"
+// none-NOT: !"cf-protection-return"
+
+// branch-NOT: !"cf-protection-return"
+// branch: !"cf-protection-branch", i32 1
+// branch-NOT: !"cf-protection-return"
+
+// return-NOT: !"cf-protection-branch"
+// return: !"cf-protection-return", i32 1
+// return-NOT: !"cf-protection-branch"
+
+// full: !"cf-protection-branch", i32 1
+// full: !"cf-protection-return", i32 1
// compile-flags: -Cdebuginfo=2 -Copt-level=0 -Csymbol-mangling-version=v0
// ignore-tidy-linelength
+// NONMSVC: ![[USIZE:[0-9]+]] = !DIBasicType(name: "usize"
+// MSVC: ![[USIZE:[0-9]+]] = !DIDerivedType(tag: DW_TAG_typedef, name: "usize"
+// NONMSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()"
+// MSVC: ![[PTR:[0-9]+]] = !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >"
+
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable$"
-// NONMSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "*const ()",
-// MSVC: !DIDerivedType(tag: DW_TAG_pointer_type, name: "ptr_const$<tuple$<> >",
-// CHECK: !DISubrange(count: 5
+
+// NONMSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as debug_vtable::SomeTrait>::{vtable_type}", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]],
+// MSVC: ![[VTABLE_TY0:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, debug_vtable::SomeTrait>::vtable_type$", {{.*}} size: {{320|160}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}} vtableHolder: ![[FOO_TYPE:[0-9]+]],
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method4", scope: ![[VTABLE_TY0]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{256|128}})
+// CHECK: ![[FOO_TYPE]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo",
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable$"
-// CHECK: !DISubrange(count: 4
+
+// NONMSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as debug_vtable::SomeTraitWithGenerics<u64, i8>>::{vtable_type}", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
+// MSVC: ![[VTABLE_TY1:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, debug_vtable::SomeTraitWithGenerics<u64,i8> >::vtable_type$", {{.*}}, size: {{256|128}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "__method3", scope: ![[VTABLE_TY1]], {{.*}} baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}}, offset: {{192|96}})
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::Foo as _>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::Foo, _>::vtable$"
-// CHECK: !DISubrange(count: 3
+
+// NONMSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "<debug_vtable::Foo as _>::{vtable_type}", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
+// MSVC: ![[VTABLE_TY2:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "impl$<debug_vtable::Foo, _>::vtable_type$", {{.*}}, size: {{192|96}}, align: {{64|32}}, flags: DIFlagArtificial, {{.*}}, vtableHolder: ![[FOO_TYPE]],
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "drop_in_place", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[PTR]], size: {{64|32}}, align: {{64|32}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "size", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{64|32}})
+// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "align", scope: ![[VTABLE_TY2]], {{.*}}, baseType: ![[USIZE]], size: {{64|32}}, align: {{64|32}}, offset: {{128|64}})
// NONMSVC: !DIGlobalVariable(name: "<debug_vtable::bar::{closure_env#0} as core::ops::function::FnOnce<(core::option::Option<&dyn core::ops::function::Fn<(), Output=()>>)>>::{vtable}"
// MSVC: !DIGlobalVariable(name: "impl$<debug_vtable::bar::closure_env$0, core::ops::function::FnOnce<tuple$<enum$<core::option::Option<ref$<dyn$<core::ops::function::Fn<tuple$<>,assoc$<Output,tuple$<> > > > > >, {{.*}}, {{.*}}, Some> > > >::vtable$"
#![crate_type = "lib"]
+// Force emission for debuginfo for usize and *const() early..
+pub static mut XYZ: Option<(usize, *const ())> = None;
+
pub struct Foo;
pub trait SomeTrait {
// compile-flags: -Cdebuginfo=2 --edition 2021 -Copt-level=0 -Csymbol-mangling-version=v0
-
-// CHECK: [[non_generic_closure_NAMESPACE:!.*]] = !DINamespace(name: "non_generic_closure"
-// CHECK: [[function_containing_closure_NAMESPACE:!.*]] = !DINamespace(name: "function_containing_closure"
-// CHECK: [[generic_async_function_NAMESPACE:!.*]] = !DINamespace(name: "generic_async_function"
-// CHECK: [[generic_async_block_NAMESPACE:!.*]] = !DINamespace(name: "generic_async_block"
-
// non_generic_closure()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: [[non_generic_closure_NAMESPACE]]
-// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: [[non_generic_closure_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]],
+// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0", scope: ![[non_generic_closure_NAMESPACE:[0-9]+]],
+// CHECK: ![[non_generic_closure_NAMESPACE]] = !DINamespace(name: "non_generic_closure"
+
+// CHECK: ![[function_containing_closure_NAMESPACE:[0-9]+]] = !DINamespace(name: "function_containing_closure"
+// CHECK: ![[generic_async_function_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_function"
+// CHECK: ![[generic_async_block_NAMESPACE:[0-9]+]] = !DINamespace(name: "generic_async_block"
// function_containing_closure<u32>()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<u32>", scope: [[function_containing_closure_NAMESPACE]]
-// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<u32>", scope: [[function_containing_closure_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<u32>", scope: ![[function_containing_closure_NAMESPACE]]
+// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<u32>", scope: ![[function_containing_closure_NAMESPACE]]
// generic_async_function<Foo>()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: [[generic_async_function_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: ![[generic_async_function_NAMESPACE]]
// generic_async_function<u32>()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<u32>", scope: [[generic_async_function_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_fn_env#0}<u32>", scope: ![[generic_async_function_NAMESPACE]]
// generic_async_block<Foo>()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: [[generic_async_block_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: ![[generic_async_block_NAMESPACE]]
// generic_async_block<u32>()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<u32>", scope: [[generic_async_block_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{async_block_env#0}<u32>", scope: ![[generic_async_block_NAMESPACE]]
// function_containing_closure<Foo>()
-// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: [[function_containing_closure_NAMESPACE]]
-// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<debuginfo_generic_closure_env_names::Foo>", scope: [[function_containing_closure_NAMESPACE]]
+// NONMSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}<debuginfo_generic_closure_env_names::Foo>", scope: ![[function_containing_closure_NAMESPACE]]
+// MSVC: !DICompositeType(tag: DW_TAG_structure_type, name: "closure_env$0<debuginfo_generic_closure_env_names::Foo>", scope: ![[function_containing_closure_NAMESPACE]]
#![crate_type = "lib"]
pub struct Foo;
pub fn non_generic_closure(x: Foo) -> Box<dyn FnOnce() -> Foo> {
- // This static only exists to trigger generating the namespace debuginfo for
- // `function_containing_closure` at a predictable, early point, which makes
- // writing the FileCheck tests above simpler.
- static _X: u8 = 0;
return Box::new(move || x);
}
fn function_containing_closure<T: 'static>(x: T) -> impl FnOnce() -> T {
- static _X: u8 = 0; // Same as above
+ // This static only exists to trigger generating the namespace debuginfo for
+ // `function_containing_closure` at a predictable, early point, which makes
+ // writing the FileCheck tests above simpler.
+ static _X: u8 = 0;
return move || x;
}
// as "inreg" like the C/C++ compilers for the platforms.
// x86 only.
-// ignore-aarch64
-// ignore-aarch64_be
-// ignore-arm
-// ignore-armeb
-// ignore-avr
-// ignore-bpfel
-// ignore-bpfeb
-// ignore-hexagon
-// ignore-mips
-// ignore-mips64
-// ignore-msp430
-// ignore-powerpc64
-// ignore-powerpc64le
-// ignore-powerpc
-// ignore-r600
-// ignore-riscv64
-// ignore-amdgcn
-// ignore-sparc
-// ignore-sparc64
-// ignore-sparcv9
-// ignore-sparcel
-// ignore-s390x
-// ignore-tce
-// ignore-thumb
-// ignore-thumbeb
-// ignore-x86_64
-// ignore-xcore
-// ignore-nvptx
-// ignore-nvptx64
-// ignore-le32
-// ignore-le64
-// ignore-amdil
-// ignore-amdil64
-// ignore-hsail
-// ignore-hsail64
-// ignore-spir
-// ignore-spir64
-// ignore-kalimba
-// ignore-shave
-// ignore-wasm32
-// ignore-wasm64
-// ignore-emscripten
-
-// compile-flags: -C no-prepopulate-passes
+// compile-flags: --target i686-unknown-linux-gnu -C no-prepopulate-passes
+// needs-llvm-components: x86
#![crate_type = "lib"]
+#![no_core]
+#![feature(no_core, lang_items)]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
pub mod tests {
// CHECK: @f1(i32 inreg %_1, i32 inreg %_2, i32 %_3)
#[no_mangle]
pub extern "fastcall" fn f5(_: i64, _: i32) {}
- // CHECK: @f6(i1 inreg zeroext %_1, i32 inreg %_2, i32 %_3)
+ // CHECK: @f6(i1 inreg noundef zeroext %_1, i32 inreg %_2, i32 %_3)
#[no_mangle]
pub extern "fastcall" fn f6(_: bool, _: i32, _: i32) {}
}
#![crate_type = "lib"]
#![feature(rustc_attrs)]
+use std::mem::MaybeUninit;
+
pub struct S {
_field: [i32; 8],
}
_field: std::cell::UnsafeCell<i16>,
}
-// CHECK: zeroext i1 @boolean(i1 zeroext %x)
+// CHECK: noundef zeroext i1 @boolean(i1 noundef zeroext %x)
#[no_mangle]
pub fn boolean(x: bool) -> bool {
x
}
-// CHECK: @readonly_borrow(i32* noalias readonly align 4 dereferenceable(4) %_1)
+// CHECK: i8 @maybeuninit_boolean(i8 %x)
+#[no_mangle]
+pub fn maybeuninit_boolean(x: MaybeUninit<bool>) -> MaybeUninit<bool> {
+ x
+}
+
+// CHECK: @readonly_borrow(i32* noalias noundef readonly align 4 dereferenceable(4) %_1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn readonly_borrow(_: &i32) {
}
-// CHECK: @static_borrow(i32* noalias readonly align 4 dereferenceable(4) %_1)
+// CHECK: @static_borrow(i32* noalias noundef readonly align 4 dereferenceable(4) %_1)
// static borrow may be captured
#[no_mangle]
pub fn static_borrow(_: &'static i32) {
}
-// CHECK: @named_borrow(i32* noalias readonly align 4 dereferenceable(4) %_1)
+// CHECK: @named_borrow(i32* noalias noundef readonly align 4 dereferenceable(4) %_1)
// borrow with named lifetime may be captured
#[no_mangle]
pub fn named_borrow<'r>(_: &'r i32) {
}
-// CHECK: @unsafe_borrow(i16* align 2 dereferenceable(2) %_1)
+// CHECK: @unsafe_borrow(i16* noundef align 2 dereferenceable(2) %_1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_borrow(_: &UnsafeInner) {
}
-// CHECK: @mutable_unsafe_borrow(i16* noalias align 2 dereferenceable(2) %_1)
+// CHECK: @mutable_unsafe_borrow(i16* noalias noundef align 2 dereferenceable(2) %_1)
// ... unless this is a mutable borrow, those never alias
#[no_mangle]
pub fn mutable_unsafe_borrow(_: &mut UnsafeInner) {
}
-// CHECK: @mutable_borrow(i32* noalias align 4 dereferenceable(4) %_1)
+// CHECK: @mutable_borrow(i32* noalias noundef align 4 dereferenceable(4) %_1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_borrow(_: &mut i32) {
}
-// CHECK: @indirect_struct(%S* noalias nocapture dereferenceable(32) %_1)
+// CHECK: @indirect_struct(%S* noalias nocapture noundef dereferenceable(32) %_1)
#[no_mangle]
pub fn indirect_struct(_: S) {
}
-// CHECK: @borrowed_struct(%S* noalias readonly align 4 dereferenceable(32) %_1)
+// CHECK: @borrowed_struct(%S* noalias noundef readonly align 4 dereferenceable(32) %_1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn borrowed_struct(_: &S) {
}
+// CHECK: @raw_struct(%S* %_1)
+#[no_mangle]
+pub fn raw_struct(_: *const S) {
+}
+
// `Box` can get deallocated during execution of the function, so it should
// not get `dereferenceable`.
-// CHECK: noalias nonnull align 4 i32* @_box(i32* noalias nonnull align 4 %x)
+// CHECK: noalias noundef nonnull align 4 i32* @_box(i32* noalias noundef nonnull align 4 %x)
#[no_mangle]
pub fn _box(x: Box<i32>) -> Box<i32> {
x
}
-// CHECK: @struct_return(%S* noalias nocapture sret(%S) dereferenceable(32){{( %0)?}})
+// CHECK: @struct_return(%S* noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}})
#[no_mangle]
pub fn struct_return() -> S {
S {
pub fn helper(_: usize) {
}
-// CHECK: @slice([0 x i8]* noalias nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @slice([0 x i8]* noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn slice(_: &[u8]) {
}
-// CHECK: @mutable_slice([0 x i8]* noalias nonnull align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @mutable_slice([0 x i8]* noalias noundef nonnull align 1 %_1.0, [[USIZE]] %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn mutable_slice(_: &mut [u8]) {
}
-// CHECK: @unsafe_slice([0 x i16]* nonnull align 2 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @unsafe_slice([0 x i16]* noundef nonnull align 2 %_1.0, [[USIZE]] %_1.1)
// unsafe interior means this isn't actually readonly and there may be aliases ...
#[no_mangle]
pub fn unsafe_slice(_: &[UnsafeInner]) {
}
-// CHECK: @str([0 x i8]* noalias nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
+// CHECK: @raw_slice([0 x i8]* %_1.0, [[USIZE]] %_1.1)
+#[no_mangle]
+pub fn raw_slice(_: *const [u8]) {
+}
+
+// CHECK: @str([0 x i8]* noalias noundef nonnull readonly align 1 %_1.0, [[USIZE]] %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn str(_: &[u8]) {
}
-// CHECK: @trait_borrow({}* nonnull align 1 %_1.0, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
+// CHECK: @trait_borrow({}* noundef nonnull align 1 %_1.0, [3 x [[USIZE]]]* noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
// FIXME #25759 This should also have `nocapture`
#[no_mangle]
pub fn trait_borrow(_: &Drop) {
}
-// CHECK: @trait_box({}* noalias nonnull align 1{{( %0)?}}, [3 x [[USIZE]]]* noalias readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
+// CHECK: @trait_raw({}* %_1.0, [3 x [[USIZE]]]* noalias noundef readonly align {{.*}} dereferenceable({{.*}}) %_1.1)
+#[no_mangle]
+pub fn trait_raw(_: *const Drop) {
+}
+
+// CHECK: @trait_box({}* noalias noundef nonnull align 1{{( %0)?}}, [3 x [[USIZE]]]* noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
pub fn trait_box(_: Box<Drop>) {
}
-// CHECK: { i8*, i8* } @trait_option(i8* noalias align 1 %x.0, i8* %x.1)
+// CHECK: { i8*, i8* } @trait_option(i8* noalias noundef align 1 %x.0, i8* %x.1)
#[no_mangle]
pub fn trait_option(x: Option<Box<Drop>>) -> Option<Box<Drop>> {
x
}
-// CHECK: { [0 x i16]*, [[USIZE]] } @return_slice([0 x i16]* noalias nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
+// CHECK: { [0 x i16]*, [[USIZE]] } @return_slice([0 x i16]* noalias noundef nonnull readonly align 2 %x.0, [[USIZE]] %x.1)
#[no_mangle]
pub fn return_slice(x: &[u16]) -> &[u16] {
x
x
}
-// CHECK: { i8, i8 } @enum_id_2(i1 zeroext %x.0, i8 %x.1)
+// CHECK: { i8, i8 } @enum_id_2(i1 noundef zeroext %x.0, i8 %x.1)
#[no_mangle]
pub fn enum_id_2(x: Option<u8>) -> Option<u8> {
x
#[no_mangle]
pub fn call_pkd1(f: fn() -> Array) -> BigPacked1 {
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
-// CHECK: call void %{{.*}}(%Array* noalias nocapture sret{{.*}} dereferenceable(32) [[ALLOCA]])
+// CHECK: call void %{{.*}}(%Array* noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]])
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 1 %{{.*}}, i8* align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false)
// check that calls whose destination is a field of a packed struct
// go through an alloca rather than calling the function with an
#[no_mangle]
pub fn call_pkd2(f: fn() -> Array) -> BigPacked2 {
// CHECK: [[ALLOCA:%[_a-z0-9]+]] = alloca %Array
-// CHECK: call void %{{.*}}(%Array* noalias nocapture sret{{.*}} dereferenceable(32) [[ALLOCA]])
+// CHECK: call void %{{.*}}(%Array* noalias nocapture noundef sret{{.*}} dereferenceable(32) [[ALLOCA]])
// CHECK: call void @llvm.memcpy.{{.*}}(i8* align 2 %{{.*}}, i8* align 4 %{{.*}}, i{{[0-9]+}} 32, i1 false)
// check that calls whose destination is a field of a packed struct
// go through an alloca rather than calling the function with an
-//
-// compile-flags: -C no-prepopulate-passes
-// only-riscv64
-// only-linux
+// compile-flags: --target riscv64gc-unknown-linux-gnu -C no-prepopulate-passes
+// needs-llvm-components: riscv
+
#![crate_type = "lib"]
+#![no_core]
+#![feature(no_core, lang_items)]
#![allow(improper_ctypes)]
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
// CHECK: define void @f_void()
#[no_mangle]
pub extern "C" fn f_void() {}
-// CHECK: define zeroext i1 @f_scalar_0(i1 zeroext %a)
+// CHECK: define noundef zeroext i1 @f_scalar_0(i1 noundef zeroext %a)
#[no_mangle]
pub extern "C" fn f_scalar_0(a: bool) -> bool {
a
// CHECK: define void @f_agg_tiny(i64 %0)
#[no_mangle]
pub extern "C" fn f_agg_tiny(mut e: Tiny) {
- e.a += e.b;
- e.c += e.d;
}
// CHECK: define i64 @f_agg_tiny_ret()
// CHECK: define void @f_agg_small([2 x i64] %0)
#[no_mangle]
pub extern "C" fn f_agg_small(mut x: Small) {
- x.a += unsafe { *x.b };
- x.b = &mut x.a;
}
// CHECK: define [2 x i64] @f_agg_small_ret()
#[no_mangle]
pub extern "C" fn f_agg_small_ret() -> Small {
- Small { a: 1, b: core::ptr::null_mut() }
+ Small { a: 1, b: 0 as *mut _ }
}
#[repr(C)]
// CHECK: define void @f_agg_small_aligned(i128 %0)
#[no_mangle]
pub extern "C" fn f_agg_small_aligned(mut x: SmallAligned) {
- x.a += x.a;
}
#[repr(C)]
// CHECK: define void @f_agg_large(%Large* {{.*}}%x)
#[no_mangle]
pub extern "C" fn f_agg_large(mut x: Large) {
- x.a = x.b + x.c + x.d;
}
// CHECK: define void @f_agg_large_ret(%Large* {{.*}}sret{{.*}}, i32 signext %i, i8 signext %j)
4.0f64,
5.0f64,
Tiny { a: 1, b: 2, c: 3, d: 4 },
- Small { a: 10, b: core::ptr::null_mut() },
+ Small { a: 10, b: 0 as *mut _ },
SmallAligned { a: 11 },
Large { a: 12, b: 13, c: 14, d: 15 },
);
#![crate_type = "lib"]
-// CHECK: define{{.*}}{ i8, i8 } @pair_bool_bool(i1 zeroext %pair.0, i1 zeroext %pair.1)
+// CHECK: define{{.*}}{ i8, i8 } @pair_bool_bool(i1 noundef zeroext %pair.0, i1 noundef zeroext %pair.1)
#[no_mangle]
pub fn pair_bool_bool(pair: (bool, bool)) -> (bool, bool) {
pair
}
-// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 zeroext %pair.0, i32 %pair.1)
+// CHECK: define{{.*}}{ i8, i32 } @pair_bool_i32(i1 noundef zeroext %pair.0, i32 %pair.1)
#[no_mangle]
pub fn pair_bool_i32(pair: (bool, i32)) -> (bool, i32) {
pair
}
-// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 %pair.0, i1 zeroext %pair.1)
+// CHECK: define{{.*}}{ i32, i8 } @pair_i32_bool(i32 %pair.0, i1 noundef zeroext %pair.1)
#[no_mangle]
pub fn pair_i32_bool(pair: (i32, bool)) -> (i32, bool) {
pair
}
-// CHECK: define{{.*}}{ i8, i8 } @pair_and_or(i1 zeroext %_1.0, i1 zeroext %_1.1)
+// CHECK: define{{.*}}{ i8, i8 } @pair_and_or(i1 noundef zeroext %_1.0, i1 noundef zeroext %_1.1)
#[no_mangle]
pub fn pair_and_or((a, b): (bool, bool)) -> (bool, bool) {
// Make sure it can operate directly on the unpacked args
(a && b, a || b)
}
-// CHECK: define{{.*}}void @pair_branches(i1 zeroext %_1.0, i1 zeroext %_1.1)
+// CHECK: define{{.*}}void @pair_branches(i1 noundef zeroext %_1.0, i1 noundef zeroext %_1.1)
#[no_mangle]
pub fn pair_branches((a, b): (bool, bool)) {
// Make sure it can branch directly on the unpacked bool args
unsafe { std::mem::transmute(x) }
}
-// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 zeroext %b)
+// CHECK-LABEL: define{{.*}}i8 @bool_to_byte(i1 noundef zeroext %b)
// CHECK: %1 = zext i1 %b to i8
// CHECK-NEXT: store i8 %1, i8* %0
// CHECK-NEXT: %2 = load i8, i8* %0
unsafe { std::mem::transmute(b) }
}
-// CHECK-LABEL: define{{.*}}zeroext i1 @byte_to_bool(i8 %byte)
+// CHECK-LABEL: define{{.*}}noundef zeroext i1 @byte_to_bool(i8 %byte)
// CHECK: %1 = trunc i8 %byte to i1
// CHECK-NEXT: %2 = zext i1 %1 to i8
// CHECK-NEXT: store i8 %2, i8* %0
pub fn test_CUnionU128x2(_: CUnionU128x2) { loop {} }
pub union UnionBool { b:bool }
-// CHECK: define zeroext i1 @test_UnionBool(i8 %b)
+// CHECK: define noundef zeroext i1 @test_UnionBool(i8 %b)
#[no_mangle]
pub fn test_UnionBool(b: UnionBool) -> bool { unsafe { b.b } }
// CHECK: %0 = trunc i8 %b to i1
--- /dev/null
+// needs-llvm-components: arm
+// compile-flags: --target=armv7-unknown-linux-gnueabihf --crate-type=rlib -Cno-prepopulate-passes
+#![no_core]
+#![feature(no_core, lang_items, c_unwind)]
+#[lang="sized"]
+trait Sized { }
+
+// Test that `nounwind` atributes are correctly applied to exported `aapcs` and
+// `aapcs-unwind` extern functions. `aapcs-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "aapcs" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "aapcs-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
--- /dev/null
+// compile-flags: -C opt-level=0
+
+// Test that `nounwind` atributes are correctly applied to exported `cdecl` and
+// `cdecl-unwind` extern functions. `cdecl-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "cdecl" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "cdecl-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
--- /dev/null
+// needs-llvm-components: x86
+// compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes
+#![no_core]
+#![feature(no_core, lang_items, c_unwind)]
+#[lang="sized"]
+trait Sized { }
+
+// Test that `nounwind` atributes are correctly applied to exported `fastcall` and
+// `fastcall-unwind` extern functions. `fastcall-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "fastcall" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "fastcall-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
--- /dev/null
+// needs-llvm-components: x86
+// compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib -Cno-prepopulate-passes
+#![no_core]
+#![feature(no_core, lang_items, c_unwind)]
+#[lang="sized"]
+trait Sized { }
+
+// Test that `nounwind` atributes are correctly applied to exported `sysv64` and
+// `sysv64-unwind` extern functions. `sysv64-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "sysv64" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "sysv64-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
--- /dev/null
+// needs-llvm-components: x86
+// compile-flags: --target=i686-pc-windows-msvc --crate-type=rlib -Cno-prepopulate-passes
+#![no_core]
+#![feature(no_core, lang_items, c_unwind, abi_vectorcall)]
+#[lang="sized"]
+trait Sized { }
+
+// Test that `nounwind` atributes are correctly applied to exported `vectorcall` and
+// `vectorcall-unwind` extern functions. `vectorcall-unwind` functions MUST NOT have this attribute.
+// We disable optimizations above to prevent LLVM from inferring the attribute.
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "vectorcall" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "vectorcall-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
--- /dev/null
+// needs-llvm-components: x86
+// compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib -Cno-prepopulate-passes
+#![no_core]
+#![feature(no_core, lang_items, c_unwind)]
+#[lang="sized"]
+trait Sized { }
+
+// Test that `nounwind` atributes are correctly applied to exported `win64` and
+// `win64-unwind` extern functions. `win64-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "win64" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "win64-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct. First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
--- /dev/null
+#![crate_type = "lib"]
+#![feature(used_with_arg)]
+
+// CHECK: @llvm.used = appending global [1 x i8*]{{.*}}USED_LINKER
+#[used(linker)]
+static mut USED_LINKER: [usize; 1] = [0];
+
+// CHECK-NEXT: @llvm.compiler.used = appending global [1 x i8*]{{.*}}USED_COMPILER
+#[used(compiler)]
+static mut USED_COMPILER: [usize; 1] = [0];
#[cfg(any(cfail1,cfail4))]
impl AddDefaultTrait for Foo {
- // -------------------------------------------------------------------------------------------
+ // ----------------------------------------------------
// -------------------------
- fn method_name() { }
+ // ----------------------------------------------------
+ // -------------------------
+ fn method_name() { }
}
#[cfg(not(any(cfail1,cfail4)))]
#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
impl AddDefaultTrait for Foo {
- #[rustc_clean(except="hir_owner,hir_owner_nodes,associated_item", cfg="cfail2")]
+ #[rustc_clean(except="associated_item", cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
- #[rustc_clean(except="hir_owner,hir_owner_nodes,associated_item,optimized_mir", cfg="cfail5")]
+ #[rustc_clean(except="associated_item", cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
default fn method_name() { }
}
StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
_2 = const {alloc1: &&[(Option<i32>, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
- // ty::Const
- // + ty: &&[(std::option::Option<i32>, &[&str])]
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const_allocation.rs:8:5: 8:8
- // + literal: Const { ty: &&[(std::option::Option<i32>, &[&str])], val: Value(Scalar(alloc1)) }
+ // + literal: Const { ty: &&[(Option<i32>, &[&str])], val: Value(Scalar(alloc1)) }
_1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9
StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9
StorageLive(_1); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
StorageLive(_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
_2 = const {alloc1: &&[(Option<i32>, &[&str])]}; // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
- // ty::Const
- // + ty: &&[(std::option::Option<i32>, &[&str])]
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const_allocation.rs:8:5: 8:8
- // + literal: Const { ty: &&[(std::option::Option<i32>, &[&str])], val: Value(Scalar(alloc1)) }
+ // + literal: Const { ty: &&[(Option<i32>, &[&str])], val: Value(Scalar(alloc1)) }
_1 = (*_2); // scope 0 at $DIR/const_allocation.rs:8:5: 8:8
StorageDead(_2); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9
StorageDead(_1); // scope 0 at $DIR/const_allocation.rs:8:8: 8:9
StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
_2 = const {alloc1: &&[(Option<i32>, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
- // ty::Const
- // + ty: &&[(std::option::Option<i32>, &[&u8])]
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const_allocation2.rs:5:5: 5:8
- // + literal: Const { ty: &&[(std::option::Option<i32>, &[&u8])], val: Value(Scalar(alloc1)) }
+ // + literal: Const { ty: &&[(Option<i32>, &[&u8])], val: Value(Scalar(alloc1)) }
_1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
StorageLive(_1); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
StorageLive(_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
_2 = const {alloc1: &&[(Option<i32>, &[&u8])]}; // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
- // ty::Const
- // + ty: &&[(std::option::Option<i32>, &[&u8])]
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const_allocation2.rs:5:5: 5:8
- // + literal: Const { ty: &&[(std::option::Option<i32>, &[&u8])], val: Value(Scalar(alloc1)) }
+ // + literal: Const { ty: &&[(Option<i32>, &[&u8])], val: Value(Scalar(alloc1)) }
_1 = (*_2); // scope 0 at $DIR/const_allocation2.rs:5:5: 5:8
StorageDead(_2); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
StorageDead(_1); // scope 0 at $DIR/const_allocation2.rs:5:8: 5:9
StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
_2 = const {alloc1: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
- // ty::Const
- // + ty: &&Packed
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const_allocation3.rs:5:5: 5:8
// + literal: Const { ty: &&Packed, val: Value(Scalar(alloc1)) }
StorageLive(_1); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
StorageLive(_2); // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
_2 = const {alloc1: &&Packed}; // scope 0 at $DIR/const_allocation3.rs:5:5: 5:8
- // ty::Const
- // + ty: &&Packed
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const_allocation3.rs:5:5: 5:8
// + literal: Const { ty: &&Packed, val: Value(Scalar(alloc1)) }
bb0: {
_3 = const {alloc1: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
- // ty::Const
- // + ty: &i32
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/const-promotion-extern-static.rs:9:33: 9:34
// + literal: Const { ty: &i32, val: Value(Scalar(alloc1)) }
- StorageLive(_5); // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
- _5 = const {alloc1: &i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:9:33: 9:34
+ _6 = const BAR::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:9:31: 9:44
- // ty::Const
-- // + ty: &i32
-- // + val: Value(Scalar(alloc1))
++ // ty::Const
+ // + ty: &[&i32; 1]
+ // + val: Unevaluated(BAR, [], Some(promoted[0]))
// mir::Constant
bb0: {
_3 = const {alloc3: *const i32}; // scope 0 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
- // ty::Const
- // + ty: *const i32
- // + val: Value(Scalar(alloc3))
// mir::Constant
// + span: $DIR/const-promotion-extern-static.rs:13:42: 13:43
// + literal: Const { ty: *const i32, val: Value(Scalar(alloc3)) }
- StorageLive(_5); // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
- _5 = const {alloc3: *const i32}; // scope 1 at $DIR/const-promotion-extern-static.rs:13:42: 13:43
+ _6 = const FOO::promoted[0]; // scope 0 at $DIR/const-promotion-extern-static.rs:13:31: 13:55
- // ty::Const
-- // + ty: *const i32
-- // + val: Value(Scalar(alloc3))
++ // ty::Const
+ // + ty: &[&i32; 1]
+ // + val: Unevaluated(FOO, [], Some(promoted[0]))
// mir::Constant
--- /dev/null
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/invalid_constant.rs:15:11: 15:11
+ let _1: std::option::Option<()>; // in scope 0 at $DIR/invalid_constant.rs:16:5: 16:12
+ let mut _2: std::option::Option<std::option::Option<()>>; // in scope 0 at $DIR/invalid_constant.rs:16:7: 16:11
+ scope 1 (inlined f) { // at $DIR/invalid_constant.rs:16:5: 16:12
+ debug x => _2; // in scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+ let mut _3: isize; // in scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+ let _4: std::option::Option<()>; // in scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+ scope 2 {
+ debug y => _4; // in scope 2 at $DIR/invalid_constant.rs:16:5: 16:12
+ }
+ }
+
+ bb0: {
+ discriminant(_2) = 0; // scope 0 at $DIR/invalid_constant.rs:16:7: 16:11
+- _3 = discriminant(_2); // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+- switchInt(move _3) -> [0_isize: bb3, otherwise: bb2]; // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
++ _3 = const 0_isize; // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
++ switchInt(const 0_isize) -> [0_isize: bb3, otherwise: bb2]; // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+ }
+
+ bb1: {
+ nop; // scope 0 at $DIR/invalid_constant.rs:15:11: 17:2
+ return; // scope 0 at $DIR/invalid_constant.rs:17:2: 17:2
+ }
+
+ bb2: {
+- _4 = ((_2 as Some).0: std::option::Option<()>); // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+- _1 = _4; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12
++ _4 = const Scalar(0x02): Option::<()>; // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
++ // ty::Const
++ // + ty: std::option::Option<()>
++ // + val: Value(Scalar(0x02))
++ // mir::Constant
++ // + span: $DIR/invalid_constant.rs:16:5: 16:12
++ // + literal: Const { ty: std::option::Option<()>, val: Value(Scalar(0x02)) }
++ _1 = const Scalar(0x02): Option::<()>; // scope 2 at $DIR/invalid_constant.rs:16:5: 16:12
++ // ty::Const
++ // + ty: std::option::Option<()>
++ // + val: Value(Scalar(0x02))
++ // mir::Constant
++ // + span: $DIR/invalid_constant.rs:16:5: 16:12
++ // + literal: Const { ty: std::option::Option<()>, val: Value(Scalar(0x02)) }
+ goto -> bb1; // scope 0 at $DIR/invalid_constant.rs:10:20: 10:21
+ }
+
+ bb3: {
+ discriminant(_1) = 0; // scope 1 at $DIR/invalid_constant.rs:16:5: 16:12
+ goto -> bb1; // scope 0 at $DIR/invalid_constant.rs:9:17: 9:21
+ }
+ }
+
--- /dev/null
+// Verify that we can pretty print invalid constant introduced
+// by constant propagation. Regression test for issue #93688.
+//
+// compile-flags: -Copt-level=0 -Zinline-mir
+
+#[inline(always)]
+pub fn f(x: Option<Option<()>>) -> Option<()> {
+ match x {
+ None => None,
+ Some(y) => y,
+ }
+}
+
+// EMIT_MIR invalid_constant.main.ConstProp.diff
+fn main() {
+ f(None);
+}
StorageLive(_3); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19
StorageLive(_4); // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19
_4 = const {alloc1: *mut u32}; // scope 2 at $DIR/mutable_variable_no_prop.rs:9:13: 9:19
- // ty::Const
- // + ty: *mut u32
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/mutable_variable_no_prop.rs:9:13: 9:19
// + literal: Const { ty: *mut u32, val: Value(Scalar(alloc1)) }
StorageLive(_2); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16
StorageLive(_3); // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16
_3 = const {alloc1: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:13: 7:16
- // ty::Const
- // + ty: &u8
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/read_immutable_static.rs:7:13: 7:16
// + literal: Const { ty: &u8, val: Value(Scalar(alloc1)) }
StorageLive(_4); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22
StorageLive(_5); // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22
_5 = const {alloc1: &u8}; // scope 0 at $DIR/read_immutable_static.rs:7:19: 7:22
- // ty::Const
- // + ty: &u8
- // + val: Value(Scalar(alloc1))
// mir::Constant
// + span: $DIR/read_immutable_static.rs:7:19: 7:22
// + literal: Const { ty: &u8, val: Value(Scalar(alloc1)) }
-// Test that `-Z instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file)
+// Test that `-C instrument-coverage` with `-Z dump-mir-graphviz` generates a graphviz (.dot file)
// rendering of the `BasicCoverageBlock` coverage control flow graph, with counters and
// expressions.
// needs-profiler-support
-// compile-flags: -Z instrument-coverage -Z dump-mir-graphviz
+// compile-flags: -C instrument-coverage -Z dump-mir-graphviz
// EMIT_MIR coverage_graphviz.main.InstrumentCoverage.0.dot
// EMIT_MIR coverage_graphviz.bar.InstrumentCoverage.0.dot
fn main() {
-// Test that `-Z instrument-coverage` injects Coverage statements. The Coverage Counter statements
+// Test that `-C instrument-coverage` injects Coverage statements. The Coverage Counter statements
// are later converted into LLVM instrprof.increment intrinsics, during codegen.
// needs-profiler-support
// ignore-windows
-// compile-flags: -Z instrument-coverage --remap-path-prefix={{src-base}}=/the/src
+// compile-flags: -C instrument-coverage --remap-path-prefix={{src-base}}=/the/src
// EMIT_MIR instrument_coverage.main.InstrumentCoverage.diff
// EMIT_MIR instrument_coverage.bar.InstrumentCoverage.diff
let mut _19: *const T; // in scope 0 at $DIR/issue_76432.rs:9:54: 9:68
let mut _20: *const T; // in scope 0 at $DIR/issue_76432.rs:9:70: 9:84
let mut _21: *const T; // in scope 0 at $DIR/issue_76432.rs:9:70: 9:84
- let mut _22: !; // in scope 0 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ let mut _22: !; // in scope 0 at $SRC_DIR/core/src/panic.rs:LL:COL
let mut _23: &[T; 3]; // in scope 0 at $DIR/issue_76432.rs:7:19: 7:29
scope 1 {
debug v => _2; // in scope 1 at $DIR/issue_76432.rs:7:9: 7:10
}
bb1: {
- StorageLive(_22); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- core::panicking::panic(const "internal error: entered unreachable code"); // scope 1 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ StorageLive(_22); // scope 1 at $SRC_DIR/core/src/panic.rs:LL:COL
+ core::panicking::panic(const "internal error: entered unreachable code"); // scope 1 at $SRC_DIR/core/src/panic.rs:LL:COL
// mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ // + span: $SRC_DIR/core/src/panic.rs:LL:COL
// + literal: Const { ty: fn(&'static str) -> ! {core::panicking::panic}, val: Value(Scalar(<ZST>)) }
// ty::Const
// + ty: &str
// + val: Value(Slice { data: Allocation { bytes: [105, 110, 116, 101, 114, 110, 97, 108, 32, 101, 114, 114, 111, 114, 58, 32, 101, 110, 116, 101, 114, 101, 100, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 32, 99, 111, 100, 101], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1099511627775], len: Size { raw: 40 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 40 })
// mir::Constant
- // + span: $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ // + span: $SRC_DIR/core/src/panic.rs:LL:COL
// + literal: Const { ty: &str, val: Value(Slice { data: Allocation { bytes: [105, 110, 116, 101, 114, 110, 97, 108, 32, 101, 114, 114, 111, 114, 58, 32, 101, 110, 116, 101, 114, 101, 100, 32, 117, 110, 114, 101, 97, 99, 104, 97, 98, 108, 101, 32, 99, 111, 100, 101], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [1099511627775], len: Size { raw: 40 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, start: 0, end: 40 }) }
}
let _ = #[attr] foo![#! [attr]];
let _ = #[attr] foo! {};
let _ = #[attr] foo! { #! [attr] };
- let _ = #[attr] Foo{bar: baz,};
- let _ = #[attr] Foo{..foo};
- let _ = #[attr] Foo{bar: baz, ..foo};
+ let _ = #[attr] Foo { bar: baz };
+ let _ = #[attr] Foo { ..foo };
+ let _ = #[attr] Foo { bar: baz, ..foo };
let _ = #[attr] (0);
{
#[allow()]
const C: C =
- C{
+ C {
#[cfg(debug_assertions)]
field: 0,
#[cfg(not(debug_assertions))]
- field: 1,};
+ field: 1,
+ };
fn _7() {
#[rustc_dummy]
- Foo{data: (),};
+ Foo { data: () };
- let _ = #[rustc_dummy] Foo{data: (),};
+ let _ = #[rustc_dummy] Foo { data: () };
}
fn _8() {
let mut x = 0;
let _ = #[rustc_dummy] x = 15;
let _ = #[rustc_dummy] x += 15;
- let s = Foo{data: (),};
+ let s = Foo { data: () };
let _ = #[rustc_dummy] s.data;
let _ = (#[rustc_dummy] s).data;
let t = Bar(());
let _ = #[rustc_dummy] expr_mac!();
let _ = #[rustc_dummy] expr_mac![];
let _ = #[rustc_dummy] expr_mac! {};
- let _ = #[rustc_dummy] Foo{data: (),};
- let _ = #[rustc_dummy] Foo{..s};
- let _ = #[rustc_dummy] Foo{data: (), ..s};
+ let _ = #[rustc_dummy] Foo { data: () };
+ let _ = #[rustc_dummy] Foo { ..s };
+ let _ = #[rustc_dummy] Foo { data: (), ..s };
let _ = #[rustc_dummy] (0);
}
--- /dev/null
+// pp-exact
+// edition:2021
+
+#![allow(unused_imports)]
+
+use ::std::fmt::{self, Debug, Display, Write as _};
+
+use core::option::Option::*;
+
+use core::{
+ cmp::{Eq, Ord, PartialEq, PartialOrd},
+ convert::{AsMut, AsRef, From, Into},
+ iter::{
+ DoubleEndedIterator, ExactSizeIterator, Extend, FromIterator,
+ IntoIterator, Iterator,
+ },
+ marker::{
+ Copy as Copy, Send as Send, Sized as Sized, Sync as Sync, Unpin as U,
+ },
+ ops::{*, Drop, Fn, FnMut, FnOnce},
+};
+
+fn main() {}
-#![feature(cfg_target_has_atomic, no_core, intrinsics, lang_items)]
+#![feature(no_core, intrinsics, lang_items)]
#![crate_type="rlib"]
#![no_core]
-include ../tools.mk
all:
- $(RUSTC) --edition=2018 --crate-type=rlib ../../../../library/core/src/lib.rs --cfg no_fp_fmt_parse
+ $(RUSTC) --edition=2021 --crate-type=rlib ../../../../library/core/src/lib.rs --cfg no_fp_fmt_parse
test_llvm_ir:
# Compile the test program with non-experimental coverage instrumentation, and generate LLVM IR
$(RUSTC) $(BASEDIR)/testprog.rs \
- -Zinstrument-coverage \
+ -Cinstrument-coverage \
--emit=llvm-ir
cat "$(TMPDIR)"/testprog.ll | \
# Check for metadata, variables, declarations, and function definitions injected
-# into LLVM IR when compiling with -Zinstrument-coverage.
+# into LLVM IR when compiling with -Cinstrument-coverage.
WINDOWS: $__llvm_profile_runtime_user = comdat any
CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8
CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog14will_be_called = {{private|internal}} global
-CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called,
+CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog14will_be_called
CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8
CHECK: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global
CHECK-SAME: section "[[INSTR_PROF_CNTS]]"{{.*}}, align 8
CHECK: @__profd__R{{[a-zA-Z0-9_]+}}testprog4main = {{private|internal}} global
-CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main,
+CHECK-SAME: @__profc__R{{[a-zA-Z0-9_]+}}testprog4main
CHECK-SAME: section "[[INSTR_PROF_DATA]]"{{.*}}, align 8
CHECK: @__llvm_prf_nm = private constant
# Compile the test library with coverage instrumentation
$(RUSTC) $(SOURCEDIR)/lib/$@.rs \
$$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/lib/$@.rs ) \
- --crate-type rlib -Zinstrument-coverage
+ --crate-type rlib -Cinstrument-coverage
%: $(SOURCEDIR)/%.rs
# Compile the test program with coverage instrumentation
$(RUSTC) $(SOURCEDIR)/$@.rs \
$$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \
- -L "$(TMPDIR)" -Zinstrument-coverage
+ -L "$(TMPDIR)" -Cinstrument-coverage
# Run it in order to generate some profiling data,
# with `LLVM_PROFILE_FILE=<profdata_file>` environment variable set to
LLVM_PROFILE_FILE="$(TMPDIR)"/$@-%p-%m.profraw \
$(RUSTDOC) --crate-name workaround_for_79771 --test $(SOURCEDIR)/$@.rs \
$$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \
- -L "$(TMPDIR)" -Zinstrument-coverage \
+ -L "$(TMPDIR)" -Cinstrument-coverage \
-Z unstable-options --persist-doctests=$(TMPDIR)/rustdoc-$@
# Postprocess the profiling data so it can be used by the llvm-cov tool
10| 1|}
../coverage/lib/inline_always_with_dead_code.rs:
- 1| |// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 -Copt-level=0
+ 1| |// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0
2| |
3| |#![allow(dead_code)]
4| |
6| | println!("called but not covered");
7| |}
8| |
- 9| |#[no_coverage]
- 10| |fn do_not_add_coverage_2() {
+ 9| |fn do_not_add_coverage_2() {
+ 10| | #![no_coverage]
11| | println!("called but not covered");
12| |}
13| |
28| 0| println!("not called but covered");
29| 0|}
30| |
- 31| 1|fn main() {
- 32| 1| do_not_add_coverage_1();
- 33| 1| do_not_add_coverage_2();
- 34| 1| add_coverage_1();
- 35| 1| add_coverage_2();
- 36| 1|}
+ 31| |// FIXME: These test-cases illustrate confusing results of nested functions.
+ 32| |// See https://github.com/rust-lang/rust/issues/93319
+ 33| |mod nested_fns {
+ 34| | #[no_coverage]
+ 35| | pub fn outer_not_covered(is_true: bool) {
+ 36| 1| fn inner(is_true: bool) {
+ 37| 1| if is_true {
+ 38| 1| println!("called and covered");
+ 39| 1| } else {
+ 40| 0| println!("absolutely not covered");
+ 41| 0| }
+ 42| 1| }
+ 43| | println!("called but not covered");
+ 44| | inner(is_true);
+ 45| | }
+ 46| |
+ 47| 1| pub fn outer(is_true: bool) {
+ 48| 1| println!("called and covered");
+ 49| 1| inner_not_covered(is_true);
+ 50| 1|
+ 51| 1| #[no_coverage]
+ 52| 1| fn inner_not_covered(is_true: bool) {
+ 53| 1| if is_true {
+ 54| 1| println!("called but not covered");
+ 55| 1| } else {
+ 56| 1| println!("absolutely not covered");
+ 57| 1| }
+ 58| 1| }
+ 59| 1| }
+ 60| |
+ 61| 1| pub fn outer_both_covered(is_true: bool) {
+ 62| 1| println!("called and covered");
+ 63| 1| inner(is_true);
+ 64| 1|
+ 65| 1| fn inner(is_true: bool) {
+ 66| 1| if is_true {
+ 67| 1| println!("called and covered");
+ 68| 1| } else {
+ 69| 0| println!("absolutely not covered");
+ 70| 0| }
+ 71| 1| }
+ 72| 1| }
+ 73| |}
+ 74| |
+ 75| 1|fn main() {
+ 76| 1| let is_true = std::env::args().len() == 1;
+ 77| 1|
+ 78| 1| do_not_add_coverage_1();
+ 79| 1| do_not_add_coverage_2();
+ 80| 1| add_coverage_1();
+ 81| 1| add_coverage_2();
+ 82| 1|
+ 83| 1| nested_fns::outer_not_covered(is_true);
+ 84| 1| nested_fns::outer(is_true);
+ 85| 1| nested_fns::outer_both_covered(is_true);
+ 86| 1|}
78| |// generic functions with:
79| |//
80| |// ```shell
- 81| |// $ `rustc -Z instrument-coverage=except-unused-generics ...`
+ 81| |// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...`
82| |// ```
83| |//
84| |// Even though this function is used by `uses_crate.rs` (and
-// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 -Copt-level=0
+// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0
#![allow(dead_code)]
// generic functions with:
//
// ```shell
-// $ `rustc -Z instrument-coverage=except-unused-generics ...`
+// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...`
// ```
//
// Even though this function is used by `uses_crate.rs` (and
println!("called but not covered");
}
-#[no_coverage]
fn do_not_add_coverage_2() {
+ #![no_coverage]
println!("called but not covered");
}
println!("not called but covered");
}
+// FIXME: These test-cases illustrate confusing results of nested functions.
+// See https://github.com/rust-lang/rust/issues/93319
+mod nested_fns {
+ #[no_coverage]
+ pub fn outer_not_covered(is_true: bool) {
+ fn inner(is_true: bool) {
+ if is_true {
+ println!("called and covered");
+ } else {
+ println!("absolutely not covered");
+ }
+ }
+ println!("called but not covered");
+ inner(is_true);
+ }
+
+ pub fn outer(is_true: bool) {
+ println!("called and covered");
+ inner_not_covered(is_true);
+
+ #[no_coverage]
+ fn inner_not_covered(is_true: bool) {
+ if is_true {
+ println!("called but not covered");
+ } else {
+ println!("absolutely not covered");
+ }
+ }
+ }
+
+ pub fn outer_both_covered(is_true: bool) {
+ println!("called and covered");
+ inner(is_true);
+
+ fn inner(is_true: bool) {
+ if is_true {
+ println!("called and covered");
+ } else {
+ println!("absolutely not covered");
+ }
+ }
+ }
+}
+
fn main() {
+ let is_true = std::env::args().len() == 1;
+
do_not_add_coverage_1();
do_not_add_coverage_2();
add_coverage_1();
add_coverage_2();
+
+ nested_fns::outer_not_covered(is_true);
+ nested_fns::outer(is_true);
+ nested_fns::outer_both_covered(is_true);
}
output_dir: None,
file_loader: None,
diagnostic_output: DiagnosticOutput::Default,
- stderr: None,
lint_caps: Default::default(),
parse_sess_created: None,
register_lints: None,
+++ /dev/null
--include ../tools.mk
-
-all:
- $(RUSTC) foo.rs
- $(call RUN,foo)
- rm $(TMPDIR)/$(call DYLIB_GLOB,foo)
+++ /dev/null
-#![crate_type = "dylib"]
-#![crate_type = "bin"]
-
-fn main() {}
{ "type": "test", "name": "c", "event": "ok" }
{ "type": "test", "event": "started", "name": "d" }
{ "type": "test", "name": "d", "event": "ignored" }
-{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "allowed_fail": 0, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
+{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
{ "type": "test", "name": "c", "event": "ok", "stdout": "thread 'main' panicked at 'assertion failed: false', f.rs:15:5\n" }
{ "type": "test", "event": "started", "name": "d" }
{ "type": "test", "name": "d", "event": "ignored" }
-{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "allowed_fail": 0, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
+{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
-include ../tools.mk
all:
- $(RUSTC) foo-bar.rs
+ $(RUSTC) foo-bar.rs --crate-type bin
[ -f $(TMPDIR)/$(call BIN,foo-bar) ]
+ $(RUSTC) foo-bar.rs --crate-type lib
[ -f $(TMPDIR)/libfoo_bar.rlib ]
-#![crate_type = "lib"]
-#![crate_type = "bin"]
-
fn main() {}
--- /dev/null
+deps := ex
+
+-include ../rustdoc-scrape-examples-multiple/scrape.mk
+
+all: scrape
--- /dev/null
+struct Foo;
+impl Foo {
+ fn bar() { foobar::ok(); }
+}
+
+fn main() {
+ Foo::bar();
+}
--- /dev/null
+// @has foobar/fn.ok.html '//*[@class="docblock scraped-example-list"]//code' ' '
+
+pub fn ok() {}
move-cursor-to: "#impl"
assert-css: ("#impl a.anchor", {"color": "rgb(0, 0, 0)"})
-// Now we check the positions: only the first heading of the top doc comment should
-// have a different position.
-move-cursor-to: ".top-doc .docblock .section-header:first-child"
-assert-css: (
- ".top-doc .docblock .section-header:first-child > a::before",
- {"left": "-10px", "padding-right": "10px"},
-)
-// We also check that the heading itself has a different indent.
-assert-css: (".top-doc .docblock .section-header:first-child", {"margin-left": "15px"})
-
-move-cursor-to: ".top-doc .docblock .section-header:not(:first-child)"
-assert-css: (
- ".top-doc .docblock .section-header:not(:first-child) > a::before",
- {"left": "-25px", "padding-right": "10px"},
-)
-assert-css: (".top-doc .docblock .section-header:not(:first-child)", {"margin-left": "0px"})
-
-// Now let's check some other docblock headings...
-// First the impl block docs.
-move-cursor-to: "#title-for-struct-impl-doc"
-assert-css: (
- "#title-for-struct-impl-doc > a::before",
- {"left": "-25px", "padding-right": "10px"},
-)
-assert-css: ("#title-for-struct-impl-doc", {"margin-left": "0px"})
-// Now a method docs.
-move-cursor-to: "#title-for-struct-impl-item-doc"
-assert-css: (
- "#title-for-struct-impl-item-doc > a::before",
- {"left": "-25px", "padding-right": "10px"},
-)
assert-css: ("#title-for-struct-impl-item-doc", {"margin-left": "0px"})
-
-// Finally, we want to ensure that if the first element of the doc block isn't a heading,
-// if there is a heading afterwards, it won't have the indent.
-goto: file://|DOC_PATH|/test_docs/enum.WhoLetTheDogOut.html
-
-move-cursor-to: ".top-doc .docblock .section-header"
-assert-css: (
- ".top-doc .docblock .section-header > a::before",
- {"left": "-25px", "padding-right": "10px"},
-)
-assert-css: (".top-doc .docblock .section-header", {"margin-left": "0px"})
show-text: true // We need to enable text draw to be able to have the "real" size
// Little explanations for this test: if the text wasn't displayed on two lines, it would take
// around 20px (which is the font size).
-assert-property: (".docblock p > code", {"offsetHeight": "42"})
+assert-property: (".docblock p > code", {"offsetHeight": "44"})
assert-attribute: ("#blanket-implementations-list > details:nth-child(2)", {"open": ""})
// We first check that the impl block is open by default.
assert-attribute: ("#implementations + details", {"open": ""})
-// We collapse it.
-click: "#implementations + details > summary"
-// We check that it was collapsed as expected.
-assert-attribute-false: ("#implementations + details", {"open": ""})
// To ensure that we will click on the currently hidden method.
-assert-text: (".sidebar-links > a", "must_use")
-click: ".sidebar-links > a"
+assert-text: (".sidebar-elems section .block li > a", "must_use")
+click: ".sidebar-elems section .block li > a"
// We check that the impl block was opened as expected so that we can see the method.
assert-attribute: ("#implementations + details", {"open": ""})
assert-css: (".small-section-header a", {"color": "rgb(197, 197, 197)"}, ALL)
goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html
-assert-css: (".section-header a", {"color": "rgb(57, 175, 215)"}, ALL)
+// We select headings (h2, h3, h...).
+assert-css: (".docblock > :not(p) > a", {"color": "rgb(57, 175, 215)"}, ALL)
// Dark theme
local-storage: {
assert-css: (".small-section-header a", {"color": "rgb(221, 221, 221)"}, ALL)
goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html
-assert-css: (".section-header a", {"color": "rgb(210, 153, 29)"}, ALL)
+// We select headings (h2, h3, h...).
+assert-css: (".docblock > :not(p) > a", {"color": "rgb(210, 153, 29)"}, ALL)
// Light theme
local-storage: {"rustdoc-theme": "light", "rustdoc-use-system-theme": "false"}
assert-css: (".small-section-header a", {"color": "rgb(0, 0, 0)"}, ALL)
goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html
-assert-css: (".section-header a", {"color": "rgb(56, 115, 173)"}, ALL)
+// We select headings (h2, h3, h...).
+assert-css: (".docblock > :not(p) > a", {"color": "rgb(56, 115, 173)"}, ALL)
// Most of these sizes are set in CSS in `em` units, so here's a conversion chart based on our
// default 16px font size:
// 24px 1.5em
-// 22.4px 1.4em
-// 20.8px 1.3em
-// 18.4px 1.15em
-// 17.6px 1.1em
-// 16px 1em
-// 15.2px 0.95em
+// 22px 1.375rem
+// 20px 1.25rem
+// 18px 1.125em
+// 16px 1rem
+// 14px 0.875rem
goto: file://|DOC_PATH|/test_docs/struct.HeavilyDocumentedStruct.html
assert-css: ("h1.fqn", {"font-size": "24px"})
-assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"})
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
-assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
-assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "17.6px"})
+assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "18px"})
assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"})
-assert-css: ("h2#fields", {"font-size": "22.4px"})
+assert-css: ("h2#fields", {"font-size": "22px"})
assert-css: ("h2#fields", {"border-bottom-width": "1px"})
-assert-css: ("h3#title-for-field", {"font-size": "20.8px"})
+assert-css: ("h3#title-for-field", {"font-size": "20px"})
assert-css: ("h3#title-for-field", {"border-bottom-width": "0px"})
assert-css: ("h4#sub-heading-for-field", {"font-size": "16px"})
assert-css: ("h4#sub-heading-for-field", {"border-bottom-width": "0px"})
-assert-css: ("h2#implementations", {"font-size": "22.4px"})
+assert-css: ("h2#implementations", {"font-size": "22px"})
assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
-assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"})
+assert-css: ("#impl > h3.code-header", {"font-size": "18px"})
assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"})
assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"})
assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"})
assert-css: ("h4#title-for-struct-impl-doc", {"border-bottom-width": "0px"})
assert-css: ("h5#sub-heading-for-struct-impl-doc", {"font-size": "16px"})
assert-css: ("h5#sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"font-size": "14px"})
assert-css: ("h6#sub-sub-heading-for-struct-impl-doc", {"border-bottom-width": "0px"})
assert-css: ("h5#title-for-struct-impl-item-doc", {"font-size": "16px"})
assert-css: ("h5#title-for-struct-impl-item-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"font-size": "14px"})
assert-css: ("h6#sub-heading-for-struct-impl-item-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-sub-heading-for-struct-impl-item-doc", {"font-size": "14px"})
goto: file://|DOC_PATH|/test_docs/enum.HeavilyDocumentedEnum.html
assert-css: ("h1.fqn", {"font-size": "24px"})
-assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"})
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
-assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
-assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "17.6px"})
+assert-css: ("h4#top-doc-prose-sub-sub-heading", {"font-size": "18px"})
assert-css: ("h4#top-doc-prose-sub-sub-heading", {"border-bottom-width": "1px"})
-assert-css: ("h2#variants", {"font-size": "22.4px"})
+assert-css: ("h2#variants", {"font-size": "22px"})
assert-css: ("h2#variants", {"border-bottom-width": "1px"})
assert-css: ("h4#none-prose-title", {"font-size": "16px"})
assert-css: ("h5#wrapped0-prose-title", {"font-size": "16px"})
assert-css: ("h5#wrapped0-prose-title", {"border-bottom-width": "0px"})
-assert-css: ("h6#wrapped0-prose-sub-heading", {"font-size": "15.2px"})
+assert-css: ("h6#wrapped0-prose-sub-heading", {"font-size": "14px"})
assert-css: ("h6#wrapped0-prose-sub-heading", {"border-bottom-width": "0px"})
assert-css: ("h5#structy-prose-title", {"font-size": "16px"})
assert-css: ("h5#structy-prose-title", {"border-bottom-width": "0px"})
-assert-css: ("h6#structy-prose-sub-heading", {"font-size": "15.2px"})
+assert-css: ("h6#structy-prose-sub-heading", {"font-size": "14px"})
assert-css: ("h6#structy-prose-sub-heading", {"border-bottom-width": "0px"})
-assert-css: ("h2#implementations", {"font-size": "22.4px"})
+assert-css: ("h2#implementations", {"font-size": "22px"})
assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
-assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"})
+assert-css: ("#impl > h3.code-header", {"font-size": "18px"})
assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"})
assert-css: ("#method\.do_nothing > h4.code-header", {"font-size": "16px"})
assert-css: ("#method\.do_nothing > h4.code-header", {"border-bottom-width": "0px"})
assert-css: ("h4#title-for-enum-impl-doc", {"border-bottom-width": "0px"})
assert-css: ("h5#sub-heading-for-enum-impl-doc", {"font-size": "16px"})
assert-css: ("h5#sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"font-size": "14px"})
assert-css: ("h6#sub-sub-heading-for-enum-impl-doc", {"border-bottom-width": "0px"})
assert-css: ("h5#title-for-enum-impl-item-doc", {"font-size": "16px"})
assert-css: ("h5#title-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"font-size": "14px"})
assert-css: ("h6#sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"font-size": "14px"})
assert-css: ("h6#sub-sub-heading-for-enum-impl-item-doc", {"border-bottom-width": "0px"})
assert-text: (".sidebar .others h3", "Modules")
assert-css: ("h1.fqn", {"font-size": "24px"})
-assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"})
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
-assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
-assert-css: ("h2#fields", {"font-size": "22.4px"})
+assert-css: ("h2#fields", {"font-size": "22px"})
assert-css: ("h2#fields", {"border-bottom-width": "1px"})
-assert-css: ("h3#title-for-union-variant", {"font-size": "20.8px"})
+assert-css: ("h3#title-for-union-variant", {"font-size": "20px"})
assert-css: ("h3#title-for-union-variant", {"border-bottom-width": "0px"})
assert-css: ("h4#sub-heading-for-union-variant", {"font-size": "16px"})
assert-css: ("h4#sub-heading-for-union-variant", {"border-bottom-width": "0px"})
-assert-css: ("h2#implementations", {"font-size": "22.4px"})
+assert-css: ("h2#implementations", {"font-size": "22px"})
assert-css: ("h2#implementations", {"border-bottom-width": "1px"})
-assert-css: ("#impl > h3.code-header", {"font-size": "17.6px"})
+assert-css: ("#impl > h3.code-header", {"font-size": "18px"})
assert-css: ("#impl > h3.code-header", {"border-bottom-width": "0px"})
assert-css: ("h4#title-for-union-impl-doc", {"font-size": "16px"})
assert-css: ("h4#title-for-union-impl-doc", {"border-bottom-width": "0px"})
assert-css: ("h5#title-for-union-impl-item-doc", {"font-size": "16px"})
assert-css: ("h5#title-for-union-impl-item-doc", {"border-bottom-width": "0px"})
-assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"font-size": "15.2px"})
+assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"font-size": "14px"})
assert-css: ("h6#sub-heading-for-union-impl-item-doc", {"border-bottom-width": "0px"})
goto: file://|DOC_PATH|/test_docs/macro.heavily_documented_macro.html
assert-css: ("h1.fqn", {"font-size": "24px"})
-assert-css: ("h2#top-doc-prose-title", {"font-size": "20.8px"})
+assert-css: ("h2#top-doc-prose-title", {"font-size": "22px"})
assert-css: ("h2#top-doc-prose-title", {"border-bottom-width": "1px"})
-assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "18.4px"})
+assert-css: ("h3#top-doc-prose-sub-heading", {"font-size": "20px"})
assert-css: ("h3#top-doc-prose-sub-heading", {"border-bottom-width": "1px"})
goto: file://|DOC_PATH|/staged_api/struct.Foo.html
assert-attribute: ("#implementors-list .impl:nth-child(2)", {"id": "impl-Whatever-1"})
assert-attribute: ("#implementors-list .impl:nth-child(2) > a.anchor", {"href": "#impl-Whatever-1"})
assert: "#implementors-list .impl:nth-child(2) > .code-header.in-band"
+
+goto: file://|DOC_PATH|/test_docs/struct.HasEmptyTraits.html
+compare-elements-position-near-false: ("#impl-EmptyTrait1", "#impl-EmptyTrait2", {"y": 30})
+compare-elements-position-near: ("#impl-EmptyTrait3 h3", "#impl-EmptyTrait3 .item-info", {"y": 30})
// We set a fixed size so there is no chance of "random" resize.
size: (1100, 800)
// We check that ".item-info" is bigger than its content.
-assert-css: (".item-info", {"width": "757px"})
-assert-css: (".item-info .stab", {"width": "341px"})
+assert-css: (".item-info", {"width": "790px"})
+assert-css: (".item-info .stab", {"width": "340px"})
+assert-position: (".item-info .stab", {"x": 295})
"flex-direction": "column"
})
-assert-property: (".mobile-topbar h2.location", {"offsetHeight": 45})
+assert-property: (".mobile-topbar h2.location", {"offsetHeight": 36})
// Note: We can't use assert-text here because the 'Since' is set by CSS and
// is therefore not part of the DOM.
click: "#crate-search"
// We select "lib2" option then press enter to change the filter.
press-key: "ArrowDown"
+press-key: "ArrowDown"
press-key: "Enter"
// Waiting for the search results to appear...
wait-for: "#titles"
// We check that there is no more "test_docs" appearing.
assert-false: "#results .externcrate"
+// We also check that "lib2" is the filter crate.
+assert-property: ("#crate-search", {"value": "lib2"})
+
+// Now we check that leaving the search results and putting them back keeps the
+// crate filtering.
+press-key: "Escape"
+wait-for: 100
+assert-css: ("#main-content", {"display": "block"})
+focus: ".search-input"
+wait-for: 100
+assert-css: ("#main-content", {"display": "none"})
+// We check that there is no more "test_docs" appearing.
+assert-false: "#results .externcrate"
+assert-property: ("#crate-search", {"value": "lib2"})
+
+// Selecting back "All crates"
+click: "#crate-search"
+press-key: "ArrowUp"
+press-key: "ArrowUp"
+press-key: "Enter"
+// Waiting for the search results to appear...
+wait-for: "#titles"
+assert-property: ("#crate-search", {"value": "All crates"})
+
+// Checking that the URL parameter is taken into account for crate filtering.
+goto: file://|DOC_PATH|/test_docs/index.html?search=test&filter-crate=lib2
+wait-for: "#crate-search"
+assert-property: ("#crate-search", {"value": "lib2"})
+assert-false: "#results .externcrate"
// Check that clicking an element from the sidebar scrolls to the right place
// so the target is not obscured by the topbar.
click: ".sidebar-menu-toggle"
-click: ".sidebar-links a"
+click: ".sidebar-elems section .block li > a"
assert-position: ("#method\.must_use", {"y": 45})
// Check that the bottom-most item on the sidebar menu can be scrolled fully into view.
click: ".sidebar-menu-toggle"
scroll-to: ".block.keyword li:nth-child(1)"
-assert-position: (".block.keyword li:nth-child(1)", {"y": 542.96875})
+compare-elements-position-near: (".block.keyword li:nth-child(1)", ".mobile-topbar", {"y": 543})
// We wait for the sidebar to be expanded (there is a 0.5s animation).
wait-for: 600
assert-css: ("nav.sidebar.expanded", {"width": "300px"})
-assert-css: ("nav.sidebar.expanded a", {"font-size": "14.4px"})
+assert-css: ("nav.sidebar.expanded a", {"font-size": "14px"})
// We collapse the sidebar.
click: (10, 10)
// We wait for the sidebar to be collapsed (there is a 0.5s animation).
// We check that we have the crates list and that the "current" on is "test_docs".
assert-text: (".sidebar-elems .crate > ul > li > a.current", "test_docs")
// And we're also supposed to have the list of items in the current module.
-assert-text: (".sidebar-elems .items > ul > li:nth-child(1)", "Modules")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(2)", "Macros")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(3)", "Structs")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(4)", "Enums")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(5)", "Traits")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(6)", "Functions")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(7)", "Type Definitions")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(8)", "Unions")
-assert-text: (".sidebar-elems .items > ul > li:nth-child(9)", "Keywords")
+assert-text: (".sidebar-elems section ul > li:nth-child(1)", "Modules")
+assert-text: (".sidebar-elems section ul > li:nth-child(2)", "Macros")
+assert-text: (".sidebar-elems section ul > li:nth-child(3)", "Structs")
+assert-text: (".sidebar-elems section ul > li:nth-child(4)", "Enums")
+assert-text: (".sidebar-elems section ul > li:nth-child(5)", "Traits")
+assert-text: (".sidebar-elems section ul > li:nth-child(6)", "Functions")
+assert-text: (".sidebar-elems section ul > li:nth-child(7)", "Type Definitions")
+assert-text: (".sidebar-elems section ul > li:nth-child(8)", "Unions")
+assert-text: (".sidebar-elems section ul > li:nth-child(9)", "Keywords")
assert-text: ("#structs + .item-table .item-left > a", "Foo")
click: "#structs + .item-table .item-left > a"
// We check that there is no crate listed outside of the top level.
assert-false: ".sidebar-elems > .crate"
-click: ".sidebar-links a"
+click: ".sidebar-elems section .block li > a"
assert-property-false: ("html", {"scrollTop": "0"})
click: ".sidebar h2.location a"
// We check that we have the crates list and that the "current" on is now "lib2".
assert-text: (".sidebar-elems .crate > ul > li > a.current", "lib2")
// We now go to the "foobar" function page.
-assert-text: (".sidebar-elems > .items > ul > li:nth-child(1)", "Modules")
-assert-text: (".sidebar-elems > .items > ul > li:nth-child(2)", "Structs")
-assert-text: (".sidebar-elems > .items > ul > li:nth-child(3)", "Traits")
-assert-text: (".sidebar-elems > .items > ul > li:nth-child(4)", "Functions")
-assert-text: (".sidebar-elems > .items > ul > li:nth-child(5)", "Type Definitions")
+assert-text: (".sidebar-elems > section .block ul > li:nth-child(1)", "Modules")
+assert-text: (".sidebar-elems > section .block ul > li:nth-child(2)", "Structs")
+assert-text: (".sidebar-elems > section .block ul > li:nth-child(3)", "Traits")
+assert-text: (".sidebar-elems > section .block ul > li:nth-child(4)", "Functions")
+assert-text: (".sidebar-elems > section .block ul > li:nth-child(5)", "Type Definitions")
assert-text: ("#functions + .item-table .item-left > a", "foobar")
click: "#functions + .item-table .item-left > a"
assert-text: (".sidebar > .location", "Module sub_sub_module")
// We check that we don't have the crate list.
assert-false: ".sidebar-elems .crate"
-assert-text: (".sidebar-elems .items > ul > li:nth-child(1)", "Functions")
+assert-text: (".sidebar-elems > section ul > li:nth-child(1)", "Functions")
assert-text: ("#functions + .item-table .item-left > a", "foo")
// Links to trait implementations in the sidebar should not wrap even if they are long.
goto: file://|DOC_PATH|/lib2/struct.HasALongTraitWithParams.html
-assert-property: (".sidebar-links a", {"offsetHeight": 29})
+assert-property: (".sidebar-elems section .block li > a", {"offsetHeight": 29})
+
+// Test that clicking on of the "In <module>" headings in the sidebar links to the
+// appropriate anchor in index.html.
+goto: file://|DOC_PATH|/test_docs/struct.Foo.html
+click: ".block.mod h3 a"
+// PAGE: index.html
+assert-css: ("#modules", {"background-color": "rgb(253, 255, 211)"})
goto: file://|DOC_PATH|/test_docs/struct.Foo.html
show-text: true
// Check the impl headers.
-assert-css: (".impl.has-srclink .srclink", {"font-size": "17px"}, ALL)
-// The ".6" part is because the font-size is actually "1.1em".
-assert-css: (".impl.has-srclink .code-header.in-band", {"font-size": "17.6px"}, ALL)
+assert-css: (".impl.has-srclink .srclink", {"font-size": "16px"}, ALL)
+assert-css: (".impl.has-srclink .code-header.in-band", {"font-size": "18px"}, ALL)
// Check the impl items.
assert-css: (".impl-items .has-srclink .srclink", {"font-size": "16px"}, ALL)
assert-css: (".impl-items .has-srclink .code-header", {"font-size": "16px"}, ALL)
[lib]
path = "lib.rs"
+
+[features]
+default = ["some-feature"]
+some-feature = []
macro_rules! heavily_documented_macro {
() => {};
}
+
+pub trait EmptyTrait1 {}
+pub trait EmptyTrait2 {}
+pub trait EmptyTrait3 {}
+
+pub struct HasEmptyTraits{}
+
+impl EmptyTrait1 for HasEmptyTraits {}
+impl EmptyTrait2 for HasEmptyTraits {}
+#[doc(cfg(feature = "some-feature"))]
+impl EmptyTrait3 for HasEmptyTraits {}
// Checks that the elements in the sidebar are alphabetically sorted.
goto: file://|DOC_PATH|/test_docs/trait.AnotherOne.html
-assert-text: (".sidebar-links a:nth-of-type(1)", "another")
-assert-text: (".sidebar-links a:nth-of-type(2)", "func1")
-assert-text: (".sidebar-links a:nth-of-type(3)", "func2")
-assert-text: (".sidebar-links a:nth-of-type(4)", "func3")
-assert-text: (".sidebar-links a:nth-of-type(5)", "hello")
-assert-text: (".sidebar-links a:nth-of-type(6)", "why_not")
+assert-text: (".sidebar-elems section .block li:nth-of-type(1) > a", "another")
+assert-text: (".sidebar-elems section .block li:nth-of-type(2) > a", "func1")
+assert-text: (".sidebar-elems section .block li:nth-of-type(3) > a", "func2")
+assert-text: (".sidebar-elems section .block li:nth-of-type(4) > a", "func3")
+assert-text: (".sidebar-elems section .block li:nth-of-type(5) > a", "hello")
+assert-text: (".sidebar-elems section .block li:nth-of-type(6) > a", "why_not")
// On mobile:
size: (600, 600)
goto: file://|DOC_PATH|/lib2/too_long/struct.SuperIncrediblyLongLongLongLongLongLongLongGigaGigaGigaMegaLongLongLongStructName.html
-assert-property: (".mobile-topbar .location", {"scrollWidth": "986"})
-assert-property: (".mobile-topbar .location", {"clientWidth": "504"})
+// It shouldn't have an overflow in the topbar either.
+assert-property: (".mobile-topbar .location", {"scrollWidth": "492"})
+assert-property: (".mobile-topbar .location", {"clientWidth": "492"})
assert-css: (".mobile-topbar .location", {"overflow-x": "hidden"})
+++ /dev/null
-const QUERY = 'str,u8';
-
-const EXPECTED = {
- 'others': [
- { 'path': 'std', 'name': 'str', 'href': '../std/primitive.str.html' },
- { 'path': 'std', 'name': 'u8', 'href': '../std/primitive.u8.html' },
- { 'path': 'std', 'name': 'str', 'href': '../std/str/index.html' },
- { 'path': 'std', 'name': 'u8', 'href': '../std/u8/index.html' },
- ],
-};
{ 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'println' },
{ 'path': 'std', 'name': 'eprintln' },
+ { 'path': 'std::pin', 'name': 'pin' },
+ { 'path': 'core::pin', 'name': 'pin' },
],
};
--- /dev/null
+// ignore-tidy-linelength
+
+// @count dyn.json "$.index[*][?(@.name=='dyn')].inner.items" 1
+// @set sync_int_gen = - "$.index[*][?(@.name=='SyncIntGen')].id"
+// @is - "$.index[*][?(@.name=='dyn')].inner.items[0]" $sync_int_gen
+
+// @is - "$.index[*][?(@.name=='SyncIntGen')].kind" \"typedef\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.generics" '{"params": [], "where_predicates": []}'
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.kind" \"resolved_path\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.name" \"Box\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.bindings" []
+// @count - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args" 1
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"resolved_path\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"resolved_path\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.name" \"Fn\"
+// @count - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[*]" 3
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[0].trait_bound.trait.inner.name" \"Send\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[1].trait_bound.trait.inner.name" \"Sync\"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.param_names[2]" "{\"outlives\": \"'static\"}"
+// @is - "$.index[*][?(@.name=='SyncIntGen')].inner.type.inner.args.angle_bracketed.args[0].type.inner.args" '{"parenthesized": {"inputs": [],"output": {"inner": "i32","kind": "primitive"}}}'
+pub type SyncIntGen = Box<dyn Fn() -> i32 + Send + Sync + 'static>;
--- /dev/null
+// ignore-tidy-linelength
+
+// @is fn_lifetime.json "$.index[*][?(@.name=='GenericFn')].kind" \"typedef\"
+
+// @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*]" 1
+// @is - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].name" \"\'a\"
+// @has - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime"
+// @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.params[*].kind.lifetime.outlives[*]" 0
+// @count - "$.index[*][?(@.name=='GenericFn')].inner.generics.where_predicates[*]" 0
+// @is - "$.index[*][?(@.name=='GenericFn')].inner.type.kind" \"function_pointer\"
+// @count - "$.index[*][?(@.name=='GenericFn')].inner.type.inner.generic_params[*]" 0
+// @count - "$.index[*][?(@.name=='GenericFn')].inner.type.inner.decl.inputs[*]" 1
+// @is - "$.index[*][?(@.name=='GenericFn')].inner.type.inner.decl.inputs[*][1].inner.lifetime" \"\'a\"
+// @is - "$.index[*][?(@.name=='GenericFn')].inner.type.inner.decl.output.inner.lifetime" \"\'a\"
+
+pub type GenericFn<'a> = fn(&'a i32) -> &'a i32;
+
+// @is fn_lifetime.json "$.index[*][?(@.name=='ForAll')].kind" \"typedef\"
+// @count - "$.index[*][?(@.name=='ForAll')].inner.generics.params[*]" 0
+// @count - "$.index[*][?(@.name=='ForAll')].inner.generics.where_predicates[*]" 0
+// @count - "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*]" 1
+// @is - "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*].name" \"\'a\"
+// @has - "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*].kind.lifetime"
+// @count - "$.index[*][?(@.name=='ForAll')].inner.type.inner.generic_params[*].kind.lifetime.outlives[*]" 0
+// @count - "$.index[*][?(@.name=='ForAll')].inner.type.inner.decl.inputs[*]" 1
+// @is - "$.index[*][?(@.name=='ForAll')].inner.type.inner.decl.inputs[*][1].inner.lifetime" \"\'a\"
+// @is - "$.index[*][?(@.name=='ForAll')].inner.type.inner.decl.output.inner.lifetime" \"\'a\"
+pub type ForAll = for<'a> fn(&'a i32) -> &'a i32;
--- /dev/null
+// ignore-tidy-linelength
+
+// @set result = generic_default.json "$.index[*][?(@.name=='Result')].id"
+pub enum Result<T, E> {
+ Ok(T),
+ Err(E),
+}
+
+// @set my_error = - "$.index[*][?(@.name=='MyError')].id"
+pub struct MyError {}
+
+// @is - "$.index[*][?(@.name=='MyResult')].kind" \"typedef\"
+// @count - "$.index[*][?(@.name=='MyResult')].inner.generics.where_predicates[*]" 0
+// @count - "$.index[*][?(@.name=='MyResult')].inner.generics.params[*]" 2
+// @is - "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].name" \"T\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].name" \"E\"
+// @has - "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].kind.type"
+// @has - "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type"
+// @count - "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].kind.type.bounds[*]" 0
+// @count - "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.bounds[*]" 0
+// @is - "$.index[*][?(@.name=='MyResult')].inner.generics.params[0].kind.type.default" null
+// @is - "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.default.kind" \"resolved_path\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.default.inner.id" $my_error
+// @is - "$.index[*][?(@.name=='MyResult')].inner.generics.params[1].kind.type.default.inner.name" \"MyError\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.kind" \"resolved_path\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.id" $result
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.name" \"Result\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.bindings" []
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[0].type.kind" \"generic\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[1].type.kind" \"generic\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[0].type.inner" \"T\"
+// @is - "$.index[*][?(@.name=='MyResult')].inner.type.inner.args.angle_bracketed.args[1].type.inner" \"E\"
+pub type MyResult<T, E = MyError> = Result<T, E>;
--- /dev/null
+// check-pass
+// compile-flags:--test
+
+// This test ensures that no code block is detected in the doc comments.
+
+pub mod Wormhole {
+ /** # Returns
+ *
+ */
+ pub fn foofoo() {}
+ /**
+ * # Returns
+ *
+ */
+ pub fn barbar() {}
+}
--- /dev/null
+
+running 0 tests
+
+test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
+
/// ```
pub fn foobar() {}
-/// barfoo
-///
-/// ```allow-fail,allowfail,allOw_fail
-/// boo
-/// ```
-pub fn barfoo() {}
-
/// b
///
/// ```test-harness,testharness,tesT_harness
|
= help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
-error: unknown attribute `allow-fail`. Did you mean `allow_fail`?
+error: unknown attribute `test-harness`. Did you mean `test_harness`?
--> $DIR/check-attr-test.rs:26:1
|
-26 | / /// barfoo
+26 | / /// b
27 | | ///
-28 | | /// ```allow-fail,allowfail,allOw_fail
+28 | | /// ```test-harness,testharness,tesT_harness
29 | | /// boo
30 | | /// ```
| |_______^
|
- = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
+ = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
-error: unknown attribute `allowfail`. Did you mean `allow_fail`?
+error: unknown attribute `testharness`. Did you mean `test_harness`?
--> $DIR/check-attr-test.rs:26:1
|
-26 | / /// barfoo
+26 | / /// b
27 | | ///
-28 | | /// ```allow-fail,allowfail,allOw_fail
+28 | | /// ```test-harness,testharness,tesT_harness
29 | | /// boo
30 | | /// ```
| |_______^
|
- = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
+ = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
-error: unknown attribute `allOw_fail`. Did you mean `allow_fail`?
+error: unknown attribute `tesT_harness`. Did you mean `test_harness`?
--> $DIR/check-attr-test.rs:26:1
|
-26 | / /// barfoo
+26 | / /// b
27 | | ///
-28 | | /// ```allow-fail,allowfail,allOw_fail
+28 | | /// ```test-harness,testharness,tesT_harness
29 | | /// boo
30 | | /// ```
- | |_______^
- |
- = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
-
-error: unknown attribute `test-harness`. Did you mean `test_harness`?
- --> $DIR/check-attr-test.rs:33:1
- |
-33 | / /// b
-34 | | ///
-35 | | /// ```test-harness,testharness,tesT_harness
-36 | | /// boo
-37 | | /// ```
- | |_______^
- |
- = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
-
-error: unknown attribute `testharness`. Did you mean `test_harness`?
- --> $DIR/check-attr-test.rs:33:1
- |
-33 | / /// b
-34 | | ///
-35 | | /// ```test-harness,testharness,tesT_harness
-36 | | /// boo
-37 | | /// ```
- | |_______^
- |
- = help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
-
-error: unknown attribute `tesT_harness`. Did you mean `test_harness`?
- --> $DIR/check-attr-test.rs:33:1
- |
-33 | / /// b
-34 | | ///
-35 | | /// ```test-harness,testharness,tesT_harness
-36 | | /// boo
-37 | | /// ```
| |_______^
|
= help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
-error: aborting due to 15 previous errors
+error: aborting due to 12 previous errors
/// ```
pub fn foobar() {}
-/// barfoo
-//~^ ERROR
-//~^^ ERROR
-//~^^^ ERROR
-///
-/// ```allow-fail,allowfail,alLow_fail
-/// boo
-/// ```
-pub fn barfoo() {}
-
/// b
//~^ ERROR
//~^^ ERROR
|
= help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
-error: unknown attribute `allow-fail`. Did you mean `allow_fail`?
- --> $DIR/check-attr.rs:33:1
- |
-LL | / /// barfoo
-LL | |
-LL | |
-LL | |
-... |
-LL | | /// boo
-LL | | /// ```
- | |_______^
- |
- = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
-
-error: unknown attribute `allowfail`. Did you mean `allow_fail`?
- --> $DIR/check-attr.rs:33:1
- |
-LL | / /// barfoo
-LL | |
-LL | |
-LL | |
-... |
-LL | | /// boo
-LL | | /// ```
- | |_______^
- |
- = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
-
-error: unknown attribute `alLow_fail`. Did you mean `allow_fail`?
- --> $DIR/check-attr.rs:33:1
- |
-LL | / /// barfoo
-LL | |
-LL | |
-LL | |
-... |
-LL | | /// boo
-LL | | /// ```
- | |_______^
- |
- = help: the code block will either not be tested if not marked as a rust one or will be run (which you might not want)
-
error: unknown attribute `test-harness`. Did you mean `test_harness`?
- --> $DIR/check-attr.rs:43:1
+ --> $DIR/check-attr.rs:33:1
|
LL | / /// b
LL | |
= help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
error: unknown attribute `testharness`. Did you mean `test_harness`?
- --> $DIR/check-attr.rs:43:1
+ --> $DIR/check-attr.rs:33:1
|
LL | / /// b
LL | |
= help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
error: unknown attribute `teSt_harness`. Did you mean `test_harness`?
- --> $DIR/check-attr.rs:43:1
+ --> $DIR/check-attr.rs:33:1
|
LL | / /// b
LL | |
|
= help: the code block will either not be tested if not marked as a rust one or the code will be wrapped inside a main function
-error: aborting due to 15 previous errors
+error: aborting due to 12 previous errors
--- /dev/null
+// check-pass
+#![allow(rustdoc::private_intra_doc_links)]
+
+macro_rules! foo {
+ () => {};
+}
+
+/// [foo!]
+pub fn baz() {}
--- /dev/null
+#![deny(rustdoc::invalid_html_tags)]
+
+/// This Vec<32> thing!
+// Numbers aren't valid HTML tags, so no error.
+pub struct ConstGeneric;
+
+/// This Vec<i32, i32> thing!
+// HTML tags cannot contain commas, so no error.
+pub struct MultipleGenerics;
+
+/// This Vec<i32 class="test"> thing!
+//~^ERROR unclosed HTML tag `i32`
+// HTML attributes shouldn't be treated as Rust syntax, so no suggestions.
+pub struct TagWithAttributes;
+
+/// This Vec<i32></i32> thing!
+// There should be no error, and no suggestion, since the tags are balanced.
+pub struct DoNotWarnOnMatchingTags;
+
+/// This Vec</i32> thing!
+//~^ERROR unopened HTML tag `i32`
+// This should produce an error, but no suggestion.
+pub struct EndTagsAreNotValidRustSyntax;
+
+/// This 123<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+// This should produce an error, but no suggestion.
+pub struct NumbersAreNotPaths;
+
+/// This Vec:<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+// This should produce an error, but no suggestion.
+pub struct InvalidTurbofish;
+
+/// This [link](https://rust-lang.org)<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+// This should produce an error, but no suggestion.
+pub struct BareTurbofish;
--- /dev/null
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics-no-suggestions.rs:11:13
+ |
+LL | /// This Vec<i32 class="test"> thing!
+ | ^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/html-as-generics-no-suggestions.rs:1:9
+ |
+LL | #![deny(rustdoc::invalid_html_tags)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: unopened HTML tag `i32`
+ --> $DIR/html-as-generics-no-suggestions.rs:20:13
+ |
+LL | /// This Vec</i32> thing!
+ | ^^^^^^
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics-no-suggestions.rs:25:13
+ |
+LL | /// This 123<i32> thing!
+ | ^^^^^
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics-no-suggestions.rs:30:14
+ |
+LL | /// This Vec:<i32> thing!
+ | ^^^^^
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics-no-suggestions.rs:35:39
+ |
+LL | /// This [link](https://rust-lang.org)<i32> thing!
+ | ^^^^^
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+// run-rustfix
+#![deny(rustdoc::invalid_html_tags)]
+
+/// This `Vec<i32>` thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct Generic;
+
+/// This `vec::Vec<i32>` thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct GenericPath;
+
+/// This `i32<i32>` thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct PathsCanContainTrailingNumbers;
+
+/// This `Vec::<i32>` thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct Turbofish;
+
+/// This [link](https://rust-lang.org)`::<i32>` thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct BareTurbofish;
+
+/// This <span>`Vec::<i32>`</span> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct Nested;
--- /dev/null
+// run-rustfix
+#![deny(rustdoc::invalid_html_tags)]
+
+/// This Vec<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct Generic;
+
+/// This vec::Vec<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct GenericPath;
+
+/// This i32<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct PathsCanContainTrailingNumbers;
+
+/// This Vec::<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct Turbofish;
+
+/// This [link](https://rust-lang.org)::<i32> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct BareTurbofish;
+
+/// This <span>Vec::<i32></span> thing!
+//~^ERROR unclosed HTML tag `i32`
+//~|HELP try marking as source
+pub struct Nested;
--- /dev/null
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics.rs:4:13
+ |
+LL | /// This Vec<i32> thing!
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/html-as-generics.rs:2:9
+ |
+LL | #![deny(rustdoc::invalid_html_tags)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try marking as source code
+ |
+LL | /// This `Vec<i32>` thing!
+ | + +
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics.rs:9:18
+ |
+LL | /// This vec::Vec<i32> thing!
+ | ^^^^^
+ |
+help: try marking as source code
+ |
+LL | /// This `vec::Vec<i32>` thing!
+ | + +
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics.rs:14:13
+ |
+LL | /// This i32<i32> thing!
+ | ^^^^^
+ |
+help: try marking as source code
+ |
+LL | /// This `i32<i32>` thing!
+ | + +
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics.rs:19:15
+ |
+LL | /// This Vec::<i32> thing!
+ | ^^^^^
+ |
+help: try marking as source code
+ |
+LL | /// This `Vec::<i32>` thing!
+ | + +
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics.rs:24:41
+ |
+LL | /// This [link](https://rust-lang.org)::<i32> thing!
+ | ^^^^^
+ |
+help: try marking as source code
+ |
+LL | /// This [link](https://rust-lang.org)`::<i32>` thing!
+ | + +
+
+error: unclosed HTML tag `i32`
+ --> $DIR/html-as-generics.rs:29:21
+ |
+LL | /// This <span>Vec::<i32></span> thing!
+ | ^^^^^
+ |
+help: try marking as source code
+ |
+LL | /// This <span>`Vec::<i32>`</span> thing!
+ | + +
+
+error: aborting due to 6 previous errors
+
// @has 'foo/struct.Bar.html'
// @has - '//h3[@class="sidebar-title"]' 'Associated Constants'
-// @has - '//div[@class="sidebar-elems"]//div[@class="sidebar-links"]/a' 'FOO'
+// @has - '//div[@class="sidebar-elems"]//a' 'FOO'
impl Trait for Bar {
const FOO: u32 = 1;
// @has 'foo/enum.Foo.html'
// @has - '//h3[@class="sidebar-title"]' 'Associated Constants'
-// @has - '//div[@class="sidebar-elems"]//div[@class="sidebar-links"]/a' 'FOO'
+// @has - '//div[@class="sidebar-elems"]//a' 'FOO'
impl Trait for Foo {
const FOO: u32 = 1;
impl Foo {
// @has async_fn/struct.Foo.html
- // @has - '//div[@class="method has-srclink"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator<Item = &usize>'
+ // @has - '//*[@class="method has-srclink"]' 'pub async fn complicated_lifetimes( &self, context: &impl Bar) -> impl Iterator<Item = &usize>'
pub async fn complicated_lifetimes(&self, context: &impl Bar) -> impl Iterator<Item = &usize> {}
// taken from `tokio` as an example of a method that was particularly bad before
- // @has - '//div[@class="method has-srclink"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>"
+ // @has - '//*[@class="method has-srclink"]' "pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()>"
pub async fn readable<T>(&self) -> Result<AsyncFdReadyGuard<'_, T>, ()> {}
- // @has - '//div[@class="method has-srclink"]' "pub async fn mut_self(&mut self)"
+ // @has - '//*[@class="method has-srclink"]' "pub async fn mut_self(&mut self)"
pub async fn mut_self(&mut self) {}
}
#![feature(auto_traits)]
-// @has auto_aliases/trait.Bar.html '//div[@data-aliases="auto_aliases::Foo"]' 'impl Bar for Foo'
+// @has auto_aliases/trait.Bar.html '//*[@data-aliases="auto_aliases::Foo"]' 'impl Bar for Foo'
pub struct Foo;
pub auto trait Bar {}
#![crate_name = "foo"]
-// @has foo/struct.S.html '//div[@id="impl-Into%3CU%3E"]//h3[@class="code-header in-band"]' 'impl<T, U> Into<U> for T'
+// @has foo/struct.S.html '//*[@id="impl-Into%3CU%3E"]//h3[@class="code-header in-band"]' 'impl<T, U> Into<U> for T'
pub struct S2 {}
mod m {
pub struct S {}
pub struct Foo;
impl Foo {
- // @has 'foo/struct.Foo.html' '//div[@id="method.gated"]/h4[@class="code-header"]' 'pub fn gated() -> u32'
+ // @has 'foo/struct.Foo.html' '//*[@id="method.gated"]/h4[@class="code-header"]' 'pub fn gated() -> u32'
// @has - '//span[@class="since"]' '1.0.0 (const: unstable)'
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature="foo", issue = "none")]
pub const fn gated() -> u32 { 42 }
- // @has 'foo/struct.Foo.html' '//div[@id="method.gated_unsafe"]/h4[@class="code-header"]' 'pub unsafe fn gated_unsafe() -> u32'
+ // @has 'foo/struct.Foo.html' '//*[@id="method.gated_unsafe"]/h4[@class="code-header"]' 'pub unsafe fn gated_unsafe() -> u32'
// @has - '//span[@class="since"]' '1.0.0 (const: unstable)'
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature="foo", issue = "none")]
pub const unsafe fn gated_unsafe() -> u32 { 42 }
- // @has 'foo/struct.Foo.html' '//div[@id="method.stable_impl"]/h4[@class="code-header"]' 'pub const fn stable_impl() -> u32'
+ // @has 'foo/struct.Foo.html' '//*[@id="method.stable_impl"]/h4[@class="code-header"]' 'pub const fn stable_impl() -> u32'
// @has - '//span[@class="since"]' '1.0.0 (const: 1.2.0)'
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_stable(feature = "rust1", since = "1.2.0")]
inner: T,
}
-// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//div/h3[@class="code-header in-band"]' 'impl Add<Simd<u8, 16_usize>> for Simd<u8, 16>'
+// @has foo/struct.Simd.html '//div[@id="trait-implementations-list"]//h3[@class="code-header in-band"]' 'impl Add<Simd<u8, 16_usize>> for Simd<u8, 16>'
impl Add for Simd<u8, 16> {
type Output = Self;
}
// @has foo/trait.Array.html
-// @has - '//div[@class="impl has-srclink"]' 'impl<T, const N: usize> Array for [T; N]'
-impl <T, const N: usize> Array for [T; N] {
+// @has - '//*[@class="impl has-srclink"]' 'impl<T, const N: usize> Array for [T; N]'
+impl<T, const N: usize> Array for [T; N] {
type Item = T;
}
// @has foo/struct.Bar.html '//pre[@class="rust struct"]' 'pub struct Bar<T, const N: usize>(_)'
pub struct Bar<T, const N: usize>([T; N]);
-// @has foo/struct.Foo.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M> where u8: Trait<M>'
+// @has foo/struct.Foo.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Foo<M> where u8: Trait<M>'
impl<const M: usize> Foo<M> where u8: Trait<M> {
// @has - '//*[@id="associatedconstant.FOO_ASSOC"]' 'pub const FOO_ASSOC: usize'
pub const FOO_ASSOC: usize = M + 13;
}
}
-// @has foo/struct.Bar.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Bar<u8, M>'
+// @has foo/struct.Bar.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl<const M: usize> Bar<u8, M>'
impl<const M: usize> Bar<u8, M> {
// @has - '//*[@id="method.hey"]' \
// 'pub fn hey<const N: usize>(&self) -> Foo<N> where u8: Trait<N>'
#![allow(incomplete_features)]
-
#![feature(adt_const_params)]
-
#![crate_name = "foo"]
#[derive(PartialEq, Eq)]
}
// @has foo/struct.VSet.html '//pre[@class="rust struct"]' 'pub struct VSet<T, const ORDER: Order>'
-// @has foo/struct.VSet.html '//div[@id="impl-Send"]/h3[@class="code-header in-band"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>'
-// @has foo/struct.VSet.html '//div[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>'
+// @has foo/struct.VSet.html '//*[@id="impl-Send"]/h3[@class="code-header in-band"]' 'impl<T, const ORDER: Order> Send for VSet<T, ORDER>'
+// @has foo/struct.VSet.html '//*[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl<T, const ORDER: Order> Sync for VSet<T, ORDER>'
pub struct VSet<T, const ORDER: Order> {
inner: Vec<T>,
}
-// @has foo/struct.VSet.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl<T> VSet<T, { Order::Sorted }>'
+// @has foo/struct.VSet.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl<T> VSet<T, { Order::Sorted }>'
impl<T> VSet<T, { Order::Sorted }> {
pub fn new() -> Self {
Self { inner: Vec::new() }
}
}
-// @has foo/struct.VSet.html '//div[@id="impl-1"]/h3[@class="code-header in-band"]' 'impl<T> VSet<T, { Order::Unsorted }>'
+// @has foo/struct.VSet.html '//*[@id="impl-1"]/h3[@class="code-header in-band"]' 'impl<T> VSet<T, { Order::Unsorted }>'
impl<T> VSet<T, { Order::Unsorted }> {
pub fn new() -> Self {
Self { inner: Vec::new() }
pub struct Escape<const S: &'static str>;
-// @has foo/struct.Escape.html '//div[@id="impl"]/h3[@class="code-header in-band"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>'
+// @has foo/struct.Escape.html '//*[@id="impl"]/h3[@class="code-header in-band"]' 'impl Escape<r#"<script>alert("Escape");</script>"#>'
impl Escape<r#"<script>alert("Escape");</script>"#> {
pub fn f() {}
}
}
// @has foo/struct.Bar.html
-// @has - '//div[@class="sidebar-links"]/a[@href="#method.foo"]' 'foo'
+// @has - '//*[@class="sidebar-elems"]//*[@class="block"]//a[@href="#method.foo"]' 'foo'
pub struct Bar {
foo: Foo,
}
// @has '-' '//*[@id="deref-methods-Path"]' 'Methods from Deref<Target = Path>'
// @has '-' '//*[@class="impl-items"]//*[@id="method.exists"]' 'pub fn exists(&self)'
// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-PathBuf"]' 'Methods from Deref<Target=PathBuf>'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.as_path"]' 'as_path'
+// @has '-' '//*[@class="sidebar-elems"]//*[@class="block"]//a[@href="#method.as_path"]' 'as_path'
// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-Path"]' 'Methods from Deref<Target=Path>'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.exists"]' 'exists'
+// @has '-' '//*[@class="sidebar-elems"]//*[@class="block"]//a[@href="#method.exists"]' 'exists'
#![crate_name = "foo"]
// @has '-' '//*[@id="deref-methods-Baz"]' 'Methods from Deref<Target = Baz>'
// @has '-' '//*[@class="impl-items"]//*[@id="method.baz"]' 'pub fn baz(&self)'
// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-Bar"]' 'Methods from Deref<Target=Bar>'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.bar"]' 'bar'
+// @has '-' '//*[@class="sidebar-elems"]//section//a[@href="#method.bar"]' 'bar'
// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-Baz"]' 'Methods from Deref<Target=Baz>'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.baz"]' 'baz'
+// @has '-' '//*[@class="sidebar-elems"]//section//a[@href="#method.baz"]' 'baz'
#![crate_name = "foo"]
// @has '-' '//*[@class="impl-items"]//*[@id="method.foo_c"]' 'pub fn foo_c(&self)'
// @has '-' '//*[@class="impl-items"]//*[@id="method.foo_j"]' 'pub fn foo_j(&self)'
// @has '-' '//*[@class="sidebar-title"]/a[@href="#deref-methods-FooJ"]' 'Methods from Deref<Target=FooJ>'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_a"]' 'foo_a'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_b"]' 'foo_b'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_c"]' 'foo_c'
-// @has '-' '//*[@class="sidebar-links"]/a[@href="#method.foo_j"]' 'foo_j'
+// @has '-' '//*[@class="sidebar-elems"]//section//a[@href="#method.foo_a"]' 'foo_a'
+// @has '-' '//*[@class="sidebar-elems"]//section//a[@href="#method.foo_b"]' 'foo_b'
+// @has '-' '//*[@class="sidebar-elems"]//section//a[@href="#method.foo_c"]' 'foo_c'
+// @has '-' '//*[@class="sidebar-elems"]//section//a[@href="#method.foo_j"]' 'foo_j'
pub struct FooA;
pub type FooB = FooA;
pub struct Bar;
// @has foo/struct.Bar.html
-// @has - '//*[@class="sidebar-links"]/a[@href="#impl-Foo%3Cunsafe%20extern%20%22C%22%20fn()%3E"]' 'Foo<unsafe extern "C" fn()>'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo%3Cunsafe%20extern%20%22C%22%20fn()%3E"]' 'Foo<unsafe extern "C" fn()>'
impl Foo<unsafe extern "C" fn()> for Bar {}
#![crate_name = "foo"]
// @has foo/struct.Foo.html
-// @has - '//div[@id="synthetic-implementations-list"]/div[@id="impl-Send"]' 'impl Send for Foo'
+// @has - '//div[@id="synthetic-implementations-list"]/*[@id="impl-Send"]' 'impl Send for Foo'
pub struct Foo;
pub trait EmptyTrait {}
-// @has - '//div[@id="trait-implementations-list"]/div[@id="impl-EmptyTrait"]' 'impl EmptyTrait for Foo'
+// @has - '//div[@id="trait-implementations-list"]/*[@id="impl-EmptyTrait"]' 'impl EmptyTrait for Foo'
impl EmptyTrait for Foo {}
pub trait NotEmpty {
fn foo(&self);
}
-// @has - '//div[@id="trait-implementations-list"]/details/summary/div[@id="impl-NotEmpty"]' 'impl NotEmpty for Foo'
+// @has - '//div[@id="trait-implementations-list"]/details/summary/*[@id="impl-NotEmpty"]' 'impl NotEmpty for Foo'
impl NotEmpty for Foo {
fn foo(&self) {}
}
// This test ensures that the [src] link is present on traits items.
-// @has foo/trait.Iterator.html '//div[@id="method.zip"]//a[@class="srclink"]' "source"
+// @has foo/trait.Iterator.html '//*[@id="method.zip"]//a[@class="srclink"]' "source"
pub use std::iter::Iterator;
extern crate rustdoc_extern_default_method as ext;
// @count extern_default_method/struct.Struct.html '//*[@id="method.provided"]' 1
-// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided
-// @has extern_default_method/struct.Struct.html '//div[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided
+// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="fnname"]/@href' #method.provided
+// @has extern_default_method/struct.Struct.html '//*[@id="method.provided"]//a[@class="anchor"]/@href' #method.provided
pub use ext::Struct;
use std::fmt;
-// @!has foo/struct.Bar.html '//div[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl<T> ToString for T'
+// @!has foo/struct.Bar.html '//*[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl<T> ToString for T'
pub struct Bar;
-// @has foo/struct.Foo.html '//div[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl<T> ToString for T'
+// @has foo/struct.Foo.html '//*[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl<T> ToString for T'
pub struct Foo;
-// @has foo/struct.Foo.html '//div[@class="sidebar-links"]/a[@href="#impl-ToString"]' 'ToString'
+// @has foo/struct.Foo.html '//*[@class="sidebar-elems"]//section//a[@href="#impl-ToString"]' 'ToString'
impl fmt::Display for Foo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
--- /dev/null
+#[derive(Clone)]
+pub struct PublicStruct;
+
+mod inner {
+ use super::PublicStruct;
+
+ impl PublicStruct {
+ /// [PublicStruct::clone]
+ pub fn method() {}
+ }
+}
--- /dev/null
+// no-prefer-dynamic
+
+#![feature(lang_items)]
+
+#![crate_type = "rlib"]
+#![no_std]
+
+pub struct DerefsToF64(f64);
+
+impl core::ops::Deref for DerefsToF64 {
+ type Target = f64;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+mod inner {
+ #[lang = "f64_runtime"]
+ impl f64 {
+ /// [f64::clone]
+ pub fn method() {}
+ }
+}
+
+#[lang = "eh_personality"]
+fn foo() {}
+
+#[panic_handler]
+fn bar(_: &core::panic::PanicInfo) -> ! { loop {} }
--- /dev/null
+// Reexport of a structure with public inherent impls having doc links in their comments. The doc
+// link points to an associated item, so we check that traits in scope for that link are populated.
+
+// aux-build:extern-inherent-impl-dep.rs
+
+extern crate extern_inherent_impl_dep;
+
+pub use extern_inherent_impl_dep::PublicStruct;
--- /dev/null
+// Reexport of a structure that derefs to a type with lang item impls having doc links in their
+// comments. The doc link points to an associated item, so we check that traits in scope for that
+// link are populated.
+
+// aux-build:extern-lang-item-impl-dep.rs
+
+#![no_std]
+
+extern crate extern_lang_item_impl_dep;
+
+pub use extern_lang_item_impl_dep::DerefsToF64;
fn my_string(&self) -> String;
}
-// @has - "//div[@id='implementors-list']//div[@id='impl-MyTrait']//h3[@class='code-header in-band']" "impl<T> MyTrait for T where T: Debug"
-impl<T> MyTrait for T where T: fmt::Debug {
+// @has - "//div[@id='implementors-list']//*[@id='impl-MyTrait']//h3[@class='code-header in-band']" "impl<T> MyTrait for T where T: Debug"
+impl<T> MyTrait for T
+where
+ T: fmt::Debug,
+{
fn my_string(&self) -> String {
format!("{:?}", self)
}
}
-pub fn main() {
-}
+pub fn main() {}
// Ensure constant and array length values are not taken from source
// code, which wreaks havoc with macros.
-
macro_rules! make {
($n:expr) => {
pub struct S;
}
// @has issue_33302/struct.S.html \
- // '//div[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S'
+ // '//*[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S'
// @has - '//*[@id="associatedconstant.C"]' 'const C: [i32; 16]'
// @has - '//*[@id="associatedconstant.D"]' 'const D: i32'
impl T<[i32; ($n * $n)]> for S {
}
// @has issue_33302/struct.S.html \
- // '//div[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S'
+ // '//*[@class="impl has-srclink"]' 'impl T<[i32; 16]> for S'
// @has - '//*[@id="associatedconstant.C-1"]' 'const C: (i32,)'
// @has - '//*[@id="associatedconstant.D-1"]' 'const D: i32'
impl T<(i32,)> for S {
}
// @has issue_33302/struct.S.html \
- // '//div[@class="impl has-srclink"]' 'impl T<(i32, i32)> for S'
+ // '//*[@class="impl has-srclink"]' 'impl T<(i32, i32)> for S'
// @has - '//*[@id="associatedconstant.C-2"]' 'const C: (i32, i32)'
// @has - '//*[@id="associatedconstant.D-2"]' 'const D: i32'
impl T<(i32, i32)> for S {
const C: (i32, i32) = ($n, $n);
const D: i32 = ($n / $n);
}
- }
+ };
}
make!(4);
-pub trait MyIterator {
-}
+pub trait MyIterator {}
pub struct MyStruct<T>(T);
}
// @has issue_53812/trait.MyIterator.html
-// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][1]' 'MyStruct<[T; 0]>'
-// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][2]' 'MyStruct<[T; 1]>'
-// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][3]' 'MyStruct<[T; 2]>'
-// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][4]' 'MyStruct<[T; 3]>'
-// @has - '//*[@id="implementors-list"]/div[@class="impl has-srclink"][5]' 'MyStruct<[T; 10]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][1]' 'MyStruct<[T; 0]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][2]' 'MyStruct<[T; 1]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][3]' 'MyStruct<[T; 2]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][4]' 'MyStruct<[T; 3]>'
+// @has - '//*[@id="implementors-list"]/*[@class="impl has-srclink"][5]' 'MyStruct<[T; 10]>'
array_impls! { 10 3 2 1 0 }
use issue_86620_1::*;
-// @!has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
-// @has issue_86620/struct.S.html '//div[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
+// @!has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="fnname"]/@href' #tymethod.vzip
+// @has issue_86620/struct.S.html '//*[@id="method.vzip"]//a[@class="anchor"]/@href' #method.vzip
pub struct S;
#![crate_name = "foo"]
// @has foo/struct.Foo.html
-// @has - '//*[@class="sidebar-links"]/a' 'super_long_name'
-// @has - '//*[@class="sidebar-links"]/a' 'Disp'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'super_long_name'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'Disp'
pub struct Foo(usize);
impl Foo {
// @has foo/struct.Foo.html
// @has - '//*[@class="sidebar-title"]/a[@href="#trait-implementations"]' 'Trait Implementations'
-// @has - '//*[@class="sidebar-links"]/a' '!Sync'
+// @has - '//*[@class="sidebar-elems"]//section//a' '!Sync'
impl !Sync for Foo {}
#![feature(rustdoc_internals)]
-
#![crate_name = "foo"]
-// @has foo/primitive.i32.html '//div[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl<T> ToString for T'
+// @has foo/primitive.i32.html '//*[@id="impl-ToString"]//h3[@class="code-header in-band"]' 'impl<T> ToString for T'
#[doc(primitive = "i32")]
/// Some useless docs, wouhou!
pub struct C {}
impl C { pub fn foo_c(&self) {} }
-// @has recursive_deref_sidebar/struct.A.html '//div[@class="sidebar-links"]' 'foo_b'
+// @has recursive_deref_sidebar/struct.A.html '//*[@class="sidebar-elems"]//section' 'foo_b'
impl Deref for A {
type Target = B;
fn deref(&self) -> &B { todo!() }
}
-// @has recursive_deref_sidebar/struct.A.html '//div[@class="sidebar-links"]' 'foo_c'
+// @has recursive_deref_sidebar/struct.A.html '//*[@class="sidebar-elems"]//section' 'foo_c'
impl Deref for B {
type Target = C;
fn deref(&self) -> &C { todo!() }
// @has foo/trait.Foo.html
// @has - '//*[@class="sidebar-title"]/a[@href="#required-methods"]' 'Required Methods'
-// @has - '//*[@class="sidebar-links"]/a' 'bar'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'bar'
// @has - '//*[@class="sidebar-title"]/a[@href="#provided-methods"]' 'Provided Methods'
-// @has - '//*[@class="sidebar-links"]/a' 'foo'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'foo'
// @has - '//*[@class="sidebar-title"]/a[@href="#associated-const"]' 'Associated Constants'
-// @has - '//*[@class="sidebar-links"]/a' 'BAR'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'BAR'
// @has - '//*[@class="sidebar-title"]/a[@href="#associated-types"]' 'Associated Types'
-// @has - '//*[@class="sidebar-links"]/a' 'Output'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'Output'
pub trait Foo {
const BAR: u32 = 0;
type Output: ?Sized;
// @has foo/struct.Bar.html
// @has - '//*[@class="sidebar-title"]/a[@href="#fields"]' 'Fields'
-// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f"]' 'f'
-// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.u"]' 'u'
-// @!has - '//*[@class="sidebar-links"]/a' 'waza'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#structfield.f"]' 'f'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#structfield.u"]' 'u'
+// @!has - '//*[@class="sidebar-elems"]//section//a' 'waza'
pub struct Bar {
pub f: u32,
pub u: u32,
// @has foo/enum.En.html
// @has - '//*[@class="sidebar-title"]/a[@href="#variants"]' 'Variants'
-// @has - '//*[@class="sidebar-links"]/a' 'Foo'
-// @has - '//*[@class="sidebar-links"]/a' 'Bar'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'Foo'
+// @has - '//*[@class="sidebar-elems"]//section//a' 'Bar'
pub enum En {
Foo,
Bar,
// @has foo/union.MyUnion.html
// @has - '//*[@class="sidebar-title"]/a[@href="#fields"]' 'Fields'
-// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f1"]' 'f1'
-// @has - '//*[@class="sidebar-links"]/a[@href="#structfield.f2"]' 'f2'
-// @!has - '//*[@class="sidebar-links"]/a' 'waza'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#structfield.f1"]' 'f1'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#structfield.f2"]' 'f2'
+// @!has - '//*[@class="sidebar-elems"]//section//a' 'waza'
pub union MyUnion {
pub f1: u32,
pub f2: f32,
#![crate_name = "foo"]
-// @has foo/struct.SomeStruct.html '//*[@class="sidebar-links"]/a[@href="#method.some_fn-1"]' \
+// @has foo/struct.SomeStruct.html '//*[@class="sidebar-elems"]//section//li/a[@href="#method.some_fn-1"]' \
// "some_fn"
pub struct SomeStruct<T> { _inner: T }
// @has foo/trait.Foo.html
// @has - '//*[@class="sidebar-title"]/a[@href="#foreign-impls"]' 'Implementations on Foreign Types'
// @has - '//h2[@id="foreign-impls"]' 'Implementations on Foreign Types'
-// @has - '//*[@class="sidebar-links"]/a[@href="#impl-Foo-for-u32"]' 'u32'
-// @has - '//div[@id="impl-Foo-for-u32"]//h3[@class="code-header in-band"]' 'impl Foo for u32'
-// @has - '//*[@class="sidebar-links"]/a[@href="#impl-Foo-for-%26%27a%20str"]' "&'a str"
-// @has - '//div[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header in-band"]' "impl<'a> Foo for &'a str"
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-u32"]' 'u32'
+// @has - '//*[@id="impl-Foo-for-u32"]//h3[@class="code-header in-band"]' 'impl Foo for u32'
+// @has - '//*[@class="sidebar-elems"]//section//a[@href="#impl-Foo-for-%26%27a%20str"]' "&'a str"
+// @has - '//*[@id="impl-Foo-for-%26%27a%20str"]//h3[@class="code-header in-band"]' "impl<'a> Foo for &'a str"
pub trait Foo {}
impl Foo for u32 {}
#![crate_name = "foo"]
// @has foo/struct.Bar.html
-// @!has - '//div[@id="impl-Sized"]'
+// @!has - '//*[@id="impl-Sized"]'
pub struct Bar {
a: u16,
}
// @has foo/struct.Foo.html
-// @!has - '//div[@id="impl-Sized"]'
+// @!has - '//*[@id="impl-Sized"]'
pub struct Foo<T: ?Sized>(T);
// @has foo/struct.Unsized.html
-// @has - '//div[@id="impl-Sized"]//h3[@class="code-header in-band"]' 'impl !Sized for Unsized'
+// @has - '//*[@id="impl-Sized"]//h3[@class="code-header in-band"]' 'impl !Sized for Unsized'
pub struct Unsized {
data: [u8],
}
// @has - '//div[@class="main-heading"]/*[@class="out-of-band"]' '1.0 · source · '
#[stable(feature = "bar", since = "1.0")]
pub trait Bar {
- // @has - '//div[@id="tymethod.foo"]/*[@class="rightside"]' '3.0 · source'
+ // @has - '//*[@id="tymethod.foo"]/*[@class="rightside"]' '3.0 · source'
#[stable(feature = "foobar", since = "3.0")]
fn foo();
}
pub struct Foo;
impl Foo {
- // @has - '//div[@id="method.foofoo"]/*[@class="rightside"]' '3.0 · source'
+ // @has - '//*[@id="method.foofoo"]/*[@class="rightside"]' '3.0 · source'
#[stable(feature = "foobar", since = "3.0")]
pub fn foofoo() {}
}
use std::iter::Iterator;
// @has foo/struct.Odd.html
-// @has - '//div[@id="method.new"]//span[@class="notable-traits"]//code/span' 'impl Iterator for Odd'
+// @has - '//*[@id="method.new"]//span[@class="notable-traits"]//code/span' 'impl Iterator for Odd'
pub struct Odd {
current: usize,
}
#![crate_name = "foo"]
// @has foo/struct.Unsized.html
-// @has - '//div[@id="impl-Sized"]/h3[@class="code-header in-band"]' 'impl !Sized for Unsized'
-// @!has - '//div[@id="impl-Sized"]//a[@class="srclink"]' 'source'
-// @has - '//div[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for Unsized'
-// @!has - '//div[@id="impl-Sync"]//a[@class="srclink"]' 'source'
-// @has - '//div[@id="impl-Any"]/h3[@class="code-header in-band"]' 'impl<T> Any for T'
-// @has - '//div[@id="impl-Any"]//a[@class="srclink"]' 'source'
+// @has - '//*[@id="impl-Sized"]/h3[@class="code-header in-band"]' 'impl !Sized for Unsized'
+// @!has - '//*[@id="impl-Sized"]//a[@class="srclink"]' 'source'
+// @has - '//*[@id="impl-Sync"]/h3[@class="code-header in-band"]' 'impl Sync for Unsized'
+// @!has - '//*[@id="impl-Sync"]//a[@class="srclink"]' 'source'
+// @has - '//*[@id="impl-Any"]/h3[@class="code-header in-band"]' 'impl<T> Any for T'
+// @has - '//*[@id="impl-Any"]//a[@class="srclink"]' 'source'
pub struct Unsized {
data: [u8],
}
#![crate_name = "foo"]
-// The goal of this test is to answer that it won't be generated as a list because
+// The goal of this test is to ensure that it won't be generated as a list because
// block doc comments can have their lines starting with a star.
// @has foo/fn.foo.html
}
impl MyTrait for String {
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-1"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-1"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-1"]//a[@class="anchor"]/@href' #associatedtype.Assoc-1
type Assoc = ();
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-1"]//a[@class="constant"]/@href' #associatedconstant.VALUE
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-1"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-1
const VALUE: u32 = 5;
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
fn trait_function(&self) {}
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="fnname"]/@href' #method.defaulted_override
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-1"]//a[@class="anchor"]/@href' #method.defaulted_override-1
fn defaulted_override(&self) {}
}
impl MyTrait for Vec<u8> {
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-2"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-2"]//a[@class="associatedtype"]/@href' #associatedtype.Assoc
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-2"]//a[@class="anchor"]/@href' #associatedtype.Assoc-2
type Assoc = ();
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-2"]//a[@class="constant"]/@href' #associatedconstant.VALUE
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-2"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-2
const VALUE: u32 = 5;
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' #tymethod.trait_function
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.trait_function-1"]//a[@class="anchor"]/@href' #method.trait_function-1
fn trait_function(&self) {}
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="fnname"]/@href' #method.defaulted_override
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="method.defaulted_override-2"]//a[@class="anchor"]/@href' #method.defaulted_override-2
fn defaulted_override(&self) {}
}
impl MyTrait for MyStruct {
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedtype.Assoc"]//a[@class="associatedtype"]/@href' trait.MyTrait.html#associatedtype.Assoc
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedtype.Assoc-3"]//a[@class="anchor"]/@href' #associatedtype.Assoc-3
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedtype.Assoc"]//a[@class="associatedtype"]/@href' trait.MyTrait.html#associatedtype.Assoc
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedtype.Assoc"]//a[@class="anchor"]/@href' #associatedtype.Assoc
type Assoc = bool;
- // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//div[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE
+ // @has trait_impl_items_links_and_anchors/trait.MyTrait.html '//*[@id="associatedconstant.VALUE-3"]//a[@class="anchor"]/@href' #associatedconstant.VALUE-3
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedconstant.VALUE"]//a[@class="constant"]/@href' trait.MyTrait.html#associatedconstant.VALUE
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="associatedconstant.VALUE"]//a[@class="anchor"]/@href' #associatedconstant.VALUE
const VALUE: u32 = 20;
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="fnname"]/@href' trait.MyTrait.html#tymethod.trait_function
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.trait_function"]//a[@class="anchor"]/@href' #method.trait_function
fn trait_function(&self) {}
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted_override
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted_override"]//a[@class="anchor"]/@href' #method.defaulted_override
fn defaulted_override(&self) {}
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted
- // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//div[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="fnname"]/@href' trait.MyTrait.html#method.defaulted
+ // @has trait_impl_items_links_and_anchors/struct.MyStruct.html '//*[@id="method.defaulted"]//a[@class="anchor"]/@href' #method.defaulted
}
pub struct MyStruct;
// @has foo/all.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
// @has foo/all.html '//a[@href="traitalias.Foo.html"]' 'Foo'
-// @has foo/index.html '//h2[@id="trait-aliases"]' 'Trait aliases'
+// @has foo/index.html '//h2[@id="trait-aliases"]' 'Trait Aliases'
// @has foo/index.html '//a[@class="traitalias"]' 'CopyAlias'
// @has foo/index.html '//a[@class="traitalias"]' 'Alias2'
// @has foo/index.html '//a[@class="traitalias"]' 'Foo'
pub struct Bar;
impl Bar {
- // @has - '//div[@id="method.bar"]/*[@class="rightside"]' '2.0'
- // @!has - '//div[@id="method.bar"]/*[@class="rightside"]' '2.0 ·'
+ // @has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0'
+ // @!has - '//*[@id="method.bar"]/*[@class="rightside"]' '2.0 ·'
#[stable(feature = "foobar", since = "2.0")]
pub fn bar() {}
}
#![crate_name = "foo"]
pub trait SomeTrait<Rhs = Self>
-where Rhs: ?Sized
-{}
+where
+ Rhs: ?Sized,
+{
+}
// @has 'foo/trait.SomeTrait.html'
-// @has - "//div[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
-impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where
+// @has - "//*[@id='impl-SomeTrait%3C(A%2C%20B%2C%20C%2C%20D%2C%20E)%3E-for-(A%2C%20B%2C%20C%2C%20D%2C%20E)']/h3" "impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E) where A: PartialOrd<A> + PartialEq<A>, B: PartialOrd<B> + PartialEq<B>, C: PartialOrd<C> + PartialEq<C>, D: PartialOrd<D> + PartialEq<D>, E: PartialOrd<E> + PartialEq<E> + ?Sized, "
+impl<A, B, C, D, E> SomeTrait<(A, B, C, D, E)> for (A, B, C, D, E)
+where
A: PartialOrd<A> + PartialEq<A>,
B: PartialOrd<B> + PartialEq<B>,
C: PartialOrd<C> + PartialEq<C>,
D: PartialOrd<D> + PartialEq<D>,
- E: PartialOrd<E> + PartialEq<E> + ?Sized
-{}
+ E: PartialOrd<E> + PartialEq<E> + ?Sized,
+{
+}
--- /dev/null
+// run-pass
+// allows aligned custom discriminant enums to cast into other types
+// See the issue #92464 for more info
+#[allow(dead_code)]
+#[repr(align(8))]
+enum Aligned {
+ Zero = 0,
+ One = 1,
+}
+
+fn main() {
+ let aligned = Aligned::Zero;
+ let fo = aligned as u8;
+ println!("foo {}",fo);
+}
error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
- --> $DIR/not-an-allocator.rs:2:1
+ --> $DIR/not-an-allocator.rs:2:11
|
LL | #[global_allocator]
| ------------------- in this procedural macro expansion
LL | static A: usize = 0;
- | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
+ | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
- --> $DIR/not-an-allocator.rs:2:1
+ --> $DIR/not-an-allocator.rs:2:11
|
LL | #[global_allocator]
| ------------------- in this procedural macro expansion
LL | static A: usize = 0;
- | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
+ | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
- --> $DIR/not-an-allocator.rs:2:1
+ --> $DIR/not-an-allocator.rs:2:11
|
LL | #[global_allocator]
| ------------------- in this procedural macro expansion
LL | static A: usize = 0;
- | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
+ | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied
- --> $DIR/not-an-allocator.rs:2:1
+ --> $DIR/not-an-allocator.rs:2:11
|
LL | #[global_allocator]
| ------------------- in this procedural macro expansion
LL | static A: usize = 0;
- | ^^^^^^^^^^^^^^^^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
+ | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize`
|
= note: this error originates in the attribute macro `global_allocator` (in Nightly builds, run with -Z macro-backtrace for more info)
--> $DIR/parse-error.rs:39:37
|
LL | let mut foo = 0;
- | ---------- help: consider using `const` instead of `let`: `const foo`
+ | ----------- help: consider using `const` instead of `let`: `const foo`
...
LL | asm!("{}", options(), const foo);
| ^^^ non-constant value
--> $DIR/parse-error.rs:48:44
|
LL | let mut foo = 0;
- | ---------- help: consider using `const` instead of `let`: `const foo`
+ | ----------- help: consider using `const` instead of `let`: `const foo`
...
LL | asm!("{}", clobber_abi("C"), const foo);
| ^^^ non-constant value
--> $DIR/parse-error.rs:55:31
|
LL | let mut foo = 0;
- | ---------- help: consider using `const` instead of `let`: `const foo`
+ | ----------- help: consider using `const` instead of `let`: `const foo`
...
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:55:46
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:62:45
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("x0") foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:65:45
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("x0") foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:68:41
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{1}", in("x0") foo, const bar);
| ^^^ non-constant value
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:36:15
+ --> $DIR/bad-template.rs:36:16
|
LL | asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:63:14
+ --> $DIR/bad-template.rs:63:15
|
LL | global_asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:36:15
+ --> $DIR/bad-template.rs:36:16
|
LL | asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:63:14
+ --> $DIR/bad-template.rs:63:15
|
LL | global_asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:36:15
+ --> $DIR/bad-template.rs:36:16
|
LL | asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:63:14
+ --> $DIR/bad-template.rs:63:15
|
LL | global_asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:36:15
+ --> $DIR/bad-template.rs:36:16
|
LL | asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:38:15
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {0} */"`
error: there is no argument named `a`
- --> $DIR/bad-template.rs:63:14
+ --> $DIR/bad-template.rs:63:15
|
LL | global_asm!("{a}");
- | ^^^
+ | ^
error: invalid reference to argument at index 0
--> $DIR/bad-template.rs:65:14
--- /dev/null
+// compile-flags: --target armv7-unknown-linux-gnueabihf
+// needs-llvm-components: arm
+
+#![feature(no_core, lang_items, rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+ () => {};
+}
+#[lang = "sized"]
+trait Sized {}
+
+fn main() {
+ unsafe {
+ asm!("", out("d0") _, out("d1") _);
+ asm!("", out("d0") _, out("s1") _);
+ //~^ ERROR register `s1` conflicts with register `d0`
+ }
+}
--- /dev/null
+error: register `s1` conflicts with register `d0`
+ --> $DIR/reg-conflict.rs:17:31
+ |
+LL | asm!("", out("d0") _, out("s1") _);
+ | ----------- ^^^^^^^^^^^ register `s1`
+ | |
+ | register `d0`
+
+error: aborting due to previous error
+
--> $DIR/parse-error.rs:39:37
|
LL | let mut foo = 0;
- | ---------- help: consider using `const` instead of `let`: `const foo`
+ | ----------- help: consider using `const` instead of `let`: `const foo`
...
LL | asm!("{}", options(), const foo);
| ^^^ non-constant value
--> $DIR/parse-error.rs:50:44
|
LL | let mut foo = 0;
- | ---------- help: consider using `const` instead of `let`: `const foo`
+ | ----------- help: consider using `const` instead of `let`: `const foo`
...
LL | asm!("{}", clobber_abi("C"), const foo);
| ^^^ non-constant value
--> $DIR/parse-error.rs:57:31
|
LL | let mut foo = 0;
- | ---------- help: consider using `const` instead of `let`: `const foo`
+ | ----------- help: consider using `const` instead of `let`: `const foo`
...
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:57:46
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", a = const foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:64:46
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("eax") foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:67:46
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{a}", in("eax") foo, a = const bar);
| ^^^ non-constant value
--> $DIR/parse-error.rs:70:42
|
LL | let mut bar = 0;
- | ---------- help: consider using `const` instead of `let`: `const bar`
+ | ----------- help: consider using `const` instead of `let`: `const bar`
...
LL | asm!("{1}", in("eax") foo, const bar);
| ^^^ non-constant value
--- /dev/null
+fn e() {
+ p:a<p:p<e=6>>
+ //~^ ERROR comparison operators
+ //~| ERROR cannot find value
+ //~| ERROR associated const equality
+ //~| ERROR associated const equality
+ //~| ERROR associated type bounds
+}
+
+fn main() {}
--- /dev/null
+error: comparison operators cannot be chained
+ --> $DIR/issue-93835.rs:2:8
+ |
+LL | fn e() {
+ | - while parsing this struct
+LL | p:a<p:p<e=6>>
+ | ^ ^
+ |
+ = help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+ = help: or use `(...)` if you meant to specify fn arguments
+
+error[E0425]: cannot find value `p` in this scope
+ --> $DIR/issue-93835.rs:2:5
+ |
+LL | p:a<p:p<e=6>>
+ | ^ not found in this scope
+ |
+help: you might have meant to write a `struct` literal
+ |
+LL ~ fn e() { SomeStruct {
+LL | p:a<p:p<e=6>>
+LL |
+LL |
+LL |
+LL |
+ ...
+help: maybe you meant to write a path separator here
+ |
+LL | p::a<p:p<e=6>>
+ | ~~
+help: maybe you meant to write an assignment here
+ |
+LL | let p:a<p:p<e=6>>
+ | ~~~~~
+
+error[E0658]: associated const equality is incomplete
+ --> $DIR/issue-93835.rs:2:13
+ |
+LL | p:a<p:p<e=6>>
+ | ^^^
+ |
+ = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
+ = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+
+error[E0658]: associated const equality is incomplete
+ --> $DIR/issue-93835.rs:2:13
+ |
+LL | p:a<p:p<e=6>>
+ | ^^^
+ |
+ = note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
+ = help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
+
+error[E0658]: associated type bounds are unstable
+ --> $DIR/issue-93835.rs:2:9
+ |
+LL | p:a<p:p<e=6>>
+ | ^^^^^^^^
+ |
+ = note: see issue #52662 <https://github.com/rust-lang/rust/issues/52662> for more information
+ = help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0425, E0658.
+For more information about an error, try `rustc --explain E0425`.
--- /dev/null
+// Checking that none of these ICE, which was introduced in
+// https://github.com/rust-lang/rust/issues/93553
+trait Foo {
+ type Bar;
+}
+
+trait Baz: Foo {
+ const Bar: Self::Bar;
+}
+
+trait Baz2: Foo {
+ const Bar: u32;
+
+ fn foo() -> Self::Bar;
+}
+
+trait Baz3 {
+ const BAR: usize;
+ const QUX: Self::BAR;
+ //~^ ERROR found associated const
+}
+
+fn main() {}
--- /dev/null
+error: found associated const `BAR` when type was expected
+ --> $DIR/shadowed-const.rs:19:14
+ |
+LL | const QUX: Self::BAR;
+ | ^^^^^^^^^
+
+error: aborting due to previous error
+
// NOTE: rustc cannot currently handle bounds of the form `for<'a> <Foo as Bar<'a>>::Assoc: Baz`.
// This should hopefully be fixed with Chalk.
-// ignore-compare-mode-chalk
#![feature(associated_type_bounds)]
error[E0277]: `<<Self as Case1>::C as Iterator>::Item` cannot be sent between threads safely
- --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:36
+ --> $DIR/bad-bounds-on-assoc-in-trait.rs:26:36
|
LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>;
| ^^^^ `<<Self as Case1>::C as Iterator>::Item` cannot be sent between threads safely
| ++++++++++++++++++++++++++++++++++++++++++++++++++
error[E0277]: `<<Self as Case1>::C as Iterator>::Item` is not an iterator
- --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:43
+ --> $DIR/bad-bounds-on-assoc-in-trait.rs:26:43
|
LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `<<Self as Case1>::C as Iterator>::Item` is not an iterator
| ++++++++++++++++++++++++++++++++++++++++++++++++++++++
error[E0277]: `<<Self as Case1>::C as Iterator>::Item` cannot be shared between threads safely
- --> $DIR/bad-bounds-on-assoc-in-trait.rs:27:93
+ --> $DIR/bad-bounds-on-assoc-in-trait.rs:26:93
|
LL | type C: Clone + Iterator<Item: Send + Iterator<Item: for<'a> Lam<&'a u8, App: Debug>> + Sync>;
| ^^^^ `<<Self as Case1>::C as Iterator>::Item` cannot be shared between threads safely
// build-pass (FIXME(62277): could be check-pass?)
-// ignore-compare-mode-chalk
#![feature(associated_type_bounds)]
// run-pass
-// ignore-compare-mode-chalk
#![feature(associated_type_bounds)]
#![feature(untagged_unions)]
LL | f1(2u32, 4u32);
| ^^ the trait `Foo` is not implemented for `u32`
|
+ = help: the following implementations were found:
+ <i32 as Foo>
note: required by a bound in `f1`
--> $DIR/associated-types-path-2.rs:13:14
|
|
LL | f1(2u32, 4u32);
| ^^^^ the trait `Foo` is not implemented for `u32`
+ |
+ = help: the following implementations were found:
+ <i32 as Foo>
error[E0277]: the trait bound `u32: Foo` is not satisfied
--> $DIR/associated-types-path-2.rs:35:8
| |
| required by a bound introduced by this call
|
+ = help: the following implementations were found:
+ <i32 as Foo>
note: required by a bound in `f1`
--> $DIR/associated-types-path-2.rs:13:14
|
|
LL | f1(2u32, 4i32);
| ^^^^ the trait `Foo` is not implemented for `u32`
+ |
+ = help: the following implementations were found:
+ <i32 as Foo>
error[E0308]: mismatched types
--> $DIR/associated-types-path-2.rs:41:18
// run-pass
// Test references to the trait `Stream` in the bounds for associated
// types defined on `Stream`. Issue #20551.
-// ignore-compare-mode-chalk
-
trait Stream {
type Car;
LL | type U = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `X`
--> $DIR/hr-associated-type-bound-1.rs:3:33
|
LL | fn f<'a, T: X<'a> + ?Sized>(x: &<T as X<'a>>::U) {
| ^^^^^ the trait `for<'b> Clone` is not implemented for `<T as X<'b>>::U`
|
- = help: the following implementations were found:
- <&T as Clone>
note: required by a bound in `X`
--> $DIR/hr-associated-type-bound-object.rs:3:33
|
LL | type V = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `Y`
--> $DIR/hr-associated-type-bound-param-1.rs:4:36
|
-// ignore-compare-mode-chalk
trait Z<'a, T: ?Sized>
where
T: Z<'a, u16>,
error[E0277]: the trait bound `str: Clone` is not satisfied
- --> $DIR/hr-associated-type-bound-param-2.rs:4:8
+ --> $DIR/hr-associated-type-bound-param-2.rs:3:8
|
LL | T: Z<'a, u16>,
| ^^^^^^^^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `Z`
- --> $DIR/hr-associated-type-bound-param-2.rs:7:35
+ --> $DIR/hr-associated-type-bound-param-2.rs:6:35
|
LL | trait Z<'a, T: ?Sized>
| - required by a bound in this
| ^^^^^ required by this bound in `Z`
error[E0277]: the trait bound `str: Clone` is not satisfied
- --> $DIR/hr-associated-type-bound-param-2.rs:4:8
+ --> $DIR/hr-associated-type-bound-param-2.rs:3:8
|
LL | T: Z<'a, u16>,
| ^^^^^^^^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `Z`
- --> $DIR/hr-associated-type-bound-param-2.rs:7:35
+ --> $DIR/hr-associated-type-bound-param-2.rs:6:35
|
LL | trait Z<'a, T: ?Sized>
| - required by a bound in this
| ^^^^^ required by this bound in `Z`
error[E0277]: the trait bound `str: Clone` is not satisfied
- --> $DIR/hr-associated-type-bound-param-2.rs:16:14
+ --> $DIR/hr-associated-type-bound-param-2.rs:15:14
|
LL | type W = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `Z`
- --> $DIR/hr-associated-type-bound-param-2.rs:7:35
+ --> $DIR/hr-associated-type-bound-param-2.rs:6:35
|
LL | trait Z<'a, T: ?Sized>
| - required by a bound in this
LL | type U = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `X`
--> $DIR/hr-associated-type-bound-param-3.rs:4:33
|
LL | type U = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `X`
--> $DIR/hr-associated-type-bound-param-4.rs:4:36
|
-// ignore-compare-mode-chalk
trait Cycle: Sized {
type Next: Cycle<Next = Self>;
}
error[E0277]: the trait bound `str: Clone` is not satisfied
- --> $DIR/hr-associated-type-bound-param-5.rs:27:14
+ --> $DIR/hr-associated-type-bound-param-5.rs:26:14
|
LL | type U = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `X`
- --> $DIR/hr-associated-type-bound-param-5.rs:18:45
+ --> $DIR/hr-associated-type-bound-param-5.rs:17:45
|
LL | trait X<'a, T: Cycle + for<'b> X<'b, T>>
| - required by a bound in this
| ^^^^^ required by this bound in `X`
error[E0277]: the trait bound `str: Clone` is not satisfied
- --> $DIR/hr-associated-type-bound-param-5.rs:32:14
+ --> $DIR/hr-associated-type-bound-param-5.rs:31:14
|
LL | type U = str;
| ^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `X`
- --> $DIR/hr-associated-type-bound-param-5.rs:18:45
+ --> $DIR/hr-associated-type-bound-param-5.rs:17:45
|
LL | trait X<'a, T: Cycle + for<'b> X<'b, T>>
| - required by a bound in this
// Tests that HRTBs are correctly accepted -- https://github.com/rust-lang/rust/issues/50301
// check-pass
-// ignore-compare-mode-chalk
trait Trait
where
for<'a> &'a Self::IntoIter: IntoIterator<Item = u32>,
--- /dev/null
+// check-pass
+
+pub trait Associate {
+ type Associated;
+}
+
+pub struct Wrap<'a> {
+ pub field: &'a i32,
+}
+
+pub trait Create<T> {
+ fn create() -> Self;
+}
+
+pub fn oh_no<'a, T>()
+where
+ Wrap<'a>: Associate,
+ <Wrap<'a> as Associate>::Associated: Create<T>,
+{
+ <Wrap<'a> as Associate>::Associated::create();
+}
+
+
+pub fn main() {}
let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
//[verbose]~^ ERROR mismatched types
//[verbose]~| expected unit type `()`
- //[verbose]~| found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u32>>::bar::<ReStatic, char>}`
+ //[verbose]~| found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
//[normal]~^^^^ ERROR mismatched types
//[normal]~| expected unit type `()`
//[normal]~| found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
--> $DIR/substs-ppaux.rs:25:17
|
LL | fn bar<'a, T>() where T: 'a {}
- | --------------------------- fn() {<i8 as Foo<ReStatic, ReStatic, u32>>::bar::<ReStatic, char>} defined here
+ | --------------------------- fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>} defined here
...
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found fn item
| expected due to this
|
= note: expected unit type `()`
- found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u32>>::bar::<ReStatic, char>}`
+ found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
help: use parentheses to call this function
|
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
// Regression test for #93197
// check-pass
// edition:2021
+// compile-flags: -Zdrop-tracking
#![feature(try_blocks)]
--- /dev/null
+// edition:2021
+// build-pass
+// compile-flags: -Zdrop-tracking
+
+fn main() {
+ let _ = async {
+ let mut s = (String::new(),);
+ s.0.push_str("abc");
+ std::mem::drop(s);
+ async {}.await;
+ };
+}
--- /dev/null
+#[main] //~ ERROR cannot find attribute `main` in this scope
+fn main() {}
--- /dev/null
+error: cannot find attribute `main` in this scope
+ --> $DIR/main-removed-1.rs:1:3
+ |
+LL | #[main]
+ | ^^^^
+ |
+ = note: `main` is in scope, but it is a function, not an attribute
+
+error: aborting due to previous error
+
--- /dev/null
+// force-host
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+use proc_macro::TokenStream;
+
+#[proc_macro_attribute]
+pub fn main(_: TokenStream, input: TokenStream) -> TokenStream {
+ "fn main() { println!(\"Hello Tokyo!\"); }".parse().unwrap()
+}
--- /dev/null
+// run-pass
+// aux-build:tokyo.rs
+// compile-flags:--extern tokyo
+// edition:2021
+
+use tokyo::main;
+
+#[main]
+fn main() {
+ panic!("the #[main] macro should replace this with non-panicking code")
+}
--- /dev/null
+#![feature(used_with_arg)]
+
+#[used(linker)]
+static mut USED_LINKER: [usize; 1] = [0];
+
+#[used(compiler)]
+static mut USED_COMPILER: [usize; 1] = [0];
+
+#[used(compiler)] //~ ERROR `used(compiler)` and `used(linker)` can't be used together
+#[used(linker)]
+static mut USED_COMPILER_LINKER2: [usize; 1] = [0];
+
+#[used(compiler)] //~ ERROR `used(compiler)` and `used(linker)` can't be used together
+#[used(linker)]
+#[used(compiler)]
+#[used(linker)]
+static mut USED_COMPILER_LINKER3: [usize; 1] = [0];
+
+fn main() {}
--- /dev/null
+error: `used(compiler)` and `used(linker)` can't be used together
+ --> $DIR/used_with_arg.rs:9:1
+ |
+LL | #[used(compiler)]
+ | ^^^^^^^^^^^^^^^^^
+LL | #[used(linker)]
+ | ^^^^^^^^^^^^^^^
+
+error: `used(compiler)` and `used(linker)` can't be used together
+ --> $DIR/used_with_arg.rs:13:1
+ |
+LL | #[used(compiler)]
+ | ^^^^^^^^^^^^^^^^^
+LL | #[used(linker)]
+ | ^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+#![feature(used_with_arg)]
+
+#[used(compiler, linker)] //~ expected `used`, `used(compiler)` or `used(linker)`
+static mut USED_COMPILER_LINKER: [usize; 1] = [0];
+
+fn main() {}
--- /dev/null
+error: expected `used`, `used(compiler)` or `used(linker)`
+ --> $DIR/used_with_multi_args.rs:3:1
+ |
+LL | #[used(compiler, linker)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
LL | is_defaulted::<&'static u32>();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Signed` is not implemented for `u32`
|
+ = help: the following implementations were found:
+ <i32 as Signed>
note: required because of the requirements on the impl of `Defaulted` for `&'static u32`
--> $DIR/typeck-default-trait-impl-precedence.rs:10:19
|
|
LL | struct A;
| ^^^^^^^^^ must implement `PartialOrd<_>`
-help: consider annotating `A` with `#[derive(PartialOrd)]`
+help: consider annotating `A` with `#[derive(PartialEq, PartialOrd)]`
|
-LL | #[derive(PartialOrd)]
+LL | #[derive(PartialEq, PartialOrd)]
|
error[E0369]: binary operation `<=` cannot be applied to type `A`
|
LL | struct A;
| ^^^^^^^^^ must implement `PartialOrd<_>`
-help: consider annotating `A` with `#[derive(PartialOrd)]`
+help: consider annotating `A` with `#[derive(PartialEq, PartialOrd)]`
|
-LL | #[derive(PartialOrd)]
+LL | #[derive(PartialEq, PartialOrd)]
|
error[E0369]: binary operation `>` cannot be applied to type `A`
|
LL | struct A;
| ^^^^^^^^^ must implement `PartialOrd<_>`
-help: consider annotating `A` with `#[derive(PartialOrd)]`
+help: consider annotating `A` with `#[derive(PartialEq, PartialOrd)]`
|
-LL | #[derive(PartialOrd)]
+LL | #[derive(PartialEq, PartialOrd)]
|
error[E0369]: binary operation `>=` cannot be applied to type `A`
|
LL | struct A;
| ^^^^^^^^^ must implement `PartialOrd<_>`
-help: consider annotating `A` with `#[derive(PartialOrd)]`
+help: consider annotating `A` with `#[derive(PartialEq, PartialOrd)]`
|
-LL | #[derive(PartialOrd)]
+LL | #[derive(PartialEq, PartialOrd)]
|
error: aborting due to 15 previous errors
struct Value;
static settings_dir: String = format!("");
-//~^ ERROR calls in statics are limited to constant functions
+//~^ ERROR cannot call non-const fn
//~| ERROR is not yet stable as a const
fn from_string(_: String) -> Value {
= help: add `#![feature(const_fmt_arguments_new)]` to the crate attributes to enable
= note: this error originates in the macro `$crate::__export::format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `format` in statics
--> $DIR/issue-64453.rs:4:31
|
LL | static settings_dir: String = format!("");
| ^^^^^^^^^^^
|
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0507]: cannot move out of static item `settings_dir`
--- /dev/null
+// check-pass
+#![feature(allocator_api)]
+
+fn main() {
+ Box::new_in((), &std::alloc::Global);
+}
--- /dev/null
+// run-pass
+// compile-flags: -Z chalk
+
+fn main() {
+ assert_eq!(1, 1);
+}
LL | gimme::<f32>();
| ^^^ the trait `Foo` is not implemented for `f32`
|
+ = help: the following implementations were found:
+ <i32 as Foo>
+ <u32 as Foo>
note: required by a bound in `gimme`
--> $DIR/chalk_initial_program.rs:9:13
|
LL | impl Baz<f32> for f32 { }
| ^^^^^^^^ the trait `Foo` is not implemented for `f32`
|
+ = help: the following implementations were found:
+ <i32 as Foo>
note: required by a bound in `Baz`
--> $DIR/impl_wf.rs:18:31
|
LL | type Item = f32;
| ^^^ the trait `Foo` is not implemented for `f32`
|
+ = help: the following implementations were found:
+ <i32 as Foo>
note: required by a bound in `Bar::Item`
--> $DIR/impl_wf_2.rs:8:16
|
// compile-flags: -Z chalk
fn main() {
- // FIXME(chalk): Require `RegionOutlives`/`TypeOutlives`/`Subtype` support
- //println!("hello");
+ println!("hello");
}
fn main() {
let d: &dyn Display = &mut 3;
- // FIXME(chalk) should be able to call d.to_string() as well, but doing so
- // requires Chalk to be able to prove trait object well-formed goals.
+ d.to_string();
(&d).to_string();
let f: &dyn Fn(i32) -> _ = &|x| x + x;
f(2);
| ^ the trait `Foo` is not implemented for `{float}`
|
= help: the following implementations were found:
- <Option<T> as Foo>
<i32 as Foo>
note: required by a bound in `S`
--> $DIR/type_wf.rs:6:13
static mut STATIC14: SafeStruct = SafeStruct {
field1: SafeEnum::Variant1,
field2: SafeEnum::Variant4("str".to_string())
-//~^ ERROR calls in statics are limited to constant functions
+//~^ ERROR cannot call non-const fn
};
static STATIC15: &'static [Box<MyOwned>] = &[
LL | static STATIC11: Box<MyOwned> = box MyOwned;
| ^^^^^^^^^^^ allocation not allowed in statics
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
- --> $DIR/check-static-values-constraints.rs:89:32
+error[E0015]: cannot call non-const fn `<str as ToString>::to_string` in statics
+ --> $DIR/check-static-values-constraints.rs:89:38
|
LL | field2: SafeEnum::Variant4("str".to_string())
- | ^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error[E0010]: allocations are not allowed in statics
--> $DIR/check-static-values-constraints.rs:94:5
LL | extern "路濫狼á́́" fn foo() {}
| ^^^^^^^^^ invalid ABI
|
- = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
+ = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
error: aborting due to previous error
#![crate_type = "lib"]
#![feature(negative_impls)]
+#![feature(with_negative_coherence)]
pub trait Error {}
impl !Error for &str {}
--- /dev/null
+#![feature(negative_impls)]
+
+// FIXME: this should compile
+
+trait MyPredicate<'a> {}
+impl<'a, T> !MyPredicate<'a> for &T where T: 'a {}
+trait MyTrait<'a> {}
+impl<'a, T: MyPredicate<'a>> MyTrait<'a> for T {}
+impl<'a, T> MyTrait<'a> for &'a T {}
+//~^ ERROR: conflicting implementations of trait `MyTrait<'_>` for type `&_`
+
+fn main() {}
--- /dev/null
+error[E0119]: conflicting implementations of trait `MyTrait<'_>` for type `&_`
+ --> $DIR/coherence-negative-outlives-lifetimes.rs:9:1
+ |
+LL | impl<'a, T: MyPredicate<'a>> MyTrait<'a> for T {}
+ | ---------------------------------------------- first implementation here
+LL | impl<'a, T> MyTrait<'a> for &'a T {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&_`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
// check-pass
-#![feature(negative_impls)]
+#![feature(with_negative_coherence)]
use std::ops::DerefMut;
//
// Check that if we promise to not impl what would overlap it doesn't actually overlap
-#![feature(negative_impls)]
+#![feature(with_negative_coherence)]
extern crate error_lib as lib;
use lib::Error;
--- /dev/null
+// check-pass
+
+#![feature(negative_impls)]
+#![feature(rustc_attrs)]
+#![feature(with_negative_coherence)]
+
+#[rustc_strict_coherence]
+trait Foo {}
+impl<T> !Foo for &T where T: 'static {}
+
+#[rustc_strict_coherence]
+trait Bar {}
+impl<T: Foo> Bar for T {}
+impl<T> Bar for &T where T: 'static {}
+
+fn main() {}
--- /dev/null
+struct X<const N: usize = {
+ let s: &'static str; s.len()
+ //~^ ERROR borrow of possibly-uninitialized variable
+}>;
+
+fn main() {}
--- /dev/null
+error[E0381]: borrow of possibly-uninitialized variable: `s`
+ --> $DIR/const-generic-default-wont-borrowck.rs:2:26
+ |
+LL | let s: &'static str; s.len()
+ | ^^^^^^^ use of possibly-uninitialized `*s`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0381`.
|
= help: the following implementations were found:
<u32 as Traitor<N, 2_u8>>
+ <u64 as Traitor<1_u8, 2_u8>>
error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
--> $DIR/rp_impl_trait_fail.rs:22:13
|
= help: the following implementations were found:
<u64 as Traitor<1_u8, 2_u8>>
+ <u32 as Traitor<N, 2_u8>>
error: aborting due to 3 previous errors
--- /dev/null
+struct X<const N: usize = {
+ (||1usize)()
+ //~^ ERROR cannot call
+}>;
+
+fn main() {}
--- /dev/null
+error[E0015]: cannot call non-const closure in constants
+ --> $DIR/issue-93647.rs:2:5
+ |
+LL | (||1usize)()
+ | ^^^^^^^^^^^^
+ |
+ = note: closures need an RFC before allowed to be called in constants
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0015`.
where
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
//~^ ERROR: overly complex generic constant
- //~| ERROR: calls in constants are limited to constant functions
+ //~| ERROR: cannot call non-const operator in constants
{
}
where
If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
//~^ ERROR: overly complex generic constant
- //~| ERROR: calls in constants are limited to constant functions
+ //~| ERROR: cannot call non-const operator in constants
{
}
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const operator in constants
--> $DIR/issue-90318.rs:14:10
|
LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: impl defined here, but it is not `const`
+ --> $SRC_DIR/core/src/any.rs:LL:COL
+ |
+LL | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+ | ^^^^^^^^^
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
error: overly complex generic constant
--> $DIR/issue-90318.rs:22:8
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const operator in constants
--> $DIR/issue-90318.rs:22:10
|
LL | If<{ TypeId::of::<T>() != TypeId::of::<()>() }>: True,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: impl defined here, but it is not `const`
+ --> $SRC_DIR/core/src/any.rs:LL:COL
+ |
+LL | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+ | ^^^^^^^^^
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
+ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 4 previous errors
--- /dev/null
+trait AlwaysApplicable {
+ type Assoc;
+}
+impl<T: ?Sized> AlwaysApplicable for T {
+ type Assoc = usize;
+}
+
+trait BindsParam<T> {
+ type ArrayTy;
+}
+impl<T> BindsParam<T> for <T as AlwaysApplicable>::Assoc {
+ type ArrayTy = [u8; Self::MAX]; //~ ERROR generic `Self` types
+}
+
+fn main() {}
--- /dev/null
+error: generic `Self` types are currently not permitted in anonymous constants
+ --> $DIR/forbid-self-no-normalize.rs:12:25
+ |
+LL | type ArrayTy = [u8; Self::MAX];
+ | ^^^^
+ |
+note: not a concrete type
+ --> $DIR/forbid-self-no-normalize.rs:11:27
+ |
+LL | impl<T> BindsParam<T> for <T as AlwaysApplicable>::Assoc {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `Foo::{constant#0}::Foo::<17_usize>::value` in constants
--> $DIR/nested-type.rs:15:5
|
LL | Foo::<17>::value()
| ^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#![feature(adt_const_params)]`
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `Foo::{constant#0}::Foo::<17_usize>::value` in constants
--> $DIR/nested-type.rs:15:5
|
LL | Foo::<17>::value()
| ^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to 2 previous errors
}
Foo::<17>::value()
- //~^ ERROR calls in constants are limited to constant functions
+ //~^ ERROR cannot call non-const fn
}]>;
fn main() {}
--- /dev/null
+struct Foo<
+ 'a,
+ const N: usize = {
+ let x: &'a ();
+ //~^ ERROR use of non-static lifetime `'a` in const generic
+ 3
+ },
+>(&'a ());
+
+fn main() {}
--- /dev/null
+error[E0771]: use of non-static lifetime `'a` in const generic
+ --> $DIR/outer-lifetime-in-const-generic-default.rs:4:17
+ |
+LL | let x: &'a ();
+ | ^^
+ |
+ = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0771`.
fn main() {
let _ = [0; f(2)];
- //~^ ERROR calls in constants are limited to constant functions
+ //~^ ERROR cannot call non-const fn
}
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `f` in constants
--> $DIR/const-call.rs:6:17
|
LL | let _ = [0; f(2)];
| ^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
|
LL | : [u32; 5i8 as char as usize]
| ^^^^^^^^^^^ invalid cast
+ |
+help: try casting from `u8` instead
+ --> $DIR/const-eval-overflow-4b.rs:22:13
+ |
+LL | : [u32; 5i8 as char as usize]
+ | ^^^^^^^^^^^
error: aborting due to 3 previous errors
LL | const Y: () = std::unreachable!();
| ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:12:15
|
- = note: this error originates in the macro `std::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `$crate::panic::unreachable_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/const_panic.rs:15:15
LL | const Y_CORE: () = core::unreachable!();
| ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic.rs:30:20
|
- = note: this error originates in the macro `core::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `$crate::panic::unreachable_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/const_panic.rs:33:20
LL | const C: () = std::unreachable!();
| ^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:12:15
|
- = note: this error originates in the macro `std::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `$crate::panic::unreachable_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/const_panic_2021.rs:15:15
LL | const C_CORE: () = core::unreachable!();
| ^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_2021.rs:27:20
|
- = note: this error originates in the macro `core::unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `$crate::panic::unreachable_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/const_panic_2021.rs:30:20
LL | const Y: () = unreachable!();
| ^^^^^^^^^^^^^^ the evaluated program panicked at 'internal error: entered unreachable code', $DIR/const_panic_libcore_bin.rs:11:15
|
- = note: this error originates in the macro `unreachable` (in Nightly builds, run with -Z macro-backtrace for more info)
+ = note: this error originates in the macro `$crate::panic::unreachable_2015` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0080]: evaluation of constant value failed
--> $DIR/const_panic_libcore_bin.rs:14:15
--- /dev/null
+error[E0284]: type annotations needed: cannot satisfy `<usize as SliceIndex<[u8]>>::Output == _`
+ --> $DIR/ub-nonnull.rs:19:30
+ |
+LL | let out_of_bounds_ptr = &ptr[255];
+ | ^^^^^^^^ cannot satisfy `<usize as SliceIndex<[u8]>>::Output == _`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0284`.
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/ub-wide-ptr.rs:90:67
+ |
+LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]);
+ | ^^^^^^^^^^^^^^ cannot infer type for type parameter `U` declared on the function `transmute`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
const extern "C" fn bar() {
unsafe {
regular_in_block();
- //~^ ERROR: calls in constant functions
+ //~^ ERROR: cannot call non-const fn
}
}
const extern "C" fn foo() {
unsafe {
regular();
- //~^ ERROR: calls in constant functions
+ //~^ ERROR: cannot call non-const fn
}
}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `regular_in_block` in constant functions
--> $DIR/const-extern-fn-call-extern-fn.rs:9:9
|
LL | regular_in_block();
| ^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `regular` in constant functions
--> $DIR/const-extern-fn-call-extern-fn.rs:18:9
|
LL | regular();
| ^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: aborting due to 2 previous errors
let mut sum = 0;
for i in 0..x {
//~^ ERROR mutable references
- //~| ERROR calls in constant functions
- //~| ERROR calls in constant functions
- //~| ERROR E0080
+ //~| ERROR cannot convert
+ //~| ERROR cannot call non-const fn
//~| ERROR `for` is not allowed in a `const fn`
sum += i;
}
LL | |
LL | |
LL | |
-... |
+LL | |
LL | | sum += i;
LL | | }
| |_____^
= note: see issue #87575 <https://github.com/rust-lang/rust/issues/87575> for more information
= help: add `#![feature(const_for)]` to the crate attributes to enable
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot convert `std::ops::Range<usize>` into an iterator in constant functions
--> $DIR/const-fn-error.rs:5:14
|
LL | for i in 0..x {
| ^^^^
+ |
+note: impl defined here, but it is not `const`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | impl<I: Iterator> IntoIterator for I {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error[E0658]: mutable references are not allowed in constant functions
--> $DIR/const-fn-error.rs:5:14
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `<std::ops::Range<usize> as Iterator>::next` in constant functions
--> $DIR/const-fn-error.rs:5:14
|
LL | for i in 0..x {
| ^^^^
-
-error[E0080]: evaluation of constant value failed
- --> $DIR/const-fn-error.rs:5:14
|
-LL | for i in 0..x {
- | ^^^^
- | |
- | calling non-const function `<std::ops::Range<usize> as IntoIterator>::into_iter`
- | inside `f` at $DIR/const-fn-error.rs:5:14
-...
-LL | let a : [i32; f(X)];
- | ---- inside `main::{constant#0}` at $DIR/const-fn-error.rs:18:19
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: aborting due to 5 previous errors
+error: aborting due to 4 previous errors
-Some errors have detailed explanations: E0015, E0080, E0658.
+Some errors have detailed explanations: E0015, E0658.
For more information about an error, try `rustc --explain E0015`.
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `random` in constant functions
--> $DIR/const-fn-not-safe-for-const.rs:14:5
|
LL | random()
| ^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error[E0013]: constant functions cannot refer to statics
--> $DIR/const-fn-not-safe-for-const.rs:20:5
const _: () = {
for _ in 0..5 {}
- //~^ error: calls in constants are limited to
- //~| error: calls in constants are limited to
+ //~^ error: cannot convert
+ //~| error: cannot call non-const fn
};
fn main() {}
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot convert `std::ops::Range<i32>` into an iterator in constants
--> $DIR/const-for.rs:5:14
|
LL | for _ in 0..5 {}
| ^^^^
+ |
+note: impl defined here, but it is not `const`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | impl<I: Iterator> IntoIterator for I {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `<std::ops::Range<i32> as Iterator>::next` in constants
--> $DIR/const-for.rs:5:14
|
LL | for _ in 0..5 {}
| ^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to 2 previous errors
LL | const S: &'static mut str = &mut " hello ";
| ^^^^^^^^^^^^^^ cannot borrow as mutable
-error[E0080]: it is undefined behavior to use this value
- --> $DIR/issue-76510.rs:5:1
- |
-LL | const S: &'static mut str = &mut " hello ";
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered mutable reference in a `const`
- |
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
- = note: the raw bytes of the constant (size: 8, align: 4) {
- ╾─alloc3──╼ 07 00 00 00 │ ╾──╼....
- }
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0080, E0596, E0658, E0764.
-For more information about an error, try `rustc --explain E0080`.
+Some errors have detailed explanations: E0596, E0658, E0764.
+For more information about an error, try `rustc --explain E0596`.
LL | const S: &'static mut str = &mut " hello ";
| ^^^^^^^^^^^^^^ cannot borrow as mutable
-error[E0080]: it is undefined behavior to use this value
- --> $DIR/issue-76510.rs:5:1
- |
-LL | const S: &'static mut str = &mut " hello ";
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed: encountered mutable reference in a `const`
- |
- = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
- = note: the raw bytes of the constant (size: 16, align: 8) {
- ╾───────alloc3────────╼ 07 00 00 00 00 00 00 00 │ ╾──────╼........
- }
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0080, E0596, E0658, E0764.
-For more information about an error, try `rustc --explain E0080`.
+Some errors have detailed explanations: E0596, E0658, E0764.
+For more information about an error, try `rustc --explain E0596`.
//~^ ERROR: mutable references are not allowed in the final value of constants
//~| ERROR: mutation through a reference is not allowed in constants
//~| ERROR: cannot borrow data in a `&` reference as mutable
-//~| ERROR: it is undefined behavior to use this value
const fn trigger() -> [(); unsafe {
let s = transmute::<(*const u8, usize), &ManuallyDrop<str>>((S.as_ptr(), 3));
|
= note: expected tuple `(usize,)`
found type `usize`
+help: use a trailing comma to create a tuple with one element
+ |
+LL | const TUP: (usize,) = (5usize << 64,);
+ | + ++
error: aborting due to previous error
LL | const fn test1<T: std::ops::Add>() {}
| ^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
LL | const fn test2(_x: &dyn Send) {}
| ^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
LL | const fn test3() -> &'static dyn Send { loop {} }
| ^^^^^^^^^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error: aborting due to 3 previous errors
}
pub const Q: i32 = match non_const() {
- //~^ ERROR calls in constants are limited to constant functions
+ //~^ ERROR cannot call non-const fn
Thing::This => 1,
Thing::That => 0
};
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `non_const` in constants
--> $DIR/issue-46843.rs:10:26
|
LL | pub const Q: i32 = match non_const() {
| ^^^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
}
unsafe { copy(src, dst, count) }
- //~^ ERROR calls in constant functions are limited to constant functions
+ //~^ ERROR cannot call non-const fn
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `copy::copy::<T>` in constant functions
--> $DIR/intrinsic_without_const_stab.rs:13:14
|
LL | unsafe { copy(src, dst, count) }
| ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
#[rustc_const_unstable(feature = "const_intrinsic_copy", issue = "80697")]
#[inline]
pub const unsafe fn stuff<T>(src: *const T, dst: *mut T, count: usize) {
- unsafe { copy(src, dst, count) } //~ ERROR calls in constant functions are limited
+ unsafe { copy(src, dst, count) } //~ ERROR cannot call non-const fn
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `copy::<T>` in constant functions
--> $DIR/intrinsic_without_const_stab_fail.rs:12:14
|
LL | unsafe { copy(src, dst, count) }
| ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
const X: u8 =
|| -> u8 { 5 }()
- //~^ ERROR calls in constants are limited to constant functions
+ //~^ ERROR cannot call non-const closure
;
fn main() {}
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const closure in constants
--> $DIR/issue-28113.rs:4:5
|
LL | || -> u8 { 5 }()
| ^^^^^^^^^^^^^^^^
+ |
+ = note: closures need an RFC before allowed to be called in constants
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
const bad_two : u32 = {
{
invalid();
- //~^ ERROR: calls in constants are limited to constant functions, tuple structs and tuple variants
+ //~^ ERROR: cannot call non-const fn `invalid`
0
}
};
static bad_five : u32 = {
{
invalid();
- //~^ ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants
+ //~^ ERROR: cannot call non-const fn `invalid`
0
}
};
static mut bad_eight : u32 = {
{
invalid();
- //~^ ERROR: calls in statics are limited to constant functions, tuple structs and tuple variants
+ //~^ ERROR: cannot call non-const fn `invalid`
0
}
};
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `invalid` in constants
--> $DIR/issue-32829-2.rs:10:9
|
LL | invalid();
| ^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `invalid` in statics
--> $DIR/issue-32829-2.rs:32:9
|
LL | invalid();
| ^^^^^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `invalid` in statics
--> $DIR/issue-32829-2.rs:54:9
|
LL | invalid();
| ^^^^^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error: aborting due to 3 previous errors
fn xyz() -> u8 { 42 }
const NUM: u8 = xyz();
-//~^ ERROR calls in constants are limited to constant functions, tuple structs and tuple variants
+//~^ ERROR cannot call non-const fn
fn main() {
match 1 {
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `xyz` in constants
--> $DIR/issue-43105.rs:3:17
|
LL | const NUM: u8 = xyz();
| ^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: could not evaluate constant pattern
--> $DIR/issue-43105.rs:8:9
#![feature(const_fn_fn_ptr_basics)]
const fn foo() { (||{})() }
-//~^ ERROR calls in constant functions
+//~^ ERROR cannot call non-const closure
const fn bad(input: fn()) {
input()
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const closure in constant functions
--> $DIR/issue-56164.rs:3:18
|
LL | const fn foo() { (||{})() }
| ^^^^^^^^
+ |
+ = note: closures need an RFC before allowed to be called in constant functions
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: function pointers are not allowed in const fn
--> $DIR/issue-56164.rs:7:5
// in the length part of an array.
struct Bug {
- a: [(); (|| { 0 })()] //~ ERROR calls in constants are limited to
+ a: [(); (|| { 0 })()] //~ ERROR cannot call non-const closure
}
fn main() {}
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const closure in constants
--> $DIR/issue-68542-closure-in-array-len.rs:6:13
|
LL | a: [(); (|| { 0 })()]
| ^^^^^^^^^^^^
+ |
+ = note: closures need an RFC before allowed to be called in constants
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
-const FOO: *const u32 = { //~ ERROR encountered dangling pointer in final constant
+const FOO: *const u32 = {
let x;
&x //~ ERROR borrow of possibly-uninitialized variable: `x`
};
LL | &x
| ^^ use of possibly-uninitialized `x`
-error: encountered dangling pointer in final constant
- --> $DIR/issue-78655.rs:1:1
- |
-LL | / const FOO: *const u32 = {
-LL | | let x;
-LL | | &x
-LL | | };
- | |__^
-
error: could not evaluate constant pattern
--> $DIR/issue-78655.rs:7:9
|
LL | let FOO = FOO;
| ^^^
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0381`.
const fn f(a: &u8, b: &u8) -> bool {
*a == *b
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
}
const fn g(a: &&&&i64, b: &&&&i64) -> bool {
****a == ****b
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
}
const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
if *l == *r {
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
a = at;
b = bt;
const fn f(a: &u8, b: &u8) -> bool {
a == b
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
}
const fn g(a: &&&&i64, b: &&&&i64) -> bool {
a == b
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
}
const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
if l == r {
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~^ ERROR: cannot call non-const operator in constant functions [E0015]
//~| HELP: consider dereferencing here
a = at;
b = bt;
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const operator in constant functions
--> $DIR/issue-90870.rs:8:5
|
LL | a == b
| ^^^^^^
|
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: consider dereferencing here
|
LL | *a == *b
| + +
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const operator in constant functions
--> $DIR/issue-90870.rs:14:5
|
LL | a == b
| ^^^^^^
|
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: consider dereferencing here
|
LL | ****a == ****b
| ++++ ++++
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const operator in constant functions
--> $DIR/issue-90870.rs:21:12
|
LL | if l == r {
| ^^^^^^
|
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
help: consider dereferencing here
|
LL | if *l == *r {
--- /dev/null
+// Regression test for issue #91560.
+
+// run-rustfix
+
+#![allow(unused,non_upper_case_globals)]
+
+fn foo() {
+ const length: usize = 2;
+ //~^ HELP: consider using `const`
+ let arr = [0; length];
+ //~^ ERROR: attempt to use a non-constant value in a constant [E0435]
+}
+
+fn bar() {
+ const length: usize = 2;
+ //~^ HELP: consider using `const`
+ let arr = [0; length];
+ //~^ ERROR: attempt to use a non-constant value in a constant [E0435]
+}
+
+fn main() {}
--- /dev/null
+// Regression test for issue #91560.
+
+// run-rustfix
+
+#![allow(unused,non_upper_case_globals)]
+
+fn foo() {
+ let mut length: usize = 2;
+ //~^ HELP: consider using `const`
+ let arr = [0; length];
+ //~^ ERROR: attempt to use a non-constant value in a constant [E0435]
+}
+
+fn bar() {
+ let length: usize = 2;
+ //~^ HELP: consider using `const`
+ let arr = [0; length];
+ //~^ ERROR: attempt to use a non-constant value in a constant [E0435]
+}
+
+fn main() {}
--- /dev/null
+error[E0435]: attempt to use a non-constant value in a constant
+ --> $DIR/issue-91560.rs:10:19
+ |
+LL | let mut length: usize = 2;
+ | -------------- help: consider using `const` instead of `let`: `const length`
+LL |
+LL | let arr = [0; length];
+ | ^^^^^^ non-constant value
+
+error[E0435]: attempt to use a non-constant value in a constant
+ --> $DIR/issue-91560.rs:17:19
+ |
+LL | let length: usize = 2;
+ | ------------ help: consider using `const` instead of `let`: `const length`
+LL |
+LL | let arr = [0; length];
+ | ^^^^^^ non-constant value
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0435`.
const fn foo(a: i32) -> Vec<i32> {
vec![1, 2, 3]
//~^ ERROR allocations are not allowed
- //~| ERROR calls in constant functions
+ //~| ERROR cannot call non-const fn
}
fn main() {}
|
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `slice::<impl [i32]>::into_vec::<std::alloc::Global>` in constant functions
--> $DIR/bad_const_fn_body_ice.rs:2:5
|
LL | vec![1, 2, 3]
| ^^^^^^^^^^^^^
|
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
= note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
LL | const fn foo11<T: std::fmt::Display>(t: T) -> T { t }
| ^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
LL | const fn foo11_2<T: Send>(t: T) -> T { t }
| ^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0013]: constant functions cannot refer to statics
LL | const fn foo(&self) {}
| ------------------- function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
LL | const fn foo2(&self) {}
| -------------------- function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
LL | const fn foo3(&self) {}
| -------------------- function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait bounds other than `Sized` on const fn parameters are unstable
LL | const fn no_apit2(_x: AlanTuring<impl std::fmt::Debug>) {}
| ^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0493]: destructors cannot be evaluated at compile-time
LL | const fn no_apit(_x: impl std::fmt::Debug) {}
| ^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0493]: destructors cannot be evaluated at compile-time
LL | const fn no_dyn_trait(_x: &dyn std::fmt::Debug) {}
| ^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
| |
| function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
| |
| function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
| |
| function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: function pointers cannot appear in constant functions
LL | x.0.field;
| ^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error[E0658]: trait objects in const fn are unstable
| |
| function declared as const here
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error: aborting due to 2 previous errors
}
static foo: Foo = bar();
-//~^ ERROR calls in statics are limited to constant functions, tuple structs and tuple variants
+//~^ ERROR cannot call non-const fn
fn main() {}
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `bar` in statics
--> $DIR/mir_check_nonconst.rs:8:19
|
LL | static foo: Foo = bar();
| ^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
--- /dev/null
+#![allow(unused)]
+
+const fn f<T>(x: T) { //~ WARN function cannot return without recursing
+ f(x);
+ //~^ ERROR any use of this value will cause an error
+ //~| WARN this was previously accepted by the compiler
+}
+
+const X: () = f(1);
+
+fn main() {}
--- /dev/null
+warning: function cannot return without recursing
+ --> $DIR/recursive.rs:3:1
+ |
+LL | const fn f<T>(x: T) {
+ | ^^^^^^^^^^^^^^^^^^^ cannot return without recursing
+LL | f(x);
+ | ---- recursive call site
+ |
+ = note: `#[warn(unconditional_recursion)]` on by default
+ = help: a `loop` may express intention better if this is on purpose
+
+error: any use of this value will cause an error
+ --> $DIR/recursive.rs:4:5
+ |
+LL | f(x);
+ | ^^^^
+ | |
+ | reached the configured maximum number of stack frames
+ | inside `f::<i32>` at $DIR/recursive.rs:4:5
+ | [... 126 additional calls inside `f::<i32>` at $DIR/recursive.rs:4:5 ...]
+ | inside `X` at $DIR/recursive.rs:9:15
+...
+LL | const X: () = f(1);
+ | -------------------
+ |
+ = note: `#[deny(const_err)]` on by default
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
+
+error: aborting due to previous error; 1 warning emitted
+
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const closure in constant functions
--> $DIR/unstable-const-fn-in-libcore.rs:24:26
|
LL | Opt::None => f(),
| ^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+help: consider further restricting this bound
+ |
+LL | const fn unwrap_or_else<F: FnOnce() -> T + ~const std::ops::FnOnce<()>>(self, f: F) -> T {
+ | +++++++++++++++++++++++++++++
error[E0493]: destructors cannot be evaluated at compile-time
--> $DIR/unstable-const-fn-in-libcore.rs:19:53
--- /dev/null
+use std::collections::HashSet;
+
+/// natural case from the issue
+struct Value(u32);
+
+fn main() {
+ let hs = HashSet::<Value>::new();
+ hs.insert(Value(0)); //~ ERROR
+}
+
+/// synthetic cases
+pub struct NoDerives;
+
+struct Object<T>(T);
+impl<T: Eq> Object<T> {
+ fn use_eq(&self) {}
+}
+impl<T: Ord> Object<T> {
+ fn use_ord(&self) {}
+}
+impl<T: Ord + PartialOrd> Object<T> {
+ fn use_ord_and_partial_ord(&self) {}
+}
+
+fn function(foo: Object<NoDerives>) {
+ foo.use_eq(); //~ ERROR
+ foo.use_ord(); //~ ERROR
+ foo.use_ord_and_partial_ord(); //~ ERROR
+}
--- /dev/null
+error[E0599]: the method `insert` exists for struct `HashSet<Value>`, but its trait bounds were not satisfied
+ --> $DIR/issue-91550.rs:8:8
+ |
+LL | struct Value(u32);
+ | ------------------
+ | |
+ | doesn't satisfy `Value: Eq`
+ | doesn't satisfy `Value: Hash`
+...
+LL | hs.insert(Value(0));
+ | ^^^^^^ method cannot be called on `HashSet<Value>` due to unsatisfied trait bounds
+ |
+ = note: the following trait bounds were not satisfied:
+ `Value: Eq`
+ `Value: Hash`
+help: consider annotating `Value` with `#[derive(Eq, Hash, PartialEq)]`
+ |
+LL | #[derive(Eq, Hash, PartialEq)]
+ |
+
+error[E0599]: the method `use_eq` exists for struct `Object<NoDerives>`, but its trait bounds were not satisfied
+ --> $DIR/issue-91550.rs:26:9
+ |
+LL | pub struct NoDerives;
+ | --------------------- doesn't satisfy `NoDerives: Eq`
+LL |
+LL | struct Object<T>(T);
+ | -------------------- method `use_eq` not found for this
+...
+LL | foo.use_eq();
+ | ^^^^^^ method cannot be called on `Object<NoDerives>` due to unsatisfied trait bounds
+ |
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: Eq`
+help: consider annotating `NoDerives` with `#[derive(Eq, PartialEq)]`
+ |
+LL | #[derive(Eq, PartialEq)]
+ |
+
+error[E0599]: the method `use_ord` exists for struct `Object<NoDerives>`, but its trait bounds were not satisfied
+ --> $DIR/issue-91550.rs:27:9
+ |
+LL | pub struct NoDerives;
+ | --------------------- doesn't satisfy `NoDerives: Ord`
+LL |
+LL | struct Object<T>(T);
+ | -------------------- method `use_ord` not found for this
+...
+LL | foo.use_ord();
+ | ^^^^^^^ method cannot be called on `Object<NoDerives>` due to unsatisfied trait bounds
+ |
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: Ord`
+help: consider annotating `NoDerives` with `#[derive(Eq, Ord, PartialEq, PartialOrd)]`
+ |
+LL | #[derive(Eq, Ord, PartialEq, PartialOrd)]
+ |
+
+error[E0599]: the method `use_ord_and_partial_ord` exists for struct `Object<NoDerives>`, but its trait bounds were not satisfied
+ --> $DIR/issue-91550.rs:28:9
+ |
+LL | pub struct NoDerives;
+ | ---------------------
+ | |
+ | doesn't satisfy `NoDerives: Ord`
+ | doesn't satisfy `NoDerives: PartialOrd`
+LL |
+LL | struct Object<T>(T);
+ | -------------------- method `use_ord_and_partial_ord` not found for this
+...
+LL | foo.use_ord_and_partial_ord();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ method cannot be called on `Object<NoDerives>` due to unsatisfied trait bounds
+ |
+ = note: the following trait bounds were not satisfied:
+ `NoDerives: Ord`
+ `NoDerives: PartialOrd`
+help: consider annotating `NoDerives` with `#[derive(Eq, Ord, PartialEq, PartialOrd)]`
+ |
+LL | #[derive(Eq, Ord, PartialEq, PartialOrd)]
+ |
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
// run-pass
-// ignore-compare-mode-chalk
pub trait DeclaredTrait {
type Type;
}
<i8 as Foo<u16>>
<i8 as Foo<u32>>
<i8 as Foo<u64>>
- <i8 as Foo<u8>>
+ and 5 others
error[E0277]: the trait bound `u8: Foo<i32>` is not satisfied
--> $DIR/issue-39802-show-5-trait-impls.rs:25:21
<u8 as Foo<u16>>
<u8 as Foo<u32>>
<u8 as Foo<u64>>
+ and 5 others
error[E0277]: the trait bound `bool: Foo<i32>` is not satisfied
--> $DIR/issue-39802-show-5-trait-impls.rs:26:21
|
LL | 1u32 as char;
| ^^^^^^^^^^^^ invalid cast
+ |
+help: try `char::from_u32` instead
+ --> $DIR/E0604.rs:2:5
+ |
+LL | 1u32 as char;
+ | ^^^^^^^^^^^^
error: aborting due to previous error
|
LL | 0u32 as char;
| ^^^^^^^^^^^^ invalid cast
+ |
+help: try `char::from_u32` instead
+ --> $DIR/error-festival.rs:25:5
+ |
+LL | 0u32 as char;
+ | ^^^^^^^^^^^^
error[E0605]: non-primitive cast: `u8` as `Vec<u8>`
--> $DIR/error-festival.rs:29:5
+++ /dev/null
-// check that #[allow_fail] is feature-gated
-
-#[allow_fail] //~ ERROR the `#[allow_fail]` attribute is an experimental feature
-fn ok_to_fail() {
- assert!(false);
-}
-
-fn main() {}
+++ /dev/null
-error[E0658]: the `#[allow_fail]` attribute is an experimental feature
- --> $DIR/feature-gate-allow_fail.rs:3:1
- |
-LL | #[allow_fail]
- | ^^^^^^^^^^^^^
- |
- = note: see issue #46488 <https://github.com/rust-lang/rust/issues/46488> for more information
- = help: add `#![feature(allow_fail)]` to the crate attributes to enable
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0658`.
LL | asm!("mov eax, {}", const 123);
| ^^^^^^^^^
|
- = note: see issue #72016 <https://github.com/rust-lang/rust/issues/72016> for more information
+ = note: see issue #93332 <https://github.com/rust-lang/rust/issues/93332> for more information
= help: add `#![feature(asm_const)]` to the crate attributes to enable
error: aborting due to previous error
LL | asm!("");
| ^^^^^^^^
|
- = note: see issue #72016 <https://github.com/rust-lang/rust/issues/72016> for more information
+ = note: see issue #93335 <https://github.com/rust-lang/rust/issues/93335> for more information
= help: add `#![feature(asm_experimental_arch)]` to the crate attributes to enable
error: aborting due to previous error
LL | asm!("mov eax, {}", sym main);
| ^^^^^^^^
|
- = note: see issue #72016 <https://github.com/rust-lang/rust/issues/72016> for more information
+ = note: see issue #93333 <https://github.com/rust-lang/rust/issues/93333> for more information
= help: add `#![feature(asm_sym)]` to the crate attributes to enable
error: aborting due to previous error
LL | asm!("", options(may_unwind));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #72016 <https://github.com/rust-lang/rust/issues/72016> for more information
+ = note: see issue #93334 <https://github.com/rust-lang/rust/issues/93334> for more information
= help: add `#![feature(asm_unwind)]` to the crate attributes to enable
error: aborting due to previous error
--- /dev/null
+fn main() {
+ cfg!(target_has_atomic_equal_alignment = "8");
+ //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ cfg!(target_has_atomic_equal_alignment = "16");
+ //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ cfg!(target_has_atomic_equal_alignment = "32");
+ //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ cfg!(target_has_atomic_equal_alignment = "64");
+ //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ cfg!(target_has_atomic_equal_alignment = "128");
+ //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ cfg!(target_has_atomic_equal_alignment = "ptr");
+ //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+}
--- /dev/null
+error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg-target-has-atomic-equal-alignment.rs:2:10
+ |
+LL | cfg!(target_has_atomic_equal_alignment = "8");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93822 <https://github.com/rust-lang/rust/issues/93822> for more information
+ = help: add `#![feature(cfg_target_has_atomic_equal_alignment)]` to the crate attributes to enable
+
+error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg-target-has-atomic-equal-alignment.rs:4:10
+ |
+LL | cfg!(target_has_atomic_equal_alignment = "16");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93822 <https://github.com/rust-lang/rust/issues/93822> for more information
+ = help: add `#![feature(cfg_target_has_atomic_equal_alignment)]` to the crate attributes to enable
+
+error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg-target-has-atomic-equal-alignment.rs:6:10
+ |
+LL | cfg!(target_has_atomic_equal_alignment = "32");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93822 <https://github.com/rust-lang/rust/issues/93822> for more information
+ = help: add `#![feature(cfg_target_has_atomic_equal_alignment)]` to the crate attributes to enable
+
+error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg-target-has-atomic-equal-alignment.rs:8:10
+ |
+LL | cfg!(target_has_atomic_equal_alignment = "64");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93822 <https://github.com/rust-lang/rust/issues/93822> for more information
+ = help: add `#![feature(cfg_target_has_atomic_equal_alignment)]` to the crate attributes to enable
+
+error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg-target-has-atomic-equal-alignment.rs:10:10
+ |
+LL | cfg!(target_has_atomic_equal_alignment = "128");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93822 <https://github.com/rust-lang/rust/issues/93822> for more information
+ = help: add `#![feature(cfg_target_has_atomic_equal_alignment)]` to the crate attributes to enable
+
+error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
+ --> $DIR/feature-gate-cfg-target-has-atomic-equal-alignment.rs:12:10
+ |
+LL | cfg!(target_has_atomic_equal_alignment = "ptr");
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93822 <https://github.com/rust-lang/rust/issues/93822> for more information
+ = help: add `#![feature(cfg_target_has_atomic_equal_alignment)]` to the crate attributes to enable
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
-#![feature(intrinsics, lang_items, no_core, rustc_attrs)]
-
-#![crate_type="rlib"]
-#![no_core]
-
-extern "rust-intrinsic" {
- fn atomic_xadd<T>(dst: *mut T, src: T) -> T;
-}
-
-#[lang = "sized"]
-trait Sized {}
-#[lang = "copy"]
-trait Copy {}
-
-#[cfg(target_has_atomic = "8")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_u8(x: *mut u8) {
- atomic_xadd(x, 1);
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "8")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_i8(x: *mut i8) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "16")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_u16(x: *mut u16) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "16")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_i16(x: *mut i16) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "32")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_u32(x: *mut u32) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "32")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_i32(x: *mut i32) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "64")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_u64(x: *mut u64) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "64")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_i64(x: *mut i64) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "128")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_u128(x: *mut u128) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "128")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_i128(x: *mut i128) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "ptr")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_usize(x: *mut usize) {
- atomic_xadd(x, 1);
-}
-#[cfg(target_has_atomic = "ptr")]
-//~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
-pub unsafe fn atomic_isize(x: *mut isize) {
- atomic_xadd(x, 1);
-}
-
fn main() {
- cfg!(target_has_atomic = "8");
- //~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
- cfg!(target_has_atomic = "16");
- //~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
- cfg!(target_has_atomic = "32");
- //~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
- cfg!(target_has_atomic = "64");
- //~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
- cfg!(target_has_atomic = "128");
- //~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
- cfg!(target_has_atomic = "ptr");
- //~^ ERROR `cfg(target_has_atomic)` is experimental and subject to change
cfg!(target_has_atomic_load_store = "8");
//~^ ERROR `cfg(target_has_atomic_load_store)` is experimental and subject to change
cfg!(target_has_atomic_load_store = "16");
//~^ ERROR `cfg(target_has_atomic_load_store)` is experimental and subject to change
cfg!(target_has_atomic_load_store = "ptr");
//~^ ERROR `cfg(target_has_atomic_load_store)` is experimental and subject to change
- cfg!(target_has_atomic_equal_alignment = "8");
- //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- cfg!(target_has_atomic_equal_alignment = "16");
- //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- cfg!(target_has_atomic_equal_alignment = "32");
- //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- cfg!(target_has_atomic_equal_alignment = "64");
- //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- cfg!(target_has_atomic_equal_alignment = "128");
- //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- cfg!(target_has_atomic_equal_alignment = "ptr");
- //~^ ERROR `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
}
-
-#[macro_export]
-#[rustc_builtin_macro]
-macro_rules! cfg { () => () }
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:15:7
- |
-LL | #[cfg(target_has_atomic = "8")]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:21:7
- |
-LL | #[cfg(target_has_atomic = "8")]
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:26:7
- |
-LL | #[cfg(target_has_atomic = "16")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:31:7
- |
-LL | #[cfg(target_has_atomic = "16")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:36:7
- |
-LL | #[cfg(target_has_atomic = "32")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:41:7
- |
-LL | #[cfg(target_has_atomic = "32")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:46:7
- |
-LL | #[cfg(target_has_atomic = "64")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:51:7
- |
-LL | #[cfg(target_has_atomic = "64")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:56:7
- |
-LL | #[cfg(target_has_atomic = "128")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:61:7
- |
-LL | #[cfg(target_has_atomic = "128")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:66:7
- |
-LL | #[cfg(target_has_atomic = "ptr")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:71:7
- |
-LL | #[cfg(target_has_atomic = "ptr")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:78:10
- |
-LL | cfg!(target_has_atomic = "8");
- | ^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:80:10
- |
-LL | cfg!(target_has_atomic = "16");
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:82:10
- |
-LL | cfg!(target_has_atomic = "32");
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:84:10
- |
-LL | cfg!(target_has_atomic = "64");
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:86:10
- |
-LL | cfg!(target_has_atomic = "128");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:88:10
- |
-LL | cfg!(target_has_atomic = "ptr");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
error[E0658]: `cfg(target_has_atomic_load_store)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:90:10
+ --> $DIR/feature-gate-cfg-target-has-atomic.rs:2:10
|
LL | cfg!(target_has_atomic_load_store = "8");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
+ = note: see issue #94039 <https://github.com/rust-lang/rust/issues/94039> for more information
= help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
error[E0658]: `cfg(target_has_atomic_load_store)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:92:10
+ --> $DIR/feature-gate-cfg-target-has-atomic.rs:4:10
|
LL | cfg!(target_has_atomic_load_store = "16");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
+ = note: see issue #94039 <https://github.com/rust-lang/rust/issues/94039> for more information
= help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
error[E0658]: `cfg(target_has_atomic_load_store)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:94:10
+ --> $DIR/feature-gate-cfg-target-has-atomic.rs:6:10
|
LL | cfg!(target_has_atomic_load_store = "32");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
+ = note: see issue #94039 <https://github.com/rust-lang/rust/issues/94039> for more information
= help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
error[E0658]: `cfg(target_has_atomic_load_store)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:96:10
+ --> $DIR/feature-gate-cfg-target-has-atomic.rs:8:10
|
LL | cfg!(target_has_atomic_load_store = "64");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
+ = note: see issue #94039 <https://github.com/rust-lang/rust/issues/94039> for more information
= help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
error[E0658]: `cfg(target_has_atomic_load_store)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:98:10
+ --> $DIR/feature-gate-cfg-target-has-atomic.rs:10:10
|
LL | cfg!(target_has_atomic_load_store = "128");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
+ = note: see issue #94039 <https://github.com/rust-lang/rust/issues/94039> for more information
= help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
error[E0658]: `cfg(target_has_atomic_load_store)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:100:10
+ --> $DIR/feature-gate-cfg-target-has-atomic.rs:12:10
|
LL | cfg!(target_has_atomic_load_store = "ptr");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:102:10
- |
-LL | cfg!(target_has_atomic_equal_alignment = "8");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:104:10
- |
-LL | cfg!(target_has_atomic_equal_alignment = "16");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:106:10
- |
-LL | cfg!(target_has_atomic_equal_alignment = "32");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:108:10
- |
-LL | cfg!(target_has_atomic_equal_alignment = "64");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:110:10
- |
-LL | cfg!(target_has_atomic_equal_alignment = "128");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
- = help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-
-error[E0658]: `cfg(target_has_atomic_equal_alignment)` is experimental and subject to change
- --> $DIR/feature-gate-cfg-target-has-atomic.rs:112:10
- |
-LL | cfg!(target_has_atomic_equal_alignment = "ptr");
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: see issue #32976 <https://github.com/rust-lang/rust/issues/32976> for more information
+ = note: see issue #94039 <https://github.com/rust-lang/rust/issues/94039> for more information
= help: add `#![feature(cfg_target_has_atomic)]` to the crate attributes to enable
-error: aborting due to 30 previous errors
+error: aborting due to 6 previous errors
For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+// edition:2018
+#![forbid(incomplete_features, unsafe_code)]
+#![feature(unsafe_pin_internals)]
+//~^ ERROR the feature `unsafe_pin_internals` is incomplete and may not be safe to use
+
+use core::{marker::PhantomPinned, pin::Pin};
+
+/// The `unsafe_pin_internals` is indeed unsound.
+fn non_unsafe_pin_new_unchecked<T>(pointer: &mut T) -> Pin<&mut T> {
+ Pin { pointer }
+}
+
+fn main() {
+ let mut self_referential = PhantomPinned;
+ let _: Pin<&mut PhantomPinned> = non_unsafe_pin_new_unchecked(&mut self_referential);
+ core::mem::forget(self_referential); // move and disable drop glue!
+}
--- /dev/null
+error: the feature `unsafe_pin_internals` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/feature-gate-unsafe_pin_internals.rs:3:12
+ |
+LL | #![feature(unsafe_pin_internals)]
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/feature-gate-unsafe_pin_internals.rs:2:11
+ |
+LL | #![forbid(incomplete_features, unsafe_code)]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
}
union U3 {
- a: String, //~ ERROR unions may not contain fields that need dropping
+ a: String, //~ ERROR unions cannot contain fields that may need dropping
}
union U32 { // field that does not drop but is not `Copy`, either -- this is the real feature gate test!
}
union U4<T> {
- a: T, //~ ERROR unions may not contain fields that need dropping
+ a: T, //~ ERROR unions cannot contain fields that may need dropping
}
union U5 { // Having a drop impl is OK
= note: see issue #55149 <https://github.com/rust-lang/rust/issues/55149> for more information
= help: add `#![feature(untagged_unions)]` to the crate attributes to enable
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/feature-gate-untagged_unions.rs:16:5
|
LL | a: String,
| ^^^^^^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<String>,
| +++++++++++++++++++++++ +
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/feature-gate-untagged_unions.rs:24:5
|
LL | a: T,
| ^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<T>,
| +++++++++++++++++++++++ +
--- /dev/null
+#[used(linker)] //~ ERROR `#[used(linker)]` is currently unstable
+static mut USED_LINKER: [usize; 1] = [0];
+
+#[used(compiler)] //~ ERROR `#[used(compiler)]` is currently unstable
+static mut USED_COMPILER: [usize; 1] = [0];
+
+fn main() {}
--- /dev/null
+error[E0658]: `#[used(linker)]` is currently unstable
+ --> $DIR/feature-gate-used_with_arg.rs:1:1
+ |
+LL | #[used(linker)]
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93798 <https://github.com/rust-lang/rust/issues/93798> for more information
+ = help: add `#![feature(used_with_arg)]` to the crate attributes to enable
+
+error[E0658]: `#[used(compiler)]` is currently unstable
+ --> $DIR/feature-gate-used_with_arg.rs:4:1
+ |
+LL | #[used(compiler)]
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: see issue #93798 <https://github.com/rust-lang/rust/issues/93798> for more information
+ = help: add `#![feature(used_with_arg)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+fn main() {
+ let a = "a";
+ let b = "b";
+
+ println!("{a} {b} {} {} {c} {}", c = "c");
+ //~^ ERROR: invalid reference to positional arguments 1 and 2 (there is 1 argument)
+
+ let n = 1;
+ println!("{a:.n$} {b:.*}");
+ //~^ ERROR: invalid reference to positional argument 0 (no arguments were given)
+}
--- /dev/null
+error: invalid reference to positional arguments 1 and 2 (there is 1 argument)
+ --> $DIR/format-args-capture-issue-93378.rs:5:26
+ |
+LL | println!("{a} {b} {} {} {c} {}", c = "c");
+ | ^^ ^^
+ |
+ = note: positional arguments are zero-based
+
+error: invalid reference to positional argument 0 (no arguments were given)
+ --> $DIR/format-args-capture-issue-93378.rs:9:23
+ |
+LL | println!("{a:.n$} {b:.*}");
+ | - ^^^--^
+ | | |
+ | | this precision flag adds an extra required argument at position 0, which is why there are 3 arguments expected
+ | this parameter corresponds to the precision flag
+ |
+ = note: positional arguments are zero-based
+ = note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+fn main() {
+ const FOO: i32 = 123;
+ println!("{foo:X}");
+ //~^ ERROR: cannot find value `foo` in this scope
+ println!("{:.foo$}", 0);
+ //~^ ERROR: cannot find value `foo` in this scope
+}
--- /dev/null
+error[E0425]: cannot find value `foo` in this scope
+ --> $DIR/format-args-capture-issue-94010.rs:3:16
+ |
+LL | const FOO: i32 = 123;
+ | --------------------- similarly named constant `FOO` defined here
+LL | println!("{foo:X}");
+ | ^^^ help: a constant with a similar name exists (notice the capitalization): `FOO`
+
+error[E0425]: cannot find value `foo` in this scope
+ --> $DIR/format-args-capture-issue-94010.rs:5:18
+ |
+LL | const FOO: i32 = 123;
+ | --------------------- similarly named constant `FOO` defined here
+...
+LL | println!("{:.foo$}", 0);
+ | ^^^ help: a constant with a similar name exists (notice the capitalization): `FOO`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0425`.
| formatting specifier missing
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:2:17
+ --> $DIR/format-args-capture-missing-variables.rs:2:18
|
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `bar` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:2:26
+ --> $DIR/format-args-capture-missing-variables.rs:2:27
|
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:6:14
+ --> $DIR/format-args-capture-missing-variables.rs:6:15
|
LL | format!("{foo}");
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `valueb` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:8:23
+ --> $DIR/format-args-capture-missing-variables.rs:8:24
|
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
- | ^^^^^^^^ not found in this scope
+ | ^^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:14:9
+ --> $DIR/format-args-capture-missing-variables.rs:14:10
|
LL | {foo}
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/format-args-capture-missing-variables.rs:19:13
+ --> $DIR/format-args-capture-missing-variables.rs:19:14
|
LL | panic!("{foo} {bar}", bar=1);
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error: aborting due to 7 previous errors
named_argument_takes_precedence_to_captured();
formatting_parameters_can_be_captured();
capture_raw_strings_and_idents();
+ repeated_capture();
#[cfg(panic = "unwind")]
{
let s = format!("{x:-^width$.precision$}");
assert_eq!(&s, "--7.000--");
}
+
+fn repeated_capture() {
+ let a = 1;
+ let b = 2;
+ let s = format!("{a} {b} {a}");
+ assert_eq!(&s, "1 2 1");
+}
= note: for information about formatting flags, visit https://doc.rust-lang.org/std/fmt/index.html
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/ifmt-bad-arg.rs:27:17
+ --> $DIR/ifmt-bad-arg.rs:27:18
|
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `bar` in this scope
- --> $DIR/ifmt-bad-arg.rs:27:26
+ --> $DIR/ifmt-bad-arg.rs:27:27
|
LL | format!("{} {foo} {} {bar} {}", 1, 2, 3);
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/ifmt-bad-arg.rs:31:14
+ --> $DIR/ifmt-bad-arg.rs:31:15
|
LL | format!("{foo}");
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0425]: cannot find value `valueb` in this scope
- --> $DIR/ifmt-bad-arg.rs:45:23
+ --> $DIR/ifmt-bad-arg.rs:45:24
|
LL | format!("{valuea} {valueb}", valuea=5, valuec=7);
- | ^^^^^^^^ not found in this scope
+ | ^^^^^^ not found in this scope
error[E0425]: cannot find value `foo` in this scope
- --> $DIR/ifmt-bad-arg.rs:60:9
+ --> $DIR/ifmt-bad-arg.rs:60:10
|
LL | {foo}
- | ^^^^^ not found in this scope
+ | ^^^ not found in this scope
error[E0308]: mismatched types
--> $DIR/ifmt-bad-arg.rs:78:32
--- /dev/null
+struct Struct<T>(T);
+impl Struct<T>
+//~^ ERROR cannot find type `T` in this scope
+//~| NOTE not found in this scope
+//~| HELP you might be missing a type parameter
+where
+ T: Copy,
+ //~^ ERROR cannot find type `T` in this scope
+ //~| NOTE not found in this scope
+{
+ // The part where it claims that there is no method named `len` is a bug. Feel free to fix it.
+ // This test is intended to ensure that a different bug, where it claimed
+ // that `v` was a function, does not regress.
+ fn method(v: Vec<u8>) { v.len(); }
+ //~^ ERROR type annotations needed
+ //~| NOTE cannot infer type
+ //~| NOTE type must be known at this point
+ //~| ERROR no method named `len`
+ //~| NOTE private field, not a method
+}
+
+fn main() {}
--- /dev/null
+error[E0412]: cannot find type `T` in this scope
+ --> $DIR/fn-help-with-err-generic-is-not-function.rs:2:13
+ |
+LL | impl Struct<T>
+ | - ^ not found in this scope
+ | |
+ | help: you might be missing a type parameter: `<T>`
+
+error[E0412]: cannot find type `T` in this scope
+ --> $DIR/fn-help-with-err-generic-is-not-function.rs:7:5
+ |
+LL | T: Copy,
+ | ^ not found in this scope
+
+error[E0282]: type annotations needed
+ --> $DIR/fn-help-with-err-generic-is-not-function.rs:14:31
+ |
+LL | fn method(v: Vec<u8>) { v.len(); }
+ | ^^^ cannot infer type
+ |
+ = note: type must be known at this point
+
+error[E0599]: no method named `len` found for struct `Vec<u8>` in the current scope
+ --> $DIR/fn-help-with-err-generic-is-not-function.rs:14:31
+ |
+LL | fn method(v: Vec<u8>) { v.len(); }
+ | ^^^ private field, not a method
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0282, E0412, E0599.
+For more information about an error, try `rustc --explain E0282`.
--- /dev/null
+// This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
+fn main() {
+ let arc = std::sync::Arc::new(oops);
+ //~^ ERROR cannot find value `oops` in this scope
+ //~| NOTE not found
+ // The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear.
+ arc.blablabla();
+ //~^ ERROR no method named `blablabla`
+ //~| NOTE method not found
+ let arc2 = std::sync::Arc::new(|| 1);
+ // The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear
+ arc2.blablabla();
+ //~^ ERROR no method named `blablabla`
+ //~| NOTE method not found
+ //~| NOTE `arc2` is a function, perhaps you wish to call it
+}
--- /dev/null
+error[E0425]: cannot find value `oops` in this scope
+ --> $DIR/fn-help-with-err.rs:3:35
+ |
+LL | let arc = std::sync::Arc::new(oops);
+ | ^^^^ not found in this scope
+
+error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
+ --> $DIR/fn-help-with-err.rs:7:9
+ |
+LL | arc.blablabla();
+ | ^^^^^^^^^ method not found in `Arc<_>`
+
+error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>` in the current scope
+ --> $DIR/fn-help-with-err.rs:12:10
+ |
+LL | arc2.blablabla();
+ | ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
+ |
+ = note: `arc2` is a function, perhaps you wish to call it
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0425, E0599.
+For more information about an error, try `rustc --explain E0425`.
// build-pass
+// compile-flags: -Zdrop-tracking
// FIXME(eholk): temporarily disabled while drop range tracking is disabled
// (see generator_interior.rs:27)
};
}
+fn loop_continue(b: bool) {
+ let _ = || {
+ let mut arr = [Ptr];
+ let mut count = 0;
+ drop(arr);
+ while count < 3 {
+ count += 1;
+ yield;
+ if b {
+ arr = [Ptr];
+ continue;
+ }
+ }
+ };
+}
+
fn main() {
one_armed_if(true);
if_let(Some(41));
reinit();
loop_uninit();
nested_loop();
+ loop_continue(true);
}
--- /dev/null
+// check-fail
+
+// This should pass, but it requires `Sized` to be coinductive.
+
+#![feature(generic_associated_types)]
+
+trait Allocator {
+ type Allocated<T>;
+}
+
+enum LinkedList<A: Allocator> {
+ Head,
+ Next(A::Allocated<Self>)
+ //~^ overflow
+}
+
+fn main() {}
--- /dev/null
+error[E0275]: overflow evaluating the requirement `LinkedList<A>: Sized`
+ --> $DIR/issue-80626.rs:13:10
+ |
+LL | Next(A::Allocated<Self>)
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: no field of an enum variant may have a dynamically sized type
+ = help: change the field's type to have a statically known size
+help: borrowed types always have a statically known size
+ |
+LL | Next(&A::Allocated<Self>)
+ | +
+help: the `Box` type always has a statically known size and allocates its contents in the heap
+ |
+LL | Next(Box<A::Allocated<Self>>)
+ | ++++ +
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
--- /dev/null
+// check-fail
+
+// This should pass, but seems to run into a TAIT issue.
+
+#![feature(generic_associated_types)]
+#![feature(type_alias_impl_trait)]
+
+pub trait Stream {
+ type Item;
+}
+
+impl Stream for () {
+ type Item = i32;
+}
+
+trait Yay<AdditionalValue> {
+ type InnerStream<'s>: Stream<Item = i32> + 's;
+ fn foo<'s>() -> Self::InnerStream<'s>;
+}
+
+impl<'a> Yay<&'a ()> for () {
+ type InnerStream<'s> = impl Stream<Item = i32> + 's;
+ //~^ the type
+ fn foo<'s>() -> Self::InnerStream<'s> { todo!() }
+}
+
+fn main() {}
--- /dev/null
+error[E0477]: the type `impl Stream<Item = i32>` does not fulfill the required lifetime
+ --> $DIR/issue-86218.rs:22:28
+ |
+LL | type InnerStream<'s> = impl Stream<Item = i32> + 's;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must outlive the lifetime `'s` as defined here as required by this binding
+ --> $DIR/issue-86218.rs:22:22
+ |
+LL | type InnerStream<'s> = impl Stream<Item = i32> + 's;
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0477`.
--- /dev/null
+// check-fail
+
+// This should pass, but we need an extension of implied bounds (probably).
+
+#![feature(generic_associated_types)]
+
+pub trait AsRef2 {
+ type Output<'a> where Self: 'a;
+
+ fn as_ref2<'a>(&'a self) -> Self::Output<'a>;
+}
+
+impl<T> AsRef2 for Vec<T> {
+ type Output<'a> where Self: 'a = &'a [T];
+
+ fn as_ref2<'a>(&'a self) -> Self::Output<'a> {
+ &self[..]
+ }
+}
+
+#[derive(Debug)]
+struct Foo<T>(T);
+#[derive(Debug)]
+struct FooRef<'a, U>(&'a [U]);
+
+impl<'b, T, U> AsRef2 for Foo<T> //~ the type parameter
+where
+ // * `for<'b, 'c> T: AsRef2<Output<'b> = &'c [U]>>` does not work
+ //
+ // * `U` is unconstrained but should be allowed in this context because `Output` is
+ // an associated type
+ T: AsRef2<Output<'b> = &'b [U]>,
+ U: 'b
+{
+ type Output<'a> where Self: 'a = FooRef<'a, U>;
+
+ fn as_ref2<'a>(&'a self) -> Self::Output<'a> {
+ FooRef(self.0.as_ref2())
+ }
+}
+
+fn main() {
+ let foo = Foo(vec![1, 2, 3]);
+ dbg!(foo.as_ref2());
+}
--- /dev/null
+error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
+ --> $DIR/issue-87735.rs:26:13
+ |
+LL | impl<'b, T, U> AsRef2 for Foo<T>
+ | ^ unconstrained type parameter
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0207`.
--- /dev/null
+// check-fail
+
+// This should pass, but unnormalized input args aren't treated as implied.
+
+#![feature(generic_associated_types)]
+
+trait MyTrait {
+ type Assoc<'a, 'b> where 'b: 'a;
+ fn do_sth(arg: Self::Assoc<'_, '_>);
+}
+
+struct Foo;
+
+impl MyTrait for Foo {
+ type Assoc<'a, 'b> where 'b: 'a = u32;
+
+ fn do_sth(_: u32) {} //~ lifetime bound
+ // fn do_sth(_: Self::Assoc<'static, 'static>) {}
+ // fn do_sth(_: Self::Assoc<'_, '_>) {}
+}
+
+fn main() {}
--- /dev/null
+error[E0478]: lifetime bound not satisfied
+ --> $DIR/issue-87748.rs:17:5
+ |
+LL | fn do_sth(_: u32) {}
+ | ^^^^^^^^^^^^^^^^^
+ |
+note: lifetime parameter instantiated with the anonymous lifetime #2 defined here
+ --> $DIR/issue-87748.rs:17:5
+ |
+LL | fn do_sth(_: u32) {}
+ | ^^^^^^^^^^^^^^^^^
+note: but lifetime parameter must outlive the anonymous lifetime #1 defined here
+ --> $DIR/issue-87748.rs:17:5
+ |
+LL | fn do_sth(_: u32) {}
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0478`.
--- /dev/null
+// check-fail
+
+// This should pass.
+
+#![feature(generic_associated_types)]
+
+use std::fmt::Debug;
+
+trait Foo {
+ type Ass where Self::Ass: Debug;
+}
+
+#[derive(Debug)]
+struct Bar;
+
+impl Foo for Bar {
+ type Ass = Bar;
+ //~^ overflow
+}
+
+fn main() {}
--- /dev/null
+error[E0275]: overflow evaluating the requirement `<Bar as Foo>::Ass == _`
+ --> $DIR/issue-87755.rs:17:16
+ |
+LL | type Ass = Bar;
+ | ^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0275`.
--- /dev/null
+// check-fail
+
+// This should pass, but using a type alias vs a reference directly
+// changes late-bound -> early-bound.
+
+#![feature(generic_associated_types)]
+
+trait Scanner {
+ type Input<'a>;
+ type Token<'a>;
+
+ fn scan<'a>(&mut self, i : Self::Input<'a>) -> Self::Token<'a>;
+}
+
+struct IdScanner();
+
+impl Scanner for IdScanner {
+ type Input<'a> = &'a str;
+ type Token<'a> = &'a str;
+
+ fn scan<'a>(&mut self, s : &'a str) -> &'a str { //~ lifetime parameters
+ s
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0195]: lifetime parameters or bounds on method `scan` do not match the trait declaration
+ --> $DIR/issue-87803.rs:21:12
+ |
+LL | fn scan<'a>(&mut self, i : Self::Input<'a>) -> Self::Token<'a>;
+ | ---- lifetimes in impl do not match this method in trait
+...
+LL | fn scan<'a>(&mut self, s : &'a str) -> &'a str {
+ | ^^^^ lifetimes do not match method in trait
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0195`.
--- /dev/null
+// check-fail
+
+// This should pass, but has a missed normalization due to HRTB.
+
+#![feature(generic_associated_types)]
+
+trait Iterable {
+ type Iterator<'a> where Self: 'a;
+ fn iter(&self) -> Self::Iterator<'_>;
+}
+
+struct SomeImplementation();
+
+impl Iterable for SomeImplementation {
+ type Iterator<'a> = std::iter::Empty<usize>;
+ fn iter(&self) -> Self::Iterator<'_> {
+ std::iter::empty()
+ }
+}
+
+fn do_something<I: Iterable>(i: I, mut f: impl for<'a> Fn(&mut I::Iterator<'a>)) {
+ f(&mut i.iter());
+}
+
+fn main() {
+ do_something(SomeImplementation(), |_| ());
+ do_something(SomeImplementation(), test);
+ //~^ type mismatch
+}
+
+fn test<'a, I: Iterable>(_: &mut I::Iterator<'a>) {}
--- /dev/null
+error[E0631]: type mismatch in function arguments
+ --> $DIR/issue-88382.rs:27:40
+ |
+LL | do_something(SomeImplementation(), test);
+ | ------------ ^^^^ expected signature of `for<'a> fn(&mut <SomeImplementation as Iterable>::Iterator<'a>) -> _`
+ | |
+ | required by a bound introduced by this call
+...
+LL | fn test<'a, I: Iterable>(_: &mut I::Iterator<'a>) {}
+ | ------------------------------------------------- found signature of `for<'r> fn(&'r mut std::iter::Empty<usize>) -> _`
+ |
+note: required by a bound in `do_something`
+ --> $DIR/issue-88382.rs:21:56
+ |
+LL | fn do_something<I: Iterable>(i: I, mut f: impl for<'a> Fn(&mut I::Iterator<'a>)) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `do_something`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0631`.
--- /dev/null
+// check-fail
+
+// This should pass, but has a missed normalization due to HRTB.
+
+#![feature(generic_associated_types)]
+
+pub trait Marker {}
+
+pub trait Trait {
+ type Assoc<'a>;
+}
+
+fn test<T>(value: T)
+where
+ T: Trait,
+ for<'a> T::Assoc<'a>: Marker,
+{
+}
+
+impl Marker for () {}
+
+struct Foo;
+
+impl Trait for Foo {
+ type Assoc<'a> = ();
+}
+
+fn main() {
+ test(Foo);
+ //~^ the trait bound
+}
--- /dev/null
+error[E0277]: the trait bound `for<'a> <_ as Trait>::Assoc<'a>: Marker` is not satisfied
+ --> $DIR/issue-88460.rs:29:5
+ |
+LL | test(Foo);
+ | ^^^^ the trait `for<'a> Marker` is not implemented for `<_ as Trait>::Assoc<'a>`
+ |
+note: required by a bound in `test`
+ --> $DIR/issue-88460.rs:16:27
+ |
+LL | fn test<T>(value: T)
+ | ---- required by a bound in this
+...
+LL | for<'a> T::Assoc<'a>: Marker,
+ | ^^^^^^ required by this bound in `test`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// check-fail
+
+// This should pass, but requires more logic.
+
+#![feature(generic_associated_types)]
+
+trait A {
+ type I<'a>;
+}
+
+pub struct TestA<F>
+{
+ f: F,
+}
+
+impl<F> A for TestA<F> {
+ type I<'a> = &'a F;
+}
+
+struct TestB<Q, F>
+{
+ q: Q,
+ f: F,
+}
+
+impl<'q, Q, I, F> A for TestB<Q, F> //~ the type parameter
+where
+ Q: A<I<'q> = &'q I>,
+ F: Fn(I),
+{
+ type I<'a> = ();
+}
+
+fn main() {}
--- /dev/null
+error[E0207]: the type parameter `I` is not constrained by the impl trait, self type, or predicates
+ --> $DIR/issue-88526.rs:26:13
+ |
+LL | impl<'q, Q, I, F> A for TestB<Q, F>
+ | ^ unconstrained type parameter
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0207`.
--- /dev/null
+// check-fail
+// edition:2021
+
+// This should pass, but seems to run into a TAIT bug.
+
+#![feature(type_alias_impl_trait)]
+#![feature(generic_associated_types)]
+
+use std::future::Future;
+
+trait Stream {
+ type Item;
+}
+
+struct Empty<T>(T);
+impl<T> Stream for Empty<T> {
+ type Item = ();
+}
+fn empty<T>() -> Empty<T> {
+ todo!()
+}
+
+trait X {
+ type LineStream<'a, Repr>: Stream<Item = Repr> where Self: 'a;
+
+ type LineStreamFut<'a,Repr>: Future<Output = Self::LineStream<'a, Repr>> where Self: 'a;
+
+ fn line_stream<'a,Repr>(&'a self) -> Self::LineStreamFut<'a,Repr>;
+}
+
+struct Y;
+
+impl X for Y {
+ type LineStream<'a, Repr> = impl Stream<Item = Repr>; //~ could not find
+
+ type LineStreamFut<'a, Repr> = impl Future<Output = Self::LineStream<'a, Repr>> ;
+
+ fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> { //~ type mismatch
+ async {empty()}
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0271]: type mismatch resolving `<impl Future<Output = [async output]> as Future>::Output == impl Stream<Item = Repr>`
+ --> $DIR/issue-89008.rs:38:43
+ |
+LL | type LineStream<'a, Repr> = impl Stream<Item = Repr>;
+ | ------------------------ the expected opaque type
+...
+LL | fn line_stream<'a, Repr>(&'a self) -> Self::LineStreamFut<'a, Repr> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found struct `Empty`
+ |
+ = note: expected opaque type `impl Stream<Item = Repr>`
+ found struct `Empty<_>`
+
+error: could not find defining uses
+ --> $DIR/issue-89008.rs:34:33
+ |
+LL | type LineStream<'a, Repr> = impl Stream<Item = Repr>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0271`.
error[E0478]: lifetime bound not satisfied
--> $DIR/impl_bounds.rs:17:35
|
+LL | type B<'a, 'b> where 'a: 'b;
+ | ---------------------------- definition of `B` from trait
+...
LL | type B<'a, 'b> where 'b: 'a = (&'a(), &'b ());
- | ^^^^^^^^^^^^^^^
+ | - ^^^^^^^^^^^^^^^
+ | |
+ | help: try copying this clause from the trait: `, 'a: 'b`
|
note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/impl_bounds.rs:17:12
fn main() {
let b = Box::new(42usize);
let copy = <()>::copy(&b);
+ //~^ type annotations needed
let raw_b = Box::deref(&b) as *const _;
let raw_copy = Box::deref(©) as *const _;
LL | type Copy<T: std::clone::Clone>: Copy = Box<T>;
| +++++++++++++++++++
-error: aborting due to 2 previous errors
+error[E0282]: type annotations needed
+ --> $DIR/issue-74824.rs:19:16
+ |
+LL | let copy = <()>::copy(&b);
+ | ^^^^^^^^^^ cannot infer type for type parameter `T` declared on the associated function `copy`
+
+error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0277, E0282.
+For more information about an error, try `rustc --explain E0277`.
// FIXME(generic_associated_types): Remove one of the below bounds
// https://github.com/rust-lang/rust/pull/90678#discussion_r744976085
where
- 'a: 'b, Self: 'a, Self: 'b;
+ Self: 'a, Self: 'b;
fn a(&'a self) -> Self::B<'a>;
}
impl<'a> A<'a> for C {
type B<'b> = impl Clone;
- //~^ ERROR: lifetime bound not satisfied
- //~| ERROR: could not find defining uses
+ //~^ ERROR: could not find defining uses
fn a(&'a self) -> Self::B<'a> {} //~ ERROR: non-defining opaque type use in defining scope
}
-error[E0478]: lifetime bound not satisfied
- --> $DIR/issue-88595.rs:19:18
- |
-LL | type B<'b> = impl Clone;
- | ^^^^^^^^^^
- |
-note: lifetime parameter instantiated with the lifetime `'a` as defined here
- --> $DIR/issue-88595.rs:18:6
- |
-LL | impl<'a> A<'a> for C {
- | ^^
-note: but lifetime parameter must outlive the lifetime `'b` as defined here
- --> $DIR/issue-88595.rs:19:12
- |
-LL | type B<'b> = impl Clone;
- | ^^
-
error: non-defining opaque type use in defining scope
- --> $DIR/issue-88595.rs:23:23
+ --> $DIR/issue-88595.rs:22:23
|
LL | fn a(&'a self) -> Self::B<'a> {}
| ^^^^^^^^^^^
LL | type B<'b> = impl Clone;
| ^^^^^^^^^^
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0478`.
error[E0477]: the type `&mut ()` does not fulfill the required lifetime
--> $DIR/issue-90014.rs:14:20
|
+LL | type Fut<'a> where Self: 'a;
+ | ---------------------------- definition of `Fut` from trait
+...
LL | type Fut<'a> = impl Future<Output = ()>;
- | ^^^^^^^^^^^^^^^^^^^^^^^^
+ | - ^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try copying this clause from the trait: `where Self: 'a`
|
note: type must outlive the lifetime `'a` as defined here
--> $DIR/issue-90014.rs:14:14
--- /dev/null
+// check-pass
+
+#![feature(generic_associated_types)]
+
+trait Foo<T> {
+ type Type<'a>
+ where
+ T: 'a;
+}
+
+impl<T> Foo<T> for () {
+ type Type<'a>
+ where
+ T: 'a,
+ = ();
+}
+
+fn foo<T>() {
+ let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
+}
+
+pub fn main() {}
--- /dev/null
+// check-fail
+
+// FIXME(generic_associated_types): We almost certaintly want this to pass, but
+// it's particularly difficult currently, because we need a way of specifying
+// that `<Self::Base as Functor>::With<T> = Self` without using that when we have
+// a `U`. See `https://github.com/rust-lang/rust/pull/92728` for a (hacky)
+// solution. This might be better to just wait for Chalk.
+
+#![feature(generic_associated_types)]
+
+pub trait Functor {
+ type With<T>;
+
+ fn fmap<T, U>(this: Self::With<T>) -> Self::With<U>;
+}
+
+pub trait FunctorExt<T>: Sized {
+ type Base: Functor<With<T> = Self>;
+
+ fn fmap<U>(self) {
+ let arg: <Self::Base as Functor>::With<T>;
+ let ret: <Self::Base as Functor>::With<U>;
+
+ arg = self;
+ ret = <Self::Base as Functor>::fmap(arg);
+ //~^ type annotations needed
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/issue-91762.rs:25:15
+ |
+LL | ret = <Self::Base as Functor>::fmap(arg);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the associated function `fmap`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
--- /dev/null
+#![feature(generic_associated_types)]
+
+struct Texture;
+
+trait Surface {
+ type TextureIter<'a>: Iterator<Item = &'a Texture>
+ where
+ Self: 'a;
+
+ fn get_texture(&self) -> Self::TextureIter<'_>;
+}
+
+trait Swapchain {
+ type Surface<'a>: Surface
+ where
+ Self: 'a;
+
+ fn get_surface(&self) -> Self::Surface<'_>;
+}
+
+impl<'s> Surface for &'s Texture {
+ type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
+ //~^ ERROR the type
+
+ fn get_texture(&self) -> Self::TextureIter<'_> {
+ let option: Option<&Texture> = Some(self);
+ option.into_iter()
+ }
+}
+
+impl Swapchain for Texture {
+ type Surface<'a> = &'a Texture;
+
+ fn get_surface(&self) -> Self::Surface<'_> {
+ self
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0477]: the type `&'s Texture` does not fulfill the required lifetime
+ --> $DIR/issue-92033.rs:22:28
+ |
+LL | / type TextureIter<'a>: Iterator<Item = &'a Texture>
+LL | | where
+LL | | Self: 'a;
+ | |_________________- definition of `TextureIter` from trait
+...
+LL | type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
+ | - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | help: try copying this clause from the trait: `where Self: 'a`
+ |
+note: type must outlive the lifetime `'a` as defined here
+ --> $DIR/issue-92033.rs:22:22
+ |
+LL | type TextureIter<'a> = std::option::IntoIter<&'a Texture>;
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0477`.
// We obviously error for `Iterator`, but we should also error for `Item`
trait IterableTwo {
type Item<'a>;
+ //~^ missing required
type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
//~^ missing required
fn iter<'a>(&'a self) -> Self::Iterator<'a>;
}
+trait IterableTwoWhere {
+ type Item<'a>;
+ //~^ missing required
+ type Iterator<'a>: Iterator<Item = Self::Item<'a>> where Self: 'a;
+ fn iter<'a>(&'a self) -> Self::Iterator<'a>;
+}
+
// We also should report region outlives clauses. Here, we know that `'y: 'x`,
// because of `&'x &'y`, so we require that `'b: 'a`.
trait RegionOutlives {
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+error: missing required bound on `Item`
+ --> $DIR/self-outlives-lint.rs:142:5
+ |
+LL | type Item<'a>;
+ | ^^^^^^^^^^^^^-
+ | |
+ | help: add the required where clause: `where Self: 'a`
+ |
+ = note: this bound is currently required to ensure that impls have maximum flexibility
+ = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+
error: missing required bound on `Iterator`
- --> $DIR/self-outlives-lint.rs:143:5
+ --> $DIR/self-outlives-lint.rs:144:5
|
LL | type Iterator<'a>: Iterator<Item = Self::Item<'a>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+error: missing required bound on `Item`
+ --> $DIR/self-outlives-lint.rs:150:5
+ |
+LL | type Item<'a>;
+ | ^^^^^^^^^^^^^-
+ | |
+ | help: add the required where clause: `where Self: 'a`
+ |
+ = note: this bound is currently required to ensure that impls have maximum flexibility
+ = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
+
error: missing required bound on `Bar`
- --> $DIR/self-outlives-lint.rs:151:5
+ --> $DIR/self-outlives-lint.rs:159:5
|
LL | type Bar<'a, 'b>;
| ^^^^^^^^^^^^^^^^-
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
error: missing required bound on `Fut`
- --> $DIR/self-outlives-lint.rs:167:5
+ --> $DIR/self-outlives-lint.rs:175:5
|
LL | type Fut<'out>;
| ^^^^^^^^^^^^^^-
= note: this bound is currently required to ensure that impls have maximum flexibility
= note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
-error: aborting due to 13 previous errors
+error: aborting due to 15 previous errors
// run-pass
-// ignore-compare-mode-chalk
#![feature(fn_traits,
step_trait,
// edition:2018
-// ignore-compare-mode-chalk
#![feature(type_alias_impl_trait)]
error[E0277]: the trait bound `impl Future<Output = [async output]>: Copy` is not satisfied
- --> $DIR/issue-55872-2.rs:14:20
+ --> $DIR/issue-55872-2.rs:13:20
|
LL | fn foo<T>() -> Self::E {
| ^^^^^^^ the trait `Copy` is not implemented for `impl Future<Output = [async output]>`
error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
- --> $DIR/issue-55872-2.rs:14:28
+ --> $DIR/issue-55872-2.rs:13:28
|
LL | fn foo<T>() -> Self::E {
| ____________________________^
-// ignore-compare-mode-chalk
#![feature(type_alias_impl_trait)]
pub trait Bar {
error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
- --> $DIR/issue-55872.rs:13:28
+ --> $DIR/issue-55872.rs:12:28
|
LL | fn foo<T>() -> Self::E {
| ____________________________^
// check-pass
+// ignore-compare-mode-chalk
#![allow(dead_code)]
--- /dev/null
+// A test exploiting the bug behind #25860 except with
+// implied trait bounds which currently don't exist without `-Zchalk`.
+use std::marker::PhantomData;
+struct Foo<'a, 'b, T>(PhantomData<(&'a (), &'b (), T)>)
+where
+ T: Convert<'a, 'b>;
+
+trait Convert<'a, 'b>: Sized {
+ fn cast(&'a self) -> &'b Self;
+}
+impl<'long: 'short, 'short, T> Convert<'long, 'short> for T {
+ fn cast(&'long self) -> &'short T {
+ self
+ }
+}
+
+// This function will compile once we add implied trait bounds.
+//
+// If we're not careful with our impl, the transformations
+// in `bad` would succeed, which is unsound ✨
+//
+// FIXME: the error is pretty bad, this should say
+//
+// `T: Convert<'in_, 'out>` is not implemented
+//
+// help: needed by `Foo<'in_, 'out, T>`
+//
+// Please ping @lcnr if your changes end up causing `badboi` to compile.
+fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ T) -> &'out T {
+ //~^ ERROR lifetime mismatch
+ sadness.cast()
+}
+
+fn bad<'short, T>(value: &'short T) -> &'static T {
+ let x: for<'in_, 'out> fn(Foo<'in_, 'out, T>, &'in_ T) -> &'out T = badboi;
+ let x: for<'out> fn(Foo<'short, 'out, T>, &'short T) -> &'out T = x;
+ let x: for<'out> fn(Foo<'static, 'out, T>, &'short T) -> &'out T = x;
+ let x: fn(Foo<'static, 'static, T>, &'short T) -> &'static T = x;
+ x(Foo(PhantomData), value)
+}
+
+// Use `bad` to cause a segfault.
+fn main() {
+ let mut outer: Option<&'static u32> = Some(&3);
+ let static_ref: &'static &'static u32 = match outer {
+ Some(ref reference) => bad(reference),
+ None => unreachable!(),
+ };
+ outer = None;
+ println!("{}", static_ref);
+}
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/hrlt-implied-trait-bounds-guard.rs:29:29
+ |
+LL | fn badboi<'in_, 'out, T>(x: Foo<'in_, 'out, T>, sadness: &'in_ T) -> &'out T {
+ | ^^^^^^^^^^^^^^^^^^ -------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+ | ...but data from `x` is returned here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+// check-pass
+struct Foo<'a>(&'a ())
+where
+ (): Trait<'a>;
+
+trait Trait<'a> {
+ fn id<T>(value: &'a T) -> &'static T;
+}
+
+impl Trait<'static> for () {
+ fn id<T>(value: &'static T) -> &'static T {
+ value
+ }
+}
+
+fn could_use_implied_bounds<'a, T>(_: Foo<'a>, x: &'a T) -> &'static T
+where
+ (): Trait<'a>, // This could be an implied bound
+{
+ <()>::id(x)
+}
+
+fn main() {
+ let bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = |_, _| {};
+
+ // If `could_use_implied_bounds` were to use implied bounds,
+ // keeping 'a late-bound, then we could assign that function
+ // to this variable.
+ let bar: for<'a> fn(Foo<'a>, &'a ()) = bar;
+
+ // In this case, the subtyping relation here would be unsound,
+ // allowing us to transmute lifetimes. This currently compiles
+ // because we incorrectly deal with implied bounds inside of binders.
+ let _bar: for<'a, 'b> fn(Foo<'a>, &'b ()) = bar;
+}
-// When a MULTI-character string literal is used where a char should be,
+// When a MULTI/NO-character string literal is used where a char should be,
// DO NOT suggest changing to single quotes.
fn main() {
let _: char = "foo"; //~ ERROR mismatched types
+ let _: char = ""; //~ ERROR mismatched types
}
| |
| expected due to this
-error: aborting due to previous error
+error[E0308]: mismatched types
+ --> $DIR/char-as-str-multi.rs:6:19
+ |
+LL | let _: char = "";
+ | ---- ^^ expected `char`, found `&str`
+ | |
+ | expected due to this
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-// compile-flags: --test
-
-#[test]
-mod foo {} //~ ERROR only functions may be used as tests
-
-fn main() {}
+++ /dev/null
-error: only functions may be used as tests
- --> $DIR/issue-14772.rs:4:1
- |
-LL | mod foo {}
- | ^^^^^^^^^^
-
-error: aborting due to previous error
-
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `Y::foo` in statics
--> $DIR/issue-16538.rs:15:23
|
LL | static foo: &Y::X = &*Y::foo(Y::x as *const Y::X);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error[E0133]: use of extern static is unsafe and requires unsafe function or block
--> $DIR/issue-16538.rs:15:30
|
= note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `Y::foo` in statics
--> $DIR/issue-16538.rs:15:23
|
LL | static foo: &Y::X = &*Y::foo(Y::x as *const Y::X);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error: aborting due to 3 previous errors
// run-pass
+// ignore-wasm32-bare FIXME(#93923) llvm miscompilation
use std::collections::HashMap;
use std::path::Path;
-// ignore-compare-mode-chalk
-
trait Next {
type Next: Next;
}
-struct GetNext<T: Next> { t: T }
+struct GetNext<T: Next> {
+ t: T,
+}
impl<T: Next> Next for GetNext<T> {
type Next = <GetNext<T> as Next>::Next;
-// ignore-compare-mode-chalk
trait Next {
type Next: Next;
}
-struct GetNext<T: Next> { t: T }
+struct GetNext<T: Next> {
+ t: T,
+}
impl<T: Next> Next for GetNext<T> {
type Next = <GetNext<T::Next> as Next>::Next;
error[E0275]: overflow evaluating the requirement `<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized`
- --> $DIR/issue-23122-2.rs:9:17
+ --> $DIR/issue-23122-2.rs:10:17
|
LL | type Next = <GetNext<T::Next> as Next>::Next;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`)
note: required because of the requirements on the impl of `Next` for `GetNext<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<T as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next>`
- --> $DIR/issue-23122-2.rs:8:15
+ --> $DIR/issue-23122-2.rs:9:15
|
LL | impl<T: Next> Next for GetNext<T> {
| ^^^^ ^^^^^^^^^^
struct B;
static S: &'static B = &A;
-//~^ ERROR calls in statics are limited to constant functions
+//~^ ERROR cannot perform deref coercion on `A` in statics
use std::ops::Deref;
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot perform deref coercion on `A` in statics
--> $DIR/issue-25901.rs:4:24
|
LL | static S: &'static B = &A;
| ^^
+ |
+ = note: attempting to deref into `B`
+note: deref defined here
+ --> $DIR/issue-25901.rs:10:5
+ |
+LL | type Target = B;
+ | ^^^^^^^^^^^^^^^^
+note: impl defined here, but it is not `const`
+ --> $DIR/issue-25901.rs:9:1
+ |
+LL | impl Deref for A {
+ | ^^^^^^^^^^^^^^^^
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
// check-pass
-// ignore-compare-mode-chalk
#[derive(Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct Array<T> {
f00: [T; 00],
// run-pass
-// ignore-compare-mode-chalk
+
struct Foo<A: Repr>(<A as Repr>::Data);
-impl<A> Copy for Foo<A> where <A as Repr>::Data: Copy { }
-impl<A> Clone for Foo<A> where <A as Repr>::Data: Clone {
- fn clone(&self) -> Self { Foo(self.0.clone()) }
+impl<A> Copy for Foo<A> where <A as Repr>::Data: Copy {}
+impl<A> Clone for Foo<A>
+where
+ <A as Repr>::Data: Clone,
+{
+ fn clone(&self) -> Self {
+ Foo(self.0.clone())
+ }
}
trait Repr {
type Data = u32;
}
-fn main() {
-}
+fn main() {}
error[E0277]: the trait bound `for<'a> (): Trait2<'a>` is not satisfied
- --> $DIR/issue-35570.rs:8:4
+ --> $DIR/issue-35570.rs:8:40
|
LL | fn _ice(param: Box<dyn for <'a> Trait1<<() as Trait2<'a>>::Ty>>) {
- | ^^^^ the trait `for<'a> Trait2<'a>` is not implemented for `()`
+ | ^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> Trait2<'a>` is not implemented for `()`
error: aborting due to previous error
// check-pass
-// ignore-compare-mode-chalk
#![feature(associated_type_defaults)]
-error[E0015]: 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:14:24
|
LL | let array: [usize; Dim3::dim()]
| ^^^^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
-error[E0015]: 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
error: lifetime may not live long enough
- --> $DIR/issue-55796.rs:18:9
+ --> $DIR/issue-55796.rs:16:9
|
LL | pub trait Graph<'a> {
| -- lifetime `'a` defined here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
error: lifetime may not live long enough
- --> $DIR/issue-55796.rs:23:9
+ --> $DIR/issue-55796.rs:21:9
|
LL | pub trait Graph<'a> {
| -- lifetime `'a` defined here
-// ignore-compare-mode-chalk
-
pub trait EdgeTrait<N> {
fn target(&self) -> N;
}
fn out_neighbors(&'a self, u: &Self::Node) -> Box<dyn Iterator<Item = Self::Node>> {
Box::new(self.out_edges(u).map(|e| e.target()))
-//~^ ERROR cannot infer
+ //~^ ERROR cannot infer
}
fn in_neighbors(&'a self, u: &Self::Node) -> Box<dyn Iterator<Item = Self::Node>> {
Box::new(self.in_edges(u).map(|e| e.target()))
-//~^ ERROR cannot infer
+ //~^ ERROR cannot infer
}
}
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/issue-55796.rs:18:9
+ --> $DIR/issue-55796.rs:16:9
|
LL | Box::new(self.out_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/issue-55796.rs:7:17
+ --> $DIR/issue-55796.rs:5:17
|
LL | pub trait Graph<'a> {
| ^^
-note: ...so that the type `Map<<Self as Graph<'a>>::EdgesIter, [closure@$DIR/issue-55796.rs:18:40: 18:54]>` will meet its required lifetime bounds
- --> $DIR/issue-55796.rs:18:9
+note: ...so that the type `Map<<Self as Graph<'a>>::EdgesIter, [closure@$DIR/issue-55796.rs:16:40: 16:54]>` will meet its required lifetime bounds
+ --> $DIR/issue-55796.rs:16:9
|
LL | Box::new(self.out_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
- --> $DIR/issue-55796.rs:18:9
+ --> $DIR/issue-55796.rs:16:9
|
LL | Box::new(self.out_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
found `Box<dyn Iterator<Item = <Self as Graph<'a>>::Node>>`
error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/issue-55796.rs:23:9
+ --> $DIR/issue-55796.rs:21:9
|
LL | Box::new(self.in_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/issue-55796.rs:7:17
+ --> $DIR/issue-55796.rs:5:17
|
LL | pub trait Graph<'a> {
| ^^
-note: ...so that the type `Map<<Self as Graph<'a>>::EdgesIter, [closure@$DIR/issue-55796.rs:23:39: 23:53]>` will meet its required lifetime bounds
- --> $DIR/issue-55796.rs:23:9
+note: ...so that the type `Map<<Self as Graph<'a>>::EdgesIter, [closure@$DIR/issue-55796.rs:21:39: 21:53]>` will meet its required lifetime bounds
+ --> $DIR/issue-55796.rs:21:9
|
LL | Box::new(self.in_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that the types are compatible
- --> $DIR/issue-55796.rs:23:9
+ --> $DIR/issue-55796.rs:21:9
|
LL | Box::new(self.in_edges(u).map(|e| e.target()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// build-pass
// ignore-tidy-filelength
-// ignore-compare-mode-chalk
#![crate_type = "rlib"]
fn banana(v: &str) -> u32 {
-// compile-flags: -Zinstrument-coverage -Ccodegen-units=4 --crate-type dylib -Copt-level=0
+// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 --crate-type dylib -Copt-level=0
// build-pass
// needs-profiler-support
--- /dev/null
+fn process_slice(data: &[i32]) {
+ //~^ NOTE required by a bound in this
+ todo!()
+}
+
+fn main() {
+ let some_generated_vec = (0..10).collect();
+ //~^ ERROR the size for values of type `[i32]` cannot be known at compilation time
+ //~| ERROR a value of type `[i32]` cannot be built since `[i32]` has no definite size
+ //~| NOTE try explicitly collecting into a `Vec<{integer}>`
+ //~| NOTE required by a bound in `collect`
+ //~| NOTE all local variables must have a statically known size
+ //~| NOTE doesn't have a size known at compile-time
+ process_slice(&some_generated_vec);
+}
--- /dev/null
+error[E0277]: the size for values of type `[i32]` cannot be known at compilation time
+ --> $DIR/collect-into-slice.rs:7:9
+ |
+LL | let some_generated_vec = (0..10).collect();
+ | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
+ |
+ = help: the trait `Sized` is not implemented for `[i32]`
+ = note: all local variables must have a statically known size
+ = help: unsized locals are gated as an unstable feature
+
+error[E0277]: a value of type `[i32]` cannot be built since `[i32]` has no definite size
+ --> $DIR/collect-into-slice.rs:7:38
+ |
+LL | let some_generated_vec = (0..10).collect();
+ | ^^^^^^^ try explicitly collecting into a `Vec<{integer}>`
+ |
+ = help: the trait `FromIterator<{integer}>` is not implemented for `[i32]`
+note: required by a bound in `collect`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+ |
+LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
|
= help: the following implementations were found:
<isize as Copy>
+ <f32 as Copy>
+ <f64 as Copy>
+ <i128 as Copy>
+ and 10 others
note: required by a bound in `assert_copy`
--> $DIR/kindck-copy.rs:5:18
|
|
= help: the following implementations were found:
<isize as Copy>
+ <f32 as Copy>
+ <f64 as Copy>
+ <i128 as Copy>
+ and 10 others
note: required by a bound in `assert_copy`
--> $DIR/kindck-copy.rs:5:18
|
| ^ unknown character escape
|
= help: for more information, visit <https://static.rust-lang.org/doc/master/reference.html#literals>
+help: if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal
+ |
+LL | r"\●"
+ | ~~~~~
error: unknown character escape: `\u{25cf}`
--> $DIR/lex-bad-char-literals-1.rs:14:7
| ^ unknown character escape
|
= help: for more information, visit <https://static.rust-lang.org/doc/master/reference.html#literals>
+help: if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal
+ |
+LL | r"\●"
+ | ~~~~~
error: aborting due to 4 previous errors
assert_eq!(stringify_expr!(mac! { ... }), "mac! { ... }");
// ExprKind::Struct
- assert_eq!(stringify_expr!(Struct {}), "Struct{}"); // FIXME
+ assert_eq!(stringify_expr!(Struct {}), "Struct {}");
#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5151
- assert_eq!(stringify_expr!(<Struct as Trait>::Type {}), "<Struct as Trait>::Type{}");
- assert_eq!(stringify_expr!(Struct { .. }), "Struct{..}"); // FIXME
- assert_eq!(stringify_expr!(Struct { ..base }), "Struct{..base}"); // FIXME
- assert_eq!(stringify_expr!(Struct { x }), "Struct{x,}");
- assert_eq!(stringify_expr!(Struct { x, .. }), "Struct{x, ..}");
- assert_eq!(stringify_expr!(Struct { x, ..base }), "Struct{x, ..base}");
- assert_eq!(stringify_expr!(Struct { x: true }), "Struct{x: true,}");
- assert_eq!(stringify_expr!(Struct { x: true, .. }), "Struct{x: true, ..}");
- assert_eq!(stringify_expr!(Struct { x: true, ..base }), "Struct{x: true, ..base}");
+ assert_eq!(stringify_expr!(<Struct as Trait>::Type {}), "<Struct as Trait>::Type {}");
+ assert_eq!(stringify_expr!(Struct { .. }), "Struct { .. }");
+ assert_eq!(stringify_expr!(Struct { ..base }), "Struct { ..base }");
+ assert_eq!(stringify_expr!(Struct { x }), "Struct { x }");
+ assert_eq!(stringify_expr!(Struct { x, .. }), "Struct { x, .. }");
+ assert_eq!(stringify_expr!(Struct { x, ..base }), "Struct { x, ..base }");
+ assert_eq!(stringify_expr!(Struct { x: true }), "Struct { x: true }");
+ assert_eq!(stringify_expr!(Struct { x: true, .. }), "Struct { x: true, .. }");
+ assert_eq!(stringify_expr!(Struct { x: true, ..base }), "Struct { x: true, ..base }");
// ExprKind::Repeat
assert_eq!(stringify_expr!([(); 0]), "[(); 0]");
--- /dev/null
+error: format argument must be a string literal
+ --> $DIR/unreachable-arg.rs:15:18
+ |
+LL | unreachable!(a);
+ | ^
+ |
+help: you might be missing a string literal to format with
+ |
+LL | unreachable!("{}", a);
+ | +++++
+
+error: aborting due to previous error
+
--- /dev/null
+// ignore-emscripten no processes
+
+// revisions: edition_2015 edition_2021
+// [edition_2015]edition:2015
+// [edition_2021]edition:2021
+// [edition_2015]run-fail
+// [edition_2021]check-fail
+// [edition_2015]error-pattern:internal error: entered unreachable code: hello
+// [edition_2021]error-pattern:format argument must be a string literal
+
+#![allow(non_fmt_panics)]
+
+fn main() {
+ let a = "hello";
+ unreachable!(a);
+}
--- /dev/null
+// run-fail
+// ignore-emscripten no processes
+
+// revisions: edition_2015 edition_2021
+// [edition_2015]edition:2015
+// [edition_2021]edition:2021
+// [edition_2015]error-pattern:internal error: entered unreachable code: x is {x}
+// [edition_2021]error-pattern:internal error: entered unreachable code: x is 5
+
+#![allow(non_fmt_panics)]
+
+fn main() {
+ let x = 5;
+ unreachable!("x is {x}");
+}
--- /dev/null
+error: there is no argument named `x`
+ --> $DIR/unreachable-format-args.rs:13:5
+ |
+LL | unreachable!("x is {x} and y is {y}", y = 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: did you intend to capture a variable `x` from the surrounding scope?
+ = note: to avoid ambiguity, `format_args!` cannot capture variables when the format string is expanded from a macro
+ = note: this error originates in the macro `$crate::concat` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
--- /dev/null
+// ignore-emscripten no processes
+
+// revisions: edition_2015 edition_2021
+// [edition_2015]edition:2015
+// [edition_2021]edition:2021
+// [edition_2015]check-fail
+// [edition_2021]run-fail
+// [edition_2015]error-pattern:there is no argument named `x`
+// [edition_2021]error-pattern:internal error: entered unreachable code: x is 5 and y is 0
+
+fn main() {
+ let x = 5;
+ unreachable!("x is {x} and y is {y}", y = 0);
+}
|
LL | let _ = 0x61u32 as char;
| ^^^^^^^^^^^^^^^ invalid cast
+ |
+help: try `char::from_u32` instead
+ --> $DIR/cast-rfc0401.rs:41:13
+ |
+LL | let _ = 0x61u32 as char;
+ | ^^^^^^^^^^^^^^^
error[E0606]: casting `bool` as `f32` is invalid
--> $DIR/cast-rfc0401.rs:43:13
[(); { for _ in 0usize.. {}; 0}];
//~^ ERROR `for` is not allowed in a `const`
- //~| ERROR calls in constants are limited to constant functions
+ //~| ERROR cannot convert
//~| ERROR mutable references are not allowed in constants
- //~| ERROR calls in constants are limited to constant functions
+ //~| ERROR cannot call non-const fn
}
| expected `usize`, found `()`
| help: give it a value of the expected type: `break 42`
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot convert `RangeFrom<usize>` into an iterator in constants
--> $DIR/issue-52443.rs:9:21
|
LL | [(); { for _ in 0usize.. {}; 0}];
| ^^^^^^^^
+ |
+note: impl defined here, but it is not `const`
+ --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
+ |
+LL | impl<I: Iterator> IntoIterator for I {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error[E0658]: mutable references are not allowed in constants
--> $DIR/issue-52443.rs:9:21
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
-error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `<RangeFrom<usize> as Iterator>::next` in constants
--> $DIR/issue-52443.rs:9:21
|
LL | [(); { for _ in 0usize.. {}; 0}];
| ^^^^^^^^
+ |
+ = note: calls in constants are limited to constant functions, tuple structs and tuple variants
error: aborting due to 6 previous errors; 1 warning emitted
LL | const fn no_dyn_trait_ret() -> &'static dyn std::fmt::Debug { &() }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
+ = note: see issue #93706 <https://github.com/rust-lang/rust/issues/93706> for more information
= help: add `#![feature(const_fn_trait_bound)]` to the crate attributes to enable
error: aborting due to previous error
// Regression test for #53789.
//
// check-pass
-// ignore-compare-mode-chalk
+use std::cmp::Ord;
use std::collections::BTreeMap;
use std::ops::Range;
-use std::cmp::Ord;
macro_rules! valuetree {
() => {
- type ValueTree =
- <Self::Strategy as $crate::Strategy>::Value;
+ type ValueTree = <Self::Strategy as $crate::Strategy>::Value;
};
}
macro_rules! default {
($type: ty, $val: expr) => {
impl Default for $type {
- fn default() -> Self { $val.into() }
+ fn default() -> Self {
+ $val.into()
+ }
}
};
}
}
trait Strategy {
- type Value : ValueTree;
+ type Value: ValueTree;
}
#[derive(Clone)]
-struct VecStrategy<T : Strategy> {
+struct VecStrategy<T: Strategy> {
element: T,
size: Range<usize>,
}
-fn vec<T : Strategy>(element: T, size: Range<usize>)
- -> VecStrategy<T> {
- VecStrategy {
- element: element,
- size: size,
- }
+fn vec<T: Strategy>(element: T, size: Range<usize>) -> VecStrategy<T> {
+ VecStrategy { element: element, size: size }
}
type ValueFor<S> = <<S as Strategy>::Value as ValueTree>::Value;
struct SizeBounds(Range<usize>);
default!(SizeBounds, 0..100);
-
impl From<Range<usize>> for SizeBounds {
fn from(high: Range<usize>) -> Self {
unimplemented!()
}
}
-
-fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters)
- -> StrategyType<'a, A> {
+fn any_with<'a, A: Arbitrary<'a>>(args: A::Parameters) -> StrategyType<'a, A> {
unimplemented!()
}
-impl<K: ValueTree, V: ValueTree> Strategy for (K, V) where
- <K as ValueTree>::Value: Ord {
+impl<K: ValueTree, V: ValueTree> Strategy for (K, V)
+where
+ <K as ValueTree>::Value: Ord,
+{
type Value = TupleValueTree<(K, V)>;
}
-impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)> where
- <K as ValueTree>::Value: Ord {
+impl<K: ValueTree, V: ValueTree> ValueTree for TupleValueTree<(K, V)>
+where
+ <K as ValueTree>::Value: Ord,
+{
type Value = BTreeMapValueTree<K, V>;
}
#[derive(Clone)]
-struct VecValueTree<T : ValueTree> {
+struct VecValueTree<T: ValueTree> {
elements: Vec<T>,
}
where
A: Arbitrary<'static> + Ord,
B: Arbitrary<'static>,
-StrategyFor<A>: 'static,
-StrategyFor<B>: 'static,
+ StrategyFor<A>: 'static,
+ StrategyFor<B>: 'static,
{
valuetree!();
type Parameters = RangedParams2<A::Parameters, B::Parameters>;
}
}
-fn btree_map<K : Strategy + 'static, V : Strategy + 'static>
- (key: K, value: V, size: Range<usize>)
- -> BTreeMapStrategy<K, V>
-where ValueFor<K> : Ord {
+fn btree_map<K: Strategy + 'static, V: Strategy + 'static>(
+ key: K,
+ value: V,
+ size: Range<usize>,
+) -> BTreeMapStrategy<K, V>
+where
+ ValueFor<K>: Ord,
+{
unimplemented!()
}
}
}
-fn main() { }
+fn main() {}
|
= note: defining type: no_region::<'_#1r, T>::{closure#0} with closure substs [
i32,
- extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#2r), std::alloc::Global>,
+ extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#2r)>,
(),
]
= note: number of external vids: 3
|
= note: defining type: correct_region::<'_#1r, T>::{closure#0} with closure substs [
i32,
- extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#2r), std::alloc::Global>,
+ extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#2r)>,
(),
]
= note: number of external vids: 3
|
= note: defining type: wrong_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [
i32,
- extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#3r), std::alloc::Global>,
+ extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#3r)>,
(),
]
= note: number of external vids: 4
|
= note: defining type: outlives_region::<'_#1r, '_#2r, T>::{closure#0} with closure substs [
i32,
- extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn Anything + '_#3r), std::alloc::Global>,
+ extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn Anything + '_#3r)>,
(),
]
= note: number of external vids: 4
|
= note: defining type: no_region::<'_#1r, T>::{closure#0} with closure substs [
i32,
- extern "rust-call" fn((std::boxed::Box<T, std::alloc::Global>,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r), std::alloc::Global>,
+ extern "rust-call" fn((std::boxed::Box<T>,)) -> std::boxed::Box<(dyn std::fmt::Debug + '_#2r)>,
(),
]
= note: number of external vids: 3
#[allow(unreachable_code)]
fn main() {
panic!("{}", "here's a brace: {"); //~ WARN panic message contains a brace
+ unreachable!("{}", "here's a brace: {"); //~ WARN panic message contains a brace
std::panic!("{}", "another one: }"); //~ WARN panic message contains a brace
core::panic!("{}", "Hello {}"); //~ WARN panic message contains an unused formatting placeholder
assert!(false, "{}", "{:03x} {test} bla");
debug_assert!(false, "{}", "{{}} bla"); //~ WARN panic message contains braces
panic!("{}", C); //~ WARN panic message is not a string literal
panic!("{}", S); //~ WARN panic message is not a string literal
+ unreachable!("{}", S); //~ WARN panic message is not a string literal
+ unreachable!("{}", S); //~ WARN panic message is not a string literal
std::panic::panic_any(123); //~ WARN panic message is not a string literal
core::panic!("{}", &*"abc"); //~ WARN panic message is not a string literal
std::panic::panic_any(Some(123)); //~ WARN panic message is not a string literal
}
std::panic::panic_any(a!()); //~ WARN panic message is not a string literal
+ unreachable!("{}", a!()); //~ WARN panic message is not a string literal
panic!("{}", 1); //~ WARN panic message is not a string literal
+ unreachable!("{}", 1); //~ WARN panic message is not a string literal
assert!(false, "{}", 1); //~ WARN panic message is not a string literal
debug_assert!(false, "{}", 1); //~ WARN panic message is not a string literal
#[allow(unreachable_code)]
fn main() {
panic!("here's a brace: {"); //~ WARN panic message contains a brace
+ unreachable!("here's a brace: {"); //~ WARN panic message contains a brace
std::panic!("another one: }"); //~ WARN panic message contains a brace
core::panic!("Hello {}"); //~ WARN panic message contains an unused formatting placeholder
assert!(false, "{:03x} {test} bla");
debug_assert!(false, "{{}} bla"); //~ WARN panic message contains braces
panic!(C); //~ WARN panic message is not a string literal
panic!(S); //~ WARN panic message is not a string literal
+ unreachable!(S); //~ WARN panic message is not a string literal
+ unreachable!(S); //~ WARN panic message is not a string literal
std::panic!(123); //~ WARN panic message is not a string literal
core::panic!(&*"abc"); //~ WARN panic message is not a string literal
panic!(Some(123)); //~ WARN panic message is not a string literal
}
panic!(a!()); //~ WARN panic message is not a string literal
+ unreachable!(a!()); //~ WARN panic message is not a string literal
panic!(format!("{}", 1)); //~ WARN panic message is not a string literal
+ unreachable!(format!("{}", 1)); //~ WARN panic message is not a string literal
assert!(false, format!("{}", 1)); //~ WARN panic message is not a string literal
debug_assert!(false, format!("{}", 1)); //~ WARN panic message is not a string literal
| +++++
warning: panic message contains a brace
- --> $DIR/non-fmt-panic.rs:14:31
+ --> $DIR/non-fmt-panic.rs:14:35
+ |
+LL | unreachable!("here's a brace: {");
+ | ^
+ |
+ = note: this message is not used as a format string, but will be in Rust 2021
+help: add a "{}" format string to use the message literally
+ |
+LL | unreachable!("{}", "here's a brace: {");
+ | +++++
+
+warning: panic message contains a brace
+ --> $DIR/non-fmt-panic.rs:15:31
|
LL | std::panic!("another one: }");
| ^
| +++++
warning: panic message contains an unused formatting placeholder
- --> $DIR/non-fmt-panic.rs:15:25
+ --> $DIR/non-fmt-panic.rs:16:25
|
LL | core::panic!("Hello {}");
| ^^
| +++++
warning: panic message contains unused formatting placeholders
- --> $DIR/non-fmt-panic.rs:16:21
+ --> $DIR/non-fmt-panic.rs:17:21
|
LL | assert!(false, "{:03x} {test} bla");
| ^^^^^^ ^^^^^^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:18:20
+ --> $DIR/non-fmt-panic.rs:19:20
|
LL | assert!(false, S);
| ^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:20:20
+ --> $DIR/non-fmt-panic.rs:21:20
|
LL | assert!(false, 123);
| ^^^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:22:20
+ --> $DIR/non-fmt-panic.rs:23:20
|
LL | assert!(false, Some(123));
| ^^^^^^^^^
| +++++++
warning: panic message contains braces
- --> $DIR/non-fmt-panic.rs:24:27
+ --> $DIR/non-fmt-panic.rs:25:27
|
LL | debug_assert!(false, "{{}} bla");
| ^^^^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:25:12
+ --> $DIR/non-fmt-panic.rs:26:12
|
LL | panic!(C);
| ^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:26:12
+ --> $DIR/non-fmt-panic.rs:27:12
|
LL | panic!(S);
| ^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:27:17
+ --> $DIR/non-fmt-panic.rs:28:18
+ |
+LL | unreachable!(S);
+ | ^
+ |
+ = note: this usage of unreachable!() is deprecated; it will be a hard error in Rust 2021
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+ |
+LL | unreachable!("{}", S);
+ | +++++
+
+warning: panic message is not a string literal
+ --> $DIR/non-fmt-panic.rs:29:18
+ |
+LL | unreachable!(S);
+ | ^
+ |
+ = note: this usage of unreachable!() is deprecated; it will be a hard error in Rust 2021
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+ |
+LL | unreachable!("{}", S);
+ | +++++
+
+warning: panic message is not a string literal
+ --> $DIR/non-fmt-panic.rs:30:17
|
LL | std::panic!(123);
| ^^^
| ~~~~~~~~~~~~~~~~~~~~~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:28:18
+ --> $DIR/non-fmt-panic.rs:31:18
|
LL | core::panic!(&*"abc");
| ^^^^^^^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:29:12
+ --> $DIR/non-fmt-panic.rs:32:12
|
LL | panic!(Some(123));
| ^^^^^^^^^
| ~~~~~~~~~~~~~~~~~~~~~
warning: panic message contains an unused formatting placeholder
- --> $DIR/non-fmt-panic.rs:30:12
+ --> $DIR/non-fmt-panic.rs:33:12
|
LL | panic!(concat!("{", "}"));
| ^^^^^^^^^^^^^^^^^
| +++++
warning: panic message contains braces
- --> $DIR/non-fmt-panic.rs:31:5
+ --> $DIR/non-fmt-panic.rs:34:5
|
LL | panic!(concat!("{", "{"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| +++++
warning: panic message contains an unused formatting placeholder
- --> $DIR/non-fmt-panic.rs:33:37
+ --> $DIR/non-fmt-panic.rs:36:37
|
LL | fancy_panic::fancy_panic!("test {} 123");
| ^^
= note: this message is not used as a format string when given without arguments, but will be in Rust 2021
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:43:12
+ --> $DIR/non-fmt-panic.rs:46:12
|
LL | panic!(a!());
| ^^^^
| ~~~~~~~~~~~~~~~~~~~~~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:45:12
+ --> $DIR/non-fmt-panic.rs:47:18
+ |
+LL | unreachable!(a!());
+ | ^^^^
+ |
+ = note: this usage of unreachable!() is deprecated; it will be a hard error in Rust 2021
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+help: add a "{}" format string to Display the message
+ |
+LL | unreachable!("{}", a!());
+ | +++++
+
+warning: panic message is not a string literal
+ --> $DIR/non-fmt-panic.rs:49:12
|
LL | panic!(format!("{}", 1));
| ^^^^^^^^^^^^^^^^
|
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:46:20
+ --> $DIR/non-fmt-panic.rs:50:18
+ |
+LL | unreachable!(format!("{}", 1));
+ | ^^^^^^^^^^^^^^^^
+ |
+ = note: this usage of unreachable!() is deprecated; it will be a hard error in Rust 2021
+ = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
+ = note: the unreachable!() macro supports formatting, so there's no need for the format!() macro here
+help: remove the `format!(..)` macro call
+ |
+LL - unreachable!(format!("{}", 1));
+LL + unreachable!("{}", 1);
+ |
+
+warning: panic message is not a string literal
+ --> $DIR/non-fmt-panic.rs:51:20
|
LL | assert!(false, format!("{}", 1));
| ^^^^^^^^^^^^^^^^
|
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:47:26
+ --> $DIR/non-fmt-panic.rs:52:26
|
LL | debug_assert!(false, format!("{}", 1));
| ^^^^^^^^^^^^^^^^
|
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:49:12
+ --> $DIR/non-fmt-panic.rs:54:12
|
LL | panic![123];
| ^^^
| ~~~~~~~~~~~~~~~~~~~~~~ ~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:50:12
+ --> $DIR/non-fmt-panic.rs:55:12
|
LL | panic!{123};
| ^^^
| ~~~~~~~~~~~~~~~~~~~~~~ ~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:67:12
+ --> $DIR/non-fmt-panic.rs:72:12
|
LL | panic!(v);
| ------ ^
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:68:20
+ --> $DIR/non-fmt-panic.rs:73:20
|
LL | assert!(false, v);
| ^
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/panic-macro-consistency.html>
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:72:12
+ --> $DIR/non-fmt-panic.rs:77:12
|
LL | panic!(v);
| ^
| ~~~~~~~~~~~~~~~~~~~~~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:73:20
+ --> $DIR/non-fmt-panic.rs:78:20
|
LL | assert!(false, v);
| ^
| +++++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:77:12
+ --> $DIR/non-fmt-panic.rs:82:12
|
LL | panic!(v);
| ^
| ~~~~~~~~~~~~~~~~~~~~~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:78:20
+ --> $DIR/non-fmt-panic.rs:83:20
|
LL | assert!(false, v);
| ^
| +++++
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:82:12
+ --> $DIR/non-fmt-panic.rs:87:12
|
LL | panic!(v);
| ^
| ~~~~~~~~~~~~~~~~~~~~~
warning: panic message is not a string literal
- --> $DIR/non-fmt-panic.rs:83:20
+ --> $DIR/non-fmt-panic.rs:88:20
|
LL | assert!(false, v);
| ^
LL | assert!(false, "{}", v);
| +++++
-warning: 30 warnings emitted
+warning: 35 warnings emitted
// build-pass
// ignore-pass
-// no-system-llvm
+// min-llvm-version: 14.0.0
// revisions: all inline merge1 merge2
// compile-flags: --crate-type=lib -Cdebuginfo=1 -Copt-level=2
//
// [merge1] compile-flags: -Cremark=all -Cremark=giraffe
// [merge2] compile-flags: -Cremark=inline -Cremark=giraffe
//
-// error-pattern: inline: f not inlined into g
+// error-pattern: inline: 'f' not inlined into 'g'
// dont-check-compiler-stderr
#[no_mangle]
--- /dev/null
+// unset-rustc-env:RUST_BACKTRACE
+// compile-flags:-Z treat-err-as-bug=1
+// error-pattern:stack backtrace:
+// failure-status:101
+// normalize-stderr-test "note: .*" -> ""
+// normalize-stderr-test "thread 'rustc' .*" -> ""
+// normalize-stderr-test " .*\n" -> ""
+
+fn main() { missing_ident; }
--- /dev/null
+error[E0425]: cannot find value `missing_ident` in this scope
+LL | fn main() { missing_ident; }
+
+
+stack backtrace:
+
+error: internal compiler error: unexpected panic
+
+
+
+
+
+
+
+
+
+query stack during panic:
+end of query stack
--- /dev/null
+fn main() {
+ let ok = r"ab\[c";
+ let bad = "ab\[c";
+ //~^ ERROR unknown character escape: `[`
+ //~| HELP for more information, visit <https://static.rust-lang.org/doc/master/reference.html#literals>
+ //~| HELP if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal
+}
--- /dev/null
+error: unknown character escape: `[`
+ --> $DIR/bad-escape-suggest-raw-string.rs:3:19
+ |
+LL | let bad = "ab\[c";
+ | ^ unknown character escape
+ |
+ = help: for more information, visit <https://static.rust-lang.org/doc/master/reference.html#literals>
+help: if you meant to write a literal backslash (perhaps escaping in a regular expression), consider a raw string literal
+ |
+LL | let bad = r"ab\[c";
+ | ~~~~~~~~
+
+error: aborting due to previous error
+
LL | "invalid-ab_isize"
| ^^^^^^^^^^^^^^^^^^ invalid ABI
|
- = help: valid ABIs: Rust, C, C-unwind, cdecl, stdcall, stdcall-unwind, fastcall, vectorcall, thiscall, thiscall-unwind, aapcs, win64, sysv64, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
+ = help: valid ABIs: Rust, C, C-unwind, cdecl, cdecl-unwind, stdcall, stdcall-unwind, fastcall, fastcall-unwind, vectorcall, vectorcall-unwind, thiscall, thiscall-unwind, aapcs, aapcs-unwind, win64, win64-unwind, sysv64, sysv64-unwind, ptx-kernel, msp430-interrupt, x86-interrupt, amdgpu-kernel, efiapi, avr-interrupt, avr-non-blocking-interrupt, C-cmse-nonsecure-call, wasm, system, system-unwind, rust-intrinsic, rust-call, platform-intrinsic, unadjusted
error: aborting due to previous error
fn main() {
f<'a,>
//~^ ERROR expected
+ //~| ERROR expected
+}
+
+fn bar(a: usize, b: usize) -> usize {
+ a + b
+}
+
+fn foo() {
+ let x = 1;
+ bar('y, x);
+ //~^ ERROR expected
}
+error: expected `while`, `for`, `loop` or `{` after a label
+ --> $DIR/issue-93282.rs:2:9
+ |
+LL | f<'a,>
+ | ^ expected `while`, `for`, `loop` or `{` after a label
+
error: expected one of `.`, `:`, `;`, `?`, `for`, `loop`, `while`, `{`, `}`, or an operator, found `,`
--> $DIR/issue-93282.rs:2:9
|
LL | f::<'a,>
| ++
-error: aborting due to previous error
+error: expected `while`, `for`, `loop` or `{` after a label
+ --> $DIR/issue-93282.rs:13:11
+ |
+LL | bar('y, x);
+ | ^ expected `while`, `for`, `loop` or `{` after a label
+
+error: aborting due to 3 previous errors
let _ = f<'_, i8>();
//~^ ERROR expected one of
//~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+ //~| ERROR expected
f<'_>();
//~^ comparison operators cannot be chained
//~| HELP use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
+ //~| ERROR expected
let _ = f<u8>;
//~^ ERROR comparison operators cannot be chained
LL | let _ = f::<u8, i8>();
| ++
+error: expected `while`, `for`, `loop` or `{` after a label
+ --> $DIR/require-parens-for-chained-comparison.rs:22:17
+ |
+LL | let _ = f<'_, i8>();
+ | ^ expected `while`, `for`, `loop` or `{` after a label
+
error: expected one of `.`, `:`, `;`, `?`, `else`, `for`, `loop`, `while`, `{`, or an operator, found `,`
--> $DIR/require-parens-for-chained-comparison.rs:22:17
|
LL | let _ = f::<'_, i8>();
| ++
+error: expected `while`, `for`, `loop` or `{` after a label
+ --> $DIR/require-parens-for-chained-comparison.rs:27:9
+ |
+LL | f<'_>();
+ | ^ expected `while`, `for`, `loop` or `{` after a label
+
error: comparison operators cannot be chained
- --> $DIR/require-parens-for-chained-comparison.rs:26:6
+ --> $DIR/require-parens-for-chained-comparison.rs:27:6
|
LL | f<'_>();
| ^ ^
| ++
error: comparison operators cannot be chained
- --> $DIR/require-parens-for-chained-comparison.rs:30:14
+ --> $DIR/require-parens-for-chained-comparison.rs:32:14
|
LL | let _ = f<u8>;
| ^ ^
= help: use `::<...>` instead of `<...>` to specify lifetime, type, or const arguments
= help: or use `(...)` if you meant to specify fn arguments
-error: aborting due to 8 previous errors
+error: aborting due to 10 previous errors
--- /dev/null
+// edition:2018
+#![feature(pin_macro)]
+
+use core::{
+ marker::PhantomPinned,
+ mem,
+ pin::{pin, Pin},
+};
+
+fn main() {
+ let mut phantom_pinned = pin!(PhantomPinned);
+ mem::take(phantom_pinned.pointer); //~ ERROR use of unstable library feature 'unsafe_pin_internals'
+}
--- /dev/null
+error[E0658]: use of unstable library feature 'unsafe_pin_internals'
+ --> $DIR/cant_access_internals.rs:12:15
+ |
+LL | mem::take(phantom_pinned.pointer);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: add `#![feature(unsafe_pin_internals)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+// edition:2018
+#![feature(pin_macro)]
+
+use core::{
+ convert::identity,
+ marker::PhantomPinned,
+ mem::drop as stuff,
+ pin::pin,
+};
+
+fn function_call_stops_borrow_extension() {
+ let phantom_pinned = identity(pin!(PhantomPinned));
+ //~^ ERROR temporary value dropped while borrowed
+ stuff(phantom_pinned)
+}
+
+fn promotion_only_works_for_the_innermost_block() {
+ let phantom_pinned = {
+ let phantom_pinned = pin!(PhantomPinned);
+ //~^ ERROR temporary value dropped while borrowed
+ phantom_pinned
+ };
+ stuff(phantom_pinned)
+}
+
+fn main() {
+ function_call_stops_borrow_extension();
+ promotion_only_works_for_the_innermost_block();
+}
--- /dev/null
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/lifetime_errors_on_promotion_misusage.rs:12:35
+ |
+LL | let phantom_pinned = identity(pin!(PhantomPinned));
+ | ^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
+ | |
+ | creates a temporary which is freed while still in use
+LL |
+LL | stuff(phantom_pinned)
+ | -------------- borrow later used here
+ |
+ = note: consider using a `let` binding to create a longer lived value
+ = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0716]: temporary value dropped while borrowed
+ --> $DIR/lifetime_errors_on_promotion_misusage.rs:19:30
+ |
+LL | let phantom_pinned = {
+ | -------------- borrow later stored here
+LL | let phantom_pinned = pin!(PhantomPinned);
+ | ^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
+...
+LL | };
+ | - temporary value is freed at the end of this statement
+ |
+ = note: consider using a `let` binding to create a longer lived value
+ = note: this error originates in the macro `pin` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0716`.
lit.set_span(crate::Span::recover_proc_macro_span(2));
lit
} else {
- {
- ::core::panicking::panic("internal error: entered unreachable code")
- }
+ ::core::panicking::panic("internal error: entered unreachable code")
}
})),
crate::TokenStream::from(crate::TokenTree::Punct(crate::Punct::new('\u{3b}',
--- /dev/null
+// run-pass
+// min-llvm-version: 13.0
+// compiler-flags: -O
+
+// Regression test for issue #80309
+
+pub fn zero(x: usize) -> usize {
+ std::ptr::null::<i8>().wrapping_add(x) as usize - x
+}
+pub fn qux(x: &[i8]) -> i8 {
+ x[zero(x.as_ptr() as usize)]
+}
+
+fn main() {
+ let z = vec![42, 43];
+ println!("{}", qux(&z));
+}
--- /dev/null
+// run-pass
+// min-llvm-version: 13.0
+// compiler-flags: -O
+
+// Regression test for issue #80309
+
+pub unsafe fn foo(x: *const i8) -> i8 {
+ *x.wrapping_sub(x as _).wrapping_add(x as _)
+}
+
+fn main() {
+ let x = 42;
+ println!("{}", unsafe { foo(&x) });
+}
error[E0277]: the trait bound `for<'z> T: Trait2<'y, 'z>` is not satisfied
- --> $DIR/regions-implied-bounds-projection-gap-hr-1.rs:21:4
+ --> $DIR/regions-implied-bounds-projection-gap-hr-1.rs:21:49
|
LL | fn callee<'x, 'y, T>(t: &'x dyn for<'z> Trait1< <T as Trait2<'y, 'z>>::Foo >)
- | ^^^^^^ the trait `for<'z> Trait2<'y, 'z>` is not implemented for `T`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'z> Trait2<'y, 'z>` is not implemented for `T`
|
help: consider restricting type parameter `T`
|
[$DIR/dbg-macro-expected-behavior.rs:20] Unit = Unit
[$DIR/dbg-macro-expected-behavior.rs:21] a = Unit
-[$DIR/dbg-macro-expected-behavior.rs:27] Point{x: 42, y: 24,} = Point {
+[$DIR/dbg-macro-expected-behavior.rs:27] Point { x: 42, y: 24 } = Point {
x: 42,
y: 24,
}
pub const fn add_u32(a: u32, b: u32) -> u32 {
a.plus(b)
- //~^ ERROR calls in constant functions are limited to constant functions
+ //~^ ERROR the trait bound
+ //~| ERROR cannot call non-const fn
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/call-const-trait-method-fail.rs:24:5
+error[E0277]: the trait bound `u32: ~const Plus` is not satisfied
+ --> $DIR/call-const-trait-method-fail.rs:24:7
|
LL | a.plus(b)
- | ^^^^^^^^^
+ | ^^^^^^^ the trait `~const Plus` is not implemented for `u32`
+ |
+note: the trait `Plus` is implemented for `u32`, but that implementation is not `const`
+ --> $DIR/call-const-trait-method-fail.rs:24:7
+ |
+LL | a.plus(b)
+ | ^^^^^^^
+
+error[E0015]: cannot call non-const fn `<u32 as Plus>::plus` in constant functions
+ --> $DIR/call-const-trait-method-fail.rs:24:7
+ |
+LL | a.plus(b)
+ | ^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: aborting due to previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
pub const fn equals_self<T: PartialEq>(t: &T) -> bool {
*t == *t
- //~^ ERROR calls in constant functions are limited to constant functions
+ //~^ ERROR can't compare
+ //~| ERROR cannot call non-const
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0277]: can't compare `T` with `T` in const contexts
+ --> $DIR/call-generic-method-fail.rs:5:5
+ |
+LL | *t == *t
+ | ^^^^^^^^ no implementation for `T == T`
+ |
+note: the trait `PartialEq` is implemented for `T`, but that implementation is not `const`
--> $DIR/call-generic-method-fail.rs:5:5
|
LL | *t == *t
| ^^^^^^^^
-error: aborting due to previous error
+error[E0015]: cannot call non-const operator in constant functions
+ --> $DIR/call-generic-method-fail.rs:5:5
+ |
+LL | *t == *t
+ | ^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+help: consider further restricting this bound
+ |
+LL | pub const fn equals_self<T: PartialEq + ~const std::cmp::PartialEq>(t: &T) -> bool {
+ | ++++++++++++++++++++++++++++
+
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
impl const T for S {
fn foo() { non_const() }
- //~^ ERROR calls in constant functions
+ //~^ ERROR cannot call non-const fn
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `non_const` in constant functions
--> $DIR/const-check-fns-in-const-impl.rs:11:16
|
LL | fn foo() { non_const() }
| ^^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
const fn test() {
NonConstImpl.a();
- //~^ ERROR calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ //~^ ERROR the trait bound
+ //~| ERROR cannot call non-const fn
ConstImpl.a();
}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/const-default-method-bodies.rs:25:5
+error[E0277]: the trait bound `NonConstImpl: ~const ConstDefaultFn` is not satisfied
+ --> $DIR/const-default-method-bodies.rs:25:18
|
LL | NonConstImpl.a();
- | ^^^^^^^^^^^^^^^^
+ | ^^^ the trait `~const ConstDefaultFn` is not implemented for `NonConstImpl`
+ |
+note: the trait `ConstDefaultFn` is implemented for `NonConstImpl`, but that implementation is not `const`
+ --> $DIR/const-default-method-bodies.rs:25:18
+ |
+LL | NonConstImpl.a();
+ | ^^^
+
+error[E0015]: cannot call non-const fn `<NonConstImpl as ConstDefaultFn>::a` in constant functions
+ --> $DIR/const-default-method-bodies.rs:25:18
+ |
+LL | NonConstImpl.a();
+ | ^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: aborting due to previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/cross-crate.rs:15:5
+error[E0277]: the trait bound `cross_crate::NonConst: ~const cross_crate::MyTrait` is not satisfied
+ --> $DIR/cross-crate.rs:15:14
|
LL | NonConst.func();
- | ^^^^^^^^^^^^^^^
+ | ^^^^^^ the trait `~const cross_crate::MyTrait` is not implemented for `cross_crate::NonConst`
+ |
+note: the trait `cross_crate::MyTrait` is implemented for `cross_crate::NonConst`, but that implementation is not `const`
+ --> $DIR/cross-crate.rs:15:14
+ |
+LL | NonConst.func();
+ | ^^^^^^
+
+error[E0015]: cannot call non-const fn `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
+ --> $DIR/cross-crate.rs:15:14
+ |
+LL | NonConst.func();
+ | ^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: aborting due to previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
}
const fn const_context() {
- NonConst.func();
- //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ NonConst.func(); //~ ERROR: cannot call non-const fn
+ //[gated]~^ ERROR: the trait bound
Const.func();
- //[stock]~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ //[stock]~^ ERROR: cannot call non-const fn
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/cross-crate.rs:15:5
+error[E0015]: cannot call non-const fn `<cross_crate::NonConst as cross_crate::MyTrait>::func` in constant functions
+ --> $DIR/cross-crate.rs:15:14
|
LL | NonConst.func();
- | ^^^^^^^^^^^^^^^
+ | ^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/cross-crate.rs:17:5
+error[E0015]: cannot call non-const fn `<cross_crate::Const as cross_crate::MyTrait>::func` in constant functions
+ --> $DIR/cross-crate.rs:17:11
|
LL | Const.func();
- | ^^^^^^^^^^^^
+ | ^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: aborting due to 2 previous errors
#[default_method_body_is_const]
fn b(&self) {
().a()
- //~^ ERROR calls in constant functions are limited
+ //~^ ERROR the trait bound
+ //~| ERROR cannot call
}
}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
- --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:9
+error[E0277]: the trait bound `(): ~const Tr` is not satisfied
+ --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:12
|
LL | ().a()
- | ^^^^^^
+ | ^^^ the trait `~const Tr` is not implemented for `()`
+ |
+note: the trait `Tr` is implemented for `()`, but that implementation is not `const`
+ --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:12
+ |
+LL | ().a()
+ | ^^^
+
+error[E0015]: cannot call non-const fn `<() as Tr>::a` in constant functions
+ --> $DIR/default-method-body-is-const-same-trait-ck.rs:10:12
+ |
+LL | ().a()
+ | ^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: aborting due to previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
pub const fn foo<T: A>() -> bool {
T::assoc()
- //~^ ERROR calls in constant functions are limited
+ //~^ ERROR the trait bound
+ //~| ERROR cannot call non-const fn
}
fn main() {}
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0277]: the trait bound `T: ~const A` is not satisfied
+ --> $DIR/issue-88155.rs:9:5
+ |
+LL | T::assoc()
+ | ^^^^^^^^^^ the trait `~const A` is not implemented for `T`
+ |
+note: the trait `A` is implemented for `T`, but that implementation is not `const`
+ --> $DIR/issue-88155.rs:9:5
+ |
+LL | T::assoc()
+ | ^^^^^^^^^^
+
+error[E0015]: cannot call non-const fn `<T as A>::assoc` in constant functions
--> $DIR/issue-88155.rs:9:5
|
LL | T::assoc()
| ^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
-error: aborting due to previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0015`.
+Some errors have detailed explanations: E0015, E0277.
+For more information about an error, try `rustc --explain E0015`.
const fn const_context() -> Vec<usize> {
Default::default()
- //[stock]~^ ERROR calls in constant functions are limited
+ //[stock]~^ ERROR cannot call non-const fn
}
fn main() {
-error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `<Vec<usize> as Default>::default` in constant functions
--> $DIR/std-impl-gate.rs:13:5
|
LL | Default::default()
| ^^^^^^^^^^^^^^^^^^
+ |
+ = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
+++ /dev/null
-// Check that we check that default associated types satisfy the required
-// bounds on them.
-// ignore-compare-mode-chalk
-
-#![feature(specialization)]
-//~^ WARNING `specialization` is incomplete
-
-trait X {
- type U: Clone;
- fn unsafe_clone(&self, x: Option<&Self::U>) {
- x.cloned();
- }
-}
-
-// We cannot normalize `<T as X>::U` to `str` here, because the default could
-// be overridden. The error here must therefore be found by a method other than
-// normalization.
-impl<T> X for T {
- default type U = str;
- //~^ ERROR the trait bound `str: Clone` is not satisfied
-}
-
-pub fn main() {
- 1.unsafe_clone(None);
-}
+++ /dev/null
-warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/deafult-associated-type-bound-1.rs:5:12
- |
-LL | #![feature(specialization)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
- = help: consider using `min_specialization` instead, which is more stable and complete
-
-error[E0277]: the trait bound `str: Clone` is not satisfied
- --> $DIR/deafult-associated-type-bound-1.rs:19:22
- |
-LL | default type U = str;
- | ^^^ the trait `Clone` is not implemented for `str`
- |
-note: required by a bound in `X::U`
- --> $DIR/deafult-associated-type-bound-1.rs:9:13
- |
-LL | type U: Clone;
- | ^^^^^ required by this bound in `X::U`
-
-error: aborting due to previous error; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// Check that generic predicates are also checked for default associated types.
-#![feature(specialization)]
-//~^ WARNING `specialization` is incomplete
-
-trait X<T> {
- type U: PartialEq<T>;
- fn unsafe_compare(x: Option<Self::U>, y: Option<T>) {
- match (x, y) {
- (Some(a), Some(b)) => a == b,
- _ => false,
- };
- }
-}
-
-impl<B: 'static, T> X<B> for T {
- default type U = &'static B;
- //~^ ERROR can't compare `&'static B` with `B`
-}
-
-pub fn main() {
- <i32 as X<i32>>::unsafe_compare(None, None);
-}
+++ /dev/null
-warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/deafult-associated-type-bound-2.rs:2:12
- |
-LL | #![feature(specialization)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
- = help: consider using `min_specialization` instead, which is more stable and complete
-
-error[E0277]: can't compare `&'static B` with `B`
- --> $DIR/deafult-associated-type-bound-2.rs:16:22
- |
-LL | default type U = &'static B;
- | ^^^^^^^^^^ no implementation for `&'static B == B`
- |
- = help: the trait `PartialEq<B>` is not implemented for `&'static B`
-note: required by a bound in `X::U`
- --> $DIR/deafult-associated-type-bound-2.rs:6:13
- |
-LL | type U: PartialEq<T>;
- | ^^^^^^^^^^^^ required by this bound in `X::U`
-help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
- |
-LL | impl<B: 'static, T> X<B> for T where &'static B: PartialEq<B> {
- | ++++++++++++++++++++++++++++++
-
-error: aborting due to previous error; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// Check that default generics associated types are validated.
-
-#![feature(specialization)]
-#![feature(generic_associated_types)]
-//~^^ WARNING `specialization` is incomplete
-
-trait X {
- type U<'a>: PartialEq<&'a Self> where Self: 'a;
- fn unsafe_compare<'b>(x: Option<Self::U<'b>>, y: Option<&'b Self>) {
- match (x, y) {
- (Some(a), Some(b)) => a == b,
- _ => false,
- };
- }
-}
-
-impl<T: 'static> X for T {
- default type U<'a> = &'a T;
- //~^ ERROR can't compare `T` with `T`
-}
-
-struct NotComparable;
-
-pub fn main() {
- <NotComparable as X>::unsafe_compare(None, None);
-}
+++ /dev/null
-warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/deafult-generic-associated-type-bound.rs:3:12
- |
-LL | #![feature(specialization)]
- | ^^^^^^^^^^^^^^
- |
- = note: `#[warn(incomplete_features)]` on by default
- = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
- = help: consider using `min_specialization` instead, which is more stable and complete
-
-error[E0277]: can't compare `T` with `T`
- --> $DIR/deafult-generic-associated-type-bound.rs:18:26
- |
-LL | default type U<'a> = &'a T;
- | ^^^^^ no implementation for `T == T`
- |
- = note: required because of the requirements on the impl of `PartialEq` for `&'a T`
-note: required by a bound in `X::U`
- --> $DIR/deafult-generic-associated-type-bound.rs:8:17
- |
-LL | type U<'a>: PartialEq<&'a Self> where Self: 'a;
- | ^^^^^^^^^^^^^^^^^^^ required by this bound in `X::U`
-help: consider further restricting this bound
- |
-LL | impl<T: 'static + std::cmp::PartialEq> X for T {
- | +++++++++++++++++++++
-
-error: aborting due to previous error; 1 warning emitted
-
-For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Check that we check that default associated types satisfy the required
+// bounds on them.
+
+#![feature(specialization)]
+//~^ WARNING `specialization` is incomplete
+
+trait X {
+ type U: Clone;
+ fn unsafe_clone(&self, x: Option<&Self::U>) {
+ x.cloned();
+ }
+}
+
+// We cannot normalize `<T as X>::U` to `str` here, because the default could
+// be overridden. The error here must therefore be found by a method other than
+// normalization.
+impl<T> X for T {
+ default type U = str;
+ //~^ ERROR the trait bound `str: Clone` is not satisfied
+}
+
+pub fn main() {
+ 1.unsafe_clone(None);
+}
--- /dev/null
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/default-associated-type-bound-1.rs:4:12
+ |
+LL | #![feature(specialization)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+ = help: consider using `min_specialization` instead, which is more stable and complete
+
+error[E0277]: the trait bound `str: Clone` is not satisfied
+ --> $DIR/default-associated-type-bound-1.rs:18:22
+ |
+LL | default type U = str;
+ | ^^^ the trait `Clone` is not implemented for `str`
+ |
+ = help: the following implementations were found:
+ <String as Clone>
+note: required by a bound in `X::U`
+ --> $DIR/default-associated-type-bound-1.rs:8:13
+ |
+LL | type U: Clone;
+ | ^^^^^ required by this bound in `X::U`
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Check that generic predicates are also checked for default associated types.
+#![feature(specialization)]
+//~^ WARNING `specialization` is incomplete
+
+trait X<T> {
+ type U: PartialEq<T>;
+ fn unsafe_compare(x: Option<Self::U>, y: Option<T>) {
+ match (x, y) {
+ (Some(a), Some(b)) => a == b,
+ _ => false,
+ };
+ }
+}
+
+impl<B: 'static, T> X<B> for T {
+ default type U = &'static B;
+ //~^ ERROR can't compare `&'static B` with `B`
+}
+
+pub fn main() {
+ <i32 as X<i32>>::unsafe_compare(None, None);
+}
--- /dev/null
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/default-associated-type-bound-2.rs:2:12
+ |
+LL | #![feature(specialization)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+ = help: consider using `min_specialization` instead, which is more stable and complete
+
+error[E0277]: can't compare `&'static B` with `B`
+ --> $DIR/default-associated-type-bound-2.rs:16:22
+ |
+LL | default type U = &'static B;
+ | ^^^^^^^^^^ no implementation for `&'static B == B`
+ |
+ = help: the trait `PartialEq<B>` is not implemented for `&'static B`
+note: required by a bound in `X::U`
+ --> $DIR/default-associated-type-bound-2.rs:6:13
+ |
+LL | type U: PartialEq<T>;
+ | ^^^^^^^^^^^^ required by this bound in `X::U`
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
+ |
+LL | impl<B: 'static, T> X<B> for T where &'static B: PartialEq<B> {
+ | ++++++++++++++++++++++++++++++
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Check that default generics associated types are validated.
+
+#![feature(specialization)]
+#![feature(generic_associated_types)]
+//~^^ WARNING `specialization` is incomplete
+
+trait X {
+ type U<'a>: PartialEq<&'a Self> where Self: 'a;
+ fn unsafe_compare<'b>(x: Option<Self::U<'b>>, y: Option<&'b Self>) {
+ match (x, y) {
+ (Some(a), Some(b)) => a == b,
+ _ => false,
+ };
+ }
+}
+
+impl<T: 'static> X for T {
+ default type U<'a> = &'a T;
+ //~^ ERROR can't compare `T` with `T`
+}
+
+struct NotComparable;
+
+pub fn main() {
+ <NotComparable as X>::unsafe_compare(None, None);
+}
--- /dev/null
+warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes
+ --> $DIR/default-generic-associated-type-bound.rs:3:12
+ |
+LL | #![feature(specialization)]
+ | ^^^^^^^^^^^^^^
+ |
+ = note: `#[warn(incomplete_features)]` on by default
+ = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information
+ = help: consider using `min_specialization` instead, which is more stable and complete
+
+error[E0277]: can't compare `T` with `T`
+ --> $DIR/default-generic-associated-type-bound.rs:18:26
+ |
+LL | default type U<'a> = &'a T;
+ | ^^^^^ no implementation for `T == T`
+ |
+ = note: required because of the requirements on the impl of `PartialEq` for `&'a T`
+note: required by a bound in `X::U`
+ --> $DIR/default-generic-associated-type-bound.rs:8:17
+ |
+LL | type U<'a>: PartialEq<&'a Self> where Self: 'a;
+ | ^^^^^^^^^^^^^^^^^^^ required by this bound in `X::U`
+help: consider further restricting this bound
+ |
+LL | impl<T: 'static + std::cmp::PartialEq> X for T {
+ | +++++++++++++++++++++
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
#![feature(staged_api)]
+#![feature(const_trait_impl)]
+#![stable(feature = "stable", since = "1.0.0")]
-#![stable(feature = "rust1", since = "1.0.0")]
+#[stable(feature = "stable", since = "1.0.0")]
+pub const fn foo() {} //~ ERROR function has missing const stability attribute
-#[stable(feature = "foo", since = "1.0.0")]
-pub const fn foo() {}
-//~^ ERROR rustc_const_stable
+#[unstable(feature = "unstable", issue = "none")]
+pub const fn bar() {} // ok because function is unstable
-#[unstable(feature = "bar", issue = "none")]
-pub const fn bar() {} // ok
+#[stable(feature = "stable", since = "1.0.0")]
+pub struct Foo;
+impl Foo {
+ #[stable(feature = "stable", since = "1.0.0")]
+ pub const fn foo() {} //~ ERROR associated function has missing const stability attribute
+
+ #[unstable(feature = "unstable", issue = "none")]
+ pub const fn bar() {} // ok because function is unstable
+}
+
+// FIXME Once #![feature(const_trait_impl)] is allowed to be stable, add a test
+// for const trait impls. Right now, a "trait methods cannot be stable const fn"
+// error is emitted. This occurs prior to the lint being tested here, such that
+// the lint cannot currently be tested on this use case.
fn main() {}
-error: `#[stable]` const functions must also be either `#[rustc_const_stable]` or `#[rustc_const_unstable]`
+error: function has missing const stability attribute
--> $DIR/missing-const-stability.rs:6:1
|
LL | pub const fn foo() {}
| ^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to previous error
+error: associated function has missing const stability attribute
+ --> $DIR/missing-const-stability.rs:15:5
+ |
+LL | pub const fn foo() {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
-error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
+error[E0015]: cannot call non-const fn `foo` in statics
--> $DIR/static-vec-repeat-not-constant.rs:3:25
|
LL | static a: [isize; 2] = [foo(); 2];
| ^^^^^
+ |
+ = note: calls in statics are limited to constant functions, tuple structs and tuple variants
error: aborting due to previous error
let _: Option<(i8,)> = Some();
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
+
+ let _: Option<(i32,)> = Some(5_usize);
+ //~^ ERROR mismatched types
+
+ let _: Option<(i32,)> = Some((5_usize));
+ //~^ ERROR mismatched types
}
fn int_bool(_: (i32, bool)) {
| expected 1 argument
|
note: function defined here
- --> $DIR/args-instead-of-tuple-errors.rs:15:4
+ --> $DIR/args-instead-of-tuple-errors.rs:21:4
|
LL | fn int_bool(_: (i32, bool)) {
| ^^^^^^^^ --------------
| |
| expected 1 argument
-error: aborting due to 3 previous errors
+error[E0308]: mismatched types
+ --> $DIR/args-instead-of-tuple-errors.rs:14:34
+ |
+LL | let _: Option<(i32,)> = Some(5_usize);
+ | ^^^^^^^ expected tuple, found `usize`
+ |
+ = note: expected tuple `(i32,)`
+ found type `usize`
+
+error[E0308]: mismatched types
+ --> $DIR/args-instead-of-tuple-errors.rs:17:34
+ |
+LL | let _: Option<(i32,)> = Some((5_usize));
+ | ^^^^^^^^^ expected tuple, found `usize`
+ |
+ = note: expected tuple `(i32,)`
+ found type `usize`
+
+error: aborting due to 5 previous errors
-For more information about this error, try `rustc --explain E0061`.
+Some errors have detailed explanations: E0061, E0308.
+For more information about an error, try `rustc --explain E0061`.
let _: Option<()> = Some(());
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
+ let _: Option<(i32,)> = Some((3,));
+ //~^ ERROR mismatched types
+
+ let _: Option<(i32,)> = Some((3,));
+ //~^ ERROR mismatched types
+
two_ints((1, 2)); //~ ERROR this function takes 1 argument
with_generic((3, 4)); //~ ERROR this function takes 1 argument
let _: Option<()> = Some();
//~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
+ let _: Option<(i32,)> = Some(3);
+ //~^ ERROR mismatched types
+
+ let _: Option<(i32,)> = Some((3));
+ //~^ ERROR mismatched types
+
two_ints(1, 2); //~ ERROR this function takes 1 argument
with_generic(3, 4); //~ ERROR this function takes 1 argument
LL | let _: Option<()> = Some(());
| ++
+error[E0308]: mismatched types
+ --> $DIR/args-instead-of-tuple.rs:14:34
+ |
+LL | let _: Option<(i32,)> = Some(3);
+ | ^ expected tuple, found integer
+ |
+ = note: expected tuple `(i32,)`
+ found type `{integer}`
+help: use a trailing comma to create a tuple with one element
+ |
+LL | let _: Option<(i32,)> = Some((3,));
+ | + ++
+
+error[E0308]: mismatched types
+ --> $DIR/args-instead-of-tuple.rs:17:34
+ |
+LL | let _: Option<(i32,)> = Some((3));
+ | ^^^ expected tuple, found integer
+ |
+ = note: expected tuple `(i32,)`
+ found type `{integer}`
+help: use a trailing comma to create a tuple with one element
+ |
+LL | let _: Option<(i32,)> = Some((3,));
+ | +
+
error[E0061]: this function takes 1 argument but 2 arguments were supplied
- --> $DIR/args-instead-of-tuple.rs:14:5
+ --> $DIR/args-instead-of-tuple.rs:20:5
|
LL | two_ints(1, 2);
| ^^^^^^^^ - - supplied 2 arguments
|
note: function defined here
- --> $DIR/args-instead-of-tuple.rs:19:4
+ --> $DIR/args-instead-of-tuple.rs:25:4
|
LL | fn two_ints(_: (i32, i32)) {
| ^^^^^^^^ -------------
| + +
error[E0061]: this function takes 1 argument but 2 arguments were supplied
- --> $DIR/args-instead-of-tuple.rs:16:5
+ --> $DIR/args-instead-of-tuple.rs:22:5
|
LL | with_generic(3, 4);
| ^^^^^^^^^^^^ - - supplied 2 arguments
|
note: function defined here
- --> $DIR/args-instead-of-tuple.rs:22:4
+ --> $DIR/args-instead-of-tuple.rs:28:4
|
LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
| ^^^^^^^^^^^^ ----------------
| + +
error[E0061]: this function takes 1 argument but 2 arguments were supplied
- --> $DIR/args-instead-of-tuple.rs:25:9
+ --> $DIR/args-instead-of-tuple.rs:31:9
|
LL | with_generic(a, b);
| ^^^^^^^^^^^^ - - supplied 2 arguments
|
note: function defined here
- --> $DIR/args-instead-of-tuple.rs:22:4
+ --> $DIR/args-instead-of-tuple.rs:28:4
|
LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
| ^^^^^^^^^^^^ ----------------
LL | with_generic((a, b));
| + +
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
-For more information about this error, try `rustc --explain E0061`.
+Some errors have detailed explanations: E0061, E0308.
+For more information about an error, try `rustc --explain E0061`.
| required by a bound introduced by this call
|
= note: to coerce a `String` into a `&str`, use `&*` as a prefix
+ = help: the following implementations were found:
+ <String as From<&String>>
+ <String as From<&mut str>>
+ <String as From<&str>>
+ <String as From<Box<str>>>
+ and 2 others
= note: required because of the requirements on the impl of `Into<&str>` for `String`
note: required by a bound in `foo`
--> $DIR/into-str.rs:1:31
LL | let _: &[i8] = data.into();
| ^^^^ the trait `From<&[u8]>` is not implemented for `&[i8]`
|
+ = help: the following implementations were found:
+ <[T; LANES] as From<Simd<T, LANES>>>
+ <[bool; LANES] as From<Mask<T, LANES>>>
= note: required because of the requirements on the impl of `Into<&[i8]>` for `&[u8]`
error: aborting due to previous error
| |
| required by a bound introduced by this call
|
+ = help: the following implementations were found:
+ <&f32 as Tr>
note: required by a bound in `bar`
--> $DIR/issue-84973-negative.rs:5:11
|
help: use a trailing comma to create a tuple with one element
|
LL | let _x: (i32,) = (5,);
- | ~~~~
+ | +
error[E0308]: mismatched types
--> $DIR/issue-86100-tuple-paren-comma.rs:13:9
help: use a trailing comma to create a tuple with one element
|
LL | foo((Some(3),));
- | ~~~~~~~~~~
+ | +
error[E0308]: mismatched types
--> $DIR/issue-86100-tuple-paren-comma.rs:17:22
help: use a trailing comma to create a tuple with one element
|
LL | let _s = S { _s: ("abc".to_string(),) };
- | ~~~~~~~~~~~~~~~~~~~~
+ | +
error[E0308]: mismatched types
--> $DIR/issue-86100-tuple-paren-comma.rs:23:22
--- /dev/null
+// Regression test for issue #57596, where -Zverbose flag unintentionally
+// affected produced symbols making it impossible to link between crates
+// with a different value of the flag (for symbols involving generic
+// arguments equal to defaults of their respective parameters).
+//
+// build-pass
+// compile-flags: -Zverbose
+
+pub fn error(msg: String) -> Box<dyn std::error::Error> {
+ msg.into()
+}
+
+fn main() {
+ error(String::new());
+}
--- /dev/null
+error: Target features paca, pacg must all be enabled or disabled together
+
+error: aborting due to previous error
+
--- /dev/null
+// only-aarch64
+// revisions: one two three four
+//[one] compile-flags: -C target-feature=+paca
+//[two] compile-flags: -C target-feature=-pacg,+pacg
+//[three] compile-flags: -C target-feature=+paca,+pacg,-paca
+//[four] check-pass
+//[four] compile-flags: -C target-feature=-paca,+pacg -C target-feature=+paca
+
+fn main() {}
--- /dev/null
+error: Target features paca, pacg must all be enabled or disabled together
+
+error: aborting due to previous error
+
--- /dev/null
+error: Target features paca, pacg must all be enabled or disabled together
+
+error: aborting due to previous error
+
--- /dev/null
+// only-aarch64
+// build-fail
+
+#![feature(aarch64_target_feature, target_feature_11)]
+
+fn main() {
+ #[target_feature(enable = "pacg")]
+ //~^ ERROR must all be either enabled or disabled together
+ unsafe fn inner() {}
+
+ unsafe {
+ foo();
+ bar();
+ baz();
+ inner();
+ }
+}
+
+#[target_feature(enable = "paca")]
+//~^ ERROR must all be either enabled or disabled together
+unsafe fn foo() {}
+
+
+#[target_feature(enable = "paca,pacg")]
+unsafe fn bar() {}
+
+#[target_feature(enable = "paca")]
+#[target_feature(enable = "pacg")]
+unsafe fn baz() {}
--- /dev/null
+error: the target features paca, pacg must all be either enabled or disabled together
+ --> $DIR/tied-features.rs:7:5
+ |
+LL | #[target_feature(enable = "pacg")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: add the missing features in a `target_feature` attribute
+
+error: the target features paca, pacg must all be either enabled or disabled together
+ --> $DIR/tied-features.rs:19:1
+ |
+LL | #[target_feature(enable = "paca")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: add the missing features in a `target_feature` attribute
+
+error: aborting due to 2 previous errors
+
+++ /dev/null
-// run-pass
-// compile-flags: --test
-#![feature(allow_fail)]
-#![feature(cfg_panic)]
-
-#[test]
-#[allow_fail]
-fn test1() {
- #[cfg(not(panic = "abort"))]
- panic!();
-}
-
-#[test]
-#[allow_fail]
-fn test2() {
- assert!(true);
-}
+++ /dev/null
-// check-pass
-// compile-flags:--test
-
-#![deny(warnings)]
-
-macro_rules! foo {
- () => (fn foo(){})
-}
-
-#[test]
-foo!(); //~ WARNING `#[test]` attribute should not be used on macros
-
-fn main(){}
+++ /dev/null
-warning: `#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.
- --> $DIR/test-on-macro.rs:11:1
- |
-LL | foo!();
- | ^^^^^^^
-
-warning: 1 warning emitted
-
--- /dev/null
+// compile-flags: --test
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+mod test {}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+mod loooooooooooooong_teeeeeeeeeest {
+ /*
+ this is a comment
+ this comment goes on for a very long time
+ this is to pad out the span for this module for a long time
+ 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.
+ */
+}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+extern "C" {}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+trait Foo {}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+impl Foo for i32 {}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+const FOO: i32 = -1_i32;
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+static BAR: u64 = 10_000_u64;
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+enum MyUnit {
+ Unit,
+}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+struct NewI32(i32);
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+union Spooky {
+ x: i32,
+ y: u32,
+}
+
+#[repr(C, align(64))]
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+#[derive(Copy, Clone, Debug)]
+struct MoreAttrs {
+ a: i32,
+ b: u64,
+}
+
+macro_rules! foo {
+ () => {};
+}
+
+#[test] //~ ERROR: the `#[test]` attribute may only be used on a non-associated function
+foo!();
+
+// make sure it doesn't erroneously trigger on a real test
+#[test]
+fn real_test() {
+ assert_eq!(42_i32, 42_i32);
+}
+
+// make sure it works with cfg test
+#[cfg(test)]
+mod real_tests {
+ #[cfg(test)]
+ fn foo() {}
+
+ #[test]
+ fn bar() {
+ foo();
+ }
+}
--- /dev/null
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:3:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | mod test {}
+ | ----------- expected a non-associated function, found a module
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:6:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | / mod loooooooooooooong_teeeeeeeeeest {
+LL | | /*
+LL | | this is a comment
+LL | | this comment goes on for a very long time
+... |
+LL | | */
+LL | | }
+ | |_- expected a non-associated function, found a module
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:20:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | extern "C" {}
+ | ------------- expected a non-associated function, found an extern block
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:23:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | trait Foo {}
+ | ------------ expected a non-associated function, found a trait
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:26:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | impl Foo for i32 {}
+ | ------------------- expected a non-associated function, found an implementation
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:29:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | const FOO: i32 = -1_i32;
+ | ------------------------ expected a non-associated function, found a constant item
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:32:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | static BAR: u64 = 10_000_u64;
+ | ----------------------------- expected a non-associated function, found a static item
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:35:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | / enum MyUnit {
+LL | | Unit,
+LL | | }
+ | |_- expected a non-associated function, found an enum
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:40:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | struct NewI32(i32);
+ | ------------------- expected a non-associated function, found a struct
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:43:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | / union Spooky {
+LL | | x: i32,
+LL | | y: u32,
+LL | | }
+ | |_- expected a non-associated function, found a union
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:50:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | #[derive(Copy, Clone, Debug)]
+LL | / struct MoreAttrs {
+LL | | a: i32,
+LL | | b: u64,
+LL | | }
+ | |_- expected a non-associated function, found a struct
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: the `#[test]` attribute may only be used on a non-associated function
+ --> $DIR/test-on-not-fn.rs:61:1
+ |
+LL | #[test]
+ | ^^^^^^^
+LL | foo!();
+ | ------- expected a non-associated function, found an item macro invocation
+ |
+ = note: the `#[test]` macro causes a a function to be run on a test and has no effect on non-functions
+help: replace with conditional compilation to make the item only exist when tests are being run
+ |
+LL | #[cfg(test)]
+ | ~~~~~~~~~~~~
+
+error: aborting due to 12 previous errors
+
--- /dev/null
+// compile-flags: -Zmir-opt-level=4
+
+pub fn bar<T>(s: &'static mut ())
+where
+ &'static mut (): Clone, //~ ERROR the trait bound
+{
+ <&'static mut () as Clone>::clone(&s);
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `&'static mut (): Clone` is not satisfied
+ --> $DIR/issue-93008.rs:5:5
+ |
+LL | &'static mut (): Clone,
+ | ^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `&'static mut ()`
+ |
+ = help: see issue #48214
+ = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
LL | f::<dyn X<Y = str>>();
| ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `f`
--> $DIR/check-trait-object-bounds-1.rs:7:9
|
LL | f::<dyn X<Y = str>>();
| ^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `str`
|
+ = help: the following implementations were found:
+ <String as Clone>
note: required by a bound in `f`
--> $DIR/check-trait-object-bounds-4.rs:10:9
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | opts.get(<String as AsRef<str>>::as_ref(opt));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ and 4 other candidates
error[E0283]: type annotations needed
--> $DIR/issue-77982.rs:13:44
|
= help: the following implementations were found:
<&T as Clone>
+ <*const T as Clone>
+ <*mut T as Clone>
= note: `Clone` is implemented for `&T`, but not for `&mut T`
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
#![feature(negative_impls)]
+#![feature(with_negative_coherence)]
pub trait ForeignTrait {}
Qux.clone();
//~^ ERROR no method named `clone` found for struct `Qux`
//~| NOTE method not found in `Qux`
- //~| NOTE `Clone` defines an item `clone`, but is explicitely unimplemented
+ //~| NOTE `Clone` defines an item `clone`, but is explicitly unimplemented
0_u32.bar();
//~^ ERROR no method named `bar` found for type `u32`
//~| NOTE method not found in `u32`
- //~| NOTE `Bar` defines an item `bar`, but is explicitely unimplemented
+ //~| NOTE `Bar` defines an item `bar`, but is explicitly unimplemented
Qux.foo();
//~^ ERROR no method named `foo` found for struct `Qux`
//~| NOTE method not found in `Qux`
- //~| NOTE the following traits define an item `foo`, but are explicitely unimplemented
+ //~| NOTE the following traits define an item `foo`, but are explicitly unimplemented
0_u32.foo();
//~^ ERROR no method named `foo` found for type `u32`
//~| NOTE method not found in `u32`
- //~| NOTE `FooBar` defines an item `foo`, but is explicitely unimplemented
+ //~| NOTE `FooBar` defines an item `foo`, but is explicitly unimplemented
}
| ^^^^^ method not found in `Qux`
|
= help: items from traits can only be used if the trait is implemented and in scope
- = note: the trait `Clone` defines an item `clone`, but is explicitely unimplemented
+ = note: the trait `Clone` defines an item `clone`, but is explicitly unimplemented
error[E0599]: no method named `bar` found for type `u32` in the current scope
--> $DIR/explicitly-unimplemented-error-message.rs:39:11
| ^^^ method not found in `u32`
|
= help: items from traits can only be used if the trait is implemented and in scope
- = note: the trait `Bar` defines an item `bar`, but is explicitely unimplemented
+ = note: the trait `Bar` defines an item `bar`, but is explicitly unimplemented
error[E0599]: no method named `foo` found for struct `Qux` in the current scope
--> $DIR/explicitly-unimplemented-error-message.rs:44:9
| ^^^ method not found in `Qux`
|
= help: items from traits can only be used if the trait is implemented and in scope
- = note: the following traits define an item `foo`, but are explicitely unimplemented:
+ = note: the following traits define an item `foo`, but are explicitly unimplemented:
Foo
FooBar
|
LL | trait Foo {
| ^^^^^^^^^
- = note: the trait `FooBar` defines an item `foo`, but is explicitely unimplemented
+ = note: the trait `FooBar` defines an item `foo`, but is explicitly unimplemented
error: aborting due to 4 previous errors
// check-pass
#![feature(negative_impls)]
+#![feature(with_negative_coherence)]
// aux-build: foreign_trait.rs
extern crate foreign_trait;
use foreign_trait::ForeignTrait;
-trait LocalTrait { }
-impl<T: ForeignTrait> LocalTrait for T { }
-impl LocalTrait for String { }
+trait LocalTrait {}
+impl<T: ForeignTrait> LocalTrait for T {}
+impl LocalTrait for String {}
-fn main() { }
+fn main() {}
| | help: consider adding dereference here: `&*string`
| required by a bound introduced by this call
|
+ = help: the following implementations were found:
+ <&str as SomeTrait>
note: required by a bound in `takes_type_parameter`
--> $DIR/issue-62530.rs:4:44
|
= help: the following implementations were found:
<u8 as From<NonZeroU8>>
<u8 as From<bool>>
+ <f32 as From<i16>>
+ <f32 as From<i8>>
+ and 71 others
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, i32>>` for `Result<u64, u8>`
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
-// ignore-compare-mode-chalk
#![feature(type_alias_impl_trait)]
use std::fmt::Debug;
error: type parameter `T` is part of concrete type but not used in parameter list for the `impl Trait` type alias
- --> $DIR/issue-53598.rs:20:42
+ --> $DIR/issue-53598.rs:19:42
|
LL | fn foo<T: Debug>(_: T) -> Self::Item {
| __________________________________________^
-// ignore-compare-mode-chalk
#![feature(arbitrary_self_types)]
#![feature(type_alias_impl_trait)]
error: type parameter `impl Deref<Target = Self>` is part of concrete type but not used in parameter list for the `impl Trait` type alias
- --> $DIR/issue-57700.rs:16:58
+ --> $DIR/issue-57700.rs:15:58
|
LL | fn foo(self: impl Deref<Target = Self>) -> Self::Bar {
| __________________________________________________________^
-// ignore-compare-mode-chalk
-
trait Bug {
type Item: Bug;
error[E0658]: `impl Trait` in type aliases is unstable
- --> $DIR/issue-60371.rs:10:17
+ --> $DIR/issue-60371.rs:8:17
|
LL | type Item = impl Bug;
| ^^^^^^^^
= help: add `#![feature(type_alias_impl_trait)]` to the crate attributes to enable
error[E0277]: the trait bound `(): Bug` is not satisfied
- --> $DIR/issue-60371.rs:12:40
+ --> $DIR/issue-60371.rs:10:40
|
LL | const FUN: fn() -> Self::Item = || ();
| ^ the trait `Bug` is not implemented for `()`
<&() as Bug>
error: non-defining opaque type use in defining scope
- --> $DIR/issue-60371.rs:12:37
+ --> $DIR/issue-60371.rs:10:37
|
LL | impl Bug for &() {
| - cannot use static lifetime; use a bound lifetime instead or remove the lifetime parameter from the opaque type
}
}
+fn qux(a: &Option<Box<[i32; 2]>>) -> i32 {
+ match a.as_deref() {
+ //~^ HELP: consider using `as_deref` here
+ Some([a, b]) => a + b,
+ //~^ ERROR: expected an array or slice
+ //~| NOTE: pattern cannot match with input type
+ _ => 42,
+ }
+}
+
fn main() {}
}
}
+fn qux(a: &Option<Box<[i32; 2]>>) -> i32 {
+ match a {
+ //~^ HELP: consider using `as_deref` here
+ Some([a, b]) => a + b,
+ //~^ ERROR: expected an array or slice
+ //~| NOTE: pattern cannot match with input type
+ _ => 42,
+ }
+}
+
fn main() {}
LL | [a, b] => a + b,
| ^^^^^^ pattern cannot match with input type `Vec<i32>`
-error: aborting due to 3 previous errors
+error[E0529]: expected an array or slice, found `Box<[i32; 2]>`
+ --> $DIR/issue-91328.rs:40:14
+ |
+LL | match a {
+ | - help: consider using `as_deref` here: `a.as_deref()`
+LL |
+LL | Some([a, b]) => a + b,
+ | ^^^^^^ pattern cannot match with input type `Box<[i32; 2]>`
+
+error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0529`.
#![feature(untagged_unions)]
union Test {
- a: A, //~ ERROR unions may not contain fields that need dropping
+ a: A, //~ ERROR unions cannot contain fields that may need dropping
b: B
}
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/issue-41073.rs:4:5
|
LL | a: A,
| ^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<A>,
| +++++++++++++++++++++++ +
#![feature(untagged_unions)]
union Foo {
- bar: Bar, //~ ERROR unions may not contain fields that need dropping
+ bar: Bar, //~ ERROR unions cannot contain fields that may need dropping
}
union Bar {
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-custom-drop.rs:7:5
|
LL | bar: Bar,
| ^^^^^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | bar: std::mem::ManuallyDrop<Bar>,
| +++++++++++++++++++++++ +
= note: the following trait bounds were not satisfied:
`CloneNoCopy: Copy`
which is required by `U5<CloneNoCopy>: Clone`
-help: consider annotating `CloneNoCopy` with `#[derive(Copy)]`
+help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]`
|
-LL | #[derive(Copy)]
+LL | #[derive(Clone, Copy)]
|
error[E0277]: the trait bound `U1: Copy` is not satisfied
= note: the following trait bounds were not satisfied:
`CloneNoCopy: Copy`
which is required by `U5<CloneNoCopy>: Clone`
-help: consider annotating `CloneNoCopy` with `#[derive(Copy)]`
+help: consider annotating `CloneNoCopy` with `#[derive(Clone, Copy)]`
|
-LL | #[derive(Copy)]
+LL | #[derive(Clone, Copy)]
|
error[E0277]: the trait bound `U1: Copy` is not satisfied
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-with-drop-fields.rs:11:5
|
LL | a: String,
| ^^^^^^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<String>,
| +++++++++++++++++++++++ +
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-with-drop-fields.rs:19:5
|
LL | a: S,
| ^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<S>,
| +++++++++++++++++++++++ +
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-with-drop-fields.rs:24:5
|
LL | a: T,
| ^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<T>,
| +++++++++++++++++++++++ +
}
union W {
- a: String, //~ ERROR unions may not contain fields that need dropping
+ a: String, //~ ERROR unions cannot contain fields that may need dropping
b: String, // OK, only one field is reported
}
// `S` doesn't implement `Drop` trait, but still has non-trivial destructor
union Y {
- a: S, //~ ERROR unions may not contain fields that need dropping
+ a: S, //~ ERROR unions cannot contain fields that may need dropping
}
// We don't know if `T` is trivially-destructable or not until trans
union J<T> {
- a: T, //~ ERROR unions may not contain fields that need dropping
+ a: T, //~ ERROR unions cannot contain fields that may need dropping
}
union H<T: Copy> {
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-with-drop-fields.rs:11:5
|
LL | a: String,
| ^^^^^^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<String>,
| +++++++++++++++++++++++ +
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-with-drop-fields.rs:19:5
|
LL | a: S,
| ^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<S>,
| +++++++++++++++++++++++ +
-error[E0740]: unions may not contain fields that need dropping
+error[E0740]: unions cannot contain fields that may need dropping
--> $DIR/union-with-drop-fields.rs:24:5
|
LL | a: T,
| ^^^^
|
-help: wrap the type with `std::mem::ManuallyDrop` and ensure it is manually dropped
+ = note: a type is guaranteed not to need dropping when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type
+help: when the type does not implement `Copy`, wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped
|
LL | a: std::mem::ManuallyDrop<T>,
| +++++++++++++++++++++++ +
anyhow = "1.0.32"
flate2 = "1.0.16"
tar = "0.4.29"
-sha2 = "0.9.1"
+sha2 = "0.10.1"
rayon = "1.5.1"
hex = "0.4.2"
num_cpus = "1.13.0"
// for users to install the additional component manually, if needed.
if self.versions.channel() == "nightly" {
self.extend_profile("complete", &mut manifest.profiles, &["rustc-dev"]);
- self.extend_profile("complete", &mut manifest.profiles, &["rustc-docs"]);
+ // Do not include the rustc-docs component for now, as it causes
+ // conflicts with the rust-docs component when installed. See
+ // #75833.
+ // self.extend_profile("complete", &mut manifest.profiles, &["rustc-docs"]);
}
}
-Subproject commit 25fcb135d02ea897ce894b67ae021f48107d522b
+Subproject commit ea2a21c994ca1e4d4c49412827b3cf4dcb158b1d
## Unreleased / In Rust Nightly
-[e181011...master](https://github.com/rust-lang/rust-clippy/compare/e181011...master)
+[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master)
-## Rust 1.58 (beta)
+## Rust 1.59 (beta)
-Current beta, release 2022-01-13
+Current beta, release 2022-02-24
+
+[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
+
+### New Lints
+
+* [`index_refutable_slice`]
+ [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
+* [`needless_splitn`]
+ [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
+* [`unnecessary_to_owned`]
+ [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
+* [`needless_late_init`]
+ [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
+* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
+* [`return_self_not_must_use`]
+ [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
+* [`init_numbered_fields`]
+ [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
+
+### Moves and Deprecations
+
+* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
+ allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
+* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
+ [`disallowed_methods`]
+ [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
+* Move [`map_flatten`] to `complexity` (now warn-by-default)
+ [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
+
+### Enhancements
+
+* [`match_overlapping_arm`]: Fix false negative where after included ranges,
+ overlapping ranges weren't linted anymore
+ [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
+* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
+ [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
+* [`cast_lossless`]: Now also lints for `bool` to integer casts
+ [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
+* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
+ [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
+* [`needless_borrow`]
+ [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
+ * Lint when a borrow is auto-dereffed more than once
+ * Lint in the trailing expression of a block for a match arm
+* [`strlen_on_c_strings`]
+ [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
+ * Lint when used without a fully-qualified path
+ * Suggest removing the surrounding unsafe block when possible
+* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
+ [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
+* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
+ `rsplit_once`, `replace`, and `replacen`
+ [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
+* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
+ `Vec::new`, `HashSet::new`, and `HashMap::new`
+ [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
+* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
+ [`shadow_unrelated`]
+ [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
+
+### False Positive Fixes
+
+* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
+ cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
+ [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
+* [`manual_split_once`]: No longer suggests code changing the original behavior
+ [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
+* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
+ implementing `FnOnce`
+ [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
+* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
+ triggered on `let-else` statements
+ [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
+* [`if_then_some_else_none`]: No longer lints if there is an early return
+ [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
+* [`needless_collect`]: No longer suggests removal of `collect` when removal
+ would create code requiring mutably borrowing a value multiple times
+ [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
+* [`shadow_same`]: Fix false positive for `async` function's params
+ [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
+* [`suboptimal_flops`]: No longer triggers in constant functions
+ [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
+* [`type_complexity`]: No longer lints on associated types in traits
+ [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
+* [`question_mark`]: No longer lints if returned object is not local
+ [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
+* [`option_if_let_else`]: No longer lint on complex sub-patterns
+ [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
+* [`blocks_in_if_conditions`]: No longer lints on empty closures
+ [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
+* [`enum_variant_names`]: No longer lint when first prefix is only a substring
+ of a camel-case word
+ [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
+* [`identity_op`]: Only lint on integral operands
+ [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
+
+### Suggestion Fixes/Improvements
+
+* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
+ [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
+* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
+ and adapts the suggestion accordingly
+ [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
+* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
+ [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
+* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
+ types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
+* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
+ [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
+* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
+ [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
+* [`option_if_let_else`]: No longer expands macros in the suggestion
+ [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
+* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
+ [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
+* [`doc_markdown`]: No longer uses inline hints to improve readability of
+ suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
+* [`needless_question_mark`]: Now better explains the suggestion
+ [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
+* [`single_char_pattern`]: Escape backslash `\` in suggestion
+ [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
+* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
+ [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
+* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
+ apply this lints suggestion
+ [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
+* [`neg_multiply`]: Now produces a suggestion
+ [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
+* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
+ in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
+* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
+ `to_radians` and `to_degrees`
+ [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
+
+### ICE Fixes
+
+* [`undocumented_unsafe_blocks`]
+ [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
+ [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
+* [`unnecessary_cast`]
+ [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
+
+### Documentation Improvements
+
+* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
+ can be changed crate-wide
+ [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
+* Added a note to the `README` that config changes don't apply to already
+ compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
+
+### Others
+
+* [Clippy's lint
+ list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
+ the version a lint was added. :tada:
+ [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
+* New and improved issue templates
+ [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
+* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
+ file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
+
+## Rust 1.58
+
+Current stable, released 2022-01-13
[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
+### Rust 1.58.1
+
+* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
+ [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
+* [`useless_format`]: Handle implicit named arguments
+ [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
+
### New lints
* [`transmute_num_to_bytes`]
## Rust 1.57
-Current stable, released 2021-12-02
+Released 2021-12-02
[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
+[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
+[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
use std::fs;
use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
-use walkdir::WalkDir;
-
-use crate::clippy_project_root;
+use walkdir::{DirEntry, WalkDir};
#[cfg(not(windows))]
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
///
/// Panics if the path to a test file is broken
pub fn bless(ignore_timestamp: bool) {
- let test_suite_dirs = [
- clippy_project_root().join("tests").join("ui"),
- clippy_project_root().join("tests").join("ui-internal"),
- clippy_project_root().join("tests").join("ui-toml"),
- clippy_project_root().join("tests").join("ui-cargo"),
- ];
- for test_suite_dir in &test_suite_dirs {
- WalkDir::new(test_suite_dir)
- .into_iter()
- .filter_map(Result::ok)
- .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
- .for_each(|f| {
- let test_name = f.path().strip_prefix(test_suite_dir).unwrap();
- for &ext in &["stdout", "stderr", "fixed"] {
- let test_name_ext = format!("stage-id.{}", ext);
- update_reference_file(
- f.path().with_extension(ext),
- test_name.with_extension(test_name_ext),
- ignore_timestamp,
- );
- }
- });
- }
+ let extensions = ["stdout", "stderr", "fixed"].map(OsStr::new);
+
+ WalkDir::new(build_dir())
+ .into_iter()
+ .map(Result::unwrap)
+ .filter(|entry| entry.path().extension().map_or(false, |ext| extensions.contains(&ext)))
+ .for_each(|entry| update_reference_file(&entry, ignore_timestamp));
}
-fn update_reference_file(reference_file_path: PathBuf, test_name: PathBuf, ignore_timestamp: bool) {
- let test_output_path = build_dir().join(test_name);
- let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();
+fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
+ let test_output_path = test_output_entry.path();
- // If compiletest did not write any changes during the test run,
- // we don't have to update anything
- if !test_output_path.exists() {
- return;
- }
+ let reference_file_name = test_output_entry.file_name().to_str().unwrap().replace(".stage-id", "");
+ let reference_file_path = Path::new("tests")
+ .join(test_output_path.strip_prefix(build_dir()).unwrap())
+ .with_file_name(reference_file_name);
// If the test output was not updated since the last clippy build, it may be outdated
- if !ignore_timestamp && !updated_since_clippy_build(&test_output_path).unwrap_or(true) {
+ if !ignore_timestamp && !updated_since_clippy_build(test_output_entry).unwrap_or(true) {
return;
}
if test_output_file != reference_file {
// If a test run caused an output file to change, update the reference file
- println!("updating {}", &relative_reference_file_path.display());
+ println!("updating {}", reference_file_path.display());
fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
-
- // We need to re-read the file now because it was potentially updated from copying
- let reference_file = fs::read(&reference_file_path).unwrap_or_default();
-
- if reference_file.is_empty() {
- // If we copied over an empty output file, we remove the now empty reference file
- println!("removing {}", &relative_reference_file_path.display());
- fs::remove_file(reference_file_path).expect("Could not remove reference file");
- }
}
}
-fn updated_since_clippy_build(path: &Path) -> Option<bool> {
+fn updated_since_clippy_build(entry: &DirEntry) -> Option<bool> {
let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
- let modified = fs::metadata(path).ok()?.modified().ok()?;
+ let modified = entry.metadata().ok()?.modified().ok()?;
Some(modified >= clippy_build_time)
}
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::get_parent_expr;
use clippy_utils::higher;
use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait;
-use clippy_utils::{differing_macro_contexts, get_parent_expr};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor};
if let Some(ex) = &block.expr {
// don't dig into the expression here, just suggest that they remove
// the block
- if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) {
+ if expr.span.from_expansion() || ex.span.from_expansion() {
return;
}
let mut applicability = Applicability::MachineApplicable;
}
} else {
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
- if span.from_expansion() || differing_macro_contexts(expr.span, span) {
+ if span.from_expansion() || expr.span.from_expansion() {
return;
}
// move block higher
then {
let mut ty = ctx.typeck_results().expr_ty(obj);
ty = match ty.kind() {
- ty::Ref(_, ty, ..) => ty,
+ ty::Ref(_, ty, ..) => *ty,
_ => ty
};
if let Some(fn_sig) = fn_sig_opt(self.cx, func.hir_id) {
for (expr, bound) in iter::zip(*args, fn_sig.skip_binder().inputs()) {
// Push found arg type, then visit arg.
- self.ty_bounds.push(TyBound::Ty(bound));
+ self.ty_bounds.push(TyBound::Ty(*bound));
self.visit_expr(expr);
self.ty_bounds.pop();
}
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
for (expr, bound) in iter::zip(*args, fn_sig.inputs()) {
- self.ty_bounds.push(TyBound::Ty(bound));
+ self.ty_bounds.push(TyBound::Ty(*bound));
self.visit_expr(expr);
self.ty_bounds.pop();
}
fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'tcx>> {
let node_ty = cx.typeck_results().node_type_opt(hir_id)?;
- // We can't use `TyS::fn_sig` because it automatically performs substs, this may result in FNs.
+ // We can't use `Ty::fn_sig` because it automatically performs substs, this may result in FNs.
match node_ty.kind() {
ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id)),
ty::FnPtr(fn_sig) => Some(*fn_sig),
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::{self as hir, HirId, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+use rustc_typeck::hir_ty_to_ty;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute).
+ ///
+ /// ### Why is this bad?
+ /// Unions in Rust have unspecified layout by default, despite many people thinking that they
+ /// lay out each field at the start of the union (like C does). That is, there are no guarantees
+ /// about the offset of the fields for unions with multiple non-ZST fields without an explicitly
+ /// specified layout. These cases may lead to undefined behavior in unsafe blocks.
+ ///
+ /// ### Example
+ /// ```rust
+ /// union Foo {
+ /// a: i32,
+ /// b: u32,
+ /// }
+ ///
+ /// fn main() {
+ /// let _x: u32 = unsafe {
+ /// Foo { a: 0_i32 }.b // Undefined behaviour: `b` is allowed to be padding
+ /// };
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[repr(C)]
+ /// union Foo {
+ /// a: i32,
+ /// b: u32,
+ /// }
+ ///
+ /// fn main() {
+ /// let _x: u32 = unsafe {
+ /// Foo { a: 0_i32 }.b // Now defined behaviour, this is just an i32 -> u32 transmute
+ /// };
+ /// }
+ /// ```
+ #[clippy::version = "1.60.0"]
+ pub DEFAULT_UNION_REPRESENTATION,
+ restriction,
+ "unions without a `#[repr(C)]` attribute"
+}
+declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]);
+
+impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ if is_union_with_two_non_zst_fields(cx, item) && !has_c_repr_attr(cx, item.hir_id()) {
+ span_lint_and_help(
+ cx,
+ DEFAULT_UNION_REPRESENTATION,
+ item.span,
+ "this union has the default representation",
+ None,
+ &format!(
+ "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout",
+ cx.tcx.def_path_str(item.def_id.to_def_id())
+ ),
+ );
+ }
+ }
+}
+
+/// Returns true if the given item is a union with at least two non-ZST fields.
+fn is_union_with_two_non_zst_fields(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
+ if let ItemKind::Union(data, _) = &item.kind {
+ data.fields().iter().filter(|f| !is_zst(cx, f.ty)).count() >= 2
+ } else {
+ false
+ }
+}
+
+fn is_zst(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool {
+ if hir_ty.span.from_expansion() {
+ return false;
+ }
+ let ty = hir_ty_to_ty(cx.tcx, hir_ty);
+ if let Ok(layout) = cx.layout_of(ty) {
+ layout.is_zst()
+ } else {
+ false
+ }
+}
+
+fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+ cx.tcx.hir().attrs(hir_id).iter().any(|attr| {
+ if attr.has_name(sym::repr) {
+ if let Some(items) = attr.meta_item_list() {
+ for item in items {
+ if item.is_word() && matches!(item.name_or_empty(), sym::C) {
+ return true;
+ }
+ }
+ }
+ }
+ false
+ })
+}
fn is_auto_borrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
if let Some(Node::Expr(parent)) = parent {
match parent.kind {
- ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
+ // ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
ExprKind::Field(..) => true,
ExprKind::Call(f, _) => f.hir_id == child_id,
_ => false,
fn check_crate(&mut self, cx: &LateContext<'_>) {
for (index, conf) in self.conf_disallowed.iter().enumerate() {
let segs: Vec<_> = conf.path().split("::").collect();
- if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
+ if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
self.disallowed.insert(id, index);
}
}
),
};
let segs: Vec<_> = path.split("::").collect();
- match clippy_utils::path_to_res(cx, &segs) {
+ match clippy_utils::def_path_res(cx, &segs) {
Res::Def(_, id) => {
self.def_ids.insert(id, reason);
},
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
- def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, Ty, TyKind,
+ def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, TyS};
+use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
}
}
-fn in_impl<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, bin_op: DefId) -> Option<(&'tcx Ty<'tcx>, &'tcx Ty<'tcx>)> {
+fn in_impl<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, bin_op: DefId) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
if_chain! {
if let Some(block) = get_enclosing_block(cx, e.hir_id);
if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
}
}
-fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: &TyS<'_>, hir_ty: &Ty<'_>) -> bool {
+fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
if_chain! {
if let ty::Adt(adt_def, _) = middle_ty.kind();
if let Some(local_did) = adt_def.did.as_local();
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::FormatArgsExpn;
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_expn_of, match_function_call, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
"print".into(),
)
};
- let msg = format!("use of `{}.unwrap()`", used);
- if let [write_output] = *format_args.format_string_parts {
- let mut write_output = write_output.to_string();
- if write_output.ends_with('\n') {
- write_output.pop();
- }
-
- let sugg = format!("{}{}!(\"{}\")", prefix, sugg_mac, write_output.escape_default());
- span_lint_and_sugg(
- cx,
- EXPLICIT_WRITE,
- expr.span,
- &msg,
- "try this",
- sugg,
- Applicability::MachineApplicable
- );
- } else {
- // We don't have a proper suggestion
- let help = format!("consider using `{}{}!` instead", prefix, sugg_mac);
- span_lint_and_help(cx, EXPLICIT_WRITE, expr.span, &msg, None, &help);
- }
+ let mut applicability = Applicability::MachineApplicable;
+ let inputs_snippet = snippet_with_applicability(
+ cx,
+ format_args.inputs_span(),
+ "..",
+ &mut applicability,
+ );
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_WRITE,
+ expr.span,
+ &format!("use of `{}.unwrap()`", used),
+ "try this",
+ format!("{}{}!({})", prefix, sugg_mac, inputs_snippet),
+ applicability,
+ )
}
}
}
if overloaded_deref.is_some() {
n_needed = n_total;
}
- ty = target;
+ ty = *target;
} else {
return (n_needed, ty);
}
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
-use clippy_utils::differing_macro_contexts;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
- if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() {
+ if !lhs.span.from_expansion() && !rhs.span.from_expansion() {
let eq_span = lhs.span.between(rhs.span);
if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! {
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
- if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
+ if !lhs.span.from_expansion() && !rhs.span.from_expansion();
// span between BinOp LHS and RHS
let binop_span = lhs.span.between(rhs.span);
// if RHS is an UnOp
if_chain! {
if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
if is_block(else_) || is_if(else_);
- if !differing_macro_contexts(then.span, else_.span);
- if !then.span.from_expansion() && !in_external_macro(cx.sess(), expr.span);
+ if !then.span.from_expansion() && !else_.span.from_expansion();
+ if !in_external_macro(cx.sess(), expr.span);
// workaround for rust-lang/rust#43081
if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
for element in array {
if_chain! {
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
- if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span);
+ if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
let space_span = lhs.span.between(op.span);
if let Some(space_snippet) = snippet_opt(cx, space_span);
let lint_span = lhs.span.with_lo(lhs.span.hi());
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
if_chain! {
- if !differing_macro_contexts(first.span, second.span);
- if !first.span.from_expansion();
+ if !first.span.from_expansion() && !second.span.from_expansion();
if let ExprKind::If(cond_expr, ..) = &first.kind;
if is_block(second) || is_if(second);
use if_chain::if_chain;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
-use clippy_utils::differing_macro_contexts;
use clippy_utils::source::{snippet, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
vis.visit_ty(impl_.self_ty);
for target in &vis.found {
- if differing_macro_contexts(item.span, target.span()) {
+ if item.span.ctxt() != target.span().ctxt() {
return;
}
// The values need to use the `ref` keyword if they can't be copied.
// This will need to be adjusted if the lint want to support multable access in the future
let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
- let needs_ref = !(src_is_ref || is_copy(cx, inner_ty));
+ let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
let slice_info = slices
.entry(value_hir_id)
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths};
+use clippy_utils::{get_trait_def_id, higher, match_def_path, path_def_id, paths};
use rustc_hir::{BorrowKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
},
ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
- ExprKind::Call(path, _) => {
- if let ExprKind::Path(ref qpath) = path.kind {
- is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into()
- } else {
- Finite
- }
- },
+ ExprKind::Call(path, _) => path_def_id(cx, path)
+ .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
+ .into(),
ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite,
}
if let ItemKind::Const(hir_ty, _) = &item.kind;
let ty = hir_ty_to_ty(cx.tcx, hir_ty);
if let ty::Array(element_type, cst) = ty.kind();
- if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val;
+ if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val();
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
- if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
+ if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
if self.maximum_allowed_size < element_count * element_size;
then {
if_chain! {
if let ExprKind::Repeat(_, _) = expr.kind;
if let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind();
- if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val;
+ if let ConstKind::Value(ConstValue::Scalar(element_count)) = cst.val();
if let Ok(element_count) = element_count.to_machine_usize(&cx.tcx);
- if let Ok(element_size) = cx.layout_of(element_type).map(|l| l.size.bytes());
+ if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes());
if self.maximum_allowed_size < element_count * element_size;
then {
span_lint_and_help(
/// Checks if the given signature matches the expectations for `is_empty`
fn check_is_empty_sig(sig: FnSig<'_>, self_kind: ImplicitSelfKind, len_output: LenOutput<'_>) -> bool {
match &**sig.inputs_and_output {
- [arg, res] if len_output.matches_is_empty_output(res) => {
+ [arg, res] if len_output.matches_is_empty_output(*res) => {
matches!(
(arg.kind(), self_kind),
(ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
default::DEFAULT_TRAIT_ACCESS,
default::FIELD_REASSIGN_WITH_DEFAULT,
default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
+ default_union_representation::DEFAULT_UNION_REPRESENTATION,
dereference::EXPLICIT_DEREF_METHODS,
dereference::NEEDLESS_BORROW,
dereference::REF_BINDING_TO_REFERENCE,
transmute::TRANSMUTE_NUM_TO_BYTES,
transmute::TRANSMUTE_PTR_TO_PTR,
transmute::TRANSMUTE_PTR_TO_REF,
+ transmute::TRANSMUTE_UNDEFINED_REPR,
transmute::UNSOUND_COLLECTION_TRANSMUTE,
transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE,
LintId::of(strings::STRING_LIT_AS_BYTES),
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
+ LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(use_self::USE_SELF),
])
LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO),
LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
+ LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![warn(rustc::internal)]
+// Disable this rustc lint for now, as it was also done in rustc
+#![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
mod dbg_macro;
mod default;
mod default_numeric_fallback;
+mod default_union_representation;
mod dereference;
mod derivable_impls;
mod derive;
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
+ store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
// add lints here, do not remove this comment, it's used in `new_lint`
}
use rustc_hir::intravisit::{walk_block, walk_expr};
use rustc_hir::{Expr, Pat};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, UintTy};
+use rustc_middle::ty::{self, Ty, UintTy};
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
// incremented exactly once in the loop body, and initialized to zero
then {
let mut applicability = Applicability::MachineApplicable;
- let int_name = match ty.map(ty::TyS::kind) {
+ let int_name = match ty.map(Ty::kind) {
// usize or inferred
Some(ty::Uint(UintTy::Usize)) | None => {
span_lint_and_sugg(
fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
match ty.kind() {
ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)),
- ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty),
- ty::Slice(ty) | ty::Array(ty, _) => Some(ty),
+ ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, *subty),
+ ty::Slice(ty) | ty::Array(ty, _) => Some(*ty),
_ => None,
}
}
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, TyS};
+use rustc_middle::ty::{self, Ty};
use rustc_span::sym;
use rustc_span::{MultiSpan, Span};
}
}
-fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet {
- fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
+fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet {
+ fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: Ty<'_>, set: &mut HirIdSet) {
match ty.kind() {
ty::Adt(_, generics) => {
for generic in *generics {
use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::single_segment_path;
-use clippy_utils::source::{indent_of, snippet};
+use clippy_utils::source::{indent_of, snippet_with_applicability};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
use rustc_lint::LateContext;
pub(super) fn check<'tcx>(
) {
let arg_expr = match arg.kind {
ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
- ExprKind::MethodCall(method, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => {
- &args[0]
- },
+ ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
_ => return,
};
if_chain! {
- if let PatKind::Binding(.., target, _) = pat.kind;
if let ExprKind::Array([arg_expression]) = arg_expr.kind;
- if let ExprKind::Path(ref list_item) = arg_expression.kind;
- if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
if let ExprKind::Block(block, _) = body.kind;
if !block.stmts.is_empty();
-
then {
- let mut block_str = snippet(cx, block.span, "..").into_owned();
+ let mut applicability = Applicability::MachineApplicable;
+ let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
+ let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
+ let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
block_str.remove(0);
block_str.pop();
-
+ let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
span_lint_and_sugg(
cx,
expr.span,
"for loop over a single element",
"try",
- format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str),
- Applicability::MachineApplicable
+ format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
+ applicability,
)
}
}
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Symbol};
use rustc_typeck::hir_ty_to_ty;
} else {
// (&x).into_iter() ==> x.iter()
// (&mut x).into_iter() ==> x.iter_mut()
- match &arg.kind {
- ExprKind::AddrOf(BorrowKind::Ref, mutability, arg_inner)
- if has_iter_method(cx, cx.typeck_results().expr_ty(arg_inner)).is_some() =>
- {
- let meth_name = match mutability {
+ let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
+ match &arg_ty.kind() {
+ ty::Ref(_, inner_ty, mutbl) if has_iter_method(cx, *inner_ty).is_some() => {
+ let method_name = match mutbl {
Mutability::Mut => "iter_mut",
Mutability::Not => "iter",
};
+ let caller = match &arg.kind {
+ ExprKind::AddrOf(BorrowKind::Ref, _, arg_inner) => arg_inner,
+ _ => arg,
+ };
format!(
"{}.{}()",
- sugg::Sugg::hir_with_applicability(cx, arg_inner, "_", applic_ref).maybe_par(),
- meth_name,
+ sugg::Sugg::hir_with_applicability(cx, caller, "_", applic_ref).maybe_par(),
+ method_name,
)
},
_ => format!(
let obj_ty = cx.typeck_results().expr_ty(obj);
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) {
- let copy = is_copy(cx, ty);
+ let copy = is_copy(cx, *ty);
self.lint_explicit_closure(cx, e.span, args[0].span, copy);
}
} else {
+++ /dev/null
-use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
-use clippy_utils::diagnostics::{
- multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
-};
-use clippy_utils::macros::{is_panic, root_macro_call};
-use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
-use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
-use clippy_utils::visitors::is_local_used;
-use clippy_utils::{
- get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
- path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
- strip_pat_refs,
-};
-use clippy_utils::{higher, peel_blocks_with_stmt};
-use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
-use core::iter::{once, ExactSizeIterator};
-use if_chain::if_chain;
-use rustc_ast::ast::{Attribute, LitKind};
-use rustc_errors::Applicability;
-use rustc_hir::def::{CtorKind, DefKind, Res};
-use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{
- self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
- Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
-};
-use rustc_hir::{HirIdMap, HirIdSet};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, Ty, VariantDef};
-use rustc_semver::RustcVersion;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::{Span, Spanned};
-use rustc_span::{sym, symbol::kw};
-use std::cmp::Ordering;
-use std::collections::hash_map::Entry;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for matches with a single arm where an `if let`
- /// will usually suffice.
- ///
- /// ### Why is this bad?
- /// Just readability – `if let` nests less than a `match`.
- ///
- /// ### Example
- /// ```rust
- /// # fn bar(stool: &str) {}
- /// # let x = Some("abc");
- /// // Bad
- /// match x {
- /// Some(ref foo) => bar(foo),
- /// _ => (),
- /// }
- ///
- /// // Good
- /// if let Some(ref foo) = x {
- /// bar(foo);
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub SINGLE_MATCH,
- style,
- "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for matches with two arms where an `if let else` will
- /// usually suffice.
- ///
- /// ### Why is this bad?
- /// Just readability – `if let` nests less than a `match`.
- ///
- /// ### Known problems
- /// Personal style preferences may differ.
- ///
- /// ### Example
- /// Using `match`:
- ///
- /// ```rust
- /// # fn bar(foo: &usize) {}
- /// # let other_ref: usize = 1;
- /// # let x: Option<&usize> = Some(&1);
- /// match x {
- /// Some(ref foo) => bar(foo),
- /// _ => bar(&other_ref),
- /// }
- /// ```
- ///
- /// Using `if let` with `else`:
- ///
- /// ```rust
- /// # fn bar(foo: &usize) {}
- /// # let other_ref: usize = 1;
- /// # let x: Option<&usize> = Some(&1);
- /// if let Some(ref foo) = x {
- /// bar(foo);
- /// } else {
- /// bar(&other_ref);
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub SINGLE_MATCH_ELSE,
- pedantic,
- "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for matches where all arms match a reference,
- /// suggesting to remove the reference and deref the matched expression
- /// instead. It also checks for `if let &foo = bar` blocks.
- ///
- /// ### Why is this bad?
- /// It just makes the code less readable. That reference
- /// destructuring adds nothing to the code.
- ///
- /// ### Example
- /// ```rust,ignore
- /// // Bad
- /// match x {
- /// &A(ref y) => foo(y),
- /// &B => bar(),
- /// _ => frob(&x),
- /// }
- ///
- /// // Good
- /// match *x {
- /// A(ref y) => foo(y),
- /// B => bar(),
- /// _ => frob(x),
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MATCH_REF_PATS,
- style,
- "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for matches where match expression is a `bool`. It
- /// suggests to replace the expression with an `if...else` block.
- ///
- /// ### Why is this bad?
- /// It makes the code less readable.
- ///
- /// ### Example
- /// ```rust
- /// # fn foo() {}
- /// # fn bar() {}
- /// let condition: bool = true;
- /// match condition {
- /// true => foo(),
- /// false => bar(),
- /// }
- /// ```
- /// Use if/else instead:
- /// ```rust
- /// # fn foo() {}
- /// # fn bar() {}
- /// let condition: bool = true;
- /// if condition {
- /// foo();
- /// } else {
- /// bar();
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MATCH_BOOL,
- pedantic,
- "a `match` on a boolean expression instead of an `if..else` block"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for overlapping match arms.
- ///
- /// ### Why is this bad?
- /// It is likely to be an error and if not, makes the code
- /// less obvious.
- ///
- /// ### Example
- /// ```rust
- /// let x = 5;
- /// match x {
- /// 1..=10 => println!("1 ... 10"),
- /// 5..=15 => println!("5 ... 15"),
- /// _ => (),
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MATCH_OVERLAPPING_ARM,
- style,
- "a `match` with overlapping arms"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for arm which matches all errors with `Err(_)`
- /// and take drastic actions like `panic!`.
- ///
- /// ### Why is this bad?
- /// It is generally a bad practice, similar to
- /// catching all exceptions in java with `catch(Exception)`
- ///
- /// ### Example
- /// ```rust
- /// let x: Result<i32, &str> = Ok(3);
- /// match x {
- /// Ok(_) => println!("ok"),
- /// Err(_) => panic!("err"),
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MATCH_WILD_ERR_ARM,
- pedantic,
- "a `match` with `Err(_)` arm and take drastic actions"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for match which is used to add a reference to an
- /// `Option` value.
- ///
- /// ### Why is this bad?
- /// Using `as_ref()` or `as_mut()` instead is shorter.
- ///
- /// ### Example
- /// ```rust
- /// let x: Option<()> = None;
- ///
- /// // Bad
- /// let r: Option<&()> = match x {
- /// None => None,
- /// Some(ref v) => Some(v),
- /// };
- ///
- /// // Good
- /// let r: Option<&()> = x.as_ref();
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MATCH_AS_REF,
- complexity,
- "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for wildcard enum matches using `_`.
- ///
- /// ### Why is this bad?
- /// New enum variants added by library updates can be missed.
- ///
- /// ### Known problems
- /// Suggested replacements may be incorrect if guards exhaustively cover some
- /// variants, and also may not use correct path to enum if it's not present in the current scope.
- ///
- /// ### Example
- /// ```rust
- /// # enum Foo { A(usize), B(usize) }
- /// # let x = Foo::B(1);
- /// // Bad
- /// match x {
- /// Foo::A(_) => {},
- /// _ => {},
- /// }
- ///
- /// // Good
- /// match x {
- /// Foo::A(_) => {},
- /// Foo::B(_) => {},
- /// }
- /// ```
- #[clippy::version = "1.34.0"]
- pub WILDCARD_ENUM_MATCH_ARM,
- restriction,
- "a wildcard enum match arm using `_`"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for wildcard enum matches for a single variant.
- ///
- /// ### Why is this bad?
- /// New enum variants added by library updates can be missed.
- ///
- /// ### Known problems
- /// Suggested replacements may not use correct path to enum
- /// if it's not present in the current scope.
- ///
- /// ### Example
- /// ```rust
- /// # enum Foo { A, B, C }
- /// # let x = Foo::B;
- /// // Bad
- /// match x {
- /// Foo::A => {},
- /// Foo::B => {},
- /// _ => {},
- /// }
- ///
- /// // Good
- /// match x {
- /// Foo::A => {},
- /// Foo::B => {},
- /// Foo::C => {},
- /// }
- /// ```
- #[clippy::version = "1.45.0"]
- pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
- pedantic,
- "a wildcard enum match for a single variant"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for wildcard pattern used with others patterns in same match arm.
- ///
- /// ### Why is this bad?
- /// Wildcard pattern already covers any other pattern as it will match anyway.
- /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
- ///
- /// ### Example
- /// ```rust
- /// // Bad
- /// match "foo" {
- /// "a" => {},
- /// "bar" | _ => {},
- /// }
- ///
- /// // Good
- /// match "foo" {
- /// "a" => {},
- /// _ => {},
- /// }
- /// ```
- #[clippy::version = "1.42.0"]
- pub WILDCARD_IN_OR_PATTERNS,
- complexity,
- "a wildcard pattern used with others patterns in same match arm"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for matches being used to destructure a single-variant enum
- /// or tuple struct where a `let` will suffice.
- ///
- /// ### Why is this bad?
- /// Just readability – `let` doesn't nest, whereas a `match` does.
- ///
- /// ### Example
- /// ```rust
- /// enum Wrapper {
- /// Data(i32),
- /// }
- ///
- /// let wrapper = Wrapper::Data(42);
- ///
- /// let data = match wrapper {
- /// Wrapper::Data(i) => i,
- /// };
- /// ```
- ///
- /// The correct use would be:
- /// ```rust
- /// enum Wrapper {
- /// Data(i32),
- /// }
- ///
- /// let wrapper = Wrapper::Data(42);
- /// let Wrapper::Data(data) = wrapper;
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub INFALLIBLE_DESTRUCTURING_MATCH,
- style,
- "a `match` statement with a single infallible arm instead of a `let`"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for useless match that binds to only one value.
- ///
- /// ### Why is this bad?
- /// Readability and needless complexity.
- ///
- /// ### Known problems
- /// Suggested replacements may be incorrect when `match`
- /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
- ///
- /// ### Example
- /// ```rust
- /// # let a = 1;
- /// # let b = 2;
- ///
- /// // Bad
- /// match (a, b) {
- /// (c, d) => {
- /// // useless match
- /// }
- /// }
- ///
- /// // Good
- /// let (c, d) = (a, b);
- /// ```
- #[clippy::version = "1.43.0"]
- pub MATCH_SINGLE_BINDING,
- complexity,
- "a match with a single binding instead of using `let` statement"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
- ///
- /// ### Why is this bad?
- /// Correctness and readability. It's like having a wildcard pattern after
- /// matching all enum variants explicitly.
- ///
- /// ### Example
- /// ```rust
- /// # struct A { a: i32 }
- /// let a = A { a: 5 };
- ///
- /// // Bad
- /// match a {
- /// A { a: 5, .. } => {},
- /// _ => {},
- /// }
- ///
- /// // Good
- /// match a {
- /// A { a: 5 } => {},
- /// _ => {},
- /// }
- /// ```
- #[clippy::version = "1.43.0"]
- pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
- restriction,
- "a match on a struct that binds all fields but still uses the wildcard pattern"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Lint for redundant pattern matching over `Result`, `Option`,
- /// `std::task::Poll` or `std::net::IpAddr`
- ///
- /// ### Why is this bad?
- /// It's more concise and clear to just use the proper
- /// utility function
- ///
- /// ### Known problems
- /// This will change the drop order for the matched type. Both `if let` and
- /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
- /// value before entering the block. For most types this change will not matter, but for a few
- /// types this will not be an acceptable change (e.g. locks). See the
- /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
- /// drop order.
- ///
- /// ### Example
- /// ```rust
- /// # use std::task::Poll;
- /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- /// if let Ok(_) = Ok::<i32, i32>(42) {}
- /// if let Err(_) = Err::<i32, i32>(42) {}
- /// if let None = None::<()> {}
- /// if let Some(_) = Some(42) {}
- /// if let Poll::Pending = Poll::Pending::<()> {}
- /// if let Poll::Ready(_) = Poll::Ready(42) {}
- /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
- /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
- /// match Ok::<i32, i32>(42) {
- /// Ok(_) => true,
- /// Err(_) => false,
- /// };
- /// ```
- ///
- /// The more idiomatic use would be:
- ///
- /// ```rust
- /// # use std::task::Poll;
- /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
- /// if Ok::<i32, i32>(42).is_ok() {}
- /// if Err::<i32, i32>(42).is_err() {}
- /// if None::<()>.is_none() {}
- /// if Some(42).is_some() {}
- /// if Poll::Pending::<()>.is_pending() {}
- /// if Poll::Ready(42).is_ready() {}
- /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
- /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
- /// Ok::<i32, i32>(42).is_ok();
- /// ```
- #[clippy::version = "1.31.0"]
- pub REDUNDANT_PATTERN_MATCHING,
- style,
- "use the proper utility function avoiding an `if let`"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for `match` or `if let` expressions producing a
- /// `bool` that could be written using `matches!`
- ///
- /// ### Why is this bad?
- /// Readability and needless complexity.
- ///
- /// ### Known problems
- /// This lint falsely triggers, if there are arms with
- /// `cfg` attributes that remove an arm evaluating to `false`.
- ///
- /// ### Example
- /// ```rust
- /// let x = Some(5);
- ///
- /// // Bad
- /// let a = match x {
- /// Some(0) => true,
- /// _ => false,
- /// };
- ///
- /// let a = if let Some(0) = x {
- /// true
- /// } else {
- /// false
- /// };
- ///
- /// // Good
- /// let a = matches!(x, Some(0));
- /// ```
- #[clippy::version = "1.47.0"]
- pub MATCH_LIKE_MATCHES_MACRO,
- style,
- "a match that could be written with the matches! macro"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for `match` with identical arm bodies.
- ///
- /// ### Why is this bad?
- /// This is probably a copy & paste error. If arm bodies
- /// are the same on purpose, you can factor them
- /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
- ///
- /// ### Known problems
- /// False positive possible with order dependent `match`
- /// (see issue
- /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
- ///
- /// ### Example
- /// ```rust,ignore
- /// match foo {
- /// Bar => bar(),
- /// Quz => quz(),
- /// Baz => bar(), // <= oops
- /// }
- /// ```
- ///
- /// This should probably be
- /// ```rust,ignore
- /// match foo {
- /// Bar => bar(),
- /// Quz => quz(),
- /// Baz => baz(), // <= fixed
- /// }
- /// ```
- ///
- /// or if the original code was not a typo:
- /// ```rust,ignore
- /// match foo {
- /// Bar | Baz => bar(), // <= shows the intent better
- /// Quz => quz(),
- /// }
- /// ```
- #[clippy::version = "pre 1.29.0"]
- pub MATCH_SAME_ARMS,
- pedantic,
- "`match` with identical arm bodies"
-}
-
-#[derive(Default)]
-pub struct Matches {
- msrv: Option<RustcVersion>,
- infallible_destructuring_match_linted: bool,
-}
-
-impl Matches {
- #[must_use]
- pub fn new(msrv: Option<RustcVersion>) -> Self {
- Self {
- msrv,
- ..Matches::default()
- }
- }
-}
-
-impl_lint_pass!(Matches => [
- SINGLE_MATCH,
- MATCH_REF_PATS,
- MATCH_BOOL,
- SINGLE_MATCH_ELSE,
- MATCH_OVERLAPPING_ARM,
- MATCH_WILD_ERR_ARM,
- MATCH_AS_REF,
- WILDCARD_ENUM_MATCH_ARM,
- MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
- WILDCARD_IN_OR_PATTERNS,
- MATCH_SINGLE_BINDING,
- INFALLIBLE_DESTRUCTURING_MATCH,
- REST_PAT_IN_FULLY_BOUND_STRUCTS,
- REDUNDANT_PATTERN_MATCHING,
- MATCH_LIKE_MATCHES_MACRO,
- MATCH_SAME_ARMS,
-]);
-
-impl<'tcx> LateLintPass<'tcx> for Matches {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if expr.span.from_expansion() {
- return;
- }
-
- redundant_pattern_match::check(cx, expr);
-
- if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
- if !check_match_like_matches(cx, expr) {
- lint_match_arms(cx, expr);
- }
- } else {
- lint_match_arms(cx, expr);
- }
-
- if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
- check_single_match(cx, ex, arms, expr);
- check_match_bool(cx, ex, arms, expr);
- check_overlapping_arms(cx, ex, arms);
- check_wild_err_arm(cx, ex, arms);
- check_wild_enum_match(cx, ex, arms);
- check_match_as_ref(cx, ex, arms, expr);
- check_wild_in_or_pats(cx, arms);
-
- if self.infallible_destructuring_match_linted {
- self.infallible_destructuring_match_linted = false;
- } else {
- check_match_single_binding(cx, ex, arms, expr);
- }
- }
- if let ExprKind::Match(ex, arms, _) = expr.kind {
- check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
- }
- }
-
- fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
- if_chain! {
- if !local.span.from_expansion();
- if let Some(expr) = local.init;
- if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
- if arms.len() == 1 && arms[0].guard.is_none();
- if let PatKind::TupleStruct(
- QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
- if args.len() == 1;
- if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
- let body = peel_blocks(arms[0].body);
- if path_to_local_id(body, arg);
-
- then {
- let mut applicability = Applicability::MachineApplicable;
- self.infallible_destructuring_match_linted = true;
- span_lint_and_sugg(
- cx,
- INFALLIBLE_DESTRUCTURING_MATCH,
- local.span,
- "you seem to be trying to use `match` to destructure a single infallible pattern. \
- Consider using `let`",
- "try this",
- format!(
- "let {}({}) = {};",
- snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
- snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
- snippet_with_applicability(cx, target.span, "..", &mut applicability),
- ),
- applicability,
- );
- }
- }
- }
-
- fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
- if_chain! {
- if !pat.span.from_expansion();
- if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
- if let Some(def_id) = path.res.opt_def_id();
- let ty = cx.tcx.type_of(def_id);
- if let ty::Adt(def, _) = ty.kind();
- if def.is_struct() || def.is_union();
- if fields.len() == def.non_enum_variant().fields.len();
-
- then {
- span_lint_and_help(
- cx,
- REST_PAT_IN_FULLY_BOUND_STRUCTS,
- pat.span,
- "unnecessary use of `..` pattern in struct binding. All fields were already bound",
- None,
- "consider removing `..` from this binding",
- );
- }
- }
- }
-
- extract_msrv_attr!(LateContext);
-}
-
-#[rustfmt::skip]
-fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
- if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
- if expr.span.from_expansion() {
- // Don't lint match expressions present in
- // macro_rules! block
- return;
- }
- if let PatKind::Or(..) = arms[0].pat.kind {
- // don't lint for or patterns for now, this makes
- // the lint noisy in unnecessary situations
- return;
- }
- let els = arms[1].body;
- let els = if is_unit_expr(peel_blocks(els)) {
- None
- } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
- if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
- // single statement/expr "else" block, don't lint
- return;
- }
- // block with 2+ statements or 1 expr and 1+ statement
- Some(els)
- } else {
- // not a block, don't lint
- return;
- };
-
- let ty = cx.typeck_results().expr_ty(ex);
- if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
- check_single_match_single_pattern(cx, ex, arms, expr, els);
- check_single_match_opt_like(cx, ex, arms, expr, ty, els);
- }
- }
-}
-
-fn check_single_match_single_pattern(
- cx: &LateContext<'_>,
- ex: &Expr<'_>,
- arms: &[Arm<'_>],
- expr: &Expr<'_>,
- els: Option<&Expr<'_>>,
-) {
- if is_wild(arms[1].pat) {
- report_single_match_single_pattern(cx, ex, arms, expr, els);
- }
-}
-
-fn report_single_match_single_pattern(
- cx: &LateContext<'_>,
- ex: &Expr<'_>,
- arms: &[Arm<'_>],
- expr: &Expr<'_>,
- els: Option<&Expr<'_>>,
-) {
- let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
- let els_str = els.map_or(String::new(), |els| {
- format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
- });
-
- let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
- let (msg, sugg) = if_chain! {
- if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
- let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
- if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
- if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
- if ty.is_integral() || ty.is_char() || ty.is_str()
- || (implements_trait(cx, ty, spe_trait_id, &[])
- && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
- then {
- // scrutinee derives PartialEq and the pattern is a constant.
- let pat_ref_count = match pat.kind {
- // string literals are already a reference.
- PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
- _ => pat_ref_count,
- };
- // References are only implicitly added to the pattern, so no overflow here.
- // e.g. will work: match &Some(_) { Some(_) => () }
- // will not: match Some(_) { &Some(_) => () }
- let ref_count_diff = ty_ref_count - pat_ref_count;
-
- // Try to remove address of expressions first.
- let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
- let ref_count_diff = ref_count_diff - removed;
-
- let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
- let sugg = format!(
- "if {} == {}{} {}{}",
- snippet(cx, ex.span, ".."),
- // PartialEq for different reference counts may not exist.
- "&".repeat(ref_count_diff),
- snippet(cx, arms[0].pat.span, ".."),
- expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
- els_str,
- );
- (msg, sugg)
- } else {
- let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
- let sugg = format!(
- "if let {} = {} {}{}",
- snippet(cx, arms[0].pat.span, ".."),
- snippet(cx, ex.span, ".."),
- expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
- els_str,
- );
- (msg, sugg)
- }
- };
-
- span_lint_and_sugg(
- cx,
- lint,
- expr.span,
- msg,
- "try this",
- sugg,
- Applicability::HasPlaceholders,
- );
-}
-
-fn check_single_match_opt_like(
- cx: &LateContext<'_>,
- ex: &Expr<'_>,
- arms: &[Arm<'_>],
- expr: &Expr<'_>,
- ty: Ty<'_>,
- els: Option<&Expr<'_>>,
-) {
- // list of candidate `Enum`s we know will never get any more members
- let candidates = &[
- (&paths::COW, "Borrowed"),
- (&paths::COW, "Cow::Borrowed"),
- (&paths::COW, "Cow::Owned"),
- (&paths::COW, "Owned"),
- (&paths::OPTION, "None"),
- (&paths::RESULT, "Err"),
- (&paths::RESULT, "Ok"),
- ];
-
- let path = match arms[1].pat.kind {
- PatKind::TupleStruct(ref path, inner, _) => {
- // Contains any non wildcard patterns (e.g., `Err(err)`)?
- if !inner.iter().all(is_wild) {
- return;
- }
- rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
- },
- PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
- PatKind::Path(ref path) => {
- rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
- },
- _ => return,
- };
-
- for &(ty_path, pat_path) in candidates {
- if path == *pat_path && match_type(cx, ty, ty_path) {
- report_single_match_single_pattern(cx, ex, arms, expr, els);
- }
- }
-}
-
-fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
- // Type of expression is `bool`.
- if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
- span_lint_and_then(
- cx,
- MATCH_BOOL,
- expr.span,
- "you seem to be trying to match on a boolean expression",
- move |diag| {
- if arms.len() == 2 {
- // no guards
- let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
- if let ExprKind::Lit(ref lit) = arm_bool.kind {
- match lit.node {
- LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
- LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
- _ => None,
- }
- } else {
- None
- }
- } else {
- None
- };
-
- if let Some((true_expr, false_expr)) = exprs {
- let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
- (false, false) => Some(format!(
- "if {} {} else {}",
- snippet(cx, ex.span, "b"),
- expr_block(cx, true_expr, None, "..", Some(expr.span)),
- expr_block(cx, false_expr, None, "..", Some(expr.span))
- )),
- (false, true) => Some(format!(
- "if {} {}",
- snippet(cx, ex.span, "b"),
- expr_block(cx, true_expr, None, "..", Some(expr.span))
- )),
- (true, false) => {
- let test = Sugg::hir(cx, ex, "..");
- Some(format!(
- "if {} {}",
- !test,
- expr_block(cx, false_expr, None, "..", Some(expr.span))
- ))
- },
- (true, true) => None,
- };
-
- if let Some(sugg) = sugg {
- diag.span_suggestion(
- expr.span,
- "consider using an `if`/`else` expression",
- sugg,
- Applicability::HasPlaceholders,
- );
- }
- }
- }
- },
- );
- }
-}
-
-fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
- if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
- let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
- if !ranges.is_empty() {
- if let Some((start, end)) = overlapping(&ranges) {
- span_lint_and_note(
- cx,
- MATCH_OVERLAPPING_ARM,
- start.span,
- "some ranges overlap",
- Some(end.span),
- "overlaps with this",
- );
- }
- }
- }
-}
-
-fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
- let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
- if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
- for arm in arms {
- if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
- let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
- if path_str == "Err" {
- let mut matching_wild = inner.iter().any(is_wild);
- let mut ident_bind_name = kw::Underscore;
- if !matching_wild {
- // Looking for unused bindings (i.e.: `_e`)
- for pat in inner.iter() {
- if let PatKind::Binding(_, id, ident, None) = pat.kind {
- if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
- ident_bind_name = ident.name;
- matching_wild = true;
- }
- }
- }
- }
- if_chain! {
- if matching_wild;
- if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
- if is_panic(cx, macro_call.def_id);
- then {
- // `Err(_)` or `Err(_e)` arm with `panic!` found
- span_lint_and_note(cx,
- MATCH_WILD_ERR_ARM,
- arm.pat.span,
- &format!("`Err({})` matches all errors", ident_bind_name),
- None,
- "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
- );
- }
- }
- }
- }
- }
- }
-}
-
-enum CommonPrefixSearcher<'a> {
- None,
- Path(&'a [PathSegment<'a>]),
- Mixed,
-}
-impl<'a> CommonPrefixSearcher<'a> {
- fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
- match path {
- [path @ .., _] => self.with_prefix(path),
- [] => (),
- }
- }
-
- fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
- match self {
- Self::None => *self = Self::Path(path),
- Self::Path(self_path)
- if path
- .iter()
- .map(|p| p.ident.name)
- .eq(self_path.iter().map(|p| p.ident.name)) => {},
- Self::Path(_) => *self = Self::Mixed,
- Self::Mixed => (),
- }
- }
-}
-
-fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
- let attrs = cx.tcx.get_attrs(variant_def.def_id);
- clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
-}
-
-#[allow(clippy::too_many_lines)]
-fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
- let ty = cx.typeck_results().expr_ty(ex).peel_refs();
- let adt_def = match ty.kind() {
- ty::Adt(adt_def, _)
- if adt_def.is_enum()
- && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
- {
- adt_def
- },
- _ => return,
- };
-
- // First pass - check for violation, but don't do much book-keeping because this is hopefully
- // the uncommon case, and the book-keeping is slightly expensive.
- let mut wildcard_span = None;
- let mut wildcard_ident = None;
- let mut has_non_wild = false;
- for arm in arms {
- match peel_hir_pat_refs(arm.pat).0.kind {
- PatKind::Wild => wildcard_span = Some(arm.pat.span),
- PatKind::Binding(_, _, ident, None) => {
- wildcard_span = Some(arm.pat.span);
- wildcard_ident = Some(ident);
- },
- _ => has_non_wild = true,
- }
- }
- let wildcard_span = match wildcard_span {
- Some(x) if has_non_wild => x,
- _ => return,
- };
-
- // Accumulate the variants which should be put in place of the wildcard because they're not
- // already covered.
- let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
- let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
-
- let mut path_prefix = CommonPrefixSearcher::None;
- for arm in arms {
- // Guards mean that this case probably isn't exhaustively covered. Technically
- // this is incorrect, as we should really check whether each variant is exhaustively
- // covered by the set of guards that cover it, but that's really hard to do.
- recurse_or_patterns(arm.pat, |pat| {
- let path = match &peel_hir_pat_refs(pat).0.kind {
- PatKind::Path(path) => {
- #[allow(clippy::match_same_arms)]
- let id = match cx.qpath_res(path, pat.hir_id) {
- Res::Def(
- DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst,
- _,
- ) => return,
- Res::Def(_, id) => id,
- _ => return,
- };
- if arm.guard.is_none() {
- missing_variants.retain(|e| e.ctor_def_id != Some(id));
- }
- path
- },
- PatKind::TupleStruct(path, patterns, ..) => {
- if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
- if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
- missing_variants.retain(|e| e.ctor_def_id != Some(id));
- }
- }
- path
- },
- PatKind::Struct(path, patterns, ..) => {
- if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
- if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
- missing_variants.retain(|e| e.def_id != id);
- }
- }
- path
- },
- _ => return,
- };
- match path {
- QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
- QPath::TypeRelative(
- hir::Ty {
- kind: TyKind::Path(QPath::Resolved(_, path)),
- ..
- },
- _,
- ) => path_prefix.with_prefix(path.segments),
- _ => (),
- }
- });
- }
-
- let format_suggestion = |variant: &VariantDef| {
- format!(
- "{}{}{}{}",
- if let Some(ident) = wildcard_ident {
- format!("{} @ ", ident.name)
- } else {
- String::new()
- },
- if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
- let mut s = String::new();
- for seg in path_prefix {
- s.push_str(seg.ident.as_str());
- s.push_str("::");
- }
- s
- } else {
- let mut s = cx.tcx.def_path_str(adt_def.did);
- s.push_str("::");
- s
- },
- variant.name,
- match variant.ctor_kind {
- CtorKind::Fn if variant.fields.len() == 1 => "(_)",
- CtorKind::Fn => "(..)",
- CtorKind::Const => "",
- CtorKind::Fictive => "{ .. }",
- }
- )
- };
-
- match missing_variants.as_slice() {
- [] => (),
- [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
- cx,
- MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
- wildcard_span,
- "wildcard matches only a single variant and will also match any future added variants",
- "try this",
- format_suggestion(x),
- Applicability::MaybeIncorrect,
- ),
- variants => {
- let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
- let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
- suggestions.push("_".into());
- "wildcard matches known variants and will also match future added variants"
- } else {
- "wildcard match will also match any future added variants"
- };
-
- span_lint_and_sugg(
- cx,
- WILDCARD_ENUM_MATCH_ARM,
- wildcard_span,
- message,
- "try this",
- suggestions.join(" | "),
- Applicability::MaybeIncorrect,
- );
- },
- };
-}
-
-fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
-where
- 'b: 'a,
- I: Clone + Iterator<Item = &'a Pat<'b>>,
-{
- if !has_multiple_ref_pats(pats.clone()) {
- return;
- }
-
- let (first_sugg, msg, title);
- let span = ex.span.source_callsite();
- if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
- first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
- msg = "try";
- title = "you don't need to add `&` to both the expression and the patterns";
- } else {
- first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
- msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
- title = "you don't need to add `&` to all patterns";
- }
-
- let remaining_suggs = pats.filter_map(|pat| {
- if let PatKind::Ref(refp, _) = pat.kind {
- Some((pat.span, snippet(cx, refp.span, "..").to_string()))
- } else {
- None
- }
- });
-
- span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
- if !expr.span.from_expansion() {
- multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
- }
- });
-}
-
-fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
- if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
- let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
- is_ref_some_arm(cx, &arms[1])
- } else if is_none_arm(cx, &arms[1]) {
- is_ref_some_arm(cx, &arms[0])
- } else {
- None
- };
- if let Some(rb) = arm_ref {
- let suggestion = if rb == BindingAnnotation::Ref {
- "as_ref"
- } else {
- "as_mut"
- };
-
- let output_ty = cx.typeck_results().expr_ty(expr);
- let input_ty = cx.typeck_results().expr_ty(ex);
-
- let cast = if_chain! {
- if let ty::Adt(_, substs) = input_ty.kind();
- let input_ty = substs.type_at(0);
- if let ty::Adt(_, substs) = output_ty.kind();
- let output_ty = substs.type_at(0);
- if let ty::Ref(_, output_ty, _) = *output_ty.kind();
- if input_ty != output_ty;
- then {
- ".map(|x| x as _)"
- } else {
- ""
- }
- };
-
- let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
- cx,
- MATCH_AS_REF,
- expr.span,
- &format!("use `{}()` instead", suggestion),
- "try this",
- format!(
- "{}.{}(){}",
- snippet_with_applicability(cx, ex.span, "_", &mut applicability),
- suggestion,
- cast,
- ),
- applicability,
- );
- }
- }
-}
-
-fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
- for arm in arms {
- if let PatKind::Or(fields) = arm.pat.kind {
- // look for multiple fields in this arm that contains at least one Wild pattern
- if fields.len() > 1 && fields.iter().any(is_wild) {
- span_lint_and_help(
- cx,
- WILDCARD_IN_OR_PATTERNS,
- arm.pat.span,
- "wildcard pattern covers any other pattern as it will match anyway",
- None,
- "consider handling `_` separately",
- );
- }
- }
- }
-}
-
-/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
-fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
- if let Some(higher::IfLet {
- let_pat,
- let_expr,
- if_then,
- if_else: Some(if_else),
- }) = higher::IfLet::hir(cx, expr)
- {
- return find_matches_sugg(
- cx,
- let_expr,
- IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
- expr,
- true,
- );
- }
-
- if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
- return find_matches_sugg(
- cx,
- scrut,
- arms.iter().map(|arm| {
- (
- cx.tcx.hir().attrs(arm.hir_id),
- Some(arm.pat),
- arm.body,
- arm.guard.as_ref(),
- )
- }),
- expr,
- false,
- );
- }
-
- false
-}
-
-/// Lint a `match` or `if let` for replacement by `matches!`
-fn find_matches_sugg<'a, 'b, I>(
- cx: &LateContext<'_>,
- ex: &Expr<'_>,
- mut iter: I,
- expr: &Expr<'_>,
- is_if_let: bool,
-) -> bool
-where
- 'b: 'a,
- I: Clone
- + DoubleEndedIterator
- + ExactSizeIterator
- + Iterator<
- Item = (
- &'a [Attribute],
- Option<&'a Pat<'b>>,
- &'a Expr<'b>,
- Option<&'a Guard<'b>>,
- ),
- >,
-{
- if_chain! {
- if iter.len() >= 2;
- if cx.typeck_results().expr_ty(expr).is_bool();
- if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
- let iter_without_last = iter.clone();
- if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
- if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
- if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
- if b0 != b1;
- if first_guard.is_none() || iter.len() == 0;
- if first_attrs.is_empty();
- if iter
- .all(|arm| {
- find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
- });
- then {
- if let Some(last_pat) = last_pat_opt {
- if !is_wild(last_pat) {
- return false;
- }
- }
-
- // The suggestion may be incorrect, because some arms can have `cfg` attributes
- // evaluated into `false` and so such arms will be stripped before.
- let mut applicability = Applicability::MaybeIncorrect;
- let pat = {
- use itertools::Itertools as _;
- iter_without_last
- .filter_map(|arm| {
- let pat_span = arm.1?.span;
- Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
- })
- .join(" | ")
- };
- let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
- format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
- } else {
- pat
- };
-
- // strip potential borrows (#6503), but only if the type is a reference
- let mut ex_new = ex;
- if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
- if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
- ex_new = ex_inner;
- }
- };
- span_lint_and_sugg(
- cx,
- MATCH_LIKE_MATCHES_MACRO,
- expr.span,
- &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
- "try this",
- format!(
- "{}matches!({}, {})",
- if b0 { "" } else { "!" },
- snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
- pat_and_guard,
- ),
- applicability,
- );
- true
- } else {
- false
- }
- }
-}
-
-/// Extract a `bool` or `{ bool }`
-fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
- match ex {
- ExprKind::Lit(Spanned {
- node: LitKind::Bool(b), ..
- }) => Some(*b),
- ExprKind::Block(
- rustc_hir::Block {
- stmts: &[],
- expr: Some(exp),
- ..
- },
- _,
- ) if is_if_let => {
- if let ExprKind::Lit(Spanned {
- node: LitKind::Bool(b), ..
- }) = exp.kind
- {
- Some(b)
- } else {
- None
- }
- },
- _ => None,
- }
-}
-
-#[allow(clippy::too_many_lines)]
-fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
- if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
- return;
- }
-
- // HACK:
- // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
- // to prevent false positives as there is currently no better way to detect if code was excluded by
- // a macro. See PR #6435
- if_chain! {
- if let Some(match_snippet) = snippet_opt(cx, expr.span);
- if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
- if let Some(ex_snippet) = snippet_opt(cx, ex.span);
- let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
- if rest_snippet.contains("=>");
- then {
- // The code it self contains another thick arrow "=>"
- // -> Either another arm or a comment
- return;
- }
- }
-
- let matched_vars = ex.span;
- let bind_names = arms[0].pat.span;
- let match_body = peel_blocks(arms[0].body);
- let mut snippet_body = if match_body.span.from_expansion() {
- Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
- } else {
- snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
- };
-
- // Do we need to add ';' to suggestion ?
- match match_body.kind {
- ExprKind::Block(block, _) => {
- // macro + expr_ty(body) == ()
- if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
- snippet_body.push(';');
- }
- },
- _ => {
- // expr_ty(body) == ()
- if cx.typeck_results().expr_ty(match_body).is_unit() {
- snippet_body.push(';');
- }
- },
- }
-
- let mut applicability = Applicability::MaybeIncorrect;
- match arms[0].pat.kind {
- PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
- // If this match is in a local (`let`) stmt
- let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
- (
- parent_let_node.span,
- format!(
- "let {} = {};\n{}let {} = {};",
- snippet_with_applicability(cx, bind_names, "..", &mut applicability),
- snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
- " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
- snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
- snippet_body
- ),
- )
- } else {
- // If we are in closure, we need curly braces around suggestion
- let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
- let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
- if let Some(parent_expr) = get_parent_expr(cx, expr) {
- if let ExprKind::Closure(..) = parent_expr.kind {
- cbrace_end = format!("\n{}}}", indent);
- // Fix body indent due to the closure
- indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
- cbrace_start = format!("{{\n{}", indent);
- }
- }
- // If the parent is already an arm, and the body is another match statement,
- // we need curly braces around suggestion
- let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
- if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
- if let ExprKind::Match(..) = arm.body.kind {
- cbrace_end = format!("\n{}}}", indent);
- // Fix body indent due to the match
- indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
- cbrace_start = format!("{{\n{}", indent);
- }
- }
- (
- expr.span,
- format!(
- "{}let {} = {};\n{}{}{}",
- cbrace_start,
- snippet_with_applicability(cx, bind_names, "..", &mut applicability),
- snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
- indent,
- snippet_body,
- cbrace_end
- ),
- )
- };
- span_lint_and_sugg(
- cx,
- MATCH_SINGLE_BINDING,
- target_span,
- "this match could be written as a `let` statement",
- "consider using `let` statement",
- sugg,
- applicability,
- );
- },
- PatKind::Wild => {
- if ex.can_have_side_effects() {
- let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
- let sugg = format!(
- "{};\n{}{}",
- snippet_with_applicability(cx, ex.span, "..", &mut applicability),
- indent,
- snippet_body
- );
- span_lint_and_sugg(
- cx,
- MATCH_SINGLE_BINDING,
- expr.span,
- "this match could be replaced by its scrutinee and body",
- "consider using the scrutinee and body instead",
- sugg,
- applicability,
- );
- } else {
- span_lint_and_sugg(
- cx,
- MATCH_SINGLE_BINDING,
- expr.span,
- "this match could be replaced by its body itself",
- "consider using the match body instead",
- snippet_body,
- Applicability::MachineApplicable,
- );
- }
- },
- _ => (),
- }
-}
-
-/// Returns true if the `ex` match expression is in a local (`let`) statement
-fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
- let map = &cx.tcx.hir();
- if_chain! {
- if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
- if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
- then {
- return Some(parent_let_expr);
- }
- }
- None
-}
-
-/// Gets the ranges for each range pattern arm. Applies `ty` bounds for open ranges.
-fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
- arms.iter()
- .filter_map(|arm| {
- if let Arm { pat, guard: None, .. } = *arm {
- if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
- let lhs_const = match lhs {
- Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
- None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
- };
- let rhs_const = match rhs {
- Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
- None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
- };
-
- let lhs_val = lhs_const.int_value(cx, ty)?;
- let rhs_val = rhs_const.int_value(cx, ty)?;
-
- let rhs_bound = match range_end {
- RangeEnd::Included => EndBound::Included(rhs_val),
- RangeEnd::Excluded => EndBound::Excluded(rhs_val),
- };
- return Some(SpannedRange {
- span: pat.span,
- node: (lhs_val, rhs_bound),
- });
- }
-
- if let PatKind::Lit(value) = pat.kind {
- let value = constant_full_int(cx, cx.typeck_results(), value)?;
- return Some(SpannedRange {
- span: pat.span,
- node: (value, EndBound::Included(value)),
- });
- }
- }
- None
- })
- .collect()
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum EndBound<T> {
- Included(T),
- Excluded(T),
-}
-
-#[derive(Debug, Eq, PartialEq)]
-struct SpannedRange<T> {
- pub span: Span,
- pub node: (T, EndBound<T>),
-}
-
-// Checks if arm has the form `None => None`
-fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
- matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
-}
-
-// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
-fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
- if_chain! {
- if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
- if is_lang_ctor(cx, qpath, OptionSome);
- if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
- if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
- if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
- if let ExprKind::Path(ref some_path) = e.kind;
- if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
- if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
- if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
- then {
- return Some(rb)
- }
- }
- None
-}
-
-fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
-where
- 'b: 'a,
- I: Iterator<Item = &'a Pat<'b>>,
-{
- let mut ref_count = 0;
- for opt in pats.map(|pat| match pat.kind {
- PatKind::Ref(..) => Some(true), // &-patterns
- PatKind::Wild => Some(false), // an "anything" wildcard is also fine
- _ => None, // any other pattern is not fine
- }) {
- if let Some(inner) = opt {
- if inner {
- ref_count += 1;
- }
- } else {
- return false;
- }
- }
- ref_count > 1
-}
-
-fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
-where
- T: Copy + Ord,
-{
- #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
- enum BoundKind {
- EndExcluded,
- Start,
- EndIncluded,
- }
-
- #[derive(Copy, Clone, Debug, Eq, PartialEq)]
- struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange<T>);
-
- impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.cmp(other))
- }
- }
-
- impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> {
- fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering {
- let RangeBound(self_value, self_kind, _) = *self;
- (self_value, self_kind).cmp(&(*other_value, *other_kind))
- }
- }
-
- let mut values = Vec::with_capacity(2 * ranges.len());
-
- for r @ SpannedRange { node: (start, end), .. } in ranges {
- values.push(RangeBound(*start, BoundKind::Start, r));
- values.push(match end {
- EndBound::Excluded(val) => RangeBound(*val, BoundKind::EndExcluded, r),
- EndBound::Included(val) => RangeBound(*val, BoundKind::EndIncluded, r),
- });
- }
-
- values.sort();
-
- let mut started = vec![];
-
- for RangeBound(_, kind, range) in values {
- match kind {
- BoundKind::Start => started.push(range),
- BoundKind::EndExcluded | BoundKind::EndIncluded => {
- let mut overlap = None;
-
- while let Some(last_started) = started.pop() {
- if last_started == range {
- break;
- }
- overlap = Some(last_started);
- }
-
- if let Some(first_overlapping) = overlap {
- return Some((range, first_overlapping));
- }
- },
- }
- }
-
- None
-}
-
-mod redundant_pattern_match {
- use super::REDUNDANT_PATTERN_MATCHING;
- use clippy_utils::diagnostics::span_lint_and_then;
- use clippy_utils::higher;
- use clippy_utils::source::snippet;
- use clippy_utils::sugg::Sugg;
- use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
- use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
- use if_chain::if_chain;
- use rustc_ast::ast::LitKind;
- use rustc_data_structures::fx::FxHashSet;
- use rustc_errors::Applicability;
- use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
- use rustc_hir::{
- intravisit::{walk_expr, Visitor},
- Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
- };
- use rustc_lint::LateContext;
- use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
- use rustc_span::sym;
-
- pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let Some(higher::IfLet {
- if_else,
- let_pat,
- let_expr,
- ..
- }) = higher::IfLet::hir(cx, expr)
- {
- find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
- }
- if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
- find_sugg_for_match(cx, expr, op, arms);
- }
- if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
- find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
- }
- }
-
- /// Checks if the drop order for a type matters. Some std types implement drop solely to
- /// deallocate memory. For these types, and composites containing them, changing the drop order
- /// won't result in any observable side effects.
- fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
- type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
- }
-
- fn type_needs_ordered_drop_inner<'tcx>(
- cx: &LateContext<'tcx>,
- ty: Ty<'tcx>,
- seen: &mut FxHashSet<Ty<'tcx>>,
- ) -> bool {
- if !seen.insert(ty) {
- return false;
- }
- if !ty.needs_drop(cx.tcx, cx.param_env) {
- false
- } else if !cx
- .tcx
- .lang_items()
- .drop_trait()
- .map_or(false, |id| implements_trait(cx, ty, id, &[]))
- {
- // This type doesn't implement drop, so no side effects here.
- // Check if any component type has any.
- match ty.kind() {
- ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
- ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
- ty::Adt(adt, subs) => adt
- .all_fields()
- .map(|f| f.ty(cx.tcx, subs))
- .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
- _ => true,
- }
- }
- // Check for std types which implement drop, but only for memory allocation.
- else if is_type_diagnostic_item(cx, ty, sym::Vec)
- || is_type_lang_item(cx, ty, LangItem::OwnedBox)
- || is_type_diagnostic_item(cx, ty, sym::Rc)
- || is_type_diagnostic_item(cx, ty, sym::Arc)
- || is_type_diagnostic_item(cx, ty, sym::cstring_type)
- || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
- || is_type_diagnostic_item(cx, ty, sym::LinkedList)
- || match_type(cx, ty, &paths::WEAK_RC)
- || match_type(cx, ty, &paths::WEAK_ARC)
- {
- // Check all of the generic arguments.
- if let ty::Adt(_, subs) = ty.kind() {
- subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
- } else {
- true
- }
- } else {
- true
- }
- }
-
- // Extract the generic arguments out of a type
- fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
- if_chain! {
- if let ty::Adt(_, subs) = ty.kind();
- if let Some(sub) = subs.get(index);
- if let GenericArgKind::Type(sub_ty) = sub.unpack();
- then {
- Some(sub_ty)
- } else {
- None
- }
- }
- }
-
- // Checks if there are any temporaries created in the given expression for which drop order
- // matters.
- fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- struct V<'a, 'tcx> {
- cx: &'a LateContext<'tcx>,
- res: bool,
- }
- impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
- fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
- match expr.kind {
- // Taking the reference of a value leaves a temporary
- // e.g. In `&String::new()` the string is a temporary value.
- // Remaining fields are temporary values
- // e.g. In `(String::new(), 0).1` the string is a temporary value.
- ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
- if !matches!(expr.kind, ExprKind::Path(_)) {
- if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
- self.res = true;
- } else {
- self.visit_expr(expr);
- }
- }
- },
- // the base type is alway taken by reference.
- // e.g. In `(vec![0])[0]` the vector is a temporary value.
- ExprKind::Index(base, index) => {
- if !matches!(base.kind, ExprKind::Path(_)) {
- if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
- self.res = true;
- } else {
- self.visit_expr(base);
- }
- }
- self.visit_expr(index);
- },
- // Method calls can take self by reference.
- // e.g. In `String::new().len()` the string is a temporary value.
- ExprKind::MethodCall(_, [self_arg, args @ ..], _) => {
- if !matches!(self_arg.kind, ExprKind::Path(_)) {
- let self_by_ref = self
- .cx
- .typeck_results()
- .type_dependent_def_id(expr.hir_id)
- .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
- if self_by_ref
- && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg))
- {
- self.res = true;
- } else {
- self.visit_expr(self_arg);
- }
- }
- args.iter().for_each(|arg| self.visit_expr(arg));
- },
- // Either explicitly drops values, or changes control flow.
- ExprKind::DropTemps(_)
- | ExprKind::Ret(_)
- | ExprKind::Break(..)
- | ExprKind::Yield(..)
- | ExprKind::Block(Block { expr: None, .. }, _)
- | ExprKind::Loop(..) => (),
-
- // Only consider the final expression.
- ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
-
- _ => walk_expr(self, expr),
- }
- }
- }
-
- let mut v = V { cx, res: false };
- v.visit_expr(expr);
- v.res
- }
-
- fn find_sugg_for_if_let<'tcx>(
- cx: &LateContext<'tcx>,
- expr: &'tcx Expr<'_>,
- let_pat: &Pat<'_>,
- let_expr: &'tcx Expr<'_>,
- keyword: &'static str,
- has_else: bool,
- ) {
- // also look inside refs
- let mut kind = &let_pat.kind;
- // if we have &None for example, peel it so we can detect "if let None = x"
- if let PatKind::Ref(inner, _mutability) = kind {
- kind = &inner.kind;
- }
- let op_ty = cx.typeck_results().expr_ty(let_expr);
- // Determine which function should be used, and the type contained by the corresponding
- // variant.
- let (good_method, inner_ty) = match kind {
- PatKind::TupleStruct(ref path, [sub_pat], _) => {
- if let PatKind::Wild = sub_pat.kind {
- if is_lang_ctor(cx, path, ResultOk) {
- ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
- } else if is_lang_ctor(cx, path, ResultErr) {
- ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
- } else if is_lang_ctor(cx, path, OptionSome) {
- ("is_some()", op_ty)
- } else if is_lang_ctor(cx, path, PollReady) {
- ("is_ready()", op_ty)
- } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
- ("is_ipv4()", op_ty)
- } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
- ("is_ipv6()", op_ty)
- } else {
- return;
- }
- } else {
- return;
- }
- },
- PatKind::Path(ref path) => {
- let method = if is_lang_ctor(cx, path, OptionNone) {
- "is_none()"
- } else if is_lang_ctor(cx, path, PollPending) {
- "is_pending()"
- } else {
- return;
- };
- // `None` and `Pending` don't have an inner type.
- (method, cx.tcx.types.unit)
- },
- _ => return,
- };
-
- // If this is the last expression in a block or there is an else clause then the whole
- // type needs to be considered, not just the inner type of the branch being matched on.
- // Note the last expression in a block is dropped after all local bindings.
- let check_ty = if has_else
- || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
- {
- op_ty
- } else {
- inner_ty
- };
-
- // All temporaries created in the scrutinee expression are dropped at the same time as the
- // scrutinee would be, so they have to be considered as well.
- // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
- // for the duration if body.
- let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
-
- // check that `while_let_on_iterator` lint does not trigger
- if_chain! {
- if keyword == "while";
- if let ExprKind::MethodCall(method_path, _, _) = let_expr.kind;
- if method_path.ident.name == sym::next;
- if is_trait_method(cx, let_expr, sym::Iterator);
- then {
- return;
- }
- }
-
- let result_expr = match &let_expr.kind {
- ExprKind::AddrOf(_, _, borrowed) => borrowed,
- ExprKind::Unary(UnOp::Deref, deref) => deref,
- _ => let_expr,
- };
-
- span_lint_and_then(
- cx,
- REDUNDANT_PATTERN_MATCHING,
- let_pat.span,
- &format!("redundant pattern matching, consider using `{}`", good_method),
- |diag| {
- // if/while let ... = ... { ... }
- // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
- let expr_span = expr.span;
-
- // if/while let ... = ... { ... }
- // ^^^
- let op_span = result_expr.span.source_callsite();
-
- // if/while let ... = ... { ... }
- // ^^^^^^^^^^^^^^^^^^^
- let span = expr_span.until(op_span.shrink_to_hi());
-
- let app = if needs_drop {
- Applicability::MaybeIncorrect
- } else {
- Applicability::MachineApplicable
- };
-
- let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
- .maybe_par()
- .to_string();
-
- diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
-
- if needs_drop {
- diag.note("this will change drop order of the result, as well as all temporaries");
- diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
- }
- },
- );
- }
-
- fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
- if arms.len() == 2 {
- let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
-
- let found_good_method = match node_pair {
- (
- PatKind::TupleStruct(ref path_left, patterns_left, _),
- PatKind::TupleStruct(ref path_right, patterns_right, _),
- ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
- if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
- find_good_method_for_match(
- cx,
- arms,
- path_left,
- path_right,
- &paths::RESULT_OK,
- &paths::RESULT_ERR,
- "is_ok()",
- "is_err()",
- )
- .or_else(|| {
- find_good_method_for_match(
- cx,
- arms,
- path_left,
- path_right,
- &paths::IPADDR_V4,
- &paths::IPADDR_V6,
- "is_ipv4()",
- "is_ipv6()",
- )
- })
- } else {
- None
- }
- },
- (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
- | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
- if patterns.len() == 1 =>
- {
- if let PatKind::Wild = patterns[0].kind {
- find_good_method_for_match(
- cx,
- arms,
- path_left,
- path_right,
- &paths::OPTION_SOME,
- &paths::OPTION_NONE,
- "is_some()",
- "is_none()",
- )
- .or_else(|| {
- find_good_method_for_match(
- cx,
- arms,
- path_left,
- path_right,
- &paths::POLL_READY,
- &paths::POLL_PENDING,
- "is_ready()",
- "is_pending()",
- )
- })
- } else {
- None
- }
- },
- _ => None,
- };
-
- if let Some(good_method) = found_good_method {
- let span = expr.span.to(op.span);
- let result_expr = match &op.kind {
- ExprKind::AddrOf(_, _, borrowed) => borrowed,
- _ => op,
- };
- span_lint_and_then(
- cx,
- REDUNDANT_PATTERN_MATCHING,
- expr.span,
- &format!("redundant pattern matching, consider using `{}`", good_method),
- |diag| {
- diag.span_suggestion(
- span,
- "try this",
- format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
- Applicability::MaybeIncorrect, // snippet
- );
- },
- );
- }
- }
- }
-
- #[allow(clippy::too_many_arguments)]
- fn find_good_method_for_match<'a>(
- cx: &LateContext<'_>,
- arms: &[Arm<'_>],
- path_left: &QPath<'_>,
- path_right: &QPath<'_>,
- expected_left: &[&str],
- expected_right: &[&str],
- should_be_left: &'a str,
- should_be_right: &'a str,
- ) -> Option<&'a str> {
- let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
- && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
- {
- (&(*arms[0].body).kind, &(*arms[1].body).kind)
- } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
- && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
- {
- (&(*arms[1].body).kind, &(*arms[0].body).kind)
- } else {
- return None;
- };
-
- match body_node_pair {
- (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
- (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
- (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
- _ => None,
- },
- _ => None,
- }
- }
-}
-
-#[test]
-fn test_overlapping() {
- use rustc_span::source_map::DUMMY_SP;
-
- let sp = |s, e| SpannedRange {
- span: DUMMY_SP,
- node: (s, e),
- };
-
- assert_eq!(None, overlapping::<u8>(&[]));
- assert_eq!(None, overlapping(&[sp(1, EndBound::Included(4))]));
- assert_eq!(
- None,
- overlapping(&[sp(1, EndBound::Included(4)), sp(5, EndBound::Included(6))])
- );
- assert_eq!(
- None,
- overlapping(&[
- sp(1, EndBound::Included(4)),
- sp(5, EndBound::Included(6)),
- sp(10, EndBound::Included(11))
- ],)
- );
- assert_eq!(
- Some((&sp(1, EndBound::Included(4)), &sp(3, EndBound::Included(6)))),
- overlapping(&[sp(1, EndBound::Included(4)), sp(3, EndBound::Included(6))])
- );
- assert_eq!(
- Some((&sp(5, EndBound::Included(6)), &sp(6, EndBound::Included(11)))),
- overlapping(&[
- sp(1, EndBound::Included(4)),
- sp(5, EndBound::Included(6)),
- sp(6, EndBound::Included(11))
- ],)
- );
-}
-
-/// Implementation of `MATCH_SAME_ARMS`.
-fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
- if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
- let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
- let mut h = SpanlessHash::new(cx);
- h.hash_expr(arm.body);
- h.finish()
- };
-
- let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
- let min_index = usize::min(lindex, rindex);
- let max_index = usize::max(lindex, rindex);
-
- let mut local_map: HirIdMap<HirId> = HirIdMap::default();
- let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
- if_chain! {
- if let Some(a_id) = path_to_local(a);
- if let Some(b_id) = path_to_local(b);
- let entry = match local_map.entry(a_id) {
- Entry::Vacant(entry) => entry,
- // check if using the same bindings as before
- Entry::Occupied(entry) => return *entry.get() == b_id,
- };
- // the names technically don't have to match; this makes the lint more conservative
- if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
- if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
- if pat_contains_local(lhs.pat, a_id);
- if pat_contains_local(rhs.pat, b_id);
- then {
- entry.insert(b_id);
- true
- } else {
- false
- }
- }
- };
- // Arms with a guard are ignored, those can’t always be merged together
- // This is also the case for arms in-between each there is an arm with a guard
- (min_index..=max_index).all(|index| arms[index].guard.is_none())
- && SpanlessEq::new(cx)
- .expr_fallback(eq_fallback)
- .eq_expr(lhs.body, rhs.body)
- // these checks could be removed to allow unused bindings
- && bindings_eq(lhs.pat, local_map.keys().copied().collect())
- && bindings_eq(rhs.pat, local_map.values().copied().collect())
- };
-
- let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
- for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
- span_lint_and_then(
- cx,
- MATCH_SAME_ARMS,
- j.body.span,
- "this `match` has identical arm bodies",
- |diag| {
- diag.span_note(i.body.span, "same as this");
-
- // Note: this does not use `span_suggestion` on purpose:
- // there is no clean way
- // to remove the other arm. Building a span and suggest to replace it to ""
- // makes an even more confusing error message. Also in order not to make up a
- // span for the whole pattern, the suggestion is only shown when there is only
- // one pattern. The user should know about `|` if they are already using it…
-
- let lhs = snippet(cx, i.pat.span, "<pat1>");
- let rhs = snippet(cx, j.pat.span, "<pat2>");
-
- if let PatKind::Wild = j.pat.kind {
- // if the last arm is _, then i could be integrated into _
- // note that i.pat cannot be _, because that would mean that we're
- // hiding all the subsequent arms, and rust won't compile
- diag.span_note(
- i.body.span,
- &format!(
- "`{}` has the same arm body as the `_` wildcard, consider removing it",
- lhs
- ),
- );
- } else {
- diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
- .help("...or consider changing the match arm bodies");
- }
- },
- );
- }
- }
-}
-
-fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
- let mut result = false;
- pat.walk_short(|p| {
- result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
- !result
- });
- result
-}
-
-/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
-fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
- let mut result = true;
- pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
- result && ids.is_empty()
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
+use rustc_errors::Applicability;
+use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
+use rustc_lint::LateContext;
+
+use super::INFALLIBLE_DESTRUCTURING_MATCH;
+
+pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
+ if_chain! {
+ if !local.span.from_expansion();
+ if let Some(expr) = local.init;
+ if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
+ if arms.len() == 1 && arms[0].guard.is_none();
+ if let PatKind::TupleStruct(
+ QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
+ if args.len() == 1;
+ if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
+ let body = peel_blocks(arms[0].body);
+ if path_to_local_id(body, arg);
+
+ then {
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ INFALLIBLE_DESTRUCTURING_MATCH,
+ local.span,
+ "you seem to be trying to use `match` to destructure a single infallible pattern. \
+ Consider using `let`",
+ "try this",
+ format!(
+ "let {}({}) = {};",
+ snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
+ snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
+ snippet_with_applicability(cx, target.span, "..", &mut applicability),
+ ),
+ applicability,
+ );
+ return true;
+ }
+ }
+ false
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{is_lang_ctor, peel_blocks};
+use rustc_errors::Applicability;
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, LangItem, PatKind, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::MATCH_AS_REF;
+
+pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
+ let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
+ is_ref_some_arm(cx, &arms[1])
+ } else if is_none_arm(cx, &arms[1]) {
+ is_ref_some_arm(cx, &arms[0])
+ } else {
+ None
+ };
+ if let Some(rb) = arm_ref {
+ let suggestion = if rb == BindingAnnotation::Ref {
+ "as_ref"
+ } else {
+ "as_mut"
+ };
+
+ let output_ty = cx.typeck_results().expr_ty(expr);
+ let input_ty = cx.typeck_results().expr_ty(ex);
+
+ let cast = if_chain! {
+ if let ty::Adt(_, substs) = input_ty.kind();
+ let input_ty = substs.type_at(0);
+ if let ty::Adt(_, substs) = output_ty.kind();
+ let output_ty = substs.type_at(0);
+ if let ty::Ref(_, output_ty, _) = *output_ty.kind();
+ if input_ty != output_ty;
+ then {
+ ".map(|x| x as _)"
+ } else {
+ ""
+ }
+ };
+
+ let mut applicability = Applicability::MachineApplicable;
+ span_lint_and_sugg(
+ cx,
+ MATCH_AS_REF,
+ expr.span,
+ &format!("use `{}()` instead", suggestion),
+ "try this",
+ format!(
+ "{}.{}(){}",
+ snippet_with_applicability(cx, ex.span, "_", &mut applicability),
+ suggestion,
+ cast,
+ ),
+ applicability,
+ );
+ }
+ }
+}
+
+// Checks if arm has the form `None => None`
+fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+ matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, LangItem::OptionNone))
+}
+
+// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
+fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
+ if_chain! {
+ if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
+ if is_lang_ctor(cx, qpath, LangItem::OptionSome);
+ if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
+ if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
+ if let ExprKind::Call(e, args) = peel_blocks(arm.body).kind;
+ if let ExprKind::Path(ref some_path) = e.kind;
+ if is_lang_ctor(cx, some_path, LangItem::OptionSome) && args.len() == 1;
+ if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
+ if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
+ then {
+ return Some(rb)
+ }
+ }
+ None
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_unit_expr;
+use clippy_utils::source::{expr_block, snippet};
+use clippy_utils::sugg::Sugg;
+use rustc_ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{Arm, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::MATCH_BOOL;
+
+pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ // Type of expression is `bool`.
+ if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
+ span_lint_and_then(
+ cx,
+ MATCH_BOOL,
+ expr.span,
+ "you seem to be trying to match on a boolean expression",
+ move |diag| {
+ if arms.len() == 2 {
+ // no guards
+ let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
+ if let ExprKind::Lit(ref lit) = arm_bool.kind {
+ match lit.node {
+ LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
+ LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
+ _ => None,
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ if let Some((true_expr, false_expr)) = exprs {
+ let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
+ (false, false) => Some(format!(
+ "if {} {} else {}",
+ snippet(cx, ex.span, "b"),
+ expr_block(cx, true_expr, None, "..", Some(expr.span)),
+ expr_block(cx, false_expr, None, "..", Some(expr.span))
+ )),
+ (false, true) => Some(format!(
+ "if {} {}",
+ snippet(cx, ex.span, "b"),
+ expr_block(cx, true_expr, None, "..", Some(expr.span))
+ )),
+ (true, false) => {
+ let test = Sugg::hir(cx, ex, "..");
+ Some(format!(
+ "if {} {}",
+ !test,
+ expr_block(cx, false_expr, None, "..", Some(expr.span))
+ ))
+ },
+ (true, true) => None,
+ };
+
+ if let Some(sugg) = sugg {
+ diag.span_suggestion(
+ expr.span,
+ "consider using an `if`/`else` expression",
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+ }
+ },
+ );
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{higher, is_wild};
+use rustc_ast::{Attribute, LitKind};
+use rustc_errors::Applicability;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Guard, MatchSource, Pat};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Spanned;
+
+use super::MATCH_LIKE_MATCHES_MACRO;
+
+/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
+pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else: Some(if_else),
+ }) = higher::IfLet::hir(cx, expr)
+ {
+ return find_matches_sugg(
+ cx,
+ let_expr,
+ IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
+ expr,
+ true,
+ );
+ }
+
+ if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
+ return find_matches_sugg(
+ cx,
+ scrut,
+ arms.iter().map(|arm| {
+ (
+ cx.tcx.hir().attrs(arm.hir_id),
+ Some(arm.pat),
+ arm.body,
+ arm.guard.as_ref(),
+ )
+ }),
+ expr,
+ false,
+ );
+ }
+
+ false
+}
+
+/// Lint a `match` or `if let` for replacement by `matches!`
+fn find_matches_sugg<'a, 'b, I>(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ mut iter: I,
+ expr: &Expr<'_>,
+ is_if_let: bool,
+) -> bool
+where
+ 'b: 'a,
+ I: Clone
+ + DoubleEndedIterator
+ + ExactSizeIterator
+ + Iterator<
+ Item = (
+ &'a [Attribute],
+ Option<&'a Pat<'b>>,
+ &'a Expr<'b>,
+ Option<&'a Guard<'b>>,
+ ),
+ >,
+{
+ if_chain! {
+ if iter.len() >= 2;
+ if cx.typeck_results().expr_ty(expr).is_bool();
+ if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
+ let iter_without_last = iter.clone();
+ if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
+ if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
+ if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
+ if b0 != b1;
+ if first_guard.is_none() || iter.len() == 0;
+ if first_attrs.is_empty();
+ if iter
+ .all(|arm| {
+ find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
+ });
+ then {
+ if let Some(last_pat) = last_pat_opt {
+ if !is_wild(last_pat) {
+ return false;
+ }
+ }
+
+ // The suggestion may be incorrect, because some arms can have `cfg` attributes
+ // evaluated into `false` and so such arms will be stripped before.
+ let mut applicability = Applicability::MaybeIncorrect;
+ let pat = {
+ use itertools::Itertools as _;
+ iter_without_last
+ .filter_map(|arm| {
+ let pat_span = arm.1?.span;
+ Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
+ })
+ .join(" | ")
+ };
+ let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
+ format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
+ } else {
+ pat
+ };
+
+ // strip potential borrows (#6503), but only if the type is a reference
+ let mut ex_new = ex;
+ if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
+ if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
+ ex_new = ex_inner;
+ }
+ };
+ span_lint_and_sugg(
+ cx,
+ MATCH_LIKE_MATCHES_MACRO,
+ expr.span,
+ &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
+ "try this",
+ format!(
+ "{}matches!({}, {})",
+ if b0 { "" } else { "!" },
+ snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
+ pat_and_guard,
+ ),
+ applicability,
+ );
+ true
+ } else {
+ false
+ }
+ }
+}
+
+/// Extract a `bool` or `{ bool }`
+fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
+ match ex {
+ ExprKind::Lit(Spanned {
+ node: LitKind::Bool(b), ..
+ }) => Some(*b),
+ ExprKind::Block(
+ rustc_hir::Block {
+ stmts: &[],
+ expr: Some(exp),
+ ..
+ },
+ _,
+ ) if is_if_let => {
+ if let ExprKind::Lit(Spanned {
+ node: LitKind::Bool(b), ..
+ }) = exp.kind
+ {
+ Some(b)
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
+use core::iter::once;
+use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
+use rustc_lint::LateContext;
+
+use super::MATCH_REF_PATS;
+
+pub(crate) fn check<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
+where
+ 'b: 'a,
+ I: Clone + Iterator<Item = &'a Pat<'b>>,
+{
+ if !has_multiple_ref_pats(pats.clone()) {
+ return;
+ }
+
+ let (first_sugg, msg, title);
+ let span = ex.span.source_callsite();
+ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
+ first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
+ msg = "try";
+ title = "you don't need to add `&` to both the expression and the patterns";
+ } else {
+ first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
+ msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
+ title = "you don't need to add `&` to all patterns";
+ }
+
+ let remaining_suggs = pats.filter_map(|pat| {
+ if let PatKind::Ref(refp, _) = pat.kind {
+ Some((pat.span, snippet(cx, refp.span, "..").to_string()))
+ } else {
+ None
+ }
+ });
+
+ span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
+ if !expr.span.from_expansion() {
+ multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
+ }
+ });
+}
+
+fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
+where
+ 'b: 'a,
+ I: Iterator<Item = &'a Pat<'b>>,
+{
+ let mut ref_count = 0;
+ for opt in pats.map(|pat| match pat.kind {
+ PatKind::Ref(..) => Some(true), // &-patterns
+ PatKind::Wild => Some(false), // an "anything" wildcard is also fine
+ _ => None, // any other pattern is not fine
+ }) {
+ if let Some(inner) = opt {
+ if inner {
+ ref_count += 1;
+ }
+ } else {
+ return false;
+ }
+ }
+ ref_count > 1
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
+use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, MatchSource, Pat, PatKind};
+use rustc_lint::LateContext;
+use std::collections::hash_map::Entry;
+
+use super::MATCH_SAME_ARMS;
+
+pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
+ let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
+ let mut h = SpanlessHash::new(cx);
+ h.hash_expr(arm.body);
+ h.finish()
+ };
+
+ let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
+ let min_index = usize::min(lindex, rindex);
+ let max_index = usize::max(lindex, rindex);
+
+ let mut local_map: HirIdMap<HirId> = HirIdMap::default();
+ let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
+ if_chain! {
+ if let Some(a_id) = path_to_local(a);
+ if let Some(b_id) = path_to_local(b);
+ let entry = match local_map.entry(a_id) {
+ Entry::Vacant(entry) => entry,
+ // check if using the same bindings as before
+ Entry::Occupied(entry) => return *entry.get() == b_id,
+ };
+ // the names technically don't have to match; this makes the lint more conservative
+ if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
+ if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
+ if pat_contains_local(lhs.pat, a_id);
+ if pat_contains_local(rhs.pat, b_id);
+ then {
+ entry.insert(b_id);
+ true
+ } else {
+ false
+ }
+ }
+ };
+ // Arms with a guard are ignored, those can’t always be merged together
+ // This is also the case for arms in-between each there is an arm with a guard
+ (min_index..=max_index).all(|index| arms[index].guard.is_none())
+ && SpanlessEq::new(cx)
+ .expr_fallback(eq_fallback)
+ .eq_expr(lhs.body, rhs.body)
+ // these checks could be removed to allow unused bindings
+ && bindings_eq(lhs.pat, local_map.keys().copied().collect())
+ && bindings_eq(rhs.pat, local_map.values().copied().collect())
+ };
+
+ let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
+ for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
+ span_lint_and_then(
+ cx,
+ MATCH_SAME_ARMS,
+ j.body.span,
+ "this `match` has identical arm bodies",
+ |diag| {
+ diag.span_note(i.body.span, "same as this");
+
+ // Note: this does not use `span_suggestion` on purpose:
+ // there is no clean way
+ // to remove the other arm. Building a span and suggest to replace it to ""
+ // makes an even more confusing error message. Also in order not to make up a
+ // span for the whole pattern, the suggestion is only shown when there is only
+ // one pattern. The user should know about `|` if they are already using it…
+
+ let lhs = snippet(cx, i.pat.span, "<pat1>");
+ let rhs = snippet(cx, j.pat.span, "<pat2>");
+
+ if let PatKind::Wild = j.pat.kind {
+ // if the last arm is _, then i could be integrated into _
+ // note that i.pat cannot be _, because that would mean that we're
+ // hiding all the subsequent arms, and rust won't compile
+ diag.span_note(
+ i.body.span,
+ &format!(
+ "`{}` has the same arm body as the `_` wildcard, consider removing it",
+ lhs
+ ),
+ );
+ } else {
+ diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
+ .help("...or consider changing the match arm bodies");
+ }
+ },
+ );
+ }
+ }
+}
+
+fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
+ let mut result = false;
+ pat.walk_short(|p| {
+ result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
+ !result
+ });
+ result
+}
+
+/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
+fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
+ let mut result = true;
+ pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
+ result && ids.is_empty()
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, snippet_block, snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
+use rustc_errors::Applicability;
+use rustc_hir::{Arm, Expr, ExprKind, Local, Node, PatKind};
+use rustc_lint::LateContext;
+
+use super::MATCH_SINGLE_BINDING;
+
+#[allow(clippy::too_many_lines)]
+pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
+ return;
+ }
+
+ // HACK:
+ // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
+ // to prevent false positives as there is currently no better way to detect if code was excluded by
+ // a macro. See PR #6435
+ if_chain! {
+ if let Some(match_snippet) = snippet_opt(cx, expr.span);
+ if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
+ if let Some(ex_snippet) = snippet_opt(cx, ex.span);
+ let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
+ if rest_snippet.contains("=>");
+ then {
+ // The code it self contains another thick arrow "=>"
+ // -> Either another arm or a comment
+ return;
+ }
+ }
+
+ let matched_vars = ex.span;
+ let bind_names = arms[0].pat.span;
+ let match_body = peel_blocks(arms[0].body);
+ let mut snippet_body = if match_body.span.from_expansion() {
+ Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
+ } else {
+ snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
+ };
+
+ // Do we need to add ';' to suggestion ?
+ match match_body.kind {
+ ExprKind::Block(block, _) => {
+ // macro + expr_ty(body) == ()
+ if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
+ snippet_body.push(';');
+ }
+ },
+ _ => {
+ // expr_ty(body) == ()
+ if cx.typeck_results().expr_ty(match_body).is_unit() {
+ snippet_body.push(';');
+ }
+ },
+ }
+
+ let mut applicability = Applicability::MaybeIncorrect;
+ match arms[0].pat.kind {
+ PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
+ // If this match is in a local (`let`) stmt
+ let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
+ (
+ parent_let_node.span,
+ format!(
+ "let {} = {};\n{}let {} = {};",
+ snippet_with_applicability(cx, bind_names, "..", &mut applicability),
+ snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
+ " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
+ snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
+ snippet_body
+ ),
+ )
+ } else {
+ // If we are in closure, we need curly braces around suggestion
+ let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
+ let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
+ if let Some(parent_expr) = get_parent_expr(cx, expr) {
+ if let ExprKind::Closure(..) = parent_expr.kind {
+ cbrace_end = format!("\n{}}}", indent);
+ // Fix body indent due to the closure
+ indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
+ cbrace_start = format!("{{\n{}", indent);
+ }
+ }
+ // If the parent is already an arm, and the body is another match statement,
+ // we need curly braces around suggestion
+ let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+ if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
+ if let ExprKind::Match(..) = arm.body.kind {
+ cbrace_end = format!("\n{}}}", indent);
+ // Fix body indent due to the match
+ indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
+ cbrace_start = format!("{{\n{}", indent);
+ }
+ }
+ (
+ expr.span,
+ format!(
+ "{}let {} = {};\n{}{}{}",
+ cbrace_start,
+ snippet_with_applicability(cx, bind_names, "..", &mut applicability),
+ snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
+ indent,
+ snippet_body,
+ cbrace_end
+ ),
+ )
+ };
+ span_lint_and_sugg(
+ cx,
+ MATCH_SINGLE_BINDING,
+ target_span,
+ "this match could be written as a `let` statement",
+ "consider using `let` statement",
+ sugg,
+ applicability,
+ );
+ },
+ PatKind::Wild => {
+ if ex.can_have_side_effects() {
+ let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
+ let sugg = format!(
+ "{};\n{}{}",
+ snippet_with_applicability(cx, ex.span, "..", &mut applicability),
+ indent,
+ snippet_body
+ );
+ span_lint_and_sugg(
+ cx,
+ MATCH_SINGLE_BINDING,
+ expr.span,
+ "this match could be replaced by its scrutinee and body",
+ "consider using the scrutinee and body instead",
+ sugg,
+ applicability,
+ );
+ } else {
+ span_lint_and_sugg(
+ cx,
+ MATCH_SINGLE_BINDING,
+ expr.span,
+ "this match could be replaced by its body itself",
+ "consider using the match body instead",
+ snippet_body,
+ Applicability::MachineApplicable,
+ );
+ }
+ },
+ _ => (),
+ }
+}
+
+/// Returns true if the `ex` match expression is in a local (`let`) statement
+fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
+ let map = &cx.tcx.hir();
+ if_chain! {
+ if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
+ if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
+ then {
+ return Some(parent_let_expr);
+ }
+ }
+ None
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns};
+use rustc_errors::Applicability;
+use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, VariantDef};
+use rustc_span::sym;
+
+use super::{MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_ENUM_MATCH_ARM};
+
+#[allow(clippy::too_many_lines)]
+pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
+ let ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ let adt_def = match ty.kind() {
+ ty::Adt(adt_def, _)
+ if adt_def.is_enum()
+ && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
+ {
+ adt_def
+ },
+ _ => return,
+ };
+
+ // First pass - check for violation, but don't do much book-keeping because this is hopefully
+ // the uncommon case, and the book-keeping is slightly expensive.
+ let mut wildcard_span = None;
+ let mut wildcard_ident = None;
+ let mut has_non_wild = false;
+ for arm in arms {
+ match peel_hir_pat_refs(arm.pat).0.kind {
+ PatKind::Wild => wildcard_span = Some(arm.pat.span),
+ PatKind::Binding(_, _, ident, None) => {
+ wildcard_span = Some(arm.pat.span);
+ wildcard_ident = Some(ident);
+ },
+ _ => has_non_wild = true,
+ }
+ }
+ let wildcard_span = match wildcard_span {
+ Some(x) if has_non_wild => x,
+ _ => return,
+ };
+
+ // Accumulate the variants which should be put in place of the wildcard because they're not
+ // already covered.
+ let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
+ let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
+
+ let mut path_prefix = CommonPrefixSearcher::None;
+ for arm in arms {
+ // Guards mean that this case probably isn't exhaustively covered. Technically
+ // this is incorrect, as we should really check whether each variant is exhaustively
+ // covered by the set of guards that cover it, but that's really hard to do.
+ recurse_or_patterns(arm.pat, |pat| {
+ let path = match &peel_hir_pat_refs(pat).0.kind {
+ PatKind::Path(path) => {
+ #[allow(clippy::match_same_arms)]
+ let id = match cx.qpath_res(path, pat.hir_id) {
+ Res::Def(
+ DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst,
+ _,
+ ) => return,
+ Res::Def(_, id) => id,
+ _ => return,
+ };
+ if arm.guard.is_none() {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ path
+ },
+ PatKind::TupleStruct(path, patterns, ..) => {
+ if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
+ missing_variants.retain(|e| e.ctor_def_id != Some(id));
+ }
+ }
+ path
+ },
+ PatKind::Struct(path, patterns, ..) => {
+ if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
+ if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
+ missing_variants.retain(|e| e.def_id != id);
+ }
+ }
+ path
+ },
+ _ => return,
+ };
+ match path {
+ QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
+ QPath::TypeRelative(
+ Ty {
+ kind: TyKind::Path(QPath::Resolved(_, path)),
+ ..
+ },
+ _,
+ ) => path_prefix.with_prefix(path.segments),
+ _ => (),
+ }
+ });
+ }
+
+ let format_suggestion = |variant: &VariantDef| {
+ format!(
+ "{}{}{}{}",
+ if let Some(ident) = wildcard_ident {
+ format!("{} @ ", ident.name)
+ } else {
+ String::new()
+ },
+ if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
+ let mut s = String::new();
+ for seg in path_prefix {
+ s.push_str(seg.ident.as_str());
+ s.push_str("::");
+ }
+ s
+ } else {
+ let mut s = cx.tcx.def_path_str(adt_def.did);
+ s.push_str("::");
+ s
+ },
+ variant.name,
+ match variant.ctor_kind {
+ CtorKind::Fn if variant.fields.len() == 1 => "(_)",
+ CtorKind::Fn => "(..)",
+ CtorKind::Const => "",
+ CtorKind::Fictive => "{ .. }",
+ }
+ )
+ };
+
+ match missing_variants.as_slice() {
+ [] => (),
+ [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
+ cx,
+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ wildcard_span,
+ "wildcard matches only a single variant and will also match any future added variants",
+ "try this",
+ format_suggestion(x),
+ Applicability::MaybeIncorrect,
+ ),
+ variants => {
+ let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
+ let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
+ suggestions.push("_".into());
+ "wildcard matches known variants and will also match future added variants"
+ } else {
+ "wildcard match will also match any future added variants"
+ };
+
+ span_lint_and_sugg(
+ cx,
+ WILDCARD_ENUM_MATCH_ARM,
+ wildcard_span,
+ message,
+ "try this",
+ suggestions.join(" | "),
+ Applicability::MaybeIncorrect,
+ );
+ },
+ };
+}
+
+enum CommonPrefixSearcher<'a> {
+ None,
+ Path(&'a [PathSegment<'a>]),
+ Mixed,
+}
+impl<'a> CommonPrefixSearcher<'a> {
+ fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
+ match path {
+ [path @ .., _] => self.with_prefix(path),
+ [] => (),
+ }
+ }
+
+ fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
+ match self {
+ Self::None => *self = Self::Path(path),
+ Self::Path(self_path)
+ if path
+ .iter()
+ .map(|p| p.ident.name)
+ .eq(self_path.iter().map(|p| p.ident.name)) => {},
+ Self::Path(_) => *self = Self::Mixed,
+ Self::Mixed => (),
+ }
+ }
+}
+
+fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
+ let attrs = cx.tcx.get_attrs(variant_def.def_id);
+ clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::macros::{is_panic, root_macro_call};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{is_wild, peel_blocks_with_stmt};
+use rustc_hir::{Arm, Expr, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::{kw, sym};
+
+use super::MATCH_WILD_ERR_ARM;
+
+pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
+ let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
+ if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
+ for arm in arms {
+ if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
+ let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
+ if path_str == "Err" {
+ let mut matching_wild = inner.iter().any(is_wild);
+ let mut ident_bind_name = kw::Underscore;
+ if !matching_wild {
+ // Looking for unused bindings (i.e.: `_e`)
+ for pat in inner.iter() {
+ if let PatKind::Binding(_, id, ident, None) = pat.kind {
+ if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
+ ident_bind_name = ident.name;
+ matching_wild = true;
+ }
+ }
+ }
+ }
+ if_chain! {
+ if matching_wild;
+ if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span);
+ if is_panic(cx, macro_call.def_id);
+ then {
+ // `Err(_)` or `Err(_e)` arm with `panic!` found
+ span_lint_and_note(cx,
+ MATCH_WILD_ERR_ARM,
+ arm.pat.span,
+ &format!("`Err({})` matches all errors", ident_bind_name),
+ None,
+ "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
+ );
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+use clippy_utils::{meets_msrv, msrvs};
+use rustc_hir::{Expr, ExprKind, Local, MatchSource, Pat};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+mod infalliable_detructuring_match;
+mod match_as_ref;
+mod match_bool;
+mod match_like_matches;
+mod match_ref_pats;
+mod match_same_arms;
+mod match_single_binding;
+mod match_wild_enum;
+mod match_wild_err_arm;
+mod overlapping_arms;
+mod redundant_pattern_match;
+mod rest_pat_in_fully_bound_struct;
+mod single_match;
+mod wild_in_or_pats;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches with a single arm where an `if let`
+ /// will usually suffice.
+ ///
+ /// ### Why is this bad?
+ /// Just readability – `if let` nests less than a `match`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn bar(stool: &str) {}
+ /// # let x = Some("abc");
+ /// // Bad
+ /// match x {
+ /// Some(ref foo) => bar(foo),
+ /// _ => (),
+ /// }
+ ///
+ /// // Good
+ /// if let Some(ref foo) = x {
+ /// bar(foo);
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub SINGLE_MATCH,
+ style,
+ "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches with two arms where an `if let else` will
+ /// usually suffice.
+ ///
+ /// ### Why is this bad?
+ /// Just readability – `if let` nests less than a `match`.
+ ///
+ /// ### Known problems
+ /// Personal style preferences may differ.
+ ///
+ /// ### Example
+ /// Using `match`:
+ ///
+ /// ```rust
+ /// # fn bar(foo: &usize) {}
+ /// # let other_ref: usize = 1;
+ /// # let x: Option<&usize> = Some(&1);
+ /// match x {
+ /// Some(ref foo) => bar(foo),
+ /// _ => bar(&other_ref),
+ /// }
+ /// ```
+ ///
+ /// Using `if let` with `else`:
+ ///
+ /// ```rust
+ /// # fn bar(foo: &usize) {}
+ /// # let other_ref: usize = 1;
+ /// # let x: Option<&usize> = Some(&1);
+ /// if let Some(ref foo) = x {
+ /// bar(foo);
+ /// } else {
+ /// bar(&other_ref);
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub SINGLE_MATCH_ELSE,
+ pedantic,
+ "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches where all arms match a reference,
+ /// suggesting to remove the reference and deref the matched expression
+ /// instead. It also checks for `if let &foo = bar` blocks.
+ ///
+ /// ### Why is this bad?
+ /// It just makes the code less readable. That reference
+ /// destructuring adds nothing to the code.
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// // Bad
+ /// match x {
+ /// &A(ref y) => foo(y),
+ /// &B => bar(),
+ /// _ => frob(&x),
+ /// }
+ ///
+ /// // Good
+ /// match *x {
+ /// A(ref y) => foo(y),
+ /// B => bar(),
+ /// _ => frob(x),
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MATCH_REF_PATS,
+ style,
+ "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches where match expression is a `bool`. It
+ /// suggests to replace the expression with an `if...else` block.
+ ///
+ /// ### Why is this bad?
+ /// It makes the code less readable.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # fn foo() {}
+ /// # fn bar() {}
+ /// let condition: bool = true;
+ /// match condition {
+ /// true => foo(),
+ /// false => bar(),
+ /// }
+ /// ```
+ /// Use if/else instead:
+ /// ```rust
+ /// # fn foo() {}
+ /// # fn bar() {}
+ /// let condition: bool = true;
+ /// if condition {
+ /// foo();
+ /// } else {
+ /// bar();
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MATCH_BOOL,
+ pedantic,
+ "a `match` on a boolean expression instead of an `if..else` block"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for overlapping match arms.
+ ///
+ /// ### Why is this bad?
+ /// It is likely to be an error and if not, makes the code
+ /// less obvious.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = 5;
+ /// match x {
+ /// 1..=10 => println!("1 ... 10"),
+ /// 5..=15 => println!("5 ... 15"),
+ /// _ => (),
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MATCH_OVERLAPPING_ARM,
+ style,
+ "a `match` with overlapping arms"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for arm which matches all errors with `Err(_)`
+ /// and take drastic actions like `panic!`.
+ ///
+ /// ### Why is this bad?
+ /// It is generally a bad practice, similar to
+ /// catching all exceptions in java with `catch(Exception)`
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: Result<i32, &str> = Ok(3);
+ /// match x {
+ /// Ok(_) => println!("ok"),
+ /// Err(_) => panic!("err"),
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MATCH_WILD_ERR_ARM,
+ pedantic,
+ "a `match` with `Err(_)` arm and take drastic actions"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for match which is used to add a reference to an
+ /// `Option` value.
+ ///
+ /// ### Why is this bad?
+ /// Using `as_ref()` or `as_mut()` instead is shorter.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x: Option<()> = None;
+ ///
+ /// // Bad
+ /// let r: Option<&()> = match x {
+ /// None => None,
+ /// Some(ref v) => Some(v),
+ /// };
+ ///
+ /// // Good
+ /// let r: Option<&()> = x.as_ref();
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MATCH_AS_REF,
+ complexity,
+ "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for wildcard enum matches using `_`.
+ ///
+ /// ### Why is this bad?
+ /// New enum variants added by library updates can be missed.
+ ///
+ /// ### Known problems
+ /// Suggested replacements may be incorrect if guards exhaustively cover some
+ /// variants, and also may not use correct path to enum if it's not present in the current scope.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # enum Foo { A(usize), B(usize) }
+ /// # let x = Foo::B(1);
+ /// // Bad
+ /// match x {
+ /// Foo::A(_) => {},
+ /// _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match x {
+ /// Foo::A(_) => {},
+ /// Foo::B(_) => {},
+ /// }
+ /// ```
+ #[clippy::version = "1.34.0"]
+ pub WILDCARD_ENUM_MATCH_ARM,
+ restriction,
+ "a wildcard enum match arm using `_`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for wildcard enum matches for a single variant.
+ ///
+ /// ### Why is this bad?
+ /// New enum variants added by library updates can be missed.
+ ///
+ /// ### Known problems
+ /// Suggested replacements may not use correct path to enum
+ /// if it's not present in the current scope.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # enum Foo { A, B, C }
+ /// # let x = Foo::B;
+ /// // Bad
+ /// match x {
+ /// Foo::A => {},
+ /// Foo::B => {},
+ /// _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match x {
+ /// Foo::A => {},
+ /// Foo::B => {},
+ /// Foo::C => {},
+ /// }
+ /// ```
+ #[clippy::version = "1.45.0"]
+ pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ pedantic,
+ "a wildcard enum match for a single variant"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for wildcard pattern used with others patterns in same match arm.
+ ///
+ /// ### Why is this bad?
+ /// Wildcard pattern already covers any other pattern as it will match anyway.
+ /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// match "foo" {
+ /// "a" => {},
+ /// "bar" | _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match "foo" {
+ /// "a" => {},
+ /// _ => {},
+ /// }
+ /// ```
+ #[clippy::version = "1.42.0"]
+ pub WILDCARD_IN_OR_PATTERNS,
+ complexity,
+ "a wildcard pattern used with others patterns in same match arm"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for matches being used to destructure a single-variant enum
+ /// or tuple struct where a `let` will suffice.
+ ///
+ /// ### Why is this bad?
+ /// Just readability – `let` doesn't nest, whereas a `match` does.
+ ///
+ /// ### Example
+ /// ```rust
+ /// enum Wrapper {
+ /// Data(i32),
+ /// }
+ ///
+ /// let wrapper = Wrapper::Data(42);
+ ///
+ /// let data = match wrapper {
+ /// Wrapper::Data(i) => i,
+ /// };
+ /// ```
+ ///
+ /// The correct use would be:
+ /// ```rust
+ /// enum Wrapper {
+ /// Data(i32),
+ /// }
+ ///
+ /// let wrapper = Wrapper::Data(42);
+ /// let Wrapper::Data(data) = wrapper;
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub INFALLIBLE_DESTRUCTURING_MATCH,
+ style,
+ "a `match` statement with a single infallible arm instead of a `let`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for useless match that binds to only one value.
+ ///
+ /// ### Why is this bad?
+ /// Readability and needless complexity.
+ ///
+ /// ### Known problems
+ /// Suggested replacements may be incorrect when `match`
+ /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # let a = 1;
+ /// # let b = 2;
+ ///
+ /// // Bad
+ /// match (a, b) {
+ /// (c, d) => {
+ /// // useless match
+ /// }
+ /// }
+ ///
+ /// // Good
+ /// let (c, d) = (a, b);
+ /// ```
+ #[clippy::version = "1.43.0"]
+ pub MATCH_SINGLE_BINDING,
+ complexity,
+ "a match with a single binding instead of using `let` statement"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
+ ///
+ /// ### Why is this bad?
+ /// Correctness and readability. It's like having a wildcard pattern after
+ /// matching all enum variants explicitly.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # struct A { a: i32 }
+ /// let a = A { a: 5 };
+ ///
+ /// // Bad
+ /// match a {
+ /// A { a: 5, .. } => {},
+ /// _ => {},
+ /// }
+ ///
+ /// // Good
+ /// match a {
+ /// A { a: 5 } => {},
+ /// _ => {},
+ /// }
+ /// ```
+ #[clippy::version = "1.43.0"]
+ pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ restriction,
+ "a match on a struct that binds all fields but still uses the wildcard pattern"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Lint for redundant pattern matching over `Result`, `Option`,
+ /// `std::task::Poll` or `std::net::IpAddr`
+ ///
+ /// ### Why is this bad?
+ /// It's more concise and clear to just use the proper
+ /// utility function
+ ///
+ /// ### Known problems
+ /// This will change the drop order for the matched type. Both `if let` and
+ /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
+ /// value before entering the block. For most types this change will not matter, but for a few
+ /// types this will not be an acceptable change (e.g. locks). See the
+ /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
+ /// drop order.
+ ///
+ /// ### Example
+ /// ```rust
+ /// # use std::task::Poll;
+ /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ /// if let Ok(_) = Ok::<i32, i32>(42) {}
+ /// if let Err(_) = Err::<i32, i32>(42) {}
+ /// if let None = None::<()> {}
+ /// if let Some(_) = Some(42) {}
+ /// if let Poll::Pending = Poll::Pending::<()> {}
+ /// if let Poll::Ready(_) = Poll::Ready(42) {}
+ /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
+ /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
+ /// match Ok::<i32, i32>(42) {
+ /// Ok(_) => true,
+ /// Err(_) => false,
+ /// };
+ /// ```
+ ///
+ /// The more idiomatic use would be:
+ ///
+ /// ```rust
+ /// # use std::task::Poll;
+ /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
+ /// if Ok::<i32, i32>(42).is_ok() {}
+ /// if Err::<i32, i32>(42).is_err() {}
+ /// if None::<()>.is_none() {}
+ /// if Some(42).is_some() {}
+ /// if Poll::Pending::<()>.is_pending() {}
+ /// if Poll::Ready(42).is_ready() {}
+ /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
+ /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
+ /// Ok::<i32, i32>(42).is_ok();
+ /// ```
+ #[clippy::version = "1.31.0"]
+ pub REDUNDANT_PATTERN_MATCHING,
+ style,
+ "use the proper utility function avoiding an `if let`"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `match` or `if let` expressions producing a
+ /// `bool` that could be written using `matches!`
+ ///
+ /// ### Why is this bad?
+ /// Readability and needless complexity.
+ ///
+ /// ### Known problems
+ /// This lint falsely triggers, if there are arms with
+ /// `cfg` attributes that remove an arm evaluating to `false`.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let x = Some(5);
+ ///
+ /// // Bad
+ /// let a = match x {
+ /// Some(0) => true,
+ /// _ => false,
+ /// };
+ ///
+ /// let a = if let Some(0) = x {
+ /// true
+ /// } else {
+ /// false
+ /// };
+ ///
+ /// // Good
+ /// let a = matches!(x, Some(0));
+ /// ```
+ #[clippy::version = "1.47.0"]
+ pub MATCH_LIKE_MATCHES_MACRO,
+ style,
+ "a match that could be written with the matches! macro"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `match` with identical arm bodies.
+ ///
+ /// ### Why is this bad?
+ /// This is probably a copy & paste error. If arm bodies
+ /// are the same on purpose, you can factor them
+ /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
+ ///
+ /// ### Known problems
+ /// False positive possible with order dependent `match`
+ /// (see issue
+ /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
+ ///
+ /// ### Example
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => bar(), // <= oops
+ /// }
+ /// ```
+ ///
+ /// This should probably be
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => baz(), // <= fixed
+ /// }
+ /// ```
+ ///
+ /// or if the original code was not a typo:
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar | Baz => bar(), // <= shows the intent better
+ /// Quz => quz(),
+ /// }
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub MATCH_SAME_ARMS,
+ pedantic,
+ "`match` with identical arm bodies"
+}
+
+#[derive(Default)]
+pub struct Matches {
+ msrv: Option<RustcVersion>,
+ infallible_destructuring_match_linted: bool,
+}
+
+impl Matches {
+ #[must_use]
+ pub fn new(msrv: Option<RustcVersion>) -> Self {
+ Self {
+ msrv,
+ ..Matches::default()
+ }
+ }
+}
+
+impl_lint_pass!(Matches => [
+ SINGLE_MATCH,
+ MATCH_REF_PATS,
+ MATCH_BOOL,
+ SINGLE_MATCH_ELSE,
+ MATCH_OVERLAPPING_ARM,
+ MATCH_WILD_ERR_ARM,
+ MATCH_AS_REF,
+ WILDCARD_ENUM_MATCH_ARM,
+ MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+ WILDCARD_IN_OR_PATTERNS,
+ MATCH_SINGLE_BINDING,
+ INFALLIBLE_DESTRUCTURING_MATCH,
+ REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ REDUNDANT_PATTERN_MATCHING,
+ MATCH_LIKE_MATCHES_MACRO,
+ MATCH_SAME_ARMS,
+]);
+
+impl<'tcx> LateLintPass<'tcx> for Matches {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if expr.span.from_expansion() {
+ return;
+ }
+
+ redundant_pattern_match::check(cx, expr);
+
+ if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
+ if !match_like_matches::check(cx, expr) {
+ match_same_arms::check(cx, expr);
+ }
+ } else {
+ match_same_arms::check(cx, expr);
+ }
+
+ if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
+ single_match::check(cx, ex, arms, expr);
+ match_bool::check(cx, ex, arms, expr);
+ overlapping_arms::check(cx, ex, arms);
+ match_wild_err_arm::check(cx, ex, arms);
+ match_wild_enum::check(cx, ex, arms);
+ match_as_ref::check(cx, ex, arms, expr);
+ wild_in_or_pats::check(cx, arms);
+
+ if self.infallible_destructuring_match_linted {
+ self.infallible_destructuring_match_linted = false;
+ } else {
+ match_single_binding::check(cx, ex, arms, expr);
+ }
+ }
+ if let ExprKind::Match(ex, arms, _) = expr.kind {
+ match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
+ }
+ }
+
+ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
+ self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local);
+ }
+
+ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
+ rest_pat_in_fully_bound_struct::check(cx, pat);
+ }
+
+ extract_msrv_attr!(LateContext);
+}
--- /dev/null
+use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt};
+use clippy_utils::diagnostics::span_lint_and_note;
+use core::cmp::Ordering;
+use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::Span;
+
+use super::MATCH_OVERLAPPING_ARM;
+
+pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
+ if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
+ let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
+ if !ranges.is_empty() {
+ if let Some((start, end)) = overlapping(&ranges) {
+ span_lint_and_note(
+ cx,
+ MATCH_OVERLAPPING_ARM,
+ start.span,
+ "some ranges overlap",
+ Some(end.span),
+ "overlaps with this",
+ );
+ }
+ }
+ }
+}
+
+/// Gets the ranges for each range pattern arm. Applies `ty` bounds for open ranges.
+fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
+ arms.iter()
+ .filter_map(|arm| {
+ if let Arm { pat, guard: None, .. } = *arm {
+ if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
+ let lhs_const = match lhs {
+ Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
+ None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
+ };
+ let rhs_const = match rhs {
+ Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
+ None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
+ };
+
+ let lhs_val = lhs_const.int_value(cx, ty)?;
+ let rhs_val = rhs_const.int_value(cx, ty)?;
+
+ let rhs_bound = match range_end {
+ RangeEnd::Included => EndBound::Included(rhs_val),
+ RangeEnd::Excluded => EndBound::Excluded(rhs_val),
+ };
+ return Some(SpannedRange {
+ span: pat.span,
+ node: (lhs_val, rhs_bound),
+ });
+ }
+
+ if let PatKind::Lit(value) = pat.kind {
+ let value = constant_full_int(cx, cx.typeck_results(), value)?;
+ return Some(SpannedRange {
+ span: pat.span,
+ node: (value, EndBound::Included(value)),
+ });
+ }
+ }
+ None
+ })
+ .collect()
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum EndBound<T> {
+ Included(T),
+ Excluded(T),
+}
+
+#[derive(Debug, Eq, PartialEq)]
+struct SpannedRange<T> {
+ pub span: Span,
+ pub node: (T, EndBound<T>),
+}
+
+fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
+where
+ T: Copy + Ord,
+{
+ #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+ enum BoundKind {
+ EndExcluded,
+ Start,
+ EndIncluded,
+ }
+
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange<T>);
+
+ impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+ }
+
+ impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> {
+ fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering {
+ let RangeBound(self_value, self_kind, _) = *self;
+ (self_value, self_kind).cmp(&(*other_value, *other_kind))
+ }
+ }
+
+ let mut values = Vec::with_capacity(2 * ranges.len());
+
+ for r @ SpannedRange { node: (start, end), .. } in ranges {
+ values.push(RangeBound(*start, BoundKind::Start, r));
+ values.push(match end {
+ EndBound::Excluded(val) => RangeBound(*val, BoundKind::EndExcluded, r),
+ EndBound::Included(val) => RangeBound(*val, BoundKind::EndIncluded, r),
+ });
+ }
+
+ values.sort();
+
+ let mut started = vec![];
+
+ for RangeBound(_, kind, range) in values {
+ match kind {
+ BoundKind::Start => started.push(range),
+ BoundKind::EndExcluded | BoundKind::EndIncluded => {
+ let mut overlap = None;
+
+ while let Some(last_started) = started.pop() {
+ if last_started == range {
+ break;
+ }
+ overlap = Some(last_started);
+ }
+
+ if let Some(first_overlapping) = overlap {
+ return Some((range, first_overlapping));
+ }
+ },
+ }
+ }
+
+ None
+}
+
+#[test]
+fn test_overlapping() {
+ use rustc_span::source_map::DUMMY_SP;
+
+ let sp = |s, e| SpannedRange {
+ span: DUMMY_SP,
+ node: (s, e),
+ };
+
+ assert_eq!(None, overlapping::<u8>(&[]));
+ assert_eq!(None, overlapping(&[sp(1, EndBound::Included(4))]));
+ assert_eq!(
+ None,
+ overlapping(&[sp(1, EndBound::Included(4)), sp(5, EndBound::Included(6))])
+ );
+ assert_eq!(
+ None,
+ overlapping(&[
+ sp(1, EndBound::Included(4)),
+ sp(5, EndBound::Included(6)),
+ sp(10, EndBound::Included(11))
+ ],)
+ );
+ assert_eq!(
+ Some((&sp(1, EndBound::Included(4)), &sp(3, EndBound::Included(6)))),
+ overlapping(&[sp(1, EndBound::Included(4)), sp(3, EndBound::Included(6))])
+ );
+ assert_eq!(
+ Some((&sp(5, EndBound::Included(6)), &sp(6, EndBound::Included(11)))),
+ overlapping(&[
+ sp(1, EndBound::Included(4)),
+ sp(5, EndBound::Included(6)),
+ sp(6, EndBound::Included(11))
+ ],)
+ );
+}
--- /dev/null
+use super::REDUNDANT_PATTERN_MATCHING;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
+use clippy_utils::{higher, match_def_path};
+use clippy_utils::{is_lang_ctor, is_trait_method, paths};
+use if_chain::if_chain;
+use rustc_ast::ast::LitKind;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::{OptionNone, PollPending};
+use rustc_hir::{
+ intravisit::{walk_expr, Visitor},
+ Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
+};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
+use rustc_span::sym;
+
+pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let Some(higher::IfLet {
+ if_else,
+ let_pat,
+ let_expr,
+ ..
+ }) = higher::IfLet::hir(cx, expr)
+ {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
+ }
+ if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
+ find_sugg_for_match(cx, expr, op, arms);
+ }
+ if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
+ }
+}
+
+/// Checks if the drop order for a type matters. Some std types implement drop solely to
+/// deallocate memory. For these types, and composites containing them, changing the drop order
+/// won't result in any observable side effects.
+fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
+}
+
+fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
+ if !seen.insert(ty) {
+ return false;
+ }
+ if !ty.needs_drop(cx.tcx, cx.param_env) {
+ false
+ } else if !cx
+ .tcx
+ .lang_items()
+ .drop_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // This type doesn't implement drop, so no side effects here.
+ // Check if any component type has any.
+ match ty.kind() {
+ ty::Tuple(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
+ ty::Adt(adt, subs) => adt
+ .all_fields()
+ .map(|f| f.ty(cx.tcx, subs))
+ .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
+ _ => true,
+ }
+ }
+ // Check for std types which implement drop, but only for memory allocation.
+ else if is_type_diagnostic_item(cx, ty, sym::Vec)
+ || is_type_lang_item(cx, ty, LangItem::OwnedBox)
+ || is_type_diagnostic_item(cx, ty, sym::Rc)
+ || is_type_diagnostic_item(cx, ty, sym::Arc)
+ || is_type_diagnostic_item(cx, ty, sym::cstring_type)
+ || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
+ || is_type_diagnostic_item(cx, ty, sym::LinkedList)
+ || match_type(cx, ty, &paths::WEAK_RC)
+ || match_type(cx, ty, &paths::WEAK_ARC)
+ {
+ // Check all of the generic arguments.
+ if let ty::Adt(_, subs) = ty.kind() {
+ subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen))
+ } else {
+ true
+ }
+ } else {
+ true
+ }
+}
+
+// Extract the generic arguments out of a type
+fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
+ if_chain! {
+ if let ty::Adt(_, subs) = ty.kind();
+ if let Some(sub) = subs.get(index);
+ if let GenericArgKind::Type(sub_ty) = sub.unpack();
+ then {
+ Some(sub_ty)
+ } else {
+ None
+ }
+ }
+}
+
+// Checks if there are any temporaries created in the given expression for which drop order
+// matters.
+fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+ struct V<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ res: bool,
+ }
+ impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ match expr.kind {
+ // Taking the reference of a value leaves a temporary
+ // e.g. In `&String::new()` the string is a temporary value.
+ // Remaining fields are temporary values
+ // e.g. In `(String::new(), 0).1` the string is a temporary value.
+ ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
+ if !matches!(expr.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
+ self.res = true;
+ } else {
+ self.visit_expr(expr);
+ }
+ }
+ },
+ // the base type is alway taken by reference.
+ // e.g. In `(vec![0])[0]` the vector is a temporary value.
+ ExprKind::Index(base, index) => {
+ if !matches!(base.kind, ExprKind::Path(_)) {
+ if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
+ self.res = true;
+ } else {
+ self.visit_expr(base);
+ }
+ }
+ self.visit_expr(index);
+ },
+ // Method calls can take self by reference.
+ // e.g. In `String::new().len()` the string is a temporary value.
+ ExprKind::MethodCall(_, [self_arg, args @ ..], _) => {
+ if !matches!(self_arg.kind, ExprKind::Path(_)) {
+ let self_by_ref = self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(expr.hir_id)
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
+ if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
+ self.res = true;
+ } else {
+ self.visit_expr(self_arg);
+ }
+ }
+ args.iter().for_each(|arg| self.visit_expr(arg));
+ },
+ // Either explicitly drops values, or changes control flow.
+ ExprKind::DropTemps(_)
+ | ExprKind::Ret(_)
+ | ExprKind::Break(..)
+ | ExprKind::Yield(..)
+ | ExprKind::Block(Block { expr: None, .. }, _)
+ | ExprKind::Loop(..) => (),
+
+ // Only consider the final expression.
+ ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
+
+ _ => walk_expr(self, expr),
+ }
+ }
+ }
+
+ let mut v = V { cx, res: false };
+ v.visit_expr(expr);
+ v.res
+}
+
+fn find_sugg_for_if_let<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ let_pat: &Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
+ keyword: &'static str,
+ has_else: bool,
+) {
+ // also look inside refs
+ // if we have &None for example, peel it so we can detect "if let None = x"
+ let check_pat = match let_pat.kind {
+ PatKind::Ref(inner, _mutability) => inner,
+ _ => let_pat,
+ };
+ let op_ty = cx.typeck_results().expr_ty(let_expr);
+ // Determine which function should be used, and the type contained by the corresponding
+ // variant.
+ let (good_method, inner_ty) = match check_pat.kind {
+ PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
+ if let PatKind::Wild = sub_pat.kind {
+ let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
+ let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
+ let lang_items = cx.tcx.lang_items();
+ if Some(id) == lang_items.result_ok_variant() {
+ ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
+ } else if Some(id) == lang_items.result_err_variant() {
+ ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
+ } else if Some(id) == lang_items.option_some_variant() {
+ ("is_some()", op_ty)
+ } else if Some(id) == lang_items.poll_ready_variant() {
+ ("is_ready()", op_ty)
+ } else if match_def_path(cx, id, &paths::IPADDR_V4) {
+ ("is_ipv4()", op_ty)
+ } else if match_def_path(cx, id, &paths::IPADDR_V6) {
+ ("is_ipv6()", op_ty)
+ } else {
+ return;
+ }
+ } else {
+ return;
+ }
+ },
+ PatKind::Path(ref path) => {
+ let method = if is_lang_ctor(cx, path, OptionNone) {
+ "is_none()"
+ } else if is_lang_ctor(cx, path, PollPending) {
+ "is_pending()"
+ } else {
+ return;
+ };
+ // `None` and `Pending` don't have an inner type.
+ (method, cx.tcx.types.unit)
+ },
+ _ => return,
+ };
+
+ // If this is the last expression in a block or there is an else clause then the whole
+ // type needs to be considered, not just the inner type of the branch being matched on.
+ // Note the last expression in a block is dropped after all local bindings.
+ let check_ty = if has_else
+ || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
+ {
+ op_ty
+ } else {
+ inner_ty
+ };
+
+ // All temporaries created in the scrutinee expression are dropped at the same time as the
+ // scrutinee would be, so they have to be considered as well.
+ // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
+ // for the duration if body.
+ let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+
+ // check that `while_let_on_iterator` lint does not trigger
+ if_chain! {
+ if keyword == "while";
+ if let ExprKind::MethodCall(method_path, _, _) = let_expr.kind;
+ if method_path.ident.name == sym::next;
+ if is_trait_method(cx, let_expr, sym::Iterator);
+ then {
+ return;
+ }
+ }
+
+ let result_expr = match &let_expr.kind {
+ ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ ExprKind::Unary(UnOp::Deref, deref) => deref,
+ _ => let_expr,
+ };
+
+ span_lint_and_then(
+ cx,
+ REDUNDANT_PATTERN_MATCHING,
+ let_pat.span,
+ &format!("redundant pattern matching, consider using `{}`", good_method),
+ |diag| {
+ // if/while let ... = ... { ... }
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ let expr_span = expr.span;
+
+ // if/while let ... = ... { ... }
+ // ^^^
+ let op_span = result_expr.span.source_callsite();
+
+ // if/while let ... = ... { ... }
+ // ^^^^^^^^^^^^^^^^^^^
+ let span = expr_span.until(op_span.shrink_to_hi());
+
+ let app = if needs_drop {
+ Applicability::MaybeIncorrect
+ } else {
+ Applicability::MachineApplicable
+ };
+
+ let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
+ .maybe_par()
+ .to_string();
+
+ diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
+
+ if needs_drop {
+ diag.note("this will change drop order of the result, as well as all temporaries");
+ diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
+ }
+ },
+ );
+}
+
+fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
+ if arms.len() == 2 {
+ let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
+
+ let found_good_method = match node_pair {
+ (
+ PatKind::TupleStruct(ref path_left, patterns_left, _),
+ PatKind::TupleStruct(ref path_right, patterns_right, _),
+ ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
+ if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::RESULT_OK,
+ &paths::RESULT_ERR,
+ "is_ok()",
+ "is_err()",
+ )
+ .or_else(|| {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::IPADDR_V4,
+ &paths::IPADDR_V6,
+ "is_ipv4()",
+ "is_ipv6()",
+ )
+ })
+ } else {
+ None
+ }
+ },
+ (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
+ | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
+ if patterns.len() == 1 =>
+ {
+ if let PatKind::Wild = patterns[0].kind {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::OPTION_SOME,
+ &paths::OPTION_NONE,
+ "is_some()",
+ "is_none()",
+ )
+ .or_else(|| {
+ find_good_method_for_match(
+ cx,
+ arms,
+ path_left,
+ path_right,
+ &paths::POLL_READY,
+ &paths::POLL_PENDING,
+ "is_ready()",
+ "is_pending()",
+ )
+ })
+ } else {
+ None
+ }
+ },
+ _ => None,
+ };
+
+ if let Some(good_method) = found_good_method {
+ let span = expr.span.to(op.span);
+ let result_expr = match &op.kind {
+ ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ _ => op,
+ };
+ span_lint_and_then(
+ cx,
+ REDUNDANT_PATTERN_MATCHING,
+ expr.span,
+ &format!("redundant pattern matching, consider using `{}`", good_method),
+ |diag| {
+ diag.span_suggestion(
+ span,
+ "try this",
+ format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
+ Applicability::MaybeIncorrect, // snippet
+ );
+ },
+ );
+ }
+ }
+}
+
+#[allow(clippy::too_many_arguments)]
+fn find_good_method_for_match<'a>(
+ cx: &LateContext<'_>,
+ arms: &[Arm<'_>],
+ path_left: &QPath<'_>,
+ path_right: &QPath<'_>,
+ expected_left: &[&str],
+ expected_right: &[&str],
+ should_be_left: &'a str,
+ should_be_right: &'a str,
+) -> Option<&'a str> {
+ let left_id = cx
+ .typeck_results()
+ .qpath_res(path_left, arms[0].pat.hir_id)
+ .opt_def_id()?;
+ let right_id = cx
+ .typeck_results()
+ .qpath_res(path_right, arms[1].pat.hir_id)
+ .opt_def_id()?;
+ let body_node_pair = if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
+ (&(*arms[0].body).kind, &(*arms[1].body).kind)
+ } else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
+ (&(*arms[1].body).kind, &(*arms[0].body).kind)
+ } else {
+ return None;
+ };
+
+ match body_node_pair {
+ (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
+ (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
+ (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
+ _ => None,
+ },
+ _ => None,
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::{Pat, PatKind, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::REST_PAT_IN_FULLY_BOUND_STRUCTS;
+
+pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
+ if_chain! {
+ if !pat.span.from_expansion();
+ if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
+ if let Some(def_id) = path.res.opt_def_id();
+ let ty = cx.tcx.type_of(def_id);
+ if let ty::Adt(def, _) = ty.kind();
+ if def.is_struct() || def.is_union();
+ if fields.len() == def.non_enum_variant().fields.len();
+
+ then {
+ span_lint_and_help(
+ cx,
+ REST_PAT_IN_FULLY_BOUND_STRUCTS,
+ pat.span,
+ "unnecessary use of `..` pattern in struct binding. All fields were already bound",
+ None,
+ "consider removing `..` from this binding",
+ );
+ }
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{expr_block, snippet};
+use clippy_utils::ty::{implements_trait, match_type, peel_mid_ty_refs};
+use clippy_utils::{
+ is_lint_allowed, is_unit_expr, is_wild, paths, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs,
+};
+use core::cmp::max;
+use rustc_errors::Applicability;
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+
+use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
+
+#[rustfmt::skip]
+pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+ if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
+ if expr.span.from_expansion() {
+ // Don't lint match expressions present in
+ // macro_rules! block
+ return;
+ }
+ if let PatKind::Or(..) = arms[0].pat.kind {
+ // don't lint for or patterns for now, this makes
+ // the lint noisy in unnecessary situations
+ return;
+ }
+ let els = arms[1].body;
+ let els = if is_unit_expr(peel_blocks(els)) {
+ None
+ } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
+ if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
+ // single statement/expr "else" block, don't lint
+ return;
+ }
+ // block with 2+ statements or 1 expr and 1+ statement
+ Some(els)
+ } else {
+ // not a block, don't lint
+ return;
+ };
+
+ let ty = cx.typeck_results().expr_ty(ex);
+ if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
+ check_single_pattern(cx, ex, arms, expr, els);
+ check_opt_like(cx, ex, arms, expr, ty, els);
+ }
+ }
+}
+
+fn check_single_pattern(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ arms: &[Arm<'_>],
+ expr: &Expr<'_>,
+ els: Option<&Expr<'_>>,
+) {
+ if is_wild(arms[1].pat) {
+ report_single_pattern(cx, ex, arms, expr, els);
+ }
+}
+
+fn report_single_pattern(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ arms: &[Arm<'_>],
+ expr: &Expr<'_>,
+ els: Option<&Expr<'_>>,
+) {
+ let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
+ let els_str = els.map_or(String::new(), |els| {
+ format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
+ });
+
+ let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
+ let (msg, sugg) = if_chain! {
+ if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
+ let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
+ if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
+ if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
+ if ty.is_integral() || ty.is_char() || ty.is_str()
+ || (implements_trait(cx, ty, spe_trait_id, &[])
+ && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
+ then {
+ // scrutinee derives PartialEq and the pattern is a constant.
+ let pat_ref_count = match pat.kind {
+ // string literals are already a reference.
+ PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
+ _ => pat_ref_count,
+ };
+ // References are only implicitly added to the pattern, so no overflow here.
+ // e.g. will work: match &Some(_) { Some(_) => () }
+ // will not: match Some(_) { &Some(_) => () }
+ let ref_count_diff = ty_ref_count - pat_ref_count;
+
+ // Try to remove address of expressions first.
+ let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
+ let ref_count_diff = ref_count_diff - removed;
+
+ let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
+ let sugg = format!(
+ "if {} == {}{} {}{}",
+ snippet(cx, ex.span, ".."),
+ // PartialEq for different reference counts may not exist.
+ "&".repeat(ref_count_diff),
+ snippet(cx, arms[0].pat.span, ".."),
+ expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
+ els_str,
+ );
+ (msg, sugg)
+ } else {
+ let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
+ let sugg = format!(
+ "if let {} = {} {}{}",
+ snippet(cx, arms[0].pat.span, ".."),
+ snippet(cx, ex.span, ".."),
+ expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
+ els_str,
+ );
+ (msg, sugg)
+ }
+ };
+
+ span_lint_and_sugg(
+ cx,
+ lint,
+ expr.span,
+ msg,
+ "try this",
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+}
+
+fn check_opt_like<'a>(
+ cx: &LateContext<'a>,
+ ex: &Expr<'_>,
+ arms: &[Arm<'_>],
+ expr: &Expr<'_>,
+ ty: Ty<'a>,
+ els: Option<&Expr<'_>>,
+) {
+ // list of candidate `Enum`s we know will never get any more members
+ let candidates = &[
+ (&paths::COW, "Borrowed"),
+ (&paths::COW, "Cow::Borrowed"),
+ (&paths::COW, "Cow::Owned"),
+ (&paths::COW, "Owned"),
+ (&paths::OPTION, "None"),
+ (&paths::RESULT, "Err"),
+ (&paths::RESULT, "Ok"),
+ ];
+
+ // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive
+ // match with the second branch, without enum variants in matches.
+ if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(arms[0].pat, arms[1].pat) {
+ return;
+ }
+
+ let mut paths_and_types = Vec::new();
+ if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) {
+ return;
+ }
+
+ let in_candidate_enum = |path_info: &(String, Ty<'_>)| -> bool {
+ let (path, ty) = path_info;
+ for &(ty_path, pat_path) in candidates {
+ if path == pat_path && match_type(cx, *ty, ty_path) {
+ return true;
+ }
+ }
+ false
+ };
+ if paths_and_types.iter().all(in_candidate_enum) {
+ report_single_pattern(cx, ex, arms, expr, els);
+ }
+}
+
+/// Collects paths and their types from the given patterns. Returns true if the given pattern could
+/// be simplified, false otherwise.
+fn collect_pat_paths<'a>(acc: &mut Vec<(String, Ty<'a>)>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool {
+ match pat.kind {
+ PatKind::Wild => true,
+ PatKind::Tuple(inner, _) => inner.iter().all(|p| {
+ let p_ty = cx.typeck_results().pat_ty(p);
+ collect_pat_paths(acc, cx, p, p_ty)
+ }),
+ PatKind::TupleStruct(ref path, ..) => {
+ let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
+ s.print_qpath(path, false);
+ });
+ acc.push((path, ty));
+ true
+ },
+ PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => {
+ acc.push((ident.to_string(), ty));
+ true
+ },
+ PatKind::Path(ref path) => {
+ let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
+ s.print_qpath(path, false);
+ });
+ acc.push((path, ty));
+ true
+ },
+ _ => false,
+ }
+}
+
+/// Returns true if the given arm of pattern matching contains wildcard patterns.
+fn contains_only_wilds(pat: &Pat<'_>) -> bool {
+ match pat.kind {
+ PatKind::Wild => true,
+ PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds),
+ _ => false,
+ }
+}
+
+/// Returns true if the given patterns forms only exhaustive matches that don't contain enum
+/// patterns without a wildcard.
+fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool {
+ match (&left.kind, &right.kind) {
+ (PatKind::Wild, _) | (_, PatKind::Wild) => true,
+ (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
+ // We don't actually know the position and the presence of the `..` (dotdot) operator
+ // in the arms, so we need to evaluate the correct offsets here in order to iterate in
+ // both arms at the same time.
+ let len = max(
+ left_in.len() + {
+ if left_pos.is_some() { 1 } else { 0 }
+ },
+ right_in.len() + {
+ if right_pos.is_some() { 1 } else { 0 }
+ },
+ );
+ let mut left_pos = left_pos.unwrap_or(usize::MAX);
+ let mut right_pos = right_pos.unwrap_or(usize::MAX);
+ let mut left_dot_space = 0;
+ let mut right_dot_space = 0;
+ for i in 0..len {
+ let mut found_dotdot = false;
+ if i == left_pos {
+ left_dot_space += 1;
+ if left_dot_space < len - left_in.len() {
+ left_pos += 1;
+ }
+ found_dotdot = true;
+ }
+ if i == right_pos {
+ right_dot_space += 1;
+ if right_dot_space < len - right_in.len() {
+ right_pos += 1;
+ }
+ found_dotdot = true;
+ }
+ if found_dotdot {
+ continue;
+ }
+ if !contains_only_wilds(&left_in[i - left_dot_space])
+ && !contains_only_wilds(&right_in[i - right_dot_space])
+ {
+ return false;
+ }
+ }
+ true
+ },
+ _ => false,
+ }
+}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_wild;
+use rustc_hir::{Arm, PatKind};
+use rustc_lint::LateContext;
+
+use super::WILDCARD_IN_OR_PATTERNS;
+
+pub(crate) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
+ for arm in arms {
+ if let PatKind::Or(fields) = arm.pat.kind {
+ // look for multiple fields in this arm that contains at least one Wild pattern
+ if fields.len() > 1 && fields.iter().any(is_wild) {
+ span_lint_and_help(
+ cx,
+ WILDCARD_IN_OR_PATTERNS,
+ arm.pat.span,
+ "wildcard pattern covers any other pattern as it will match anyway",
+ None,
+ "consider handling `_` separately",
+ );
+ }
+ }
+ }
+}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{method_chain_args, single_segment_path};
+use clippy_utils::{method_chain_args, path_def_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_lint::Lint;
-use rustc_middle::ty;
-use rustc_span::sym;
+use rustc_middle::ty::{self, DefIdTree};
/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
pub(super) fn check(
) -> bool {
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
- if let hir::ExprKind::Call(fun, arg_char) = info.other.kind;
- if arg_char.len() == 1;
- if let hir::ExprKind::Path(ref qpath) = fun.kind;
- if let Some(segment) = single_segment_path(qpath);
- if segment.ident.name == sym::Some;
+ if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
+ if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+ if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
- snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
+ snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)),
applicability,
);
if info.eq { "" } else { "!" },
snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
suggest,
- c),
+ c.escape_default()),
applicability,
);
};
match inner_ty.kind() {
// &T where T: Copy
- ty::Ref(_, ty, _) if is_copy(cx, ty) => {},
+ ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
_ => return,
};
span_lint_and_sugg(
match cx.qpath_res(p, fun.hir_id) {
hir::def::Res::Def(hir::def::DefKind::Fn | hir::def::DefKind::AssocFn, def_id) => matches!(
cx.tcx.fn_sig(def_id).output().skip_binder().kind(),
- ty::Ref(ty::ReStatic, ..)
+ ty::Ref(re, ..) if re.is_static(),
),
_ => false,
}
.map_or(false, |method_id| {
matches!(
cx.tcx.fn_sig(method_id).output().skip_binder().kind(),
- ty::Ref(ty::ReStatic, ..)
+ ty::Ref(re, ..) if re.is_static()
)
})
},
};
match inner_ty.kind() {
- ty::Ref(_, ty, _) if !is_copy(cx, ty) => {},
+ ty::Ref(_, ty, _) if !is_copy(cx, *ty) => {},
_ => return,
};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_qpath_def_path;
use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, path_def_id};
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_errors::Applicability;
let ty_str = ty.to_string();
// `std::T::MAX` `std::T::MIN` constants
- if let hir::ExprKind::Path(path) = &expr.kind {
- if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) {
+ if let Some(id) = path_def_id(cx, expr) {
+ if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
return Some(MinMax::Max);
}
- if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) {
+ if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
return Some(MinMax::Min);
}
}
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty, TyS};
+use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
use std::borrow::Cow;
} else {
let ty = cx.typeck_results().expr_ty(e);
if is_type_diagnostic_item(cx, ty, sym::String)
- || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, TyS::is_str))
- || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, TyS::is_str))
+ || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
+ || (match_type(cx, ty, &paths::COW) && get_ty_param(ty).map_or(false, Ty::is_str))
{
Some(RepeatKind::String)
} else {
///
/// ### Why is this bad?
/// Readability, this can be written more concisely as
- /// `_.flat_map(_)`
+ /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
///
/// ### Example
/// ```rust
/// let vec = vec![vec![1]];
+ /// let opt = Some(5);
///
/// // Bad
/// vec.iter().map(|x| x.iter()).flatten();
+ /// opt.map(|x| Some(x * 2)).flatten();
///
/// // Good
/// vec.iter().flat_map(|x| x.iter());
+ /// opt.and_then(|x| Some(x * 2));
/// ```
#[clippy::version = "1.31.0"]
pub MAP_FLATTEN,
let method_sig = cx.tcx.fn_sig(impl_item.def_id);
let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
- let first_arg_ty = &method_sig.inputs().iter().next();
+ let first_arg_ty = method_sig.inputs().iter().next();
// check conventions w.r.t. conversion method names and predicates
if let Some(first_arg_ty) = first_arg_ty;
if name == method_config.method_name &&
sig.decl.inputs.len() == method_config.param_count &&
method_config.output_type.matches(&sig.decl.output) &&
- method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
+ method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
fn_header_equals(method_config.fn_header, sig.header) &&
method_config.lifetime_param_cond(impl_item)
{
cx,
name,
self_ty,
- first_arg_ty,
+ *first_arg_ty,
first_arg.pat.span,
implements_trait,
false
("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
implicit_clone::check(cx, name, expr, recv);
},
- ("unwrap", []) => match method_call(recv) {
- Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
- Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
- _ => unwrap_used::check(cx, expr, recv),
+ ("unwrap", []) => {
+ match method_call(recv) {
+ Some(("get", [recv, get_arg], _)) => {
+ get_unwrap::check(cx, expr, recv, get_arg, false);
+ },
+ Some(("get_mut", [recv, get_arg], _)) => {
+ get_unwrap::check(cx, expr, recv, get_arg, true);
+ },
+ _ => {},
+ }
+ unwrap_used::check(cx, expr, recv);
},
("unwrap_or", [u_arg]) => match method_call(recv) {
Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_lang_ctor, single_segment_path};
+use clippy_utils::{is_lang_ctor, path_def_id};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_lint::LateContext;
+use rustc_middle::ty::DefIdTree;
use rustc_span::symbol::sym;
use super::OPTION_MAP_OR_NONE;
if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
let arg_snippet = snippet(cx, span, "..");
let body = cx.tcx.hir().body(id);
- if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value);
- if arg_char.len() == 1;
- if let hir::ExprKind::Path(ref qpath) = func.kind;
- if let Some(segment) = single_segment_path(qpath);
- if segment.ident.name == sym::Some;
+ if let Some((func, [arg_char])) = reduce_unit_expression(cx, &body.value);
+ if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+ if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
- let func_snippet = snippet(cx, arg_char[0].span, "..");
+ let func_snippet = snippet(cx, arg_char.span, "..");
let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
`map(..)` instead";
return span_lint_and_sugg(
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::differing_macro_contexts;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy;
use clippy_utils::ty::is_type_diagnostic_item;
}
}
- if differing_macro_contexts(unwrap_arg.span, map_span) {
+ if unwrap_arg.span.ctxt() != map_span.ctxt() {
return;
}
IterUsageKind::Next | IterUsageKind::Second => {
let self_deref = {
let adjust = cx.typeck_results().expr_adjustments(self_arg);
- if adjust.is_empty() {
+ if adjust.len() < 2 {
String::new()
} else if cx.typeck_results().expr_ty(self_arg).is_box()
|| adjust
.iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
{
- format!("&{}", "*".repeat(adjust.len() - 1))
+ format!("&{}", "*".repeat(adjust.len().saturating_sub(1)))
} else {
- "*".repeat(adjust.len() - 2)
+ "*".repeat(adjust.len().saturating_sub(2))
}
};
if matches!(usage.kind, IterUsageKind::Next) {
} else if !found_mapping && !mutates_arg {
let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
match cx.typeck_results().expr_ty(&body.value).kind() {
- ty::Adt(adt, subst)
- if cx.tcx.is_diagnostic_item(sym::Option, adt.did) && in_ty == subst.type_at(0) =>
- {
+ ty::Adt(adt, subst) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) && in_ty == subst.type_at(0) => {
"filter"
},
_ => return,
if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id);
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
- let (target_ty, n_target_refs) = peel_mid_ty_refs(target_ty);
+ let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
span_lint_and_sugg(
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
if let Some(input) = fn_sig.inputs().get(i);
- let (input, n_refs) = peel_mid_ty_refs(input);
+ let (input, n_refs) = peel_mid_ty_refs(*input);
if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
if let [trait_predicate] = trait_predicates
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec),
ty::Array(_, size) => size.try_eval_usize(cx.tcx, cx.param_env).is_some(),
- ty::Ref(_, inner, _) => may_slice(cx, inner),
+ ty::Ref(_, inner, _) => may_slice(cx, *inner),
_ => false,
}
}
ty::Slice(_) => Some(expr),
ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
ty::Ref(_, inner, _) => {
- if may_slice(cx, inner) {
+ if may_slice(cx, *inner) {
Some(expr)
} else {
None
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_copy;
use rustc_lint::LateContext;
-use rustc_middle::ty::TyS;
+use rustc_middle::ty::Ty;
use rustc_span::source_map::Span;
use std::fmt;
fn check<'tcx>(
&self,
cx: &LateContext<'tcx>,
- self_ty: &'tcx TyS<'tcx>,
+ self_ty: Ty<'tcx>,
other: &str,
implements_trait: bool,
is_trait_item: bool,
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
item_name: &str,
- self_ty: &'tcx TyS<'tcx>,
- first_arg_ty: &'tcx TyS<'tcx>,
+ self_ty: Ty<'tcx>,
+ first_arg_ty: Ty<'tcx>,
first_arg_span: Span,
implements_trait: bool,
is_trait_item: bool,
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
if_chain! {
if let ty::RawPtr(ty::TypeAndMut { ty, .. }) = cx.typeck_results().expr_ty(recv).kind();
- if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
+ if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty));
if layout.is_zst();
then {
span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value");
use clippy_utils::consts::{constant, Constant};
use clippy_utils::sugg::Sugg;
use clippy_utils::{
- expr_path_res, get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats,
- last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
+ get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats,
+ last_path_segment, match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
};
declare_clippy_lint! {
)
},
ExprKind::Call(path, [arg]) => {
- if expr_path_res(cx, path)
- .opt_def_id()
+ if path_def_id(cx, path)
.and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
.is_some()
{
impl LateLintPass<'_> for ImportRename {
fn check_crate(&mut self, cx: &LateContext<'_>) {
for Rename { path, rename } in &self.conf_renames {
- if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::<Vec<_>>()) {
+ if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &path.split("::").collect::<Vec<_>>()) {
self.renames.insert(id, Symbol::intern(rename));
}
}
use if_chain::if_chain;
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use std::fmt::Display;
}
}
-fn might_have_negative_value(t: &ty::TyS<'_>) -> bool {
+fn might_have_negative_value(t: Ty<'_>) -> bool {
t.is_signed() || t.is_floating_point()
}
let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
let fn_sig = cx.tcx.fn_sig(fn_def_id);
for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
- check_ty(cx, hir_ty.span, ty);
+ check_ty(cx, hir_ty.span, *ty);
}
check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
}
if path.ident.name == sym!(lock);
let ty = cx.typeck_results().expr_ty(self_arg);
if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
- if is_type_diagnostic_item(cx, inner_ty, sym::Mutex);
+ if is_type_diagnostic_item(cx, *inner_ty, sym::Mutex);
then {
span_lint_and_sugg(
cx,
result: Result<ConstValue<'tcx>, ErrorHandled>,
ty: Ty<'tcx>,
) -> bool {
- fn inner<'tcx>(cx: &LateContext<'tcx>, val: &'tcx Const<'tcx>) -> bool {
- match val.ty.kind() {
+ fn inner<'tcx>(cx: &LateContext<'tcx>, val: Const<'tcx>) -> bool {
+ match val.ty().kind() {
// the fact that we have to dig into every structs to search enums
// leads us to the point checking `UnsafeCell` directly is the only option.
ty::Adt(ty_def, ..) if Some(ty_def.did) == cx.tcx.lang_items().unsafe_cell_type() => true,
ty::Array(..) | ty::Adt(..) | ty::Tuple(..) => {
let val = cx.tcx.destructure_const(cx.param_env.and(val));
- val.fields.iter().any(|field| inner(cx, field))
+ val.fields.iter().any(|field| inner(cx, *field))
},
_ => false,
}
ty::Tuple(_) => ty
.tuple_fields()
.all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
- ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
+ ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, *ty, send_trait),
ty::Adt(_, substs) => {
if contains_pointer_like(cx, ty) {
// descends only if ADT contains any raw pointers
if_chain! {
if !output_lts.contains(input_lt);
- if is_copy(cx, ty);
- if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
+ if is_copy(cx, *ty);
+ if let Some(size) = cx.layout_of(*ty).ok().map(|l| l.size.bytes());
if size <= self.ref_min_size;
if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind;
then {
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::expr_sig;
-use clippy_utils::{
- expr_path_res, get_expr_use_or_unification_node, is_lint_allowed, match_any_diagnostic_items, path_to_local, paths,
-};
+use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
sig.decl.inputs,
&[],
- ) {
+ )
+ .filter(|arg| arg.mutability() == Mutability::Not)
+ {
span_lint_and_sugg(
cx,
PTR_ARG,
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
let hir = cx.tcx.hir();
let mut parents = hir.parent_iter(body.value.hir_id);
- let (item_id, decl) = match parents.next() {
+ let (item_id, decl, is_trait_item) = match parents.next() {
Some((_, Node::Item(i))) => {
if let ItemKind::Fn(sig, ..) = &i.kind {
- (i.def_id, sig.decl)
+ (i.def_id, sig.decl, false)
} else {
return;
}
return;
}
if let ImplItemKind::Fn(sig, _) = &i.kind {
- (i.def_id, sig.decl)
+ (i.def_id, sig.decl, false)
} else {
return;
}
},
Some((_, Node::TraitItem(i))) => {
if let TraitItemKind::Fn(sig, _) = &i.kind {
- (i.def_id, sig.decl)
+ (i.def_id, sig.decl, true)
} else {
return;
}
check_mut_from_ref(cx, decl);
let sig = cx.tcx.fn_sig(item_id).skip_binder();
- let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params).collect();
+ let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
+ .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
+ .collect();
let results = check_ptr_arg_usage(cx, body, &lint_args);
for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
self.deref_ty.argless_str(),
)
}
+
+ fn mutability(&self) -> Mutability {
+ self.ref_prefix.mutability
+ }
}
struct RefPrefix {
},
_ => {
skip_count += 1;
- results[arg.idx].skip = true;
+ results[i].skip = true;
None
},
}
fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
if let ExprKind::Call(pathexp, []) = expr.kind {
- expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
- match_any_diagnostic_items(cx, id, &[sym::ptr_null, sym::ptr_null_mut]).is_some()
+ path_def_id(cx, pathexp).map_or(false, |id| {
+ matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
})
} else {
false
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
use rustc_span::sym;
-use rustc_span::symbol::Ident;
use std::cmp::Ordering;
declare_clippy_lint! {
_ => return,
};
// value, name, order (higher/lower), inclusiveness
- if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) =
+ if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) =
(check_range_bounds(cx, l), check_range_bounds(cx, r))
{
// we only lint comparisons on the same name and with different
// direction
- if lname != rname || lord == rord {
+ if lid != rid || lord == rord {
return;
}
let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
}
}
-fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> {
+fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> {
if let ExprKind::Binary(ref op, l, r) = ex.kind {
let (inclusive, ordering) = match op.node {
BinOpKind::Gt => (false, Ordering::Greater),
BinOpKind::Le => (true, Ordering::Less),
_ => return None,
};
- if let Some(id) = match_ident(l) {
+ if let Some(id) = path_to_local(l) {
if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
return Some((c, id, l.span, r.span, ordering, inclusive));
}
- } else if let Some(id) = match_ident(r) {
+ } else if let Some(id) = path_to_local(r) {
if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
}
None
}
-fn match_ident(e: &Expr<'_>) -> Option<Ident> {
- if let ExprKind::Path(ref qpath) = e.kind {
- if let Some(seg) = single_segment_path(qpath) {
- if seg.args.is_none() {
- return Some(seg.ident);
- }
- }
- }
- None
-}
-
fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
if_chain! {
if path.ident.as_str() == "zip";
if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) =
cx.typeck_results().expr_ty(ptr_self).kind();
then {
- return Some((pointee_ty, count));
+ return Some((*pointee_ty, count));
}
};
None
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value, std_or_core};
+use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
if_chain! {
if let StmtKind::Semi(first) = w[0].kind;
if let StmtKind::Semi(second) = w[1].kind;
- if !differing_macro_contexts(first.span, second.span);
+ if first.span.ctxt() == second.span.ctxt();
if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
if eq_expr_value(cx, lhs0, rhs1);
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
if !bound_predicate.span.from_expansion();
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
- if let Some(PathSegment { res: Some(Res::SelfTy(Some(def_id), _)), .. }) = segments.first();
+ if let Some(PathSegment { res: Some(Res::SelfTy{ trait_: Some(def_id), alias_to: _ }), .. }) = segments.first();
if let Some(
Node::Item(
mod transmute_ptr_to_ptr;
mod transmute_ptr_to_ref;
mod transmute_ref_to_ref;
+mod transmute_undefined_repr;
mod transmutes_expressible_as_ptr_casts;
mod unsound_collection_transmute;
mod useless_transmute;
"transmute between collections of layout-incompatible types"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for transmutes either to or from a type which does not have a defined representation.
+ ///
+ /// ### Why is this bad?
+ /// The results of such a transmute are not defined.
+ ///
+ /// ### Example
+ /// ```rust
+ /// struct Foo<T>(u32, T);
+ /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// #[repr(C)]
+ /// struct Foo<T>(u32, T);
+ /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
+ /// ```
+ #[clippy::version = "1.60.0"]
+ pub TRANSMUTE_UNDEFINED_REPR,
+ nursery,
+ "transmute to or from a type with an undefined representation"
+}
+
declare_lint_pass!(Transmute => [
CROSSPOINTER_TRANSMUTE,
TRANSMUTE_PTR_TO_REF,
TRANSMUTE_NUM_TO_BYTES,
UNSOUND_COLLECTION_TRANSMUTE,
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
+ TRANSMUTE_UNDEFINED_REPR,
]);
impl<'tcx> LateLintPass<'tcx> for Transmute {
- #[allow(clippy::similar_names, clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if_chain! {
- if let ExprKind::Call(path_expr, args) = e.kind;
+ if let ExprKind::Call(path_expr, [arg]) = e.kind;
if let ExprKind::Path(ref qpath) = path_expr.kind;
if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
// And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
let const_context = in_constant(cx, e.hir_id);
- let from_ty = cx.typeck_results().expr_ty(&args[0]);
+ let from_ty = cx.typeck_results().expr_ty(arg);
let to_ty = cx.typeck_results().expr_ty(e);
// If useless_transmute is triggered, the other lints can be skipped.
- if useless_transmute::check(cx, e, from_ty, to_ty, args) {
+ if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
return;
}
- let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty);
- linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty);
- linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
- linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
- linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
- linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
- linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
- linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
- linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
- linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context);
- linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty);
+ let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
+ | crosspointer_transmute::check(cx, e, from_ty, to_ty)
+ | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
+ | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg)
+ | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
+ | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
+ | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
+ | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
+ | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
+ | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
+ | (
+ unsound_collection_transmute::check(cx, e, from_ty, to_ty)
+ || transmute_undefined_repr::check(cx, e, from_ty, to_ty)
+ );
if !linted {
- transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
+ transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
}
}
}
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ mut arg: &'tcx Expr<'_>,
const_context: bool,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
e.span,
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|diag| {
- let mut expr = &args[0];
- let mut arg = sugg::Sugg::hir(cx, expr, "..");
+ let mut sugg = sugg::Sugg::hir(cx, arg, "..");
- if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
- expr = inner_expr;
+ if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind {
+ arg = inner_expr;
}
if_chain! {
// if the expression is a float literal and it is unsuffixed then
// add a suffix so the suggestion is valid and unambiguous
- if let ExprKind::Lit(lit) = &expr.kind;
+ if let ExprKind::Lit(lit) = &arg.kind;
if let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node;
then {
- let op = format!("{}{}", arg, float_ty.name_str()).into();
- match arg {
- sugg::Sugg::MaybeParen(_) => arg = sugg::Sugg::MaybeParen(op),
- _ => arg = sugg::Sugg::NonParen(op)
+ let op = format!("{}{}", sugg, float_ty.name_str()).into();
+ match sugg {
+ sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op),
+ _ => sugg = sugg::Sugg::NonParen(op)
}
}
}
- arg = sugg::Sugg::NonParen(format!("{}.to_bits()", arg.maybe_par()).into());
+ sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_par()).into());
// cast the result of `to_bits` if `to_ty` is signed
- arg = if let ty::Int(int_ty) = to_ty.kind() {
- arg.as_ty(int_ty.name_str().to_string())
+ sugg = if let ty::Int(int_ty) = to_ty.kind() {
+ sugg.as_ty(int_ty.name_str().to_string())
} else {
- arg
+ sugg
};
- diag.span_suggestion(e.span, "consider using", arg.to_string(), Applicability::Unspecified);
+ diag.span_suggestion(e.span, "consider using", sugg.to_string(), Applicability::Unspecified);
},
);
true
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
(ty::Int(ty::IntTy::I8) | ty::Uint(ty::UintTy::U8), ty::Bool) => {
e.span,
&format!("transmute from a `{}` to a `bool`", from_ty),
|diag| {
- let arg = sugg::Sugg::hir(cx, &args[0], "..");
+ let arg = sugg::Sugg::hir(cx, arg, "..");
let zero = sugg::Sugg::NonParen(Cow::from("0"));
diag.span_suggestion(
e.span,
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
(ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => {
e.span,
&format!("transmute from a `{}` to a `char`", from_ty),
|diag| {
- let arg = sugg::Sugg::hir(cx, &args[0], "..");
+ let arg = sugg::Sugg::hir(cx, arg, "..");
let arg = if let ty::Int(_) = from_ty.kind() {
arg.as_ty(ast::UintTy::U32.name_str())
} else {
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
const_context: bool,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
e.span,
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|diag| {
- let arg = sugg::Sugg::hir(cx, &args[0], "..");
+ let arg = sugg::Sugg::hir(cx, arg, "..");
let arg = if let ty::Int(int_ty) = from_ty.kind() {
arg.as_ty(format!(
"u{}",
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
const_context: bool,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
e.span,
&format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
|diag| {
- let arg = sugg::Sugg::hir(cx, &args[0], "..");
+ let arg = sugg::Sugg::hir(cx, arg, "..");
diag.span_suggestion(
e.span,
"consider using `to_ne_bytes()`",
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
(ty::RawPtr(_), ty::RawPtr(to_ty)) => {
e.span,
"transmute from a pointer to a pointer",
|diag| {
- if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
+ if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let sugg = arg.as_ty(cx.tcx.mk_ptr(*to_ty));
diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
}
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
qpath: &'tcx QPath<'_>,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
from_ty, to_ty
),
|diag| {
- let arg = sugg::Sugg::hir(cx, &args[0], "..");
+ let arg = sugg::Sugg::hir(cx, arg, "..");
let (deref, cast) = if *mutbl == Mutability::Mut {
("&mut *", "*mut")
} else {
let arg = if from_ptr_ty.ty == *to_ref_ty {
arg
} else {
- arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, to_ref_ty)))
+ arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, *to_ref_ty)))
};
diag.span_suggestion(
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
const_context: bool,
) -> bool {
let mut triggered = false;
format!(
"std::str::from_utf8{}({}).unwrap()",
postfix,
- snippet(cx, args[0].span, ".."),
+ snippet(cx, arg.span, ".."),
),
Applicability::Unspecified,
);
TRANSMUTE_PTR_TO_PTR,
e.span,
"transmute from a reference to a reference",
- |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
+ |diag| if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let ty_from_and_mut = ty::TypeAndMut {
- ty: ty_from,
+ ty: *ty_from,
mutbl: *from_mutbl
};
- let ty_to_and_mut = ty::TypeAndMut { ty: ty_to, mutbl: *to_mutbl };
+ let ty_to_and_mut = ty::TypeAndMut { ty: *ty_to, mutbl: *to_mutbl };
let sugg_paren = arg
.as_ty(cx.tcx.mk_ptr(ty_from_and_mut))
.as_ty(cx.tcx.mk_ptr(ty_to_and_mut));
--- /dev/null
+use super::TRANSMUTE_UNDEFINED_REPR;
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_middle::ty::subst::{GenericArg, Subst};
+use rustc_middle::ty::{self, Ty, TypeAndMut};
+use rustc_span::Span;
+
+#[allow(clippy::too_many_lines)]
+pub(super) fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx Expr<'_>,
+ from_ty_orig: Ty<'tcx>,
+ to_ty_orig: Ty<'tcx>,
+) -> bool {
+ let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
+ let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
+
+ while from_ty != to_ty {
+ match reduce_refs(cx, e.span, from_ty, to_ty) {
+ ReducedTys::FromFatPtr { unsized_ty, .. } => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+ |diag| {
+ if from_ty_orig.peel_refs() != unsized_ty {
+ diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
+ }
+ },
+ );
+ return true;
+ },
+ ReducedTys::ToFatPtr { unsized_ty, .. } => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
+ |diag| {
+ if to_ty_orig.peel_refs() != unsized_ty {
+ diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
+ }
+ },
+ );
+ return true;
+ },
+ ReducedTys::ToPtr {
+ from_ty: from_sub_ty,
+ to_ty: to_sub_ty,
+ } => match reduce_ty(cx, from_sub_ty) {
+ ReducedTy::UnorderedFields(from_ty) => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+ |diag| {
+ if from_ty_orig.peel_refs() != from_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+ }
+ },
+ );
+ return true;
+ },
+ ReducedTy::Ref(from_sub_ty) => {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
+ },
+ _ => break,
+ },
+ ReducedTys::FromPtr {
+ from_ty: from_sub_ty,
+ to_ty: to_sub_ty,
+ } => match reduce_ty(cx, to_sub_ty) {
+ ReducedTy::UnorderedFields(to_ty) => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
+ |diag| {
+ if to_ty_orig.peel_refs() != to_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+ }
+ },
+ );
+ return true;
+ },
+ ReducedTy::Ref(to_sub_ty) => {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
+ },
+ _ => break,
+ },
+ ReducedTys::Other {
+ from_ty: from_sub_ty,
+ to_ty: to_sub_ty,
+ } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
+ (ReducedTy::IntArray, _) | (_, ReducedTy::IntArray) => return false,
+ (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!(
+ "transmute from `{}` to `{}`, both of which have an undefined layout",
+ from_ty_orig, to_ty_orig
+ ),
+ |diag| {
+ if_chain! {
+ if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
+ if from_def == to_def;
+ then {
+ diag.note(&format!(
+ "two instances of the same generic type (`{}`) may have different layouts",
+ cx.tcx.item_name(from_def.did)
+ ));
+ } else {
+ if from_ty_orig.peel_refs() != from_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+ }
+ if to_ty_orig.peel_refs() != to_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+ }
+ }
+ }
+ },
+ );
+ return true;
+ },
+ (
+ ReducedTy::UnorderedFields(from_ty),
+ ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
+ ) => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
+ |diag| {
+ if from_ty_orig.peel_refs() != from_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
+ }
+ },
+ );
+ return true;
+ },
+ (
+ ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
+ ReducedTy::UnorderedFields(to_ty),
+ ) => {
+ span_lint_and_then(
+ cx,
+ TRANSMUTE_UNDEFINED_REPR,
+ e.span,
+ &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
+ |diag| {
+ if to_ty_orig.peel_refs() != to_ty {
+ diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
+ }
+ },
+ );
+ return true;
+ },
+ (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
+ },
+ (
+ ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
+ ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
+ )
+ | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
+ },
+ }
+ }
+
+ false
+}
+
+enum ReducedTys<'tcx> {
+ FromFatPtr { unsized_ty: Ty<'tcx> },
+ ToFatPtr { unsized_ty: Ty<'tcx> },
+ ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
+ FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
+ Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
+}
+
+fn reduce_refs<'tcx>(
+ cx: &LateContext<'tcx>,
+ span: Span,
+ mut from_ty: Ty<'tcx>,
+ mut to_ty: Ty<'tcx>,
+) -> ReducedTys<'tcx> {
+ loop {
+ return match (from_ty.kind(), to_ty.kind()) {
+ (
+ &ty::Ref(_, from_sub_ty, _) | &ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. }),
+ &ty::Ref(_, to_sub_ty, _) | &ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. }),
+ ) => {
+ from_ty = from_sub_ty;
+ to_ty = to_sub_ty;
+ continue;
+ },
+ (&ty::Ref(_, unsized_ty, _) | &ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }), _)
+ if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+ {
+ ReducedTys::FromFatPtr { unsized_ty }
+ },
+ (_, &ty::Ref(_, unsized_ty, _) | &ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))
+ if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
+ {
+ ReducedTys::ToFatPtr { unsized_ty }
+ },
+ (&ty::Ref(_, from_ty, _) | &ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => {
+ ReducedTys::FromPtr { from_ty, to_ty }
+ },
+ (_, &ty::Ref(_, to_ty, _) | &ty::RawPtr(TypeAndMut { ty: to_ty, .. })) => {
+ ReducedTys::ToPtr { from_ty, to_ty }
+ },
+ _ => ReducedTys::Other { from_ty, to_ty },
+ };
+ }
+}
+
+enum ReducedTy<'tcx> {
+ OrderedFields(Ty<'tcx>),
+ UnorderedFields(Ty<'tcx>),
+ Ref(Ty<'tcx>),
+ Other(Ty<'tcx>),
+ IntArray,
+}
+
+fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
+ loop {
+ ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
+ return match *ty.kind() {
+ ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
+ ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
+ ty = sub_ty;
+ continue;
+ },
+ ty::Tuple(args) => {
+ let mut iter = args.iter().map(GenericArg::expect_ty);
+ let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, *ty)) else {
+ return ReducedTy::OrderedFields(ty);
+ };
+ if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
+ ty = sized_ty;
+ continue;
+ }
+ ReducedTy::UnorderedFields(ty)
+ },
+ ty::Adt(def, substs) if def.is_struct() => {
+ if def.repr.inhibit_struct_field_reordering_opt() {
+ return ReducedTy::OrderedFields(ty);
+ }
+ let mut iter = def
+ .non_enum_variant()
+ .fields
+ .iter()
+ .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
+ let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, *ty)) else {
+ return ReducedTy::OrderedFields(ty);
+ };
+ if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
+ ty = sized_ty;
+ continue;
+ }
+ ReducedTy::UnorderedFields(ty)
+ },
+ ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty),
+ _ => ReducedTy::Other(ty),
+ };
+ }
+}
+
+fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+ if_chain! {
+ if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty);
+ if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
+ then {
+ layout.layout.size.bytes() == 0
+ } else {
+ false
+ }
+ }
+}
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
) -> bool {
if can_be_expressed_as_pointer_cast(cx, e, from_ty, to_ty) {
span_lint_and_then(
from_ty, to_ty
),
|diag| {
- if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
+ if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let sugg = arg.as_ty(&to_ty.to_string()).to_string();
diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
}
use super::utils::is_layout_incompatible;
use super::UNSOUND_COLLECTION_TRANSMUTE;
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::match_any_diagnostic_items;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{sym, Symbol};
-
-// used to check for UNSOUND_COLLECTION_TRANSMUTE
-static COLLECTIONS: &[Symbol] = &[
- sym::Vec,
- sym::VecDeque,
- sym::BinaryHeap,
- sym::BTreeSet,
- sym::BTreeMap,
- sym::HashSet,
- sym::HashMap,
-];
+use rustc_span::symbol::sym;
/// Checks for `unsound_collection_transmute` lint.
/// Returns `true` if it's triggered, otherwise returns `false`.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
(ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => {
- if from_adt.did != to_adt.did || match_any_diagnostic_items(cx, to_adt.did, COLLECTIONS).is_none() {
+ if from_adt.did != to_adt.did {
+ return false;
+ }
+ if !matches!(
+ cx.tcx.get_diagnostic_name(to_adt.did),
+ Some(
+ sym::BTreeMap
+ | sym::BTreeSet
+ | sym::BinaryHeap
+ | sym::HashMap
+ | sym::HashSet
+ | sym::Vec
+ | sym::VecDeque
+ )
+ ) {
return false;
}
if from_substs
e: &'tcx Expr<'_>,
from_ty: Ty<'tcx>,
to_ty: Ty<'tcx>,
- args: &'tcx [Expr<'_>],
+ arg: &'tcx Expr<'_>,
) -> bool {
match (&from_ty.kind(), &to_ty.kind()) {
_ if from_ty == to_ty => {
e.span,
"transmute from a reference to a pointer",
|diag| {
- if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
+ if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
let rty_and_mut = ty::TypeAndMut {
- ty: rty,
+ ty: *rty,
mutbl: *rty_mutbl,
};
e.span,
"transmute from an integer to a pointer",
|diag| {
- if let Some(arg) = sugg::Sugg::hir_opt(cx, &args[0]) {
+ if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
diag.span_suggestion(
e.span,
"try",
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_ty_param_diagnostic_item;
+use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
+use rustc_span::{sym, Symbol};
use super::BOX_COLLECTION;
if Some(def_id) == cx.tcx.lang_items().owned_box();
if let Some(item_type) = get_std_collection(cx, qpath);
then {
- let generic = if item_type == "String" {
- ""
- } else {
- "<..>"
+ let generic = match item_type {
+ sym::String => "",
+ _ => "<..>",
};
span_lint_and_help(
cx,
}
}
-fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
- if is_ty_param_diagnostic_item(cx, qpath, sym::Vec).is_some() {
- Some("Vec")
- } else if is_ty_param_diagnostic_item(cx, qpath, sym::String).is_some() {
- Some("String")
- } else if is_ty_param_diagnostic_item(cx, qpath, sym::HashMap).is_some() {
- Some("HashMap")
- } else {
- None
- }
+fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
+ let param = qpath_generic_tys(qpath).next()?;
+ let id = path_def_id(cx, param)?;
+ cx.tcx
+ .get_diagnostic_name(id)
+ .filter(|&name| matches!(name, sym::HashMap | sym::String | sym::Vec))
}
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_ty_param_diagnostic_item;
+use clippy_utils::{path_def_id, qpath_generic_tys};
+use if_chain::if_chain;
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::OPTION_OPTION;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
- if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some() {
- span_lint(
- cx,
- OPTION_OPTION,
- hir_ty.span,
- "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
- enum if you need to distinguish all 3 cases",
- );
- true
- } else {
- false
+ if_chain! {
+ if cx.tcx.is_diagnostic_item(sym::Option, def_id);
+ if let Some(arg) = qpath_generic_tys(qpath).next();
+ if path_def_id(cx, arg) == Some(def_id);
+ then {
+ span_lint(
+ cx,
+ OPTION_OPTION,
+ hir_ty.span,
+ "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
+ enum if you need to distinguish all 3 cases",
+ );
+ true
+ } else {
+ false
+ }
}
}
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item};
+use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
use rustc_lint::LateContext;
format!("Rc<{}>", alternate),
Applicability::MachineApplicable,
);
- } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Vec) {
+ } else {
+ let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
+ let Some(id) = path_def_id(cx, ty) else { return false };
+ if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
+ return false;
+ }
let qpath = match &ty.kind {
TyKind::Path(qpath) => qpath,
_ => return false,
};
- let inner_span = match get_qpath_generic_tys(qpath).next() {
+ let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span,
None => return false,
};
format!("Arc<{}>", alternate),
Applicability::MachineApplicable,
);
- } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Vec) {
+ } else if let Some(ty) = qpath_generic_tys(qpath).next() {
+ let Some(id) = path_def_id(cx, ty) else { return false };
+ if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
+ return false;
+ }
let qpath = match &ty.kind {
TyKind::Path(qpath) => qpath,
_ => return false,
};
- let inner_span = match get_qpath_generic_tys(qpath).next() {
+ let inner_span = match qpath_generic_tys(qpath).next() {
Some(ty) => ty.span,
None => return false,
};
}
fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
- if is_ty_param_diagnostic_item(cx, qpath, sym::String).is_some() {
- Some("str")
- } else if is_ty_param_diagnostic_item(cx, qpath, sym::OsString).is_some() {
- Some("std::ffi::OsStr")
- } else if is_ty_param_diagnostic_item(cx, qpath, sym::PathBuf).is_some() {
- Some("std::path::Path")
- } else {
- None
- }
+ let ty = qpath_generic_tys(qpath).next()?;
+ let id = path_def_id(cx, ty)?;
+ let path = match cx.tcx.get_diagnostic_name(id)? {
+ sym::String => "str",
+ sym::OsString => "std::ffi::OsStr",
+ sym::PathBuf => "std::path::Path",
+ _ => return None,
+ };
+ Some(path)
}
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_ty_param_diagnostic_item;
+use clippy_utils::{path_def_id, qpath_generic_tys};
use if_chain::if_chain;
use rustc_hir::{self as hir, def_id::DefId, QPath};
use rustc_lint::LateContext;
pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
if_chain! {
if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
- if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym::Mutex) ;
+ if let Some(arg) = qpath_generic_tys(qpath).next();
+ if let Some(id) = path_def_id(cx, arg);
+ if cx.tcx.is_diagnostic_item(sym::Mutex, id);
then {
span_lint_and_help(
cx,
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
+use clippy_utils::{path_def_id, qpath_generic_tys};
use rustc_errors::Applicability;
-use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
+use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
return true;
}
- let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
- ("Box", ty)
- } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
- ("Rc", ty)
- } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
- ("Arc", ty)
- } else {
- return false;
+ let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
+ let Some(id) = path_def_id(cx, ty) else { return false };
+ let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
+ Some(sym::Arc) => ("Arc", ty),
+ Some(sym::Rc) => ("Rc", ty),
+ _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
+ _ => return false,
};
let inner_qpath = match &ty.kind {
TyKind::Path(inner_qpath) => inner_qpath,
_ => return false,
};
- let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
+ let inner_span = match qpath_generic_tys(inner_qpath).next() {
Some(ty) => {
// Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers
if matches!(ty.kind, TyKind::TraitObject(..)) {
let partial_ord_preds =
get_trait_predicates_for_trait_id(cx, generics, cx.tcx.lang_items().partial_ord_trait());
// Trying to call erase_late_bound_regions on fn_sig.inputs() gives the following error
- // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for `&[&rustc::ty::TyS<'_>]`
+ // The trait `rustc::ty::TypeFoldable<'_>` is not implemented for
+ // `&[rustc_middle::ty::Ty<'_>]`
let inputs_output = cx.tcx.erase_late_bound_regions(fn_sig.inputs_and_output());
inputs_output
.iter()
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated};
+use clippy_utils::{path_to_local, usage::is_potentially_mutated};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
if let Some(unwrappable) = self.unwrappables.iter()
.find(|u| u.local_id == id);
// Span contexts should not differ with the conditional branch
- if !differing_macro_contexts(unwrappable.branch.span, expr.span);
- if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span);
+ let span_ctxt = expr.span.ctxt();
+ if unwrappable.branch.span.ctxt() == span_ctxt;
+ if unwrappable.check.span.ctxt() == span_ctxt;
then {
if call_to_unwrap == unwrappable.safe_to_unwrap {
let is_entire_condition = unwrappable.is_entire_condition;
ref types_to_skip,
}) = self.stack.last();
if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
- if !matches!(path.res, Res::SelfTy(..) | Res::Def(DefKind::TyParam, _));
+ if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _));
if !types_to_skip.contains(&hir_ty.hir_id);
let ty = if in_body > 0 {
cx.typeck_results().node_type(hir_ty.hir_id)
}
match expr.kind {
ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
- Res::SelfTy(..) => (),
+ Res::SelfTy { .. } => (),
Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
_ => span_lint(cx, path.span),
},
let mut current = env::var_os("CLIPPY_CONF_DIR")
.or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
.map_or_else(|| PathBuf::from("."), PathBuf::from);
+
+ let mut found_config: Option<PathBuf> = None;
+
loop {
for config_file_name in &CONFIG_FILE_NAMES {
if let Ok(config_file) = current.join(config_file_name).canonicalize() {
Err(e) if e.kind() == io::ErrorKind::NotFound => {},
Err(e) => return Err(e),
Ok(md) if md.is_dir() => {},
- Ok(_) => return Ok(Some(config_file)),
+ Ok(_) => {
+ // warn if we happen to find two config files #8323
+ if let Some(ref found_config_) = found_config {
+ eprintln!(
+ "Using config file `{}`\nWarning: `{}` will be ignored.",
+ found_config_.display(),
+ config_file.display(),
+ );
+ } else {
+ found_config = Some(config_file);
+ }
+ },
}
}
}
+ if found_config.is_some() {
+ return Ok(found_config);
+ }
+
// If the current directory has no parent, we're done searching.
if !current.pop() {
return Ok(None);
),
hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
}
- if item.defaultness.is_default() {
- println!("default");
- }
match item.kind {
hir::ImplItemKind::Const(_, body_id) => {
println!("associated constant");
use clippy_utils::source::snippet;
use clippy_utils::ty::match_type;
use clippy_utils::{
- higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls,
- path_to_res, paths, peel_blocks_with_stmt, SpanlessEq,
+ def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
+ method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast as ast;
// Extract the path to the matched type
if let Some(segments) = path_to_matched_type(cx, ty_path);
let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
- if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
+ if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
// Check if the matched type is a diagnostic item
if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
then {
// This is not a complete resolver for paths. It works on all the paths currently used in the paths
// module. That's all it does and all it needs to do.
pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
- if path_to_res(cx, path) != Res::Err {
+ if def_path_res(cx, path) != Res::Err {
return true;
}
}
for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
- if let Some(def_id) = path_to_res(cx, module).opt_def_id() {
+ if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
for item in cx.tcx.module_children(def_id).iter() {
if_chain! {
if let Res::Def(DefKind::Const, item_def_id) = item.res;
}
fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
- for (group_name, lints, _) in &cx.lint_store.get_lint_groups() {
- if IGNORED_LINT_GROUPS.contains(group_name) {
+ for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
+ if IGNORED_LINT_GROUPS.contains(&group_name) {
continue;
}
}
}
},
- ArgumentNamed(n) => {
+ ArgumentNamed(n, _) => {
if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
match x.1.as_slice() {
// A non-empty format string has been seen already.
let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect();
if let ["core", "num", int_impl, "max_value"] = *def_path;
then {
- let value = match int_impl {
- "<impl i8>" => i8::MAX as u128,
- "<impl i16>" => i16::MAX as u128,
- "<impl i32>" => i32::MAX as u128,
- "<impl i64>" => i64::MAX as u128,
- "<impl i128>" => i128::MAX as u128,
- _ => return None,
- };
- Some(Constant::Int(value))
- }
- else {
+ let value = match int_impl {
+ "<impl i8>" => i8::MAX as u128,
+ "<impl i16>" => i16::MAX as u128,
+ "<impl i32>" => i32::MAX as u128,
+ "<impl i64>" => i64::MAX as u128,
+ "<impl i128>" => i128::MAX as u128,
+ _ => return None,
+ };
+ Some(Constant::Int(value))
+ } else {
None
}
}
}
}
-pub fn miri_to_const(result: &ty::Const<'_>) -> Option<Constant> {
+pub fn miri_to_const(result: ty::Const<'_>) -> Option<Constant> {
use rustc_middle::mir::interpret::ConstValue;
- match result.val {
+ match result.val() {
ty::ConstKind::Value(ConstValue::Scalar(Scalar::Int(int))) => {
- match result.ty.kind() {
+ match result.ty().kind() {
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
_ => None,
}
},
- ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty.kind() {
+ ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => match result.ty().kind() {
ty::Ref(_, tam, _) => match tam.kind() {
ty::Str => String::from_utf8(
data.inspect_with_uninit_and_ptr_outside_interpreter(start..end)
},
_ => None,
},
- ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty.kind() {
+ ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty().kind() {
ty::Array(sub_type, len) => match sub_type.kind() {
- ty::Float(FloatTy::F32) => match miri_to_const(len) {
+ ty::Float(FloatTy::F32) => match miri_to_const(*len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
.to_owned()
.map(Constant::Vec),
_ => None,
},
- ty::Float(FloatTy::F64) => match miri_to_const(len) {
+ ty::Float(FloatTy::F64) => match miri_to_const(*len) {
Some(Constant::Int(len)) => alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
.to_owned()
return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
// `vec![elem; size]` case
Some(VecArgs::Repeat(&args[0], &args[1]))
- }
- else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
+ } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
// `vec![a, b, c]` case
if_chain! {
if let hir::ExprKind::Box(boxed) = args[0].kind;
}
None
- }
- else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
+ } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
Some(VecArgs::Vec(&[]))
- }
- else {
+ } else {
None
};
}
if let ExprKind::Lit(lit) = &arg.kind;
if let LitKind::Int(num, _) = lit.node;
then {
- return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?))
+ return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?));
}
}
return Some(VecInitKind::WithExprCapacity(arg.hir_id));
use crate::consts::{constant_context, constant_simple};
-use crate::differing_macro_contexts;
use crate::source::snippet_opt;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHasher;
#[allow(clippy::similar_names)]
pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
- if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) {
+ if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
return false;
}
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
- ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem,
- Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
- Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
+ ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
+ MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Target,
+ TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
};
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase;
};
}
-/// Returns `true` if the two spans come from differing expansions (i.e., one is
-/// from a macro and one isn't).
-#[must_use]
-pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
- rhs.ctxt() != lhs.ctxt()
-}
-
/// If the given expression is a local binding, find the initializer expression.
/// If that initializer expression is another local binding, find its initializer again.
/// This process repeats as long as possible (but usually no more than once). Initializer
matches!(pat.kind, PatKind::Wild)
}
-/// Checks if the first type parameter is a lang item.
-pub fn is_ty_param_lang_item<'tcx>(
- cx: &LateContext<'_>,
- qpath: &QPath<'tcx>,
- item: LangItem,
-) -> Option<&'tcx hir::Ty<'tcx>> {
- let ty = get_qpath_generic_tys(qpath).next()?;
-
- if let TyKind::Path(qpath) = &ty.kind {
- cx.qpath_res(qpath, ty.hir_id)
- .opt_def_id()
- .map_or(false, |id| {
- cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
- })
- .then(|| ty)
- } else {
- None
- }
-}
-
-/// Checks if the first type parameter is a diagnostic item.
-pub fn is_ty_param_diagnostic_item<'tcx>(
- cx: &LateContext<'_>,
- qpath: &QPath<'tcx>,
- item: Symbol,
-) -> Option<&'tcx hir::Ty<'tcx>> {
- let ty = get_qpath_generic_tys(qpath).next()?;
-
- if let TyKind::Path(qpath) = &ty.kind {
- cx.qpath_res(qpath, ty.hir_id)
- .opt_def_id()
- .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
- .then(|| ty)
- } else {
- None
- }
-}
-
/// Checks if the method call given in `expr` belongs to the given trait.
/// This is a deprecated function, consider using [`is_trait_method`].
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
}
}
-pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
- match path {
- QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
- QPath::TypeRelative(_, s) => s.args,
- QPath::LangItem(..) => None,
- }
-}
-
-pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
- get_qpath_generics(path)
- .map_or([].as_ref(), |a| a.args)
+pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+ last_path_segment(qpath)
+ .args
+ .map_or(&[][..], |a| a.args)
.iter()
- .filter_map(|a| {
- if let hir::GenericArg::Type(ty) = a {
- Some(ty)
- } else {
- None
- }
+ .filter_map(|a| match a {
+ hir::GenericArg::Type(ty) => Some(ty),
+ _ => None,
})
}
-pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
- match *path {
- QPath::Resolved(_, path) => path.segments.get(0),
- QPath::TypeRelative(_, seg) => Some(seg),
- QPath::LangItem(..) => None,
- }
-}
-
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
/// `QPath::Resolved.1.res.opt_def_id()`.
}
}
-/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
-pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
- if let ExprKind::Path(p) = &expr.kind {
- cx.qpath_res(p, expr.hir_id)
- } else {
- Res::Err
- }
-}
-
-/// Resolves the path to a `DefId` and checks if it matches the given path.
-pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
- cx.qpath_res(path, hir_id)
- .opt_def_id()
- .map_or(false, |id| match_def_path(cx, id, segments))
-}
-
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
///
/// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
- expr_path_res(cx, expr)
- .opt_def_id()
- .map_or(false, |id| match_def_path(cx, id, segments))
+ path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
}
/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
/// diagnostic item.
pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
- expr_path_res(cx, expr)
- .opt_def_id()
- .map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+ path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
}
/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
path_to_local(expr) == Some(id)
}
-/// Gets the definition associated to a path.
-pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
+pub trait MaybePath<'hir> {
+ fn hir_id(&self) -> HirId;
+ fn qpath_opt(&self) -> Option<&QPath<'hir>>;
+}
+
+macro_rules! maybe_path {
+ ($ty:ident, $kind:ident) => {
+ impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
+ fn hir_id(&self) -> HirId {
+ self.hir_id
+ }
+ fn qpath_opt(&self) -> Option<&QPath<'hir>> {
+ match &self.kind {
+ hir::$kind::Path(qpath) => Some(qpath),
+ _ => None,
+ }
+ }
+ }
+ };
+}
+maybe_path!(Expr, ExprKind);
+maybe_path!(Pat, PatKind);
+maybe_path!(Ty, TyKind);
+
+/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
+pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
+ match maybe_path.qpath_opt() {
+ None => Res::Err,
+ Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
+ }
+}
+
+/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
+pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
+ path_res(cx, maybe_path).opt_def_id()
+}
+
+/// Resolves a def path like `std::vec::Vec`.
+/// This function is expensive and should be used sparingly.
+pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
macro_rules! try_res {
($e:expr) => {
match $e {
/// Convenience function to get the `DefId` of a trait by path.
/// It could be a trait or trait alias.
pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
- match path_to_res(cx, path) {
+ match def_path_res(cx, path) {
Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
_ => None,
}
if parent_impl != CRATE_DEF_ID;
if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
if let hir::ItemKind::Impl(impl_) = &item.kind;
- then { return impl_.of_trait.as_ref(); }
+ then {
+ return impl_.of_trait.as_ref();
+ }
}
None
}
if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
if is_diag_trait_item(cx, repl_def_id, sym::Default)
|| is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
- then {
- true
- }
- else {
- false
- }
+ then { true } else { false }
}
}
pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
- if let Res::SelfTy(..) = path.res {
+ if let Res::SelfTy { .. } = path.res {
return true;
}
}
if arms.len() == 2;
if arms[0].guard.is_none();
if arms[1].guard.is_none();
- if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) ||
- (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
+ if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
then {
return Some(expr);
}
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
if match_def_path(cx, fun_def_id, path);
then {
- return Some(args)
+ return Some(args);
}
};
None
/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
/// any.
///
-/// Please use `match_any_diagnostic_items` if the targets are all diagnostic items.
+/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
let search_path = cx.get_def_path(did);
paths
.position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
}
-/// Checks if the given `DefId` matches any of provided diagnostic items. Returns the index of
-/// matching path, if any.
-pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
- diag_items
- .iter()
- .position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
-}
-
/// Checks if the given `DefId` matches the path.
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
// We should probably move to Symbols in Clippy as well rather than interning every time.
match expr.kind {
ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
- ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
- _ => false,
+ _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
}
}
let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
let expr_kind = expr_type.kind();
let is_primitive = match expr_kind {
- rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+ rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
if let rustc_ty::Slice(element_type) = inner_ty.kind() {
- is_recursively_primitive_type(element_type)
+ is_recursively_primitive_type(*element_type)
} else {
unreachable!()
}
/// Checks if the function containing the given `HirId` is a `#[test]` function
///
-/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
+/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
with_test_item_names(tcx, tcx.parent_module(id), |names| {
tcx.hir()
/// Checks whether item either has `test` attribute applied, or
/// is a module with `test` in its name.
///
-/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
+/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
is_in_test_function(tcx, item.hir_id())
|| matches!(item.kind, ItemKind::Mod(..))
use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
use rustc_lint::LateContext;
use rustc_span::def_id::DefId;
-use rustc_span::hygiene::{MacroKind, SyntaxContext};
+use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
use std::ops::ControlFlow;
}
/// A parsed `format_args!` expansion
+#[derive(Debug)]
pub struct FormatArgsExpn<'tcx> {
/// Span of the first argument, the format string
pub format_string_span: Span,
if let Ok(i) = usize::try_from(position);
if let Some(&(j, format_trait)) = self.formatters.get(i);
then {
- Some(FormatArgsArg { value: self.value_args[j], format_trait, spec: Some(spec) })
+ Some(FormatArgsArg {
+ value: self.value_args[j],
+ format_trait,
+ spec: Some(spec),
+ })
} else {
None
}
.collect()
}
- /// Span of all inputs
+ /// Source callsite span of all inputs
pub fn inputs_span(&self) -> Span {
match *self.value_args {
[] => self.format_string_span,
- [.., last] => self.format_string_span.to(last.span),
+ [.., last] => self
+ .format_string_span
+ .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
}
}
}
Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
use rustc_middle::ty::cast::CastTy;
let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
- let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
+ let cast_out = CastTy::from_ty(*cast_ty).expect("bad output type for cast");
match (cast_in, cast_out) {
(CastTy::Ptr(_) | CastTy::FnPtr, CastTy::Int(_)) => {
Err((span, "casting pointers to ints is unstable in const fn".into()))
use rustc_trait_selection::traits::query::normalize::AtExt;
use std::iter;
-use crate::{expr_path_res, match_def_path, must_use_attr};
+use crate::{match_def_path, must_use_attr, path_res};
// Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
];
let ty_to_check = match probably_ref_ty.kind() {
- ty::Ref(_, ty_to_check, _) => ty_to_check,
+ ty::Ref(_, ty_to_check, _) => *ty_to_check,
_ => probably_ref_ty,
};
ty: Ty<'tcx>,
cache: &mut FxHashMap<Ty<'tcx>, bool>,
) -> bool {
- if let Some(&cached_result) = cache.get(ty) {
+ if let Some(&cached_result) = cache.get(&ty) {
return cached_result;
}
// prevent recursive loops, false-negative is better than endless loop leading to stack overflow
match ty.kind() {
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
- ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+ ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(*inner_type),
ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
_ => false,
}
pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
if let ty::Ref(_, ty, _) = ty.kind() {
- peel(ty, count + 1)
+ peel(*ty, count + 1)
} else {
(ty, count)
}
pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
match ty.kind() {
- ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
- ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
+ ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
+ ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
_ => (ty, count, mutability),
}
}
pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
match ty.kind() {
- ty::Ref(_, ty, _) => inner(ty, depth + 1),
+ ty::Ref(_, ty, _) => inner(*ty, depth + 1),
_ => (ty, depth),
}
}
/// Checks if a given type looks safe to be uninitialized.
pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
match ty.kind() {
- ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
+ ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, *component),
ty::Tuple(types) => types.types().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did),
_ => false,
/// If the expression is function like, get the signature for it.
pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
- if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) {
+ if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
} else {
let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
- does it implement a trait?
This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct,
-that gives you access to the underlying structure [`TyS`][TyS].
+that gives you access to the underlying structure [`Ty`][Ty].
Example of use:
```rust
assert_eq!(in_external_macro(cx.sess(), match_span), true);
```
-- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
+- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it
+
+One thing `SpanContext` is useful for is to check if two spans are in the same context. For example,
+in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some
+expression with a different context from `a`.
```rust
macro_rules! m {
// These spans are not from the same context
// x.is_some() is from inside the macro
// x.unwrap() is from outside the macro
- assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+ assert_eq!(x_is_some_span.ctxt(), x_unwrap_span.ctxt());
```
-[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
+[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
[toolchain]
-channel = "nightly-2022-01-27"
+channel = "nightly-2022-02-10"
components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
use std::ffi::{OsStr, OsString};
use std::fs;
use std::io;
+use std::lazy::SyncLazy;
use std::path::{Path, PathBuf};
use test_utils::IS_RUSTC_TEST_SUITE;
/// dependencies must be added to Cargo.toml at the project root. Test
/// dependencies that are not *directly* used by this test module require an
/// `extern crate` declaration.
-fn extern_flags() -> String {
+static EXTERN_FLAGS: SyncLazy<String> = SyncLazy::new(|| {
let current_exe_depinfo = {
let mut path = env::current_exe().unwrap();
path.set_extension("d");
- std::fs::read_to_string(path).unwrap()
+ fs::read_to_string(path).unwrap()
};
let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
for line in current_exe_depinfo.lines() {
.into_iter()
.map(|(name, path)| format!(" --extern {}={}", name, path))
.collect()
-}
+});
-fn default_config() -> compiletest::Config {
+fn base_config(test_dir: &str) -> compiletest::Config {
let mut config = compiletest::Config {
edition: Some("2021".into()),
+ mode: TestMode::Ui,
..compiletest::Config::default()
};
if let Ok(filters) = env::var("TESTNAME") {
- config.filters = filters.split(',').map(std::string::ToString::to_string).collect();
+ config.filters = filters.split(',').map(ToString::to_string).collect();
}
if let Some(path) = option_env!("RUSTC_LIB_PATH") {
config.run_lib_path = path.clone();
config.compile_lib_path = path;
}
- let current_exe_path = std::env::current_exe().unwrap();
+ let current_exe_path = env::current_exe().unwrap();
let deps_path = current_exe_path.parent().unwrap();
let profile_path = deps_path.parent().unwrap();
"--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
deps_path.display(),
host_libs,
- extern_flags(),
+ &*EXTERN_FLAGS,
));
- config.build_base = profile_path.join("test");
+ config.src_base = Path::new("tests").join(test_dir);
+ config.build_base = profile_path.join("test").join(test_dir);
config.rustc_path = profile_path.join(if cfg!(windows) {
"clippy-driver.exe"
} else {
config
}
-fn run_ui(cfg: &mut compiletest::Config) {
- cfg.mode = TestMode::Ui;
- cfg.src_base = Path::new("tests").join("ui");
+fn run_ui() {
+ let config = base_config("ui");
// use tests/clippy.toml
- let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
- compiletest::run_tests(cfg);
-}
-
-fn run_ui_test(cfg: &mut compiletest::Config) {
- cfg.mode = TestMode::Ui;
- cfg.src_base = Path::new("tests").join("ui_test");
- let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
- let rustcflags = cfg.target_rustcflags.get_or_insert_with(Default::default);
- let len = rustcflags.len();
- rustcflags.push_str(" --test");
- compiletest::run_tests(cfg);
- if let Some(ref mut flags) = &mut cfg.target_rustcflags {
- flags.truncate(len);
- }
+ let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
+ compiletest::run_tests(&config);
}
-fn run_internal_tests(cfg: &mut compiletest::Config) {
+fn run_internal_tests() {
// only run internal tests with the internal-tests feature
if !RUN_INTERNAL_TESTS {
return;
}
- cfg.mode = TestMode::Ui;
- cfg.src_base = Path::new("tests").join("ui-internal");
- compiletest::run_tests(cfg);
+ let config = base_config("ui-internal");
+ compiletest::run_tests(&config);
}
-fn run_ui_toml(config: &mut compiletest::Config) {
+fn run_ui_toml() {
fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
let mut result = true;
let opts = compiletest::test_opts(config);
Ok(result)
}
- config.mode = TestMode::Ui;
- config.src_base = Path::new("tests").join("ui-toml").canonicalize().unwrap();
+ let mut config = base_config("ui-toml");
+ config.src_base = config.src_base.canonicalize().unwrap();
- let tests = compiletest::make_tests(config);
+ let tests = compiletest::make_tests(&config);
- let res = run_tests(config, tests);
+ let res = run_tests(&config, tests);
match res {
Ok(true) => {},
Ok(false) => panic!("Some tests failed"),
}
}
-fn run_ui_cargo(config: &mut compiletest::Config) {
+fn run_ui_cargo() {
fn run_tests(
config: &compiletest::Config,
filters: &[String],
return;
}
- config.mode = TestMode::Ui;
- config.src_base = Path::new("tests").join("ui-cargo").canonicalize().unwrap();
+ let mut config = base_config("ui-cargo");
+ config.src_base = config.src_base.canonicalize().unwrap();
- let tests = compiletest::make_tests(config);
+ let tests = compiletest::make_tests(&config);
let current_dir = env::current_dir().unwrap();
- let res = run_tests(config, &config.filters, tests);
+ let res = run_tests(&config, &config.filters, tests);
env::set_current_dir(current_dir).unwrap();
match res {
#[test]
fn compile_test() {
set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
- let mut config = default_config();
- run_ui(&mut config);
- run_ui_test(&mut config);
- run_ui_toml(&mut config);
- run_ui_cargo(&mut config);
- run_internal_tests(&mut config);
+ run_ui();
+ run_ui_toml();
+ run_ui_cargo();
+ run_internal_tests();
}
/// Restores an env var on drop
--- /dev/null
+[package]
+name = "no_warn"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
--- /dev/null
+avoid-breaking-exported-api = false
--- /dev/null
+fn main() {
+ println!("Hello, world!");
+}
--- /dev/null
+avoid-breaking-exported-api = false
--- /dev/null
+[package]
+name = "warn"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
--- /dev/null
+avoid-breaking-exported-api = false
--- /dev/null
+// ignore-windows
+
+fn main() {
+ println!("Hello, world!");
+}
--- /dev/null
+Using config file `$SRC_DIR/tests/ui-cargo/multiple_config_files/warn/.clippy.toml`
+Warning: `$SRC_DIR/tests/ui-cargo/multiple_config_files/warn/clippy.toml` will be ignored.
// Test for https://github.com/rust-lang/rust-clippy/issues/4968
#![warn(clippy::unsound_collection_transmute)]
+#![allow(clippy::transmute_undefined_repr)]
trait Trait {
type Assoc;
--- /dev/null
+fn _f(s: &str) -> Option<()> {
+ let _ = s[1..].splitn(2, '.').next()?;
+ Some(())
+}
+
+fn main() {}
--- /dev/null
+error: manual implementation of `split_once`
+ --> $DIR/ice-8250.rs:2:13
+ |
+LL | let _ = s[1..].splitn(2, '.').next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)`
+ |
+ = note: `-D clippy::manual-split-once` implied by `-D warnings`
+
+error: unnecessary use of `splitn`
+ --> $DIR/ice-8250.rs:2:13
+ |
+LL | let _ = s[1..].splitn(2, '.').next()?;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split('.')`
+ |
+ = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+fn f(x: u32, mut arg: &String) {}
+
+fn main() {}
--- /dev/null
+#![feature(transparent_unions)]
+#![warn(clippy::default_union_representation)]
+
+union NoAttribute {
+ a: i32,
+ b: u32,
+}
+
+#[repr(C)]
+union ReprC {
+ a: i32,
+ b: u32,
+}
+
+#[repr(packed)]
+union ReprPacked {
+ a: i32,
+ b: u32,
+}
+
+#[repr(C, packed)]
+union ReprCPacked {
+ a: i32,
+ b: u32,
+}
+
+#[repr(C, align(32))]
+union ReprCAlign {
+ a: i32,
+ b: u32,
+}
+
+#[repr(align(32))]
+union ReprAlign {
+ a: i32,
+ b: u32,
+}
+
+union SingleZST {
+ f0: (),
+}
+union ZSTsAndField1 {
+ f0: u32,
+ f1: (),
+ f2: (),
+ f3: (),
+}
+union ZSTsAndField2 {
+ f0: (),
+ f1: (),
+ f2: u32,
+ f3: (),
+}
+union ZSTAndTwoFields {
+ f0: u32,
+ f1: u64,
+ f2: (),
+}
+
+#[repr(C)]
+union CZSTAndTwoFields {
+ f0: u32,
+ f1: u64,
+ f2: (),
+}
+
+#[repr(transparent)]
+union ReprTransparent {
+ a: i32,
+}
+
+#[repr(transparent)]
+union ReprTransparentZST {
+ a: i32,
+ b: (),
+}
+
+fn main() {}
--- /dev/null
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:4:1
+ |
+LL | / union NoAttribute {
+LL | | a: i32,
+LL | | b: u32,
+LL | | }
+ | |_^
+ |
+ = note: `-D clippy::default-union-representation` implied by `-D warnings`
+ = help: consider annotating `NoAttribute` with `#[repr(C)]` to explicitly specify memory layout
+
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:16:1
+ |
+LL | / union ReprPacked {
+LL | | a: i32,
+LL | | b: u32,
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ReprPacked` with `#[repr(C)]` to explicitly specify memory layout
+
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:34:1
+ |
+LL | / union ReprAlign {
+LL | | a: i32,
+LL | | b: u32,
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ReprAlign` with `#[repr(C)]` to explicitly specify memory layout
+
+error: this union has the default representation
+ --> $DIR/default_union_representation.rs:54:1
+ |
+LL | / union ZSTAndTwoFields {
+LL | | f0: u32,
+LL | | f1: u64,
+LL | | f2: (),
+LL | | }
+ | |_^
+ |
+ = help: consider annotating `ZSTAndTwoFields` with `#[repr(C)]` to explicitly specify memory layout
+
+error: aborting due to 4 previous errors
+
-// does not test any rustfixable lints
-
-#[rustfmt::skip]
-#[warn(clippy::eq_op)]
-#[allow(clippy::identity_op, clippy::double_parens)]
-#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
-#[allow(clippy::nonminimal_bool)]
-#[allow(unused)]
-#[allow(clippy::unnecessary_cast)]
+// compile-flags: --test
+
+#![warn(clippy::eq_op)]
+#![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)]
+
fn main() {
// simple values and comparisons
- 1 == 1;
- "no" == "no";
+ let _ = 1 == 1;
+ let _ = "no" == "no";
// even though I agree that no means no ;-)
- false != false;
- 1.5 < 1.5;
- 1u64 >= 1u64;
+ let _ = false != false;
+ let _ = 1.5 < 1.5;
+ let _ = 1u64 >= 1u64;
// casts, methods, parentheses
- (1 as u64) & (1 as u64);
- 1 ^ ((((((1))))));
+ let _ = (1u32 as u64) & (1u32 as u64);
+ #[rustfmt::skip]
+ {
+ let _ = 1 ^ ((((((1))))));
+ };
// unary and binary operators
- (-(2) < -(2));
- ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
- (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
+ let _ = (-(2) < -(2));
+ let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
// various other things
- ([1] != [1]);
- ((1, 2) != (1, 2));
- vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
+ let _ = ([1] != [1]);
+ let _ = ((1, 2) != (1, 2));
+ let _ = vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
// const folding
- 1 + 1 == 2;
- 1 - 1 == 0;
+ let _ = 1 + 1 == 2;
+ let _ = 1 - 1 == 0;
- 1 - 1;
- 1 / 1;
- true && true;
-
- true || true;
+ let _ = 1 - 1;
+ let _ = 1 / 1;
+ let _ = true && true;
+ let _ = true || true;
let a: u32 = 0;
let b: u32 = 0;
- a == b && b == a;
- a != b && b != a;
- a < b && b > a;
- a <= b && b >= a;
+ let _ = a == b && b == a;
+ let _ = a != b && b != a;
+ let _ = a < b && b > a;
+ let _ = a <= b && b >= a;
let mut a = vec![1];
- a == a;
- 2*a.len() == 2*a.len(); // ok, functions
- a.pop() == a.pop(); // ok, functions
+ let _ = a == a;
+ let _ = 2 * a.len() == 2 * a.len(); // ok, functions
+ let _ = a.pop() == a.pop(); // ok, functions
check_ignore_macro();
const D: u32 = A / A;
}
-#[rustfmt::skip]
macro_rules! check_if_named_foo {
- ($expression:expr) => (
+ ($expression:expr) => {
if stringify!($expression) == "foo" {
println!("foo!");
} else {
println!("not foo.");
}
- )
+ };
}
macro_rules! bool_macro {
};
}
-#[allow(clippy::short_circuit_statement)]
fn check_ignore_macro() {
check_if_named_foo!(foo);
// checks if the lint ignores macros with `!` operator
- !bool_macro!(1) && !bool_macro!("");
+ let _ = !bool_macro!(1) && !bool_macro!("");
}
struct Nested {
// `n2.inner.0.0` mistyped as `n1.inner.0.0`
(n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
}
+
+#[test]
+fn eq_op_shouldnt_trigger_in_tests() {
+ let a = 1;
+ let result = a + 1 == 1 + a;
+ assert!(result);
+}
+
+#[test]
+fn eq_op_macros_shouldnt_trigger_in_tests() {
+ let a = 1;
+ let b = 2;
+ assert_eq!(a, a);
+ assert_eq!(a + b, b + a);
+}
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:12:5
+ --> $DIR/eq_op.rs:8:13
|
-LL | 1 == 1;
- | ^^^^^^
+LL | let _ = 1 == 1;
+ | ^^^^^^
|
= note: `-D clippy::eq-op` implied by `-D warnings`
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:13:5
+ --> $DIR/eq_op.rs:9:13
|
-LL | "no" == "no";
- | ^^^^^^^^^^^^
+LL | let _ = "no" == "no";
+ | ^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:15:5
+ --> $DIR/eq_op.rs:11:13
|
-LL | false != false;
- | ^^^^^^^^^^^^^^
+LL | let _ = false != false;
+ | ^^^^^^^^^^^^^^
error: equal expressions as operands to `<`
- --> $DIR/eq_op.rs:16:5
+ --> $DIR/eq_op.rs:12:13
|
-LL | 1.5 < 1.5;
- | ^^^^^^^^^
+LL | let _ = 1.5 < 1.5;
+ | ^^^^^^^^^
error: equal expressions as operands to `>=`
- --> $DIR/eq_op.rs:17:5
+ --> $DIR/eq_op.rs:13:13
|
-LL | 1u64 >= 1u64;
- | ^^^^^^^^^^^^
+LL | let _ = 1u64 >= 1u64;
+ | ^^^^^^^^^^^^
error: equal expressions as operands to `&`
- --> $DIR/eq_op.rs:20:5
+ --> $DIR/eq_op.rs:16:13
|
-LL | (1 as u64) & (1 as u64);
- | ^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = (1u32 as u64) & (1u32 as u64);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `^`
- --> $DIR/eq_op.rs:21:5
+ --> $DIR/eq_op.rs:19:17
|
-LL | 1 ^ ((((((1))))));
- | ^^^^^^^^^^^^^^^^^
+LL | let _ = 1 ^ ((((((1))))));
+ | ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `<`
- --> $DIR/eq_op.rs:24:5
+ --> $DIR/eq_op.rs:23:13
|
-LL | (-(2) < -(2));
- | ^^^^^^^^^^^^^
+LL | let _ = (-(2) < -(2));
+ | ^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:25:5
+ --> $DIR/eq_op.rs:24:13
|
-LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&`
- --> $DIR/eq_op.rs:25:6
+ --> $DIR/eq_op.rs:24:14
|
-LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
- | ^^^^^^^^^^^^^^^^^
+LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ | ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&`
- --> $DIR/eq_op.rs:25:27
+ --> $DIR/eq_op.rs:24:35
|
-LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
- | ^^^^^^^^^^^^^^^^^
+LL | let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
+ | ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:26:5
+ --> $DIR/eq_op.rs:25:13
|
-LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:29:5
+ --> $DIR/eq_op.rs:28:13
|
-LL | ([1] != [1]);
- | ^^^^^^^^^^^^
+LL | let _ = ([1] != [1]);
+ | ^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:30:5
+ --> $DIR/eq_op.rs:29:13
|
-LL | ((1, 2) != (1, 2));
- | ^^^^^^^^^^^^^^^^^^
+LL | let _ = ((1, 2) != (1, 2));
+ | ^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:34:5
+ --> $DIR/eq_op.rs:33:13
|
-LL | 1 + 1 == 2;
- | ^^^^^^^^^^
+LL | let _ = 1 + 1 == 2;
+ | ^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:35:5
+ --> $DIR/eq_op.rs:34:13
|
-LL | 1 - 1 == 0;
- | ^^^^^^^^^^
+LL | let _ = 1 - 1 == 0;
+ | ^^^^^^^^^^
error: equal expressions as operands to `-`
- --> $DIR/eq_op.rs:35:5
+ --> $DIR/eq_op.rs:34:13
|
-LL | 1 - 1 == 0;
- | ^^^^^
+LL | let _ = 1 - 1 == 0;
+ | ^^^^^
error: equal expressions as operands to `-`
- --> $DIR/eq_op.rs:37:5
+ --> $DIR/eq_op.rs:36:13
|
-LL | 1 - 1;
- | ^^^^^
+LL | let _ = 1 - 1;
+ | ^^^^^
error: equal expressions as operands to `/`
- --> $DIR/eq_op.rs:38:5
+ --> $DIR/eq_op.rs:37:13
|
-LL | 1 / 1;
- | ^^^^^
+LL | let _ = 1 / 1;
+ | ^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:39:5
+ --> $DIR/eq_op.rs:38:13
|
-LL | true && true;
- | ^^^^^^^^^^^^
+LL | let _ = true && true;
+ | ^^^^^^^^^^^^
error: equal expressions as operands to `||`
- --> $DIR/eq_op.rs:41:5
+ --> $DIR/eq_op.rs:40:13
|
-LL | true || true;
- | ^^^^^^^^^^^^
+LL | let _ = true || true;
+ | ^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:47:5
+ --> $DIR/eq_op.rs:45:13
|
-LL | a == b && b == a;
- | ^^^^^^^^^^^^^^^^
+LL | let _ = a == b && b == a;
+ | ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:48:5
+ --> $DIR/eq_op.rs:46:13
|
-LL | a != b && b != a;
- | ^^^^^^^^^^^^^^^^
+LL | let _ = a != b && b != a;
+ | ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:49:5
+ --> $DIR/eq_op.rs:47:13
|
-LL | a < b && b > a;
- | ^^^^^^^^^^^^^^
+LL | let _ = a < b && b > a;
+ | ^^^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:50:5
+ --> $DIR/eq_op.rs:48:13
|
-LL | a <= b && b >= a;
- | ^^^^^^^^^^^^^^^^
+LL | let _ = a <= b && b >= a;
+ | ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:53:5
+ --> $DIR/eq_op.rs:51:13
|
-LL | a == a;
- | ^^^^^^
+LL | let _ = a == a;
+ | ^^^^^^
error: equal expressions as operands to `/`
- --> $DIR/eq_op.rs:63:20
+ --> $DIR/eq_op.rs:61:20
|
LL | const D: u32 = A / A;
| ^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:96:5
+ --> $DIR/eq_op.rs:92:5
|
LL | (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `#[deny(clippy::eq_op)]` on by default
error: aborting due to 28 previous errors
/// Checks implementation of the `EXPECT_FUN_CALL` lint
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
struct Foo;
let with_none_and_as_str: Option<i32> = None;
with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
+ let with_none_and_format_with_macro: Option<i32> = None;
+ with_none_and_format_with_macro.unwrap_or_else(|| panic!("Error {}: fake error", one!()));
+
let with_ok: Result<(), ()> = Ok(());
with_ok.expect("error");
/// Checks implementation of the `EXPECT_FUN_CALL` lint
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
struct Foo;
let with_none_and_as_str: Option<i32> = None;
with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
+ let with_none_and_format_with_macro: Option<i32> = None;
+ with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
+
let with_ok: Result<(), ()> = Ok(());
with_ok.expect("error");
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:29:26
+ --> $DIR/expect_fun_call.rs:35:26
|
LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
= note: `-D clippy::expect-fun-call` implied by `-D warnings`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:32:26
+ --> $DIR/expect_fun_call.rs:38:26
|
LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:42:25
+ --> $DIR/expect_fun_call.rs:41:37
+ |
+LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))`
+
+error: use of `expect` followed by a function call
+ --> $DIR/expect_fun_call.rs:51:25
|
LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:45:25
+ --> $DIR/expect_fun_call.rs:54:25
|
LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:57:17
+ --> $DIR/expect_fun_call.rs:66:17
|
LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:78:21
+ --> $DIR/expect_fun_call.rs:87:21
|
LL | Some("foo").expect(&get_string());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:79:21
+ --> $DIR/expect_fun_call.rs:88:21
|
LL | Some("foo").expect(get_string().as_ref());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:80:21
+ --> $DIR/expect_fun_call.rs:89:21
|
LL | Some("foo").expect(get_string().as_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:82:21
+ --> $DIR/expect_fun_call.rs:91:21
|
LL | Some("foo").expect(get_static_str());
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:83:21
+ --> $DIR/expect_fun_call.rs:92:21
|
LL | Some("foo").expect(get_non_static_str(&0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:87:16
+ --> $DIR/expect_fun_call.rs:96:16
|
LL | Some(true).expect(&format!("key {}, {}", 1, 2));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
error: use of `expect` followed by a function call
- --> $DIR/expect_fun_call.rs:93:17
+ --> $DIR/expect_fun_call.rs:102:17
|
LL | opt_ref.expect(&format!("{:?}", opt_ref));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
-error: aborting due to 12 previous errors
+error: aborting due to 13 previous errors
--> $DIR/explicit_counter_loop.rs:170:9
|
LL | for _item in slice {
- | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.into_iter().enumerate()`
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.iter().enumerate()`
error: the variable `idx_u32` is used as a loop counter
--> $DIR/explicit_counter_loop.rs:182:9
|
LL | for _item in slice {
- | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.into_iter())`
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.iter())`
|
= note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate`
String::new()
}
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
// these should warn
{
// including newlines
println!("test\ntest");
eprintln!("test\ntest");
+
+ let value = 1;
+ eprintln!("with {}", value);
+ eprintln!("with {} {}", 2, value);
+ eprintln!("with {value}");
+ eprintln!("macro arg {}", one!());
}
// these should not warn, different destination
{
String::new()
}
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
// these should warn
{
// including newlines
writeln!(std::io::stdout(), "test\ntest").unwrap();
writeln!(std::io::stderr(), "test\ntest").unwrap();
+
+ let value = 1;
+ writeln!(std::io::stderr(), "with {}", value).unwrap();
+ writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
+ writeln!(std::io::stderr(), "with {value}").unwrap();
+ writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
}
// these should not warn, different destination
{
error: use of `write!(stdout(), ...).unwrap()`
- --> $DIR/explicit_write.rs:17:9
+ --> $DIR/explicit_write.rs:23:9
|
LL | write!(std::io::stdout(), "test").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
= note: `-D clippy::explicit-write` implied by `-D warnings`
error: use of `write!(stderr(), ...).unwrap()`
- --> $DIR/explicit_write.rs:18:9
+ --> $DIR/explicit_write.rs:24:9
|
LL | write!(std::io::stderr(), "test").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
error: use of `writeln!(stdout(), ...).unwrap()`
- --> $DIR/explicit_write.rs:19:9
+ --> $DIR/explicit_write.rs:25:9
|
LL | writeln!(std::io::stdout(), "test").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test")`
error: use of `writeln!(stderr(), ...).unwrap()`
- --> $DIR/explicit_write.rs:20:9
+ --> $DIR/explicit_write.rs:26:9
|
LL | writeln!(std::io::stderr(), "test").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test")`
error: use of `stdout().write_fmt(...).unwrap()`
- --> $DIR/explicit_write.rs:21:9
+ --> $DIR/explicit_write.rs:27:9
|
LL | std::io::stdout().write_fmt(format_args!("test")).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `print!("test")`
error: use of `stderr().write_fmt(...).unwrap()`
- --> $DIR/explicit_write.rs:22:9
+ --> $DIR/explicit_write.rs:28:9
|
LL | std::io::stderr().write_fmt(format_args!("test")).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprint!("test")`
error: use of `writeln!(stdout(), ...).unwrap()`
- --> $DIR/explicit_write.rs:25:9
+ --> $DIR/explicit_write.rs:31:9
|
LL | writeln!(std::io::stdout(), "test/ntest").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `println!("test/ntest")`
error: use of `writeln!(stderr(), ...).unwrap()`
- --> $DIR/explicit_write.rs:26:9
+ --> $DIR/explicit_write.rs:32:9
|
LL | writeln!(std::io::stderr(), "test/ntest").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("test/ntest")`
-error: aborting due to 8 previous errors
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:35:9
+ |
+LL | writeln!(std::io::stderr(), "with {}", value).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {}", value)`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:36:9
+ |
+LL | writeln!(std::io::stderr(), "with {} {}", 2, value).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {} {}", 2, value)`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:37:9
+ |
+LL | writeln!(std::io::stderr(), "with {value}").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("with {value}")`
+
+error: use of `writeln!(stderr(), ...).unwrap()`
+ --> $DIR/explicit_write.rs:38:9
+ |
+LL | writeln!(std::io::stderr(), "macro arg {}", one!()).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `eprintln!("macro arg {}", one!())`
+
+error: aborting due to 12 previous errors
+++ /dev/null
-#![allow(unused_imports, clippy::blacklisted_name)]
-#![warn(clippy::explicit_write)]
-
-fn main() {
- use std::io::Write;
- let bar = "bar";
- writeln!(std::io::stderr(), "foo {}", bar).unwrap();
-}
+++ /dev/null
-error: use of `writeln!(stderr(), ...).unwrap()`
- --> $DIR/explicit_write_non_rustfix.rs:7:5
- |
-LL | writeln!(std::io::stderr(), "foo {}", bar).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::explicit-write` implied by `-D warnings`
- = help: consider using `eprintln!` instead
-
-error: aborting due to previous error
-
// run-rustfix
+
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
+#![warn(clippy::unwrap_used)]
#![deny(clippy::get_unwrap)]
use std::collections::BTreeMap;
let _ = &some_vecdeque[0];
let _ = &some_hashmap[&1];
let _ = &some_btreemap[&1];
+ #[allow(clippy::unwrap_used)]
let _ = false_positive.get(0).unwrap();
// Test with deref
let _: u8 = boxed_slice[1];
some_vec[0] = 1;
some_vecdeque[0] = 1;
// Check false positives
- *some_hashmap.get_mut(&1).unwrap() = 'b';
- *some_btreemap.get_mut(&1).unwrap() = 'b';
- *false_positive.get_mut(0).unwrap() = 1;
+ #[allow(clippy::unwrap_used)]
+ {
+ *some_hashmap.get_mut(&1).unwrap() = 'b';
+ *some_btreemap.get_mut(&1).unwrap() = 'b';
+ *false_positive.get_mut(0).unwrap() = 1;
+ }
}
{
// run-rustfix
+
#![allow(unused_mut, clippy::from_iter_instead_of_collect)]
+#![warn(clippy::unwrap_used)]
#![deny(clippy::get_unwrap)]
use std::collections::BTreeMap;
let _ = some_vecdeque.get(0).unwrap();
let _ = some_hashmap.get(&1).unwrap();
let _ = some_btreemap.get(&1).unwrap();
+ #[allow(clippy::unwrap_used)]
let _ = false_positive.get(0).unwrap();
// Test with deref
let _: u8 = *boxed_slice.get(1).unwrap();
*some_vec.get_mut(0).unwrap() = 1;
*some_vecdeque.get_mut(0).unwrap() = 1;
// Check false positives
- *some_hashmap.get_mut(&1).unwrap() = 'b';
- *some_btreemap.get_mut(&1).unwrap() = 'b';
- *false_positive.get_mut(0).unwrap() = 1;
+ #[allow(clippy::unwrap_used)]
+ {
+ *some_hashmap.get_mut(&1).unwrap() = 'b';
+ *some_btreemap.get_mut(&1).unwrap() = 'b';
+ *false_positive.get_mut(0).unwrap() = 1;
+ }
}
{
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:34:17
+ --> $DIR/get_unwrap.rs:36:17
|
LL | let _ = boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
|
note: the lint level is defined here
- --> $DIR/get_unwrap.rs:3:9
+ --> $DIR/get_unwrap.rs:5:9
|
LL | #![deny(clippy::get_unwrap)]
| ^^^^^^^^^^^^^^^^^^
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:36:17
+ |
+LL | let _ = boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::unwrap-used` implied by `-D warnings`
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:35:17
+ --> $DIR/get_unwrap.rs:37:17
|
LL | let _ = some_slice.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:37:17
+ |
+LL | let _ = some_slice.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:36:17
+ --> $DIR/get_unwrap.rs:38:17
|
LL | let _ = some_vec.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:38:17
+ |
+LL | let _ = some_vec.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:37:17
+ --> $DIR/get_unwrap.rs:39:17
|
LL | let _ = some_vecdeque.get(0).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:39:17
+ |
+LL | let _ = some_vecdeque.get(0).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:38:17
+ --> $DIR/get_unwrap.rs:40:17
|
LL | let _ = some_hashmap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:40:17
+ |
+LL | let _ = some_hashmap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:39:17
+ --> $DIR/get_unwrap.rs:41:17
|
LL | let _ = some_btreemap.get(&1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:41:17
+ |
+LL | let _ = some_btreemap.get(&1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:42:21
+ --> $DIR/get_unwrap.rs:45:21
|
LL | let _: u8 = *boxed_slice.get(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:45:22
+ |
+LL | let _: u8 = *boxed_slice.get(1).unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:47:9
+ --> $DIR/get_unwrap.rs:50:9
|
LL | *boxed_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:50:10
+ |
+LL | *boxed_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:48:9
+ --> $DIR/get_unwrap.rs:51:9
|
LL | *some_slice.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:51:10
+ |
+LL | *some_slice.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:49:9
+ --> $DIR/get_unwrap.rs:52:9
|
LL | *some_vec.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:52:10
+ |
+LL | *some_vec.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:50:9
+ --> $DIR/get_unwrap.rs:53:9
|
LL | *some_vecdeque.get_mut(0).unwrap() = 1;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:53:10
+ |
+LL | *some_vecdeque.get_mut(0).unwrap() = 1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:59:17
+ --> $DIR/get_unwrap.rs:65:17
|
LL | let _ = some_vec.get(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:65:17
+ |
+LL | let _ = some_vec.get(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
- --> $DIR/get_unwrap.rs:60:17
+ --> $DIR/get_unwrap.rs:66:17
|
LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
-error: aborting due to 13 previous errors
+error: used `unwrap()` on `an Option` value
+ --> $DIR/get_unwrap.rs:66:17
+ |
+LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
+
+error: aborting due to 26 previous errors
#![warn(clippy::manual_assert)]
#![allow(clippy::nonminimal_bool)]
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
let a = vec![1, 2, 3];
let c = Some(2);
assert!(!(a.is_empty() && !b.is_empty()), "panic3");
assert!(!(b.is_empty() || a.is_empty()), "panic4");
assert!(!(a.is_empty() || !b.is_empty()), "panic5");
+ assert!(!a.is_empty(), "with expansion {}", one!());
}
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:24:5
+ --> $DIR/manual_assert.rs:30:5
|
LL | / if !a.is_empty() {
LL | | panic!("qaqaq{:?}", a);
= note: `-D clippy::manual-assert` implied by `-D warnings`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:27:5
+ --> $DIR/manual_assert.rs:33:5
|
LL | / if !a.is_empty() {
LL | | panic!("qwqwq");
| |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:44:5
+ --> $DIR/manual_assert.rs:50:5
|
LL | / if b.is_empty() {
LL | | panic!("panic1");
| |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:47:5
+ --> $DIR/manual_assert.rs:53:5
|
LL | / if b.is_empty() && a.is_empty() {
LL | | panic!("panic2");
| |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:50:5
+ --> $DIR/manual_assert.rs:56:5
|
LL | / if a.is_empty() && !b.is_empty() {
LL | | panic!("panic3");
| |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:53:5
+ --> $DIR/manual_assert.rs:59:5
|
LL | / if b.is_empty() || a.is_empty() {
LL | | panic!("panic4");
| |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:56:5
+ --> $DIR/manual_assert.rs:62:5
|
LL | / if a.is_empty() || !b.is_empty() {
LL | | panic!("panic5");
LL | | }
| |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
-error: aborting due to 7 previous errors
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:65:5
+ |
+LL | / if a.is_empty() {
+LL | | panic!("with expansion {}", one!())
+LL | | }
+ | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());`
+
+error: aborting due to 8 previous errors
#![warn(clippy::manual_assert)]
#![allow(clippy::nonminimal_bool)]
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
let a = vec![1, 2, 3];
let c = Some(2);
assert!(!(a.is_empty() && !b.is_empty()), "panic3");
assert!(!(b.is_empty() || a.is_empty()), "panic4");
assert!(!(a.is_empty() || !b.is_empty()), "panic5");
+ assert!(!a.is_empty(), "with expansion {}", one!());
}
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:24:5
+ --> $DIR/manual_assert.rs:30:5
|
LL | / if !a.is_empty() {
LL | | panic!("qaqaq{:?}", a);
= note: `-D clippy::manual-assert` implied by `-D warnings`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:27:5
+ --> $DIR/manual_assert.rs:33:5
|
LL | / if !a.is_empty() {
LL | | panic!("qwqwq");
| |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:44:5
+ --> $DIR/manual_assert.rs:50:5
|
LL | / if b.is_empty() {
LL | | panic!("panic1");
| |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:47:5
+ --> $DIR/manual_assert.rs:53:5
|
LL | / if b.is_empty() && a.is_empty() {
LL | | panic!("panic2");
| |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:50:5
+ --> $DIR/manual_assert.rs:56:5
|
LL | / if a.is_empty() && !b.is_empty() {
LL | | panic!("panic3");
| |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:53:5
+ --> $DIR/manual_assert.rs:59:5
|
LL | / if b.is_empty() || a.is_empty() {
LL | | panic!("panic4");
| |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:56:5
+ --> $DIR/manual_assert.rs:62:5
|
LL | / if a.is_empty() || !b.is_empty() {
LL | | panic!("panic5");
LL | | }
| |_____^ help: try: `assert!(!(a.is_empty() || !b.is_empty()), "panic5");`
-error: aborting due to 7 previous errors
+error: only a `panic!` in `if`-then statement
+ --> $DIR/manual_assert.rs:65:5
+ |
+LL | / if a.is_empty() {
+LL | | panic!("with expansion {}", one!())
+LL | | }
+ | |_____^ help: try: `assert!(!a.is_empty(), "with expansion {}", one!());`
+
+error: aborting due to 8 previous errors
#![warn(clippy::manual_assert)]
#![allow(clippy::nonminimal_bool)]
+macro_rules! one {
+ () => {
+ 1
+ };
+}
+
fn main() {
let a = vec![1, 2, 3];
let c = Some(2);
if a.is_empty() || !b.is_empty() {
panic!("panic5");
}
+ if a.is_empty() {
+ panic!("with expansion {}", one!())
+ }
}
}
// Test for loop over an implicit reference
- // Note: if `clippy::manual_flatten` is made autofixable, this case will
- // lead to a follow-up lint `clippy::into_iter_on_ref`
let z = &y;
for n in z {
if let Ok(n) = n {
| |_________^
error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:32:5
+ --> $DIR/manual_flatten.rs:30:5
|
LL | for n in z {
- | ^ - help: try: `z.into_iter().flatten()`
+ | ^ - help: try: `z.iter().flatten()`
| _____|
| |
LL | | if let Ok(n) = n {
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:33:9
+ --> $DIR/manual_flatten.rs:31:9
|
LL | / if let Ok(n) = n {
LL | | println!("{}", n);
| |_________^
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:41:5
+ --> $DIR/manual_flatten.rs:39:5
|
LL | for n in z {
| ^ - help: try: `z.flatten()`
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:42:9
+ --> $DIR/manual_flatten.rs:40:9
|
LL | / if let Some(m) = n {
LL | | println!("{}", m);
| |_________^
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:74:5
+ --> $DIR/manual_flatten.rs:72:5
|
LL | for n in &vec_of_ref {
| ^ ----------- help: try: `vec_of_ref.iter().copied().flatten()`
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:75:9
+ --> $DIR/manual_flatten.rs:73:9
|
LL | / if let Some(n) = n {
LL | | println!("{:?}", n);
| |_________^
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:81:5
+ --> $DIR/manual_flatten.rs:79:5
|
LL | for n in vec_of_ref {
- | ^ ---------- help: try: `vec_of_ref.into_iter().copied().flatten()`
+ | ^ ---------- help: try: `vec_of_ref.iter().copied().flatten()`
| _____|
| |
LL | | if let Some(n) = n {
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:82:9
+ --> $DIR/manual_flatten.rs:80:9
|
LL | / if let Some(n) = n {
LL | | println!("{:?}", n);
| |_________^
error: unnecessary `if let` since only the `Some` variant of the iterator element is used
- --> $DIR/manual_flatten.rs:88:5
+ --> $DIR/manual_flatten.rs:86:5
|
LL | for n in slice_of_ref {
- | ^ ------------ help: try: `slice_of_ref.into_iter().copied().flatten()`
+ | ^ ------------ help: try: `slice_of_ref.iter().copied().flatten()`
| _____|
| |
LL | | if let Some(n) = n {
| |_____^
|
help: ...and remove the `if let` statement in the for loop
- --> $DIR/manual_flatten.rs:89:9
+ --> $DIR/manual_flatten.rs:87:9
|
LL | / if let Some(n) = n {
LL | | println!("{:?}", n);
*x = 5;
let s = String::new();
- let _ = s.len();
- let _ = s.capacity();
- let _ = s.capacity();
+ // let _ = (&s).len();
+ // let _ = (&s).capacity();
+ // let _ = (&&s).capacity();
let x = (1, 2);
let _ = x.0;
*x = 5;
let s = String::new();
- let _ = (&s).len();
- let _ = (&s).capacity();
- let _ = (&&s).capacity();
+ // let _ = (&s).len();
+ // let _ = (&s).capacity();
+ // let _ = (&&s).capacity();
let x = (1, 2);
let _ = (&x).0;
LL | let y: &mut i32 = &mut &mut x;
| ^^^^^^^^^^^ help: change this to: `x`
-error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:67:13
- |
-LL | let _ = (&s).len();
- | ^^^^ help: change this to: `s`
-
-error: this expression borrows a value the compiler would automatically borrow
- --> $DIR/needless_borrow.rs:68:13
- |
-LL | let _ = (&s).capacity();
- | ^^^^ help: change this to: `s`
-
-error: this expression creates a reference which is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:69:13
- |
-LL | let _ = (&&s).capacity();
- | ^^^^^ help: change this to: `s`
-
error: this expression borrows a value the compiler would automatically borrow
--> $DIR/needless_borrow.rs:72:13
|
LL | let _ = unsafe { (&*x).0 };
| ^^^^^ help: change this to: `(*x)`
-error: aborting due to 19 previous errors
+error: aborting due to 16 previous errors
// No error for types behind an alias (#7699)
type A = Vec<u8>;
fn aliased(a: &A) {}
+
+// Issue #8366
+pub trait Trait {
+ fn f(v: &mut Vec<i32>);
+ fn f2(v: &mut Vec<i32>) {}
+}
};
}
+// See: issue #8282
+fn ranges() {
+ enum E {
+ V,
+ }
+ let x = (Some(E::V), Some(42));
+
+ // Don't lint, because the `E` enum can be extended with additional fields later. Thus, the
+ // proposed replacement to `if let Some(E::V)` may hide non-exhaustive warnings that appeared
+ // because of `match` construction.
+ match x {
+ (Some(E::V), _) => {},
+ (None, _) => {},
+ }
+
+ // lint
+ match x {
+ (Some(_), _) => {},
+ (None, _) => {},
+ }
+
+ // lint
+ match x {
+ (Some(E::V), _) => todo!(),
+ (_, _) => {},
+ }
+
+ // lint
+ match (Some(42), Some(E::V), Some(42)) {
+ (.., Some(E::V), _) => {},
+ (..) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (.., Some(E::V), _) => {},
+ (.., None, _) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (Some(E::V), ..) => {},
+ (None, ..) => {},
+ }
+
+ // Don't lint, see above.
+ match (Some(E::V), Some(E::V), Some(E::V)) {
+ (_, Some(E::V), ..) => {},
+ (_, None, ..) => {},
+ }
+}
+
+fn skip_type_aliases() {
+ enum OptionEx {
+ Some(i32),
+ None,
+ }
+ enum ResultEx {
+ Err(i32),
+ Ok(i32),
+ }
+
+ use OptionEx::{None, Some};
+ use ResultEx::{Err, Ok};
+
+ // don't lint
+ match Err(42) {
+ Ok(_) => dummy(),
+ Err(_) => (),
+ };
+
+ // don't lint
+ match Some(1i32) {
+ Some(_) => dummy(),
+ None => (),
+ };
+}
+
macro_rules! single_match {
($num:literal) => {
match $num {
LL | | };
| |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
-error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match.rs:54:5
- |
-LL | / match x {
-LL | | Some(y) => dummy(),
-LL | | None => (),
-LL | | };
- | |_____^ help: try this: `if let Some(y) = x { dummy() }`
-
error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
--> $DIR/single_match.rs:59:5
|
LL | | };
| |_____^ help: try this: `if let None = x { println!() }`
-error: aborting due to 13 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:164:5
+ |
+LL | / match x {
+LL | | (Some(_), _) => {},
+LL | | (None, _) => {},
+LL | | }
+ | |_____^ help: try this: `if let (Some(_), _) = x {}`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:170:5
+ |
+LL | / match x {
+LL | | (Some(E::V), _) => todo!(),
+LL | | (_, _) => {},
+LL | | }
+ | |_____^ help: try this: `if let (Some(E::V), _) = x { todo!() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+ --> $DIR/single_match.rs:176:5
+ |
+LL | / match (Some(42), Some(E::V), Some(42)) {
+LL | | (.., Some(E::V), _) => {},
+LL | | (..) => {},
+LL | | }
+ | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}`
+
+error: aborting due to 15 previous errors
LL + }
|
-error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match_else.rs:70:5
- |
-LL | / match Some(1) {
-LL | | Some(a) => println!("${:?}", a),
-LL | | None => {
-LL | | println!("else block");
-LL | | return
-LL | | },
-LL | | }
- | |_____^
- |
-help: try this
- |
-LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else {
-LL + println!("else block");
-LL + return
-LL + }
- |
-
-error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
- --> $DIR/single_match_else.rs:79:5
- |
-LL | / match Some(1) {
-LL | | Some(a) => println!("${:?}", a),
-LL | | None => {
-LL | | println!("else block");
-LL | | return;
-LL | | },
-LL | | }
- | |_____^
- |
-help: try this
- |
-LL ~ if let Some(a) = Some(1) { println!("${:?}", a) } else {
-LL + println!("else block");
-LL + return;
-LL + }
- |
-
-error: aborting due to 3 previous errors
+error: aborting due to previous error
fn starts_with() {
"".starts_with(' ');
!"".starts_with(' ');
+
+ // Ensure that suggestion is escaped correctly
+ "".starts_with('\n');
+ !"".starts_with('\n');
}
fn chars_cmp_with_unwrap() {
// !s.ends_with('o')
// Nothing here
}
- if !s.ends_with('o') {
+ if !s.ends_with('\n') {
// !s.ends_with('o')
// Nothing here
}
!"".ends_with(' ');
"".ends_with(' ');
!"".ends_with(' ');
+
+ // Ensure that suggestion is escaped correctly
+ "".ends_with('\n');
+ !"".ends_with('\n');
}
fn starts_with() {
"".chars().next() == Some(' ');
Some(' ') != "".chars().next();
+
+ // Ensure that suggestion is escaped correctly
+ "".chars().next() == Some('\n');
+ Some('\n') != "".chars().next();
}
fn chars_cmp_with_unwrap() {
// !s.ends_with('o')
// Nothing here
}
- if s.chars().last().unwrap() != 'o' {
+ if s.chars().last().unwrap() != '\n' {
// !s.ends_with('o')
// Nothing here
}
Some(' ') != "".chars().last();
"".chars().next_back() == Some(' ');
Some(' ') != "".chars().next_back();
+
+ // Ensure that suggestion is escaped correctly
+ "".chars().last() == Some('\n');
+ Some('\n') != "".chars().last();
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with(' ')`
error: you should use the `starts_with` method
- --> $DIR/starts_ends_with.rs:14:8
+ --> $DIR/starts_ends_with.rs:12:5
+ |
+LL | "".chars().next() == Some('/n');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".starts_with('/n')`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:13:5
+ |
+LL | Some('/n') != "".chars().next();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".starts_with('/n')`
+
+error: you should use the `starts_with` method
+ --> $DIR/starts_ends_with.rs:18:8
|
LL | if s.chars().next().unwrap() == 'f' {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.starts_with('f')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:18:8
+ --> $DIR/starts_ends_with.rs:22:8
|
LL | if s.chars().next_back().unwrap() == 'o' {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
= note: `-D clippy::chars-last-cmp` implied by `-D warnings`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:22:8
+ --> $DIR/starts_ends_with.rs:26:8
|
LL | if s.chars().last().unwrap() == 'o' {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `s.ends_with('o')`
error: you should use the `starts_with` method
- --> $DIR/starts_ends_with.rs:26:8
+ --> $DIR/starts_ends_with.rs:30:8
|
LL | if s.chars().next().unwrap() != 'f' {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.starts_with('f')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:30:8
+ --> $DIR/starts_ends_with.rs:34:8
|
LL | if s.chars().next_back().unwrap() != 'o' {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:34:8
+ --> $DIR/starts_ends_with.rs:38:8
|
-LL | if s.chars().last().unwrap() != 'o' {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('o')`
+LL | if s.chars().last().unwrap() != '/n' {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!s.ends_with('/n')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:42:5
+ --> $DIR/starts_ends_with.rs:46:5
|
LL | "".chars().last() == Some(' ');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:43:5
+ --> $DIR/starts_ends_with.rs:47:5
|
LL | Some(' ') != "".chars().last();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:44:5
+ --> $DIR/starts_ends_with.rs:48:5
|
LL | "".chars().next_back() == Some(' ');
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with(' ')`
error: you should use the `ends_with` method
- --> $DIR/starts_ends_with.rs:45:5
+ --> $DIR/starts_ends_with.rs:49:5
|
LL | Some(' ') != "".chars().next_back();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with(' ')`
-error: aborting due to 12 previous errors
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:52:5
+ |
+LL | "".chars().last() == Some('/n');
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `"".ends_with('/n')`
+
+error: you should use the `ends_with` method
+ --> $DIR/starts_ends_with.rs:53:5
+ |
+LL | Some('/n') != "".chars().last();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: like this: `!"".ends_with('/n')`
+
+error: aborting due to 16 previous errors
--- /dev/null
+#![warn(clippy::transmute_undefined_repr)]
+#![allow(clippy::unit_arg)]
+
+fn value<T>() -> T {
+ unimplemented!()
+}
+
+struct Empty;
+struct Ty<T>(T);
+struct Ty2<T, U>(T, U);
+
+#[repr(C)]
+struct Ty2C<T, U>(T, U);
+
+fn main() {
+ unsafe {
+ let _: () = core::mem::transmute(value::<Empty>());
+ let _: Empty = core::mem::transmute(value::<()>());
+
+ let _: Ty<u32> = core::mem::transmute(value::<u32>());
+ let _: Ty<u32> = core::mem::transmute(value::<u32>());
+
+ let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
+ let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
+
+ let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
+ let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
+
+ let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
+
+ let _: Ty<&()> = core::mem::transmute(value::<&()>());
+ let _: &() = core::mem::transmute(value::<Ty<&()>>());
+
+ let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
+
+ let _: Ty<usize> = core::mem::transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
+ let _: &Ty2<u32, i32> = core::mem::transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
+
+ let _: Ty<[u8; 8]> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
+ let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
+ }
+}
--- /dev/null
+error: transmute from `Ty2<u32, i32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:23:33
+ |
+LL | let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
+
+error: transmute into `Ty2<u32, i32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:24:32
+ |
+LL | let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:29:32
+ |
+LL | let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:30:36
+ |
+LL | let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: two instances of the same generic type (`Ty2`) may have different layouts
+
+error: transmute to `&Ty2<u32, f32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:35:33
+ |
+LL | let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: transmute from `&Ty2<u32, f32>` which has an undefined layout
+ --> $DIR/transmute_undefined_repr.rs:36:37
+ |
+LL | let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
+++ /dev/null
-#[warn(clippy::eq_op)]
-#[test]
-fn eq_op_shouldnt_trigger_in_tests() {
- let a = 1;
- let result = a + 1 == 1 + a;
- assert!(result);
-}
-
-#[test]
-fn eq_op_macros_shouldnt_trigger_in_tests() {
- let a = 1;
- let b = 2;
- assert_eq!(a, a);
- assert_eq!(a + b, b + a);
-}
name,
ignore,
should_panic,
- allow_fail: false,
compile_fail: false,
no_run: false,
test_type: test::TestType::Unknown,
+ #[cfg(bootstrap)]
+ allow_fail: false,
}
}
);
if !res.status.success() {
- self.fatal_proc_rec("jsondocck failed!", &res)
+ self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| {
+ println!("Rustdoc Output:");
+ proc_res.print_info();
+ })
}
let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
}
impl ProcRes {
- pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
- if let Some(e) = err {
- println!("\nerror: {}", e);
- }
+ pub fn print_info(&self) {
print!(
"\
status: {}\n\
json::extract_rendered(&self.stdout),
json::extract_rendered(&self.stderr),
);
+ }
+
+ pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
+ if let Some(e) = err {
+ println!("\nerror: {}", e);
+ }
+ self.print_info();
on_failure();
// Use resume_unwind instead of panic!() to prevent a panic message + backtrace from
// compiletest, which is unnecessary noise.
let val = cache.get_value(&command.args[0])?;
let results = select(&val, &command.args[1]).unwrap();
- results.len() == expected
+ let eq = results.len() == expected;
+ if !command.negated && !eq {
+ return Err(CkError::FailedCheck(
+ format!(
+ "`{}` matched to `{:?}` with length {}, but expected length {}",
+ &command.args[1],
+ results,
+ results.len(),
+ expected
+ ),
+ command,
+ ));
+ } else {
+ eq
+ }
}
CommandKind::Is => {
// @has <path> <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables)
}))
} else {
- Cow::Owned(serde_json::from_str(s).unwrap())
+ Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s)))
}
}
fn walk(&mut self, dir: &Path, report: &mut Report) {
for entry in t!(dir.read_dir()).map(|e| t!(e)) {
let path = entry.path();
- let kind = t!(entry.file_type());
- if kind.is_dir() {
+ // Goes through symlinks
+ let metadata = t!(fs::metadata(&path));
+ if metadata.is_dir() {
self.walk(&path, report);
} else {
self.check(&path, report);
-Subproject commit deb9bfd24648d50142ab29b810175837c4718885
+Subproject commit 0db40903769f38669936c5ebb0b882b18c27f449
-Subproject commit f37425e33c864c697af06df66e7473444605c149
+Subproject commit 3df74381f37617ec800537c11fb0c3130f5f3616
-Subproject commit 9700addc82111200a2150b9a796f62dd8e600ddf
+Subproject commit 02904e99acc3daf39b56ed18aa07e62aeb9492c5
var testFileContent = readFile(testFile) + 'exports.QUERY = QUERY;exports.EXPECTED = EXPECTED;';
if (testFileContent.indexOf("FILTER_CRATE") !== -1) {
testFileContent += "exports.FILTER_CRATE = FILTER_CRATE;";
+ } else {
+ testFileContent += "exports.FILTER_CRATE = null;";
}
var loadedFile = loadContent(testFileContent);
autobins = false
[dependencies]
-cargo_metadata = "0.12"
+cargo_metadata = "0.14"
regex = "1"
lazy_static = "1"
walkdir = "2"
"chrono",
"cmake",
"compiler_builtins",
- "cpuid-bool",
+ "cpufeatures",
"crc32fast",
"crossbeam-deque",
"crossbeam-epoch",
"crossbeam-utils",
+ "crypto-common",
"cstr",
"datafrog",
"difference",
"scoped-tls",
"scopeguard",
"semver",
- "semver-parser",
"serde",
"serde_derive",
"serde_json",
//! Checks that all error codes have at least one test to prevent having error
//! codes that are silently not thrown by the compiler anymore.
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
use std::ffi::OsStr;
use std::fs::read_to_string;
use std::path::Path;
let mut found_explanations = 0;
let mut found_tests = 0;
let mut error_codes: HashMap<String, ErrorCodeStatus> = HashMap::new();
+ let mut explanations: HashSet<String> = HashSet::new();
// We want error codes which match the following cases:
//
// * foo(a, E0111, a)
for path in paths {
super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
let file_name = entry.file_name();
+ let entry_path = entry.path();
+
if file_name == "error_codes.rs" {
extract_error_codes(contents, &mut error_codes, entry.path(), &mut errors);
found_explanations += 1;
- } else if entry.path().extension() == Some(OsStr::new("stderr")) {
+ } else if entry_path.extension() == Some(OsStr::new("stderr")) {
extract_error_codes_from_tests(contents, &mut error_codes);
found_tests += 1;
- } else if entry.path().extension() == Some(OsStr::new("rs")) {
+ } else if entry_path.extension() == Some(OsStr::new("rs")) {
let path = entry.path().to_string_lossy();
if PATHS_TO_IGNORE_FOR_EXTRACTION.iter().all(|c| !path.contains(c)) {
extract_error_codes_from_source(contents, &mut error_codes, ®ex);
}
+ } else if entry_path
+ .parent()
+ .and_then(|p| p.file_name())
+ .map(|p| p == "error_codes")
+ .unwrap_or(false)
+ && entry_path.extension() == Some(OsStr::new("md"))
+ {
+ explanations.insert(file_name.to_str().unwrap().replace(".md", ""));
}
});
}
eprintln!("No error code was found in compilation errors!");
*bad = true;
}
+ if explanations.is_empty() {
+ eprintln!("No error code explanation was found!");
+ *bad = true;
+ }
if errors.is_empty() {
println!("Found {} error codes", error_codes.len());
}
}
}
+ if errors.is_empty() {
+ for explanation in explanations {
+ if !error_codes.contains_key(&explanation) {
+ errors.push(format!(
+ "{} error code explanation should be listed in `error_codes.rs`",
+ explanation
+ ));
+ }
+ }
+ }
errors.sort();
for err in &errors {
eprintln!("{}", err);
}
- println!("Found {} error codes with no tests", errors.len());
+ println!("Found {} error(s) in error codes", errors.len());
if !errors.is_empty() {
*bad = true;
}
"src/tools/rustdoc-js",
"src/tools/rustdoc-themes",
]
+exclude_labels = [
+ "T-*",
+]
[autolabel."T-compiler"]
trigger_files = [
# Tests
"src/test/ui",
]
+exclude_labels = [
+ "T-*",
+]
[notify-zulip."I-prioritize"]
zulip_stream = 245100 # #t-compiler/wg-prioritization/alerts