Merge from rustc
This commit is contained in:
commit
e898da11d2
495 changed files with 5342 additions and 1962 deletions
134
Cargo.lock
134
Cargo.lock
|
|
@ -182,9 +182,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.94"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
]
|
||||
|
|
@ -367,7 +367,7 @@ dependencies = [
|
|||
name = "cargo-miri"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.18.1",
|
||||
"directories",
|
||||
"rustc-build-sysroot",
|
||||
"rustc_tools_util",
|
||||
|
|
@ -399,15 +399,29 @@ dependencies = [
|
|||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargo_metadata"
|
||||
version = "0.19.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924"
|
||||
dependencies = [
|
||||
"camino",
|
||||
"cargo-platform",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cargotest2"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.5"
|
||||
version = "1.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
|
||||
checksum = "8d6dbb628b8f8555f86d0323c2eb39e3ec81901f4b83e091db8a6a76d316a333"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
|
@ -519,7 +533,7 @@ dependencies = [
|
|||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -533,7 +547,7 @@ name = "clippy"
|
|||
version = "0.1.85"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.18.1",
|
||||
"clippy_config",
|
||||
"clippy_lints",
|
||||
"clippy_utils",
|
||||
|
|
@ -550,7 +564,7 @@ dependencies = [
|
|||
"rustc_tools_util",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"tempfile",
|
||||
"termize",
|
||||
"tokio",
|
||||
|
|
@ -589,7 +603,7 @@ name = "clippy_lints"
|
|||
version = "0.1.85"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.18.1",
|
||||
"clippy_config",
|
||||
"clippy_utils",
|
||||
"itertools",
|
||||
|
|
@ -660,7 +674,7 @@ dependencies = [
|
|||
"nom",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -885,7 +899,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -896,7 +910,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806"
|
|||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -933,7 +947,7 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -954,7 +968,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -964,7 +978,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -976,7 +990,7 @@ dependencies = [
|
|||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1054,7 +1068,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1351,7 +1365,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1389,7 +1403,7 @@ name = "generate-copyright"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.18.1",
|
||||
"rinja",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
@ -1463,9 +1477,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
|
|
@ -1585,7 +1599,7 @@ dependencies = [
|
|||
"markup5ever",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1774,7 +1788,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2730,7 +2744,7 @@ dependencies = [
|
|||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2952,9 +2966,9 @@ checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.37"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
|
@ -3140,7 +3154,7 @@ dependencies = [
|
|||
"rinja_parser",
|
||||
"rustc-hash 2.1.0",
|
||||
"serde",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -3780,7 +3794,7 @@ dependencies = [
|
|||
"fluent-syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
|
|
@ -3915,7 +3929,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4063,7 +4077,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -4651,7 +4665,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -4740,7 +4754,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -4750,7 +4764,7 @@ dependencies = [
|
|||
"annotate-snippets 0.9.2",
|
||||
"anyhow",
|
||||
"bytecount",
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.18.1",
|
||||
"clap",
|
||||
"clap-cargo",
|
||||
"diff",
|
||||
|
|
@ -4787,9 +4801,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248"
|
||||
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
||||
|
||||
[[package]]
|
||||
name = "ruzstd"
|
||||
|
|
@ -4862,22 +4876,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.216"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e"
|
||||
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.216"
|
||||
version = "1.0.217"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e"
|
||||
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5135,9 +5149,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.90"
|
||||
version = "2.0.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
|
||||
checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -5152,7 +5166,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5294,7 +5308,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5305,7 +5319,7 @@ checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5344,7 +5358,7 @@ name = "tidy"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"build_helper",
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.19.1",
|
||||
"fluent-syntax",
|
||||
"ignore",
|
||||
"miropt-test-tools",
|
||||
|
|
@ -5506,7 +5520,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -5622,7 +5636,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"bstr",
|
||||
"cargo-platform",
|
||||
"cargo_metadata",
|
||||
"cargo_metadata 0.18.1",
|
||||
"color-eyre",
|
||||
"colored",
|
||||
"comma",
|
||||
|
|
@ -5677,15 +5691,15 @@ checksum = "1ed7f4237ba393424195053097c1516bd4590dc82b84f2f97c5c69e12704555b"
|
|||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"unic-langid-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.8.0"
|
||||
version = "2.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
|
||||
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
|
|
@ -5883,7 +5897,7 @@ dependencies = [
|
|||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
|
|
@ -5905,7 +5919,7 @@ checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
|
@ -6087,7 +6101,7 @@ dependencies = [
|
|||
"rayon",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"windows-metadata",
|
||||
]
|
||||
|
||||
|
|
@ -6120,7 +6134,7 @@ checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6131,7 +6145,7 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6410,7 +6424,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6432,7 +6446,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6452,7 +6466,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
|
|
@ -6475,5 +6489,5 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
"syn 2.0.93",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -213,7 +213,9 @@ impl<'a> State<'a> {
|
|||
|
||||
fn print_expr_call(&mut self, func: &ast::Expr, args: &[P<ast::Expr>], fixup: FixupContext) {
|
||||
let needs_paren = match func.kind {
|
||||
ast::ExprKind::Field(..) => true,
|
||||
// In order to call a named field, needs parens: `(self.fun)()`
|
||||
// But not for an unnamed field: `self.0()`
|
||||
ast::ExprKind::Field(_, name) => !name.is_numeric(),
|
||||
_ => func.precedence() < ExprPrecedence::Unambiguous,
|
||||
};
|
||||
|
||||
|
|
@ -245,19 +247,21 @@ impl<'a> State<'a> {
|
|||
base_args: &[P<ast::Expr>],
|
||||
fixup: FixupContext,
|
||||
) {
|
||||
// Unlike in `print_expr_call`, no change to fixup here because
|
||||
// The fixup here is different than in `print_expr_call` because
|
||||
// statement boundaries never occur in front of a `.` (or `?`) token.
|
||||
//
|
||||
// match () { _ => f }.method();
|
||||
// Needs parens:
|
||||
//
|
||||
// (loop { break x; })();
|
||||
//
|
||||
// Does not need parens:
|
||||
//
|
||||
// loop { break x; }.method();
|
||||
//
|
||||
// Parenthesizing only for precedence and not with regard to statement
|
||||
// boundaries, `$receiver.method()` can be parsed back as a statement
|
||||
// containing an expression if and only if `$receiver` can be parsed as
|
||||
// a statement containing an expression.
|
||||
self.print_expr_cond_paren(
|
||||
receiver,
|
||||
receiver.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
|
||||
self.word(".");
|
||||
|
|
@ -503,7 +507,7 @@ impl<'a> State<'a> {
|
|||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
self.word_nbsp(".match");
|
||||
}
|
||||
|
|
@ -567,7 +571,7 @@ impl<'a> State<'a> {
|
|||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
self.word(".await");
|
||||
}
|
||||
|
|
@ -606,7 +610,7 @@ impl<'a> State<'a> {
|
|||
self.print_expr_cond_paren(
|
||||
expr,
|
||||
expr.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
self.word(".");
|
||||
self.print_ident(*ident);
|
||||
|
|
@ -763,7 +767,11 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
ast::ExprKind::Try(e) => {
|
||||
self.print_expr_cond_paren(e, e.precedence() < ExprPrecedence::Unambiguous, fixup);
|
||||
self.print_expr_cond_paren(
|
||||
e,
|
||||
e.precedence() < ExprPrecedence::Unambiguous,
|
||||
fixup.leftmost_subexpression_with_dot(),
|
||||
);
|
||||
self.word("?")
|
||||
}
|
||||
ast::ExprKind::TryBlock(blk) => {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,20 @@ impl FixupContext {
|
|||
}
|
||||
}
|
||||
|
||||
/// Transform this fixup into the one that should apply when printing a
|
||||
/// leftmost subexpression followed by a `.` or `?` token, which confer
|
||||
/// different statement boundary rules compared to other leftmost
|
||||
/// subexpressions.
|
||||
pub(crate) fn leftmost_subexpression_with_dot(self) -> Self {
|
||||
FixupContext {
|
||||
stmt: self.stmt || self.leftmost_subexpression_in_stmt,
|
||||
leftmost_subexpression_in_stmt: false,
|
||||
match_arm: self.match_arm || self.leftmost_subexpression_in_match_arm,
|
||||
leftmost_subexpression_in_match_arm: false,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Transform this fixup into the one that should apply when printing any
|
||||
/// subexpression that is neither a leftmost subexpression nor surrounded in
|
||||
/// delimiters.
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ use rustc_middle::ty::TyCtxt;
|
|||
pub use super::borrow_set::{BorrowData, BorrowSet, TwoPhaseActivation};
|
||||
pub use super::constraints::OutlivesConstraint;
|
||||
pub use super::dataflow::{BorrowIndex, Borrows, calculate_borrows_out_of_scope_at_location};
|
||||
pub use super::facts::{AllFacts as PoloniusInput, PoloniusRegionVid, RustcFacts};
|
||||
pub use super::location::{LocationTable, RichLocation};
|
||||
pub use super::nll::PoloniusOutput;
|
||||
pub use super::place_ext::PlaceExt;
|
||||
pub use super::places_conflict::{PlaceConflictBias, places_conflict};
|
||||
pub use super::polonius::legacy::{
|
||||
AllFacts as PoloniusInput, LocationTable, PoloniusOutput, PoloniusRegionVid, RichLocation,
|
||||
RustcFacts,
|
||||
};
|
||||
pub use super::region_infer::RegionInferenceContext;
|
||||
|
||||
/// Options determining the output behavior of [`get_body_with_borrowck_facts`].
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
//! Borrow checker diagnostics.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_abi::{FieldIdx, VariantIdx};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{Applicability, Diag, MultiSpan};
|
||||
use rustc_hir::def::{CtorKind, Namespace};
|
||||
use rustc_hir::{self as hir, CoroutineKind, LangItem};
|
||||
|
|
@ -17,10 +20,10 @@ use rustc_middle::mir::{
|
|||
use rustc_middle::ty::print::Print;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_middle::util::{CallDesugaringKind, call_kind};
|
||||
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult};
|
||||
use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
|
||||
use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
|
|
@ -68,6 +71,126 @@ pub(super) struct DescribePlaceOpt {
|
|||
|
||||
pub(super) struct IncludingTupleField(pub(super) bool);
|
||||
|
||||
enum BufferedDiag<'infcx> {
|
||||
Error(Diag<'infcx>),
|
||||
NonError(Diag<'infcx, ()>),
|
||||
}
|
||||
|
||||
impl<'infcx> BufferedDiag<'infcx> {
|
||||
fn sort_span(&self) -> Span {
|
||||
match self {
|
||||
BufferedDiag::Error(diag) => diag.sort_span,
|
||||
BufferedDiag::NonError(diag) => diag.sort_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct BorrowckDiagnosticsBuffer<'infcx, '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>, Diag<'infcx>)>,
|
||||
|
||||
buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
|
||||
|
||||
/// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
|
||||
buffered_diags: Vec<BufferedDiag<'infcx>>,
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> BorrowckDiagnosticsBuffer<'infcx, 'tcx> {
|
||||
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
|
||||
self.buffered_diags.push(BufferedDiag::NonError(diag));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
|
||||
self.diags_buffer.buffered_diags.push(BufferedDiag::Error(diag));
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
|
||||
self.diags_buffer.buffer_non_error(diag);
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_move_error(
|
||||
&mut self,
|
||||
move_out_indices: Vec<MoveOutIndex>,
|
||||
place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
|
||||
) -> bool {
|
||||
if let Some((_, diag)) =
|
||||
self.diags_buffer.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(crate) fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
self.diags_buffer.buffered_mut_errors.swap_remove(&span)
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
|
||||
self.diags_buffer.buffered_mut_errors.insert(span, (diag, count));
|
||||
}
|
||||
|
||||
pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let mut res = self.infcx.tainted_by_errors();
|
||||
|
||||
// Buffer any move errors that we collected and de-duplicated.
|
||||
for (_, (_, diag)) in std::mem::take(&mut self.diags_buffer.buffered_move_errors) {
|
||||
// We have already set tainted for this error, so just buffer it.
|
||||
self.buffer_error(diag);
|
||||
}
|
||||
for (_, (mut diag, count)) in std::mem::take(&mut self.diags_buffer.buffered_mut_errors) {
|
||||
if count > 10 {
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
diag.note(format!("...and {} other attempted mutable borrows", count - 10));
|
||||
}
|
||||
self.buffer_error(diag);
|
||||
}
|
||||
|
||||
if !self.diags_buffer.buffered_diags.is_empty() {
|
||||
self.diags_buffer.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
|
||||
for buffered_diag in self.diags_buffer.buffered_diags.drain(..) {
|
||||
match buffered_diag {
|
||||
BufferedDiag::Error(diag) => res = Some(diag.emit()),
|
||||
BufferedDiag::NonError(diag) => diag.emit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn has_buffered_diags(&self) -> bool {
|
||||
self.diags_buffer.buffered_diags.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn has_move_error(
|
||||
&self,
|
||||
move_out_indices: &[MoveOutIndex],
|
||||
) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
|
||||
self.diags_buffer.buffered_move_errors.get(move_out_indices)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
/// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
|
||||
/// is moved after being invoked.
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
diag: &mut Diag<'_>,
|
||||
) {
|
||||
// We look at all the locals. Why locals? Because it's the best thing
|
||||
// I could think of that's correlated with the *instantiated* higer-ranked
|
||||
// I could think of that's correlated with the *instantiated* higher-ranked
|
||||
// binder for calls, since we don't really store those anywhere else.
|
||||
for ty in self.body.local_decls.iter().map(|local| local.ty) {
|
||||
if !ty.has_opaque_types() {
|
||||
|
|
|
|||
|
|
@ -16,14 +16,12 @@
|
|||
// tidy-alphabetical-end
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_index::bit_set::{BitSet, MixedBitSet};
|
||||
|
|
@ -41,7 +39,7 @@ use rustc_mir_dataflow::impls::{
|
|||
EverInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces,
|
||||
};
|
||||
use rustc_mir_dataflow::move_paths::{
|
||||
InitIndex, InitLocation, LookupResult, MoveData, MoveOutIndex, MovePathIndex,
|
||||
InitIndex, InitLocation, LookupResult, MoveData, MovePathIndex,
|
||||
};
|
||||
use rustc_mir_dataflow::{Analysis, EntryStates, Results, ResultsVisitor, visit_results};
|
||||
use rustc_session::lint::builtin::UNUSED_MUT;
|
||||
|
|
@ -52,12 +50,13 @@ use tracing::{debug, instrument};
|
|||
use crate::borrow_set::{BorrowData, BorrowSet};
|
||||
use crate::consumers::{BodyWithBorrowckFacts, ConsumerOptions};
|
||||
use crate::dataflow::{BorrowIndex, Borrowck, BorrowckDomain, Borrows};
|
||||
use crate::diagnostics::{AccessKind, IllegalMoveOriginKind, MoveError, RegionName};
|
||||
use crate::location::LocationTable;
|
||||
use crate::nll::PoloniusOutput;
|
||||
use crate::diagnostics::{
|
||||
AccessKind, BorrowckDiagnosticsBuffer, IllegalMoveOriginKind, MoveError, RegionName,
|
||||
};
|
||||
use crate::path_utils::*;
|
||||
use crate::place_ext::PlaceExt;
|
||||
use crate::places_conflict::{PlaceConflictBias, places_conflict};
|
||||
use crate::polonius::legacy::{LocationTable, PoloniusOutput};
|
||||
use crate::prefixes::PrefixSet;
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::renumber::RegionCtxt;
|
||||
|
|
@ -69,8 +68,6 @@ mod constraints;
|
|||
mod dataflow;
|
||||
mod def_use;
|
||||
mod diagnostics;
|
||||
mod facts;
|
||||
mod location;
|
||||
mod member_constraints;
|
||||
mod nll;
|
||||
mod path_utils;
|
||||
|
|
@ -120,11 +117,10 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
|
|||
return tcx.arena.alloc(result);
|
||||
}
|
||||
|
||||
let promoted: &IndexSlice<_, _> = &promoted.borrow();
|
||||
let opt_closure_req = do_mir_borrowck(tcx, input_body, promoted, None).0;
|
||||
let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
|
||||
debug!("mir_borrowck done");
|
||||
|
||||
tcx.arena.alloc(opt_closure_req)
|
||||
tcx.arena.alloc(borrowck_result)
|
||||
}
|
||||
|
||||
/// Perform the actual borrow checking.
|
||||
|
|
@ -215,14 +211,21 @@ fn do_mir_borrowck<'tcx>(
|
|||
consumer_options,
|
||||
);
|
||||
|
||||
// Dump MIR results into a file, if that is enabled. This let us
|
||||
// Dump MIR results into a file, if that is enabled. This lets us
|
||||
// write unit-tests, as well as helping with debugging.
|
||||
nll::dump_nll_mir(&infcx, body, ®ioncx, &opt_closure_req, &borrow_set);
|
||||
|
||||
// We also have a `#[rustc_regions]` annotation that causes us to dump
|
||||
// information.
|
||||
let diags = &mut diags::BorrowckDiags::new();
|
||||
nll::dump_annotation(&infcx, body, ®ioncx, &opt_closure_req, &opaque_type_values, diags);
|
||||
let diags_buffer = &mut BorrowckDiagnosticsBuffer::default();
|
||||
nll::dump_annotation(
|
||||
&infcx,
|
||||
body,
|
||||
®ioncx,
|
||||
&opt_closure_req,
|
||||
&opaque_type_values,
|
||||
diags_buffer,
|
||||
);
|
||||
|
||||
let movable_coroutine =
|
||||
// The first argument is the coroutine type passed by value
|
||||
|
|
@ -261,7 +264,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
next_region_name: RefCell::new(1),
|
||||
polonius_output: None,
|
||||
move_errors: Vec::new(),
|
||||
diags,
|
||||
diags_buffer,
|
||||
};
|
||||
MoveVisitor { ctxt: &mut promoted_mbcx }.visit_body(promoted_body);
|
||||
promoted_mbcx.report_move_errors();
|
||||
|
|
@ -300,7 +303,7 @@ fn do_mir_borrowck<'tcx>(
|
|||
next_region_name: RefCell::new(1),
|
||||
polonius_output,
|
||||
move_errors: Vec::new(),
|
||||
diags,
|
||||
diags_buffer,
|
||||
};
|
||||
|
||||
// Compute and report region errors, if any.
|
||||
|
|
@ -570,7 +573,7 @@ struct MirBorrowckCtxt<'a, 'infcx, 'tcx> {
|
|||
/// Results of Polonius analysis.
|
||||
polonius_output: Option<Box<PoloniusOutput>>,
|
||||
|
||||
diags: &'a mut diags::BorrowckDiags<'infcx, 'tcx>,
|
||||
diags_buffer: &'a mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
|
||||
move_errors: Vec<MoveError<'tcx>>,
|
||||
}
|
||||
|
||||
|
|
@ -2403,146 +2406,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
mod diags {
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
|
||||
use super::*;
|
||||
|
||||
enum BufferedDiag<'infcx> {
|
||||
Error(Diag<'infcx>),
|
||||
NonError(Diag<'infcx, ()>),
|
||||
}
|
||||
|
||||
impl<'infcx> BufferedDiag<'infcx> {
|
||||
fn sort_span(&self) -> Span {
|
||||
match self {
|
||||
BufferedDiag::Error(diag) => diag.sort_span,
|
||||
BufferedDiag::NonError(diag) => diag.sort_span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct BorrowckDiags<'infcx, '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>, Diag<'infcx>)>,
|
||||
|
||||
buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
|
||||
|
||||
/// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
|
||||
buffered_diags: Vec<BufferedDiag<'infcx>>,
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> BorrowckDiags<'infcx, 'tcx> {
|
||||
pub(crate) fn new() -> Self {
|
||||
BorrowckDiags {
|
||||
buffered_move_errors: BTreeMap::new(),
|
||||
buffered_mut_errors: Default::default(),
|
||||
buffered_diags: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
|
||||
self.buffered_diags.push(BufferedDiag::Error(diag));
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
|
||||
self.buffered_diags.push(BufferedDiag::NonError(diag));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
||||
pub(crate) fn buffer_error(&mut self, diag: Diag<'infcx>) {
|
||||
self.diags.buffer_error(diag);
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
|
||||
self.diags.buffer_non_error(diag);
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_move_error(
|
||||
&mut self,
|
||||
move_out_indices: Vec<MoveOutIndex>,
|
||||
place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
|
||||
) -> bool {
|
||||
if let Some((_, diag)) =
|
||||
self.diags.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(crate) fn get_buffered_mut_error(
|
||||
&mut self,
|
||||
span: Span,
|
||||
) -> Option<(Diag<'infcx>, usize)> {
|
||||
// FIXME(#120456) - is `swap_remove` correct?
|
||||
self.diags.buffered_mut_errors.swap_remove(&span)
|
||||
}
|
||||
|
||||
pub(crate) fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
|
||||
self.diags.buffered_mut_errors.insert(span, (diag, count));
|
||||
}
|
||||
|
||||
pub(crate) fn emit_errors(&mut self) -> Option<ErrorGuaranteed> {
|
||||
let mut res = self.infcx.tainted_by_errors();
|
||||
|
||||
// Buffer any move errors that we collected and de-duplicated.
|
||||
for (_, (_, diag)) in std::mem::take(&mut self.diags.buffered_move_errors) {
|
||||
// We have already set tainted for this error, so just buffer it.
|
||||
self.diags.buffer_error(diag);
|
||||
}
|
||||
for (_, (mut diag, count)) in std::mem::take(&mut self.diags.buffered_mut_errors) {
|
||||
if count > 10 {
|
||||
#[allow(rustc::diagnostic_outside_of_impl)]
|
||||
#[allow(rustc::untranslatable_diagnostic)]
|
||||
diag.note(format!("...and {} other attempted mutable borrows", count - 10));
|
||||
}
|
||||
self.diags.buffer_error(diag);
|
||||
}
|
||||
|
||||
if !self.diags.buffered_diags.is_empty() {
|
||||
self.diags.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span());
|
||||
for buffered_diag in self.diags.buffered_diags.drain(..) {
|
||||
match buffered_diag {
|
||||
BufferedDiag::Error(diag) => res = Some(diag.emit()),
|
||||
BufferedDiag::NonError(diag) => diag.emit(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn has_buffered_diags(&self) -> bool {
|
||||
self.diags.buffered_diags.is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn has_move_error(
|
||||
&self,
|
||||
move_out_indices: &[MoveOutIndex],
|
||||
) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
|
||||
self.diags.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
|
||||
|
|
|
|||
|
|
@ -26,17 +26,14 @@ use tracing::{debug, instrument};
|
|||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::consumers::ConsumerOptions;
|
||||
use crate::diagnostics::RegionErrors;
|
||||
use crate::facts::{AllFacts, AllFactsExt, RustcFacts};
|
||||
use crate::location::LocationTable;
|
||||
use crate::diagnostics::{BorrowckDiagnosticsBuffer, RegionErrors};
|
||||
use crate::polonius::LocalizedOutlivesConstraintSet;
|
||||
use crate::polonius::legacy::{AllFacts, AllFactsExt, LocationTable, PoloniusOutput};
|
||||
use crate::region_infer::RegionInferenceContext;
|
||||
use crate::type_check::{self, MirTypeckResults};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
use crate::{BorrowckInferCtxt, polonius, renumber};
|
||||
|
||||
pub type PoloniusOutput = Output<RustcFacts>;
|
||||
|
||||
/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any
|
||||
/// closure requirements to propagate, and any generated errors.
|
||||
pub(crate) struct NllOutput<'tcx> {
|
||||
|
|
@ -100,24 +97,28 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
let elements = Rc::new(DenseLocationMap::new(body));
|
||||
|
||||
// Run the MIR type-checker.
|
||||
let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } =
|
||||
type_check::type_check(
|
||||
infcx,
|
||||
body,
|
||||
promoted,
|
||||
universal_regions,
|
||||
location_table,
|
||||
borrow_set,
|
||||
&mut all_facts,
|
||||
flow_inits,
|
||||
move_data,
|
||||
Rc::clone(&elements),
|
||||
);
|
||||
let MirTypeckResults {
|
||||
constraints,
|
||||
universal_region_relations,
|
||||
opaque_type_values,
|
||||
mut polonius_context,
|
||||
} = type_check::type_check(
|
||||
infcx,
|
||||
body,
|
||||
promoted,
|
||||
universal_regions,
|
||||
location_table,
|
||||
borrow_set,
|
||||
&mut all_facts,
|
||||
flow_inits,
|
||||
move_data,
|
||||
Rc::clone(&elements),
|
||||
);
|
||||
|
||||
// Create the region inference context, taking ownership of the
|
||||
// region inference data that was contained in `infcx`, and the
|
||||
// base constraints generated by the type-check.
|
||||
let var_origins = infcx.get_region_var_origins();
|
||||
let var_infos = infcx.get_region_var_infos();
|
||||
|
||||
// If requested, emit legacy polonius facts.
|
||||
polonius::legacy::emit_facts(
|
||||
|
|
@ -133,7 +134,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
|
||||
let mut regioncx = RegionInferenceContext::new(
|
||||
infcx,
|
||||
var_origins,
|
||||
var_infos,
|
||||
constraints,
|
||||
universal_region_relations,
|
||||
elements,
|
||||
|
|
@ -141,12 +142,9 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
|
||||
// If requested for `-Zpolonius=next`, convert NLL constraints to localized outlives
|
||||
// constraints.
|
||||
let localized_outlives_constraints =
|
||||
if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
Some(polonius::create_localized_constraints(&mut regioncx, body))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let localized_outlives_constraints = polonius_context
|
||||
.as_mut()
|
||||
.map(|polonius_context| polonius_context.create_localized_constraints(&mut regioncx, body));
|
||||
|
||||
// If requested: dump NLL facts, and run legacy polonius analysis.
|
||||
let polonius_output = all_facts.as_ref().and_then(|all_facts| {
|
||||
|
|
@ -300,7 +298,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
|
|||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
|
||||
opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
|
||||
diags: &mut crate::diags::BorrowckDiags<'infcx, 'tcx>,
|
||||
diagnostics_buffer: &mut BorrowckDiagnosticsBuffer<'infcx, 'tcx>,
|
||||
) {
|
||||
let tcx = infcx.tcx;
|
||||
let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
|
||||
|
|
@ -346,7 +344,7 @@ pub(super) fn dump_annotation<'tcx, 'infcx>(
|
|||
err.note(format!("Inferred opaque type values:\n{opaque_type_values:#?}"));
|
||||
}
|
||||
|
||||
diags.buffer_non_error(err);
|
||||
diagnostics_buffer.buffer_non_error(err);
|
||||
}
|
||||
|
||||
fn for_each_region_constraint<'tcx>(
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@ use rustc_middle::ty::TyCtxt;
|
|||
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData};
|
||||
use tracing::debug;
|
||||
|
||||
use super::{AllFacts, LocationIndex, LocationTable};
|
||||
use crate::def_use::{self, DefUse};
|
||||
use crate::facts::AllFacts;
|
||||
use crate::location::{LocationIndex, LocationTable};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// Emit polonius facts for variable defs, uses, drops, and path accesses.
|
||||
|
|
|
|||
|
|
@ -4,18 +4,20 @@ use std::fs::{self, File};
|
|||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use polonius_engine::{AllFacts as PoloniusFacts, Atom};
|
||||
use polonius_engine::{AllFacts as PoloniusFacts, Atom, Output};
|
||||
use rustc_macros::extension;
|
||||
use rustc_middle::mir::Local;
|
||||
use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||
use rustc_mir_dataflow::move_paths::MovePathIndex;
|
||||
|
||||
use super::{LocationIndex, LocationTable};
|
||||
use crate::BorrowIndex;
|
||||
use crate::location::{LocationIndex, LocationTable};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct RustcFacts;
|
||||
|
||||
pub type PoloniusOutput = Output<RustcFacts>;
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// A (kinda) newtype of `RegionVid` so we can implement `Atom` on it.
|
||||
#[orderable]
|
||||
|
|
@ -246,6 +248,6 @@ impl FactCell for RegionVid {
|
|||
|
||||
impl FactCell for LocationIndex {
|
||||
fn to_string(&self, location_table: &LocationTable) -> String {
|
||||
format!("{:?}", location_table.to_location(*self))
|
||||
format!("{:?}", location_table.to_rich_location(*self))
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,8 @@ use rustc_middle::mir::{
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{AllFacts, LocationTable};
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::facts::AllFacts;
|
||||
use crate::location::LocationTable;
|
||||
use crate::path_utils::*;
|
||||
use crate::{
|
||||
AccessDepth, Activation, ArtificialField, BorrowIndex, Deep, LocalMutationIsAllowed, Read,
|
||||
|
|
|
|||
|
|
@ -6,9 +6,8 @@ use rustc_middle::mir::{
|
|||
use rustc_middle::ty::TyCtxt;
|
||||
use tracing::debug;
|
||||
|
||||
use super::{AllFacts, LocationTable};
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::facts::AllFacts;
|
||||
use crate::location::LocationTable;
|
||||
use crate::places_conflict;
|
||||
|
||||
/// Emit `loan_killed_at` and `cfg_edge` facts at the same time.
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ impl LocationTable {
|
|||
LocationIndex::from_usize(start_index + statement_index * 2 + 1)
|
||||
}
|
||||
|
||||
pub fn to_location(&self, index: LocationIndex) -> RichLocation {
|
||||
pub fn to_rich_location(&self, index: LocationIndex) -> RichLocation {
|
||||
let point_index = index.index();
|
||||
|
||||
// Find the basic block. We have a vector with the
|
||||
|
|
@ -97,6 +97,13 @@ impl LocationTable {
|
|||
RichLocation::Mid(Location { block, statement_index })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_location(&self, index: LocationIndex) -> Location {
|
||||
match self.to_rich_location(index) {
|
||||
RichLocation::Start(location) => location,
|
||||
RichLocation::Mid(location) => location,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LocationIndex {
|
||||
|
|
@ -13,8 +13,6 @@ use tracing::debug;
|
|||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::facts::{AllFacts, PoloniusRegionVid};
|
||||
use crate::location::LocationTable;
|
||||
use crate::type_check::MirTypeckRegionConstraints;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
|
@ -22,6 +20,10 @@ use crate::universal_regions::UniversalRegions;
|
|||
mod accesses;
|
||||
mod loan_invalidations;
|
||||
mod loan_kills;
|
||||
mod location;
|
||||
pub use self::location::*;
|
||||
mod facts;
|
||||
pub use self::facts::*;
|
||||
|
||||
/// When requested, emit most of the facts needed by polonius:
|
||||
/// - moves and assignments
|
||||
|
|
|
|||
336
compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
Normal file
336
compiler/rustc_borrowck/src/polonius/liveness_constraints.rs
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_index::interval::SparseIntervalMatrix;
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt, TypeVisitable};
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
use super::{
|
||||
ConstraintDirection, LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet,
|
||||
PoloniusContext,
|
||||
};
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
impl PoloniusContext {
|
||||
/// Record the variance of each region contained within the given value.
|
||||
pub(crate) fn record_live_region_variance<'tcx>(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
|
||||
) {
|
||||
let mut extractor = VarianceExtractor {
|
||||
tcx,
|
||||
ambient_variance: ty::Variance::Covariant,
|
||||
directions: &mut self.live_region_variances,
|
||||
universal_regions,
|
||||
};
|
||||
extractor.relate(value, value).expect("Can't have a type error relating to itself");
|
||||
}
|
||||
|
||||
/// Unlike NLLs, in polonius we traverse the cfg to look for regions live across an edge, so we
|
||||
/// need to transpose the "points where each region is live" matrix to a "live regions per point"
|
||||
/// matrix.
|
||||
// FIXME: avoid this conversion by always storing liveness data in this shape in the rest of
|
||||
// borrowck.
|
||||
pub(crate) fn record_live_regions_per_point(
|
||||
&mut self,
|
||||
num_regions: usize,
|
||||
points_per_live_region: &SparseIntervalMatrix<RegionVid, PointIndex>,
|
||||
) {
|
||||
let mut live_regions_per_point = SparseBitMatrix::new(num_regions);
|
||||
for region in points_per_live_region.rows() {
|
||||
for point in points_per_live_region.row(region).unwrap().iter() {
|
||||
live_regions_per_point.insert(point, region);
|
||||
}
|
||||
}
|
||||
self.live_regions = Some(live_regions_per_point);
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
|
||||
/// constraints for loans that are propagated to the next statements.
|
||||
pub(super) fn create_liveness_constraints<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
if statement_index < statement_count {
|
||||
// Intra-block edges, straight line constraints from each point to its successor
|
||||
// within the same block.
|
||||
let next_location = Location { block, statement_index: statement_index + 1 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
} else {
|
||||
// Inter-block edges, from the block's terminator to each successor block's entry
|
||||
// point.
|
||||
for successor_block in bb.terminator().successors() {
|
||||
let next_location = Location { block: successor_block, statement_index: 0 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
live_regions,
|
||||
live_region_variances,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans within a region between two points in the CFG, if that region is live at both
|
||||
/// the source and target points.
|
||||
fn propagate_loans_between_points(
|
||||
current_point: PointIndex,
|
||||
next_point: PointIndex,
|
||||
live_regions: &SparseBitMatrix<PointIndex, RegionVid>,
|
||||
live_region_variances: &BTreeMap<RegionVid, ConstraintDirection>,
|
||||
universal_regions: &UniversalRegions<'_>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
// Universal regions are semantically live at all points.
|
||||
// Note: we always have universal regions but they're not always (or often) involved in the
|
||||
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
|
||||
// will be disconnected from the rest of the graph and thus, unnecessary.
|
||||
//
|
||||
// FIXME: only emit the edges of universal regions that existential regions can reach.
|
||||
for region in universal_regions.universal_regions_iter() {
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
}
|
||||
|
||||
let Some(current_live_regions) = live_regions.row(current_point) else {
|
||||
// There are no constraints to add: there are no live regions at the current point.
|
||||
return;
|
||||
};
|
||||
let Some(next_live_regions) = live_regions.row(next_point) else {
|
||||
// There are no constraints to add: there are no live regions at the next point.
|
||||
return;
|
||||
};
|
||||
|
||||
for region in next_live_regions.iter() {
|
||||
if !current_live_regions.contains(region) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// `region` is indeed live at both points, add a constraint between them, according to
|
||||
// variance.
|
||||
if let Some(&direction) = live_region_variances.get(®ion) {
|
||||
add_liveness_constraint(
|
||||
region,
|
||||
current_point,
|
||||
next_point,
|
||||
direction,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
} else {
|
||||
// Note: there currently are cases related to promoted and const generics, where we
|
||||
// don't yet have variance information (possibly about temporary regions created when
|
||||
// typeck sanitizes the promoteds). Until that is done, we conservatively fallback to
|
||||
// maximizing reachability by adding a bidirectional edge here. This will not limit
|
||||
// traversal whatsoever, and thus propagate liveness when needed.
|
||||
//
|
||||
// FIXME: add the missing variance information and remove this fallback bidirectional
|
||||
// edge.
|
||||
let fallback = ConstraintDirection::Bidirectional;
|
||||
add_liveness_constraint(
|
||||
region,
|
||||
current_point,
|
||||
next_point,
|
||||
fallback,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds `LocalizedOutlivesConstraint`s between two connected points, according to the given edge
|
||||
/// direction.
|
||||
fn add_liveness_constraint(
|
||||
region: RegionVid,
|
||||
current_point: PointIndex,
|
||||
next_point: PointIndex,
|
||||
direction: ConstraintDirection,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
match direction {
|
||||
ConstraintDirection::Forward => {
|
||||
// Covariant cases: loans flow in the regular direction, from the current point to the
|
||||
// next point.
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
}
|
||||
ConstraintDirection::Backward => {
|
||||
// Contravariant cases: loans flow in the inverse direction, from the next point to the
|
||||
// current point.
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: next_point,
|
||||
target: region,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
ConstraintDirection::Bidirectional => {
|
||||
// For invariant cases, loans can flow in both directions: we add both edges.
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: next_point,
|
||||
target: region,
|
||||
to: current_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts variances for regions contained within types. Follows the same structure as
|
||||
/// `rustc_infer`'s `Generalizer`: we try to relate a type with itself to track and extract the
|
||||
/// variances of regions.
|
||||
struct VarianceExtractor<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ambient_variance: ty::Variance,
|
||||
directions: &'a mut BTreeMap<RegionVid, ConstraintDirection>,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> VarianceExtractor<'_, 'tcx> {
|
||||
fn record_variance(&mut self, region: ty::Region<'tcx>, variance: ty::Variance) {
|
||||
// We're only interested in the variance of vars and free regions.
|
||||
//
|
||||
// Note: even if we currently bail for two cases of unexpected region kinds here, missing
|
||||
// variance data is not a soundness problem: the regions with missing variance will still be
|
||||
// present in the constraint graph as they are live, and liveness edges construction has a
|
||||
// fallback for this case.
|
||||
//
|
||||
// FIXME: that being said, we need to investigate these cases better to not ignore regions
|
||||
// in general.
|
||||
if region.is_bound() {
|
||||
// We ignore these because they cannot be turned into the vids we need.
|
||||
return;
|
||||
}
|
||||
|
||||
if region.is_erased() {
|
||||
// These cannot be turned into a vid either, and we also ignore them: the fact that they
|
||||
// show up here looks like either an issue upstream or a combination with unexpectedly
|
||||
// continuing compilation too far when we're in a tainted by errors situation.
|
||||
//
|
||||
// FIXME: investigate the `generic_const_exprs` test that triggers this issue,
|
||||
// `ui/const-generics/generic_const_exprs/issue-97047-ice-2.rs`
|
||||
return;
|
||||
}
|
||||
|
||||
let direction = match variance {
|
||||
ty::Covariant => ConstraintDirection::Forward,
|
||||
ty::Contravariant => ConstraintDirection::Backward,
|
||||
ty::Invariant => ConstraintDirection::Bidirectional,
|
||||
ty::Bivariant => {
|
||||
// We don't add edges for bivariant cases.
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let region = self.universal_regions.to_region_vid(region);
|
||||
self.directions
|
||||
.entry(region)
|
||||
.and_modify(|entry| {
|
||||
// If there's already a recorded direction for this region, we combine the two:
|
||||
// - combining the same direction is idempotent
|
||||
// - combining different directions is trivially bidirectional
|
||||
if entry != &direction {
|
||||
*entry = ConstraintDirection::Bidirectional;
|
||||
}
|
||||
})
|
||||
.or_insert(direction);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeRelation<TyCtxt<'tcx>> for VarianceExtractor<'_, 'tcx> {
|
||||
fn cx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(
|
||||
&mut self,
|
||||
variance: ty::Variance,
|
||||
_info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,
|
||||
a: T,
|
||||
b: T,
|
||||
) -> RelateResult<'tcx, T> {
|
||||
let old_ambient_variance = self.ambient_variance;
|
||||
self.ambient_variance = self.ambient_variance.xform(variance);
|
||||
let r = self.relate(a, b)?;
|
||||
self.ambient_variance = old_ambient_variance;
|
||||
Ok(r)
|
||||
}
|
||||
|
||||
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
|
||||
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
|
||||
relate::structurally_relate_tys(self, a, b)
|
||||
}
|
||||
|
||||
fn regions(
|
||||
&mut self,
|
||||
a: ty::Region<'tcx>,
|
||||
b: ty::Region<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Region<'tcx>> {
|
||||
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
|
||||
self.record_variance(a, self.ambient_variance);
|
||||
Ok(a)
|
||||
}
|
||||
|
||||
fn consts(
|
||||
&mut self,
|
||||
a: ty::Const<'tcx>,
|
||||
b: ty::Const<'tcx>,
|
||||
) -> RelateResult<'tcx, ty::Const<'tcx>> {
|
||||
assert_eq!(a, b); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
|
||||
relate::structurally_relate_consts(self, a, b)
|
||||
}
|
||||
|
||||
fn binders<T>(
|
||||
&mut self,
|
||||
a: ty::Binder<'tcx, T>,
|
||||
_: ty::Binder<'tcx, T>,
|
||||
) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
|
||||
where
|
||||
T: Relate<TyCtxt<'tcx>>,
|
||||
{
|
||||
self.relate(a.skip_binder(), a.skip_binder())?;
|
||||
Ok(a)
|
||||
}
|
||||
}
|
||||
|
|
@ -34,45 +34,88 @@
|
|||
//!
|
||||
|
||||
mod constraints;
|
||||
pub(crate) use constraints::*;
|
||||
mod dump;
|
||||
pub(crate) use dump::dump_polonius_mir;
|
||||
pub(crate) mod legacy;
|
||||
mod liveness_constraints;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use rustc_index::bit_set::SparseBitMatrix;
|
||||
use rustc_middle::mir::{Body, Location};
|
||||
use rustc_middle::ty::RegionVid;
|
||||
use rustc_mir_dataflow::points::PointIndex;
|
||||
|
||||
pub(crate) use self::constraints::*;
|
||||
pub(crate) use self::dump::dump_polonius_mir;
|
||||
use self::liveness_constraints::create_liveness_constraints;
|
||||
use crate::RegionInferenceContext;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// Creates a constraint set for `-Zpolonius=next` by:
|
||||
/// - converting NLL typeck constraints to be localized
|
||||
/// - encoding liveness constraints
|
||||
pub(crate) fn create_localized_constraints<'tcx>(
|
||||
regioncx: &mut RegionInferenceContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> LocalizedOutlivesConstraintSet {
|
||||
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
|
||||
convert_typeck_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
create_liveness_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
/// This struct holds the data needed to create the Polonius localized constraints.
|
||||
pub(crate) struct PoloniusContext {
|
||||
/// The set of regions that are live at a given point in the CFG, used to create localized
|
||||
/// outlives constraints between regions that are live at connected points in the CFG.
|
||||
live_regions: Option<SparseBitMatrix<PointIndex, RegionVid>>,
|
||||
|
||||
// FIXME: here, we can trace loan reachability in the constraint graph and record this as loan
|
||||
// liveness for the next step in the chain, the NLL loan scope and active loans computations.
|
||||
/// The expected edge direction per live region: the kind of directed edge we'll create as
|
||||
/// liveness constraints depends on the variance of types with respect to each contained region.
|
||||
live_region_variances: BTreeMap<RegionVid, ConstraintDirection>,
|
||||
}
|
||||
|
||||
localized_outlives_constraints
|
||||
/// The direction a constraint can flow into. Used to create liveness constraints according to
|
||||
/// variance.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
enum ConstraintDirection {
|
||||
/// For covariant cases, we add a forward edge `O at P1 -> O at P2`.
|
||||
Forward,
|
||||
|
||||
/// For contravariant cases, we add a backward edge `O at P2 -> O at P1`
|
||||
Backward,
|
||||
|
||||
/// For invariant cases, we add both the forward and backward edges `O at P1 <-> O at P2`.
|
||||
Bidirectional,
|
||||
}
|
||||
|
||||
impl PoloniusContext {
|
||||
pub(crate) fn new() -> PoloniusContext {
|
||||
Self { live_region_variances: BTreeMap::new(), live_regions: None }
|
||||
}
|
||||
|
||||
/// Creates a constraint set for `-Zpolonius=next` by:
|
||||
/// - converting NLL typeck constraints to be localized
|
||||
/// - encoding liveness constraints
|
||||
pub(crate) fn create_localized_constraints<'tcx>(
|
||||
&self,
|
||||
regioncx: &RegionInferenceContext<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
) -> LocalizedOutlivesConstraintSet {
|
||||
let mut localized_outlives_constraints = LocalizedOutlivesConstraintSet::default();
|
||||
convert_typeck_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
|
||||
let live_regions = self.live_regions.as_ref().expect(
|
||||
"live regions per-point data should have been created at the end of MIR typeck",
|
||||
);
|
||||
create_liveness_constraints(
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
live_regions,
|
||||
&self.live_region_variances,
|
||||
regioncx.universal_regions(),
|
||||
&mut localized_outlives_constraints,
|
||||
);
|
||||
|
||||
// FIXME: here, we can trace loan reachability in the constraint graph and record this as loan
|
||||
// liveness for the next step in the chain, the NLL loan scope and active loans computations.
|
||||
|
||||
localized_outlives_constraints
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the subset graph at a given point (with some subtleties around the
|
||||
|
|
@ -109,72 +152,3 @@ fn convert_typeck_constraints<'tcx>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans throughout the CFG: for each statement in the MIR, create localized outlives
|
||||
/// constraints for loans that are propagated to the next statements.
|
||||
pub(crate) fn create_liveness_constraints<'tcx>(
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
for statement_index in 0..=statement_count {
|
||||
let current_location = Location { block, statement_index };
|
||||
let current_point = liveness.point_from_location(current_location);
|
||||
|
||||
if statement_index < statement_count {
|
||||
// Intra-block edges, straight line constraints from each point to its successor
|
||||
// within the same block.
|
||||
let next_location = Location { block, statement_index: statement_index + 1 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
liveness,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
} else {
|
||||
// Inter-block edges, from the block's terminator to each successor block's entry
|
||||
// point.
|
||||
for successor_block in bb.terminator().successors() {
|
||||
let next_location = Location { block: successor_block, statement_index: 0 };
|
||||
let next_point = liveness.point_from_location(next_location);
|
||||
propagate_loans_between_points(
|
||||
current_point,
|
||||
next_point,
|
||||
liveness,
|
||||
universal_regions,
|
||||
localized_outlives_constraints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagate loans within a region between two points in the CFG, if that region is live at both
|
||||
/// the source and target points.
|
||||
fn propagate_loans_between_points(
|
||||
current_point: PointIndex,
|
||||
next_point: PointIndex,
|
||||
_liveness: &LivenessValues,
|
||||
universal_regions: &UniversalRegions<'_>,
|
||||
localized_outlives_constraints: &mut LocalizedOutlivesConstraintSet,
|
||||
) {
|
||||
// Universal regions are semantically live at all points.
|
||||
// Note: we always have universal regions but they're not always (or often) involved in the
|
||||
// subset graph. For now, we emit all their edges unconditionally, but some of these subgraphs
|
||||
// will be disconnected from the rest of the graph and thus, unnecessary.
|
||||
// FIXME: only emit the edges of universal regions that existential regions can reach.
|
||||
for region in universal_regions.universal_regions_iter() {
|
||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
||||
source: region,
|
||||
from: current_point,
|
||||
target: region,
|
||||
to: next_point,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ use crate::constraints::{ConstraintSccIndex, OutlivesConstraint, OutlivesConstra
|
|||
use crate::dataflow::BorrowIndex;
|
||||
use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
|
||||
use crate::member_constraints::{MemberConstraintSet, NllMemberConstraintIndex};
|
||||
use crate::nll::PoloniusOutput;
|
||||
use crate::polonius::legacy::PoloniusOutput;
|
||||
use crate::region_infer::reverse_sccs::ReverseSccGraph;
|
||||
use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
|
|
@ -1950,8 +1950,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
|||
target_test: impl Fn(RegionVid) -> bool,
|
||||
) -> (BlameConstraint<'tcx>, Vec<ExtraConstraintInfo>) {
|
||||
// Find all paths
|
||||
let (path, target_region) =
|
||||
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
|
||||
let (path, target_region) = self
|
||||
.find_constraint_paths_between_regions(from_region, target_test)
|
||||
.or_else(|| {
|
||||
self.find_constraint_paths_between_regions(from_region, |r| {
|
||||
self.cannot_name_placeholder(from_region, r)
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
debug!(
|
||||
"path={:#?}",
|
||||
path.iter()
|
||||
|
|
|
|||
|
|
@ -99,6 +99,14 @@ impl LivenessValues {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the liveness matrix of points where each region is live. Panics if the liveness
|
||||
/// values have been created without any per-point data (that is, for promoteds).
|
||||
pub(crate) fn points(&self) -> &SparseIntervalMatrix<RegionVid, PointIndex> {
|
||||
self.points
|
||||
.as_ref()
|
||||
.expect("this `LivenessValues` wasn't created using `with_specific_points`")
|
||||
}
|
||||
|
||||
/// Iterate through each region that has a value in this set.
|
||||
pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> + '_ {
|
||||
self.points.as_ref().expect("use with_specific_points").rows()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use rustc_data_structures::fx::FxHashSet;
|
|||
use rustc_middle::mir::visit::{TyContext, Visitor};
|
||||
use rustc_middle::mir::{Body, Local, Location, SourceInfo};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::relate::Relate;
|
||||
use rustc_middle::ty::visit::TypeVisitable;
|
||||
use rustc_middle::ty::{GenericArgsRef, Region, RegionVid, Ty, TyCtxt};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
|
|
@ -13,6 +14,7 @@ use tracing::debug;
|
|||
|
||||
use super::TypeChecker;
|
||||
use crate::constraints::OutlivesConstraintSet;
|
||||
use crate::polonius::PoloniusContext;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
|
|
@ -56,7 +58,13 @@ pub(super) fn generate<'a, 'tcx>(
|
|||
|
||||
// Mark regions that should be live where they appear within rvalues or within a call: like
|
||||
// args, regions, and types.
|
||||
record_regular_live_regions(typeck.tcx(), &mut typeck.constraints.liveness_constraints, body);
|
||||
record_regular_live_regions(
|
||||
typeck.tcx(),
|
||||
&mut typeck.constraints.liveness_constraints,
|
||||
&typeck.universal_regions,
|
||||
&mut typeck.polonius_context,
|
||||
body,
|
||||
);
|
||||
}
|
||||
|
||||
// The purpose of `compute_relevant_live_locals` is to define the subset of `Local`
|
||||
|
|
@ -130,9 +138,12 @@ fn regions_that_outlive_free_regions<'tcx>(
|
|||
fn record_regular_live_regions<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &mut LivenessValues,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
polonius_context: &mut Option<PoloniusContext>,
|
||||
body: &Body<'tcx>,
|
||||
) {
|
||||
let mut visitor = LiveVariablesVisitor { tcx, liveness_constraints };
|
||||
let mut visitor =
|
||||
LiveVariablesVisitor { tcx, liveness_constraints, universal_regions, polonius_context };
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
|
|
@ -142,6 +153,8 @@ fn record_regular_live_regions<'tcx>(
|
|||
struct LiveVariablesVisitor<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
liveness_constraints: &'a mut LivenessValues,
|
||||
universal_regions: &'a UniversalRegions<'tcx>,
|
||||
polonius_context: &'a mut Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'a, 'tcx> {
|
||||
|
|
@ -184,12 +197,17 @@ impl<'a, 'tcx> LiveVariablesVisitor<'a, 'tcx> {
|
|||
/// all regions appearing in the type of `value` must be live at `location`.
|
||||
fn record_regions_live_at<T>(&mut self, value: T, location: Location)
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
T: TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
|
||||
{
|
||||
debug!("record_regions_live_at(value={:?}, location={:?})", value, location);
|
||||
self.tcx.for_each_free_region(&value, |live_region| {
|
||||
let live_region_vid = live_region.as_var();
|
||||
self.liveness_constraints.add_location(live_region_vid, location);
|
||||
});
|
||||
|
||||
// When using `-Zpolonius=next`, we record the variance of each live region.
|
||||
if let Some(polonius_context) = self.polonius_context {
|
||||
polonius_context.record_live_region_variance(self.tcx, self.universal_regions, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use rustc_infer::infer::canonical::QueryRegionConstraints;
|
|||
use rustc_infer::infer::outlives::for_liveness;
|
||||
use rustc_middle::mir::{BasicBlock, Body, ConstraintCategory, Local, Location};
|
||||
use rustc_middle::traits::query::DropckOutlivesResult;
|
||||
use rustc_middle::ty::relate::Relate;
|
||||
use rustc_middle::ty::{Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
|
||||
use rustc_mir_dataflow::ResultsCursor;
|
||||
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
|
||||
|
|
@ -14,7 +15,6 @@ use rustc_span::DUMMY_SP;
|
|||
use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, TypeOpOutput};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::location::RichLocation;
|
||||
use crate::polonius;
|
||||
use crate::region_infer::values::{self, LiveLoans};
|
||||
use crate::type_check::liveness::local_use_map::LocalUseMap;
|
||||
|
|
@ -210,7 +210,7 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
///
|
||||
/// Add facts for all locals with free regions, since regions may outlive
|
||||
/// the function body only at certain nodes in the CFG.
|
||||
fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) -> Option<()> {
|
||||
fn add_extra_drop_facts(&mut self, relevant_live_locals: &[Local]) {
|
||||
// This collect is more necessary than immediately apparent
|
||||
// because these facts go into `add_drop_live_facts_for()`,
|
||||
// which also writes to `all_facts`, and so this is genuinely
|
||||
|
|
@ -220,41 +220,30 @@ impl<'a, 'typeck, 'b, 'tcx> LivenessResults<'a, 'typeck, 'b, 'tcx> {
|
|||
// and probably maybe plausibly does not need to go back in.
|
||||
// It may be necessary to just pick out the parts of
|
||||
// `add_drop_live_facts_for()` that make sense.
|
||||
let Some(facts) = self.cx.typeck.all_facts.as_ref() else { return };
|
||||
let facts_to_add: Vec<_> = {
|
||||
let drop_used = &self.cx.typeck.all_facts.as_ref()?.var_dropped_at;
|
||||
|
||||
let relevant_live_locals: FxIndexSet<_> =
|
||||
relevant_live_locals.iter().copied().collect();
|
||||
|
||||
drop_used
|
||||
facts
|
||||
.var_dropped_at
|
||||
.iter()
|
||||
.filter_map(|(local, location_index)| {
|
||||
let local_ty = self.cx.body.local_decls[*local].ty;
|
||||
if relevant_live_locals.contains(local) || !local_ty.has_free_regions() {
|
||||
.filter_map(|&(local, location_index)| {
|
||||
let local_ty = self.cx.body.local_decls[local].ty;
|
||||
if relevant_live_locals.contains(&local) || !local_ty.has_free_regions() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let location = match self.cx.typeck.location_table.to_location(*location_index)
|
||||
{
|
||||
RichLocation::Start(l) => l,
|
||||
RichLocation::Mid(l) => l,
|
||||
};
|
||||
|
||||
Some((*local, local_ty, location))
|
||||
let location = self.cx.typeck.location_table.to_location(location_index);
|
||||
Some((local, local_ty, location))
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
// FIXME: these locations seem to have a special meaning (e.g. everywhere, at the end,
|
||||
// ...), but I don't know which one. Please help me rename it to something descriptive!
|
||||
// Also, if this IntervalSet is used in many places, it maybe should have a newtype'd
|
||||
// name with a description of what it means for future mortals passing by.
|
||||
let locations = IntervalSet::new(self.cx.elements.num_points());
|
||||
|
||||
let live_at = IntervalSet::new(self.cx.elements.num_points());
|
||||
for (local, local_ty, location) in facts_to_add {
|
||||
self.cx.add_drop_live_facts_for(local, local_ty, &[location], &locations);
|
||||
self.cx.add_drop_live_facts_for(local, local_ty, &[location], &live_at);
|
||||
}
|
||||
Some(())
|
||||
}
|
||||
|
||||
/// Clear the value of fields that are "per local variable".
|
||||
|
|
@ -532,11 +521,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
|
||||
/// Stores the result that all regions in `value` are live for the
|
||||
/// points `live_at`.
|
||||
fn add_use_live_facts_for(
|
||||
&mut self,
|
||||
value: impl TypeVisitable<TyCtxt<'tcx>>,
|
||||
live_at: &IntervalSet<PointIndex>,
|
||||
) {
|
||||
fn add_use_live_facts_for(&mut self, value: Ty<'tcx>, live_at: &IntervalSet<PointIndex>) {
|
||||
debug!("add_use_live_facts_for(value={:?})", value);
|
||||
Self::make_all_regions_live(self.elements, self.typeck, value, live_at);
|
||||
}
|
||||
|
|
@ -603,7 +588,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
fn make_all_regions_live(
|
||||
elements: &DenseLocationMap,
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
value: impl TypeVisitable<TyCtxt<'tcx>>,
|
||||
value: impl TypeVisitable<TyCtxt<'tcx>> + Relate<TyCtxt<'tcx>>,
|
||||
live_at: &IntervalSet<PointIndex>,
|
||||
) {
|
||||
debug!("make_all_regions_live(value={:?})", value);
|
||||
|
|
@ -621,6 +606,15 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
typeck.constraints.liveness_constraints.add_points(live_region_vid, live_at);
|
||||
},
|
||||
});
|
||||
|
||||
// When using `-Zpolonius=next`, we record the variance of each live region.
|
||||
if let Some(polonius_context) = typeck.polonius_context {
|
||||
polonius_context.record_live_region_variance(
|
||||
typeck.infcx.tcx,
|
||||
typeck.universal_regions,
|
||||
value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_drop_data(typeck: &TypeChecker<'_, 'tcx>, dropped_ty: Ty<'tcx>) -> DropData<'tcx> {
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ use tracing::{debug, instrument, trace};
|
|||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::{OutlivesConstraint, OutlivesConstraintSet};
|
||||
use crate::diagnostics::UniverseInfo;
|
||||
use crate::facts::AllFacts;
|
||||
use crate::location::LocationTable;
|
||||
use crate::member_constraints::MemberConstraintSet;
|
||||
use crate::polonius::PoloniusContext;
|
||||
use crate::polonius::legacy::{AllFacts, LocationTable};
|
||||
use crate::region_infer::TypeTest;
|
||||
use crate::region_infer::values::{LivenessValues, PlaceholderIndex, PlaceholderIndices};
|
||||
use crate::renumber::RegionCtxt;
|
||||
|
|
@ -140,8 +140,20 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
&mut constraints,
|
||||
);
|
||||
|
||||
let pre_obligations = infcx.take_registered_region_obligations();
|
||||
assert!(
|
||||
pre_obligations.is_empty(),
|
||||
"there should be no incoming region obligations = {pre_obligations:#?}",
|
||||
);
|
||||
|
||||
debug!(?normalized_inputs_and_output);
|
||||
|
||||
let mut polonius_context = if infcx.tcx.sess.opts.unstable_opts.polonius.is_next_enabled() {
|
||||
Some(PoloniusContext::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut typeck = TypeChecker {
|
||||
infcx,
|
||||
last_span: body.span,
|
||||
|
|
@ -156,6 +168,7 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
all_facts,
|
||||
borrow_set,
|
||||
constraints: &mut constraints,
|
||||
polonius_context: &mut polonius_context,
|
||||
};
|
||||
|
||||
typeck.check_user_type_annotations();
|
||||
|
|
@ -172,7 +185,18 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
let opaque_type_values =
|
||||
opaque_types::take_opaques_and_register_member_constraints(&mut typeck);
|
||||
|
||||
MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
|
||||
if let Some(polonius_context) = typeck.polonius_context.as_mut() {
|
||||
let num_regions = infcx.num_region_vars();
|
||||
let points_per_live_region = typeck.constraints.liveness_constraints.points();
|
||||
polonius_context.record_live_regions_per_point(num_regions, points_per_live_region);
|
||||
}
|
||||
|
||||
MirTypeckResults {
|
||||
constraints,
|
||||
universal_region_relations,
|
||||
opaque_type_values,
|
||||
polonius_context,
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
|
@ -540,6 +564,8 @@ struct TypeChecker<'a, 'tcx> {
|
|||
all_facts: &'a mut Option<AllFacts>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
constraints: &'a mut MirTypeckRegionConstraints<'tcx>,
|
||||
/// When using `-Zpolonius=next`, the helper data used to create polonius constraints.
|
||||
polonius_context: &'a mut Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
/// Holder struct for passing results from MIR typeck to the rest of the non-lexical regions
|
||||
|
|
@ -548,6 +574,7 @@ pub(crate) struct MirTypeckResults<'tcx> {
|
|||
pub(crate) constraints: MirTypeckRegionConstraints<'tcx>,
|
||||
pub(crate) universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
|
||||
pub(crate) opaque_type_values: FxIndexMap<OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>>,
|
||||
pub(crate) polonius_context: Option<PoloniusContext>,
|
||||
}
|
||||
|
||||
/// A collection of region constraints that must be satisfied for the
|
||||
|
|
|
|||
|
|
@ -337,7 +337,7 @@ impl<'tcx> UniversalRegions<'tcx> {
|
|||
self.indices.indices.iter().map(|(&r, &v)| (r, v))
|
||||
}
|
||||
|
||||
/// See `UniversalRegionIndices::to_region_vid`.
|
||||
/// See [UniversalRegionIndices::to_region_vid].
|
||||
pub(crate) fn to_region_vid(&self, r: ty::Region<'tcx>) -> RegionVid {
|
||||
self.indices.to_region_vid(r)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ index 7165c3e48af..968552ad435 100644
|
|||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
-compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
-compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] }
|
||||
+compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std', 'no-f16-f128'] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ pub(crate) fn target_machine_factory(
|
|||
let reloc_model = to_llvm_relocation_model(sess.relocation_model());
|
||||
|
||||
let (opt_level, _) = to_llvm_opt_settings(optlvl);
|
||||
let use_softfp = if sess.target.arch == "arm" && sess.target.abi == "eabihf" {
|
||||
let use_softfp = if sess.target.arch == "arm" {
|
||||
sess.opts.cg.soft_float
|
||||
} else {
|
||||
// `validate_commandline_args_with_session_available` has already warned about this being
|
||||
|
|
|
|||
|
|
@ -340,6 +340,37 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
|
|||
self.const_i32(cache_type),
|
||||
])
|
||||
}
|
||||
sym::carrying_mul_add => {
|
||||
let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx);
|
||||
|
||||
let wide_llty = self.type_ix(size.bits() * 2);
|
||||
let args = args.as_array().unwrap();
|
||||
let [a, b, c, d] = args.map(|a| self.intcast(a.immediate(), wide_llty, signed));
|
||||
|
||||
let wide = if signed {
|
||||
let prod = self.unchecked_smul(a, b);
|
||||
let acc = self.unchecked_sadd(prod, c);
|
||||
self.unchecked_sadd(acc, d)
|
||||
} else {
|
||||
let prod = self.unchecked_umul(a, b);
|
||||
let acc = self.unchecked_uadd(prod, c);
|
||||
self.unchecked_uadd(acc, d)
|
||||
};
|
||||
|
||||
let narrow_llty = self.type_ix(size.bits());
|
||||
let low = self.trunc(wide, narrow_llty);
|
||||
let bits_const = self.const_uint(wide_llty, size.bits());
|
||||
// No need for ashr when signed; LLVM changes it to lshr anyway.
|
||||
let high = self.lshr(wide, bits_const);
|
||||
// FIXME: could be `trunc nuw`, even for signed.
|
||||
let high = self.trunc(high, narrow_llty);
|
||||
|
||||
let pair_llty = self.type_struct(&[narrow_llty, narrow_llty], false);
|
||||
let pair = self.const_poison(pair_llty);
|
||||
let pair = self.insert_value(pair, low, 0);
|
||||
let pair = self.insert_value(pair, high, 1);
|
||||
pair
|
||||
}
|
||||
sym::ctlz
|
||||
| sym::ctlz_nonzero
|
||||
| sym::cttz
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#![feature(iter_intersperse)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(rustdoc_internals)]
|
||||
#![feature(slice_as_array)]
|
||||
#![feature(try_blocks)]
|
||||
#![warn(unreachable_pub)]
|
||||
// tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ arrayvec = { version = "0.7", default-features = false }
|
|||
bitflags = "2.4.1"
|
||||
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
|
||||
# `cc` in `rustc_llvm` if you update the `cc` here.
|
||||
cc = "=1.2.5"
|
||||
cc = "=1.2.6"
|
||||
either = "1.5.0"
|
||||
itertools = "0.12"
|
||||
pathdiff = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::env;
|
|||
use std::error::Error;
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
use std::io::{self, BufWriter, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use ar_archive_writer::{
|
||||
|
|
@ -509,9 +509,10 @@ impl<'a> ArArchiveBuilder<'a> {
|
|||
io_error_context("couldn't create a directory for the temp file", err)
|
||||
})?;
|
||||
let archive_tmpfile_path = archive_tmpdir.path().join("tmp.a");
|
||||
let mut archive_tmpfile = File::create_new(&archive_tmpfile_path)
|
||||
let archive_tmpfile = File::create_new(&archive_tmpfile_path)
|
||||
.map_err(|err| io_error_context("couldn't create the temp file", err))?;
|
||||
|
||||
let mut archive_tmpfile = BufWriter::new(archive_tmpfile);
|
||||
write_archive_to_stream(
|
||||
&mut archive_tmpfile,
|
||||
&entries,
|
||||
|
|
@ -519,6 +520,8 @@ impl<'a> ArArchiveBuilder<'a> {
|
|||
false,
|
||||
/* is_ec = */ self.sess.target.arch == "arm64ec",
|
||||
)?;
|
||||
archive_tmpfile.flush()?;
|
||||
drop(archive_tmpfile);
|
||||
|
||||
let any_entries = !entries.is_empty();
|
||||
drop(entries);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
|
|||
// If we're swapping something that's *not* an `OperandValue::Ref`,
|
||||
// then we can do it directly and avoid the alloca.
|
||||
// Otherwise, we'll let the fallback MIR body take care of it.
|
||||
if let sym::typed_swap = name {
|
||||
if let sym::typed_swap_nonoverlapping = name {
|
||||
let pointee_ty = fn_args.type_at(0);
|
||||
let pointee_layout = bx.layout_of(pointee_ty);
|
||||
if !bx.is_backend_ref(pointee_layout)
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ pub trait BuilderMethods<'a, 'tcx>:
|
|||
/// Avoids `alloca`s for Immediates and ScalarPairs.
|
||||
///
|
||||
/// FIXME: Maybe do something smarter for Ref types too?
|
||||
/// For now, the `typed_swap` intrinsic just doesn't call this for those
|
||||
/// For now, the `typed_swap_nonoverlapping` intrinsic just doesn't call this for those
|
||||
/// cases (in non-debug), preferring the fallback body instead.
|
||||
fn typed_place_swap(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -15,20 +15,14 @@ fn parent_impl_constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks whether an item is considered to be `const`. If it is a constructor, it is const.
|
||||
/// If it is an assoc method or function,
|
||||
/// return if it has a `const` modifier. If it is an intrinsic, report whether said intrinsic
|
||||
/// has a `rustc_const_{un,}stable` attribute. Otherwise, panic.
|
||||
/// Checks whether a function-like definition is considered to be `const`.
|
||||
fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
|
||||
let node = tcx.hir_node_by_def_id(def_id);
|
||||
|
||||
match node {
|
||||
hir::Node::Ctor(hir::VariantData::Tuple(..))
|
||||
| hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
|
||||
hir::Constness::Const
|
||||
}
|
||||
hir::Node::ForeignItem(_) => {
|
||||
// Foreign items cannot be evaluated at compile-time.
|
||||
hir::Node::Ctor(hir::VariantData::Tuple(..)) => hir::Constness::Const,
|
||||
hir::Node::ForeignItem(item) if let hir::ForeignItemKind::Fn(..) = item.kind => {
|
||||
// Foreign functions cannot be evaluated at compile-time.
|
||||
hir::Constness::NotConst
|
||||
}
|
||||
hir::Node::Expr(e) if let hir::ExprKind::Closure(c) = e.kind => c.constness,
|
||||
|
|
|
|||
|
|
@ -883,19 +883,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
.local_to_op(mir::RETURN_PLACE, None)
|
||||
.expect("return place should always be live");
|
||||
let dest = self.frame().return_place.clone();
|
||||
let res = if self.stack().len() == 1 {
|
||||
// The initializer of constants and statics will get validated separately
|
||||
// after the constant has been fully evaluated. While we could fall back to the default
|
||||
// code path, that will cause -Zenforce-validity to cycle on static initializers.
|
||||
// Reading from a static's memory is not allowed during its evaluation, and will always
|
||||
// trigger a cycle error. Validation must read from the memory of the current item.
|
||||
// For Miri this means we do not validate the root frame return value,
|
||||
// but Miri anyway calls `read_target_isize` on that so separate validation
|
||||
// is not needed.
|
||||
self.copy_op_no_dest_validation(&op, &dest)
|
||||
} else {
|
||||
self.copy_op_allow_transmute(&op, &dest)
|
||||
};
|
||||
let res = self.copy_op_allow_transmute(&op, &dest);
|
||||
trace!("return value: {:?}", self.dump_place(&dest.into()));
|
||||
// We delay actually short-circuiting on this error until *after* the stack frame is
|
||||
// popped, since we want this error to be attributed to the caller, whose type defines
|
||||
|
|
|
|||
|
|
@ -424,8 +424,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
|
||||
self.write_scalar(result, dest)?;
|
||||
}
|
||||
sym::typed_swap => {
|
||||
self.typed_swap_intrinsic(&args[0], &args[1])?;
|
||||
sym::typed_swap_nonoverlapping => {
|
||||
self.typed_swap_nonoverlapping_intrinsic(&args[0], &args[1])?;
|
||||
}
|
||||
|
||||
sym::vtable_size => {
|
||||
|
|
@ -638,19 +638,35 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
}
|
||||
|
||||
/// Does a *typed* swap of `*left` and `*right`.
|
||||
fn typed_swap_intrinsic(
|
||||
fn typed_swap_nonoverlapping_intrinsic(
|
||||
&mut self,
|
||||
left: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
|
||||
right: &OpTy<'tcx, <M as Machine<'tcx>>::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
let left = self.deref_pointer(left)?;
|
||||
let right = self.deref_pointer(right)?;
|
||||
debug_assert_eq!(left.layout, right.layout);
|
||||
assert_eq!(left.layout, right.layout);
|
||||
assert!(left.layout.is_sized());
|
||||
let kind = MemoryKind::Stack;
|
||||
let temp = self.allocate(left.layout, kind)?;
|
||||
self.copy_op(&left, &temp)?;
|
||||
self.copy_op(&right, &left)?;
|
||||
self.copy_op(&temp, &right)?;
|
||||
self.copy_op(&left, &temp)?; // checks alignment of `left`
|
||||
|
||||
// We want to always enforce non-overlapping, even if this is a scalar type.
|
||||
// Therefore we directly use the underlying `mem_copy` here.
|
||||
self.mem_copy(right.ptr(), left.ptr(), left.layout.size, /*nonoverlapping*/ true)?;
|
||||
// This means we also need to do the validation of the value that used to be in `right`
|
||||
// ourselves. This value is now in `left.` The one that started out in `left` already got
|
||||
// validated by the copy above.
|
||||
if M::enforce_validity(self, left.layout) {
|
||||
self.validate_operand(
|
||||
&left.clone().into(),
|
||||
M::enforce_validity_recursively(self, left.layout),
|
||||
/*reset_provenance_and_padding*/ true,
|
||||
)?;
|
||||
}
|
||||
|
||||
self.copy_op(&temp, &right)?; // checks alignment of `right`
|
||||
|
||||
self.deallocate_ptr(temp.ptr(), None, kind)?;
|
||||
interp_ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1359,6 +1359,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
let src_alloc = self.get_alloc_raw(src_alloc_id)?;
|
||||
let src_range = alloc_range(src_offset, size);
|
||||
assert!(!self.memory.validation_in_progress, "we can't be copying during validation");
|
||||
// For the overlapping case, it is crucial that we trigger the read hook
|
||||
// before the write hook -- the aliasing model cares about the order.
|
||||
M::before_memory_read(
|
||||
tcx,
|
||||
&self.machine,
|
||||
|
|
|
|||
|
|
@ -773,22 +773,6 @@ where
|
|||
interp_ok(())
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
/// The layouts of the `src` and `dest` may disagree.
|
||||
/// Does not perform validation of the destination.
|
||||
/// The only known use case for this function is checking the return
|
||||
/// value of a static during stack frame popping.
|
||||
#[inline(always)]
|
||||
pub(super) fn copy_op_no_dest_validation(
|
||||
&mut self,
|
||||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
src, dest, /* allow_transmute */ true, /* validate_dest */ false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
/// The layouts of the `src` and `dest` may disagree.
|
||||
#[inline(always)]
|
||||
|
|
@ -797,9 +781,7 @@ where
|
|||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
src, dest, /* allow_transmute */ true, /* validate_dest */ true,
|
||||
)
|
||||
self.copy_op_inner(src, dest, /* allow_transmute */ true)
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
|
|
@ -810,9 +792,7 @@ where
|
|||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
self.copy_op_inner(
|
||||
src, dest, /* allow_transmute */ false, /* validate_dest */ true,
|
||||
)
|
||||
self.copy_op_inner(src, dest, /* allow_transmute */ false)
|
||||
}
|
||||
|
||||
/// Copies the data from an operand to a place.
|
||||
|
|
@ -824,22 +804,21 @@ where
|
|||
src: &impl Projectable<'tcx, M::Provenance>,
|
||||
dest: &impl Writeable<'tcx, M::Provenance>,
|
||||
allow_transmute: bool,
|
||||
validate_dest: bool,
|
||||
) -> InterpResult<'tcx> {
|
||||
// These are technically *two* typed copies: `src` is a not-yet-loaded value,
|
||||
// so we're going a typed copy at `src` type from there to some intermediate storage.
|
||||
// so we're doing a typed copy at `src` type from there to some intermediate storage.
|
||||
// And then we're doing a second typed copy from that intermediate storage to `dest`.
|
||||
// But as an optimization, we only make a single direct copy here.
|
||||
|
||||
// Do the actual copy.
|
||||
self.copy_op_no_validate(src, dest, allow_transmute)?;
|
||||
|
||||
if validate_dest && M::enforce_validity(self, dest.layout()) {
|
||||
if M::enforce_validity(self, dest.layout()) {
|
||||
let dest = dest.to_place();
|
||||
// Given that there were two typed copies, we have to ensure this is valid at both types,
|
||||
// and we have to ensure this loses provenance and padding according to both types.
|
||||
// But if the types are identical, we only do one pass.
|
||||
if allow_transmute && src.layout().ty != dest.layout().ty {
|
||||
if src.layout().ty != dest.layout().ty {
|
||||
self.validate_operand(
|
||||
&dest.transmute(src.layout(), self)?,
|
||||
M::enforce_validity_recursively(self, src.layout()),
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
Attempt was made to import an unimportable value. This can happen when trying
|
||||
to import a method from a trait.
|
||||
Attempt was made to import an unimportable type. This can happen when trying
|
||||
to import a type from a trait.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```compile_fail,E0253
|
||||
mod foo {
|
||||
pub trait MyTrait {
|
||||
fn do_something();
|
||||
type SomeType;
|
||||
}
|
||||
}
|
||||
|
||||
use foo::MyTrait::do_something;
|
||||
// error: `do_something` is not directly importable
|
||||
use foo::MyTrait::SomeType;
|
||||
// error: `SomeType` is not directly importable
|
||||
|
||||
fn main() {}
|
||||
```
|
||||
|
||||
It's invalid to directly import methods belonging to a trait or concrete type.
|
||||
It's invalid to directly import types belonging to a trait.
|
||||
|
|
|
|||
|
|
@ -2216,6 +2216,11 @@ impl HumanEmitter {
|
|||
show_code_change
|
||||
{
|
||||
for part in parts {
|
||||
let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) {
|
||||
snippet
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let span_start_pos = sm.lookup_char_pos(part.span.lo()).col_display;
|
||||
let span_end_pos = sm.lookup_char_pos(part.span.hi()).col_display;
|
||||
|
||||
|
|
@ -2263,13 +2268,80 @@ impl HumanEmitter {
|
|||
}
|
||||
if let DisplaySuggestion::Diff = show_code_change {
|
||||
// Colorize removal with red in diff format.
|
||||
buffer.set_style_range(
|
||||
row_num - 2,
|
||||
(padding as isize + span_start_pos as isize) as usize,
|
||||
(padding as isize + span_end_pos as isize) as usize,
|
||||
Style::Removal,
|
||||
true,
|
||||
);
|
||||
|
||||
// Below, there's some tricky buffer indexing going on. `row_num` at this
|
||||
// point corresponds to:
|
||||
//
|
||||
// |
|
||||
// LL | CODE
|
||||
// | ++++ <- `row_num`
|
||||
//
|
||||
// in the buffer. When we have a diff format output, we end up with
|
||||
//
|
||||
// |
|
||||
// LL - OLDER <- row_num - 2
|
||||
// LL + NEWER
|
||||
// | <- row_num
|
||||
//
|
||||
// The `row_num - 2` is to select the buffer line that has the "old version
|
||||
// of the diff" at that point. When the removal is a single line, `i` is
|
||||
// `0`, `newlines` is `1` so `(newlines - i - 1)` ends up being `0`, so row
|
||||
// points at `LL - OLDER`. When the removal corresponds to multiple lines,
|
||||
// we end up with `newlines > 1` and `i` being `0..newlines - 1`.
|
||||
//
|
||||
// |
|
||||
// LL - OLDER <- row_num - 2 - (newlines - last_i - 1)
|
||||
// LL - CODE
|
||||
// LL - BEING
|
||||
// LL - REMOVED <- row_num - 2 - (newlines - first_i - 1)
|
||||
// LL + NEWER
|
||||
// | <- row_num
|
||||
|
||||
let newlines = snippet.lines().count();
|
||||
if newlines > 0 && row_num > newlines {
|
||||
// Account for removals where the part being removed spans multiple
|
||||
// lines.
|
||||
// FIXME: We check the number of rows because in some cases, like in
|
||||
// `tests/ui/lint/invalid-nan-comparison-suggestion.rs`, the rendered
|
||||
// suggestion will only show the first line of code being replaced. The
|
||||
// proper way of doing this would be to change the suggestion rendering
|
||||
// logic to show the whole prior snippet, but the current output is not
|
||||
// too bad to begin with, so we side-step that issue here.
|
||||
for (i, line) in snippet.lines().enumerate() {
|
||||
let line = normalize_whitespace(line);
|
||||
let row = row_num - 2 - (newlines - i - 1);
|
||||
// On the first line, we highlight between the start of the part
|
||||
// span, and the end of that line.
|
||||
// On the last line, we highlight between the start of the line, and
|
||||
// the column of the part span end.
|
||||
// On all others, we highlight the whole line.
|
||||
let start = if i == 0 {
|
||||
(padding as isize + span_start_pos as isize) as usize
|
||||
} else {
|
||||
padding
|
||||
};
|
||||
let end = if i == 0 {
|
||||
(padding as isize
|
||||
+ span_start_pos as isize
|
||||
+ line.len() as isize)
|
||||
as usize
|
||||
} else if i == newlines - 1 {
|
||||
(padding as isize + span_end_pos as isize) as usize
|
||||
} else {
|
||||
(padding as isize + line.len() as isize) as usize
|
||||
};
|
||||
buffer.set_style_range(row, start, end, Style::Removal, true);
|
||||
}
|
||||
} else {
|
||||
// The removed code fits all in one line.
|
||||
buffer.set_style_range(
|
||||
row_num - 2,
|
||||
(padding as isize + span_start_pos as isize) as usize,
|
||||
(padding as isize + span_end_pos as isize) as usize,
|
||||
Style::Removal,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// length of the code after substitution
|
||||
|
|
|
|||
|
|
@ -697,8 +697,10 @@ fn transcribe_metavar_expr<'a>(
|
|||
MetaVarExprConcatElem::Var(ident) => {
|
||||
match matched_from_ident(dcx, *ident, interp)? {
|
||||
NamedMatch::MatchedSeq(named_matches) => {
|
||||
let curr_idx = repeats.last().unwrap().0;
|
||||
match &named_matches[curr_idx] {
|
||||
let Some((curr_idx, _)) = repeats.last() else {
|
||||
return Err(dcx.struct_span_err(sp.entire(), "invalid syntax"));
|
||||
};
|
||||
match &named_matches[*curr_idx] {
|
||||
// FIXME(c410-f3r) Nested repetitions are unimplemented
|
||||
MatchedSeq(_) => unimplemented!(),
|
||||
MatchedSingle(pnr) => {
|
||||
|
|
|
|||
|
|
@ -519,6 +519,8 @@ declare_features! (
|
|||
(unstable, impl_trait_in_bindings, "1.64.0", Some(63065)),
|
||||
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
|
||||
(unstable, impl_trait_in_fn_trait_return, "1.64.0", Some(99697)),
|
||||
/// Allows `use` associated functions from traits.
|
||||
(unstable, import_trait_associated_functions, "CURRENT_RUSTC_VERSION", Some(134691)),
|
||||
/// Allows associated types in inherent impls.
|
||||
(incomplete, inherent_associated_types, "1.52.0", Some(8995)),
|
||||
/// Allow anonymous constants from an inline `const` block in pattern position
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
|
|||
| sym::add_with_overflow
|
||||
| sym::sub_with_overflow
|
||||
| sym::mul_with_overflow
|
||||
| sym::carrying_mul_add
|
||||
| sym::wrapping_add
|
||||
| sym::wrapping_sub
|
||||
| sym::wrapping_mul
|
||||
|
|
@ -436,6 +437,10 @@ pub fn check_intrinsic_type(
|
|||
(1, 0, vec![param(0), param(0)], Ty::new_tup(tcx, &[param(0), tcx.types.bool]))
|
||||
}
|
||||
|
||||
sym::carrying_mul_add => {
|
||||
(2, 0, vec![param(0); 4], Ty::new_tup(tcx, &[param(1), param(0)]))
|
||||
}
|
||||
|
||||
sym::ptr_guaranteed_cmp => (
|
||||
1,
|
||||
0,
|
||||
|
|
@ -496,7 +501,9 @@ pub fn check_intrinsic_type(
|
|||
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], tcx.types.unit)
|
||||
}
|
||||
|
||||
sym::typed_swap => (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit),
|
||||
sym::typed_swap_nonoverlapping => {
|
||||
(1, 0, vec![Ty::new_mut_ptr(tcx, param(0)); 2], tcx.types.unit)
|
||||
}
|
||||
|
||||
sym::discriminant_value => {
|
||||
let assoc_items = tcx.associated_item_def_ids(
|
||||
|
|
|
|||
|
|
@ -179,7 +179,12 @@ impl<T: Idx> BitSet<T> {
|
|||
/// Insert `elem`. Returns whether the set has changed.
|
||||
#[inline]
|
||||
pub fn insert(&mut self, elem: T) -> bool {
|
||||
assert!(elem.index() < self.domain_size);
|
||||
assert!(
|
||||
elem.index() < self.domain_size,
|
||||
"inserting element at index {} but domain size is {}",
|
||||
elem.index(),
|
||||
self.domain_size,
|
||||
);
|
||||
let (word_index, mask) = word_index_and_mask(elem);
|
||||
let word_ref = &mut self.words[word_index];
|
||||
let word = *word_ref;
|
||||
|
|
|
|||
|
|
@ -945,7 +945,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
|
||||
/// Clone the list of variable regions. This is used only during NLL processing
|
||||
/// to put the set of region variables into the NLL region context.
|
||||
pub fn get_region_var_origins(&self) -> VarInfos {
|
||||
pub fn get_region_var_infos(&self) -> VarInfos {
|
||||
let inner = self.inner.borrow();
|
||||
assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
|
||||
let storage = inner.region_constraint_storage.as_ref().expect("regions already resolved");
|
||||
|
|
|
|||
|
|
@ -299,10 +299,6 @@ impl<'tcx> RegionConstraintCollector<'_, 'tcx> {
|
|||
self.storage.var_infos.len()
|
||||
}
|
||||
|
||||
pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> {
|
||||
&self.storage.data
|
||||
}
|
||||
|
||||
/// Takes (and clears) the current set of constraints. Note that
|
||||
/// the set of variables remains intact, but all relationships
|
||||
/// between them are reset. This is used during NLL checking to
|
||||
|
|
|
|||
185
compiler/rustc_lint/src/default_could_be_derived.rs
Normal file
185
compiler/rustc_lint/src/default_could_be_derived.rs
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Diag;
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint, impl_lint_pass};
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use crate::{LateContext, LateLintPass};
|
||||
|
||||
declare_lint! {
|
||||
/// The `default_overrides_default_fields` lint checks for manual `impl` blocks of the
|
||||
/// `Default` trait of types with default field values.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![feature(default_field_values)]
|
||||
/// struct Foo {
|
||||
/// x: i32 = 101,
|
||||
/// y: NonDefault,
|
||||
/// }
|
||||
///
|
||||
/// struct NonDefault;
|
||||
///
|
||||
/// #[deny(default_overrides_default_fields)]
|
||||
/// impl Default for Foo {
|
||||
/// fn default() -> Foo {
|
||||
/// Foo { x: 100, y: NonDefault }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Manually writing a `Default` implementation for a type that has
|
||||
/// default field values runs the risk of diverging behavior between
|
||||
/// `Type { .. }` and `<Type as Default>::default()`, which would be a
|
||||
/// foot-gun for users of that type that would expect these to be
|
||||
/// equivalent. If `Default` can't be derived due to some fields not
|
||||
/// having a `Default` implementation, we encourage the use of `..` for
|
||||
/// the fields that do have a default field value.
|
||||
pub DEFAULT_OVERRIDES_DEFAULT_FIELDS,
|
||||
Deny,
|
||||
"detect `Default` impl that should use the type's default field values",
|
||||
@feature_gate = default_field_values;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct DefaultCouldBeDerived;
|
||||
|
||||
impl_lint_pass!(DefaultCouldBeDerived => [DEFAULT_OVERRIDES_DEFAULT_FIELDS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
|
||||
// Look for manual implementations of `Default`.
|
||||
let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return };
|
||||
let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
|
||||
let assoc = cx.tcx.associated_item(impl_item.owner_id);
|
||||
let parent = assoc.container_id(cx.tcx);
|
||||
if cx.tcx.has_attr(parent, sym::automatically_derived) {
|
||||
// We don't care about what `#[derive(Default)]` produces in this lint.
|
||||
return;
|
||||
}
|
||||
let Some(trait_ref) = cx.tcx.impl_trait_ref(parent) else { return };
|
||||
let trait_ref = trait_ref.instantiate_identity();
|
||||
if trait_ref.def_id != default_def_id {
|
||||
return;
|
||||
}
|
||||
let ty = trait_ref.self_ty();
|
||||
let ty::Adt(def, _) = ty.kind() else { return };
|
||||
|
||||
// We now know we have a manually written definition of a `<Type as Default>::default()`.
|
||||
|
||||
let hir = cx.tcx.hir();
|
||||
|
||||
let type_def_id = def.did();
|
||||
let body = hir.body(body_id);
|
||||
|
||||
// FIXME: evaluate bodies with statements and evaluate bindings to see if they would be
|
||||
// derivable.
|
||||
let hir::ExprKind::Block(hir::Block { stmts: _, expr: Some(expr), .. }, None) =
|
||||
body.value.kind
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Keep a mapping of field name to `hir::FieldDef` for every field in the type. We'll use
|
||||
// these to check for things like checking whether it has a default or using its span for
|
||||
// suggestions.
|
||||
let orig_fields = match hir.get_if_local(type_def_id) {
|
||||
Some(hir::Node::Item(hir::Item {
|
||||
kind:
|
||||
hir::ItemKind::Struct(hir::VariantData::Struct { fields, recovered: _ }, _generics),
|
||||
..
|
||||
})) => fields.iter().map(|f| (f.ident.name, f)).collect::<FxHashMap<_, _>>(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
// We check `fn default()` body is a single ADT literal and get all the fields that are
|
||||
// being set.
|
||||
let hir::ExprKind::Struct(_qpath, fields, tail) = expr.kind else { return };
|
||||
|
||||
// We have a struct literal
|
||||
//
|
||||
// struct Foo {
|
||||
// field: Type,
|
||||
// }
|
||||
//
|
||||
// impl Default for Foo {
|
||||
// fn default() -> Foo {
|
||||
// Foo {
|
||||
// field: val,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// We would suggest `#[derive(Default)]` if `field` has a default value, regardless of what
|
||||
// it is; we don't want to encourage divergent behavior between `Default::default()` and
|
||||
// `..`.
|
||||
|
||||
if let hir::StructTailExpr::Base(_) = tail {
|
||||
// This is *very* niche. We'd only get here if someone wrote
|
||||
// impl Default for Ty {
|
||||
// fn default() -> Ty {
|
||||
// Ty { ..something() }
|
||||
// }
|
||||
// }
|
||||
// where `something()` would have to be a call or path.
|
||||
// We have nothing meaninful to do with this.
|
||||
return;
|
||||
}
|
||||
|
||||
// At least one of the fields with a default value have been overriden in
|
||||
// the `Default` implementation. We suggest removing it and relying on `..`
|
||||
// instead.
|
||||
let any_default_field_given =
|
||||
fields.iter().any(|f| orig_fields.get(&f.ident.name).and_then(|f| f.default).is_some());
|
||||
|
||||
if !any_default_field_given {
|
||||
// None of the default fields were actually provided explicitly, so the manual impl
|
||||
// doesn't override them (the user used `..`), so there's no risk of divergent behavior.
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(local) = parent.as_local() else { return };
|
||||
let hir_id = cx.tcx.local_def_id_to_hir_id(local);
|
||||
let hir::Node::Item(item) = cx.tcx.hir_node(hir_id) else { return };
|
||||
cx.tcx.node_span_lint(DEFAULT_OVERRIDES_DEFAULT_FIELDS, hir_id, item.span, |diag| {
|
||||
mk_lint(diag, orig_fields, fields);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_lint(
|
||||
diag: &mut Diag<'_, ()>,
|
||||
orig_fields: FxHashMap<Symbol, &hir::FieldDef<'_>>,
|
||||
fields: &[hir::ExprField<'_>],
|
||||
) {
|
||||
diag.primary_message("`Default` impl doesn't use the declared default field values");
|
||||
|
||||
// For each field in the struct expression
|
||||
// - if the field in the type has a default value, it should be removed
|
||||
// - elif the field is an expression that could be a default value, it should be used as the
|
||||
// field's default value (FIXME: not done).
|
||||
// - else, we wouldn't touch this field, it would remain in the manual impl
|
||||
let mut removed_all_fields = true;
|
||||
for field in fields {
|
||||
if orig_fields.get(&field.ident.name).and_then(|f| f.default).is_some() {
|
||||
diag.span_label(field.expr.span, "this field has a default value");
|
||||
} else {
|
||||
removed_all_fields = false;
|
||||
}
|
||||
}
|
||||
|
||||
diag.help(if removed_all_fields {
|
||||
"to avoid divergence in behavior between `Struct { .. }` and \
|
||||
`<Struct as Default>::default()`, derive the `Default`"
|
||||
} else {
|
||||
"use the default values in the `impl` with `Struct { mandatory_field, .. }` to avoid them \
|
||||
diverging over time"
|
||||
});
|
||||
}
|
||||
|
|
@ -41,6 +41,7 @@ mod async_fn_in_trait;
|
|||
pub mod builtin;
|
||||
mod context;
|
||||
mod dangling;
|
||||
mod default_could_be_derived;
|
||||
mod deref_into_dyn_supertrait;
|
||||
mod drop_forget_useless;
|
||||
mod early;
|
||||
|
|
@ -85,6 +86,7 @@ use async_closures::AsyncClosureUsage;
|
|||
use async_fn_in_trait::AsyncFnInTrait;
|
||||
use builtin::*;
|
||||
use dangling::*;
|
||||
use default_could_be_derived::DefaultCouldBeDerived;
|
||||
use deref_into_dyn_supertrait::*;
|
||||
use drop_forget_useless::*;
|
||||
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
|
||||
|
|
@ -189,6 +191,7 @@ late_lint_methods!(
|
|||
BuiltinCombinedModuleLateLintPass,
|
||||
[
|
||||
ForLoopsOverFallibles: ForLoopsOverFallibles,
|
||||
DefaultCouldBeDerived: DefaultCouldBeDerived::default(),
|
||||
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
|
||||
DropForgetUseless: DropForgetUseless,
|
||||
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
|
||||
|
|
|
|||
|
|
@ -12,5 +12,5 @@ libc = "0.2.73"
|
|||
# tidy-alphabetical-start
|
||||
# Pinned so `cargo update` bumps don't cause breakage. Please also update the
|
||||
# pinned `cc` in `rustc_codegen_ssa` if you update `cc` here.
|
||||
cc = "=1.2.5"
|
||||
cc = "=1.2.6"
|
||||
# tidy-alphabetical-end
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@ fn main() {
|
|||
let mut cmd = Command::new(&llvm_config);
|
||||
cmd.arg(llvm_link_arg).arg("--libs");
|
||||
|
||||
// Don't link system libs if cross-compiling unless targetting Windows.
|
||||
// Don't link system libs if cross-compiling unless targeting Windows.
|
||||
// On Windows system DLLs aren't linked directly, instead import libraries are used.
|
||||
// These import libraries are independent of the host.
|
||||
if !is_crossed || target.contains("windows") {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// Return the span for a definition.
|
||||
///
|
||||
/// Contrary to `def_span` below, this query returns the full absolute span of the definition.
|
||||
/// This span is meant for dep-tracking rather than diagnostics. It should not be used outside
|
||||
/// of rustc_middle::hir::source_map.
|
||||
|
|
@ -142,6 +143,7 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// Represents crate as a whole (as distinct from the top-level crate module).
|
||||
///
|
||||
/// If you call `hir_crate` (e.g., indirectly by calling `tcx.hir().krate()`),
|
||||
/// we will have to assume that any change means that you need to be recompiled.
|
||||
/// This is because the `hir_crate` query gives you access to all other items.
|
||||
|
|
@ -202,28 +204,40 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
/// Returns the *default* of the const pararameter given by `DefId`.
|
||||
///
|
||||
/// E.g., given `struct Ty<const N: usize = 3>;` this returns `3` for `N`.
|
||||
query const_param_default(param: DefId) -> ty::EarlyBinder<'tcx, ty::Const<'tcx>> {
|
||||
desc { |tcx| "computing const default for a given parameter `{}`", tcx.def_path_str(param) }
|
||||
desc { |tcx| "computing the default for const parameter `{}`", tcx.def_path_str(param) }
|
||||
cache_on_disk_if { param.is_local() }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Returns the [`Ty`][rustc_middle::ty::Ty] of the given [`DefId`]. If the [`DefId`] points
|
||||
/// to an alias, it will "skip" this alias to return the aliased type.
|
||||
/// Returns the *type* of the definition given by `DefId`.
|
||||
///
|
||||
/// [`DefId`]: rustc_hir::def_id::DefId
|
||||
/// For type aliases (whether eager or lazy) and associated types, this returns
|
||||
/// the underlying aliased type (not the corresponding [alias type]).
|
||||
///
|
||||
/// For opaque types, this returns and thus reveals the hidden type! If you
|
||||
/// want to detect cycle errors use `type_of_opaque` instead.
|
||||
///
|
||||
/// To clarify, for type definitions, this does *not* return the "type of a type"
|
||||
/// (aka *kind* or *sort*) in the type-theoretical sense! It merely returns
|
||||
/// the type primarily *associated with* it.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query will panic if the given definition doesn't (and can't
|
||||
/// conceptually) have an (underlying) type.
|
||||
///
|
||||
/// [alias type]: rustc_middle::ty::AliasTy
|
||||
query type_of(key: DefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> {
|
||||
desc { |tcx|
|
||||
"{action} `{path}`",
|
||||
action = {
|
||||
use rustc_hir::def::DefKind;
|
||||
match tcx.def_kind(key) {
|
||||
DefKind::TyAlias => "expanding type alias",
|
||||
DefKind::TraitAlias => "expanding trait alias",
|
||||
_ => "computing type of",
|
||||
}
|
||||
action = match tcx.def_kind(key) {
|
||||
DefKind::TyAlias => "expanding type alias",
|
||||
DefKind::TraitAlias => "expanding trait alias",
|
||||
_ => "computing type of",
|
||||
},
|
||||
path = tcx.def_path_str(key),
|
||||
}
|
||||
|
|
@ -232,9 +246,14 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// Specialized instance of `type_of` that detects cycles that are due to
|
||||
/// revealing opaque because of an auto trait bound. Unless `CyclePlaceholder` needs
|
||||
/// to be handled separately, call `type_of` instead.
|
||||
/// Returns the *hidden type* of the opaque type given by `DefId` unless a cycle occurred.
|
||||
///
|
||||
/// This is a specialized instance of [`Self::type_of`] that detects query cycles.
|
||||
/// Unless `CyclePlaceholder` needs to be handled separately, call [`Self::type_of`] instead.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query will panic if the given definition is not an opaque type.
|
||||
query type_of_opaque(key: DefId) -> Result<ty::EarlyBinder<'tcx, Ty<'tcx>>, CyclePlaceholder> {
|
||||
desc { |tcx|
|
||||
"computing type of opaque `{path}`",
|
||||
|
|
@ -243,9 +262,22 @@ rustc_queries! {
|
|||
cycle_stash
|
||||
}
|
||||
|
||||
/// Returns whether the type alias given by `DefId` is lazy.
|
||||
///
|
||||
/// I.e., if the type alias expands / ought to expand to a [weak] [alias type]
|
||||
/// instead of the underyling aliased type.
|
||||
///
|
||||
/// Relevant for features `lazy_type_alias` and `type_alias_impl_trait`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query *may* panic if the given definition is not a type alias.
|
||||
///
|
||||
/// [weak]: rustc_middle::ty::Weak
|
||||
/// [alias type]: rustc_middle::ty::AliasTy
|
||||
query type_alias_is_lazy(key: DefId) -> bool {
|
||||
desc { |tcx|
|
||||
"computing whether `{path}` is a lazy type alias",
|
||||
"computing whether the type alias `{path}` is lazy",
|
||||
path = tcx.def_path_str(key),
|
||||
}
|
||||
separate_provide_extern
|
||||
|
|
@ -299,8 +331,7 @@ rustc_queries! {
|
|||
desc { "checking lint expectations (RFC 2383)" }
|
||||
}
|
||||
|
||||
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to its
|
||||
/// associated generics.
|
||||
/// Returns the *generics* of the definition given by `DefId`.
|
||||
query generics_of(key: DefId) -> &'tcx ty::Generics {
|
||||
desc { |tcx| "computing generics of `{}`", tcx.def_path_str(key) }
|
||||
arena_cache
|
||||
|
|
@ -309,10 +340,13 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to the
|
||||
/// predicates (where-clauses) that must be proven true in order
|
||||
/// to reference it. This is almost always the "predicates query"
|
||||
/// that you want.
|
||||
/// Returns the (elaborated) *predicates* of the definition given by `DefId`
|
||||
/// that must be proven true at usage sites (and which can be assumed at definition site).
|
||||
///
|
||||
/// This is almost always *the* "predicates query" that you want.
|
||||
///
|
||||
/// **Tip**: You can use `#[rustc_dump_predicates]` on an item to basically print
|
||||
/// the result of this query for use in UI tests or for debugging purposes.
|
||||
query predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
|
||||
desc { |tcx| "computing predicates of `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { key.is_local() }
|
||||
|
|
@ -328,25 +362,24 @@ rustc_queries! {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the list of bounds that are required to be satisfied
|
||||
/// by a implementation or definition. For associated types, these
|
||||
/// must be satisfied for an implementation to be well-formed,
|
||||
/// and for opaque types, these are required to be satisfied by
|
||||
/// the hidden-type of the opaque.
|
||||
/// Returns the explicitly user-written *bounds* on the associated or opaque type given by `DefId`
|
||||
/// that must be proven true at definition site (and which can be assumed at usage sites).
|
||||
///
|
||||
/// Syntactially, these are the bounds written on the trait's type
|
||||
/// definition, or those after the `impl` keyword for an opaque:
|
||||
/// For associated types, these must be satisfied for an implementation
|
||||
/// to be well-formed, and for opaque types, these are required to be
|
||||
/// satisfied by the hidden type of the opaque.
|
||||
///
|
||||
/// ```ignore (incomplete)
|
||||
/// type X: Bound + 'lt
|
||||
/// // ^^^^^^^^^^^
|
||||
/// impl Debug + Display
|
||||
/// // ^^^^^^^^^^^^^^^
|
||||
/// Bounds from the parent (e.g. with nested `impl Trait`) are not included.
|
||||
///
|
||||
/// Syntactially, these are the bounds written on associated types in trait
|
||||
/// definitions, or those after the `impl` keyword for an opaque:
|
||||
///
|
||||
/// ```ignore (illustrative)
|
||||
/// trait Trait { type X: Bound + 'lt; }
|
||||
/// // ^^^^^^^^^^^
|
||||
/// fn function() -> impl Debug + Display { /*...*/ }
|
||||
/// // ^^^^^^^^^^^^^^^
|
||||
/// ```
|
||||
///
|
||||
/// `key` is the `DefId` of the associated type or opaque type.
|
||||
///
|
||||
/// Bounds from the parent (e.g. with nested impl trait) are not included.
|
||||
query explicit_item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
|
||||
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { key.is_local() }
|
||||
|
|
@ -354,10 +387,12 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that
|
||||
/// share the `Self` type of the item. These are a subset of the bounds
|
||||
/// that may explicitly be used for things like closure signature
|
||||
/// deduction.
|
||||
/// Returns the explicitly user-written *bounds* that share the `Self` type of the item.
|
||||
///
|
||||
/// These are a subset of the [explicit item bounds] that may explicitly be used for things
|
||||
/// like closure signature deduction.
|
||||
///
|
||||
/// [explicit item bounds]: Self::explicit_item_bounds
|
||||
query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
|
||||
desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { key.is_local() }
|
||||
|
|
@ -365,26 +400,29 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// Elaborated version of the predicates from `explicit_item_bounds`.
|
||||
/// Returns the (elaborated) *bounds* on the associated or opaque type given by `DefId`
|
||||
/// that must be proven true at definition site (and which can be assumed at usage sites).
|
||||
///
|
||||
/// For example:
|
||||
/// Bounds from the parent (e.g. with nested `impl Trait`) are not included.
|
||||
///
|
||||
/// **Tip**: You can use `#[rustc_dump_item_bounds]` on an item to basically print
|
||||
/// the result of this query for use in UI tests or for debugging purposes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// trait MyTrait {
|
||||
/// type MyAType: Eq + ?Sized;
|
||||
/// }
|
||||
/// trait Trait { type Assoc: Eq + ?Sized; }
|
||||
/// ```
|
||||
///
|
||||
/// `explicit_item_bounds` returns `[<Self as MyTrait>::MyAType: Eq]`,
|
||||
/// and `item_bounds` returns
|
||||
/// While [`Self::explicit_item_bounds`] returns `[<Self as Trait>::Assoc: Eq]`
|
||||
/// here, `item_bounds` returns:
|
||||
///
|
||||
/// ```text
|
||||
/// [
|
||||
/// <Self as Trait>::MyAType: Eq,
|
||||
/// <Self as Trait>::MyAType: PartialEq<<Self as Trait>::MyAType>
|
||||
/// <Self as Trait>::Assoc: Eq,
|
||||
/// <Self as Trait>::Assoc: PartialEq<<Self as Trait>::Assoc>
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// Bounds from the parent (e.g. with nested impl trait) are not included.
|
||||
query item_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> {
|
||||
desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) }
|
||||
}
|
||||
|
|
@ -615,27 +653,35 @@ rustc_queries! {
|
|||
desc { "getting wasm import module map" }
|
||||
}
|
||||
|
||||
/// Returns everything that looks like a predicate written explicitly
|
||||
/// by the user on a trait item.
|
||||
/// Returns the explicitly user-written *predicates and bounds* of the trait given by `DefId`.
|
||||
///
|
||||
/// Traits are unusual, because predicates on associated types are
|
||||
/// converted into bounds on that type for backwards compatibility:
|
||||
///
|
||||
/// ```
|
||||
/// trait X where Self::U: Copy { type U; }
|
||||
/// ```
|
||||
///
|
||||
/// becomes
|
||||
///
|
||||
/// ```
|
||||
/// trait X { type U: Copy; }
|
||||
/// ```
|
||||
///
|
||||
/// `explicit_predicates_of` and `explicit_item_bounds` will then take
|
||||
/// the appropriate subsets of the predicates here.
|
||||
/// [`Self::explicit_predicates_of`] and [`Self::explicit_item_bounds`] will
|
||||
/// then take the appropriate subsets of the predicates here.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query will panic if the given definition is not a trait.
|
||||
query trait_explicit_predicates_and_bounds(key: LocalDefId) -> ty::GenericPredicates<'tcx> {
|
||||
desc { |tcx| "computing explicit predicates of trait `{}`", tcx.def_path_str(key) }
|
||||
}
|
||||
|
||||
/// Returns the predicates written explicitly by the user.
|
||||
/// Returns the explicitly user-written *predicates* of the definition given by `DefId`
|
||||
/// that must be proven true at usage sites (and which can be assumed at definition site).
|
||||
///
|
||||
/// You should probably use `predicates_of` unless you're looking for
|
||||
/// You should probably use [`Self::predicates_of`] unless you're looking for
|
||||
/// predicates with explicit spans for diagnostics purposes.
|
||||
query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
|
||||
desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) }
|
||||
|
|
@ -644,18 +690,24 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// Returns the inferred outlives predicates (e.g., for `struct
|
||||
/// Foo<'a, T> { x: &'a T }`, this would return `T: 'a`).
|
||||
/// Returns the *inferred outlives-predicates* of the item given by `DefId`.
|
||||
///
|
||||
/// E.g., for `struct Foo<'a, T> { x: &'a T }`, this would return `[T: 'a]`.
|
||||
///
|
||||
/// **Tip**: You can use `#[rustc_outlives]` on an item to basically print the
|
||||
/// result of this query for use in UI tests or for debugging purposes.
|
||||
query inferred_outlives_of(key: DefId) -> &'tcx [(ty::Clause<'tcx>, Span)] {
|
||||
desc { |tcx| "computing inferred outlives predicates of `{}`", tcx.def_path_str(key) }
|
||||
desc { |tcx| "computing inferred outlives-predicates of `{}`", tcx.def_path_str(key) }
|
||||
cache_on_disk_if { key.is_local() }
|
||||
separate_provide_extern
|
||||
feedable
|
||||
}
|
||||
|
||||
/// Maps from the `DefId` of a trait to the list of super-predicates of the trait,
|
||||
/// *before* elaboration (so it doesn't contain transitive super-predicates). This
|
||||
/// is a subset of the full list of predicates. We store these in a separate map
|
||||
/// Returns the explicitly user-written *super-predicates* of the trait given by `DefId`.
|
||||
///
|
||||
/// These predicates are unelaborated and consequently don't contain transitive super-predicates.
|
||||
///
|
||||
/// This is a subset of the full list of predicates. We store these in a separate map
|
||||
/// because we must evaluate them even during type conversion, often before the full
|
||||
/// predicates are available (note that super-predicates must not be cyclic).
|
||||
query explicit_super_predicates_of(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> {
|
||||
|
|
@ -664,8 +716,9 @@ rustc_queries! {
|
|||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// The predicates of the trait that are implied during elaboration. This is a
|
||||
/// superset of the super-predicates of the trait, but a subset of the predicates
|
||||
/// The predicates of the trait that are implied during elaboration.
|
||||
///
|
||||
/// This is a superset of the super-predicates of the trait, but a subset of the predicates
|
||||
/// of the trait. For regular traits, this includes all super-predicates and their
|
||||
/// associated type bounds. For trait aliases, currently, this includes all of the
|
||||
/// predicates of the trait alias.
|
||||
|
|
@ -745,14 +798,27 @@ rustc_queries! {
|
|||
desc { |tcx| "computing drop-check constraints for `{}`", tcx.def_path_str(key) }
|
||||
}
|
||||
|
||||
/// Returns the constness of function-like things (tuple struct/variant constructors, functions,
|
||||
/// methods)
|
||||
/// Returns the constness of the function-like[^1] definition given by `DefId`.
|
||||
///
|
||||
/// Will ICE if used on things that are always const or never const.
|
||||
/// Tuple struct/variant constructors are *always* const, foreign functions are
|
||||
/// *never* const. The rest is const iff marked with keyword `const` (or rather
|
||||
/// its parent in the case of associated functions).
|
||||
///
|
||||
/// **Do not call this function manually.** It is only meant to cache the base data for the
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Do not call this query** directly. It is only meant to cache the base data for the
|
||||
/// higher-level functions. Consider using `is_const_fn` or `is_const_trait_impl` instead.
|
||||
/// Also note that neither of them takes into account feature gates and stability.
|
||||
///
|
||||
/// Also note that neither of them takes into account feature gates, stability and
|
||||
/// const predicates/conditions!
|
||||
///
|
||||
/// </div>
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query will panic if the given definition is not function-like[^1].
|
||||
///
|
||||
/// [^1]: Tuple struct/variant constructors, closures and free, associated and foreign functions.
|
||||
query constness(key: DefId) -> hir::Constness {
|
||||
desc { |tcx| "checking if item is const: `{}`", tcx.def_path_str(key) }
|
||||
separate_provide_extern
|
||||
|
|
@ -798,13 +864,25 @@ rustc_queries! {
|
|||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Gets a map with the variance of every item; use `variances_of` instead.
|
||||
/// Gets a map with the variances of every item in the local crate.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Do not call this query** directly, use [`Self::variances_of`] instead.
|
||||
///
|
||||
/// </div>
|
||||
query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> {
|
||||
arena_cache
|
||||
desc { "computing the variances for items in this crate" }
|
||||
}
|
||||
|
||||
/// Maps from the `DefId` of a type or region parameter to its (inferred) variance.
|
||||
/// Returns the (inferred) variances of the item given by `DefId`.
|
||||
///
|
||||
/// The list of variances corresponds to the list of (early-bound) generic
|
||||
/// parameters of the item (including its parents).
|
||||
///
|
||||
/// **Tip**: You can use `#[rustc_variance]` on an item to basically print the
|
||||
/// result of this query for use in UI tests or for debugging purposes.
|
||||
query variances_of(def_id: DefId) -> &'tcx [ty::Variance] {
|
||||
desc { |tcx| "computing the variances of `{}`", tcx.def_path_str(def_id) }
|
||||
cache_on_disk_if { def_id.is_local() }
|
||||
|
|
@ -812,10 +890,16 @@ rustc_queries! {
|
|||
cycle_delay_bug
|
||||
}
|
||||
|
||||
/// Maps from thee `DefId` of a type to its (inferred) outlives.
|
||||
/// Gets a map with the inferred outlives-predicates of every item in the local crate.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Do not call this query** directly, use [`Self::inferred_outlives_of`] instead.
|
||||
///
|
||||
/// </div>
|
||||
query inferred_outlives_crate(_: ()) -> &'tcx ty::CratePredicatesMap<'tcx> {
|
||||
arena_cache
|
||||
desc { "computing the inferred outlives predicates for items in this crate" }
|
||||
desc { "computing the inferred outlives-predicates for items in this crate" }
|
||||
}
|
||||
|
||||
/// Maps from an impl/trait or struct/variant `DefId`
|
||||
|
|
@ -1038,20 +1122,35 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// Gets a complete map from all types to their inherent impls.
|
||||
/// Not meant to be used directly outside of coherence.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Not meant to be used** directly outside of coherence.
|
||||
///
|
||||
/// </div>
|
||||
query crate_inherent_impls(k: ()) -> (&'tcx CrateInherentImpls, Result<(), ErrorGuaranteed>) {
|
||||
desc { "finding all inherent impls defined in crate" }
|
||||
}
|
||||
|
||||
/// Checks all types in the crate for overlap in their inherent impls. Reports errors.
|
||||
/// Not meant to be used directly outside of coherence.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Not meant to be used** directly outside of coherence.
|
||||
///
|
||||
/// </div>
|
||||
query crate_inherent_impls_validity_check(_: ()) -> Result<(), ErrorGuaranteed> {
|
||||
desc { "check for inherent impls that should not be defined in crate" }
|
||||
ensure_forwards_result_if_red
|
||||
}
|
||||
|
||||
/// Checks all types in the crate for overlap in their inherent impls. Reports errors.
|
||||
/// Not meant to be used directly outside of coherence.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Not meant to be used** directly outside of coherence.
|
||||
///
|
||||
/// </div>
|
||||
query crate_inherent_impls_overlap_check(_: ()) -> Result<(), ErrorGuaranteed> {
|
||||
desc { "check for overlap between inherent impls defined in this crate" }
|
||||
ensure_forwards_result_if_red
|
||||
|
|
@ -1089,8 +1188,12 @@ rustc_queries! {
|
|||
}
|
||||
|
||||
/// Computes the tag (if any) for a given type and variant.
|
||||
///
|
||||
/// `None` means that the variant doesn't need a tag (because it is niched).
|
||||
/// Will panic for uninhabited variants.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query will panic for uninhabited variants and if the passed type is not an enum.
|
||||
query tag_for_variant(
|
||||
key: (Ty<'tcx>, abi::VariantIdx)
|
||||
) -> Option<ty::ScalarInt> {
|
||||
|
|
@ -1099,7 +1202,12 @@ rustc_queries! {
|
|||
|
||||
/// Evaluates a constant and returns the computed allocation.
|
||||
///
|
||||
/// **Do not use this** directly, use the `eval_to_const_value` or `eval_to_valtree` instead.
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Do not call this query** directly, use [`Self::eval_to_const_value_raw`] or
|
||||
/// [`Self::eval_to_valtree`] instead.
|
||||
///
|
||||
/// </div>
|
||||
query eval_to_allocation_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>)
|
||||
-> EvalToAllocationRawResult<'tcx> {
|
||||
desc { |tcx|
|
||||
|
|
@ -1120,12 +1228,18 @@ rustc_queries! {
|
|||
feedable
|
||||
}
|
||||
|
||||
/// Evaluates const items or anonymous constants
|
||||
/// (such as enum variant explicit discriminants or array lengths)
|
||||
/// into a representation suitable for the type system and const generics.
|
||||
/// Evaluates const items or anonymous constants[^1] into a representation
|
||||
/// suitable for the type system and const generics.
|
||||
///
|
||||
/// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`,
|
||||
/// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_global_id`.
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// **Do not call this** directly, use one of the following wrappers:
|
||||
/// [`TyCtxt::const_eval_poly`], [`TyCtxt::const_eval_resolve`],
|
||||
/// [`TyCtxt::const_eval_instance`], or [`TyCtxt::const_eval_global_id`].
|
||||
///
|
||||
/// </div>
|
||||
///
|
||||
/// [^1]: Such as enum variant explicit discriminants or array lengths.
|
||||
query eval_to_const_value_raw(key: ty::PseudoCanonicalInput<'tcx, GlobalId<'tcx>>)
|
||||
-> EvalToConstValueResult<'tcx> {
|
||||
desc { |tcx|
|
||||
|
|
@ -1252,13 +1366,13 @@ rustc_queries! {
|
|||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Determines whether an item is annotated with `doc(hidden)`.
|
||||
/// Determines whether an item is annotated with `#[doc(hidden)]`.
|
||||
query is_doc_hidden(def_id: DefId) -> bool {
|
||||
desc { |tcx| "checking whether `{}` is `doc(hidden)`", tcx.def_path_str(def_id) }
|
||||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Determines whether an item is annotated with `doc(notable_trait)`.
|
||||
/// Determines whether an item is annotated with `#[doc(notable_trait)]`.
|
||||
query is_doc_notable_trait(def_id: DefId) -> bool {
|
||||
desc { |tcx| "checking whether `{}` is `doc(notable_trait)`", tcx.def_path_str(def_id) }
|
||||
}
|
||||
|
|
@ -1796,13 +1910,22 @@ rustc_queries! {
|
|||
query is_late_bound_map(owner_id: hir::OwnerId) -> Option<&'tcx FxIndexSet<ItemLocalId>> {
|
||||
desc { |tcx| "testing if a region is late bound inside `{}`", tcx.def_path_str(owner_id) }
|
||||
}
|
||||
/// For a given item's generic parameter, gets the default lifetimes to be used
|
||||
/// for each parameter if a trait object were to be passed for that parameter.
|
||||
/// For example, for `T` in `struct Foo<'a, T>`, this would be `'static`.
|
||||
/// For `T` in `struct Foo<'a, T: 'a>`, this would instead be `'a`.
|
||||
/// This query will panic if passed something that is not a type parameter.
|
||||
/// Returns the *default lifetime* to be used if a trait object type were to be passed for
|
||||
/// the type parameter given by `DefId`.
|
||||
///
|
||||
/// **Tip**: You can use `#[rustc_object_lifetime_default]` on an item to basically
|
||||
/// print the result of this query for use in UI tests or for debugging purposes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// - For `T` in `struct Foo<'a, T: 'a>(&'a T);`, this would be `Param('a)`
|
||||
/// - For `T` in `struct Bar<'a, T>(&'a T);`, this would be `Empty`
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This query will panic if the given definition is not a type parameter.
|
||||
query object_lifetime_default(def_id: DefId) -> ObjectLifetimeDefault {
|
||||
desc { "looking up lifetime defaults for generic parameter `{}`", tcx.def_path_str(def_id) }
|
||||
desc { "looking up lifetime defaults for type parameter `{}`", tcx.def_path_str(def_id) }
|
||||
separate_provide_extern
|
||||
}
|
||||
query late_bound_vars_map(owner_id: hir::OwnerId)
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ impl<'tcx> TailCallCkVisitor<'_, 'tcx> {
|
|||
self.report_arguments_mismatch(expr.span, caller_sig, callee_sig);
|
||||
}
|
||||
|
||||
// FIXME(explicit_tail_calls): this currenly fails for cases where opaques are used.
|
||||
// FIXME(explicit_tail_calls): this currently fails for cases where opaques are used.
|
||||
// e.g.
|
||||
// ```
|
||||
// fn a() -> impl Sized { become b() } // ICE
|
||||
|
|
|
|||
|
|
@ -755,7 +755,7 @@ impl<'a> Parser<'a> {
|
|||
// When there are a few keywords in the last ten elements of `self.expected_token_types`
|
||||
// and the current token is an identifier, it's probably a misspelled keyword. This handles
|
||||
// code like `async Move {}`, misspelled `if` in match guard, misspelled `else` in
|
||||
// `if`-`else` and mispelled `where` in a where clause.
|
||||
// `if`-`else` and misspelled `where` in a where clause.
|
||||
if !expected_keywords.is_empty()
|
||||
&& !curr_ident.is_used_keyword()
|
||||
&& let Some(misspelled_kw) = find_similar_kw(curr_ident, &expected_keywords)
|
||||
|
|
@ -1336,7 +1336,7 @@ impl<'a> Parser<'a> {
|
|||
) -> bool {
|
||||
if let ExprKind::Binary(op, l1, r1) = &inner_op.kind {
|
||||
if let ExprKind::Field(_, ident) = l1.kind
|
||||
&& ident.as_str().parse::<i32>().is_err()
|
||||
&& !ident.is_numeric()
|
||||
&& !matches!(r1.kind, ExprKind::Lit(_))
|
||||
{
|
||||
// The parser has encountered `foo.bar<baz`, the likelihood of the turbofish
|
||||
|
|
|
|||
|
|
@ -1183,7 +1183,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
let in_module_is_extern = !in_module.def_id().is_local();
|
||||
in_module.for_each_child(self, |this, ident, ns, name_binding| {
|
||||
// avoid non-importable candidates
|
||||
if !name_binding.is_importable() {
|
||||
if !name_binding.is_importable()
|
||||
// FIXME(import_trait_associated_functions): remove this when `import_trait_associated_functions` is stable
|
||||
|| name_binding.is_assoc_const_or_fn()
|
||||
&& !this.tcx.features().import_trait_associated_functions()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,10 @@ use rustc_session::lint::builtin::{
|
|||
AMBIGUOUS_GLOB_REEXPORTS, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE,
|
||||
REDUNDANT_IMPORTS, UNUSED_IMPORTS,
|
||||
};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
use rustc_span::hygiene::LocalExpnId;
|
||||
use rustc_span::{Ident, Span, Symbol, kw};
|
||||
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||
use smallvec::SmallVec;
|
||||
use tracing::debug;
|
||||
|
||||
|
|
@ -829,6 +830,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
|
|||
// Don't update the resolution, because it was never added.
|
||||
Err(Determined) if target.name == kw::Underscore => {}
|
||||
Ok(binding) if binding.is_importable() => {
|
||||
if binding.is_assoc_const_or_fn()
|
||||
&& !this.tcx.features().import_trait_associated_functions()
|
||||
{
|
||||
feature_err(
|
||||
this.tcx.sess,
|
||||
sym::import_trait_associated_functions,
|
||||
import.span,
|
||||
"`use` associated items of traits is unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
let imported_binding = this.import(binding, import);
|
||||
target_bindings[ns].set(Some(imported_binding));
|
||||
this.define(parent, target, ns, imported_binding);
|
||||
|
|
|
|||
|
|
@ -920,10 +920,13 @@ impl<'ra> NameBindingData<'ra> {
|
|||
}
|
||||
|
||||
fn is_importable(&self) -> bool {
|
||||
!matches!(
|
||||
self.res(),
|
||||
Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _)
|
||||
)
|
||||
!matches!(self.res(), Res::Def(DefKind::AssocTy, _))
|
||||
}
|
||||
|
||||
// FIXME(import_trait_associated_functions): associate `const` or `fn` are not importable unless
|
||||
// the feature `import_trait_associated_functions` is enable
|
||||
fn is_assoc_const_or_fn(&self) -> bool {
|
||||
matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn, _))
|
||||
}
|
||||
|
||||
fn macro_kind(&self) -> Option<MacroKind> {
|
||||
|
|
|
|||
|
|
@ -1320,7 +1320,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
|
|||
}
|
||||
|
||||
if sess.opts.cg.soft_float {
|
||||
if sess.target.arch == "arm" && sess.target.abi == "eabihf" {
|
||||
if sess.target.arch == "arm" {
|
||||
sess.dcx().emit_warn(errors::SoftFloatDeprecated);
|
||||
} else {
|
||||
// All `use_softfp` does is the equivalent of `-mfloat-abi` in GCC/clang, which only exists on ARM targets.
|
||||
|
|
|
|||
|
|
@ -555,6 +555,7 @@ symbols! {
|
|||
call_ref_future,
|
||||
caller_location,
|
||||
capture_disjoint_fields,
|
||||
carrying_mul_add,
|
||||
catch_unwind,
|
||||
cause,
|
||||
cdylib,
|
||||
|
|
@ -1092,6 +1093,7 @@ symbols! {
|
|||
import,
|
||||
import_name_type,
|
||||
import_shadowing,
|
||||
import_trait_associated_functions,
|
||||
imported_main,
|
||||
in_band_lifetimes,
|
||||
include,
|
||||
|
|
@ -2058,7 +2060,7 @@ symbols! {
|
|||
type_macros,
|
||||
type_name,
|
||||
type_privacy_lints,
|
||||
typed_swap,
|
||||
typed_swap_nonoverlapping,
|
||||
u128,
|
||||
u128_legacy_const_max,
|
||||
u128_legacy_const_min,
|
||||
|
|
@ -2707,6 +2709,12 @@ impl Ident {
|
|||
pub fn is_raw_guess(self) -> bool {
|
||||
self.name.can_be_raw() && self.is_reserved()
|
||||
}
|
||||
|
||||
/// Whether this would be the identifier for a tuple field like `self.0`, as
|
||||
/// opposed to a named field like `self.thing`.
|
||||
pub fn is_numeric(self) -> bool {
|
||||
!self.name.is_empty() && self.as_str().bytes().all(|b| b.is_ascii_digit())
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all the keywords in a given edition into a vector.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, base};
|
||||
use crate::spec::{Cc, LinkerFlavor, Lld, PanicStrategy, StackProbeType, Target, base};
|
||||
|
||||
pub(crate) fn target() -> Target {
|
||||
let mut base = base::linux::opts();
|
||||
|
|
@ -7,6 +7,7 @@ pub(crate) fn target() -> Target {
|
|||
base.stack_probes = StackProbeType::Inline;
|
||||
base.linker_flavor = LinkerFlavor::Gnu(Cc::No, Lld::Yes);
|
||||
base.linker = Some("rust-lld".into());
|
||||
base.panic_strategy = PanicStrategy::Abort;
|
||||
|
||||
Target {
|
||||
llvm_target: "x86_64-unknown-linux-none".into(),
|
||||
|
|
@ -14,7 +15,7 @@ pub(crate) fn target() -> Target {
|
|||
description: None,
|
||||
tier: None,
|
||||
host_tools: None,
|
||||
std: Some(true),
|
||||
std: Some(false),
|
||||
},
|
||||
pointer_width: 64,
|
||||
data_layout:
|
||||
|
|
|
|||
|
|
@ -161,8 +161,7 @@ impl<'tcx> AutoTraitFinder<'tcx> {
|
|||
let outlives_env = OutlivesEnvironment::new(full_env);
|
||||
let _ = infcx.process_registered_region_obligations(&outlives_env, |ty, _| Ok(ty));
|
||||
|
||||
let region_data =
|
||||
infcx.inner.borrow_mut().unwrap_region_constraints().region_constraint_data().clone();
|
||||
let region_data = infcx.inner.borrow_mut().unwrap_region_constraints().data().clone();
|
||||
|
||||
let vid_to_region = self.map_vid_to_region(®ion_data);
|
||||
|
||||
|
|
|
|||
|
|
@ -819,7 +819,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
candidates.vec.push(AutoImplCandidate)
|
||||
}
|
||||
}
|
||||
ty::Error(_) => {} // do not add an auto trait impl for `ty::Error` for now.
|
||||
ty::Error(_) => {
|
||||
candidates.vec.push(AutoImplCandidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1843,7 +1843,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
// a global and a non-global where-clause.
|
||||
//
|
||||
// Our handling of where-bounds is generally fairly messy but necessary for backwards
|
||||
// compatability, see #50825 for why we need to handle global where-bounds like this.
|
||||
// compatibility, see #50825 for why we need to handle global where-bounds like this.
|
||||
let is_global = |c: ty::PolyTraitPredicate<'tcx>| c.is_global() && !c.has_bound_vars();
|
||||
let param_candidates = candidates
|
||||
.iter()
|
||||
|
|
|
|||
|
|
@ -61,9 +61,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.138"
|
||||
version = "0.1.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53f0ea7fff95b51f84371588f06062557e96bbe363d2b36218ddb806f3ca8611"
|
||||
checksum = "df14d41c5d172a886df3753d54238eefb0f61c96cbd8b363c33ccc92c457bee3"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
|
|
@ -403,9 +403,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unwinding"
|
||||
version = "0.2.4"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2c6cb20f236dae10c69b0b45d82ef50af8b7e45c10e429e7901d26b49b4dbf3"
|
||||
checksum = "51f06a05848f650946acef3bf525fe96612226b61f74ae23ffa4e98bfbb8ab3c"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli 0.31.1",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ edition = "2021"
|
|||
|
||||
[dependencies]
|
||||
core = { path = "../core" }
|
||||
compiler_builtins = { version = "=0.1.138", features = ['rustc-dep-of-std'] }
|
||||
compiler_builtins = { version = "=0.1.140", features = ['rustc-dep-of-std'] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = { version = "0.8.5", default-features = false, features = ["alloc"] }
|
||||
|
|
|
|||
|
|
@ -761,6 +761,26 @@ impl<T> Box<[T]> {
|
|||
};
|
||||
unsafe { Ok(RawVec::from_raw_parts_in(ptr.as_ptr(), len, Global).into_box(len)) }
|
||||
}
|
||||
|
||||
/// Converts the boxed slice into a boxed array.
|
||||
///
|
||||
/// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type.
|
||||
///
|
||||
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
|
||||
#[unstable(feature = "slice_as_array", issue = "133508")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn into_array<const N: usize>(self) -> Option<Box<[T; N]>> {
|
||||
if self.len() == N {
|
||||
let ptr = Self::into_raw(self) as *mut [T; N];
|
||||
|
||||
// SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length.
|
||||
let me = unsafe { Box::from_raw(ptr) };
|
||||
Some(me)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A: Allocator> Box<[T], A> {
|
||||
|
|
|
|||
|
|
@ -2289,6 +2289,10 @@ impl<K, V> FusedIterator for RangeMut<'_, K, V> {}
|
|||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<K: Ord, V> FromIterator<(K, V)> for BTreeMap<K, V> {
|
||||
/// Constructs a `BTreeMap<K, V>` from an iterator of key-value pairs.
|
||||
///
|
||||
/// If the iterator produces any pairs with equal keys,
|
||||
/// all but one of the corresponding values will be dropped.
|
||||
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> BTreeMap<K, V> {
|
||||
let mut inputs: Vec<_> = iter.into_iter().collect();
|
||||
|
||||
|
|
@ -2403,7 +2407,10 @@ where
|
|||
|
||||
#[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)>`.
|
||||
/// Converts a `[(K, V); N]` into a `BTreeMap<K, V>`.
|
||||
///
|
||||
/// If any entries in the array have equal keys,
|
||||
/// all but one of the corresponding values will be dropped.
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::BTreeMap;
|
||||
|
|
|
|||
|
|
@ -1491,6 +1491,11 @@ impl<T: Ord, A: Allocator + Clone> BTreeSet<T, A> {
|
|||
impl<T: Ord, const N: usize> From<[T; N]> for BTreeSet<T> {
|
||||
/// Converts a `[T; N]` into a `BTreeSet<T>`.
|
||||
///
|
||||
/// If the array contains any equal values,
|
||||
/// all but one will be dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::BTreeSet;
|
||||
///
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@
|
|||
#[doc(inline)]
|
||||
#[stable(feature = "alloc_c_string", since = "1.64.0")]
|
||||
pub use self::c_str::CString;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "alloc_c_string", since = "1.64.0")]
|
||||
pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError};
|
||||
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ impl<A: Allocator> RawVecInner<A> {
|
|||
match Self::try_allocate_in(capacity, AllocInit::Uninitialized, alloc, elem_layout) {
|
||||
Ok(this) => {
|
||||
unsafe {
|
||||
// Make it more obvious that a subsquent Vec::reserve(capacity) will not allocate.
|
||||
// Make it more obvious that a subsequent Vec::reserve(capacity) will not allocate.
|
||||
hint::assert_unchecked(!this.needs_to_grow(0, capacity, elem_layout));
|
||||
}
|
||||
this
|
||||
|
|
|
|||
|
|
@ -1084,6 +1084,26 @@ impl<T> Rc<[T]> {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the reference-counted slice into a reference-counted array.
|
||||
///
|
||||
/// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type.
|
||||
///
|
||||
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
|
||||
#[unstable(feature = "slice_as_array", issue = "133508")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn into_array<const N: usize>(self) -> Option<Rc<[T; N]>> {
|
||||
if self.len() == N {
|
||||
let ptr = Self::into_raw(self) as *const [T; N];
|
||||
|
||||
// SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length.
|
||||
let me = unsafe { Rc::from_raw(ptr) };
|
||||
Some(me)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A: Allocator> Rc<[T], A> {
|
||||
|
|
|
|||
|
|
@ -1203,6 +1203,26 @@ impl<T> Arc<[T]> {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the reference-counted slice into a reference-counted array.
|
||||
///
|
||||
/// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type.
|
||||
///
|
||||
/// If `N` is not exactly equal to the length of `self`, then this method returns `None`.
|
||||
#[unstable(feature = "slice_as_array", issue = "133508")]
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn into_array<const N: usize>(self) -> Option<Arc<[T; N]>> {
|
||||
if self.len() == N {
|
||||
let ptr = Self::into_raw(self) as *const [T; N];
|
||||
|
||||
// SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length.
|
||||
let me = unsafe { Arc::from_raw(ptr) };
|
||||
Some(me)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, A: Allocator> Arc<[T], A> {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ fn check_is_sorted<T: Ord + Clone + Debug, S: Sort>(v: &mut [T]) {
|
|||
known_good_stable_sort::sort(known_good_sorted_vec.as_mut_slice());
|
||||
|
||||
if is_small_test {
|
||||
eprintln!("Orginal: {:?}", v_orig);
|
||||
eprintln!("Original: {:?}", v_orig);
|
||||
eprintln!("Expected: {:?}", known_good_sorted_vec);
|
||||
eprintln!("Got: {:?}", v);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ macro_rules! pow_bench_template {
|
|||
let mut exp_iter = black_box(&exp_array).into_iter();
|
||||
|
||||
(0..ITERATIONS).fold((0 as IntType, false), |acc, _| {
|
||||
// Sometimes constants don't propogate all the way to the
|
||||
// Sometimes constants don't propagate all the way to the
|
||||
// inside of the loop, so we call a custom expression every cycle
|
||||
// rather than iter::repeat(CONST)
|
||||
let base: IntType = $base_macro!(base_iter);
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@
|
|||
#[doc(inline)]
|
||||
#[stable(feature = "core_c_str", since = "1.64.0")]
|
||||
pub use self::c_str::CStr;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
|
||||
pub use self::c_str::FromBytesUntilNulError;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "core_c_str", since = "1.64.0")]
|
||||
pub use self::c_str::FromBytesWithNulError;
|
||||
use crate::fmt;
|
||||
|
|
|
|||
111
library/core/src/intrinsics/fallback.rs
Normal file
111
library/core/src/intrinsics/fallback.rs
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#![unstable(
|
||||
feature = "core_intrinsics_fallbacks",
|
||||
reason = "The fallbacks will never be stable, as they exist only to be called \
|
||||
by the fallback MIR, but they're exported so they can be tested on \
|
||||
platforms where the fallback MIR isn't actually used",
|
||||
issue = "none"
|
||||
)]
|
||||
#![allow(missing_docs)]
|
||||
|
||||
#[const_trait]
|
||||
pub trait CarryingMulAdd: Copy + 'static {
|
||||
type Unsigned: Copy + 'static;
|
||||
fn carrying_mul_add(
|
||||
self,
|
||||
multiplicand: Self,
|
||||
addend: Self,
|
||||
carry: Self,
|
||||
) -> (Self::Unsigned, Self);
|
||||
}
|
||||
|
||||
macro_rules! impl_carrying_mul_add_by_widening {
|
||||
($($t:ident $u:ident $w:ident,)+) => {$(
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
impl const CarryingMulAdd for $t {
|
||||
type Unsigned = $u;
|
||||
#[inline]
|
||||
fn carrying_mul_add(self, a: Self, b: Self, c: Self) -> ($u, $t) {
|
||||
let wide = (self as $w) * (a as $w) + (b as $w) + (c as $w);
|
||||
(wide as _, (wide >> Self::BITS) as _)
|
||||
}
|
||||
}
|
||||
)+};
|
||||
}
|
||||
impl_carrying_mul_add_by_widening! {
|
||||
u8 u8 u16,
|
||||
u16 u16 u32,
|
||||
u32 u32 u64,
|
||||
u64 u64 u128,
|
||||
usize usize UDoubleSize,
|
||||
i8 u8 i16,
|
||||
i16 u16 i32,
|
||||
i32 u32 i64,
|
||||
i64 u64 i128,
|
||||
isize usize UDoubleSize,
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
type UDoubleSize = u32;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
type UDoubleSize = u64;
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
type UDoubleSize = u128;
|
||||
|
||||
#[inline]
|
||||
const fn wide_mul_u128(a: u128, b: u128) -> (u128, u128) {
|
||||
#[inline]
|
||||
const fn to_low_high(x: u128) -> [u128; 2] {
|
||||
const MASK: u128 = u64::MAX as _;
|
||||
[x & MASK, x >> 64]
|
||||
}
|
||||
#[inline]
|
||||
const fn from_low_high(x: [u128; 2]) -> u128 {
|
||||
x[0] | (x[1] << 64)
|
||||
}
|
||||
#[inline]
|
||||
const fn scalar_mul(low_high: [u128; 2], k: u128) -> [u128; 3] {
|
||||
let [x, c] = to_low_high(k * low_high[0]);
|
||||
let [y, z] = to_low_high(k * low_high[1] + c);
|
||||
[x, y, z]
|
||||
}
|
||||
let a = to_low_high(a);
|
||||
let b = to_low_high(b);
|
||||
let low = scalar_mul(a, b[0]);
|
||||
let high = scalar_mul(a, b[1]);
|
||||
let r0 = low[0];
|
||||
let [r1, c] = to_low_high(low[1] + high[0]);
|
||||
let [r2, c] = to_low_high(low[2] + high[1] + c);
|
||||
let r3 = high[2] + c;
|
||||
(from_low_high([r0, r1]), from_low_high([r2, r3]))
|
||||
}
|
||||
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
impl const CarryingMulAdd for u128 {
|
||||
type Unsigned = u128;
|
||||
#[inline]
|
||||
fn carrying_mul_add(self, b: u128, c: u128, d: u128) -> (u128, u128) {
|
||||
let (low, mut high) = wide_mul_u128(self, b);
|
||||
let (low, carry) = u128::overflowing_add(low, c);
|
||||
high += carry as u128;
|
||||
let (low, carry) = u128::overflowing_add(low, d);
|
||||
high += carry as u128;
|
||||
(low, high)
|
||||
}
|
||||
}
|
||||
|
||||
#[rustc_const_unstable(feature = "core_intrinsics_fallbacks", issue = "none")]
|
||||
impl const CarryingMulAdd for i128 {
|
||||
type Unsigned = u128;
|
||||
#[inline]
|
||||
fn carrying_mul_add(self, b: i128, c: i128, d: i128) -> (u128, i128) {
|
||||
let (low, high) = wide_mul_u128(self as u128, b as u128);
|
||||
let mut high = high as i128;
|
||||
high = high.wrapping_add(i128::wrapping_mul(self >> 127, b));
|
||||
high = high.wrapping_add(i128::wrapping_mul(self, b >> 127));
|
||||
let (low, carry) = u128::overflowing_add(low, c as u128);
|
||||
high = high.wrapping_add((carry as i128) + (c >> 127));
|
||||
let (low, carry) = u128::overflowing_add(low, d as u128);
|
||||
high = high.wrapping_add((carry as i128) + (d >> 127));
|
||||
(low, high)
|
||||
}
|
||||
}
|
||||
|
|
@ -68,6 +68,7 @@ use crate::marker::{DiscriminantKind, Tuple};
|
|||
use crate::mem::SizedTypeProperties;
|
||||
use crate::{ptr, ub_checks};
|
||||
|
||||
pub mod fallback;
|
||||
pub mod mir;
|
||||
pub mod simd;
|
||||
|
||||
|
|
@ -3305,6 +3306,34 @@ pub const fn mul_with_overflow<T: Copy>(_x: T, _y: T) -> (T, bool) {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Performs full-width multiplication and addition with a carry:
|
||||
/// `multiplier * multiplicand + addend + carry`.
|
||||
///
|
||||
/// This is possible without any overflow. For `uN`:
|
||||
/// MAX * MAX + MAX + MAX
|
||||
/// => (2ⁿ-1) × (2ⁿ-1) + (2ⁿ-1) + (2ⁿ-1)
|
||||
/// => (2²ⁿ - 2ⁿ⁺¹ + 1) + (2ⁿ⁺¹ - 2)
|
||||
/// => 2²ⁿ - 1
|
||||
///
|
||||
/// For `iN`, the upper bound is MIN * MIN + MAX + MAX => 2²ⁿ⁻² + 2ⁿ - 2,
|
||||
/// and the lower bound is MAX * MIN + MIN + MIN => -2²ⁿ⁻² - 2ⁿ + 2ⁿ⁺¹.
|
||||
///
|
||||
/// This currently supports unsigned integers *only*, no signed ones.
|
||||
/// The stabilized versions of this intrinsic are available on integers.
|
||||
#[unstable(feature = "core_intrinsics", issue = "none")]
|
||||
#[rustc_const_unstable(feature = "const_carrying_mul_add", issue = "85532")]
|
||||
#[rustc_nounwind]
|
||||
#[cfg_attr(not(bootstrap), rustc_intrinsic)]
|
||||
#[cfg_attr(not(bootstrap), miri::intrinsic_fallback_is_spec)]
|
||||
pub const fn carrying_mul_add<T: ~const fallback::CarryingMulAdd<Unsigned = U>, U>(
|
||||
multiplier: T,
|
||||
multiplicand: T,
|
||||
addend: T,
|
||||
carry: T,
|
||||
) -> (U, T) {
|
||||
multiplier.carrying_mul_add(multiplicand, addend, carry)
|
||||
}
|
||||
|
||||
/// Performs an exact division, resulting in undefined behavior where
|
||||
/// `x % y != 0` or `y == 0` or `x == T::MIN && y == -1`
|
||||
///
|
||||
|
|
@ -3940,6 +3969,21 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
#[rustc_nounwind]
|
||||
#[inline]
|
||||
#[rustc_intrinsic]
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
|
||||
#[cfg(bootstrap)]
|
||||
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
|
||||
// SAFETY: The caller provided single non-overlapping items behind
|
||||
// pointers, so swapping them with `count: 1` is fine.
|
||||
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
||||
}
|
||||
|
||||
#[cfg(bootstrap)]
|
||||
pub use typed_swap as typed_swap_nonoverlapping;
|
||||
|
||||
/// Non-overlapping *typed* swap of a single value.
|
||||
///
|
||||
/// The codegen backends will replace this with a better implementation when
|
||||
|
|
@ -3953,9 +3997,10 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool {
|
|||
#[rustc_nounwind]
|
||||
#[inline]
|
||||
#[rustc_intrinsic]
|
||||
// Const-unstable because `swap_nonoverlapping` is const-unstable.
|
||||
#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")]
|
||||
pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) {
|
||||
#[rustc_intrinsic_const_stable_indirect]
|
||||
#[rustc_allow_const_fn_unstable(const_swap_nonoverlapping)] // this is anyway not called since CTFE implements the intrinsic
|
||||
#[cfg(not(bootstrap))]
|
||||
pub const unsafe fn typed_swap_nonoverlapping<T>(x: *mut T, y: *mut T) {
|
||||
// SAFETY: The caller provided single non-overlapping items behind
|
||||
// pointers, so swapping them with `count: 1` is fine.
|
||||
unsafe { ptr::swap_nonoverlapping(x, y, 1) };
|
||||
|
|
@ -4364,13 +4409,11 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
|
|||
///
|
||||
/// Behavior is undefined if any of the following conditions are violated:
|
||||
///
|
||||
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes, and must remain valid even
|
||||
/// when `dst` is written for `count * size_of::<T>()` bytes. (This means if the memory ranges
|
||||
/// overlap, the two pointers must not be subject to aliasing restrictions relative to each
|
||||
/// other.)
|
||||
/// * `src` must be [valid] for reads of `count * size_of::<T>()` bytes.
|
||||
///
|
||||
/// * `dst` must be [valid] for writes of `count * size_of::<T>()` bytes, and must remain valid even
|
||||
/// when `src` is read for `count * size_of::<T>()` bytes.
|
||||
/// when `src` is read for `count * size_of::<T>()` bytes. (This means if the memory ranges
|
||||
/// overlap, the `dst` pointer must not be invalidated by `src` reads.)
|
||||
///
|
||||
/// * Both `src` and `dst` must be properly aligned.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -3051,6 +3051,7 @@ pub trait Iterator {
|
|||
///
|
||||
/// // we can still use `iter`, as there are more elements.
|
||||
/// assert_eq!(iter.next(), Some(&-1));
|
||||
/// assert_eq!(iter.next_back(), Some(&3));
|
||||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
|
|
|
|||
|
|
@ -110,8 +110,8 @@
|
|||
#![cfg_attr(bootstrap, feature(do_not_recommend))]
|
||||
#![feature(array_ptr_get)]
|
||||
#![feature(asm_experimental_arch)]
|
||||
#![feature(const_carrying_mul_add)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_typed_swap)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(coverage_attribute)]
|
||||
#![feature(internal_impls_macro)]
|
||||
|
|
|
|||
|
|
@ -725,12 +725,12 @@ pub unsafe fn uninitialized<T>() -> T {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_diagnostic_item = "mem_swap"]
|
||||
pub const fn swap<T>(x: &mut T, y: &mut T) {
|
||||
// SAFETY: `&mut` guarantees these are typed readable and writable
|
||||
// as well as non-overlapping.
|
||||
unsafe { intrinsics::typed_swap(x, y) }
|
||||
unsafe { intrinsics::typed_swap_nonoverlapping(x, y) }
|
||||
}
|
||||
|
||||
/// Replaces `dest` with the default value of `T`, returning the previous `dest` value.
|
||||
|
|
|
|||
|
|
@ -228,134 +228,6 @@ macro_rules! midpoint_impl {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! widening_impl {
|
||||
($SelfT:ty, $WideT:ty, $BITS:literal, unsigned) => {
|
||||
/// Calculates the complete product `self * rhs` without the possibility to overflow.
|
||||
///
|
||||
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
|
||||
/// of the result as two separate values, in that order.
|
||||
///
|
||||
/// If you also need to add a carry to the wide result, then you want
|
||||
/// [`Self::carrying_mul`] instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// Please note that this example is shared between integer types.
|
||||
/// Which explains why `u32` is used here.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// assert_eq!(5u32.widening_mul(2), (10, 0));
|
||||
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
|
||||
/// ```
|
||||
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
|
||||
// note: longer-term this should be done via an intrinsic,
|
||||
// but for now we can deal without an impl for u128/i128
|
||||
// SAFETY: overflow will be contained within the wider types
|
||||
let wide = unsafe { (self as $WideT).unchecked_mul(rhs as $WideT) };
|
||||
(wide as $SelfT, (wide >> $BITS) as $SelfT)
|
||||
}
|
||||
|
||||
/// Calculates the "full multiplication" `self * rhs + carry`
|
||||
/// without the possibility to overflow.
|
||||
///
|
||||
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
|
||||
/// of the result as two separate values, in that order.
|
||||
///
|
||||
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
|
||||
/// additional amount of overflow. This allows for chaining together multiple
|
||||
/// multiplications to create "big integers" which represent larger values.
|
||||
///
|
||||
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// Please note that this example is shared between integer types.
|
||||
/// Which explains why `u32` is used here.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
|
||||
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
|
||||
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
|
||||
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
|
||||
#[doc = concat!("assert_eq!(",
|
||||
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
|
||||
"(0, ", stringify!($SelfT), "::MAX));"
|
||||
)]
|
||||
/// ```
|
||||
///
|
||||
/// This is the core operation needed for scalar multiplication when
|
||||
/// implementing it for wider-than-native types.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
|
||||
/// let mut carry = 0;
|
||||
/// for d in little_endian_digits.iter_mut() {
|
||||
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
|
||||
/// }
|
||||
/// if carry != 0 {
|
||||
/// little_endian_digits.push(carry);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let mut v = vec![10, 20];
|
||||
/// scalar_mul_eq(&mut v, 3);
|
||||
/// assert_eq!(v, [30, 60]);
|
||||
///
|
||||
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
|
||||
/// let mut v = vec![0x4321, 0x8765];
|
||||
/// scalar_mul_eq(&mut v, 0xFEED);
|
||||
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
|
||||
/// ```
|
||||
///
|
||||
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
|
||||
/// except that it gives the value of the overflow instead of just whether one happened:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// let r = u8::carrying_mul(7, 13, 0);
|
||||
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
|
||||
/// let r = u8::carrying_mul(13, 42, 0);
|
||||
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
|
||||
/// ```
|
||||
///
|
||||
/// The value of the first field in the returned tuple matches what you'd get
|
||||
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
|
||||
/// [`wrapping_add`](Self::wrapping_add) methods:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// assert_eq!(
|
||||
/// 789_u16.carrying_mul(456, 123).0,
|
||||
/// 789_u16.wrapping_mul(456).wrapping_add(123),
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
|
||||
// note: longer-term this should be done via an intrinsic,
|
||||
// but for now we can deal without an impl for u128/i128
|
||||
// SAFETY: overflow will be contained within the wider types
|
||||
let wide = unsafe {
|
||||
(self as $WideT).unchecked_mul(rhs as $WideT).unchecked_add(carry as $WideT)
|
||||
};
|
||||
(wide as $SelfT, (wide >> $BITS) as $SelfT)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl i8 {
|
||||
int_impl! {
|
||||
Self = i8,
|
||||
|
|
@ -576,7 +448,6 @@ impl u8 {
|
|||
from_xe_bytes_doc = u8_xe_bytes_doc!(),
|
||||
bound_condition = "",
|
||||
}
|
||||
widening_impl! { u8, u16, 8, unsigned }
|
||||
midpoint_impl! { u8, u16, unsigned }
|
||||
|
||||
/// Checks if the value is within the ASCII range.
|
||||
|
|
@ -1192,7 +1063,6 @@ impl u16 {
|
|||
from_xe_bytes_doc = "",
|
||||
bound_condition = "",
|
||||
}
|
||||
widening_impl! { u16, u32, 16, unsigned }
|
||||
midpoint_impl! { u16, u32, unsigned }
|
||||
|
||||
/// Checks if the value is a Unicode surrogate code point, which are disallowed values for [`char`].
|
||||
|
|
@ -1240,7 +1110,6 @@ impl u32 {
|
|||
from_xe_bytes_doc = "",
|
||||
bound_condition = "",
|
||||
}
|
||||
widening_impl! { u32, u64, 32, unsigned }
|
||||
midpoint_impl! { u32, u64, unsigned }
|
||||
}
|
||||
|
||||
|
|
@ -1264,7 +1133,6 @@ impl u64 {
|
|||
from_xe_bytes_doc = "",
|
||||
bound_condition = "",
|
||||
}
|
||||
widening_impl! { u64, u128, 64, unsigned }
|
||||
midpoint_impl! { u64, u128, unsigned }
|
||||
}
|
||||
|
||||
|
|
@ -1314,7 +1182,6 @@ impl usize {
|
|||
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
|
||||
bound_condition = " on 16-bit targets",
|
||||
}
|
||||
widening_impl! { usize, u32, 16, unsigned }
|
||||
midpoint_impl! { usize, u32, unsigned }
|
||||
}
|
||||
|
||||
|
|
@ -1339,7 +1206,6 @@ impl usize {
|
|||
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
|
||||
bound_condition = " on 32-bit targets",
|
||||
}
|
||||
widening_impl! { usize, u64, 32, unsigned }
|
||||
midpoint_impl! { usize, u64, unsigned }
|
||||
}
|
||||
|
||||
|
|
@ -1364,7 +1230,6 @@ impl usize {
|
|||
from_xe_bytes_doc = usize_isize_from_xe_bytes_doc!(),
|
||||
bound_condition = " on 64-bit targets",
|
||||
}
|
||||
widening_impl! { usize, u128, 64, unsigned }
|
||||
midpoint_impl! { usize, u128, unsigned }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3347,6 +3347,122 @@ macro_rules! uint_impl {
|
|||
unsafe { mem::transmute(bytes) }
|
||||
}
|
||||
|
||||
/// Calculates the complete product `self * rhs` without the possibility to overflow.
|
||||
///
|
||||
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
|
||||
/// of the result as two separate values, in that order.
|
||||
///
|
||||
/// If you also need to add a carry to the wide result, then you want
|
||||
/// [`Self::carrying_mul`] instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// Please note that this example is shared between integer types.
|
||||
/// Which explains why `u32` is used here.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// assert_eq!(5u32.widening_mul(2), (10, 0));
|
||||
/// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2));
|
||||
/// ```
|
||||
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn widening_mul(self, rhs: Self) -> (Self, Self) {
|
||||
Self::carrying_mul(self, rhs, 0)
|
||||
}
|
||||
|
||||
/// Calculates the "full multiplication" `self * rhs + carry`
|
||||
/// without the possibility to overflow.
|
||||
///
|
||||
/// This returns the low-order (wrapping) bits and the high-order (overflow) bits
|
||||
/// of the result as two separate values, in that order.
|
||||
///
|
||||
/// Performs "long multiplication" which takes in an extra amount to add, and may return an
|
||||
/// additional amount of overflow. This allows for chaining together multiple
|
||||
/// multiplications to create "big integers" which represent larger values.
|
||||
///
|
||||
/// If you don't need the `carry`, then you can use [`Self::widening_mul`] instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage:
|
||||
///
|
||||
/// Please note that this example is shared between integer types.
|
||||
/// Which explains why `u32` is used here.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// assert_eq!(5u32.carrying_mul(2, 0), (10, 0));
|
||||
/// assert_eq!(5u32.carrying_mul(2, 10), (20, 0));
|
||||
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 0), (1410065408, 2));
|
||||
/// assert_eq!(1_000_000_000u32.carrying_mul(10, 10), (1410065418, 2));
|
||||
#[doc = concat!("assert_eq!(",
|
||||
stringify!($SelfT), "::MAX.carrying_mul(", stringify!($SelfT), "::MAX, ", stringify!($SelfT), "::MAX), ",
|
||||
"(0, ", stringify!($SelfT), "::MAX));"
|
||||
)]
|
||||
/// ```
|
||||
///
|
||||
/// This is the core operation needed for scalar multiplication when
|
||||
/// implementing it for wider-than-native types.
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// fn scalar_mul_eq(little_endian_digits: &mut Vec<u16>, multiplicand: u16) {
|
||||
/// let mut carry = 0;
|
||||
/// for d in little_endian_digits.iter_mut() {
|
||||
/// (*d, carry) = d.carrying_mul(multiplicand, carry);
|
||||
/// }
|
||||
/// if carry != 0 {
|
||||
/// little_endian_digits.push(carry);
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let mut v = vec![10, 20];
|
||||
/// scalar_mul_eq(&mut v, 3);
|
||||
/// assert_eq!(v, [30, 60]);
|
||||
///
|
||||
/// assert_eq!(0x87654321_u64 * 0xFEED, 0x86D3D159E38D);
|
||||
/// let mut v = vec![0x4321, 0x8765];
|
||||
/// scalar_mul_eq(&mut v, 0xFEED);
|
||||
/// assert_eq!(v, [0xE38D, 0xD159, 0x86D3]);
|
||||
/// ```
|
||||
///
|
||||
/// If `carry` is zero, this is similar to [`overflowing_mul`](Self::overflowing_mul),
|
||||
/// except that it gives the value of the overflow instead of just whether one happened:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// let r = u8::carrying_mul(7, 13, 0);
|
||||
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13));
|
||||
/// let r = u8::carrying_mul(13, 42, 0);
|
||||
/// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(13, 42));
|
||||
/// ```
|
||||
///
|
||||
/// The value of the first field in the returned tuple matches what you'd get
|
||||
/// by combining the [`wrapping_mul`](Self::wrapping_mul) and
|
||||
/// [`wrapping_add`](Self::wrapping_add) methods:
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(bigint_helper_methods)]
|
||||
/// assert_eq!(
|
||||
/// 789_u16.carrying_mul(456, 123).0,
|
||||
/// 789_u16.wrapping_mul(456).wrapping_add(123),
|
||||
/// );
|
||||
/// ```
|
||||
#[unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")]
|
||||
#[must_use = "this returns the result of the operation, \
|
||||
without modifying the original"]
|
||||
#[inline]
|
||||
pub const fn carrying_mul(self, rhs: Self, carry: Self) -> (Self, Self) {
|
||||
intrinsics::carrying_mul_add(self, rhs, 0, carry)
|
||||
}
|
||||
|
||||
/// New code should prefer to use
|
||||
#[doc = concat!("[`", stringify!($SelfT), "::MIN", "`] instead.")]
|
||||
///
|
||||
|
|
|
|||
|
|
@ -595,7 +595,7 @@
|
|||
//! [drop-impl]: self#implementing-drop-for-types-with-address-sensitive-states
|
||||
//!
|
||||
//! The [`drop`] function takes [`&mut self`], but this is called *even if that `self` has been
|
||||
//! pinned*! Implementing [`Drop`] for a type with address-sensitive states, because if `self` was
|
||||
//! pinned*! Implementing [`Drop`] for a type with address-sensitive states requires some care, because if `self` was
|
||||
//! indeed in an address-sensitive state before [`drop`] was called, it is as if the compiler
|
||||
//! automatically called [`Pin::get_unchecked_mut`].
|
||||
//!
|
||||
|
|
|
|||
|
|
@ -1009,9 +1009,8 @@ pub const fn slice_from_raw_parts_mut<T>(data: *mut T, len: usize) -> *mut [T] {
|
|||
/// ```
|
||||
#[inline]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[rustc_diagnostic_item = "ptr_swap"]
|
||||
#[rustc_const_stable_indirect]
|
||||
pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
|
||||
// Give ourselves some scratch space to work with.
|
||||
// We do not have to worry about drops: `MaybeUninit` does nothing when dropped.
|
||||
|
|
|
|||
|
|
@ -1594,7 +1594,7 @@ impl<T: ?Sized> *mut T {
|
|||
///
|
||||
/// [`ptr::swap`]: crate::ptr::swap()
|
||||
#[stable(feature = "pointer_methods", since = "1.26.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline(always)]
|
||||
pub const unsafe fn swap(self, with: *mut T)
|
||||
where
|
||||
|
|
|
|||
|
|
@ -1146,7 +1146,7 @@ impl<T: ?Sized> NonNull<T> {
|
|||
/// [`ptr::swap`]: crate::ptr::swap()
|
||||
#[inline(always)]
|
||||
#[stable(feature = "non_null_convenience", since = "1.80.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
pub const unsafe fn swap(self, with: NonNull<T>)
|
||||
where
|
||||
T: Sized,
|
||||
|
|
|
|||
|
|
@ -913,7 +913,7 @@ impl<T> [T] {
|
|||
/// assert!(v == ["a", "b", "e", "d", "c"]);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
|
||||
#[rustc_const_stable(feature = "const_swap", since = "CURRENT_RUSTC_VERSION")]
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub const fn swap(&mut self, a: usize, b: usize) {
|
||||
|
|
|
|||
|
|
@ -125,3 +125,71 @@ fn test_three_way_compare_in_const_contexts() {
|
|||
assert_eq!(SIGNED_EQUAL, Equal);
|
||||
assert_eq!(SIGNED_GREATER, Greater);
|
||||
}
|
||||
|
||||
fn fallback_cma<T: core::intrinsics::fallback::CarryingMulAdd>(
|
||||
a: T,
|
||||
b: T,
|
||||
c: T,
|
||||
d: T,
|
||||
) -> (T::Unsigned, T) {
|
||||
a.carrying_mul_add(b, c, d)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carrying_mul_add_fallback_u32() {
|
||||
let r = fallback_cma::<u32>(0x9e37_79b9, 0x7f4a_7c15, 0xf39c_c060, 0x5ced_c834);
|
||||
assert_eq!(r, (0x2087_20c1, 0x4eab_8e1d));
|
||||
let r = fallback_cma::<u32>(0x1082_276b, 0xf3a2_7251, 0xf86c_6a11, 0xd0c1_8e95);
|
||||
assert_eq!(r, (0x7aa0_1781, 0x0fb6_0528));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carrying_mul_add_fallback_i32() {
|
||||
let r = fallback_cma::<i32>(-1, -1, -1, -1);
|
||||
assert_eq!(r, (u32::MAX, -1));
|
||||
let r = fallback_cma::<i32>(1, -1, 1, 1);
|
||||
assert_eq!(r, (1, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carrying_mul_add_fallback_u128() {
|
||||
assert_eq!(fallback_cma::<u128>(u128::MAX, u128::MAX, 0, 0), (1, u128::MAX - 1));
|
||||
assert_eq!(fallback_cma::<u128>(1, 1, 1, 1), (3, 0));
|
||||
assert_eq!(fallback_cma::<u128>(0, 0, u128::MAX, u128::MAX), (u128::MAX - 1, 1));
|
||||
assert_eq!(
|
||||
fallback_cma::<u128>(u128::MAX, u128::MAX, u128::MAX, u128::MAX),
|
||||
(u128::MAX, u128::MAX),
|
||||
);
|
||||
|
||||
let r = fallback_cma::<u128>(
|
||||
0x243f6a8885a308d313198a2e03707344,
|
||||
0xa4093822299f31d0082efa98ec4e6c89,
|
||||
0x452821e638d01377be5466cf34e90c6c,
|
||||
0xc0ac29b7c97c50dd3f84d5b5b5470917,
|
||||
);
|
||||
assert_eq!(r, (0x8050ec20ed554e40338d277e00b674e7, 0x1739ee6cea07da409182d003859b59d8));
|
||||
let r = fallback_cma::<u128>(
|
||||
0x9216d5d98979fb1bd1310ba698dfb5ac,
|
||||
0x2ffd72dbd01adfb7b8e1afed6a267e96,
|
||||
0xba7c9045f12c7f9924a19947b3916cf7,
|
||||
0x0801f2e2858efc16636920d871574e69,
|
||||
);
|
||||
assert_eq!(r, (0x185525545fdb2fefb502a3a602efd628, 0x1b62d35fe3bff6b566f99667ef7ebfd6));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn carrying_mul_add_fallback_i128() {
|
||||
assert_eq!(fallback_cma::<i128>(-1, -1, 0, 0), (1, 0));
|
||||
let r = fallback_cma::<i128>(-1, -1, -1, -1);
|
||||
assert_eq!(r, (u128::MAX, -1));
|
||||
let r = fallback_cma::<i128>(1, -1, 1, 1);
|
||||
assert_eq!(r, (1, 0));
|
||||
assert_eq!(
|
||||
fallback_cma::<i128>(i128::MAX, i128::MAX, i128::MAX, i128::MAX),
|
||||
(u128::MAX, i128::MAX / 2),
|
||||
);
|
||||
assert_eq!(
|
||||
fallback_cma::<i128>(i128::MIN, i128::MIN, i128::MAX, i128::MAX),
|
||||
(u128::MAX - 1, -(i128::MIN / 2)),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,10 +15,10 @@
|
|||
#![feature(clone_to_uninit)]
|
||||
#![feature(const_black_box)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_swap)]
|
||||
#![feature(const_swap_nonoverlapping)]
|
||||
#![feature(const_trait_impl)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(core_intrinsics_fallbacks)]
|
||||
#![feature(core_io_borrowed_buf)]
|
||||
#![feature(core_private_bignum)]
|
||||
#![feature(core_private_diy_float)]
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
|
|||
panic_unwind = { path = "../panic_unwind", optional = true }
|
||||
panic_abort = { path = "../panic_abort" }
|
||||
core = { path = "../core", public = true }
|
||||
compiler_builtins = { version = "=0.1.138" }
|
||||
compiler_builtins = { version = "=0.1.140" }
|
||||
unwind = { path = "../unwind" }
|
||||
hashbrown = { version = "0.15", default-features = false, features = [
|
||||
'rustc-dep-of-std',
|
||||
|
|
|
|||
|
|
@ -1446,6 +1446,11 @@ impl<K, V, const N: usize> From<[(K, V); N]> for HashMap<K, V, RandomState>
|
|||
where
|
||||
K: Eq + Hash,
|
||||
{
|
||||
/// Converts a `[(K, V); N]` into a `HashMap<K, V>`.
|
||||
///
|
||||
/// If any entries in the array have equal keys,
|
||||
/// all but one of the corresponding values will be dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
|
@ -3219,6 +3224,10 @@ where
|
|||
K: Eq + Hash,
|
||||
S: BuildHasher + Default,
|
||||
{
|
||||
/// Constructs a `HashMap<K, V>` from an iterator of key-value pairs.
|
||||
///
|
||||
/// If the iterator produces any pairs with equal keys,
|
||||
/// all but one of the corresponding values will be dropped.
|
||||
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> {
|
||||
let mut map = HashMap::with_hasher(Default::default());
|
||||
map.extend(iter);
|
||||
|
|
|
|||
|
|
@ -1091,6 +1091,11 @@ impl<T, const N: usize> From<[T; N]> for HashSet<T, RandomState>
|
|||
where
|
||||
T: Eq + Hash,
|
||||
{
|
||||
/// Converts a `[T; N]` into a `HashSet<T>`.
|
||||
///
|
||||
/// If the array contains any equal values,
|
||||
/// all but one will be dropped.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -179,19 +179,19 @@ pub use core::ffi::{
|
|||
c_ulong, c_ulonglong, c_ushort,
|
||||
};
|
||||
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstr_from_bytes_until_nul", since = "1.69.0")]
|
||||
pub use self::c_str::FromBytesUntilNulError;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstr_from_bytes", since = "1.10.0")]
|
||||
pub use self::c_str::FromBytesWithNulError;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstring_from_vec_with_nul", since = "1.58.0")]
|
||||
pub use self::c_str::FromVecWithNulError;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "cstring_into", since = "1.7.0")]
|
||||
pub use self::c_str::IntoStringError;
|
||||
#[doc(no_inline)]
|
||||
#[doc(inline)]
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::c_str::NulError;
|
||||
#[doc(inline)]
|
||||
|
|
|
|||
|
|
@ -1953,3 +1953,32 @@ fn test_rename_directory_to_non_empty_directory() {
|
|||
|
||||
error!(fs::rename(source_path, target_path), 145); // ERROR_DIR_NOT_EMPTY
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_symlink() {
|
||||
let tmpdir = tmpdir();
|
||||
let original = tmpdir.join("original");
|
||||
let dest = tmpdir.join("dest");
|
||||
let not_exist = Path::new("does not exist");
|
||||
|
||||
symlink_file(not_exist, &original).unwrap();
|
||||
fs::rename(&original, &dest).unwrap();
|
||||
// Make sure that renaming `original` to `dest` preserves the symlink.
|
||||
assert_eq!(fs::read_link(&dest).unwrap().as_path(), not_exist);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(windows)]
|
||||
fn test_rename_junction() {
|
||||
let tmpdir = tmpdir();
|
||||
let original = tmpdir.join("original");
|
||||
let dest = tmpdir.join("dest");
|
||||
let not_exist = Path::new("does not exist");
|
||||
|
||||
junction_point(¬_exist, &original).unwrap();
|
||||
fs::rename(&original, &dest).unwrap();
|
||||
|
||||
// Make sure that renaming `original` to `dest` preserves the junction point.
|
||||
// Junction links are always absolute so we just check the file name is correct.
|
||||
assert_eq!(fs::read_link(&dest).unwrap().file_name(), Some(not_exist.as_os_str()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,26 +10,22 @@ use crate::sync::{Condvar, Mutex};
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Barrier};
|
||||
/// use std::sync::Barrier;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let n = 10;
|
||||
/// let mut handles = Vec::with_capacity(n);
|
||||
/// let barrier = Arc::new(Barrier::new(n));
|
||||
/// for _ in 0..n {
|
||||
/// let c = Arc::clone(&barrier);
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// handles.push(thread::spawn(move || {
|
||||
/// println!("before wait");
|
||||
/// c.wait();
|
||||
/// println!("after wait");
|
||||
/// }));
|
||||
/// }
|
||||
/// // Wait for other threads to finish.
|
||||
/// for handle in handles {
|
||||
/// handle.join().unwrap();
|
||||
/// }
|
||||
/// let barrier = Barrier::new(n);
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..n {
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// s.spawn(|| {
|
||||
/// println!("before wait");
|
||||
/// barrier.wait();
|
||||
/// println!("after wait");
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct Barrier {
|
||||
|
|
@ -105,26 +101,22 @@ impl Barrier {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::sync::{Arc, Barrier};
|
||||
/// use std::sync::Barrier;
|
||||
/// use std::thread;
|
||||
///
|
||||
/// let n = 10;
|
||||
/// let mut handles = Vec::with_capacity(n);
|
||||
/// let barrier = Arc::new(Barrier::new(n));
|
||||
/// for _ in 0..n {
|
||||
/// let c = Arc::clone(&barrier);
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// handles.push(thread::spawn(move || {
|
||||
/// println!("before wait");
|
||||
/// c.wait();
|
||||
/// println!("after wait");
|
||||
/// }));
|
||||
/// }
|
||||
/// // Wait for other threads to finish.
|
||||
/// for handle in handles {
|
||||
/// handle.join().unwrap();
|
||||
/// }
|
||||
/// let barrier = Barrier::new(n);
|
||||
/// thread::scope(|s| {
|
||||
/// for _ in 0..n {
|
||||
/// // The same messages will be printed together.
|
||||
/// // You will NOT see any interleaving.
|
||||
/// s.spawn(|| {
|
||||
/// println!("before wait");
|
||||
/// barrier.wait();
|
||||
/// println!("after wait");
|
||||
/// });
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub fn wait(&self) -> BarrierWaitResult {
|
||||
|
|
|
|||
|
|
@ -168,7 +168,8 @@ cfg_has_statx! {{
|
|||
) -> c_int
|
||||
}
|
||||
|
||||
if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
|
||||
let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
|
||||
if statx_availability == STATX_STATE::Unavailable as u8 {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
@ -200,6 +201,9 @@ cfg_has_statx! {{
|
|||
return None;
|
||||
}
|
||||
}
|
||||
if statx_availability == STATX_STATE::Unknown as u8 {
|
||||
STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// We cannot fill `stat64` exhaustively because of private padding fields.
|
||||
let mut stat: stat64 = mem::zeroed();
|
||||
|
|
@ -1944,7 +1948,7 @@ fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)>
|
|||
#[cfg(target_os = "espidf")]
|
||||
fn open_to_and_set_permissions(
|
||||
to: &Path,
|
||||
_reader_metadata: crate::fs::Metadata,
|
||||
_reader_metadata: &crate::fs::Metadata,
|
||||
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
|
||||
use crate::fs::OpenOptions;
|
||||
let writer = OpenOptions::new().open(to)?;
|
||||
|
|
@ -1955,7 +1959,7 @@ fn open_to_and_set_permissions(
|
|||
#[cfg(not(target_os = "espidf"))]
|
||||
fn open_to_and_set_permissions(
|
||||
to: &Path,
|
||||
reader_metadata: crate::fs::Metadata,
|
||||
reader_metadata: &crate::fs::Metadata,
|
||||
) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
|
||||
use crate::fs::OpenOptions;
|
||||
use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
|
||||
|
|
@ -1980,30 +1984,63 @@ fn open_to_and_set_permissions(
|
|||
Ok((writer, writer_metadata))
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android", target_vendor = "apple")))]
|
||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
let (mut reader, reader_metadata) = open_from(from)?;
|
||||
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
|
||||
mod cfm {
|
||||
use crate::fs::{File, Metadata};
|
||||
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write};
|
||||
|
||||
io::copy(&mut reader, &mut writer)
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
pub struct CachedFileMetadata(pub File, pub Metadata);
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
let (mut reader, reader_metadata) = open_from(from)?;
|
||||
let max_len = u64::MAX;
|
||||
let (mut writer, _) = open_to_and_set_permissions(to, reader_metadata)?;
|
||||
|
||||
use super::kernel_copy::{CopyResult, copy_regular_files};
|
||||
|
||||
match copy_regular_files(reader.as_raw_fd(), writer.as_raw_fd(), max_len) {
|
||||
CopyResult::Ended(bytes) => Ok(bytes),
|
||||
CopyResult::Error(e, _) => Err(e),
|
||||
CopyResult::Fallback(written) => match io::copy::generic_copy(&mut reader, &mut writer) {
|
||||
Ok(bytes) => Ok(bytes + written),
|
||||
Err(e) => Err(e),
|
||||
},
|
||||
impl Read for CachedFileMetadata {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||
self.0.read_vectored(bufs)
|
||||
}
|
||||
fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
|
||||
self.0.read_buf(cursor)
|
||||
}
|
||||
#[inline]
|
||||
fn is_read_vectored(&self) -> bool {
|
||||
self.0.is_read_vectored()
|
||||
}
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
|
||||
self.0.read_to_string(buf)
|
||||
}
|
||||
}
|
||||
impl Write for CachedFileMetadata {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
|
||||
self.0.write_vectored(bufs)
|
||||
}
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.0.is_write_vectored()
|
||||
}
|
||||
#[inline]
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.0.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub(crate) use cfm::CachedFileMetadata;
|
||||
|
||||
#[cfg(not(target_vendor = "apple"))]
|
||||
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
||||
let (reader, reader_metadata) = open_from(from)?;
|
||||
let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
|
||||
|
||||
io::copy(
|
||||
&mut cfm::CachedFileMetadata(reader, reader_metadata),
|
||||
&mut cfm::CachedFileMetadata(writer, writer_metadata),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "apple")]
|
||||
|
|
@ -2040,7 +2077,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
|
|||
}
|
||||
|
||||
// Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
|
||||
let (writer, writer_metadata) = open_to_and_set_permissions(to, reader_metadata)?;
|
||||
let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
|
||||
|
||||
// We ensure that `FreeOnDrop` never contains a null pointer so it is
|
||||
// always safe to call `copyfile_state_free`
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ use crate::process::{ChildStderr, ChildStdin, ChildStdout};
|
|||
use crate::ptr;
|
||||
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
use crate::sys::cvt;
|
||||
use crate::sys::fs::CachedFileMetadata;
|
||||
use crate::sys::weak::syscall;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -192,7 +193,7 @@ impl<R: CopyRead, W: CopyWrite> SpecCopy for Copier<'_, '_, R, W> {
|
|||
let w_cfg = writer.properties();
|
||||
|
||||
// before direct operations on file descriptors ensure that all source and sink buffers are empty
|
||||
let mut flush = || -> crate::io::Result<u64> {
|
||||
let mut flush = || -> Result<u64> {
|
||||
let bytes = reader.drain_to(writer, u64::MAX)?;
|
||||
// BufWriter buffered bytes have already been accounted for in earlier write() calls
|
||||
writer.flush()?;
|
||||
|
|
@ -537,6 +538,18 @@ impl<T: ?Sized + CopyWrite> CopyWrite for BufWriter<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl CopyRead for CachedFileMetadata {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CopyWrite for CachedFileMetadata {
|
||||
fn properties(&self) -> CopyParams {
|
||||
CopyParams(FdMeta::Metadata(self.1.clone()), Some(self.0.as_raw_fd()))
|
||||
}
|
||||
}
|
||||
|
||||
fn fd_to_meta<T: AsRawFd>(fd: &T) -> FdMeta {
|
||||
let fd = fd.as_raw_fd();
|
||||
let file: ManuallyDrop<File> = ManuallyDrop::new(unsafe { File::from_raw_fd(fd) });
|
||||
|
|
|
|||
|
|
@ -2426,6 +2426,7 @@ Windows.Win32.System.Console.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
|||
Windows.Win32.System.Console.ENABLE_WINDOW_INPUT
|
||||
Windows.Win32.System.Console.ENABLE_WRAP_AT_EOL_OUTPUT
|
||||
Windows.Win32.System.Console.GetConsoleMode
|
||||
Windows.Win32.System.Console.GetConsoleOutputCP
|
||||
Windows.Win32.System.Console.GetStdHandle
|
||||
Windows.Win32.System.Console.ReadConsoleW
|
||||
Windows.Win32.System.Console.STD_ERROR_HANDLE
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ windows_targets::link!("kernel32.dll" "system" fn FreeEnvironmentStringsW(penv :
|
|||
windows_targets::link!("kernel32.dll" "system" fn GetActiveProcessorCount(groupnumber : u16) -> u32);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetCommandLineW() -> PCWSTR);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetConsoleMode(hconsolehandle : HANDLE, lpmode : *mut CONSOLE_MODE) -> BOOL);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetConsoleOutputCP() -> u32);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetCurrentDirectoryW(nbufferlength : u32, lpbuffer : PWSTR) -> u32);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcess() -> HANDLE);
|
||||
windows_targets::link!("kernel32.dll" "system" fn GetCurrentProcessId() -> u32);
|
||||
|
|
@ -3333,6 +3334,7 @@ pub struct XSAVE_FORMAT {
|
|||
pub XmmRegisters: [M128A; 8],
|
||||
pub Reserved4: [u8; 224],
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "arm")]
|
||||
#[repr(C)]
|
||||
pub struct WSADATA {
|
||||
|
|
|
|||
|
|
@ -323,7 +323,7 @@ impl File {
|
|||
let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
|
||||
let result = c::SetFileInformationByHandle(
|
||||
handle.as_raw_handle(),
|
||||
c::FileEndOfFileInfo,
|
||||
c::FileAllocationInfo,
|
||||
(&raw const alloc).cast::<c_void>(),
|
||||
mem::size_of::<c::FILE_ALLOCATION_INFO>() as u32,
|
||||
);
|
||||
|
|
@ -1295,15 +1295,18 @@ pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
|
|||
} else {
|
||||
// SAFETY: The struct has been initialized by GetFileInformationByHandleEx
|
||||
let file_attribute_tag_info = unsafe { file_attribute_tag_info.assume_init() };
|
||||
let file_type = FileType::new(
|
||||
file_attribute_tag_info.FileAttributes,
|
||||
file_attribute_tag_info.ReparseTag,
|
||||
);
|
||||
|
||||
if file_attribute_tag_info.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
|
||||
&& file_attribute_tag_info.ReparseTag != c::IO_REPARSE_TAG_MOUNT_POINT
|
||||
{
|
||||
// The file is not a mount point: Reopen the file without inhibiting reparse point behavior.
|
||||
None
|
||||
} else {
|
||||
// The file is a mount point: Don't reopen the file so that the mount point gets renamed.
|
||||
if file_type.is_symlink() {
|
||||
// The file is a mount point, junction point or symlink so
|
||||
// don't reopen the file so that the link gets renamed.
|
||||
Some(Ok(handle))
|
||||
} else {
|
||||
// Otherwise reopen the file without inhibiting reparse point behavior.
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,21 +84,43 @@ fn is_console(handle: c::HANDLE) -> bool {
|
|||
unsafe { c::GetConsoleMode(handle, &mut mode) != 0 }
|
||||
}
|
||||
|
||||
/// Returns true if the attached console's code page is currently UTF-8.
|
||||
#[cfg(not(target_vendor = "win7"))]
|
||||
fn is_utf8_console() -> bool {
|
||||
unsafe { c::GetConsoleOutputCP() == c::CP_UTF8 }
|
||||
}
|
||||
|
||||
#[cfg(target_vendor = "win7")]
|
||||
fn is_utf8_console() -> bool {
|
||||
// Windows 7 has a fun "feature" where WriteFile on a console handle will return
|
||||
// the number of UTF-16 code units written and not the number of bytes from the input string.
|
||||
// So we always claim the console isn't UTF-8 to trigger the WriteConsole fallback code.
|
||||
false
|
||||
}
|
||||
|
||||
fn write(handle_id: u32, data: &[u8], incomplete_utf8: &mut IncompleteUtf8) -> io::Result<usize> {
|
||||
if data.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let handle = get_handle(handle_id)?;
|
||||
if !is_console(handle) {
|
||||
if !is_console(handle) || is_utf8_console() {
|
||||
unsafe {
|
||||
let handle = Handle::from_raw_handle(handle);
|
||||
let ret = handle.write(data);
|
||||
let _ = handle.into_raw_handle(); // Don't close the handle
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
write_console_utf16(data, incomplete_utf8, handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_console_utf16(
|
||||
data: &[u8],
|
||||
incomplete_utf8: &mut IncompleteUtf8,
|
||||
handle: c::HANDLE,
|
||||
) -> io::Result<usize> {
|
||||
if incomplete_utf8.len > 0 {
|
||||
assert!(
|
||||
incomplete_utf8.len < 4,
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ pub(crate) fn set_current(thread: Thread) -> Result<(), Thread> {
|
|||
/// one thread and is guaranteed not to call the global allocator.
|
||||
#[inline]
|
||||
pub(crate) fn current_id() -> ThreadId {
|
||||
// If accessing the persistant thread ID takes multiple TLS accesses, try
|
||||
// If accessing the persistent thread ID takes multiple TLS accesses, try
|
||||
// to retrieve it from the current thread handle, which will only take one
|
||||
// TLS access.
|
||||
if !id::CHEAP {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ cfg-if = "1.0"
|
|||
libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
|
||||
|
||||
[target.'cfg(target_os = "xous")'.dependencies]
|
||||
unwinding = { version = "0.2.3", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
|
||||
unwinding = { version = "0.2.5", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }
|
||||
|
||||
[features]
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ default-run = "bootstrap"
|
|||
|
||||
[features]
|
||||
build-metrics = ["sysinfo"]
|
||||
bootstrap-self-test = [] # enabled in the bootstrap unit tests
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue