New upstream version 1.49.0~beta.4+dfsg1

This commit is contained in:
Ximin Luo 2020-12-20 15:06:41 +00:00
parent 2a31497206
commit 29967ef60b
4426 changed files with 120082 additions and 413365 deletions

View File

@ -2,14 +2,14 @@
Thank you for your interest in contributing to Rust!
To get started, read the [Getting Started] guide in the [rustc-dev-guide].
To get started, read the [Contributing to Rust] chapter of the [rustc-dev-guide].
## Bug reports
Did a compiler error message tell you to come here? If you want to create an ICE report,
refer to [this section][contributing-bug-reports] and [open an issue][issue template].
[Getting Started]: https://rustc-dev-guide.rust-lang.org/getting-started.html
[Contributing to Rust]: https://rustc-dev-guide.rust-lang.org/contributing.html#contributing-to-rust
[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
[contributing-bug-reports]: https://rustc-dev-guide.rust-lang.org/contributing.html#bug-reports
[issue template]: https://github.com/rust-lang/rust/issues/new/choose

323
Cargo.lock generated
View File

@ -91,9 +91,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.32"
version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b602bfe940d21c130f3895acd65221e8a61270debe89d628b9cb4e3ccb8569b"
checksum = "bf8dcb5b4bbaa28653b647d8c77bd4ed40183b48882e130c1f1ffb73de069fd7"
[[package]]
name = "arc-swap"
@ -132,13 +132,13 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.50"
version = "0.3.53"
dependencies = [
"addr2line",
"cfg-if",
"cfg-if 1.0.0",
"libc",
"miniz_oxide",
"object",
"object 0.21.1",
"rustc-demangle",
]
@ -295,7 +295,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da"
[[package]]
name = "cargo"
version = "0.49.0"
version = "0.50.0"
dependencies = [
"anyhow",
"atty",
@ -306,11 +306,11 @@ dependencies = [
"clap",
"core-foundation",
"crates-io",
"crossbeam-utils 0.7.2",
"crossbeam-utils 0.8.0",
"crypto-hash",
"curl",
"curl-sys",
"env_logger 0.7.1",
"env_logger 0.8.1",
"filetime",
"flate2",
"fwdansi",
@ -418,6 +418,17 @@ dependencies = [
"serde_json",
]
[[package]]
name = "cargo_metadata"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5a5f7b42f606b7f23674f6f4d877628350682bc40687d3fae65679a58d55345"
dependencies = [
"semver 0.11.0",
"serde",
"serde_json",
]
[[package]]
name = "cargotest2"
version = "0.1.0"
@ -442,10 +453,16 @@ dependencies = [
]
[[package]]
name = "chalk-derive"
version = "0.29.0"
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a7f257e3bcdc56d8877ae31c012bd69fba0be66929d588e603905f2632c0c59"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chalk-derive"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f88ce4deae1dace71e49b7611cfae2d5489de3530d6daba5758043c47ac3a10"
dependencies = [
"proc-macro2",
"quote",
@ -455,9 +472,9 @@ dependencies = [
[[package]]
name = "chalk-engine"
version = "0.29.0"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c43fcc7edf4d51b42f44ed50e2337bd90ddc8e088d0cd78a71db92a6f780f782"
checksum = "0e34c9b1b10616782143d7f49490f91ae94afaf2202de3ab0b2835e78b4f0ccc"
dependencies = [
"chalk-derive",
"chalk-ir",
@ -468,9 +485,9 @@ dependencies = [
[[package]]
name = "chalk-ir"
version = "0.29.0"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a4050029ecb2b5a1ff3bfc64c39279179b294821ec2e8891a4a5c6e3a08db0"
checksum = "63362c629c2014ab639b04029070763fb8224df136d1363d30e9ece4c8877da3"
dependencies = [
"chalk-derive",
"lazy_static",
@ -478,9 +495,9 @@ dependencies = [
[[package]]
name = "chalk-solve"
version = "0.29.0"
version = "0.36.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "828c1f80d4eaf681027cce02050c54a3c97370f81988d31bf2a56df54048746c"
checksum = "cac338a67af52a7f50bb2f8232e730a3518ce432dbe303246acfe525ddd838c7"
dependencies = [
"chalk-derive",
"chalk-ir",
@ -524,15 +541,14 @@ dependencies = [
name = "clippy"
version = "0.0.212"
dependencies = [
"cargo_metadata 0.11.1",
"cargo_metadata 0.12.0",
"clippy-mini-macro-test",
"clippy_lints",
"compiletest_rs",
"derive-new",
"lazy_static",
"rustc-workspace-hack",
"rustc_tools_util 0.2.0",
"semver 0.10.0",
"semver 0.11.0",
"serde",
"tempfile",
"tester",
@ -546,15 +562,14 @@ version = "0.2.0"
name = "clippy_lints"
version = "0.0.212"
dependencies = [
"cargo_metadata 0.11.1",
"cargo_metadata 0.12.0",
"if_chain",
"itertools 0.9.0",
"lazy_static",
"pulldown-cmark 0.8.0",
"quine-mc_cluskey",
"quote",
"regex-syntax",
"semver 0.10.0",
"semver 0.11.0",
"serde",
"smallvec 1.4.2",
"syn",
@ -621,9 +636,9 @@ dependencies = [
[[package]]
name = "compiler_builtins"
version = "0.1.35"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3fcd8aba10d17504c87ef12d4f62ef404c6a4703d16682a9eb5543e6cf24455"
checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369"
dependencies = [
"cc",
"rustc-std-workspace-core",
@ -634,7 +649,6 @@ name = "compiletest"
version = "0.0.0"
dependencies = [
"diff",
"env_logger 0.7.1",
"getopts",
"glob",
"lazy_static",
@ -645,6 +659,7 @@ dependencies = [
"serde",
"serde_json",
"tracing",
"tracing-subscriber",
"walkdir",
"winapi 0.3.9",
]
@ -671,6 +686,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "const_fn"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@ -724,7 +745,7 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
@ -755,7 +776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"lazy_static",
"maybe-uninit",
@ -778,7 +799,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"maybe-uninit",
]
@ -789,7 +810,7 @@ version = "0.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"lazy_static",
]
@ -800,7 +821,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
"cfg-if 0.1.10",
"lazy_static",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"const_fn",
"lazy_static",
]
@ -930,7 +963,7 @@ version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"dirs-sys",
]
@ -1012,6 +1045,19 @@ dependencies = [
"termcolor",
]
[[package]]
name = "env_logger"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd"
dependencies = [
"atty",
"humantime 2.0.1",
"log",
"regex",
"termcolor",
]
[[package]]
name = "error_index_generator"
version = "0.0.0"
@ -1072,7 +1118,7 @@ version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed85775dcc68644b5c950ac06a2b23768d3bc9390464151aaf27136998dcf9e"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"redox_syscall",
"winapi 0.3.9",
@ -1090,7 +1136,7 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"crc32fast",
"libc",
"libz-sys",
@ -1221,7 +1267,7 @@ version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"wasi",
]
@ -1232,7 +1278,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"wasi",
]
@ -1330,9 +1376,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.1.15"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"compiler_builtins",
"libc",
@ -1661,9 +1707,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.77"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
dependencies = [
"rustc-std-workspace-core",
]
@ -1721,6 +1767,10 @@ dependencies = [
[[package]]
name = "linkchecker"
version = "0.1.0"
dependencies = [
"once_cell",
"regex",
]
[[package]]
name = "linked-hash-map"
@ -1761,16 +1811,7 @@ version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
[[package]]
name = "log_settings"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19af41f0565d7c19b2058153ad0b42d4d5ce89ec4dbf06ed6741114a8b63e7cd"
dependencies = [
"lazy_static",
"cfg-if 0.1.10",
]
[[package]]
@ -1887,6 +1928,17 @@ dependencies = [
"opaque-debug 0.2.3",
]
[[package]]
name = "md-5"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
dependencies = [
"block-buffer 0.9.0",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "mdbook"
version = "0.4.3"
@ -1914,18 +1966,6 @@ dependencies = [
"toml",
]
[[package]]
name = "measureme"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef709d3257013bba7cff14fc504e07e80631d3fe0f6d38ce63b8f6510ccb932"
dependencies = [
"byteorder",
"memmap",
"parking_lot 0.9.0",
"rustc-hash",
]
[[package]]
name = "measureme"
version = "9.0.0"
@ -2011,7 +2051,7 @@ version = "0.6.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"fuchsia-zircon",
"fuchsia-zircon-sys",
"iovec",
@ -2092,7 +2132,7 @@ version = "0.2.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"winapi 0.3.9",
]
@ -2143,6 +2183,12 @@ dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "object"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
[[package]]
name = "once_cell"
version = "1.4.1"
@ -2186,7 +2232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
dependencies = [
"bitflags",
"cfg-if",
"cfg-if 0.1.10",
"foreign-types",
"lazy_static",
"libc",
@ -2201,9 +2247,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
[[package]]
name = "openssl-src"
version = "111.10.2+1.1.1g"
version = "111.12.0+1.1.1h"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a287fdb22e32b5b60624d4a5a7a02dbe82777f730ec0dbc42a0554326fef5a70"
checksum = "858a4132194f8570a7ee9eb8629e85b23cbc4565f2d4a162e87556e5956abf61"
dependencies = [
"cc",
]
@ -2243,14 +2289,14 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a85ea9fc0d4ac0deb6fe7911d38786b32fc11119afd9e9d38b84ff691ce64220"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
]
[[package]]
name = "panic_abort"
version = "0.0.0"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"compiler_builtins",
"core",
"libc",
@ -2261,7 +2307,7 @@ name = "panic_unwind"
version = "0.0.0"
dependencies = [
"alloc",
"cfg-if",
"cfg-if 0.1.10",
"compiler_builtins",
"core",
"libc",
@ -2324,7 +2370,7 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi 0.0.3",
"libc",
"redox_syscall",
@ -2339,7 +2385,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi 0.0.3",
"libc",
"redox_syscall",
@ -2353,7 +2399,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"cloudabi 0.1.0",
"instant",
"libc",
@ -2420,7 +2466,7 @@ checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
dependencies = [
"maplit",
"pest",
"sha-1",
"sha-1 0.8.2",
]
[[package]]
@ -2959,8 +3005,9 @@ dependencies = [
[[package]]
name = "rust-demangler"
version = "0.0.0"
version = "0.0.1"
dependencies = [
"regex",
"rustc-demangle",
]
@ -3056,13 +3103,13 @@ checksum = "b1cb2b6a38759cf7c0c1434c8b4cbfcab9cd24970d05f960f2ca01226ddb4d68"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"ena",
"indexmap",
"jobserver",
"libc",
"measureme 9.0.0",
"measureme",
"parking_lot 0.11.0",
"rustc-ap-rustc_graphviz",
"rustc-ap-rustc_index",
@ -3232,15 +3279,15 @@ version = "686.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf3db7b4ca5d21c14c45475df155e5e020c9a3760346945a662c9a9053b49c8"
dependencies = [
"cfg-if",
"md-5",
"cfg-if 0.1.10",
"md-5 0.8.0",
"rustc-ap-rustc_arena",
"rustc-ap-rustc_data_structures",
"rustc-ap-rustc_index",
"rustc-ap-rustc_macros",
"rustc-ap-rustc_serialize",
"scoped-tls",
"sha-1",
"sha-1 0.8.2",
"tracing",
"unicode-width",
]
@ -3262,9 +3309,9 @@ dependencies = [
[[package]]
name = "rustc-demangle"
version = "0.1.16"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
@ -3359,7 +3406,6 @@ dependencies = [
name = "rustc_arena"
version = "0.0.0"
dependencies = [
"rustc_data_structures",
"smallvec 1.4.2",
]
@ -3466,7 +3512,7 @@ version = "0.0.0"
dependencies = [
"bitflags",
"libc",
"measureme 0.7.1",
"measureme",
"rustc-demangle",
"rustc_ast",
"rustc_attr",
@ -3526,13 +3572,13 @@ version = "0.0.0"
dependencies = [
"arrayvec",
"bitflags",
"cfg-if",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"ena",
"indexmap",
"jobserver",
"libc",
"measureme 0.7.1",
"measureme",
"parking_lot 0.11.0",
"rustc-hash",
"rustc-rayon",
@ -3577,6 +3623,7 @@ dependencies = [
"rustc_target",
"tracing",
"tracing-subscriber",
"tracing-tree",
"winapi 0.3.9",
]
@ -3591,6 +3638,7 @@ dependencies = [
"annotate-snippets 0.8.0",
"atty",
"rustc_data_structures",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
"rustc_span",
@ -3783,6 +3831,18 @@ dependencies = [
"unicode-security",
]
[[package]]
name = "rustc_lint_defs"
version = "0.0.0"
dependencies = [
"rustc_ast",
"rustc_data_structures",
"rustc_macros",
"rustc_serialize",
"rustc_span",
"tracing",
]
[[package]]
name = "rustc_llvm"
version = "0.0.0"
@ -3836,7 +3896,7 @@ version = "0.0.0"
dependencies = [
"bitflags",
"chalk-ir",
"measureme 0.7.1",
"measureme",
"polonius-engine",
"rustc-rayon-core",
"rustc_apfloat",
@ -3864,7 +3924,6 @@ version = "0.0.0"
dependencies = [
"either",
"itertools 0.9.0",
"log_settings",
"polonius-engine",
"regex",
"rustc_apfloat",
@ -4066,6 +4125,7 @@ dependencies = [
"rustc_errors",
"rustc_feature",
"rustc_fs_util",
"rustc_lint_defs",
"rustc_macros",
"rustc_serialize",
"rustc_span",
@ -4077,15 +4137,16 @@ dependencies = [
name = "rustc_span"
version = "0.0.0"
dependencies = [
"cfg-if",
"md-5",
"cfg-if 0.1.10",
"md-5 0.9.1",
"rustc_arena",
"rustc_data_structures",
"rustc_index",
"rustc_macros",
"rustc_serialize",
"scoped-tls",
"sha-1",
"sha-1 0.9.1",
"sha2",
"tracing",
"unicode-width",
]
@ -4225,6 +4286,7 @@ dependencies = [
"itertools 0.9.0",
"minifier",
"pulldown-cmark 0.8.0",
"regex",
"rustc-rayon",
"serde",
"serde_json",
@ -4267,7 +4329,7 @@ dependencies = [
[[package]]
name = "rustfmt-nightly"
version = "1.4.24"
version = "1.4.25"
dependencies = [
"annotate-snippets 0.6.1",
"anyhow",
@ -4348,7 +4410,7 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
"semver-parser 0.7.0",
"serde",
]
@ -4358,7 +4420,17 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "394cec28fa623e00903caf7ba4fa6fb9a0e260280bb8cdbbba029611108a0190"
dependencies = [
"semver-parser",
"semver-parser 0.7.0",
"serde",
]
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser 0.10.1",
"serde",
]
@ -4368,6 +4440,15 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "semver-parser"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ef146c2ad5e5f4b037cd6ce2ebb775401729b19a82040c1beac9d36c7d1428"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.115"
@ -4399,9 +4480,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.57"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
dependencies = [
"itoa",
"ryu",
@ -4431,6 +4512,19 @@ dependencies = [
"opaque-debug 0.2.3",
]
[[package]]
name = "sha-1"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
dependencies = [
"block-buffer 0.9.0",
"cfg-if 0.1.10",
"cpuid-bool",
"digest 0.9.0",
"opaque-debug 0.3.0",
]
[[package]]
name = "sha2"
version = "0.9.1"
@ -4438,7 +4532,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
dependencies = [
"block-buffer 0.9.0",
"cfg-if",
"cfg-if 0.1.10",
"cpuid-bool",
"digest 0.9.0",
"opaque-debug 0.3.0",
@ -4524,7 +4618,7 @@ version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"redox_syscall",
"winapi 0.3.9",
@ -4543,7 +4637,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21ccb4c06ec57bc82d0f610f1a2963d7648700e43a6f513e564b9c89f7991786"
dependencies = [
"cc",
"cfg-if",
"cfg-if 0.1.10",
"libc",
"psm",
"winapi 0.3.9",
@ -4555,7 +4649,7 @@ version = "0.0.0"
dependencies = [
"addr2line",
"alloc",
"cfg-if",
"cfg-if 0.1.10",
"compiler_builtins",
"core",
"dlmalloc",
@ -4564,7 +4658,7 @@ dependencies = [
"hermit-abi",
"libc",
"miniz_oxide",
"object",
"object 0.20.0",
"panic_abort",
"panic_unwind",
"profiler_builtins",
@ -4697,7 +4791,7 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"libc",
"rand",
"redox_syscall",
@ -4757,7 +4851,7 @@ dependencies = [
name = "test"
version = "0.0.0"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"core",
"getopts",
"libc",
@ -5102,7 +5196,7 @@ version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c"
dependencies = [
"cfg-if",
"cfg-if 0.1.10",
"tracing-attributes",
"tracing-core",
]
@ -5120,9 +5214,9 @@ dependencies = [
[[package]]
name = "tracing-core"
version = "0.1.15"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f0e00789804e99b20f12bc7003ca416309d28a6f495d6af58d1e2c2842461b5"
checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f"
dependencies = [
"lazy_static",
]
@ -5140,9 +5234,9 @@ dependencies = [
[[package]]
name = "tracing-serde"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6ccba2f8f16e0ed268fc765d9b7ff22e965e7185d32f8f1ec8294fe17d86e79"
checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b"
dependencies = [
"serde",
"tracing-core",
@ -5150,21 +5244,22 @@ dependencies = [
[[package]]
name = "tracing-subscriber"
version = "0.2.11"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abd165311cc4d7a555ad11cc77a37756df836182db0d81aac908c8184c584f40"
checksum = "4ef0a5e15477aa303afbfac3a44cba9b6430fdaad52423b1e6c0dbbe28c3eedd"
dependencies = [
"ansi_term 0.12.1",
"chrono",
"lazy_static",
"matchers",
"parking_lot 0.11.0",
"parking_lot 0.9.0",
"regex",
"serde",
"serde_json",
"sharded-slab",
"smallvec 1.4.2",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
@ -5172,9 +5267,9 @@ dependencies = [
[[package]]
name = "tracing-tree"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f"
checksum = "43aac8afb493b08e1e1904956f7407c1e671b9c83b26a17e1bd83d6a3520e350"
dependencies = [
"ansi_term 0.12.1",
"atty",
@ -5298,7 +5393,7 @@ name = "unwind"
version = "0.0.0"
dependencies = [
"cc",
"cfg-if",
"cfg-if 0.1.10",
"compiler_builtins",
"core",
"libc",

View File

@ -29,10 +29,17 @@ members = [
"src/tools/unicode-table-generator",
"src/tools/expand-yaml-anchors",
]
exclude = [
"build",
"compiler/rustc_codegen_cranelift",
# HACK(eddyb) This hardcodes the fact that our CI uses `/checkout/obj`.
"obj",
# The `x` binary is a thin wrapper that calls `x.py`, which initializes
# submodules, before which workspace members cannot be invoked because
# not all `Cargo.toml` files are available, so we exclude the `x` binary,
# so it can be invoked before the current checkout is set up.
"src/tools/x",
]
[profile.release.package.compiler_builtins]

View File

@ -9,8 +9,7 @@ standard library, and documentation.
**Note: this README is for _users_ rather than _contributors_.
If you wish to _contribute_ to the compiler, you should read the
[Getting Started][gettingstarted] of the rustc-dev-guide instead of this
section.**
[Getting Started][gettingstarted] section of the rustc-dev-guide instead.**
## Quick Start

View File

@ -1511,11 +1511,16 @@ impl<S: Semantics, T: Semantics> FloatConvert<IeeeFloat<T>> for IeeeFloat<S> {
sig::set_bit(&mut r.sig, T::PRECISION - 1);
}
// gcc forces the Quiet bit on, which means (float)(double)(float_sNan)
// does not give you back the same bits. This is dubious, and we
// don't currently do it. You're really supposed to get
// an invalid operation signal at runtime, but nobody does that.
status = Status::OK;
// Convert of sNaN creates qNaN and raises an exception (invalid op).
// This also guarantees that a sNaN does not become Inf on a truncation
// that loses all payload bits.
if self.is_signaling() {
// Quiet signaling NaN.
sig::set_bit(&mut r.sig, T::QNAN_BIT);
status = Status::INVALID_OP;
} else {
status = Status::OK;
}
} else {
*loses_info = false;
status = Status::OK;

View File

@ -566,6 +566,17 @@ fn fma() {
}
}
#[test]
fn issue_69532() {
let f = Double::from_bits(0x7FF0_0000_0000_0001u64 as u128);
let mut loses_info = false;
let sta = f.convert(&mut loses_info);
let r: Single = sta.value;
assert!(loses_info);
assert!(r.is_nan());
assert_eq!(sta.status, Status::INVALID_OP);
}
#[test]
fn min_num() {
let f1 = Double::from_f64(1.0);
@ -1492,27 +1503,32 @@ fn convert() {
assert_eq!(4294967295.0, test.to_f64());
assert!(!loses_info);
let test = Single::snan(None);
let x87_snan = X87DoubleExtended::snan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_snan));
assert!(!loses_info);
let test = Single::qnan(None);
let x87_qnan = X87DoubleExtended::qnan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_qnan));
assert!(!loses_info);
let test = X87DoubleExtended::snan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_snan));
let test = Single::snan(None);
let sta = test.convert(&mut loses_info);
let test: X87DoubleExtended = sta.value;
assert!(test.is_nan());
assert!(!test.is_signaling());
assert!(!loses_info);
assert_eq!(sta.status, Status::INVALID_OP);
let test = X87DoubleExtended::qnan(None);
let test: X87DoubleExtended = test.convert(&mut loses_info).value;
assert!(test.bitwise_eq(x87_qnan));
assert!(!loses_info);
let test = X87DoubleExtended::snan(None);
let sta = test.convert(&mut loses_info);
let test: X87DoubleExtended = sta.value;
assert!(test.is_nan());
assert!(!test.is_signaling());
assert!(!loses_info);
assert_eq!(sta.status, Status::INVALID_OP);
}
#[test]

View File

@ -5,5 +5,4 @@ version = "0.0.0"
edition = "2018"
[dependencies]
rustc_data_structures = { path = "../rustc_data_structures" }
smallvec = { version = "1.0", features = ["union", "may_dangle"] }

View File

@ -16,7 +16,6 @@
#![feature(maybe_uninit_slice)]
#![cfg_attr(test, feature(test))]
use rustc_data_structures::cold_path;
use smallvec::SmallVec;
use std::alloc::Layout;
@ -27,6 +26,12 @@ use std::mem::{self, MaybeUninit};
use std::ptr;
use std::slice;
#[inline(never)]
#[cold]
pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
f()
}
/// An arena that can hold objects of only one type.
pub struct TypedArena<T> {
/// A pointer to the next object to be allocated.
@ -212,16 +217,18 @@ impl<T> TypedArena<T> {
let mut chunks = self.chunks.borrow_mut();
let mut new_cap;
if let Some(last_chunk) = chunks.last_mut() {
let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
last_chunk.entries = used_bytes / mem::size_of::<T>();
// If a type is `!needs_drop`, we don't need to keep track of how many elements
// the chunk stores - the field will be ignored anyway.
if mem::needs_drop::<T>() {
let used_bytes = self.ptr.get() as usize - last_chunk.start() as usize;
last_chunk.entries = used_bytes / mem::size_of::<T>();
}
// If the previous chunk's len is less than HUGE_PAGE
// bytes, then this chunk will be least double the previous
// chunk's size.
new_cap = last_chunk.storage.len();
if new_cap < HUGE_PAGE / elem_size {
new_cap = new_cap.checked_mul(2).unwrap();
}
new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2);
new_cap *= 2;
} else {
new_cap = PAGE / elem_size;
}
@ -338,10 +345,8 @@ impl DroplessArena {
// If the previous chunk's len is less than HUGE_PAGE
// bytes, then this chunk will be least double the previous
// chunk's size.
new_cap = last_chunk.storage.len();
if new_cap < HUGE_PAGE {
new_cap = new_cap.checked_mul(2).unwrap();
}
new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2);
new_cap *= 2;
} else {
new_cap = PAGE;
}
@ -528,7 +533,7 @@ impl DropArena {
ptr::write(mem, object);
let result = &mut *mem;
// Record the destructor after doing the allocation as that may panic
// and would cause `object`'s destuctor to run twice if it was recorded before
// and would cause `object`'s destructor to run twice if it was recorded before
self.destructors
.borrow_mut()
.push(DropType { drop_fn: drop_for_type::<T>, obj: result as *mut T as *mut u8 });
@ -555,12 +560,10 @@ impl DropArena {
mem::forget(vec.drain(..));
// Record the destructors after doing the allocation as that may panic
// and would cause `object`'s destuctor to run twice if it was recorded before
// and would cause `object`'s destructor to run twice if it was recorded before
for i in 0..len {
destructors.push(DropType {
drop_fn: drop_for_type::<T>,
obj: start_ptr.offset(i as isize) as *mut u8,
});
destructors
.push(DropType { drop_fn: drop_for_type::<T>, obj: start_ptr.add(i) as *mut u8 });
}
slice::from_raw_parts_mut(start_ptr, len)

View File

@ -24,9 +24,10 @@ pub use UnsafeSource::*;
use crate::ptr::P;
use crate::token::{self, CommentKind, DelimToken};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::thin_vec::ThinVec;
use rustc_macros::HashStable_Generic;
@ -96,7 +97,7 @@ pub struct Path {
/// The segments in the path: the things separated by `::`.
/// Global paths begin with `kw::PathRoot`.
pub segments: Vec<PathSegment>,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
impl PartialEq<Symbol> for Path {
@ -166,13 +167,6 @@ pub enum GenericArgs {
}
impl GenericArgs {
pub fn is_parenthesized(&self) -> bool {
match *self {
Parenthesized(..) => true,
_ => false,
}
}
pub fn is_angle_bracketed(&self) -> bool {
match *self {
AngleBracketed(..) => true,
@ -228,6 +222,15 @@ pub enum AngleBracketedArg {
Constraint(AssocTyConstraint),
}
impl AngleBracketedArg {
pub fn span(&self) -> Span {
match self {
AngleBracketedArg::Arg(arg) => arg.span(),
AngleBracketedArg::Constraint(constraint) => constraint.span,
}
}
}
impl Into<Option<P<GenericArgs>>> for AngleBracketedArgs {
fn into(self) -> Option<P<GenericArgs>> {
Some(P(GenericArgs::AngleBracketed(self)))
@ -541,7 +544,7 @@ pub struct Block {
/// Distinguishes between `unsafe { ... }` and `{ ... }`.
pub rules: BlockCheckMode,
pub span: Span,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
/// A match pattern.
@ -552,7 +555,7 @@ pub struct Pat {
pub id: NodeId,
pub kind: PatKind,
pub span: Span,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
impl Pat {
@ -856,13 +859,6 @@ impl BinOpKind {
}
}
pub fn is_shift(&self) -> bool {
match *self {
BinOpKind::Shl | BinOpKind::Shr => true,
_ => false,
}
}
pub fn is_comparison(&self) -> bool {
use BinOpKind::*;
// Note for developers: please keep this as is;
@ -872,11 +868,6 @@ impl BinOpKind {
And | Or | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr => false,
}
}
/// Returns `true` if the binary operator takes its arguments by value
pub fn is_by_value(&self) -> bool {
!self.is_comparison()
}
}
pub type BinOp = Spanned<BinOpKind>;
@ -895,14 +886,6 @@ pub enum UnOp {
}
impl UnOp {
/// Returns `true` if the unary operator takes its argument by value
pub fn is_by_value(u: UnOp) -> bool {
match u {
UnOp::Neg | UnOp::Not => true,
_ => false,
}
}
pub fn to_string(op: UnOp) -> &'static str {
match op {
UnOp::Deref => "*",
@ -918,10 +901,17 @@ pub struct Stmt {
pub id: NodeId,
pub kind: StmtKind,
pub span: Span,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
impl Stmt {
pub fn has_trailing_semicolon(&self) -> bool {
match &self.kind {
StmtKind::Semi(_) => true,
StmtKind::MacCall(mac) => matches!(mac.style, MacStmtStyle::Semicolon),
_ => false,
}
}
pub fn add_trailing_semicolon(mut self) -> Self {
self.kind = match self.kind {
StmtKind::Expr(expr) => StmtKind::Semi(expr),
@ -1066,12 +1056,12 @@ pub struct Expr {
pub kind: ExprKind,
pub span: Span,
pub attrs: AttrVec,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
// `Expr` is used a lot. Make sure it doesn't unintentionally get bigger.
#[cfg(target_arch = "x86_64")]
rustc_data_structures::static_assert_size!(Expr, 112);
rustc_data_structures::static_assert_size!(Expr, 120);
impl Expr {
/// Returns `true` if this expression would be valid somewhere that expects a value;
@ -1178,6 +1168,7 @@ impl Expr {
match self.kind {
ExprKind::Box(_) => ExprPrecedence::Box,
ExprKind::Array(_) => ExprPrecedence::Array,
ExprKind::ConstBlock(_) => ExprPrecedence::ConstBlock,
ExprKind::Call(..) => ExprPrecedence::Call,
ExprKind::MethodCall(..) => ExprPrecedence::MethodCall,
ExprKind::Tup(_) => ExprPrecedence::Tup,
@ -1227,12 +1218,24 @@ pub enum RangeLimits {
Closed,
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum StructRest {
/// `..x`.
Base(P<Expr>),
/// `..`.
Rest(Span),
/// No trailing `..` or expression.
None,
}
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum ExprKind {
/// A `box x` expression.
Box(P<Expr>),
/// An array (`[a, b, c, d]`)
Array(Vec<P<Expr>>),
/// Allow anonymous constants from an inline `const` block
ConstBlock(AnonConst),
/// A function call
///
/// The first field resolves to the function itself,
@ -1319,7 +1322,7 @@ pub enum ExprKind {
Field(P<Expr>, Ident),
/// An indexing operation (e.g., `foo[2]`).
Index(P<Expr>, P<Expr>),
/// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`).
/// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
/// Variable reference, possibly containing `::` and/or type
@ -1347,9 +1350,8 @@ pub enum ExprKind {
/// A struct literal expression.
///
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`,
/// where `base` is the `Option<Expr>`.
Struct(Path, Vec<Field>, Option<P<Expr>>),
/// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`.
Struct(Path, Vec<Field>, StructRest),
/// An array literal constructed from one repeated element.
///
@ -1606,7 +1608,7 @@ pub enum LitKind {
/// A string literal (`"foo"`).
Str(Symbol, StrStyle),
/// A byte string (`b"foo"`).
ByteStr(Lrc<Vec<u8>>),
ByteStr(Lrc<[u8]>),
/// A byte char (`b'f'`).
Byte(u8),
/// A character literal (`'a'`).
@ -1752,13 +1754,6 @@ impl IntTy {
}
}
pub fn val_to_string(&self, val: i128) -> String {
// Cast to a `u128` so we can correctly print `INT128_MIN`. All integral types
// are parsed as `u128`, so we wouldn't want to print an extra negative
// sign.
format!("{}{}", val as u128, self.name_str())
}
pub fn bit_width(&self) -> Option<u64> {
Some(match *self {
IntTy::Isize => return None,
@ -1817,10 +1812,6 @@ impl UintTy {
}
}
pub fn val_to_string(&self, val: u128) -> String {
format!("{}{}", val, self.name_str())
}
pub fn bit_width(&self) -> Option<u64> {
Some(match *self {
UintTy::Usize => return None,
@ -1864,12 +1855,33 @@ pub enum AssocTyConstraintKind {
Bound { bounds: GenericBounds },
}
#[derive(Clone, Encodable, Decodable, Debug)]
#[derive(Encodable, Decodable, Debug)]
pub struct Ty {
pub id: NodeId,
pub kind: TyKind,
pub span: Span,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
impl Clone for Ty {
fn clone(&self) -> Self {
ensure_sufficient_stack(|| Self {
id: self.id,
kind: self.kind.clone(),
span: self.span,
tokens: self.tokens.clone(),
})
}
}
impl Ty {
pub fn peel_refs(&self) -> &Self {
let mut final_ty = self;
while let TyKind::Rptr(_, MutTy { ty, .. }) = &final_ty.kind {
final_ty = &ty;
}
final_ty
}
}
#[derive(Clone, Encodable, Decodable, Debug)]
@ -2421,7 +2433,7 @@ impl<D: Decoder> rustc_serialize::Decodable<D> for AttrId {
pub struct AttrItem {
pub path: Path,
pub args: MacArgs,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
/// A list of attributes.
@ -2441,7 +2453,7 @@ pub struct Attribute {
#[derive(Clone, Encodable, Decodable, Debug)]
pub enum AttrKind {
/// A normal attribute.
Normal(AttrItem),
Normal(AttrItem, Option<LazyTokenStream>),
/// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
/// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
@ -2495,7 +2507,7 @@ pub enum CrateSugar {
pub struct Visibility {
pub kind: VisibilityKind,
pub span: Span,
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
#[derive(Clone, Encodable, Decodable, Debug)]
@ -2582,7 +2594,7 @@ pub struct Item<K = ItemKind> {
///
/// Note that the tokens here do not include the outer attributes, but will
/// include inner attributes.
pub tokens: Option<TokenStream>,
pub tokens: Option<LazyTokenStream>,
}
impl Item {

View File

@ -8,7 +8,7 @@ use crate::ast::{Path, PathSegment};
use crate::mut_visit::visit_clobber;
use crate::ptr::P;
use crate::token::{self, CommentKind, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree, TreeAndSpacing};
use rustc_index::bit_set::GrowableBitSet;
use rustc_span::source_map::{BytePos, Spanned};
@ -66,7 +66,7 @@ impl NestedMetaItem {
self.meta_item().and_then(|meta_item| meta_item.ident())
}
pub fn name_or_empty(&self) -> Symbol {
self.ident().unwrap_or(Ident::invalid()).name
self.ident().unwrap_or_else(Ident::invalid).name
}
/// Gets the string value if `self` is a `MetaItem` and the `MetaItem` is a
@ -101,11 +101,6 @@ impl NestedMetaItem {
self.meta_item().is_some()
}
/// Returns `true` if the variant is `Literal`.
pub fn is_literal(&self) -> bool {
self.literal().is_some()
}
/// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
pub fn is_word(&self) -> bool {
self.meta_item().map_or(false, |meta_item| meta_item.is_word())
@ -125,7 +120,7 @@ impl NestedMetaItem {
impl Attribute {
pub fn has_name(&self, name: Symbol) -> bool {
match self.kind {
AttrKind::Normal(ref item) => item.path == name,
AttrKind::Normal(ref item, _) => item.path == name,
AttrKind::DocComment(..) => false,
}
}
@ -133,7 +128,7 @@ impl Attribute {
/// For a single-segment attribute, returns its name; otherwise, returns `None`.
pub fn ident(&self) -> Option<Ident> {
match self.kind {
AttrKind::Normal(ref item) => {
AttrKind::Normal(ref item, _) => {
if item.path.segments.len() == 1 {
Some(item.path.segments[0].ident)
} else {
@ -144,19 +139,19 @@ impl Attribute {
}
}
pub fn name_or_empty(&self) -> Symbol {
self.ident().unwrap_or(Ident::invalid()).name
self.ident().unwrap_or_else(Ident::invalid).name
}
pub fn value_str(&self) -> Option<Symbol> {
match self.kind {
AttrKind::Normal(ref item) => item.meta(self.span).and_then(|meta| meta.value_str()),
AttrKind::Normal(ref item, _) => item.meta(self.span).and_then(|meta| meta.value_str()),
AttrKind::DocComment(..) => None,
}
}
pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
match self.kind {
AttrKind::Normal(ref item) => match item.meta(self.span) {
AttrKind::Normal(ref item, _) => match item.meta(self.span) {
Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
_ => None,
},
@ -165,7 +160,7 @@ impl Attribute {
}
pub fn is_word(&self) -> bool {
if let AttrKind::Normal(item) = &self.kind {
if let AttrKind::Normal(item, _) = &self.kind {
matches!(item.args, MacArgs::Empty)
} else {
false
@ -188,7 +183,7 @@ impl MetaItem {
if self.path.segments.len() == 1 { Some(self.path.segments[0].ident) } else { None }
}
pub fn name_or_empty(&self) -> Symbol {
self.ident().unwrap_or(Ident::invalid()).name
self.ident().unwrap_or_else(Ident::invalid).name
}
// Example:
@ -232,10 +227,6 @@ impl MetaItem {
pub fn is_value_str(&self) -> bool {
self.value_str().is_some()
}
pub fn is_meta_item_list(&self) -> bool {
self.meta_item_list().is_some()
}
}
impl AttrItem {
@ -255,7 +246,7 @@ impl AttrItem {
impl Attribute {
pub fn is_doc_comment(&self) -> bool {
match self.kind {
AttrKind::Normal(_) => false,
AttrKind::Normal(..) => false,
AttrKind::DocComment(..) => true,
}
}
@ -263,7 +254,7 @@ impl Attribute {
pub fn doc_str(&self) -> Option<Symbol> {
match self.kind {
AttrKind::DocComment(.., data) => Some(data),
AttrKind::Normal(ref item) if item.path == sym::doc => {
AttrKind::Normal(ref item, _) if item.path == sym::doc => {
item.meta(self.span).and_then(|meta| meta.value_str())
}
_ => None,
@ -272,14 +263,14 @@ impl Attribute {
pub fn get_normal_item(&self) -> &AttrItem {
match self.kind {
AttrKind::Normal(ref item) => item,
AttrKind::Normal(ref item, _) => item,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
pub fn unwrap_normal_item(self) -> AttrItem {
match self.kind {
AttrKind::Normal(item) => item,
AttrKind::Normal(item, _) => item,
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
}
}
@ -287,10 +278,22 @@ impl Attribute {
/// Extracts the MetaItem from inside this Attribute.
pub fn meta(&self) -> Option<MetaItem> {
match self.kind {
AttrKind::Normal(ref item) => item.meta(self.span),
AttrKind::Normal(ref item, _) => item.meta(self.span),
AttrKind::DocComment(..) => None,
}
}
pub fn tokens(&self) -> TokenStream {
match self.kind {
AttrKind::Normal(_, ref tokens) => tokens
.as_ref()
.unwrap_or_else(|| panic!("attribute is missing tokens: {:?}", self))
.create_token_stream(),
AttrKind::DocComment(comment_kind, data) => TokenStream::from(TokenTree::Token(
Token::new(token::DocComment(comment_kind, self.style, data), self.span),
)),
}
}
}
/* Constructors */
@ -330,11 +333,16 @@ crate fn mk_attr_id() -> AttrId {
}
pub fn mk_attr(style: AttrStyle, path: Path, args: MacArgs, span: Span) -> Attribute {
mk_attr_from_item(style, AttrItem { path, args, tokens: None }, span)
mk_attr_from_item(AttrItem { path, args, tokens: None }, None, style, span)
}
pub fn mk_attr_from_item(style: AttrStyle, item: AttrItem, span: Span) -> Attribute {
Attribute { kind: AttrKind::Normal(item), id: mk_attr_id(), style, span }
pub fn mk_attr_from_item(
item: AttrItem,
tokens: Option<LazyTokenStream>,
style: AttrStyle,
span: Span,
) -> Attribute {
Attribute { kind: AttrKind::Normal(item, tokens), id: mk_attr_id(), style, span }
}
/// Returns an inner attribute with the given value and span.
@ -363,7 +371,7 @@ pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
impl MetaItem {
fn token_trees_and_spacings(&self) -> Vec<TreeAndSpacing> {
let mut idents = vec![];
let mut last_pos = BytePos(0 as u32);
let mut last_pos = BytePos(0_u32);
for (i, segment) in self.path.segments.iter().enumerate() {
let is_first = i == 0;
if !is_first {
@ -632,7 +640,8 @@ impl HasAttrs for StmtKind {
match *self {
StmtKind::Local(ref local) => local.attrs(),
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => expr.attrs(),
StmtKind::Empty | StmtKind::Item(..) => &[],
StmtKind::Item(ref item) => item.attrs(),
StmtKind::Empty => &[],
StmtKind::MacCall(ref mac) => mac.attrs.attrs(),
}
}
@ -641,7 +650,8 @@ impl HasAttrs for StmtKind {
match self {
StmtKind::Local(local) => local.visit_attrs(f),
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr.visit_attrs(f),
StmtKind::Empty | StmtKind::Item(..) => {}
StmtKind::Item(item) => item.visit_attrs(f),
StmtKind::Empty => {}
StmtKind::MacCall(mac) => {
mac.attrs.visit_attrs(f);
}

View File

@ -34,6 +34,13 @@ impl<A: Array> ExpectOne<A> for SmallVec<A> {
}
pub trait MutVisitor: Sized {
/// Mutable token visiting only exists for the `macro_rules` token marker and should not be
/// used otherwise. Token visitor would be entirely separate from the regular visitor if
/// the marker didn't have to visit AST fragments in nonterminal tokens.
fn token_visiting_enabled(&self) -> bool {
false
}
// Methods in this trait have one of three forms:
//
// fn visit_t(&mut self, t: &mut T); // common
@ -203,11 +210,8 @@ pub trait MutVisitor: Sized {
noop_visit_local(l, self);
}
fn visit_mac(&mut self, _mac: &mut MacCall) {
panic!("visit_mac disabled by default");
// N.B., see note about macros above. If you really want a visitor that
// works on macros, use this definition in your trait impl:
// mut_visit::noop_visit_mac(_mac, self);
fn visit_mac_call(&mut self, mac: &mut MacCall) {
noop_visit_mac(mac, self);
}
fn visit_macro_def(&mut self, def: &mut MacroDef) {
@ -246,22 +250,6 @@ pub trait MutVisitor: Sized {
noop_flat_map_generic_param(param, self)
}
fn visit_tt(&mut self, tt: &mut TokenTree) {
noop_visit_tt(tt, self);
}
fn visit_tts(&mut self, tts: &mut TokenStream) {
noop_visit_tts(tts, self);
}
fn visit_token(&mut self, t: &mut Token) {
noop_visit_token(t, self);
}
fn visit_interpolated(&mut self, nt: &mut token::Nonterminal) {
noop_visit_interpolated(nt, self);
}
fn visit_param_bound(&mut self, tpb: &mut GenericBound) {
noop_visit_param_bound(tpb, self);
}
@ -375,11 +363,30 @@ pub fn visit_mac_args<T: MutVisitor>(args: &mut MacArgs, vis: &mut T) {
MacArgs::Empty => {}
MacArgs::Delimited(dspan, _delim, tokens) => {
visit_delim_span(dspan, vis);
vis.visit_tts(tokens);
visit_tts(tokens, vis);
}
MacArgs::Eq(eq_span, tokens) => {
vis.visit_span(eq_span);
vis.visit_tts(tokens);
visit_tts(tokens, vis);
// The value in `#[key = VALUE]` must be visited as an expression for backward
// compatibility, so that macros can be expanded in that position.
if !vis.token_visiting_enabled() {
if let Some(TokenTree::Token(token)) = tokens.trees_ref().next() {
if let token::Interpolated(..) = token.kind {
// ^^ Do not `make_mut` unless we have to.
match Lrc::make_mut(&mut tokens.0).get_mut(0) {
Some((TokenTree::Token(token), _spacing)) => match &mut token.kind {
token::Interpolated(nt) => match Lrc::make_mut(nt) {
token::NtExpr(expr) => vis.visit_expr(expr),
t => panic!("unexpected token in key-value attribute: {:?}", t),
},
t => panic!("unexpected token in key-value attribute: {:?}", t),
},
t => panic!("unexpected token in key-value attribute: {:?}", t),
}
}
}
}
}
}
}
@ -451,7 +458,7 @@ pub fn noop_visit_ty_constraint<T: MutVisitor>(
}
pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
let Ty { id, kind, span, tokens: _ } = ty.deref_mut();
let Ty { id, kind, span, tokens } = ty.deref_mut();
vis.visit_id(id);
match kind {
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err | TyKind::Never | TyKind::CVarArgs => {}
@ -484,9 +491,10 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
vis.visit_id(id);
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
TyKind::MacCall(mac) => vis.visit_mac(mac),
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
}
vis.visit_span(span);
visit_lazy_tts(tokens, vis);
}
pub fn noop_visit_foreign_mod<T: MutVisitor>(foreign_mod: &mut ForeignMod, vis: &mut T) {
@ -513,13 +521,14 @@ pub fn noop_visit_ident<T: MutVisitor>(Ident { name: _, span }: &mut Ident, vis:
vis.visit_span(span);
}
pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens: _ }: &mut Path, vis: &mut T) {
pub fn noop_visit_path<T: MutVisitor>(Path { segments, span, tokens }: &mut Path, vis: &mut T) {
vis.visit_span(span);
for PathSegment { ident, id, args } in segments {
vis.visit_ident(ident);
vis.visit_id(id);
visit_opt(args, |args| vis.visit_generic_args(args));
}
visit_lazy_tts(tokens, vis);
}
pub fn noop_visit_qself<T: MutVisitor>(qself: &mut Option<QSelf>, vis: &mut T) {
@ -579,9 +588,11 @@ pub fn noop_visit_local<T: MutVisitor>(local: &mut P<Local>, vis: &mut T) {
pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
let Attribute { kind, id: _, style: _, span } = attr;
match kind {
AttrKind::Normal(AttrItem { path, args, tokens: _ }) => {
AttrKind::Normal(AttrItem { path, args, tokens }, attr_tokens) => {
vis.visit_path(path);
visit_mac_args(args, vis);
visit_lazy_tts(tokens, vis);
visit_lazy_tts(attr_tokens, vis);
}
AttrKind::DocComment(..) => {}
}
@ -626,28 +637,43 @@ pub fn noop_flat_map_param<T: MutVisitor>(mut param: Param, vis: &mut T) -> Smal
smallvec![param]
}
pub fn noop_visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_tt<T: MutVisitor>(tt: &mut TokenTree, vis: &mut T) {
match tt {
TokenTree::Token(token) => {
vis.visit_token(token);
visit_token(token, vis);
}
TokenTree::Delimited(DelimSpan { open, close }, _delim, tts) => {
vis.visit_span(open);
vis.visit_span(close);
vis.visit_tts(tts);
visit_tts(tts, vis);
}
}
}
pub fn noop_visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
let tts = Lrc::make_mut(tts);
visit_vec(tts, |(tree, _is_joint)| vis.visit_tt(tree));
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
pub fn visit_tts<T: MutVisitor>(TokenStream(tts): &mut TokenStream, vis: &mut T) {
if vis.token_visiting_enabled() && !tts.is_empty() {
let tts = Lrc::make_mut(tts);
visit_vec(tts, |(tree, _is_joint)| visit_tt(tree, vis));
}
}
pub fn visit_lazy_tts<T: MutVisitor>(lazy_tts: &mut Option<LazyTokenStream>, vis: &mut T) {
if vis.token_visiting_enabled() {
visit_opt(lazy_tts, |lazy_tts| {
let mut tts = lazy_tts.create_token_stream();
visit_tts(&mut tts, vis);
*lazy_tts = LazyTokenStream::new(tts);
})
}
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
// Applies ident visitor if it's an ident; applies other visits to interpolated nodes.
// In practice the ident part is not actually used by specific visitors right now,
// but there's a test below checking that it works.
pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
pub fn visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
let Token { kind, span } = t;
match kind {
token::Ident(name, _) | token::Lifetime(name) => {
@ -659,13 +685,14 @@ pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
}
token::Interpolated(nt) => {
let mut nt = Lrc::make_mut(nt);
vis.visit_interpolated(&mut nt);
visit_interpolated(&mut nt, vis);
}
_ => {}
}
vis.visit_span(span);
}
// No `noop_` prefix because there isn't a corresponding method in `MutVisitor`.
/// Applies the visitor to elements of interpolated nodes.
//
// N.B., this can occur only when applying a visitor to partially expanded
@ -689,7 +716,7 @@ pub fn noop_visit_token<T: MutVisitor>(t: &mut Token, vis: &mut T) {
// contain multiple items, but decided against it when I looked at
// `parse_item_or_view_item` and tried to figure out what I would do with
// multiple items there....
pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
pub fn visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis: &mut T) {
match nt {
token::NtItem(item) => visit_clobber(item, |item| {
// This is probably okay, because the only visitors likely to
@ -709,12 +736,13 @@ pub fn noop_visit_interpolated<T: MutVisitor>(nt: &mut token::Nonterminal, vis:
token::NtLifetime(ident) => vis.visit_ident(ident),
token::NtLiteral(expr) => vis.visit_expr(expr),
token::NtMeta(item) => {
let AttrItem { path, args, tokens: _ } = item.deref_mut();
let AttrItem { path, args, tokens } = item.deref_mut();
vis.visit_path(path);
visit_mac_args(args, vis);
visit_lazy_tts(tokens, vis);
}
token::NtPath(path) => vis.visit_path(path),
token::NtTT(tt) => vis.visit_tt(tt),
token::NtTT(tt) => visit_tt(tt, vis),
token::NtVis(visib) => vis.visit_vis(visib),
}
}
@ -871,10 +899,11 @@ pub fn noop_visit_mt<T: MutVisitor>(MutTy { ty, mutbl: _ }: &mut MutTy, vis: &mu
}
pub fn noop_visit_block<T: MutVisitor>(block: &mut P<Block>, vis: &mut T) {
let Block { id, stmts, rules: _, span, tokens: _ } = block.deref_mut();
let Block { id, stmts, rules: _, span, tokens } = block.deref_mut();
vis.visit_id(id);
stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
vis.visit_span(span);
visit_lazy_tts(tokens, vis);
}
pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
@ -930,7 +959,7 @@ pub fn noop_visit_item_kind<T: MutVisitor>(kind: &mut ItemKind, vis: &mut T) {
vis.visit_generics(generics);
visit_bounds(bounds, vis);
}
ItemKind::MacCall(m) => vis.visit_mac(m),
ItemKind::MacCall(m) => vis.visit_mac_call(m),
ItemKind::MacroDef(def) => vis.visit_macro_def(def),
}
}
@ -939,7 +968,7 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
mut item: P<AssocItem>,
visitor: &mut T,
) -> SmallVec<[P<AssocItem>; 1]> {
let Item { id, ident, vis, attrs, kind, span, tokens: _ } = item.deref_mut();
let Item { id, ident, vis, attrs, kind, span, tokens } = item.deref_mut();
visitor.visit_id(id);
visitor.visit_ident(ident);
visitor.visit_vis(vis);
@ -959,9 +988,10 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
visit_bounds(bounds, visitor);
visit_opt(ty, |ty| visitor.visit_ty(ty));
}
AssocItemKind::MacCall(mac) => visitor.visit_mac(mac),
AssocItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
}
visitor.visit_span(span);
visit_lazy_tts(tokens, visitor);
smallvec![item]
}
@ -1012,16 +1042,14 @@ pub fn noop_flat_map_item<T: MutVisitor>(
mut item: P<Item>,
visitor: &mut T,
) -> SmallVec<[P<Item>; 1]> {
let Item { ident, attrs, id, kind, vis, span, tokens: _ } = item.deref_mut();
let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
visitor.visit_ident(ident);
visit_attrs(attrs, visitor);
visitor.visit_id(id);
visitor.visit_item_kind(kind);
visitor.visit_vis(vis);
visitor.visit_span(span);
// FIXME: if `tokens` is modified with a call to `vis.visit_tts` it causes
// an ICE during resolve... odd!
visit_lazy_tts(tokens, visitor);
smallvec![item]
}
@ -1030,7 +1058,7 @@ pub fn noop_flat_map_foreign_item<T: MutVisitor>(
mut item: P<ForeignItem>,
visitor: &mut T,
) -> SmallVec<[P<ForeignItem>; 1]> {
let Item { ident, attrs, id, kind, vis, span, tokens: _ } = item.deref_mut();
let Item { ident, attrs, id, kind, vis, span, tokens } = item.deref_mut();
visitor.visit_id(id);
visitor.visit_ident(ident);
visitor.visit_vis(vis);
@ -1050,14 +1078,15 @@ pub fn noop_flat_map_foreign_item<T: MutVisitor>(
visit_bounds(bounds, visitor);
visit_opt(ty, |ty| visitor.visit_ty(ty));
}
ForeignItemKind::MacCall(mac) => visitor.visit_mac(mac),
ForeignItemKind::MacCall(mac) => visitor.visit_mac_call(mac),
}
visitor.visit_span(span);
visit_lazy_tts(tokens, visitor);
smallvec![item]
}
pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
let Pat { id, kind, span, tokens: _ } = pat.deref_mut();
let Pat { id, kind, span, tokens } = pat.deref_mut();
vis.visit_id(id);
match kind {
PatKind::Wild | PatKind::Rest => {}
@ -1089,9 +1118,10 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
visit_vec(elems, |elem| vis.visit_pat(elem))
}
PatKind::Paren(inner) => vis.visit_pat(inner),
PatKind::MacCall(mac) => vis.visit_mac(mac),
PatKind::MacCall(mac) => vis.visit_mac_call(mac),
}
vis.visit_span(span);
visit_lazy_tts(tokens, vis);
}
pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonConst, vis: &mut T) {
@ -1100,12 +1130,15 @@ pub fn noop_visit_anon_const<T: MutVisitor>(AnonConst { id, value }: &mut AnonCo
}
pub fn noop_visit_expr<T: MutVisitor>(
Expr { kind, id, span, attrs, tokens: _ }: &mut Expr,
Expr { kind, id, span, attrs, tokens }: &mut Expr,
vis: &mut T,
) {
match kind {
ExprKind::Box(expr) => vis.visit_expr(expr),
ExprKind::Array(exprs) => visit_exprs(exprs, vis),
ExprKind::ConstBlock(anon_const) => {
vis.visit_anon_const(anon_const);
}
ExprKind::Repeat(expr, count) => {
vis.visit_expr(expr);
vis.visit_anon_const(count);
@ -1251,11 +1284,15 @@ pub fn noop_visit_expr<T: MutVisitor>(
}
visit_vec(inputs, |(_c, expr)| vis.visit_expr(expr));
}
ExprKind::MacCall(mac) => vis.visit_mac(mac),
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(path, fields, expr) => {
vis.visit_path(path);
fields.flat_map_in_place(|field| vis.flat_map_field(field));
visit_opt(expr, |expr| vis.visit_expr(expr));
match expr {
StructRest::Base(expr) => vis.visit_expr(expr),
StructRest::Rest(_span) => {}
StructRest::None => {}
}
}
ExprKind::Paren(expr) => {
vis.visit_expr(expr);
@ -1276,6 +1313,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
vis.visit_id(id);
vis.visit_span(span);
visit_thin_attrs(attrs, vis);
visit_lazy_tts(tokens, vis);
}
pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Option<P<Expr>> {
@ -1286,11 +1324,12 @@ pub fn noop_filter_map_expr<T: MutVisitor>(mut e: P<Expr>, vis: &mut T) -> Optio
}
pub fn noop_flat_map_stmt<T: MutVisitor>(
Stmt { kind, mut span, mut id, tokens }: Stmt,
Stmt { kind, mut span, mut id, mut tokens }: Stmt,
vis: &mut T,
) -> SmallVec<[Stmt; 1]> {
vis.visit_id(&mut id);
vis.visit_span(&mut span);
visit_lazy_tts(&mut tokens, vis);
noop_flat_map_stmt_kind(kind, vis)
.into_iter()
.map(|kind| Stmt { id, kind, span, tokens: tokens.clone() })
@ -1312,7 +1351,7 @@ pub fn noop_flat_map_stmt_kind<T: MutVisitor>(
StmtKind::Empty => smallvec![StmtKind::Empty],
StmtKind::MacCall(mut mac) => {
let MacCallStmt { mac: mac_, style: _, attrs } = mac.deref_mut();
vis.visit_mac(mac_);
vis.visit_mac_call(mac_);
visit_thin_attrs(attrs, vis);
smallvec![StmtKind::MacCall(mac)]
}

View File

@ -13,8 +13,8 @@ rustc_data_structures::define_id_collections!(NodeMap, NodeSet, NodeId);
pub const CRATE_NODE_ID: NodeId = NodeId::from_u32(0);
/// When parsing and doing expansions, we initially give all AST nodes this AST
/// node value. Then later, in the renumber pass, we renumber them to have
/// small, positive ids.
/// node value. Then later, during expansion, we renumber them to have small,
/// positive ids.
pub const DUMMY_NODE_ID: NodeId = NodeId::MAX;
impl NodeId {

View File

@ -54,16 +54,6 @@ pub enum DelimToken {
NoDelim,
}
impl DelimToken {
pub fn len(self) -> usize {
if self == NoDelim { 0 } else { 1 }
}
pub fn is_empty(self) -> bool {
self == NoDelim
}
}
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum LitKind {
Bool, // AST only, must never appear in a `Token`
@ -163,6 +153,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
kw::Do,
kw::Box,
kw::Break,
kw::Const,
kw::Continue,
kw::False,
kw::For,
@ -312,6 +303,13 @@ impl TokenKind {
_ => None,
}
}
pub fn should_end_const_arg(&self) -> bool {
match self {
Gt | Ge | BinOp(Shr) | BinOpEq(Shr) => true,
_ => false,
}
}
}
impl Token {
@ -810,10 +808,10 @@ impl Nonterminal {
if let ExpnKind::Macro(_, macro_name) = orig_span.ctxt().outer_expn_data().kind {
let filename = source_map.span_to_filename(orig_span);
if let FileName::Real(RealFileName::Named(path)) = filename {
let matches_prefix = |prefix| {
// Check for a path that ends with 'prefix*/src/lib.rs'
let matches_prefix = |prefix, filename| {
// Check for a path that ends with 'prefix*/src/<filename>'
let mut iter = path.components().rev();
iter.next().and_then(|p| p.as_os_str().to_str()) == Some("lib.rs")
iter.next().and_then(|p| p.as_os_str().to_str()) == Some(filename)
&& iter.next().and_then(|p| p.as_os_str().to_str()) == Some("src")
&& iter
.next()
@ -821,14 +819,25 @@ impl Nonterminal {
.map_or(false, |p| p.starts_with(prefix))
};
if (macro_name == sym::impl_macros && matches_prefix("time-macros-impl"))
|| (macro_name == sym::arrays && matches_prefix("js-sys"))
if (macro_name == sym::impl_macros
&& matches_prefix("time-macros-impl", "lib.rs"))
|| (macro_name == sym::arrays && matches_prefix("js-sys", "lib.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$name") {
return Some((*ident, *is_raw));
}
}
if macro_name == sym::tuple_from_req
&& (matches_prefix("actix-web", "extract.rs")
|| matches_prefix("actori-web", "extract.rs"))
{
let snippet = source_map.span_to_snippet(orig_span);
if snippet.as_deref() == Ok("$T") {
return Some((*ident, *is_raw));
}
}
}
}
}

View File

@ -16,12 +16,13 @@
use crate::token::{self, DelimToken, Token, TokenKind};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::sync::{self, Lrc};
use rustc_macros::HashStable_Generic;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_span::{Span, DUMMY_SP};
use smallvec::{smallvec, SmallVec};
use std::{iter, mem};
use std::{fmt, iter, mem};
/// When the main rust parser encounters a syntax-extension invocation, it
/// parses the arguments to the invocation as a token-tree. This is a very
@ -119,13 +120,64 @@ where
}
}
pub trait CreateTokenStream: sync::Send + sync::Sync {
fn create_token_stream(&self) -> TokenStream;
}
impl CreateTokenStream for TokenStream {
fn create_token_stream(&self) -> TokenStream {
self.clone()
}
}
/// A lazy version of `TokenStream`, which defers creation
/// of an actual `TokenStream` until it is needed.
/// `Box` is here only to reduce the structure size.
#[derive(Clone)]
pub struct LazyTokenStream(Lrc<Box<dyn CreateTokenStream>>);
impl LazyTokenStream {
pub fn new(inner: impl CreateTokenStream + 'static) -> LazyTokenStream {
LazyTokenStream(Lrc::new(Box::new(inner)))
}
pub fn create_token_stream(&self) -> TokenStream {
self.0.create_token_stream()
}
}
impl fmt::Debug for LazyTokenStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt("LazyTokenStream", f)
}
}
impl<S: Encoder> Encodable<S> for LazyTokenStream {
fn encode(&self, s: &mut S) -> Result<(), S::Error> {
// Used by AST json printing.
Encodable::encode(&self.create_token_stream(), s)
}
}
impl<D: Decoder> Decodable<D> for LazyTokenStream {
fn decode(_d: &mut D) -> Result<Self, D::Error> {
panic!("Attempted to decode LazyTokenStream");
}
}
impl<CTX> HashStable<CTX> for LazyTokenStream {
fn hash_stable(&self, _hcx: &mut CTX, _hasher: &mut StableHasher) {
panic!("Attempted to compute stable hash for LazyTokenStream");
}
}
/// A `TokenStream` is an abstract sequence of tokens, organized into `TokenTree`s.
///
/// The goal is for procedural macros to work with `TokenStream`s and `TokenTree`s
/// instead of a representation of the abstract syntax tree.
/// Today's `TokenTree`s can still contain AST via `token::Interpolated` for back-compat.
#[derive(Clone, Debug, Default, Encodable, Decodable)]
pub struct TokenStream(pub Lrc<Vec<TreeAndSpacing>>);
pub struct TokenStream(pub(crate) Lrc<Vec<TreeAndSpacing>>);
pub type TreeAndSpacing = (TokenTree, Spacing);
@ -266,6 +318,10 @@ impl TokenStream {
}
}
pub fn trees_ref(&self) -> CursorRef<'_> {
CursorRef::new(self)
}
pub fn trees(&self) -> Cursor {
self.clone().into_trees()
}
@ -286,21 +342,15 @@ impl TokenStream {
t1.next().is_none() && t2.next().is_none()
}
pub fn map_enumerated<F: FnMut(usize, TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
pub fn map_enumerated<F: FnMut(usize, &TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
TokenStream(Lrc::new(
self.0
.iter()
.enumerate()
.map(|(i, (tree, is_joint))| (f(i, tree.clone()), *is_joint))
.map(|(i, (tree, is_joint))| (f(i, tree), *is_joint))
.collect(),
))
}
pub fn map<F: FnMut(TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
TokenStream(Lrc::new(
self.0.iter().map(|(tree, is_joint)| (f(tree.clone()), *is_joint)).collect(),
))
}
}
// 99.5%+ of the time we have 1 or 2 elements in this vector.
@ -362,6 +412,36 @@ impl TokenStreamBuilder {
}
}
/// By-reference iterator over a `TokenStream`.
#[derive(Clone)]
pub struct CursorRef<'t> {
stream: &'t TokenStream,
index: usize,
}
impl<'t> CursorRef<'t> {
fn new(stream: &TokenStream) -> CursorRef<'_> {
CursorRef { stream, index: 0 }
}
fn next_with_spacing(&mut self) -> Option<&'t TreeAndSpacing> {
self.stream.0.get(self.index).map(|tree| {
self.index += 1;
tree
})
}
}
impl<'t> Iterator for CursorRef<'t> {
type Item = &'t TokenTree;
fn next(&mut self) -> Option<&'t TokenTree> {
self.next_with_spacing().map(|(tree, _)| tree)
}
}
/// Owning by-value iterator over a `TokenStream`.
/// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
#[derive(Clone)]
pub struct Cursor {
pub stream: TokenStream,
@ -400,8 +480,8 @@ impl Cursor {
self.index = index;
}
pub fn look_ahead(&self, n: usize) -> Option<TokenTree> {
self.stream.0[self.index..].get(n).map(|(tree, _)| tree.clone())
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
self.stream.0[self.index..].get(n).map(|(tree, _)| tree)
}
}

View File

@ -54,7 +54,7 @@ where
T: Iterator<Item = &'a Symbol>,
{
let lookup = &lookup.as_str();
let max_dist = dist.map_or_else(|| cmp::max(lookup.len(), 3) / 3, |d| d);
let max_dist = dist.unwrap_or_else(|| cmp::max(lookup.len(), 3) / 3);
let name_vec: Vec<&Symbol> = iter_names.collect();
let (case_insensitive_match, levenshtein_match) = name_vec

View File

@ -4,7 +4,6 @@ use crate::ast::{self, Lit, LitKind};
use crate::token::{self, Token};
use crate::tokenstream::TokenTree;
use rustc_data_structures::sync::Lrc;
use rustc_lexer::unescape::{unescape_byte, unescape_char};
use rustc_lexer::unescape::{unescape_byte_literal, unescape_literal, Mode};
use rustc_span::symbol::{kw, sym, Symbol};
@ -108,7 +107,7 @@ impl LitKind {
});
error?;
buf.shrink_to_fit();
LitKind::ByteStr(Lrc::new(buf))
LitKind::ByteStr(buf.into())
}
token::ByteStrRaw(_) => {
let s = symbol.as_str();
@ -128,7 +127,7 @@ impl LitKind {
symbol.to_string().into_bytes()
};
LitKind::ByteStr(Lrc::new(bytes))
LitKind::ByteStr(bytes.into())
}
token::Err => LitKind::Err(symbol),
})

View File

@ -231,7 +231,6 @@ impl AssocOp {
}
}
pub const PREC_RESET: i8 = -100;
pub const PREC_CLOSURE: i8 = -40;
pub const PREC_JUMP: i8 = -30;
pub const PREC_RANGE: i8 = -10;
@ -283,6 +282,7 @@ pub enum ExprPrecedence {
ForLoop,
Loop,
Match,
ConstBlock,
Block,
TryBlock,
Struct,
@ -347,6 +347,7 @@ impl ExprPrecedence {
ExprPrecedence::ForLoop |
ExprPrecedence::Loop |
ExprPrecedence::Match |
ExprPrecedence::ConstBlock |
ExprPrecedence::Block |
ExprPrecedence::TryBlock |
ExprPrecedence::Async |

View File

@ -14,8 +14,8 @@
//! those that are created by the expansion of a macro.
use crate::ast::*;
use crate::token::Token;
use crate::tokenstream::{TokenStream, TokenTree};
use crate::token;
use crate::tokenstream::TokenTree;
use rustc_span::symbol::{Ident, Symbol};
use rustc_span::Span;
@ -176,13 +176,8 @@ pub trait Visitor<'ast>: Sized {
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) {
walk_lifetime(self, lifetime)
}
fn visit_mac(&mut self, _mac: &'ast MacCall) {
panic!("visit_mac disabled by default");
// N.B., see note about macros above.
// if you really want a visitor that
// works on macros, use this
// definition in your trait impl:
// visit::walk_mac(self, _mac)
fn visit_mac_call(&mut self, mac: &'ast MacCall) {
walk_mac(self, mac)
}
fn visit_mac_def(&mut self, _mac: &'ast MacroDef, _id: NodeId) {
// Nothing to do
@ -200,11 +195,7 @@ pub trait Visitor<'ast>: Sized {
walk_generic_args(self, path_span, generic_args)
}
fn visit_generic_arg(&mut self, generic_arg: &'ast GenericArg) {
match generic_arg {
GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
GenericArg::Type(ty) => self.visit_ty(ty),
GenericArg::Const(ct) => self.visit_anon_const(ct),
}
walk_generic_arg(self, generic_arg)
}
fn visit_assoc_ty_constraint(&mut self, constraint: &'ast AssocTyConstraint) {
walk_assoc_ty_constraint(self, constraint)
@ -212,14 +203,6 @@ pub trait Visitor<'ast>: Sized {
fn visit_attribute(&mut self, attr: &'ast Attribute) {
walk_attribute(self, attr)
}
fn visit_tt(&mut self, tt: TokenTree) {
walk_tt(self, tt)
}
fn visit_tts(&mut self, tts: TokenStream) {
walk_tts(self, tts)
}
fn visit_token(&mut self, _t: Token) {}
// FIXME: add `visit_interpolated` and `walk_interpolated`
fn visit_vis(&mut self, vis: &'ast Visibility) {
walk_vis(self, vis)
}
@ -358,7 +341,7 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
visitor.visit_generics(generics);
walk_list!(visitor, visit_param_bound, bounds);
}
ItemKind::MacCall(ref mac) => visitor.visit_mac(mac),
ItemKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id),
}
walk_list!(visitor, visit_attribute, &item.attrs);
@ -426,7 +409,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
}
TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
TyKind::MacCall(ref mac) => visitor.visit_mac(mac),
TyKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
TyKind::Never | TyKind::CVarArgs => {}
}
}
@ -486,6 +469,17 @@ where
}
}
pub fn walk_generic_arg<'a, V>(visitor: &mut V, generic_arg: &'a GenericArg)
where
V: Visitor<'a>,
{
match generic_arg {
GenericArg::Lifetime(lt) => visitor.visit_lifetime(lt),
GenericArg::Type(ty) => visitor.visit_ty(ty),
GenericArg::Const(ct) => visitor.visit_anon_const(ct),
}
}
pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>(
visitor: &mut V,
constraint: &'a AssocTyConstraint,
@ -533,7 +527,7 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
PatKind::Tuple(ref elems) | PatKind::Slice(ref elems) | PatKind::Or(ref elems) => {
walk_list!(visitor, visit_pat, elems);
}
PatKind::MacCall(ref mac) => visitor.visit_mac(mac),
PatKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
}
}
@ -558,7 +552,7 @@ pub fn walk_foreign_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a ForeignI
walk_list!(visitor, visit_ty, ty);
}
ForeignItemKind::MacCall(mac) => {
visitor.visit_mac(mac);
visitor.visit_mac_call(mac);
}
}
}
@ -663,7 +657,7 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem,
walk_list!(visitor, visit_ty, ty);
}
AssocItemKind::MacCall(mac) => {
visitor.visit_mac(mac);
visitor.visit_mac_call(mac);
}
}
}
@ -693,7 +687,7 @@ pub fn walk_stmt<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Stmt) {
StmtKind::Empty => {}
StmtKind::MacCall(ref mac) => {
let MacCallStmt { ref mac, style: _, ref attrs } = **mac;
visitor.visit_mac(mac);
visitor.visit_mac_call(mac);
for attr in attrs.iter() {
visitor.visit_attribute(attr);
}
@ -717,6 +711,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Array(ref subexpressions) => {
walk_list!(visitor, visit_expr, subexpressions);
}
ExprKind::ConstBlock(ref anon_const) => visitor.visit_anon_const(anon_const),
ExprKind::Repeat(ref element, ref count) => {
visitor.visit_expr(element);
visitor.visit_anon_const(count)
@ -724,7 +719,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Struct(ref path, ref fields, ref optional_base) => {
visitor.visit_path(path, expression.id);
walk_list!(visitor, visit_field, fields);
walk_list!(visitor, visit_expr, optional_base);
match optional_base {
StructRest::Base(expr) => visitor.visit_expr(expr),
StructRest::Rest(_span) => {}
StructRest::None => {}
}
}
ExprKind::Tup(ref subexpressions) => {
walk_list!(visitor, visit_expr, subexpressions);
@ -823,7 +822,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
ExprKind::Ret(ref optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
ExprKind::MacCall(ref mac) => visitor.visit_mac(mac),
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(ref ia) => {
for (op, _) in &ia.operands {
@ -886,7 +885,7 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
match attr.kind {
AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
AttrKind::Normal(ref item, ref _tokens) => walk_mac_args(visitor, &item.args),
AttrKind::DocComment(..) => {}
}
}
@ -894,20 +893,19 @@ pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute)
pub fn walk_mac_args<'a, V: Visitor<'a>>(visitor: &mut V, args: &'a MacArgs) {
match args {
MacArgs::Empty => {}
MacArgs::Delimited(_dspan, _delim, tokens) => visitor.visit_tts(tokens.clone()),
MacArgs::Eq(_eq_span, tokens) => visitor.visit_tts(tokens.clone()),
}
}
pub fn walk_tt<'a, V: Visitor<'a>>(visitor: &mut V, tt: TokenTree) {
match tt {
TokenTree::Token(token) => visitor.visit_token(token),
TokenTree::Delimited(_, _, tts) => visitor.visit_tts(tts),
}
}
pub fn walk_tts<'a, V: Visitor<'a>>(visitor: &mut V, tts: TokenStream) {
for tt in tts.trees() {
visitor.visit_tt(tt);
MacArgs::Delimited(_dspan, _delim, _tokens) => {}
// The value in `#[key = VALUE]` must be visited as an expression for backward
// compatibility, so that macros can be expanded in that position.
MacArgs::Eq(_eq_span, tokens) => match tokens.trees_ref().next() {
Some(TokenTree::Token(token)) => match &token.kind {
token::Interpolated(nt) => match &**nt {
token::NtExpr(expr) => visitor.visit_expr(expr),
t => panic!("unexpected token in key-value attribute: {:?}", t),
},
token::Literal(..) | token::Ident(..) => {}
t => panic!("unexpected token in key-value attribute: {:?}", t),
},
t => panic!("unexpected token in key-value attribute: {:?}", t),
},
}
}

View File

@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_session::parse::feature_err;
use rustc_span::hygiene::ForLoopLoc;
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
use rustc_span::symbol::{sym, Ident, Symbol};
@ -30,6 +31,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
let kind = match e.kind {
ExprKind::Box(ref inner) => hir::ExprKind::Box(self.lower_expr(inner)),
ExprKind::Array(ref exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)),
ExprKind::ConstBlock(ref anon_const) => {
let anon_const = self.lower_anon_const(anon_const);
hir::ExprKind::ConstBlock(anon_const)
}
ExprKind::Repeat(ref expr, ref count) => {
let expr = self.lower_expr(expr);
let count = self.lower_anon_const(count);
@ -142,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
}
ExprKind::Assign(ref el, ref er, span) => {
hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span)
self.lower_expr_assign(el, er, span, e.span)
}
ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
self.lower_binop(op),
@ -182,8 +187,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm),
ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm),
ExprKind::Struct(ref path, ref fields, ref maybe_expr) => {
let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x));
ExprKind::Struct(ref path, ref fields, ref rest) => {
let rest = match rest {
StructRest::Base(e) => Some(self.lower_expr(e)),
StructRest::Rest(sp) => {
self.sess
.struct_span_err(*sp, "base expression required after `..`")
.span_label(*sp, "add a base expression here")
.emit();
Some(&*self.arena.alloc(self.expr_err(*sp)))
}
StructRest::None => None,
};
hir::ExprKind::Struct(
self.arena.alloc(self.lower_qpath(
e.id,
@ -193,7 +208,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
ImplTraitContext::disallowed(),
)),
self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))),
maybe_expr,
rest,
)
}
ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
@ -206,9 +221,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
ex.span = e.span;
}
// Merge attributes into the inner expression.
let mut attrs = e.attrs.clone();
let mut attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect();
attrs.extend::<Vec<_>>(ex.attrs.into());
ex.attrs = attrs;
ex.attrs = attrs.into();
return ex;
}
@ -432,17 +447,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.with_catch_scope(body.id, |this| {
let mut block = this.lower_block_noalloc(body, true);
let try_span = this.mark_span_with_reason(
DesugaringKind::TryBlock,
body.span,
this.allow_try_trait.clone(),
);
// Final expression of the block (if present) or `()` with span at the end of block
let tail_expr = block
.expr
.take()
.unwrap_or_else(|| this.expr_unit(this.sess.source_map().end_point(try_span)));
let (try_span, tail_expr) = if let Some(expr) = block.expr.take() {
(
this.mark_span_with_reason(
DesugaringKind::TryBlock,
expr.span,
this.allow_try_trait.clone(),
),
expr,
)
} else {
let try_span = this.mark_span_with_reason(
DesugaringKind::TryBlock,
this.sess.source_map().end_point(body.span),
this.allow_try_trait.clone(),
);
(try_span, this.expr_unit(try_span))
};
let ok_wrapped_span =
this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None);
@ -828,6 +851,236 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}
/// Destructure the LHS of complex assignments.
/// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
fn lower_expr_assign(
&mut self,
lhs: &Expr,
rhs: &Expr,
eq_sign_span: Span,
whole_span: Span,
) -> hir::ExprKind<'hir> {
// Return early in case of an ordinary assignment.
fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
match &lhs.kind {
ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false,
// Check for tuple struct constructor.
ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
ExprKind::Paren(e) => {
match e.kind {
// We special-case `(..)` for consistency with patterns.
ExprKind::Range(None, None, RangeLimits::HalfOpen) => false,
_ => is_ordinary(lower_ctx, e),
}
}
_ => true,
}
}
if is_ordinary(self, lhs) {
return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span);
}
if !self.sess.features_untracked().destructuring_assignment {
feature_err(
&self.sess.parse_sess,
sym::destructuring_assignment,
eq_sign_span,
"destructuring assignments are unstable",
)
.span_label(lhs.span, "cannot assign to this expression")
.emit();
}
let mut assignments = vec![];
// The LHS becomes a pattern: `(lhs1, lhs2)`.
let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments);
let rhs = self.lower_expr(rhs);
// Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
let destructure_let = self.stmt_let_pat(
ThinVec::new(),
whole_span,
Some(rhs),
pat,
hir::LocalSource::AssignDesugar(eq_sign_span),
);
// `a = lhs1; b = lhs2;`.
let stmts = self
.arena
.alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
// Wrap everything in a block.
hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
}
/// If the given expression is a path to a tuple struct, returns that path.
/// It is not a complete check, but just tries to reject most paths early
/// if they are not tuple structs.
/// Type checking will take care of the full validation later.
fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> {
// For tuple struct destructuring, it must be a non-qualified path (like in patterns).
if let ExprKind::Path(None, path) = &expr.kind {
// Does the path resolves to something disallowed in a tuple struct/variant pattern?
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
if partial_res.unresolved_segments() == 0
&& !partial_res.base_res().expected_in_tuple_struct_pat()
{
return None;
}
}
return Some(path);
}
None
}
/// Convert the LHS of a destructuring assignment to a pattern.
/// Each sub-assignment is recorded in `assignments`.
fn destructure_assign(
&mut self,
lhs: &Expr,
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
) -> &'hir hir::Pat<'hir> {
match &lhs.kind {
// Slice patterns.
ExprKind::Array(elements) => {
let (pats, rest) =
self.destructure_sequence(elements, "slice", eq_sign_span, assignments);
let slice_pat = if let Some((i, span)) = rest {
let (before, after) = pats.split_at(i);
hir::PatKind::Slice(
before,
Some(self.pat_without_dbm(span, hir::PatKind::Wild)),
after,
)
} else {
hir::PatKind::Slice(pats, None, &[])
};
return self.pat_without_dbm(lhs.span, slice_pat);
}
// Tuple structs.
ExprKind::Call(callee, args) => {
if let Some(path) = self.extract_tuple_struct_path(callee) {
let (pats, rest) = self.destructure_sequence(
args,
"tuple struct or variant",
eq_sign_span,
assignments,
);
let qpath = self.lower_qpath(
callee.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
// Destructure like a tuple struct.
let tuple_struct_pat =
hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0));
return self.pat_without_dbm(lhs.span, tuple_struct_pat);
}
}
// Structs.
ExprKind::Struct(path, fields, rest) => {
let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| {
let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments);
hir::FieldPat {
hir_id: self.next_id(),
ident: f.ident,
pat,
is_shorthand: f.is_shorthand,
span: f.span,
}
}));
let qpath = self.lower_qpath(
lhs.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
let fields_omitted = match rest {
StructRest::Base(e) => {
self.sess
.struct_span_err(
e.span,
"functional record updates are not allowed in destructuring \
assignments",
)
.span_suggestion(
e.span,
"consider removing the trailing pattern",
String::new(),
rustc_errors::Applicability::MachineApplicable,
)
.emit();
true
}
StructRest::Rest(_) => true,
StructRest::None => false,
};
let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted);
return self.pat_without_dbm(lhs.span, struct_pat);
}
// Tuples.
ExprKind::Tup(elements) => {
let (pats, rest) =
self.destructure_sequence(elements, "tuple", eq_sign_span, assignments);
let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0));
return self.pat_without_dbm(lhs.span, tuple_pat);
}
ExprKind::Paren(e) => {
// We special-case `(..)` for consistency with patterns.
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
return self.pat_without_dbm(lhs.span, tuple_pat);
} else {
return self.destructure_assign(e, eq_sign_span, assignments);
}
}
_ => {}
}
// Treat all other cases as normal lvalue.
let ident = Ident::new(sym::lhs, lhs.span);
let (pat, binding) = self.pat_ident(lhs.span, ident);
let ident = self.expr_ident(lhs.span, ident, binding);
let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
let expr = self.expr(lhs.span, assign, ThinVec::new());
assignments.push(self.stmt_expr(lhs.span, expr));
pat
}
/// Destructure a sequence of expressions occurring on the LHS of an assignment.
/// Such a sequence occurs in a tuple (struct)/slice.
/// Return a sequence of corresponding patterns, and the index and the span of `..` if it
/// exists.
/// Each sub-assignment is recorded in `assignments`.
fn destructure_sequence(
&mut self,
elements: &[AstP<Expr>],
ctx: &str,
eq_sign_span: Span,
assignments: &mut Vec<hir::Stmt<'hir>>,
) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
let mut rest = None;
let elements =
self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
// Check for `..` pattern.
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
if let Some((_, prev_span)) = rest {
self.ban_extra_rest_pat(e.span, prev_span, ctx);
} else {
rest = Some((i, e.span));
}
None
} else {
Some(self.destructure_assign(e, eq_sign_span, assignments))
}
}));
(elements, rest)
}
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
let e1 = self.lower_expr_mut(e1);
@ -977,7 +1230,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
asm::InlineAsmReg::parse(
sess.asm_arch?,
|feature| sess.target_features.contains(&Symbol::intern(feature)),
&sess.target.target,
&sess.target,
s,
)
.map_err(|e| {
@ -1178,52 +1431,47 @@ impl<'hir> LoweringContext<'_, 'hir> {
input| {
match used_regs.entry(r) {
Entry::Occupied(o) => {
if !skip {
skip = true;
let idx2 = *o.get();
let op2 = &operands[idx2];
let op_sp2 = asm.operands[idx2].1;
let reg2 = match op2.reg() {
Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
_ => unreachable!(),
};
let msg = format!(
"register `{}` conflicts with register `{}`",
reg.name(),
reg2.name()
);
let mut err = sess.struct_span_err(op_sp, &msg);
err.span_label(
op_sp,
&format!("register `{}`", reg.name()),
);
err.span_label(
op_sp2,
&format!("register `{}`", reg2.name()),
);
match (op, op2) {
(
hir::InlineAsmOperand::In { .. },
hir::InlineAsmOperand::Out { late, .. },
)
| (
hir::InlineAsmOperand::Out { late, .. },
hir::InlineAsmOperand::In { .. },
) => {
assert!(!*late);
let out_op_sp = if input { op_sp2 } else { op_sp };
let msg = "use `lateout` instead of \
`out` to avoid conflict";
err.span_help(out_op_sp, msg);
}
_ => {}
}
err.emit();
if skip {
return;
}
skip = true;
let idx2 = *o.get();
let op2 = &operands[idx2];
let op_sp2 = asm.operands[idx2].1;
let reg2 = match op2.reg() {
Some(asm::InlineAsmRegOrRegClass::Reg(r)) => r,
_ => unreachable!(),
};
let msg = format!(
"register `{}` conflicts with register `{}`",
reg.name(),
reg2.name()
);
let mut err = sess.struct_span_err(op_sp, &msg);
err.span_label(op_sp, &format!("register `{}`", reg.name()));
err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
match (op, op2) {
(
hir::InlineAsmOperand::In { .. },
hir::InlineAsmOperand::Out { late, .. },
)
| (
hir::InlineAsmOperand::Out { late, .. },
hir::InlineAsmOperand::In { .. },
) => {
assert!(!*late);
let out_op_sp = if input { op_sp2 } else { op_sp };
let msg = "use `lateout` instead of \
`out` to avoid conflict";
err.span_help(out_op_sp, msg);
}
_ => {}
}
err.emit();
}
Entry::Vacant(v) => {
v.insert(idx);
@ -1464,13 +1712,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::MatchSource::ForLoopDesugar,
));
let attrs: Vec<_> = e.attrs.iter().map(|a| self.lower_attr(a)).collect();
// This is effectively `{ let _result = ...; _result }`.
// The construct was introduced in #21984 and is necessary to make sure that
// temporaries in the `head` expression are dropped and do not leak to the
// surrounding scope of the `match` since the `match` is not a terminating scope.
//
// Also, add the attributes to the outer returned expr node.
self.expr_drop_temps_mut(desugared_span, match_expr, e.attrs.clone())
self.expr_drop_temps_mut(desugared_span, match_expr, attrs.into())
}
/// Desugar `ExprKind::Try` from: `<expr>?` into:
@ -1553,7 +1803,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::LangItem::TryFromError,
unstable_span,
from_expr,
try_span,
unstable_span,
);
let thin_attrs = ThinVec::from(attrs);
let catch_scope = self.catch_scopes.last().copied();

View File

@ -1096,8 +1096,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
// Check if this is a binding pattern, if so, we can optimize and avoid adding a
// `let <pat> = __argN;` statement. In this case, we do not rename the parameter.
let (ident, is_simple_parameter) = match parameter.pat.kind {
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, ident, _) => {
(ident, true)
hir::PatKind::Binding(
hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
_,
ident,
_,
) => (ident, true),
// For `ref mut` or wildcard arguments, we can't reuse the binding, but
// we can keep the same name for the parameter.
// This lets rustdoc render it correctly in documentation.
hir::PatKind::Binding(_, _, ident, _) => (ident, false),
hir::PatKind::Wild => {
(Ident::with_dummy_span(rustc_span::symbol::kw::Underscore), false)
}
_ => {
// Replace the ident for bindings that aren't simple.

View File

@ -148,7 +148,7 @@ struct LoweringContext<'a, 'hir: 'a> {
is_collecting_in_band_lifetimes: bool,
/// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
/// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
/// When `is_collecting_in_band_lifetimes` is true, each lifetime is checked
/// against this list to see if it is already in-scope, or if a definition
/// needs to be created for it.
///
@ -257,7 +257,7 @@ enum ImplTraitPosition {
/// Disallowed in `let` / `const` / `static` bindings.
Binding,
/// All other posiitons.
/// All other positions.
Other,
}
@ -363,7 +363,7 @@ enum ParenthesizedGenericArgs {
/// elided bounds follow special rules. Note that this only covers
/// cases where *nothing* is written; the `'_` in `Box<dyn Foo +
/// '_>` is a case of "modern" elision.
/// - **Deprecated** -- this coverse cases like `Ref<T>`, where the lifetime
/// - **Deprecated** -- this covers cases like `Ref<T>`, where the lifetime
/// parameter to ref is completely elided. `Ref<'_, T>` would be the modern,
/// non-deprecated equivalent.
///
@ -490,10 +490,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let count = generics
.params
.iter()
.filter(|param| match param.kind {
ast::GenericParamKind::Lifetime { .. } => true,
_ => false,
})
.filter(|param| matches!(param.kind, ast::GenericParamKind::Lifetime { .. }))
.count();
self.lctx.type_def_lifetime_params.insert(def_id.to_def_id(), count);
}
@ -538,6 +535,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
self.visit_fn_ret_ty(&f.decl.output)
}
TyKind::ImplTrait(def_node_id, _) => {
self.lctx.allocate_hir_id_counter(def_node_id);
self.with_hir_id_owner(Some(def_node_id), |this| {
visit::walk_ty(this, t);
});
}
_ => visit::walk_ty(self, t),
}
}
@ -963,12 +966,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Note that we explicitly do not walk the path. Since we don't really
// lower attributes (we use the AST version) there is nowhere to keep
// the `HirId`s. We don't actually need HIR version of attributes anyway.
// Tokens are also not needed after macro expansion and parsing.
let kind = match attr.kind {
AttrKind::Normal(ref item) => AttrKind::Normal(AttrItem {
path: item.path.clone(),
args: self.lower_mac_args(&item.args),
tokens: None,
}),
AttrKind::Normal(ref item, _) => AttrKind::Normal(
AttrItem {
path: item.path.clone(),
args: self.lower_mac_args(&item.args),
tokens: None,
},
None,
),
AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
};
@ -1346,10 +1353,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Add a definition for the in-band `Param`.
let def_id = self.resolver.local_def_id(def_node_id);
let hir_bounds = self.lower_param_bounds(
bounds,
ImplTraitContext::Universal(in_band_ty_params),
);
self.allocate_hir_id_counter(def_node_id);
let hir_bounds = self.with_hir_id_owner(def_node_id, |this| {
this.lower_param_bounds(
bounds,
ImplTraitContext::Universal(in_band_ty_params),
)
});
// Set the name to `impl Bound1 + Bound2`.
let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
in_band_ty_params.push(hir::GenericParam {
@ -1713,7 +1724,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
pat: self.lower_pat(&l.pat),
init,
span: l.span,
attrs: l.attrs.clone(),
attrs: l.attrs.iter().map(|a| self.lower_attr(a)).collect::<Vec<_>>().into(),
source: hir::LocalSource::Normal,
},
ids,
@ -2200,7 +2211,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.attrs
.iter()
.filter(|attr| self.sess.check_name(attr, sym::rustc_synthetic))
.map(|_| hir::SyntheticTyParamKind::ImplTrait)
.map(|_| hir::SyntheticTyParamKind::FromAttr)
.next(),
};
@ -2523,6 +2534,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
hir_id,
kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
span,
default_binding_modes: true,
}),
hir_id,
)
@ -2533,7 +2545,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span })
self.arena.alloc(hir::Pat {
hir_id: self.next_id(),
kind,
span,
default_binding_modes: true,
})
}
fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
self.arena.alloc(hir::Pat {
hir_id: self.next_id(),
kind,
span,
default_binding_modes: false,
})
}
fn ty_path(

View File

@ -10,82 +10,90 @@ use rustc_span::symbol::Ident;
use rustc_span::{source_map::Spanned, Span};
impl<'a, 'hir> LoweringContext<'a, 'hir> {
crate fn lower_pat(&mut self, p: &Pat) -> &'hir hir::Pat<'hir> {
crate fn lower_pat(&mut self, mut pattern: &Pat) -> &'hir hir::Pat<'hir> {
ensure_sufficient_stack(|| {
let node = match p.kind {
PatKind::Wild => hir::PatKind::Wild,
PatKind::Ident(ref binding_mode, ident, ref sub) => {
let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
let node = self.lower_pat_ident(p, binding_mode, ident, lower_sub);
node
}
PatKind::Lit(ref e) => hir::PatKind::Lit(self.lower_expr(e)),
PatKind::TupleStruct(ref path, ref pats) => {
let qpath = self.lower_qpath(
p.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
hir::PatKind::TupleStruct(qpath, pats, ddpos)
}
PatKind::Or(ref pats) => hir::PatKind::Or(
self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
),
PatKind::Path(ref qself, ref path) => {
let qpath = self.lower_qpath(
p.id,
qself,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
hir::PatKind::Path(qpath)
}
PatKind::Struct(ref path, ref fields, etc) => {
let qpath = self.lower_qpath(
p.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
// loop here to avoid recursion
let node = loop {
match pattern.kind {
PatKind::Wild => break hir::PatKind::Wild,
PatKind::Ident(ref binding_mode, ident, ref sub) => {
let lower_sub = |this: &mut Self| sub.as_ref().map(|s| this.lower_pat(&*s));
break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
}
PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)),
PatKind::TupleStruct(ref path, ref pats) => {
let qpath = self.lower_qpath(
pattern.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple struct");
break hir::PatKind::TupleStruct(qpath, pats, ddpos);
}
PatKind::Or(ref pats) => {
break hir::PatKind::Or(
self.arena.alloc_from_iter(pats.iter().map(|x| self.lower_pat(x))),
);
}
PatKind::Path(ref qself, ref path) => {
let qpath = self.lower_qpath(
pattern.id,
qself,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
break hir::PatKind::Path(qpath);
}
PatKind::Struct(ref path, ref fields, etc) => {
let qpath = self.lower_qpath(
pattern.id,
&None,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
);
let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
hir_id: self.next_id(),
ident: f.ident,
pat: self.lower_pat(&f.pat),
is_shorthand: f.is_shorthand,
span: f.span,
}));
hir::PatKind::Struct(qpath, fs, etc)
let fs = self.arena.alloc_from_iter(fields.iter().map(|f| hir::FieldPat {
hir_id: self.next_id(),
ident: f.ident,
pat: self.lower_pat(&f.pat),
is_shorthand: f.is_shorthand,
span: f.span,
}));
break hir::PatKind::Struct(qpath, fs, etc);
}
PatKind::Tuple(ref pats) => {
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
break hir::PatKind::Tuple(pats, ddpos);
}
PatKind::Box(ref inner) => {
break hir::PatKind::Box(self.lower_pat(inner));
}
PatKind::Ref(ref inner, mutbl) => {
break hir::PatKind::Ref(self.lower_pat(inner), mutbl);
}
PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
break hir::PatKind::Range(
e1.as_deref().map(|e| self.lower_expr(e)),
e2.as_deref().map(|e| self.lower_expr(e)),
self.lower_range_end(end, e2.is_some()),
);
}
PatKind::Slice(ref pats) => break self.lower_pat_slice(pats),
PatKind::Rest => {
// If we reach here the `..` pattern is not semantically allowed.
break self.ban_illegal_rest_pat(pattern.span);
}
// return inner to be processed in next loop
PatKind::Paren(ref inner) => pattern = inner,
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", pattern.span),
}
PatKind::Tuple(ref pats) => {
let (pats, ddpos) = self.lower_pat_tuple(pats, "tuple");
hir::PatKind::Tuple(pats, ddpos)
}
PatKind::Box(ref inner) => hir::PatKind::Box(self.lower_pat(inner)),
PatKind::Ref(ref inner, mutbl) => hir::PatKind::Ref(self.lower_pat(inner), mutbl),
PatKind::Range(ref e1, ref e2, Spanned { node: ref end, .. }) => {
hir::PatKind::Range(
e1.as_deref().map(|e| self.lower_expr(e)),
e2.as_deref().map(|e| self.lower_expr(e)),
self.lower_range_end(end, e2.is_some()),
)
}
PatKind::Slice(ref pats) => self.lower_pat_slice(pats),
PatKind::Rest => {
// If we reach here the `..` pattern is not semantically allowed.
self.ban_illegal_rest_pat(p.span)
}
// FIXME: consider not using recursion to lower this.
PatKind::Paren(ref inner) => return self.lower_pat(inner),
PatKind::MacCall(_) => panic!("{:?} shouldn't exist here", p.span),
};
self.pat_with_node_id_of(p, node)
self.pat_with_node_id_of(pattern, node)
})
}
@ -265,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
/// Construct a `Pat` with the `HirId` of `p.id` lowered.
fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
self.arena.alloc(hir::Pat {
hir_id: self.lower_node_id(p.id),
kind,
span: p.span,
default_binding_modes: true,
})
}
/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
self.diagnostic()
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
.span_label(sp, &format!("can only be used once per {} pattern", ctx))

View File

@ -262,10 +262,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.lower_angle_bracketed_parameter_data(&Default::default(), param_mode, itctx)
};
let has_lifetimes = generic_args.args.iter().any(|arg| match arg {
GenericArg::Lifetime(_) => true,
_ => false,
});
let has_lifetimes =
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
let first_generic_span = generic_args
.args
.iter()
@ -310,8 +308,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
E0726,
"implicit elided lifetime not allowed here"
);
rustc_session::lint::add_elided_lifetime_in_path_suggestion(
&self.sess,
rustc_errors::add_elided_lifetime_in_path_suggestion(
&self.sess.source_map(),
&mut err,
expected_lifetimes,
path_span,

View File

@ -287,7 +287,7 @@ impl<'a> AstValidator<'a> {
// ```
fn check_expr_within_pat(&self, expr: &Expr, allow_paths: bool) {
match expr.kind {
ExprKind::Lit(..) | ExprKind::Err => {}
ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Err => {}
ExprKind::Path(..) if allow_paths => {}
ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
_ => self.err_handler().span_err(
@ -516,7 +516,7 @@ impl<'a> AstValidator<'a> {
self.session.source_map().guess_head_span(self.extern_mod.unwrap().span)
}
/// An `fn` in `extern { ... }` cannot have qualfiers, e.g. `async fn`.
/// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) {
if header.has_qualifiers() {
self.err_handler()
@ -796,7 +796,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
match &expr.kind {
ExprKind::LlvmInlineAsm(..) if !self.session.target.target.options.allow_asm => {
ExprKind::LlvmInlineAsm(..) if !self.session.target.allow_asm => {
struct_span_err!(
self.session,
expr.span,

View File

@ -629,6 +629,8 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
gate_all!(const_trait_impl, "const trait impls are experimental");
gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
gate_all!(inline_const, "inline-const is experimental");
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
// All uses of `gate_all!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded).

View File

@ -114,9 +114,9 @@ impl<'ast> Visitor<'ast> for NodeCounter {
self.count += 1;
walk_lifetime(self, lifetime)
}
fn visit_mac(&mut self, _mac: &MacCall) {
fn visit_mac_call(&mut self, mac: &MacCall) {
self.count += 1;
walk_mac(self, _mac)
walk_mac(self, mac)
}
fn visit_path(&mut self, path: &Path, _id: NodeId) {
self.count += 1;

View File

@ -54,10 +54,6 @@ impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
}
visit::walk_ty(self, t);
}
fn visit_mac(&mut self, mac: &'a ast::MacCall) {
visit::walk_mac(self, mac);
}
}
pub fn run(span_diagnostic: &rustc_errors::Handler, mode: &str, krate: &ast::Crate) {

View File

@ -170,17 +170,11 @@ pub enum Token {
impl Token {
crate fn is_eof(&self) -> bool {
match *self {
Token::Eof => true,
_ => false,
}
matches!(self, Token::Eof)
}
pub fn is_hardbreak_tok(&self) -> bool {
match *self {
Token::Break(BreakToken { offset: 0, blank_space: bs }) if bs == SIZE_INFINITY => true,
_ => false,
}
matches!(self, Token::Break(BreakToken { offset: 0, blank_space: SIZE_INFINITY }))
}
}
@ -396,7 +390,7 @@ impl Printer {
self.scan_stack.pop_front().unwrap()
}
fn scan_top(&mut self) -> usize {
fn scan_top(&self) -> usize {
*self.scan_stack.front().unwrap()
}
@ -490,13 +484,10 @@ impl Printer {
self.pending_indentation += amount;
}
fn get_top(&mut self) -> PrintStackElem {
match self.print_stack.last() {
Some(el) => *el,
None => {
PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
}
}
fn get_top(&self) -> PrintStackElem {
*self.print_stack.last().unwrap_or({
&PrintStackElem { offset: 0, pbreak: PrintStackBreak::Broken(Breaks::Inconsistent) }
})
}
fn print_begin(&mut self, b: BeginToken, l: isize) {

View File

@ -0,0 +1,104 @@
#[cfg(test)]
mod tests;
pub mod state;
pub use state::{print_crate, AnnNode, Comments, PpAnn, PrintState, State};
use rustc_ast as ast;
use rustc_ast::token::{Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
pub fn nonterminal_to_string_no_extra_parens(nt: &Nonterminal) -> String {
let state = State::without_insert_extra_parens();
state.nonterminal_to_string(nt)
}
pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
State::new().nonterminal_to_string(nt)
}
/// Print the token kind precisely, without converting `$crate` into its respective crate name.
pub fn token_kind_to_string(tok: &TokenKind) -> String {
State::new().token_kind_to_string(tok)
}
/// Print the token precisely, without converting `$crate` into its respective crate name.
pub fn token_to_string(token: &Token) -> String {
State::new().token_to_string(token)
}
pub fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
State::new().token_to_string_ext(token, convert_dollar_crate)
}
pub fn ty_to_string(ty: &ast::Ty) -> String {
State::new().ty_to_string(ty)
}
pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
State::new().bounds_to_string(bounds)
}
pub fn pat_to_string(pat: &ast::Pat) -> String {
State::new().pat_to_string(pat)
}
pub fn expr_to_string(e: &ast::Expr) -> String {
State::new().expr_to_string(e)
}
pub fn tt_to_string(tt: &TokenTree) -> String {
State::new().tt_to_string(tt)
}
pub fn tts_to_string(tokens: &TokenStream) -> String {
State::new().tts_to_string(tokens)
}
pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
State::new().stmt_to_string(stmt)
}
pub fn item_to_string(i: &ast::Item) -> String {
State::new().item_to_string(i)
}
pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
State::new().generic_params_to_string(generic_params)
}
pub fn path_to_string(p: &ast::Path) -> String {
State::new().path_to_string(p)
}
pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
State::new().path_segment_to_string(p)
}
pub fn vis_to_string(v: &ast::Visibility) -> String {
State::new().vis_to_string(v)
}
pub fn block_to_string(blk: &ast::Block) -> String {
State::new().block_to_string(blk)
}
pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
State::new().meta_list_item_to_string(li)
}
pub fn attr_item_to_string(ai: &ast::AttrItem) -> String {
State::new().attr_item_to_string(ai)
}
pub fn attribute_to_string(attr: &ast::Attribute) -> String {
State::new().attribute_to_string(attr)
}
pub fn param_to_string(arg: &ast::Param) -> String {
State::new().param_to_string(arg)
}
pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
State::new().to_string(f)
}

View File

@ -20,9 +20,6 @@ use rustc_span::{BytePos, FileName, Span};
use std::borrow::Cow;
#[cfg(test)]
mod tests;
pub enum MacHeader<'a> {
Path(&'a ast::Path),
Keyword(&'static str),
@ -66,7 +63,7 @@ impl<'a> Comments<'a> {
}
pub fn trailing_comment(
&mut self,
&self,
span: rustc_span::Span,
next_pos: Option<BytePos>,
) -> Option<Comment> {
@ -91,6 +88,13 @@ pub struct State<'a> {
comments: Option<Comments<'a>>,
ann: &'a (dyn PpAnn + 'a),
is_expanded: bool,
// If `true`, additional parenthesis (separate from `ExprKind::Paren`)
// are inserted to ensure that proper precedence is preserved
// in the pretty-printed output.
//
// This is usually `true`, except when performing the pretty-print/reparse
// check in `nt_to_tokenstream`
insert_extra_parens: bool,
}
crate const INDENT_UNIT: usize = 4;
@ -112,6 +116,7 @@ pub fn print_crate<'a>(
comments: Some(Comments::new(sm, filename, input)),
ann,
is_expanded,
insert_extra_parens: true,
};
if is_expanded && has_injected_crate {
@ -142,13 +147,6 @@ pub fn print_crate<'a>(
s.s.eof()
}
pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
let mut printer =
State { s: pp::mk_printer(), comments: None, ann: &NoAnn, is_expanded: false };
f(&mut printer);
printer.s.eof()
}
// This makes printed token streams look slightly nicer,
// and also addresses some specific regressions described in #63896 and #73345.
fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
@ -158,24 +156,13 @@ fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
}
}
match tt {
TokenTree::Token(token) => match token.kind {
token::Comma => false,
_ => true,
},
TokenTree::Delimited(_, DelimToken::Paren, _) => match prev {
TokenTree::Token(token) => match token.kind {
token::Ident(_, _) => false,
_ => true,
},
_ => true,
},
TokenTree::Delimited(_, DelimToken::Bracket, _) => match prev {
TokenTree::Token(token) => match token.kind {
token::Pound => false,
_ => true,
},
_ => true,
},
TokenTree::Token(token) => token.kind != token::Comma,
TokenTree::Delimited(_, DelimToken::Paren, _) => {
!matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }))
}
TokenTree::Delimited(_, DelimToken::Bracket, _) => {
!matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }))
}
TokenTree::Delimited(..) => true,
}
}
@ -231,173 +218,8 @@ pub fn literal_to_string(lit: token::Lit) -> String {
out
}
/// Print the token kind precisely, without converting `$crate` into its respective crate name.
pub fn token_kind_to_string(tok: &TokenKind) -> String {
token_kind_to_string_ext(tok, None)
}
fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>) -> String {
match *tok {
token::Eq => "=".to_string(),
token::Lt => "<".to_string(),
token::Le => "<=".to_string(),
token::EqEq => "==".to_string(),
token::Ne => "!=".to_string(),
token::Ge => ">=".to_string(),
token::Gt => ">".to_string(),
token::Not => "!".to_string(),
token::Tilde => "~".to_string(),
token::OrOr => "||".to_string(),
token::AndAnd => "&&".to_string(),
token::BinOp(op) => binop_to_string(op).to_string(),
token::BinOpEq(op) => format!("{}=", binop_to_string(op)),
/* Structural symbols */
token::At => "@".to_string(),
token::Dot => ".".to_string(),
token::DotDot => "..".to_string(),
token::DotDotDot => "...".to_string(),
token::DotDotEq => "..=".to_string(),
token::Comma => ",".to_string(),
token::Semi => ";".to_string(),
token::Colon => ":".to_string(),
token::ModSep => "::".to_string(),
token::RArrow => "->".to_string(),
token::LArrow => "<-".to_string(),
token::FatArrow => "=>".to_string(),
token::OpenDelim(token::Paren) => "(".to_string(),
token::CloseDelim(token::Paren) => ")".to_string(),
token::OpenDelim(token::Bracket) => "[".to_string(),
token::CloseDelim(token::Bracket) => "]".to_string(),
token::OpenDelim(token::Brace) => "{".to_string(),
token::CloseDelim(token::Brace) => "}".to_string(),
token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".to_string(),
token::Pound => "#".to_string(),
token::Dollar => "$".to_string(),
token::Question => "?".to_string(),
token::SingleQuote => "'".to_string(),
/* Literals */
token::Literal(lit) => literal_to_string(lit),
/* Name components */
token::Ident(s, is_raw) => IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string(),
token::Lifetime(s) => s.to_string(),
/* Other */
token::DocComment(comment_kind, attr_style, data) => {
doc_comment_to_string(comment_kind, attr_style, data)
}
token::Eof => "<eof>".to_string(),
token::Interpolated(ref nt) => nonterminal_to_string(nt),
}
}
/// Print the token precisely, without converting `$crate` into its respective crate name.
pub fn token_to_string(token: &Token) -> String {
token_to_string_ext(token, false)
}
fn token_to_string_ext(token: &Token, convert_dollar_crate: bool) -> String {
let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
token_kind_to_string_ext(&token.kind, convert_dollar_crate)
}
pub fn nonterminal_to_string(nt: &Nonterminal) -> String {
match *nt {
token::NtExpr(ref e) => expr_to_string(e),
token::NtMeta(ref e) => attr_item_to_string(e),
token::NtTy(ref e) => ty_to_string(e),
token::NtPath(ref e) => path_to_string(e),
token::NtItem(ref e) => item_to_string(e),
token::NtBlock(ref e) => block_to_string(e),
token::NtStmt(ref e) => stmt_to_string(e),
token::NtPat(ref e) => pat_to_string(e),
token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
token::NtLifetime(e) => e.to_string(),
token::NtLiteral(ref e) => expr_to_string(e),
token::NtTT(ref tree) => tt_to_string(tree),
token::NtVis(ref e) => vis_to_string(e),
}
}
pub fn ty_to_string(ty: &ast::Ty) -> String {
to_string(|s| s.print_type(ty))
}
pub fn bounds_to_string(bounds: &[ast::GenericBound]) -> String {
to_string(|s| s.print_type_bounds("", bounds))
}
pub fn pat_to_string(pat: &ast::Pat) -> String {
to_string(|s| s.print_pat(pat))
}
pub fn expr_to_string(e: &ast::Expr) -> String {
to_string(|s| s.print_expr(e))
}
pub fn tt_to_string(tt: &TokenTree) -> String {
to_string(|s| s.print_tt(tt, false))
}
pub fn tts_to_string(tokens: &TokenStream) -> String {
to_string(|s| s.print_tts(tokens, false))
}
pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
to_string(|s| s.print_stmt(stmt))
}
pub fn item_to_string(i: &ast::Item) -> String {
to_string(|s| s.print_item(i))
}
pub fn generic_params_to_string(generic_params: &[ast::GenericParam]) -> String {
to_string(|s| s.print_generic_params(generic_params))
}
pub fn path_to_string(p: &ast::Path) -> String {
to_string(|s| s.print_path(p, false, 0))
}
pub fn path_segment_to_string(p: &ast::PathSegment) -> String {
to_string(|s| s.print_path_segment(p, false))
}
pub fn vis_to_string(v: &ast::Visibility) -> String {
to_string(|s| s.print_visibility(v))
}
fn block_to_string(blk: &ast::Block) -> String {
to_string(|s| {
// Containing cbox, will be closed by `print_block` at `}`.
s.cbox(INDENT_UNIT);
// Head-ibox, will be closed by `print_block` after `{`.
s.ibox(0);
s.print_block(blk)
})
}
pub fn meta_list_item_to_string(li: &ast::NestedMetaItem) -> String {
to_string(|s| s.print_meta_list_item(li))
}
fn attr_item_to_string(ai: &ast::AttrItem) -> String {
to_string(|s| s.print_attr_item(ai, ai.path.span))
}
pub fn attribute_to_string(attr: &ast::Attribute) -> String {
to_string(|s| s.print_attribute(attr))
}
pub fn param_to_string(arg: &ast::Param) -> String {
to_string(|s| s.print_param(arg, false))
}
fn visibility_qualified(vis: &ast::Visibility, s: &str) -> String {
format!("{}{}", to_string(|s| s.print_visibility(vis)), s)
format!("{}{}", State::new().to_string(|s| s.print_visibility(vis)), s)
}
impl std::ops::Deref for State<'_> {
@ -414,6 +236,7 @@ impl std::ops::DerefMut for State<'_> {
}
pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::DerefMut {
fn insert_extra_parens(&self) -> bool;
fn comments(&mut self) -> &mut Option<Comments<'a>>;
fn print_ident(&mut self, ident: Ident);
fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
@ -603,7 +426,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
}
self.maybe_print_comment(attr.span.lo());
match attr.kind {
ast::AttrKind::Normal(ref item) => {
ast::AttrKind::Normal(ref item, _) => {
match attr.style {
ast::AttrStyle::Inner => self.word("#!["),
ast::AttrStyle::Outer => self.word("#["),
@ -679,7 +502,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
fn print_tt(&mut self, tt: &TokenTree, convert_dollar_crate: bool) {
match tt {
TokenTree::Token(token) => {
self.word(token_to_string_ext(&token, convert_dollar_crate));
let token_str = self.token_to_string_ext(&token, convert_dollar_crate);
self.word(token_str);
if let token::DocComment(..) = token.kind {
self.hardbreak()
}
@ -745,14 +569,20 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
self.space();
}
}
_ => self.word(token_kind_to_string(&token::OpenDelim(delim))),
_ => {
let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
self.word(token_str)
}
}
self.ibox(0);
self.print_tts(tts, convert_dollar_crate);
self.end();
match delim {
DelimToken::Brace => self.bclose(span),
_ => self.word(token_kind_to_string(&token::CloseDelim(delim))),
_ => {
let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
self.word(token_str)
}
}
}
@ -809,18 +639,197 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
if !self.is_beginning_of_line() {
self.break_offset(n, off)
} else {
if off != 0 && self.last_token().is_hardbreak_tok() {
// We do something pretty sketchy here: tuck the nonzero
// offset-adjustment we were going to deposit along with the
// break into the previous hardbreak.
self.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
}
} else if off != 0 && self.last_token().is_hardbreak_tok() {
// We do something pretty sketchy here: tuck the nonzero
// offset-adjustment we were going to deposit along with the
// break into the previous hardbreak.
self.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
}
}
fn nonterminal_to_string(&self, nt: &Nonterminal) -> String {
match *nt {
token::NtExpr(ref e) => self.expr_to_string(e),
token::NtMeta(ref e) => self.attr_item_to_string(e),
token::NtTy(ref e) => self.ty_to_string(e),
token::NtPath(ref e) => self.path_to_string(e),
token::NtItem(ref e) => self.item_to_string(e),
token::NtBlock(ref e) => self.block_to_string(e),
token::NtStmt(ref e) => self.stmt_to_string(e),
token::NtPat(ref e) => self.pat_to_string(e),
token::NtIdent(e, is_raw) => IdentPrinter::for_ast_ident(e, is_raw).to_string(),
token::NtLifetime(e) => e.to_string(),
token::NtLiteral(ref e) => self.expr_to_string(e),
token::NtTT(ref tree) => self.tt_to_string(tree),
token::NtVis(ref e) => self.vis_to_string(e),
}
}
/// Print the token kind precisely, without converting `$crate` into its respective crate name.
fn token_kind_to_string(&self, tok: &TokenKind) -> String {
self.token_kind_to_string_ext(tok, None)
}
fn token_kind_to_string_ext(
&self,
tok: &TokenKind,
convert_dollar_crate: Option<Span>,
) -> String {
match *tok {
token::Eq => "=".to_string(),
token::Lt => "<".to_string(),
token::Le => "<=".to_string(),
token::EqEq => "==".to_string(),
token::Ne => "!=".to_string(),
token::Ge => ">=".to_string(),
token::Gt => ">".to_string(),
token::Not => "!".to_string(),
token::Tilde => "~".to_string(),
token::OrOr => "||".to_string(),
token::AndAnd => "&&".to_string(),
token::BinOp(op) => binop_to_string(op).to_string(),
token::BinOpEq(op) => format!("{}=", binop_to_string(op)),
/* Structural symbols */
token::At => "@".to_string(),
token::Dot => ".".to_string(),
token::DotDot => "..".to_string(),
token::DotDotDot => "...".to_string(),
token::DotDotEq => "..=".to_string(),
token::Comma => ",".to_string(),
token::Semi => ";".to_string(),
token::Colon => ":".to_string(),
token::ModSep => "::".to_string(),
token::RArrow => "->".to_string(),
token::LArrow => "<-".to_string(),
token::FatArrow => "=>".to_string(),
token::OpenDelim(token::Paren) => "(".to_string(),
token::CloseDelim(token::Paren) => ")".to_string(),
token::OpenDelim(token::Bracket) => "[".to_string(),
token::CloseDelim(token::Bracket) => "]".to_string(),
token::OpenDelim(token::Brace) => "{".to_string(),
token::CloseDelim(token::Brace) => "}".to_string(),
token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".to_string(),
token::Pound => "#".to_string(),
token::Dollar => "$".to_string(),
token::Question => "?".to_string(),
token::SingleQuote => "'".to_string(),
/* Literals */
token::Literal(lit) => literal_to_string(lit),
/* Name components */
token::Ident(s, is_raw) => {
IdentPrinter::new(s, is_raw, convert_dollar_crate).to_string()
}
token::Lifetime(s) => s.to_string(),
/* Other */
token::DocComment(comment_kind, attr_style, data) => {
doc_comment_to_string(comment_kind, attr_style, data)
}
token::Eof => "<eof>".to_string(),
token::Interpolated(ref nt) => self.nonterminal_to_string(nt),
}
}
/// Print the token precisely, without converting `$crate` into its respective crate name.
fn token_to_string(&self, token: &Token) -> String {
self.token_to_string_ext(token, false)
}
fn token_to_string_ext(&self, token: &Token, convert_dollar_crate: bool) -> String {
let convert_dollar_crate = convert_dollar_crate.then_some(token.span);
self.token_kind_to_string_ext(&token.kind, convert_dollar_crate)
}
fn ty_to_string(&self, ty: &ast::Ty) -> String {
self.to_string(|s| s.print_type(ty))
}
fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String {
self.to_string(|s| s.print_type_bounds("", bounds))
}
fn pat_to_string(&self, pat: &ast::Pat) -> String {
self.to_string(|s| s.print_pat(pat))
}
fn expr_to_string(&self, e: &ast::Expr) -> String {
self.to_string(|s| s.print_expr(e))
}
fn tt_to_string(&self, tt: &TokenTree) -> String {
self.to_string(|s| s.print_tt(tt, false))
}
fn tts_to_string(&self, tokens: &TokenStream) -> String {
self.to_string(|s| s.print_tts(tokens, false))
}
fn stmt_to_string(&self, stmt: &ast::Stmt) -> String {
self.to_string(|s| s.print_stmt(stmt))
}
fn item_to_string(&self, i: &ast::Item) -> String {
self.to_string(|s| s.print_item(i))
}
fn generic_params_to_string(&self, generic_params: &[ast::GenericParam]) -> String {
self.to_string(|s| s.print_generic_params(generic_params))
}
fn path_to_string(&self, p: &ast::Path) -> String {
self.to_string(|s| s.print_path(p, false, 0))
}
fn path_segment_to_string(&self, p: &ast::PathSegment) -> String {
self.to_string(|s| s.print_path_segment(p, false))
}
fn vis_to_string(&self, v: &ast::Visibility) -> String {
self.to_string(|s| s.print_visibility(v))
}
fn block_to_string(&self, blk: &ast::Block) -> String {
self.to_string(|s| {
// Containing cbox, will be closed by `print_block` at `}`.
s.cbox(INDENT_UNIT);
// Head-ibox, will be closed by `print_block` after `{`.
s.ibox(0);
s.print_block(blk)
})
}
fn meta_list_item_to_string(&self, li: &ast::NestedMetaItem) -> String {
self.to_string(|s| s.print_meta_list_item(li))
}
fn attr_item_to_string(&self, ai: &ast::AttrItem) -> String {
self.to_string(|s| s.print_attr_item(ai, ai.path.span))
}
fn attribute_to_string(&self, attr: &ast::Attribute) -> String {
self.to_string(|s| s.print_attribute(attr))
}
fn param_to_string(&self, arg: &ast::Param) -> String {
self.to_string(|s| s.print_param(arg, false))
}
fn to_string(&self, f: impl FnOnce(&mut State<'_>)) -> String {
let mut printer = State::new();
printer.insert_extra_parens = self.insert_extra_parens();
f(&mut printer);
printer.s.eof()
}
}
impl<'a> PrintState<'a> for State<'a> {
fn insert_extra_parens(&self) -> bool {
self.insert_extra_parens
}
fn comments(&mut self) -> &mut Option<Comments<'a>> {
&mut self.comments
}
@ -856,6 +865,20 @@ impl<'a> PrintState<'a> for State<'a> {
}
impl<'a> State<'a> {
pub fn new() -> State<'a> {
State {
s: pp::mk_printer(),
comments: None,
ann: &NoAnn,
is_expanded: false,
insert_extra_parens: true,
}
}
pub(super) fn without_insert_extra_parens() -> State<'a> {
State { insert_extra_parens: false, ..State::new() }
}
// Synthesizes a comment that was not textually present in the original source
// file.
pub fn synth_comment(&mut self, text: String) {
@ -1139,7 +1162,7 @@ impl<'a> State<'a> {
self.print_fn_full(sig, item.ident, gen, &item.vis, def, body, &item.attrs);
}
ast::ItemKind::Mod(ref _mod) => {
self.head(to_string(|s| {
self.head(self.to_string(|s| {
s.print_visibility(&item.vis);
s.print_unsafety(_mod.unsafety);
s.word("mod");
@ -1158,7 +1181,7 @@ impl<'a> State<'a> {
}
}
ast::ItemKind::ForeignMod(ref nmod) => {
self.head(to_string(|s| {
self.head(self.to_string(|s| {
s.print_unsafety(nmod.unsafety);
s.word("extern");
}));
@ -1366,7 +1389,7 @@ impl<'a> State<'a> {
ast::CrateSugar::JustCrate => self.word_nbsp("crate"),
},
ast::VisibilityKind::Restricted { ref path, .. } => {
let path = to_string(|s| s.print_path(path, false, 0));
let path = self.to_string(|s| s.print_path(path, false, 0));
if path == "self" || path == "super" {
self.word_nbsp(format!("pub({})", path))
} else {
@ -1658,7 +1681,8 @@ impl<'a> State<'a> {
}
/// Prints `expr` or `(expr)` when `needs_par` holds.
fn print_expr_cond_paren(&mut self, expr: &ast::Expr, needs_par: bool) {
fn print_expr_cond_paren(&mut self, expr: &ast::Expr, mut needs_par: bool) {
needs_par &= self.insert_extra_parens;
if needs_par {
self.popen();
}
@ -1677,6 +1701,14 @@ impl<'a> State<'a> {
self.end();
}
fn print_expr_anon_const(&mut self, expr: &ast::AnonConst, attrs: &[ast::Attribute]) {
self.ibox(INDENT_UNIT);
self.s.word("const");
self.print_inner_attributes_inline(attrs);
self.print_expr(&expr.value);
self.end();
}
fn print_expr_repeat(
&mut self,
element: &ast::Expr,
@ -1697,7 +1729,7 @@ impl<'a> State<'a> {
&mut self,
path: &ast::Path,
fields: &[ast::Field],
wth: &Option<P<ast::Expr>>,
rest: &ast::StructRest,
attrs: &[ast::Attribute],
) {
self.print_path(path, true, 0);
@ -1718,22 +1750,21 @@ impl<'a> State<'a> {
},
|f| f.span,
);
match *wth {
Some(ref expr) => {
match rest {
ast::StructRest::Base(_) | ast::StructRest::Rest(_) => {
self.ibox(INDENT_UNIT);
if !fields.is_empty() {
self.s.word(",");
self.s.space();
}
self.s.word("..");
self.print_expr(expr);
if let ast::StructRest::Base(ref expr) = *rest {
self.print_expr(expr);
}
self.end();
}
_ => {
if !fields.is_empty() {
self.s.word(",")
}
}
ast::StructRest::None if !fields.is_empty() => self.s.word(","),
_ => {}
}
self.s.word("}");
}
@ -1853,11 +1884,14 @@ impl<'a> State<'a> {
ast::ExprKind::Array(ref exprs) => {
self.print_expr_vec(&exprs[..], attrs);
}
ast::ExprKind::ConstBlock(ref anon_const) => {
self.print_expr_anon_const(anon_const, attrs);
}
ast::ExprKind::Repeat(ref element, ref count) => {
self.print_expr_repeat(element, count, attrs);
}
ast::ExprKind::Struct(ref path, ref fields, ref wth) => {
self.print_expr_struct(path, &fields[..], wth, attrs);
ast::ExprKind::Struct(ref path, ref fields, ref rest) => {
self.print_expr_struct(path, &fields[..], rest, attrs);
}
ast::ExprKind::Tup(ref exprs) => {
self.print_expr_tup(&exprs[..], attrs);

View File

@ -75,6 +75,12 @@ pub enum InlineAttr {
Never,
}
#[derive(Clone, Encodable, Decodable)]
pub enum InstructionSetAttr {
ArmA32,
ArmT32,
}
#[derive(Clone, Encodable, Decodable)]
pub enum OptimizeAttr {
None,
@ -148,7 +154,7 @@ pub struct ConstStability {
}
/// The available stability levels.
#[derive(Encodable, Decodable, PartialEq, PartialOrd, Copy, Clone, Debug, Eq, Hash)]
#[derive(Encodable, Decodable, PartialEq, Copy, Clone, Debug, Eq, Hash)]
#[derive(HashStable_Generic)]
pub enum StabilityLevel {
// Reason for the current stability level and the relevant rust-lang issue
@ -631,19 +637,15 @@ pub struct Deprecation {
}
/// Finds the deprecation attribute. `None` if none exists.
pub fn find_deprecation(sess: &Session, attrs: &[Attribute], item_sp: Span) -> Option<Deprecation> {
find_deprecation_generic(sess, attrs.iter(), item_sp)
pub fn find_deprecation(sess: &Session, attrs: &[Attribute]) -> Option<(Deprecation, Span)> {
find_deprecation_generic(sess, attrs.iter())
}
fn find_deprecation_generic<'a, I>(
sess: &Session,
attrs_iter: I,
item_sp: Span,
) -> Option<Deprecation>
fn find_deprecation_generic<'a, I>(sess: &Session, attrs_iter: I) -> Option<(Deprecation, Span)>
where
I: Iterator<Item = &'a Attribute>,
{
let mut depr: Option<Deprecation> = None;
let mut depr: Option<(Deprecation, Span)> = None;
let diagnostic = &sess.parse_sess.span_diagnostic;
'outer: for attr in attrs_iter {
@ -652,8 +654,11 @@ where
continue;
}
if depr.is_some() {
struct_span_err!(diagnostic, item_sp, E0550, "multiple deprecated attributes").emit();
if let Some((_, span)) = &depr {
struct_span_err!(diagnostic, attr.span, E0550, "multiple deprecated attributes")
.span_label(attr.span, "repeated deprecation attribute")
.span_label(*span, "first deprecation attribute")
.emit();
break;
}
@ -774,7 +779,7 @@ where
sess.mark_attr_used(&attr);
let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated);
depr = Some(Deprecation { since, note, suggestion, is_since_rustc_version });
depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span));
}
depr
@ -895,38 +900,36 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
)
.emit();
}
} else {
if let Some(meta_item) = item.meta_item() {
if meta_item.has_name(sym::align) {
if let MetaItemKind::NameValue(ref value) = meta_item.kind {
recognised = true;
let mut err = struct_span_err!(
diagnostic,
item.span(),
E0693,
"incorrect `repr(align)` attribute format"
);
match value.kind {
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
err.span_suggestion(
item.span(),
"use parentheses instead",
format!("align({})", int),
Applicability::MachineApplicable,
);
}
ast::LitKind::Str(s, _) => {
err.span_suggestion(
item.span(),
"use parentheses instead",
format!("align({})", s),
Applicability::MachineApplicable,
);
}
_ => {}
} else if let Some(meta_item) = item.meta_item() {
if meta_item.has_name(sym::align) {
if let MetaItemKind::NameValue(ref value) = meta_item.kind {
recognised = true;
let mut err = struct_span_err!(
diagnostic,
item.span(),
E0693,
"incorrect `repr(align)` attribute format"
);
match value.kind {
ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
err.span_suggestion(
item.span(),
"use parentheses instead",
format!("align({})", int),
Applicability::MachineApplicable,
);
}
err.emit();
ast::LitKind::Str(s, _) => {
err.span_suggestion(
item.span(),
"use parentheses instead",
format!("align({})", s),
Applicability::MachineApplicable,
);
}
_ => {}
}
err.emit();
}
}
}
@ -1007,13 +1010,28 @@ pub fn allow_internal_unstable<'a>(
sess: &'a Session,
attrs: &'a [Attribute],
) -> Option<impl Iterator<Item = Symbol> + 'a> {
let attrs = sess.filter_by_name(attrs, sym::allow_internal_unstable);
allow_unstable(sess, attrs, sym::allow_internal_unstable)
}
pub fn rustc_allow_const_fn_unstable<'a>(
sess: &'a Session,
attrs: &'a [Attribute],
) -> Option<impl Iterator<Item = Symbol> + 'a> {
allow_unstable(sess, attrs, sym::rustc_allow_const_fn_unstable)
}
fn allow_unstable<'a>(
sess: &'a Session,
attrs: &'a [Attribute],
symbol: Symbol,
) -> Option<impl Iterator<Item = Symbol> + 'a> {
let attrs = sess.filter_by_name(attrs, symbol);
let list = attrs
.filter_map(move |attr| {
attr.meta_item_list().or_else(|| {
sess.diagnostic().span_err(
attr.span,
"`allow_internal_unstable` expects a list of feature names",
&format!("`{}` expects a list of feature names", symbol.to_ident_string()),
);
None
})
@ -1023,8 +1041,10 @@ pub fn allow_internal_unstable<'a>(
Some(list.into_iter().filter_map(move |it| {
let name = it.ident().map(|ident| ident.name);
if name.is_none() {
sess.diagnostic()
.span_err(it.span(), "`allow_internal_unstable` expects feature names");
sess.diagnostic().span_err(
it.span(),
&format!("`{}` expects feature names", symbol.to_ident_string()),
);
}
name
}))

View File

@ -81,7 +81,7 @@ fn parse_args<'a>(
} // accept trailing commas
// Parse options
if p.eat(&token::Ident(sym::options, false)) {
if p.eat_keyword(sym::options) {
parse_options(&mut p, &mut args)?;
allow_templates = false;
continue;
@ -101,19 +101,19 @@ fn parse_args<'a>(
};
let mut explicit_reg = false;
let op = if p.eat(&token::Ident(kw::In, false)) {
let op = if p.eat_keyword(kw::In) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if p.eat(&token::Ident(sym::out, false)) {
} else if p.eat_keyword(sym::out) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if p.eat(&token::Ident(sym::lateout, false)) {
} else if p.eat_keyword(sym::lateout) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = if p.eat_keyword(kw::Underscore) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if p.eat(&token::Ident(sym::inout, false)) {
} else if p.eat_keyword(sym::inout) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?;
if p.eat(&token::FatArrow) {
@ -123,7 +123,7 @@ fn parse_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if p.eat(&token::Ident(sym::inlateout, false)) {
} else if p.eat_keyword(sym::inlateout) {
let reg = parse_reg(&mut p, &mut explicit_reg)?;
let expr = p.parse_expr()?;
if p.eat(&token::FatArrow) {
@ -133,10 +133,10 @@ fn parse_args<'a>(
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if p.eat(&token::Ident(kw::Const, false)) {
} else if p.eat_keyword(kw::Const) {
let expr = p.parse_expr()?;
ast::InlineAsmOperand::Const { expr }
} else if p.eat(&token::Ident(sym::sym, false)) {
} else if p.eat_keyword(sym::sym) {
let expr = p.parse_expr()?;
match expr.kind {
ast::ExprKind::Path(..) => {}
@ -164,7 +164,7 @@ fn parse_args<'a>(
args.templates.push(template);
continue;
} else {
return Err(p.expect_one_of(&[], &[]).unwrap_err());
return p.unexpected();
};
allow_templates = false;
@ -333,21 +333,22 @@ fn parse_options<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> Result<(), Diagn
p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
if p.eat(&token::Ident(sym::pure, false)) {
if p.eat_keyword(sym::pure) {
try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
} else if p.eat(&token::Ident(sym::nomem, false)) {
} else if p.eat_keyword(sym::nomem) {
try_set_option(p, args, sym::nomem, ast::InlineAsmOptions::NOMEM);
} else if p.eat(&token::Ident(sym::readonly, false)) {
} else if p.eat_keyword(sym::readonly) {
try_set_option(p, args, sym::readonly, ast::InlineAsmOptions::READONLY);
} else if p.eat(&token::Ident(sym::preserves_flags, false)) {
} else if p.eat_keyword(sym::preserves_flags) {
try_set_option(p, args, sym::preserves_flags, ast::InlineAsmOptions::PRESERVES_FLAGS);
} else if p.eat(&token::Ident(sym::noreturn, false)) {
} else if p.eat_keyword(sym::noreturn) {
try_set_option(p, args, sym::noreturn, ast::InlineAsmOptions::NORETURN);
} else if p.eat(&token::Ident(sym::nostack, false)) {
} else if p.eat_keyword(sym::nostack) {
try_set_option(p, args, sym::nostack, ast::InlineAsmOptions::NOSTACK);
} else {
p.expect(&token::Ident(sym::att_syntax, false))?;
} else if p.eat_keyword(sym::att_syntax) {
try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
} else {
return p.unexpected();
}
// Allow trailing commas

View File

@ -120,8 +120,7 @@ fn parse_assert<'a>(
};
if parser.token != token::Eof {
parser.expect_one_of(&[], &[])?;
unreachable!();
return parser.unexpected();
}
Ok(Assert { cond_expr, custom_message })

View File

@ -15,7 +15,7 @@ pub fn inject(mut krate: ast::Crate, parse_sess: &ParseSess, attrs: &[String]) -
);
let start_span = parser.token.span;
let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item() {
let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) {
Ok(ai) => ai,
Err(mut err) => {
err.emit();

View File

@ -358,7 +358,7 @@ fn find_type_parameters(
visit::walk_ty(self, ty)
}
fn visit_mac(&mut self, mac: &ast::MacCall) {
fn visit_mac_call(&mut self, mac: &ast::MacCall) {
self.cx.span_err(mac.span(), "`derive` cannot be used on items with type macros");
}
}
@ -1137,12 +1137,9 @@ impl<'a> MethodDef<'a> {
/// for each of the self-args, carried in precomputed variables.
/// ```{.text}
/// let __self0_vi = unsafe {
/// std::intrinsics::discriminant_value(&self) };
/// let __self1_vi = unsafe {
/// std::intrinsics::discriminant_value(&arg1) };
/// let __self2_vi = unsafe {
/// std::intrinsics::discriminant_value(&arg2) };
/// let __self0_vi = std::intrinsics::discriminant_value(&self);
/// let __self1_vi = std::intrinsics::discriminant_value(&arg1);
/// let __self2_vi = std::intrinsics::discriminant_value(&arg2);
///
/// if __self0_vi == __self1_vi && __self0_vi == __self2_vi && ... {
/// match (...) {
@ -1325,7 +1322,7 @@ impl<'a> MethodDef<'a> {
// Since we know that all the arguments will match if we reach
// the match expression we add the unreachable intrinsics as the
// result of the catch all which should help llvm in optimizing it
Some(deriving::call_intrinsic(cx, sp, sym::unreachable, vec![]))
Some(deriving::call_unreachable(cx, sp))
}
_ => None,
};
@ -1356,12 +1353,9 @@ impl<'a> MethodDef<'a> {
// with three Self args, builds three statements:
//
// ```
// let __self0_vi = unsafe {
// std::intrinsics::discriminant_value(&self) };
// let __self1_vi = unsafe {
// std::intrinsics::discriminant_value(&arg1) };
// let __self2_vi = unsafe {
// std::intrinsics::discriminant_value(&arg2) };
// let __self0_vi = std::intrinsics::discriminant_value(&self);
// let __self1_vi = std::intrinsics::discriminant_value(&arg1);
// let __self2_vi = std::intrinsics::discriminant_value(&arg2);
// ```
let mut index_let_stmts: Vec<ast::Stmt> = Vec::with_capacity(vi_idents.len() + 1);
@ -1474,7 +1468,7 @@ impl<'a> MethodDef<'a> {
// derive Debug on such a type could here generate code
// that needs the feature gate enabled.)
deriving::call_intrinsic(cx, sp, sym::unreachable, vec![])
deriving::call_unreachable(cx, sp)
} else {
// Final wrinkle: the self_args are expressions that deref
// down to desired places, but we cannot actually deref

View File

@ -68,7 +68,14 @@ fn call_intrinsic(
) -> P<ast::Expr> {
let span = cx.with_def_site_ctxt(span);
let path = cx.std_path(&[sym::intrinsics, intrinsic]);
let call = cx.expr_call_global(span, path, args);
cx.expr_call_global(span, path, args)
}
/// Constructs an expression that calls the `unreachable` intrinsic.
fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
let span = cx.with_def_site_ctxt(span);
let path = cx.std_path(&[sym::intrinsics, sym::unreachable]);
let call = cx.expr_call_global(span, path, vec![]);
cx.expr_block(P(ast::Block {
stmts: vec![cx.stmt_expr(call)],

View File

@ -385,7 +385,7 @@ pub mod printf {
if let Start = state {
match c {
'1'..='9' => {
let end = at_next_cp_while(next, is_digit);
let end = at_next_cp_while(next, char::is_ascii_digit);
match end.next_cp() {
// Yes, this *is* the parameter.
Some(('$', end2)) => {
@ -427,7 +427,7 @@ pub mod printf {
move_to!(next);
}
'1'..='9' => {
let end = at_next_cp_while(next, is_digit);
let end = at_next_cp_while(next, char::is_ascii_digit);
state = Prec;
width = Some(Num::from_str(at.slice_between(end).unwrap(), None));
move_to!(end);
@ -441,7 +441,7 @@ pub mod printf {
}
if let WidthArg = state {
let end = at_next_cp_while(at, is_digit);
let end = at_next_cp_while(at, char::is_ascii_digit);
match end.next_cp() {
Some(('$', end2)) => {
state = Prec;
@ -473,7 +473,7 @@ pub mod printf {
if let PrecInner = state {
match c {
'*' => {
let end = at_next_cp_while(next, is_digit);
let end = at_next_cp_while(next, char::is_ascii_digit);
match end.next_cp() {
Some(('$', end2)) => {
state = Length;
@ -488,7 +488,7 @@ pub mod printf {
}
}
'0'..='9' => {
let end = at_next_cp_while(next, is_digit);
let end = at_next_cp_while(next, char::is_ascii_digit);
state = Length;
precision = Some(Num::from_str(at.slice_between(end).unwrap(), None));
move_to!(end);
@ -563,12 +563,12 @@ pub mod printf {
fn at_next_cp_while<F>(mut cur: Cur<'_>, mut pred: F) -> Cur<'_>
where
F: FnMut(char) -> bool,
F: FnMut(&char) -> bool,
{
loop {
match cur.next_cp() {
Some((c, next)) => {
if pred(c) {
if pred(&c) {
cur = next;
} else {
return cur;
@ -579,14 +579,7 @@ pub mod printf {
}
}
fn is_digit(c: char) -> bool {
match c {
'0'..='9' => true,
_ => false,
}
}
fn is_flag(c: char) -> bool {
fn is_flag(c: &char) -> bool {
match c {
'0' | '-' | '+' | ' ' | '#' | '\'' => true,
_ => false,
@ -723,17 +716,11 @@ pub mod shell {
}
fn is_ident_head(c: char) -> bool {
match c {
'a'..='z' | 'A'..='Z' | '_' => true,
_ => false,
}
c.is_ascii_alphabetic() || c == '_'
}
fn is_ident_tail(c: char) -> bool {
match c {
'0'..='9' => true,
c => is_ident_head(c),
}
c.is_ascii_alphanumeric() || c == '_'
}
#[cfg(test)]

View File

@ -344,10 +344,6 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
visit::walk_item(self, item);
self.in_root = prev_in_root;
}
fn visit_mac(&mut self, mac: &'a ast::MacCall) {
visit::walk_mac(self, mac)
}
}
// Creates a new module which looks like:

View File

@ -13,8 +13,6 @@ use rustc_span::{self, Pos, Span};
use smallvec::SmallVec;
use std::rc::Rc;
use rustc_data_structures::sync::Lrc;
// These macros all relate to the file system; they either return
// the column/row/filename of the expression, or they include
// a given file into the current one.
@ -216,7 +214,7 @@ pub fn expand_include_bytes(
}
};
match cx.source_map().load_binary_file(&file) {
Ok(bytes) => base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(Lrc::new(bytes)))),
Ok(bytes) => base::MacEager::expr(cx.expr_lit(sp, ast::LitKind::ByteStr(bytes.into()))),
Err(e) => {
cx.span_err(sp, &format!("couldn't read {}: {}", file.display(), e));
DummyResult::any(sp)

View File

@ -37,7 +37,7 @@ struct TestCtxt<'a> {
pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) {
let span_diagnostic = sess.diagnostic();
let panic_strategy = sess.panic_strategy();
let platform_panic_strategy = sess.target.target.options.panic_strategy;
let platform_panic_strategy = sess.target.panic_strategy;
// Check for #![reexport_test_harness_main = "some_name"] which gives the
// main test function the name `some_name` without hygiene. This needs to be
@ -130,10 +130,6 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
}
smallvec![P(item)]
}
fn visit_mac(&mut self, _mac: &mut ast::MacCall) {
// Do nothing.
}
}
// Beware, this is duplicated in librustc_passes/entry.rs (with
@ -201,10 +197,6 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
smallvec![item]
}
fn visit_mac(&mut self, _mac: &mut ast::MacCall) {
// Do nothing.
}
}
/// Crawl over the crate, inserting test reexports and the test main function
@ -290,7 +282,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
let mut test_runner = cx
.test_runner
.clone()
.unwrap_or(ecx.path(sp, vec![test_id, Ident::from_str_and_span(runner_name, sp)]));
.unwrap_or_else(|| ecx.path(sp, vec![test_id, Ident::from_str_and_span(runner_name, sp)]));
test_runner.span = sp;

View File

@ -0,0 +1,44 @@
name: Bootstrap rustc using cg_clif
on:
- push
jobs:
bootstrap_rustc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache cargo installed crates
uses: actions/cache@v2
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-cargo-installed-crates
- name: Cache cargo registry and index
uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo target dir
uses: actions/cache@v2
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
- name: Prepare dependencies
run: |
git config --global user.email "user@example.com"
git config --global user.name "User"
./prepare.sh
- name: Test
run: |
# Enable backtraces for easier debugging
export RUST_BACKTRACE=1
./scripts/test_bootstrap.sh

View File

@ -0,0 +1,63 @@
name: CI
on:
- push
- pull_request
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- uses: actions/checkout@v2
- name: Cache cargo installed crates
uses: actions/cache@v2
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-cargo-installed-crates
- name: Cache cargo registry and index
uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-registry-and-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo target dir
uses: actions/cache@v2
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('rust-toolchain', '**/Cargo.lock') }}
- name: Prepare dependencies
run: |
git config --global user.email "user@example.com"
git config --global user.name "User"
./prepare.sh
- name: Test
run: |
# Enable backtraces for easier debugging
export RUST_BACKTRACE=1
# Reduce amount of benchmark runs as they are slow
export COMPILE_RUNS=2
export RUN_RUNS=2
./test.sh
- name: Package prebuilt cg_clif
run: tar cvfJ cg_clif.tar.xz build
- name: Upload prebuilt cg_clif
uses: actions/upload-artifact@v2
with:
name: cg_clif-${{ runner.os }}
path: cg_clif.tar.xz

View File

@ -0,0 +1,53 @@
{
// source for rustc_* is not included in the rust-src component; disable the errors about this
"rust-analyzer.diagnostics.disabled": ["unresolved-extern-crate"],
"rust-analyzer.cargo.loadOutDirsFromCheck": true,
"rust-analyzer.linkedProjects": [
"./Cargo.toml",
//"./build_sysroot/sysroot_src/src/libstd/Cargo.toml",
{
"roots": [
"./example/mini_core.rs",
"./example/mini_core_hello_world.rs",
"./example/mod_bench.rs"
],
"crates": [
{
"root_module": "./example/mini_core.rs",
"edition": "2018",
"deps": [],
"cfg": [],
},
{
"root_module": "./example/mini_core_hello_world.rs",
"edition": "2018",
"deps": [{ "crate": 0, "name": "mini_core" }],
"cfg": [],
},
{
"root_module": "./example/mod_bench.rs",
"edition": "2018",
"deps": [],
"cfg": [],
},
]
},
{
"roots": ["./scripts/filter_profile.rs"],
"crates": [
{
"root_module": "./scripts/filter_profile.rs",
"edition": "2018",
"deps": [{ "crate": 1, "name": "std" }],
"cfg": [],
},
{
"root_module": "./build_sysroot/sysroot_src/library/std/src/lib.rs",
"edition": "2018",
"deps": [],
"cfg": [],
},
]
}
]
}

View File

@ -0,0 +1,425 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "anyhow"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
[[package]]
name = "ar"
version = "0.8.0"
source = "git+https://github.com/bjorn3/rust-ar.git?branch=do_not_remove_cg_clif_ranlib#de9ab0e56bf3a208381d342aa5b60f9ff2891648"
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "byteorder"
version = "1.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
[[package]]
name = "cc"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cranelift-bforest"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"byteorder",
"cranelift-bforest",
"cranelift-codegen-meta",
"cranelift-codegen-shared",
"cranelift-entity",
"gimli",
"log",
"regalloc",
"smallvec",
"target-lexicon",
"thiserror",
]
[[package]]
name = "cranelift-codegen-meta"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"cranelift-codegen-shared",
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen-shared"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
[[package]]
name = "cranelift-entity"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
[[package]]
name = "cranelift-frontend"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"cranelift-codegen",
"log",
"smallvec",
"target-lexicon",
]
[[package]]
name = "cranelift-module"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-entity",
"log",
"thiserror",
]
[[package]]
name = "cranelift-native"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"cranelift-codegen",
"raw-cpuid",
"target-lexicon",
]
[[package]]
name = "cranelift-object"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"anyhow",
"cranelift-codegen",
"cranelift-module",
"log",
"object",
"target-lexicon",
]
[[package]]
name = "cranelift-simplejit"
version = "0.67.0"
source = "git+https://github.com/bytecodealliance/wasmtime/?branch=main#44cbdecea03c360ea82e6482f0cf6c614effef21"
dependencies = [
"cranelift-codegen",
"cranelift-entity",
"cranelift-module",
"cranelift-native",
"errno",
"libc",
"log",
"region",
"target-lexicon",
"winapi",
]
[[package]]
name = "crc32fast"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
dependencies = [
"cfg-if",
]
[[package]]
name = "errno"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067"
dependencies = [
"gcc",
"libc",
]
[[package]]
name = "gcc"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
[[package]]
name = "gimli"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
dependencies = [
"indexmap",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "indexmap"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "libc"
version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2448f6066e80e3bfc792e9c98bf705b4b0fc6e8ef5b43e5889aff0eaa9c58743"
[[package]]
name = "libloading"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3557c9384f7f757f6d139cd3a4c62ef4e850696c16bf27924a5538c8a09717a1"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if",
]
[[package]]
name = "mach"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
dependencies = [
"crc32fast",
"indexmap",
]
[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
[[package]]
name = "raw-cpuid"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf"
dependencies = [
"bitflags",
"cc",
"rustc_version",
]
[[package]]
name = "regalloc"
version = "0.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5"
dependencies = [
"log",
"rustc-hash",
"smallvec",
]
[[package]]
name = "region"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877e54ea2adcd70d80e9179344c97f93ef0dffd6b03e1f4529e6e83ab2fa9ae0"
dependencies = [
"bitflags",
"libc",
"mach",
"winapi",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_codegen_cranelift"
version = "0.1.0"
dependencies = [
"ar",
"cranelift-codegen",
"cranelift-frontend",
"cranelift-module",
"cranelift-object",
"cranelift-simplejit",
"gimli",
"indexmap",
"libloading",
"object",
"target-lexicon",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "smallvec"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
[[package]]
name = "syn"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e03e57e4fcbfe7749842d53e24ccb9aa12b7252dbe5e91d2acad31834c8b8fdd"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "target-lexicon"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe2635952a442a01fd4cb53d98858b5e4bb461b02c0d111f22f31772e3e7a8b2"
[[package]]
name = "thiserror"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View File

@ -0,0 +1,76 @@
[package]
name = "rustc_codegen_cranelift"
version = "0.1.0"
authors = ["bjorn3 <bjorn3@users.noreply.github.com>"]
edition = "2018"
[lib]
crate-type = ["dylib"]
[dependencies]
# These have to be in sync with each other
cranelift-codegen = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", features = ["unwind"] }
cranelift-frontend = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" }
cranelift-module = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" }
cranelift-simplejit = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main", optional = true }
cranelift-object = { git = "https://github.com/bytecodealliance/wasmtime/", branch = "main" }
target-lexicon = "0.11.0"
gimli = { version = "0.22.0", default-features = false, features = ["write"]}
object = { version = "0.21.1", default-features = false, features = ["std", "read_core", "write", "coff", "elf", "macho", "pe"] }
ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" }
indexmap = "1.0.2"
libloading = { version = "0.6.0", optional = true }
# Uncomment to use local checkout of cranelift
#[patch."https://github.com/bytecodealliance/wasmtime/"]
#cranelift-codegen = { path = "../wasmtime/cranelift/codegen" }
#cranelift-frontend = { path = "../wasmtime/cranelift/frontend" }
#cranelift-module = { path = "../wasmtime/cranelift/module" }
#cranelift-simplejit = { path = "../wasmtime/cranelift/simplejit" }
#cranelift-object = { path = "../wasmtime/cranelift/object" }
#[patch.crates-io]
#gimli = { path = "../" }
[features]
default = ["jit", "inline_asm"]
jit = ["cranelift-simplejit", "libloading"]
inline_asm = []
[profile.dev]
# By compiling dependencies with optimizations, performing tests gets much faster.
opt-level = 3
[profile.dev.package.rustc_codegen_cranelift]
# Disabling optimizations for cg_clif itself makes compilation after a change faster.
opt-level = 0
[profile.release.package.rustc_codegen_cranelift]
incremental = true
# Disable optimizations and debuginfo of build scripts and some of the heavy build deps, as the
# execution time of build scripts is so fast that optimizing them slows down the total build time.
[profile.dev.build-override]
opt-level = 0
debug = false
[profile.release.build-override]
opt-level = 0
debug = false
[profile.dev.package.cranelift-codegen-meta]
opt-level = 0
debug = false
[profile.release.package.cranelift-codegen-meta]
opt-level = 0
debug = false
[profile.dev.package.syn]
opt-level = 0
debug = false
[profile.release.package.syn]
opt-level = 0
debug = false

View File

@ -174,3 +174,28 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,103 @@
# WIP Cranelift codegen backend for rust
> ⚠⚠⚠ Certain kinds of FFI don't work yet. ⚠⚠⚠
The goal of this project is to create an alternative codegen backend for the rust compiler based on [Cranelift](https://github.com/bytecodealliance/wasmtime/blob/master/cranelift).
This has the potential to improve compilation times in debug mode.
If your project doesn't use any of the things listed under "Not yet supported", it should work fine.
If not please open an issue.
## Building and testing
```bash
$ git clone https://github.com/bjorn3/rustc_codegen_cranelift.git
$ cd rustc_codegen_cranelift
$ ./prepare.sh # download and patch sysroot src and install hyperfine for benchmarking
$ ./build.sh
```
To run the test suite replace the last command with:
```bash
$ ./test.sh
```
This will implicitly build cg_clif too. Both `build.sh` and `test.sh` accept a `--debug` argument to
build in debug mode.
Alternatively you can download a pre built version from [GHA]. It is listed in the artifacts section
of workflow runs. Unfortunately due to GHA restrictions you need to be logged in to access it.
[GHA]: https://github.com/bjorn3/rustc_codegen_cranelift/actions?query=branch%3Amaster+event%3Apush+is%3Asuccess
## Usage
rustc_codegen_cranelift can be used as a near-drop-in replacement for `cargo build` or `cargo run` for existing projects.
Assuming `$cg_clif_dir` is the directory you cloned this repo into and you followed the instructions (`prepare.sh` and `build.sh` or `test.sh`).
### Cargo
In the directory with your project (where you can do the usual `cargo build`), run:
```bash
$ $cg_clif_dir/build/cargo.sh run
```
This should build and run your project with rustc_codegen_cranelift instead of the usual LLVM backend.
### Rustc
> You should prefer using the Cargo method.
```bash
$ $cg_clif_dir/build/cg_clif my_crate.rs
```
### Jit mode
In jit mode cg_clif will immediately execute your code without creating an executable file.
> This requires all dependencies to be available as dynamic library.
> The jit mode will probably need cargo integration to make this possible.
```bash
$ $cg_clif_dir/build/cargo.sh jit
```
or
```bash
$ $cg_clif_dir/build/cg_clif --jit my_crate.rs
```
### Shell
These are a few functions that allow you to easily run rust code from the shell using cg_clif as jit.
```bash
function jit_naked() {
echo "$@" | $cg_clif_dir/build/cg_clif - --jit
}
function jit() {
jit_naked "fn main() { $@ }"
}
function jit_calc() {
jit 'println!("0x{:x}", ' $@ ');';
}
```
## Env vars
[see env_vars.md](docs/env_vars.md)
## Not yet supported
* Good non-rust abi support ([several problems](https://github.com/bjorn3/rustc_codegen_cranelift/issues/10))
* Inline assembly ([no cranelift support](https://github.com/bytecodealliance/wasmtime/issues/1041)
* On Linux there is support for invoking an external assembler for `global_asm!` and `asm!`.
`llvm_asm!` will remain unimplemented forever. `asm!` doesn't yet support reg classes. You
have to specify specific registers instead.
* SIMD ([tracked here](https://github.com/bjorn3/rustc_codegen_cranelift/issues/171), some basic things work)

View File

@ -0,0 +1,47 @@
#!/bin/bash
set -e
# Settings
export CHANNEL="release"
build_sysroot=1
target_dir='build'
while [[ $# != 0 ]]; do
case $1 in
"--debug")
export CHANNEL="debug"
;;
"--without-sysroot")
build_sysroot=0
;;
"--target-dir")
target_dir=$2
shift
;;
*)
echo "Unknown flag '$1'"
echo "Usage: ./build.sh [--debug] [--without-sysroot] [--target-dir DIR]"
;;
esac
shift
done
# Build cg_clif
export RUSTFLAGS="-Zrun_dsymutil=no"
if [[ "$CHANNEL" == "release" ]]; then
cargo build --release
else
cargo build
fi
rm -rf $target_dir
mkdir $target_dir
cp -a target/$CHANNEL/cg_clif{,_build_sysroot} target/$CHANNEL/*rustc_codegen_cranelift* $target_dir/
cp -a rust-toolchain scripts/config.sh scripts/cargo.sh $target_dir
if [[ "$build_sysroot" == "1" ]]; then
echo "[BUILD] sysroot"
export CG_CLIF_INCR_CACHE_DISABLED=1
dir=$(pwd)
cd $target_dir
time $dir/build_sysroot/build_sysroot.sh
fi

View File

@ -0,0 +1,324 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "addr2line"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"compiler_builtins",
"gimli",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "adler"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "alloc"
version = "0.0.0"
dependencies = [
"compiler_builtins",
"core",
]
[[package]]
name = "alloc_system"
version = "0.0.0"
dependencies = [
"compiler_builtins",
"core",
"libc",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "cc"
version = "1.0.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "compiler_builtins"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd0782e0a7da7598164153173e5a5d4d9b1da094473c98dce0ff91406112369"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "core"
version = "0.0.0"
[[package]]
name = "dlmalloc"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35055b1021724f4eb5262eb49130eebff23fc59fc5a14160e05faad8eeb36673"
dependencies = [
"compiler_builtins",
"libc",
"rustc-std-workspace-core",
]
[[package]]
name = "fortanix-sgx-abi"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c56c422ef86062869b2d57ae87270608dc5929969dd130a6e248979cf4fb6ca6"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"rustc-std-workspace-core",
"rustc-std-workspace-std",
"unicode-width",
]
[[package]]
name = "gimli"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "hermit-abi"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
dependencies = [
"compiler_builtins",
"libc",
"rustc-std-workspace-core",
]
[[package]]
name = "libc"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "miniz_oxide"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f2d26ec3309788e423cfbf68ad1800f061638098d76a83681af979dc4eda19d"
dependencies = [
"adler",
"autocfg",
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "object"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]
[[package]]
name = "panic_abort"
version = "0.0.0"
dependencies = [
"cfg-if",
"compiler_builtins",
"core",
"libc",
]
[[package]]
name = "panic_unwind"
version = "0.0.0"
dependencies = [
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"libc",
"unwind",
]
[[package]]
name = "proc_macro"
version = "0.0.0"
dependencies = [
"std",
]
[[package]]
name = "rustc-demangle"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e3bad0ee36814ca07d7968269dd4b7ec89ec2da10c4bb613928d3077083c232"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
]
[[package]]
name = "rustc-std-workspace-alloc"
version = "1.99.0"
dependencies = [
"alloc",
]
[[package]]
name = "rustc-std-workspace-core"
version = "1.99.0"
dependencies = [
"core",
]
[[package]]
name = "rustc-std-workspace-std"
version = "1.99.0"
dependencies = [
"std",
]
[[package]]
name = "std"
version = "0.0.0"
dependencies = [
"addr2line",
"alloc",
"cfg-if",
"compiler_builtins",
"core",
"dlmalloc",
"fortanix-sgx-abi",
"hashbrown",
"hermit-abi",
"libc",
"miniz_oxide",
"object",
"panic_abort",
"panic_unwind",
"rustc-demangle",
"unwind",
"wasi",
]
[[package]]
name = "sysroot"
version = "0.0.0"
dependencies = [
"alloc",
"alloc_system",
"compiler_builtins",
"core",
"std",
"test",
]
[[package]]
name = "term"
version = "0.0.0"
dependencies = [
"core",
"std",
]
[[package]]
name = "test"
version = "0.0.0"
dependencies = [
"cfg-if",
"core",
"getopts",
"libc",
"panic_abort",
"panic_unwind",
"proc_macro",
"std",
"term",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-core",
"rustc-std-workspace-std",
]
[[package]]
name = "unwind"
version = "0.0.0"
dependencies = [
"cc",
"cfg-if",
"compiler_builtins",
"core",
"libc",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
"rustc-std-workspace-core",
]

View File

@ -0,0 +1,26 @@
[package]
authors = ["bjorn3 <bjorn3@users.noreply.github.com>"]
name = "sysroot"
version = "0.0.0"
[dependencies]
core = { path = "./sysroot_src/library/core" }
compiler_builtins = "0.1"
alloc = { path = "./sysroot_src/library/alloc" }
std = { path = "./sysroot_src/library/std", features = ["panic_unwind", "backtrace"] }
test = { path = "./sysroot_src/library/test" }
alloc_system = { path = "./alloc_system" }
[patch.crates-io]
rustc-std-workspace-core = { path = "./sysroot_src/library/rustc-std-workspace-core" }
rustc-std-workspace-alloc = { path = "./sysroot_src/library/rustc-std-workspace-alloc" }
rustc-std-workspace-std = { path = "./sysroot_src/library/rustc-std-workspace-std" }
[profile.dev]
lto = "off"
[profile.release]
debug = true
incremental = true
lto = "off"

View File

@ -0,0 +1,13 @@
[package]
authors = ["The Rust Project Developers", "bjorn3 (edited to be usable outside the rust source)"]
name = "alloc_system"
version = "0.0.0"
[lib]
name = "alloc_system"
path = "lib.rs"
test = false
doc = false
[dependencies]
core = { path = "../sysroot_src/library/core" }
libc = { version = "0.2.43", features = ['rustc-dep-of-std'], default-features = false }
compiler_builtins = "0.1"

View File

@ -0,0 +1,342 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![no_std]
#![allow(unused_attributes)]
#![unstable(feature = "alloc_system",
reason = "this library is unlikely to be stabilized in its current \
form or name",
issue = "32838")]
#![feature(allocator_api)]
#![feature(core_intrinsics)]
#![feature(nll)]
#![feature(staged_api)]
#![feature(rustc_attrs)]
#![feature(alloc_layout_extra)]
#![cfg_attr(
all(target_arch = "wasm32", not(target_os = "emscripten")),
feature(integer_atomics, stdsimd)
)]
#![cfg_attr(any(unix, target_os = "cloudabi", target_os = "redox"), feature(libc))]
// The minimum alignment guaranteed by the architecture. This value is used to
// add fast paths for low alignment values.
#[cfg(all(any(target_arch = "x86",
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "powerpc64",
target_arch = "asmjs",
target_arch = "wasm32")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 8;
#[cfg(all(any(target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "mips64",
target_arch = "s390x",
target_arch = "sparc64")))]
#[allow(dead_code)]
const MIN_ALIGN: usize = 16;
/// The default memory allocator provided by the operating system.
///
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
/// plus related functions.
///
/// This type can be used in a `static` item
/// with the `#[global_allocator]` attribute
/// to force the global allocator to be the systems one.
/// (The default is jemalloc for executables, on some platforms.)
///
/// ```rust
/// use std::alloc::System;
///
/// #[global_allocator]
/// static A: System = System;
///
/// fn main() {
/// let a = Box::new(4); // Allocates from the system allocator.
/// println!("{}", a);
/// }
/// ```
///
/// It can also be used directly to allocate memory
/// independently of the standard librarys global allocator.
#[stable(feature = "alloc_system_type", since = "1.28.0")]
pub struct System;
#[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))]
mod realloc_fallback {
use core::alloc::{GlobalAlloc, Layout};
use core::cmp;
use core::ptr;
impl super::System {
pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout,
new_size: usize) -> *mut u8 {
// Docs for GlobalAlloc::realloc require this to be valid:
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
let new_ptr = GlobalAlloc::alloc(self, new_layout);
if !new_ptr.is_null() {
let size = cmp::min(old_layout.size(), new_size);
ptr::copy_nonoverlapping(ptr, new_ptr, size);
GlobalAlloc::dealloc(self, ptr, old_layout);
}
new_ptr
}
}
}
#[cfg(any(unix, target_os = "cloudabi", target_os = "redox"))]
mod platform {
extern crate libc;
use core::ptr;
use MIN_ALIGN;
use System;
use core::alloc::{GlobalAlloc, Layout};
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
libc::malloc(layout.size()) as *mut u8
} else {
#[cfg(target_os = "macos")]
{
if layout.align() > (1 << 31) {
return ptr::null_mut()
}
}
aligned_malloc(&layout)
}
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
libc::calloc(layout.size(), 1) as *mut u8
} else {
let ptr = self.alloc(layout.clone());
if !ptr.is_null() {
ptr::write_bytes(ptr, 0, layout.size());
}
ptr
}
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
libc::free(ptr as *mut libc::c_void)
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
} else {
self.realloc_fallback(ptr, layout, new_size)
}
}
}
#[cfg(any(target_os = "android",
target_os = "hermit",
target_os = "redox",
target_os = "solaris"))]
#[inline]
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
// On android we currently target API level 9 which unfortunately
// doesn't have the `posix_memalign` API used below. Instead we use
// `memalign`, but this unfortunately has the property on some systems
// where the memory returned cannot be deallocated by `free`!
//
// Upon closer inspection, however, this appears to work just fine with
// Android, so for this platform we should be fine to call `memalign`
// (which is present in API level 9). Some helpful references could
// possibly be chromium using memalign [1], attempts at documenting that
// memalign + free is ok [2] [3], or the current source of chromium
// which still uses memalign on android [4].
//
// [1]: https://codereview.chromium.org/10796020/
// [2]: https://code.google.com/p/android/issues/detail?id=35391
// [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579
// [4]: https://chromium.googlesource.com/chromium/src/base/+/master/
// /memory/aligned_memory.cc
libc::memalign(layout.align(), layout.size()) as *mut u8
}
#[cfg(not(any(target_os = "android",
target_os = "hermit",
target_os = "redox",
target_os = "solaris")))]
#[inline]
unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 {
let mut out = ptr::null_mut();
let ret = libc::posix_memalign(&mut out, layout.align(), layout.size());
if ret != 0 {
ptr::null_mut()
} else {
out as *mut u8
}
}
}
#[cfg(windows)]
#[allow(nonstandard_style)]
mod platform {
use MIN_ALIGN;
use System;
use core::alloc::{GlobalAlloc, Layout};
type LPVOID = *mut u8;
type HANDLE = LPVOID;
type SIZE_T = usize;
type DWORD = u32;
type BOOL = i32;
extern "system" {
fn GetProcessHeap() -> HANDLE;
fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) -> LPVOID;
fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID, dwBytes: SIZE_T) -> LPVOID;
fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: LPVOID) -> BOOL;
fn GetLastError() -> DWORD;
}
#[repr(C)]
struct Header(*mut u8);
const HEAP_ZERO_MEMORY: DWORD = 0x00000008;
unsafe fn get_header<'a>(ptr: *mut u8) -> &'a mut Header {
&mut *(ptr as *mut Header).offset(-1)
}
unsafe fn align_ptr(ptr: *mut u8, align: usize) -> *mut u8 {
let aligned = ptr.add(align - (ptr as usize & (align - 1)));
*get_header(aligned) = Header(ptr);
aligned
}
#[inline]
unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 {
let ptr = if layout.align() <= MIN_ALIGN {
HeapAlloc(GetProcessHeap(), flags, layout.size())
} else {
let size = layout.size() + layout.align();
let ptr = HeapAlloc(GetProcessHeap(), flags, size);
if ptr.is_null() {
ptr
} else {
align_ptr(ptr, layout.align())
}
};
ptr as *mut u8
}
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
allocate_with_flags(layout, 0)
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
allocate_with_flags(layout, HEAP_ZERO_MEMORY)
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
if layout.align() <= MIN_ALIGN {
let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID);
debug_assert!(err != 0, "Failed to free heap memory: {}",
GetLastError());
} else {
let header = get_header(ptr);
let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID);
debug_assert!(err != 0, "Failed to free heap memory: {}",
GetLastError());
}
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= MIN_ALIGN {
HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8
} else {
self.realloc_fallback(ptr, layout, new_size)
}
}
}
}
// This is an implementation of a global allocator on the wasm32 platform when
// emscripten is not in use. In that situation there's no actual runtime for us
// to lean on for allocation, so instead we provide our own!
//
// The wasm32 instruction set has two instructions for getting the current
// amount of memory and growing the amount of memory. These instructions are the
// foundation on which we're able to build an allocator, so we do so! Note that
// the instructions are also pretty "global" and this is the "global" allocator
// after all!
//
// The current allocator here is the `dlmalloc` crate which we've got included
// in the rust-lang/rust repository as a submodule. The crate is a port of
// dlmalloc.c from C to Rust and is basically just so we can have "pure Rust"
// for now which is currently technically required (can't link with C yet).
//
// The crate itself provides a global allocator which on wasm has no
// synchronization as there are no threads!
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
mod platform {
extern crate dlmalloc;
use core::alloc::{GlobalAlloc, Layout};
use System;
static mut DLMALLOC: dlmalloc::Dlmalloc = dlmalloc::DLMALLOC_INIT;
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let _lock = lock::lock();
DLMALLOC.malloc(layout.size(), layout.align())
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let _lock = lock::lock();
DLMALLOC.calloc(layout.size(), layout.align())
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let _lock = lock::lock();
DLMALLOC.free(ptr, layout.size(), layout.align())
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
let _lock = lock::lock();
DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size)
}
}
#[cfg(target_feature = "atomics")]
mod lock {
use core::arch::wasm32;
use core::sync::atomic::{AtomicI32, Ordering::SeqCst};
static LOCKED: AtomicI32 = AtomicI32::new(0);
pub struct DropLock;
pub fn lock() -> DropLock {
loop {
if LOCKED.swap(1, SeqCst) == 0 {
return DropLock
}
unsafe {
let r = wasm32::atomic::wait_i32(
&LOCKED as *const AtomicI32 as *mut i32,
1, // expected value
-1, // timeout
);
debug_assert!(r == 0 || r == 1);
}
}
}
impl Drop for DropLock {
fn drop(&mut self) {
let r = LOCKED.swap(0, SeqCst);
debug_assert_eq!(r, 1);
unsafe {
wasm32::atomic::wake(
&LOCKED as *const AtomicI32 as *mut i32,
1, // only one thread
);
}
}
}
}
#[cfg(not(target_feature = "atomics"))]
mod lock {
#[inline]
pub fn lock() {} // no atomics, no threads, that's easy!
}
}

View File

@ -0,0 +1,39 @@
#!/bin/bash
# Requires the CHANNEL env var to be set to `debug` or `release.`
set -e
source ./config.sh
dir=$(pwd)
# Use rustc with cg_clif as hotpluggable backend instead of the custom cg_clif driver so that
# build scripts are still compiled using cg_llvm.
export RUSTC=$dir"/cg_clif_build_sysroot"
export RUSTFLAGS=$RUSTFLAGS" --clif"
cd $(dirname "$0")
# Cleanup for previous run
# v Clean target dir except for build scripts and incremental cache
rm -r target/*/{debug,release}/{build,deps,examples,libsysroot*,native} 2>/dev/null || true
# We expect the target dir in the default location. Guard against the user changing it.
export CARGO_TARGET_DIR=target
# Build libs
export RUSTFLAGS="$RUSTFLAGS -Zforce-unstable-if-unmarked -Cpanic=abort"
if [[ "$1" != "--debug" ]]; then
sysroot_channel='release'
# FIXME Enable incremental again once rust-lang/rust#74946 is fixed
# FIXME Enable -Zmir-opt-level=2 again once it doesn't ice anymore
CARGO_INCREMENTAL=0 RUSTFLAGS="$RUSTFLAGS" cargo build --target $TARGET_TRIPLE --release
else
sysroot_channel='debug'
cargo build --target $TARGET_TRIPLE
fi
# Copy files to sysroot
mkdir -p $dir/sysroot/lib/rustlib/$TARGET_TRIPLE/lib/
cp -a target/$TARGET_TRIPLE/$sysroot_channel/deps/* $dir/sysroot/lib/rustlib/$TARGET_TRIPLE/lib/

View File

@ -0,0 +1,32 @@
#!/bin/bash
set -e
cd $(dirname "$0")
SRC_DIR=$(dirname $(rustup which rustc))"/../lib/rustlib/src/rust/"
DST_DIR="sysroot_src"
if [ ! -e $SRC_DIR ]; then
echo "Please install rust-src component"
exit 1
fi
rm -rf $DST_DIR
mkdir -p $DST_DIR/library
cp -a $SRC_DIR/library $DST_DIR/
pushd $DST_DIR
echo "[GIT] init"
git init
echo "[GIT] add"
git add .
echo "[GIT] commit"
git commit -m "Initial commit" -q
for file in $(ls ../../patches/ | grep -v patcha); do
echo "[GIT] apply" $file
git apply ../../patches/$file
git add -A
git commit --no-gpg-sign -m "Patch $file"
done
popd
echo "Successfully prepared libcore for building"

View File

@ -0,0 +1 @@
#![no_std]

View File

@ -0,0 +1,5 @@
#!/bin/bash --verbose
set -e
rm -rf target/ build/ build_sysroot/{sysroot_src/,target/} perf.data{,.old}
rm -rf rand/ regex/ simple-raytracer/

View File

@ -0,0 +1,23 @@
From 9c5663e36391fa20becf84f3af2e82afa5bb720b Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Sat, 15 Aug 2020 19:56:03 +0200
Subject: [PATCH] [rand] Enable c2-chacha simd feature
---
rand_chacha/Cargo.toml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rand_chacha/Cargo.toml b/rand_chacha/Cargo.toml
index 9190b7f..872cca2 100644
--- a/rand_chacha/Cargo.toml
+++ b/rand_chacha/Cargo.toml
@@ -24,5 +24,5 @@ ppv-lite86 = { version = "0.2.8", default-features = false }
[features]
default = ["std"]
-std = ["ppv-lite86/std"]
+std = ["ppv-lite86/std", "ppv-lite86/simd"]
simd = [] # deprecated
--
2.20.1

View File

@ -0,0 +1,33 @@
From a8fb97120d71252538b6b026695df40d02696bdb Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Sat, 15 Aug 2020 20:04:38 +0200
Subject: [PATCH] [rand] Disable failing test
---
src/distributions/uniform.rs | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/distributions/uniform.rs b/src/distributions/uniform.rs
index 480b859..c80bb6f 100644
--- a/src/distributions/uniform.rs
+++ b/src/distributions/uniform.rs
@@ -1085,7 +1085,7 @@ mod tests {
_ => panic!("`UniformDurationMode` was not serialized/deserialized correctly")
}
}
-
+
#[test]
#[cfg(feature = "serde1")]
fn test_uniform_serialization() {
@@ -1314,6 +1314,7 @@ mod tests {
not(target_arch = "wasm32"),
not(target_arch = "asmjs")
))]
+ #[ignore] // FIXME
fn test_float_assertions() {
use super::SampleUniform;
use std::panic::catch_unwind;
--
2.20.1

View File

@ -0,0 +1,153 @@
# Line number information
Line number information maps between machine code instructions and the source level location.
## Encoding
The line number information is stored in the `.debug_line` section for ELF and `__debug_line`
section of the `__DWARF` segment for Mach-O object files. The line number information contains a
header followed by the line program. The line program is a program for a virtual machine with
instructions like set line number for the current machine code instruction and advance the current
machine code instruction.
## Tips
You need to set either `DW_AT_low_pc` and `DW_AT_high_pc` **or** `DW_AT_ranges` of a
`DW_TAG_compilation_unit` to the range of addresses in the compilation unit. After that you need
to set `DW_AT_stmt_list` to the `.debug_line` section offset of the line program. Otherwise a
debugger won't find the line number information. On macOS the debuginfo relocations **must** be
section relative and not symbol relative.
See [#303 (comment)](https://github.com/bjorn3/rustc_codegen_cranelift/issues/303#issuecomment-457825535)
for more information.
# Function debuginfo
## Tips
`DW_TAG_subprogram` requires `DW_AT_name`, `DW_AT_low_pc` and `DW_AT_high_pc` **or** `DW_AT_ranges`.
Otherwise gdb will silently skip it. When `DW_AT_high_pc` is a length instead of an address, the
DWARF version must be at least 4.
<details>
<summary>IRC log of #gdb on irc.freenode.org at 2020-04-23</summary>
```
(13:46:11) bjorn3: i am writing a backend for a compiler that uses DWARF for debuginfo. for some reason gdb seems to completely ignore all DW_TAG_subprogram, while lldb works fine. any idea what the problem could be?
(13:47:49) bjorn3: this is the output of llvm-dwarfdump: https://gist.github.com/bjorn3/8a34e333c80f13cb048381e94b4a3756
(13:47:50) osa1: luispm: why is that problem not exists in 'commands'? (the target vs. host)
(13:52:16) luispm: osa1, commands is a bit more high level. It executes isolated commands. Breakpoint conditions need to be evaluated in the context of a valid expression. That expression may involve variables, symbols etc.
(13:52:36) luispm: osa1, Oh, i see your point now. Commands is only executed on the host.
(13:53:18) luispm: osa1, The commands are not tied to the execution context of the debugged program. The breakpoint conditions determine if execution must stop or continue etc.
(13:55:00) luispm: bjorn3, Likely something GDB thinks is wrong. Does enabling "set debug dwarf*" show anything?
(13:56:01) bjorn3: luispm: no
(13:56:12) bjorn3: for more context: https://github.com/bjorn3/rustc_codegen_cranelift/pull/978
(13:58:16) osa1 verliet de ruimte (quit: Quit: osa1).
(13:58:28) bjorn3: luispm: wait, for b m<TAB> it shows nothing, but when stepping into a new function it does
(13:58:45) bjorn3: it still doesn't show anything for `info args` though
(13:58:50) bjorn3: No symbol table info available.
(14:00:50) luispm: bjorn3, Is that expected given the nature of the binary?
(14:01:17) bjorn3: b main<TAB> may show nothing as I only set DW_AT_linkage_name and not DW_AT_name
(14:01:24) bjorn3: info args should work though
(14:03:26) luispm: Sorry, I'm not sure what's up. There may be a genuine bug there.
(14:03:41) luispm: tromey (not currently in the channel, but maybe later today) may have more input.
(14:04:08) bjorn3: okay, thanks luispm!
(14:04:27) luispm: In the worst case, reporting a bug may prompt someone to look into that as well.
(14:04:48) luispm: Or send an e-mail to the gdb@sourceware.org mailing list.
(14:05:11) bjorn3: I don't know if it is a bug in gdb, or just me producing (slightly) wrong DWARF
(14:39:40) irker749: gdb: tom binutils-gdb.git:master * 740480b88af / gdb/ChangeLog gdb/darwin-nat.c gdb/inferior.c gdb/inferior.h: Remove iterate_over_inferiors
(15:22:45) irker749: gdb: tromey binutils-gdb.git:master * ecc6c6066b5 / gdb/ChangeLog gdb/dwarf2/read.c gdb/unittests/lookup_name_info-selftests.c: Fix Ada crash with .debug_names
(15:23:13) bjorn3: tromey: ping
(15:23:29) tromey: bjorn3: hey
(15:24:16) bjorn3: I am writing a backend for a compiler which uses DWARF for debuginfo. I unfortunately can't get gdb to show arguments. lldb works fine.
(15:25:13) bjorn3: it just says: No symbol table info available.
(15:25:21) bjorn3: any idea what it could be?
(15:25:34) bjorn3: dwarfdump output: https://gist.github.com/bjorn3/8a34e333c80f13cb048381e94b4a3756
(15:26:48) bjorn3: more context: https://github.com/bjorn3/rustc_codegen_cranelift/pull/978
(15:28:05) tromey: offhand I don't know, but if you can send me an executable I can look
(15:28:17) bjorn3: how should I send it?
(15:29:26) tromey: good question
(15:29:41) tromey: you could try emailing it to tromey at adacore.com
(15:29:47) tromey: dunno if that will work or not
(15:30:26) bjorn3: i will try
(15:37:27) bjorn3: tromey: i sent an email with the subject "gdb args not showing"
(15:38:29) tromey: will check now
(15:38:40) bjorn3: thanks!
(15:42:51) irker749: gdb: tdevries binutils-gdb.git:master * de82891ce5b / gdb/ChangeLog gdb/block.c gdb/block.h gdb/symtab.c gdb/testsuite/ChangeLog gdb/testsuite/gdb.base/decl-before-def-decl.c gdb/testsuite/gdb.base/decl-before-def-def.c gdb/testsuite/gdb.base/decl-before-def.exp: [gdb/symtab] Prefer def over decl (inter-CU case)
(15:42:52) irker749: gdb: tdevries binutils-gdb.git:master * 70bc38f5138 / gdb/ChangeLog gdb/symtab.c gdb/testsuite/ChangeLog gdb/testsuite/gdb.base/decl-before-def.exp: [gdb/symtab] Prefer def over decl (inter-CU case, with context)
(15:43:36) tromey: bjorn3: sorry, got distracted. I have the file now
(15:45:35) tromey: my first thing when investigating was to enable complaints
(15:45:37) tromey: so I did
(15:45:40) tromey: set complaints 1000
(15:45:42) tromey: then
(15:45:51) tromey: file -readnow mini_core_hello_world
(15:46:00) tromey: gdb printed just one style of complaint
(15:46:07) tromey: During symbol reading: missing name for subprogram DIE at 0x3f7
(15:46:18) tromey: (which is really pretty good, most compilers manage to generate a bunch)
(15:46:29) tromey: and then the gdb DWARF reader says
(15:46:34) tromey: /* Ignore functions with missing or empty names. These are actually
(15:46:34) tromey: illegal according to the DWARF standard. */
(15:46:34) tromey: if (name == NULL)
(15:46:34) tromey: {
(15:46:37) tromey: complaint (_("missing name for subprogram DIE at %s"),
(15:46:40) tromey: sect_offset_str (die->sect_off));
(15:46:47) tromey: I wonder if that comment is correct though
(15:47:34) tromey: I guess pedantically maybe it is, DWARF 5 3.3.1 says
(15:47:43) tromey: The subroutine or entry point entry has a DW_AT_name attribute whose value is
(15:47:43) tromey: a null-terminated string containing the subroutine or entry point name.
(15:48:14) bjorn3: i tried set complaints, but it returned complaints for system files. i didn't know about file -readnow.
(15:48:21) tromey: cool
(15:48:26) bjorn3: i will try adding DW_AT_name
(15:48:45) tromey: without readnow unfortunately you get less stuff, because for whatever reason gdb has 2 separate DWARF scanners
(15:49:02) tromey: sort of anyway
(15:49:43) tromey: this seems kind of pedantic of gdb, like if there's a linkage name but no DW_AT_name, then why bail?
(15:50:01) tromey: also what about anonymous functions
(15:50:17) tromey: but anyway this explains the current situation and if you don't mind adding DW_AT_name, then that's probably simplest
(15:51:47) bjorn3: i added DW_AT_name.
(15:51:54) bjorn3: now it says cannot get low and high bounds for subprogram DIE at ...
(15:52:01) tromey: ugh
(15:52:10) bjorn3: i will add DW_AT_low_pc and DW_AT_high_pc
(15:52:15) tromey: /* Ignore functions with missing or invalid low and high pc attributes. */
(15:52:37) tromey: you can also use DW_AT_ranges
(15:52:55) tromey: if you'd prefer
(15:53:08) bjorn3: already using DW_AT_ranges for DW_TAG_compilation_unit
(15:53:19) bjorn3: for individual functions, there are no gaps
(15:57:07) bjorn3: still the same error with DW_AT_low_pc and DW_AT_high_pc
(15:57:24) bjorn3: tromey: ^
(15:58:08) tromey: hmmm
(15:58:30) bjorn3: should i send the new executable?
(15:58:31) tromey: send me another executable & I will debug
(15:58:33) tromey: yep
(15:59:23) bjorn3: sent as repy of the previous mail
(16:03:23) tromey: the low PC has DW_FORM_addr, but the high PC has DW_FORM_udata, which seems weird
(16:03:50) mjw: no
(16:03:54) tromey: no?
(16:04:00) mjw: I suggested that for the DWARF standard...
(16:04:05) mjw: sorry
(16:04:58) mjw: The idea was that instead of two relocations and two address wide fields, you have one address and a constant offset.
(16:05:05) tromey: ahh, I see the code now
(16:05:07) tromey: I forgot about this
(16:05:18) tromey: if (cu->header.version >= 4 && attr_high->form_is_constant ())
(16:05:18) tromey: high += low;
(16:05:36) mjw: that second offset doesn't need a relocation and can often be packed in something small, like an uleb128
(16:05:51) mjw: using udata might not be ideal though, but is allowed
(16:05:51) tromey: bjorn3: the problem is that this CU claims to be DWARF 3 but is using a DWARF 4 feature
(16:05:58) mjw: aha
(16:05:59) bjorn3: which one?
(16:06:03) ryoshu: hi
(16:06:08) tromey: high_pc (udata) 107 (+0x00000000000011b0 <_ZN21mini_core_hello_world5start17hec55b7ca64fc434eE>)
(16:06:08) tromey:
(16:06:12) ryoshu: just soft ping, I have a queue of patches :)
(16:06:22) tromey: using this as a length requires DWARF 4
(16:06:36) tromey: for gdb at least it's fine to always emit DWARF 4
(16:06:44) bjorn3: trying dwarf 4 now
(16:06:48) tromey: I think there are some DWARF 5 features still in the works but DWARF 4 should be solid AFAIK
(16:07:03) tromey: fini
(16:07:08) tromey: lol wrong window
(16:07:56) mjw: Maybe you can accept it for DWARF < 4. But if I remember correctly it might be that people might have been using udata as if it was an address...
(16:08:13) tromey: yeah, I vaguely recall this as well, though I'd expect there to be a comment
(16:08:21) mjw: Cannot really remember why it needed version >= 4. Maybe there was no good reason?
(16:08:32) bjorn3: tromey: it works!!!! thanks for all the help!
(16:08:41) tromey: my pleasure bjorn3
```
</details>

View File

@ -0,0 +1,12 @@
# List of env vars recognized by cg_clif
<dl>
<dt>CG_CLIF_JIT_ARGS</dt>
<dd>When JIT mode is enable pass these arguments to the program.</dd>
<dt>CG_CLIF_INCR_CACHE_DISABLED</dt>
<dd>Don't cache object files in the incremental cache. Useful during development of cg_clif
to make it possible to use incremental mode for all analyses performed by rustc without caching
object files when their content should have been changed by a change to cg_clif.</dd>
<dt>CG_CLIF_DISPLAY_CG_TIME</dt>
<dd>If "1", display the time it took to perform codegen for a crate</dd>
</dl>

View File

@ -0,0 +1,37 @@
#![feature(start, box_syntax, alloc_system, core_intrinsics, alloc_prelude, alloc_error_handler)]
#![no_std]
extern crate alloc;
extern crate alloc_system;
use alloc::prelude::v1::*;
use alloc_system::System;
#[global_allocator]
static ALLOC: System = System;
#[link(name = "c")]
extern "C" {
fn puts(s: *const u8) -> i32;
}
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
core::intrinsics::abort();
}
#[alloc_error_handler]
fn alloc_error_handler(_: alloc::alloc::Layout) -> ! {
core::intrinsics::abort();
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
let world: Box<&str> = box "Hello World!\0";
unsafe {
puts(*world as *const str as *const u8);
}
0
}

View File

@ -0,0 +1,82 @@
// Adapted from rustc run-pass test suite
#![feature(no_core, arbitrary_self_types, box_syntax)]
#![feature(rustc_attrs)]
#![feature(start, lang_items)]
#![no_core]
extern crate mini_core;
use mini_core::*;
macro_rules! assert_eq {
($l:expr, $r: expr) => {
if $l != $r {
panic(stringify!($l != $r));
}
}
}
struct Ptr<T: ?Sized>(Box<T>);
impl<T: ?Sized> Deref for Ptr<T> {
type Target = T;
fn deref(&self) -> &T {
&*self.0
}
}
impl<T: Unsize<U> + ?Sized, U: ?Sized> CoerceUnsized<Ptr<U>> for Ptr<T> {}
impl<T: Unsize<U> + ?Sized, U: ?Sized> DispatchFromDyn<Ptr<U>> for Ptr<T> {}
struct Wrapper<T: ?Sized>(T);
impl<T: ?Sized> Deref for Wrapper<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T: CoerceUnsized<U>, U> CoerceUnsized<Wrapper<U>> for Wrapper<T> {}
impl<T: DispatchFromDyn<U>, U> DispatchFromDyn<Wrapper<U>> for Wrapper<T> {}
trait Trait {
// This method isn't object-safe yet. Unsized by-value `self` is object-safe (but not callable
// without unsized_locals), but wrappers arond `Self` currently are not.
// FIXME (mikeyhew) uncomment this when unsized rvalues object-safety is implemented
// fn wrapper(self: Wrapper<Self>) -> i32;
fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32;
fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32;
fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32;
}
impl Trait for i32 {
fn ptr_wrapper(self: Ptr<Wrapper<Self>>) -> i32 {
**self
}
fn wrapper_ptr(self: Wrapper<Ptr<Self>>) -> i32 {
**self
}
fn wrapper_ptr_wrapper(self: Wrapper<Ptr<Wrapper<Self>>>) -> i32 {
***self
}
}
#[start]
fn main(_: isize, _: *const *const u8) -> isize {
let pw = Ptr(box Wrapper(5)) as Ptr<Wrapper<dyn Trait>>;
assert_eq!(pw.ptr_wrapper(), 5);
let wp = Wrapper(Ptr(box 6)) as Wrapper<Ptr<dyn Trait>>;
assert_eq!(wp.wrapper_ptr(), 6);
let wpw = Wrapper(Ptr(box Wrapper(7))) as Wrapper<Ptr<Wrapper<dyn Trait>>>;
assert_eq!(wpw.wrapper_ptr_wrapper(), 7);
0
}

View File

@ -0,0 +1,67 @@
// run-pass
#![allow(dead_code)]
struct Foo<T: ?Sized> {
a: u16,
b: T
}
trait Bar {
fn get(&self) -> usize;
}
impl Bar for usize {
fn get(&self) -> usize { *self }
}
struct Baz<T: ?Sized> {
a: T
}
struct HasDrop<T: ?Sized> {
ptr: Box<usize>,
data: T
}
fn main() {
// Test that zero-offset works properly
let b : Baz<usize> = Baz { a: 7 };
assert_eq!(b.a.get(), 7);
let b : &Baz<dyn Bar> = &b;
assert_eq!(b.a.get(), 7);
// Test that the field is aligned properly
let f : Foo<usize> = Foo { a: 0, b: 11 };
assert_eq!(f.b.get(), 11);
let ptr1 : *const u8 = &f.b as *const _ as *const u8;
let f : &Foo<dyn Bar> = &f;
let ptr2 : *const u8 = &f.b as *const _ as *const u8;
assert_eq!(f.b.get(), 11);
// The pointers should be the same
assert_eq!(ptr1, ptr2);
// Test that nested DSTs work properly
let f : Foo<Foo<usize>> = Foo { a: 0, b: Foo { a: 1, b: 17 }};
assert_eq!(f.b.b.get(), 17);
let f : &Foo<Foo<dyn Bar>> = &f;
assert_eq!(f.b.b.get(), 17);
// Test that get the pointer via destructuring works
let f : Foo<usize> = Foo { a: 0, b: 11 };
let f : &Foo<dyn Bar> = &f;
let &Foo { a: _, b: ref bar } = f;
assert_eq!(bar.get(), 11);
// Make sure that drop flags don't screw things up
let d : HasDrop<Baz<[i32; 4]>> = HasDrop {
ptr: Box::new(0),
data: Baz { a: [1,2,3,4] }
};
assert_eq!([1,2,3,4], d.data.a);
let d : &HasDrop<Baz<[i32]>> = &d;
assert_eq!(&[1,2,3,4], &d.data.a);
}

View File

@ -0,0 +1,208 @@
#![feature(no_core, unboxed_closures)]
#![no_core]
#![allow(dead_code)]
extern crate mini_core;
use mini_core::*;
pub fn abc(a: u8) -> u8 {
a * 2
}
pub fn bcd(b: bool, a: u8) -> u8 {
if b {
a * 2
} else {
a * 3
}
}
pub fn call() {
abc(42);
}
pub fn indirect_call() {
let f: fn() = call;
f();
}
pub enum BoolOption {
Some(bool),
None,
}
pub fn option_unwrap_or(o: BoolOption, d: bool) -> bool {
match o {
BoolOption::Some(b) => b,
BoolOption::None => d,
}
}
pub fn ret_42() -> u8 {
42
}
pub fn return_str() -> &'static str {
"hello world"
}
pub fn promoted_val() -> &'static u8 {
&(1 * 2)
}
pub fn cast_ref_to_raw_ptr(abc: &u8) -> *const u8 {
abc as *const u8
}
pub fn cmp_raw_ptr(a: *const u8, b: *const u8) -> bool {
a == b
}
pub fn int_cast(a: u16, b: i16) -> (u8, u16, u32, usize, i8, i16, i32, isize, u8, u32) {
(
a as u8, a as u16, a as u32, a as usize, a as i8, a as i16, a as i32, a as isize, b as u8,
b as u32,
)
}
pub fn char_cast(c: char) -> u8 {
c as u8
}
pub struct DebugTuple(());
pub fn debug_tuple() -> DebugTuple {
DebugTuple(())
}
pub fn size_of<T>() -> usize {
intrinsics::size_of::<T>()
}
pub fn use_size_of() -> usize {
size_of::<u64>()
}
pub unsafe fn use_copy_intrinsic(src: *const u8, dst: *mut u8) {
intrinsics::copy::<u8>(src, dst, 1);
}
pub unsafe fn use_copy_intrinsic_ref(src: *const u8, dst: *mut u8) {
let copy2 = &intrinsics::copy::<u8>;
copy2(src, dst, 1);
}
pub const ABC: u8 = 6 * 7;
pub fn use_const() -> u8 {
ABC
}
pub fn call_closure_3arg() {
(|_, _, _| {})(0u8, 42u16, 0u8)
}
pub fn call_closure_2arg() {
(|_, _| {})(0u8, 42u16)
}
pub struct IsNotEmpty;
impl<'a, 'b> FnOnce<(&'a &'b [u16],)> for IsNotEmpty {
type Output = (u8, u8);
#[inline]
extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u16],)) -> (u8, u8) {
self.call_mut(arg)
}
}
impl<'a, 'b> FnMut<(&'a &'b [u16],)> for IsNotEmpty {
#[inline]
extern "rust-call" fn call_mut(&mut self, _arg: (&'a &'b [u16],)) -> (u8, u8) {
(0, 42)
}
}
pub fn call_is_not_empty() {
IsNotEmpty.call_once((&(&[0u16] as &[_]),));
}
pub fn eq_char(a: char, b: char) -> bool {
a == b
}
pub unsafe fn transmute(c: char) -> u32 {
intrinsics::transmute(c)
}
pub unsafe fn deref_str_ptr(s: *const str) -> &'static str {
&*s
}
pub fn use_array(arr: [u8; 3]) -> u8 {
arr[1]
}
pub fn repeat_array() -> [u8; 3] {
[0; 3]
}
pub fn array_as_slice(arr: &[u8; 3]) -> &[u8] {
arr
}
pub unsafe fn use_ctlz_nonzero(a: u16) -> u16 {
intrinsics::ctlz_nonzero(a)
}
pub fn ptr_as_usize(ptr: *const u8) -> usize {
ptr as usize
}
pub fn float_cast(a: f32, b: f64) -> (f64, f32) {
(a as f64, b as f32)
}
pub fn int_to_float(a: u8, b: i32) -> (f64, f32) {
(a as f64, b as f32)
}
pub fn make_array() -> [u8; 3] {
[42, 0, 5]
}
pub fn some_promoted_tuple() -> &'static (&'static str, &'static str) {
&("abc", "some")
}
pub fn index_slice(s: &[u8]) -> u8 {
s[2]
}
pub struct StrWrapper {
s: str,
}
pub fn str_wrapper_get(w: &StrWrapper) -> &str {
&w.s
}
pub fn i16_as_i8(a: i16) -> i8 {
a as i8
}
pub struct Unsized(u8, str);
pub fn get_sized_field_ref_from_unsized_type(u: &Unsized) -> &u8 {
&u.0
}
pub fn get_unsized_field_ref_from_unsized_type(u: &Unsized) -> &str {
&u.1
}
pub fn reuse_byref_argument_storage(a: (u8, u16, u32)) -> u8 {
a.0
}

View File

@ -0,0 +1,613 @@
#![feature(
no_core, lang_items, intrinsics, unboxed_closures, type_ascription, extern_types,
untagged_unions, decl_macro, rustc_attrs, transparent_unions, optin_builtin_traits,
thread_local,
)]
#![no_core]
#![allow(dead_code)]
#[lang = "sized"]
pub trait Sized {}
#[lang = "unsize"]
pub trait Unsize<T: ?Sized> {}
#[lang = "coerce_unsized"]
pub trait CoerceUnsized<T> {}
impl<'a, 'b: 'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a U> for &'b T {}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<&'a mut U> for &'a mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*const U> for *const T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<*mut U> for *mut T {}
#[lang = "dispatch_from_dyn"]
pub trait DispatchFromDyn<T> {}
// &T -> &U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {}
// &mut T -> &mut U
impl<'a, T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {}
// *const T -> *const U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*const U> for *const T {}
// *mut T -> *mut U
impl<T: ?Sized+Unsize<U>, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {}
impl<T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Box<U>> for Box<T> {}
#[lang = "receiver"]
pub trait Receiver {}
impl<T: ?Sized> Receiver for &T {}
impl<T: ?Sized> Receiver for &mut T {}
impl<T: ?Sized> Receiver for Box<T> {}
#[lang = "copy"]
pub unsafe trait Copy {}
unsafe impl Copy for bool {}
unsafe impl Copy for u8 {}
unsafe impl Copy for u16 {}
unsafe impl Copy for u32 {}
unsafe impl Copy for u64 {}
unsafe impl Copy for u128 {}
unsafe impl Copy for usize {}
unsafe impl Copy for i8 {}
unsafe impl Copy for i16 {}
unsafe impl Copy for i32 {}
unsafe impl Copy for isize {}
unsafe impl Copy for f32 {}
unsafe impl Copy for char {}
unsafe impl<'a, T: ?Sized> Copy for &'a T {}
unsafe impl<T: ?Sized> Copy for *const T {}
unsafe impl<T: ?Sized> Copy for *mut T {}
unsafe impl<T: Copy> Copy for Option<T> {}
#[lang = "sync"]
pub unsafe trait Sync {}
unsafe impl Sync for bool {}
unsafe impl Sync for u8 {}
unsafe impl Sync for u16 {}
unsafe impl Sync for u32 {}
unsafe impl Sync for u64 {}
unsafe impl Sync for usize {}
unsafe impl Sync for i8 {}
unsafe impl Sync for i16 {}
unsafe impl Sync for i32 {}
unsafe impl Sync for isize {}
unsafe impl Sync for char {}
unsafe impl<'a, T: ?Sized> Sync for &'a T {}
unsafe impl Sync for [u8; 16] {}
#[lang = "freeze"]
unsafe auto trait Freeze {}
unsafe impl<T: ?Sized> Freeze for PhantomData<T> {}
unsafe impl<T: ?Sized> Freeze for *const T {}
unsafe impl<T: ?Sized> Freeze for *mut T {}
unsafe impl<T: ?Sized> Freeze for &T {}
unsafe impl<T: ?Sized> Freeze for &mut T {}
#[lang = "structural_peq"]
pub trait StructuralPartialEq {}
#[lang = "structural_teq"]
pub trait StructuralEq {}
#[lang = "not"]
pub trait Not {
type Output;
fn not(self) -> Self::Output;
}
impl Not for bool {
type Output = bool;
fn not(self) -> bool {
!self
}
}
#[lang = "mul"]
pub trait Mul<RHS = Self> {
type Output;
#[must_use]
fn mul(self, rhs: RHS) -> Self::Output;
}
impl Mul for u8 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self * rhs
}
}
impl Mul for usize {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
self * rhs
}
}
#[lang = "add"]
pub trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
impl Add for u8 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
self + rhs
}
}
impl Add for i8 {
type Output = Self;
fn add(self, rhs: Self) -> Self {
self + rhs
}
}
impl Add for usize {
type Output = Self;
fn add(self, rhs: Self) -> Self {
self + rhs
}
}
#[lang = "sub"]
pub trait Sub<RHS = Self> {
type Output;
fn sub(self, rhs: RHS) -> Self::Output;
}
impl Sub for usize {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self - rhs
}
}
impl Sub for u8 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self - rhs
}
}
impl Sub for i8 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self - rhs
}
}
impl Sub for i16 {
type Output = Self;
fn sub(self, rhs: Self) -> Self {
self - rhs
}
}
#[lang = "rem"]
pub trait Rem<RHS = Self> {
type Output;
fn rem(self, rhs: RHS) -> Self::Output;
}
impl Rem for usize {
type Output = Self;
fn rem(self, rhs: Self) -> Self {
self % rhs
}
}
#[lang = "bitor"]
pub trait BitOr<RHS = Self> {
type Output;
#[must_use]
fn bitor(self, rhs: RHS) -> Self::Output;
}
impl BitOr for bool {
type Output = bool;
fn bitor(self, rhs: bool) -> bool {
self | rhs
}
}
impl<'a> BitOr<bool> for &'a bool {
type Output = bool;
fn bitor(self, rhs: bool) -> bool {
*self | rhs
}
}
#[lang = "eq"]
pub trait PartialEq<Rhs: ?Sized = Self> {
fn eq(&self, other: &Rhs) -> bool;
fn ne(&self, other: &Rhs) -> bool;
}
impl PartialEq for u8 {
fn eq(&self, other: &u8) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &u8) -> bool {
(*self) != (*other)
}
}
impl PartialEq for u16 {
fn eq(&self, other: &u16) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &u16) -> bool {
(*self) != (*other)
}
}
impl PartialEq for u32 {
fn eq(&self, other: &u32) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &u32) -> bool {
(*self) != (*other)
}
}
impl PartialEq for u64 {
fn eq(&self, other: &u64) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &u64) -> bool {
(*self) != (*other)
}
}
impl PartialEq for u128 {
fn eq(&self, other: &u128) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &u128) -> bool {
(*self) != (*other)
}
}
impl PartialEq for usize {
fn eq(&self, other: &usize) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &usize) -> bool {
(*self) != (*other)
}
}
impl PartialEq for i8 {
fn eq(&self, other: &i8) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &i8) -> bool {
(*self) != (*other)
}
}
impl PartialEq for i32 {
fn eq(&self, other: &i32) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &i32) -> bool {
(*self) != (*other)
}
}
impl PartialEq for isize {
fn eq(&self, other: &isize) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &isize) -> bool {
(*self) != (*other)
}
}
impl PartialEq for char {
fn eq(&self, other: &char) -> bool {
(*self) == (*other)
}
fn ne(&self, other: &char) -> bool {
(*self) != (*other)
}
}
impl<T: ?Sized> PartialEq for *const T {
fn eq(&self, other: &*const T) -> bool {
*self == *other
}
fn ne(&self, other: &*const T) -> bool {
*self != *other
}
}
impl <T: PartialEq> PartialEq for Option<T> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Some(lhs), Some(rhs)) => *lhs == *rhs,
(None, None) => true,
_ => false,
}
}
fn ne(&self, other: &Self) -> bool {
match (self, other) {
(Some(lhs), Some(rhs)) => *lhs != *rhs,
(None, None) => false,
_ => true,
}
}
}
#[lang = "neg"]
pub trait Neg {
type Output;
fn neg(self) -> Self::Output;
}
impl Neg for i8 {
type Output = i8;
fn neg(self) -> i8 {
-self
}
}
impl Neg for i16 {
type Output = i16;
fn neg(self) -> i16 {
self
}
}
impl Neg for isize {
type Output = isize;
fn neg(self) -> isize {
-self
}
}
impl Neg for f32 {
type Output = f32;
fn neg(self) -> f32 {
-self
}
}
pub enum Option<T> {
Some(T),
None,
}
pub use Option::*;
#[lang = "phantom_data"]
pub struct PhantomData<T: ?Sized>;
#[lang = "fn_once"]
#[rustc_paren_sugar]
pub trait FnOnce<Args> {
#[lang = "fn_once_output"]
type Output;
extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
}
#[lang = "fn_mut"]
#[rustc_paren_sugar]
pub trait FnMut<Args>: FnOnce<Args> {
extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output;
}
#[lang = "panic"]
#[track_caller]
pub fn panic(_msg: &str) -> ! {
unsafe {
libc::puts("Panicking\n\0" as *const str as *const i8);
intrinsics::abort();
}
}
#[lang = "panic_bounds_check"]
#[track_caller]
fn panic_bounds_check(index: usize, len: usize) -> ! {
unsafe {
libc::printf("index out of bounds: the len is %d but the index is %d\n\0" as *const str as *const i8, len, index);
intrinsics::abort();
}
}
#[lang = "eh_personality"]
fn eh_personality() -> ! {
loop {}
}
#[lang = "drop_in_place"]
#[allow(unconditional_recursion)]
pub unsafe fn drop_in_place<T: ?Sized>(to_drop: *mut T) {
// Code here does not matter - this is replaced by the
// real drop glue by the compiler.
drop_in_place(to_drop);
}
#[lang = "deref"]
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
#[lang = "owned_box"]
pub struct Box<T: ?Sized>(*mut T);
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Box<U>> for Box<T> {}
impl<T: ?Sized> Drop for Box<T> {
fn drop(&mut self) {
// drop is currently performed by compiler.
}
}
impl<T> Deref for Box<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&**self
}
}
#[lang = "exchange_malloc"]
unsafe fn allocate(size: usize, _align: usize) -> *mut u8 {
libc::malloc(size)
}
#[lang = "box_free"]
unsafe fn box_free<T: ?Sized>(ptr: *mut T) {
libc::free(ptr as *mut u8);
}
#[lang = "drop"]
pub trait Drop {
fn drop(&mut self);
}
#[lang = "manually_drop"]
#[repr(transparent)]
pub struct ManuallyDrop<T: ?Sized> {
pub value: T,
}
#[lang = "maybe_uninit"]
#[repr(transparent)]
pub union MaybeUninit<T> {
pub uninit: (),
pub value: ManuallyDrop<T>,
}
pub mod intrinsics {
extern "rust-intrinsic" {
pub fn abort() -> !;
pub fn size_of<T>() -> usize;
pub fn size_of_val<T: ?::Sized>(val: *const T) -> usize;
pub fn min_align_of<T>() -> usize;
pub fn min_align_of_val<T: ?::Sized>(val: *const T) -> usize;
pub fn copy<T>(src: *const T, dst: *mut T, count: usize);
pub fn transmute<T, U>(e: T) -> U;
pub fn ctlz_nonzero<T>(x: T) -> T;
pub fn needs_drop<T>() -> bool;
pub fn bitreverse<T>(x: T) -> T;
pub fn bswap<T>(x: T) -> T;
pub fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
}
}
pub mod libc {
#[cfg_attr(not(windows), link(name = "c"))]
#[cfg_attr(windows, link(name = "msvcrt"))]
extern "C" {
pub fn puts(s: *const i8) -> i32;
pub fn printf(format: *const i8, ...) -> i32;
pub fn malloc(size: usize) -> *mut u8;
pub fn free(ptr: *mut u8);
pub fn memcpy(dst: *mut u8, src: *const u8, size: usize);
pub fn memmove(dst: *mut u8, src: *const u8, size: usize);
pub fn strncpy(dst: *mut u8, src: *const u8, size: usize);
}
}
#[lang = "index"]
pub trait Index<Idx: ?Sized> {
type Output: ?Sized;
fn index(&self, index: Idx) -> &Self::Output;
}
impl<T> Index<usize> for [T; 3] {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self[index]
}
}
impl<T> Index<usize> for [T] {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self[index]
}
}
extern {
type VaListImpl;
}
#[lang = "va_list"]
#[repr(transparent)]
pub struct VaList<'a>(&'a mut VaListImpl);
#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro stringify($($t:tt)*) { /* compiler built-in */ }
#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro file() { /* compiler built-in */ }
#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro line() { /* compiler built-in */ }
#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro cfg() { /* compiler built-in */ }
#[rustc_builtin_macro]
#[rustc_macro_transparency = "semitransparent"]
pub macro global_asm() { /* compiler built-in */ }
pub static A_STATIC: u8 = 42;
#[lang = "panic_location"]
struct PanicLocation {
file: &'static str,
line: u32,
column: u32,
}
#[no_mangle]
pub fn get_tls() -> u8 {
#[thread_local]
static A: u8 = 42;
A
}

View File

@ -0,0 +1,470 @@
#![feature(
no_core, start, lang_items, box_syntax, never_type, linkage,
extern_types, thread_local
)]
#![no_core]
#![allow(dead_code, non_camel_case_types)]
extern crate mini_core;
use mini_core::*;
use mini_core::libc::*;
unsafe extern "C" fn my_puts(s: *const i8) {
puts(s);
}
#[lang = "termination"]
trait Termination {
fn report(self) -> i32;
}
impl Termination for () {
fn report(self) -> i32 {
unsafe {
NUM = 6 * 7 + 1 + (1u8 == 1u8) as u8; // 44
*NUM_REF as i32
}
}
}
trait SomeTrait {
fn object_safe(&self);
}
impl SomeTrait for &'static str {
fn object_safe(&self) {
unsafe {
puts(*self as *const str as *const i8);
}
}
}
struct NoisyDrop {
text: &'static str,
inner: NoisyDropInner,
}
struct NoisyDropInner;
impl Drop for NoisyDrop {
fn drop(&mut self) {
unsafe {
puts(self.text as *const str as *const i8);
}
}
}
impl Drop for NoisyDropInner {
fn drop(&mut self) {
unsafe {
puts("Inner got dropped!\0" as *const str as *const i8);
}
}
}
impl SomeTrait for NoisyDrop {
fn object_safe(&self) {}
}
enum Ordering {
Less = -1,
Equal = 0,
Greater = 1,
}
#[lang = "start"]
fn start<T: Termination + 'static>(
main: fn() -> T,
argc: isize,
argv: *const *const u8,
) -> isize {
if argc == 3 {
unsafe { puts(*argv as *const i8); }
unsafe { puts(*((argv as usize + intrinsics::size_of::<*const u8>()) as *const *const i8)); }
unsafe { puts(*((argv as usize + 2 * intrinsics::size_of::<*const u8>()) as *const *const i8)); }
}
main().report();
0
}
static mut NUM: u8 = 6 * 7;
static NUM_REF: &'static u8 = unsafe { &NUM };
macro_rules! assert {
($e:expr) => {
if !$e {
panic(stringify!(! $e));
}
};
}
macro_rules! assert_eq {
($l:expr, $r: expr) => {
if $l != $r {
panic(stringify!($l != $r));
}
}
}
struct Unique<T: ?Sized> {
pointer: *const T,
_marker: PhantomData<T>,
}
impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
unsafe fn zeroed<T>() -> T {
let mut uninit = MaybeUninit { uninit: () };
intrinsics::write_bytes(&mut uninit.value.value as *mut T, 0, 1);
uninit.value.value
}
fn take_f32(_f: f32) {}
fn take_unique(_u: Unique<()>) {}
fn return_u128_pair() -> (u128, u128) {
(0, 0)
}
fn call_return_u128_pair() {
return_u128_pair();
}
fn main() {
take_unique(Unique {
pointer: 0 as *const (),
_marker: PhantomData,
});
take_f32(0.1);
call_return_u128_pair();
let slice = &[0, 1] as &[i32];
let slice_ptr = slice as *const [i32] as *const i32;
assert_eq!(slice_ptr as usize % 4, 0);
//return;
unsafe {
printf("Hello %s\n\0" as *const str as *const i8, "printf\0" as *const str as *const i8);
let hello: &[u8] = b"Hello\0" as &[u8; 6];
let ptr: *const i8 = hello as *const [u8] as *const i8;
puts(ptr);
let world: Box<&str> = box "World!\0";
puts(*world as *const str as *const i8);
world as Box<dyn SomeTrait>;
assert_eq!(intrinsics::bitreverse(0b10101000u8), 0b00010101u8);
assert_eq!(intrinsics::bswap(0xabu8), 0xabu8);
assert_eq!(intrinsics::bswap(0xddccu16), 0xccddu16);
assert_eq!(intrinsics::bswap(0xffee_ddccu32), 0xccdd_eeffu32);
assert_eq!(intrinsics::bswap(0x1234_5678_ffee_ddccu64), 0xccdd_eeff_7856_3412u64);
assert_eq!(intrinsics::size_of_val(hello) as u8, 6);
let chars = &['C', 'h', 'a', 'r', 's'];
let chars = chars as &[char];
assert_eq!(intrinsics::size_of_val(chars) as u8, 4 * 5);
let a: &dyn SomeTrait = &"abc\0";
a.object_safe();
assert_eq!(intrinsics::size_of_val(a) as u8, 16);
assert_eq!(intrinsics::size_of_val(&0u32) as u8, 4);
assert_eq!(intrinsics::min_align_of::<u16>() as u8, 2);
assert_eq!(intrinsics::min_align_of_val(&a) as u8, intrinsics::min_align_of::<&str>() as u8);
assert!(!intrinsics::needs_drop::<u8>());
assert!(intrinsics::needs_drop::<NoisyDrop>());
Unique {
pointer: 0 as *const &str,
_marker: PhantomData,
} as Unique<dyn SomeTrait>;
struct MyDst<T: ?Sized>(T);
intrinsics::size_of_val(&MyDst([0u8; 4]) as &MyDst<[u8]>);
struct Foo {
x: u8,
y: !,
}
unsafe fn uninitialized<T>() -> T {
MaybeUninit { uninit: () }.value.value
}
zeroed::<(u8, u8)>();
#[allow(unreachable_code)]
{
if false {
zeroed::<!>();
zeroed::<Foo>();
uninitialized::<Foo>();
}
}
}
let _ = box NoisyDrop {
text: "Boxed outer got dropped!\0",
inner: NoisyDropInner,
} as Box<dyn SomeTrait>;
const FUNC_REF: Option<fn()> = Some(main);
match FUNC_REF {
Some(_) => {},
None => assert!(false),
}
match Ordering::Less {
Ordering::Less => {},
_ => assert!(false),
}
[NoisyDropInner, NoisyDropInner];
let x = &[0u32, 42u32] as &[u32];
match x {
[] => assert_eq!(0u32, 1),
[_, ref y @ ..] => assert_eq!(&x[1] as *const u32 as usize, &y[0] as *const u32 as usize),
}
assert_eq!(((|()| 42u8) as fn(()) -> u8)(()), 42);
#[cfg(not(jit))]
{
extern {
#[linkage = "extern_weak"]
static ABC: *const u8;
}
{
extern {
#[linkage = "extern_weak"]
static ABC: *const u8;
}
}
unsafe { assert_eq!(ABC as usize, 0); }
}
&mut (|| Some(0 as *const ())) as &mut dyn FnMut() -> Option<*const ()>;
let f = 1000.0;
assert_eq!(f as u8, 255);
let f2 = -1000.0;
assert_eq!(f2 as i8, -128);
assert_eq!(f2 as u8, 0);
static ANOTHER_STATIC: &u8 = &A_STATIC;
assert_eq!(*ANOTHER_STATIC, 42);
check_niche_behavior();
extern "C" {
type ExternType;
}
struct ExternTypeWrapper {
_a: ExternType,
}
let nullptr = 0 as *const ();
let extern_nullptr = nullptr as *const ExternTypeWrapper;
extern_nullptr as *const ();
let slice_ptr = &[] as *const [u8];
slice_ptr as *const u8;
let repeat = [Some(42); 2];
assert_eq!(repeat[0], Some(42));
assert_eq!(repeat[1], Some(42));
from_decimal_string();
#[cfg(not(jit))]
test_tls();
#[cfg(all(not(jit), target_os = "linux"))]
unsafe {
global_asm_test();
}
}
#[cfg(all(not(jit), target_os = "linux"))]
extern "C" {
fn global_asm_test();
}
#[cfg(all(not(jit), target_os = "linux"))]
global_asm! {
"
.global global_asm_test
global_asm_test:
// comment that would normally be removed by LLVM
ret
"
}
#[repr(C)]
enum c_void {
_1,
_2,
}
type c_int = i32;
type c_ulong = u64;
type pthread_t = c_ulong;
#[repr(C)]
struct pthread_attr_t {
__size: [u64; 7],
}
#[link(name = "pthread")]
extern "C" {
fn pthread_attr_init(attr: *mut pthread_attr_t) -> c_int;
fn pthread_create(
native: *mut pthread_t,
attr: *const pthread_attr_t,
f: extern "C" fn(_: *mut c_void) -> *mut c_void,
value: *mut c_void
) -> c_int;
fn pthread_join(
native: pthread_t,
value: *mut *mut c_void
) -> c_int;
}
#[thread_local]
#[cfg(not(jit))]
static mut TLS: u8 = 42;
#[cfg(not(jit))]
extern "C" fn mutate_tls(_: *mut c_void) -> *mut c_void {
unsafe { TLS = 0; }
0 as *mut c_void
}
#[cfg(not(jit))]
fn test_tls() {
unsafe {
let mut attr: pthread_attr_t = zeroed();
let mut thread: pthread_t = 0;
assert_eq!(TLS, 42);
if pthread_attr_init(&mut attr) != 0 {
assert!(false);
}
if pthread_create(&mut thread, &attr, mutate_tls, 0 as *mut c_void) != 0 {
assert!(false);
}
let mut res = 0 as *mut c_void;
pthread_join(thread, &mut res);
// TLS of main thread must not have been changed by the other thread.
assert_eq!(TLS, 42);
puts("TLS works!\n\0" as *const str as *const i8);
}
}
// Copied ui/issues/issue-61696.rs
pub enum Infallible {}
// The check that the `bool` field of `V1` is encoding a "niche variant"
// (i.e. not `V1`, so `V3` or `V4`) used to be mathematically incorrect,
// causing valid `V1` values to be interpreted as other variants.
pub enum E1 {
V1 { f: bool },
V2 { f: Infallible },
V3,
V4,
}
// Computing the discriminant used to be done using the niche type (here `u8`,
// from the `bool` field of `V1`), overflowing for variants with large enough
// indices (`V3` and `V4`), causing them to be interpreted as other variants.
pub enum E2<X> {
V1 { f: bool },
/*_00*/ _01(X), _02(X), _03(X), _04(X), _05(X), _06(X), _07(X),
_08(X), _09(X), _0A(X), _0B(X), _0C(X), _0D(X), _0E(X), _0F(X),
_10(X), _11(X), _12(X), _13(X), _14(X), _15(X), _16(X), _17(X),
_18(X), _19(X), _1A(X), _1B(X), _1C(X), _1D(X), _1E(X), _1F(X),
_20(X), _21(X), _22(X), _23(X), _24(X), _25(X), _26(X), _27(X),
_28(X), _29(X), _2A(X), _2B(X), _2C(X), _2D(X), _2E(X), _2F(X),
_30(X), _31(X), _32(X), _33(X), _34(X), _35(X), _36(X), _37(X),
_38(X), _39(X), _3A(X), _3B(X), _3C(X), _3D(X), _3E(X), _3F(X),
_40(X), _41(X), _42(X), _43(X), _44(X), _45(X), _46(X), _47(X),
_48(X), _49(X), _4A(X), _4B(X), _4C(X), _4D(X), _4E(X), _4F(X),
_50(X), _51(X), _52(X), _53(X), _54(X), _55(X), _56(X), _57(X),
_58(X), _59(X), _5A(X), _5B(X), _5C(X), _5D(X), _5E(X), _5F(X),
_60(X), _61(X), _62(X), _63(X), _64(X), _65(X), _66(X), _67(X),
_68(X), _69(X), _6A(X), _6B(X), _6C(X), _6D(X), _6E(X), _6F(X),
_70(X), _71(X), _72(X), _73(X), _74(X), _75(X), _76(X), _77(X),
_78(X), _79(X), _7A(X), _7B(X), _7C(X), _7D(X), _7E(X), _7F(X),
_80(X), _81(X), _82(X), _83(X), _84(X), _85(X), _86(X), _87(X),
_88(X), _89(X), _8A(X), _8B(X), _8C(X), _8D(X), _8E(X), _8F(X),
_90(X), _91(X), _92(X), _93(X), _94(X), _95(X), _96(X), _97(X),
_98(X), _99(X), _9A(X), _9B(X), _9C(X), _9D(X), _9E(X), _9F(X),
_A0(X), _A1(X), _A2(X), _A3(X), _A4(X), _A5(X), _A6(X), _A7(X),
_A8(X), _A9(X), _AA(X), _AB(X), _AC(X), _AD(X), _AE(X), _AF(X),
_B0(X), _B1(X), _B2(X), _B3(X), _B4(X), _B5(X), _B6(X), _B7(X),
_B8(X), _B9(X), _BA(X), _BB(X), _BC(X), _BD(X), _BE(X), _BF(X),
_C0(X), _C1(X), _C2(X), _C3(X), _C4(X), _C5(X), _C6(X), _C7(X),
_C8(X), _C9(X), _CA(X), _CB(X), _CC(X), _CD(X), _CE(X), _CF(X),
_D0(X), _D1(X), _D2(X), _D3(X), _D4(X), _D5(X), _D6(X), _D7(X),
_D8(X), _D9(X), _DA(X), _DB(X), _DC(X), _DD(X), _DE(X), _DF(X),
_E0(X), _E1(X), _E2(X), _E3(X), _E4(X), _E5(X), _E6(X), _E7(X),
_E8(X), _E9(X), _EA(X), _EB(X), _EC(X), _ED(X), _EE(X), _EF(X),
_F0(X), _F1(X), _F2(X), _F3(X), _F4(X), _F5(X), _F6(X), _F7(X),
_F8(X), _F9(X), _FA(X), _FB(X), _FC(X), _FD(X), _FE(X), _FF(X),
V3,
V4,
}
fn check_niche_behavior () {
if let E1::V2 { .. } = (E1::V1 { f: true }) {
intrinsics::abort();
}
if let E2::V1 { .. } = E2::V3::<Infallible> {
intrinsics::abort();
}
}
fn from_decimal_string() {
loop {
let multiplier = 1;
take_multiplier_ref(&multiplier);
if multiplier == 1 {
break;
}
unreachable();
}
}
fn take_multiplier_ref(_multiplier: &u128) {}
fn unreachable() -> ! {
panic("unreachable")
}

View File

@ -0,0 +1,35 @@
#![feature(start, box_syntax, core_intrinsics, lang_items)]
#![no_std]
#[link(name = "c")]
extern {}
#[panic_handler]
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
core::intrinsics::abort();
}
#[lang="eh_personality"]
fn eh_personality(){}
// Required for rustc_codegen_llvm
#[no_mangle]
unsafe extern "C" fn _Unwind_Resume() {
core::intrinsics::unreachable();
}
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
for i in 2..10_000_000 {
black_box((i + 1) % i);
}
0
}
#[inline(never)]
fn black_box(i: u32) {
if i != 1 {
core::intrinsics::abort();
}
}

View File

@ -0,0 +1,343 @@
#![feature(core_intrinsics, generators, generator_trait, is_sorted)]
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use std::io::Write;
use std::ops::Generator;
fn main() {
println!("{:?}", std::env::args().collect::<Vec<_>>());
let mutex = std::sync::Mutex::new(());
let _guard = mutex.lock().unwrap();
let _ = ::std::iter::repeat('a' as u8).take(10).collect::<Vec<_>>();
let stderr = ::std::io::stderr();
let mut stderr = stderr.lock();
std::thread::spawn(move || {
println!("Hello from another thread!");
});
writeln!(stderr, "some {} text", "<unknown>").unwrap();
let _ = std::process::Command::new("true").env("c", "d").spawn();
println!("cargo:rustc-link-lib=z");
static ONCE: std::sync::Once = std::sync::Once::new();
ONCE.call_once(|| {});
let _eq = LoopState::Continue(()) == LoopState::Break(());
// Make sure ByValPair values with differently sized components are correctly passed
map(None::<(u8, Box<Instruction>)>);
println!("{}", 2.3f32.exp());
println!("{}", 2.3f32.exp2());
println!("{}", 2.3f32.abs());
println!("{}", 2.3f32.sqrt());
println!("{}", 2.3f32.floor());
println!("{}", 2.3f32.ceil());
println!("{}", 2.3f32.min(1.0));
println!("{}", 2.3f32.max(1.0));
println!("{}", 2.3f32.powi(2));
println!("{}", 2.3f32.log2());
assert_eq!(2.3f32.copysign(-1.0), -2.3f32);
println!("{}", 2.3f32.powf(2.0));
assert_eq!(-128i8, (-128i8).saturating_sub(1));
assert_eq!(127i8, 127i8.saturating_sub(-128));
assert_eq!(-128i8, (-128i8).saturating_add(-128));
assert_eq!(127i8, 127i8.saturating_add(1));
assert_eq!(0b0000000000000000000000000010000010000000000000000000000000000000_0000000000100000000000000000000000001000000000000100000000000000u128.leading_zeros(), 26);
assert_eq!(0b0000000000000000000000000010000000000000000000000000000000000000_0000000000000000000000000000000000001000000000000000000010000000u128.trailing_zeros(), 7);
let _d = 0i128.checked_div(2i128);
let _d = 0u128.checked_div(2u128);
assert_eq!(1u128 + 2, 3);
assert_eq!(0b100010000000000000000000000000000u128 >> 10, 0b10001000000000000000000u128);
assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 >> 64, 0xFEDCBA98765432u128);
assert_eq!(0xFEDCBA987654321123456789ABCDEFu128 as i128 >> 64, 0xFEDCBA98765432i128);
let tmp = 353985398u128;
assert_eq!(tmp * 932490u128, 330087843781020u128);
let tmp = -0x1234_5678_9ABC_DEF0i64;
assert_eq!(tmp as i128, -0x1234_5678_9ABC_DEF0i128);
// Check that all u/i128 <-> float casts work correctly.
let houndred_u128 = 100u128;
let houndred_i128 = 100i128;
let houndred_f32 = 100.0f32;
let houndred_f64 = 100.0f64;
assert_eq!(houndred_u128 as f32, 100.0);
assert_eq!(houndred_u128 as f64, 100.0);
assert_eq!(houndred_f32 as u128, 100);
assert_eq!(houndred_f64 as u128, 100);
assert_eq!(houndred_i128 as f32, 100.0);
assert_eq!(houndred_i128 as f64, 100.0);
assert_eq!(houndred_f32 as i128, 100);
assert_eq!(houndred_f64 as i128, 100);
// Test signed 128bit comparing
let max = usize::MAX as i128;
if 100i128 < 0i128 || 100i128 > max {
panic!();
}
test_checked_mul();
let _a = 1u32 << 2u8;
let empty: [i32; 0] = [];
assert!(empty.is_sorted());
println!("{:?}", std::intrinsics::caller_location());
#[cfg(target_arch = "x86_64")]
unsafe {
test_simd();
}
Box::pin(move |mut _task_context| {
yield ();
}).as_mut().resume(0);
#[derive(Copy, Clone)]
enum Nums {
NegOne = -1,
}
let kind = Nums::NegOne;
assert_eq!(-1i128, kind as i128);
let options = [1u128];
match options[0] {
1 => (),
0 => loop {},
v => panic(v),
};
}
fn panic(_: u128) {
panic!();
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_simd() {
assert!(is_x86_feature_detected!("sse2"));
let x = _mm_setzero_si128();
let y = _mm_set1_epi16(7);
let or = _mm_or_si128(x, y);
let cmp_eq = _mm_cmpeq_epi8(y, y);
let cmp_lt = _mm_cmplt_epi8(y, y);
assert_eq!(std::mem::transmute::<_, [u16; 8]>(or), [7, 7, 7, 7, 7, 7, 7, 7]);
assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_eq), [0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff]);
assert_eq!(std::mem::transmute::<_, [u16; 8]>(cmp_lt), [0, 0, 0, 0, 0, 0, 0, 0]);
test_mm_slli_si128();
test_mm_movemask_epi8();
test_mm256_movemask_epi8();
test_mm_add_epi8();
test_mm_add_pd();
test_mm_cvtepi8_epi16();
test_mm_cvtsi128_si64();
test_mm_extract_epi8();
test_mm_insert_epi16();
let mask1 = _mm_movemask_epi8(dbg!(_mm_setr_epi8(255u8 as i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)));
assert_eq!(mask1, 1);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_mm_slli_si128() {
#[rustfmt::skip]
let a = _mm_setr_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
);
let r = _mm_slli_si128(a, 1);
let e = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
assert_eq_m128i(r, e);
#[rustfmt::skip]
let a = _mm_setr_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
);
let r = _mm_slli_si128(a, 15);
let e = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1);
assert_eq_m128i(r, e);
#[rustfmt::skip]
let a = _mm_setr_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
);
let r = _mm_slli_si128(a, 16);
assert_eq_m128i(r, _mm_set1_epi8(0));
#[rustfmt::skip]
let a = _mm_setr_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
);
let r = _mm_slli_si128(a, -1);
assert_eq_m128i(_mm_set1_epi8(0), r);
#[rustfmt::skip]
let a = _mm_setr_epi8(
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
);
let r = _mm_slli_si128(a, -0x80000000);
assert_eq_m128i(r, _mm_set1_epi8(0));
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_mm_movemask_epi8() {
#[rustfmt::skip]
let a = _mm_setr_epi8(
0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8, 0b01,
0b0101, 0b1111_0000u8 as i8, 0, 0,
0, 0, 0b1111_0000u8 as i8, 0b0101,
0b01, 0b1000_0000u8 as i8, 0b0, 0b1000_0000u8 as i8,
);
let r = _mm_movemask_epi8(a);
assert_eq!(r, 0b10100100_00100101);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn test_mm256_movemask_epi8() {
let a = _mm256_set1_epi8(-1);
let r = _mm256_movemask_epi8(a);
let e = -1;
assert_eq!(r, e);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_mm_add_epi8() {
let a = _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
#[rustfmt::skip]
let b = _mm_setr_epi8(
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
);
let r = _mm_add_epi8(a, b);
#[rustfmt::skip]
let e = _mm_setr_epi8(
16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46,
);
assert_eq_m128i(r, e);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_mm_add_pd() {
let a = _mm_setr_pd(1.0, 2.0);
let b = _mm_setr_pd(5.0, 10.0);
let r = _mm_add_pd(a, b);
assert_eq_m128d(r, _mm_setr_pd(6.0, 12.0));
}
#[cfg(target_arch = "x86_64")]
fn assert_eq_m128i(x: std::arch::x86_64::__m128i, y: std::arch::x86_64::__m128i) {
unsafe {
assert_eq!(std::mem::transmute::<_, [u8; 16]>(x), std::mem::transmute::<_, [u8; 16]>(y));
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
pub unsafe fn assert_eq_m128d(a: __m128d, b: __m128d) {
if _mm_movemask_pd(_mm_cmpeq_pd(a, b)) != 0b11 {
panic!("{:?} != {:?}", a, b);
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_mm_cvtsi128_si64() {
let r = _mm_cvtsi128_si64(std::mem::transmute::<[i64; 2], _>([5, 0]));
assert_eq!(r, 5);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse4.1")]
unsafe fn test_mm_cvtepi8_epi16() {
let a = _mm_set1_epi8(10);
let r = _mm_cvtepi8_epi16(a);
let e = _mm_set1_epi16(10);
assert_eq_m128i(r, e);
let a = _mm_set1_epi8(-10);
let r = _mm_cvtepi8_epi16(a);
let e = _mm_set1_epi16(-10);
assert_eq_m128i(r, e);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse4.1")]
unsafe fn test_mm_extract_epi8() {
#[rustfmt::skip]
let a = _mm_setr_epi8(
-1, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15
);
let r1 = _mm_extract_epi8(a, 0);
let r2 = _mm_extract_epi8(a, 19);
assert_eq!(r1, 0xFF);
assert_eq!(r2, 3);
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "sse2")]
unsafe fn test_mm_insert_epi16() {
let a = _mm_setr_epi16(0, 1, 2, 3, 4, 5, 6, 7);
let r = _mm_insert_epi16(a, 9, 0);
let e = _mm_setr_epi16(9, 1, 2, 3, 4, 5, 6, 7);
assert_eq_m128i(r, e);
}
fn test_checked_mul() {
let u: Option<u8> = u8::from_str_radix("1000", 10).ok();
assert_eq!(u, None);
assert_eq!(1u8.checked_mul(255u8), Some(255u8));
assert_eq!(255u8.checked_mul(255u8), None);
assert_eq!(1i8.checked_mul(127i8), Some(127i8));
assert_eq!(127i8.checked_mul(127i8), None);
assert_eq!((-1i8).checked_mul(-127i8), Some(127i8));
assert_eq!(1i8.checked_mul(-128i8), Some(-128i8));
assert_eq!((-128i8).checked_mul(-128i8), None);
assert_eq!(1u64.checked_mul(u64::MAX), Some(u64::MAX));
assert_eq!(u64::MAX.checked_mul(u64::MAX), None);
assert_eq!(1i64.checked_mul(i64::MAX), Some(i64::MAX));
assert_eq!(i64::MAX.checked_mul(i64::MAX), None);
assert_eq!((-1i64).checked_mul(i64::MIN + 1), Some(i64::MAX));
assert_eq!(1i64.checked_mul(i64::MIN), Some(i64::MIN));
assert_eq!(i64::MIN.checked_mul(i64::MIN), None);
}
#[derive(PartialEq)]
enum LoopState {
Continue(()),
Break(())
}
pub enum Instruction {
Increment,
Loop,
}
fn map(a: Option<(u8, Box<Instruction>)>) -> Option<Box<Instruction>> {
match a {
None => None,
Some((_, instr)) => Some(instr),
}
}

View File

@ -0,0 +1,97 @@
// Based on https://github.com/rust-lang/rust/blob/c5840f9d252c2f5cc16698dbf385a29c5de3ca07/src/test/ui/array-slice-vec/subslice-patterns-const-eval-match.rs
// Test that array subslice patterns are correctly handled in const evaluation.
// run-pass
#[derive(PartialEq, Debug, Clone)]
struct N(u8);
#[derive(PartialEq, Debug, Clone)]
struct Z;
macro_rules! n {
($($e:expr),* $(,)?) => {
[$(N($e)),*]
}
}
// This macro has an unused variable so that it can be repeated base on the
// number of times a repeated variable (`$e` in `z`) occurs.
macro_rules! zed {
($e:expr) => { Z }
}
macro_rules! z {
($($e:expr),* $(,)?) => {
[$(zed!($e)),*]
}
}
// Compare constant evaluation and runtime evaluation of a given expression.
macro_rules! compare_evaluation {
($e:expr, $t:ty $(,)?) => {{
const CONST_EVAL: $t = $e;
const fn const_eval() -> $t { $e }
static CONST_EVAL2: $t = const_eval();
let runtime_eval = $e;
assert_eq!(CONST_EVAL, runtime_eval);
assert_eq!(CONST_EVAL2, runtime_eval);
}}
}
// Repeat `$test`, substituting the given macro variables with the given
// identifiers.
//
// For example:
//
// repeat! {
// ($name); X; Y:
// struct $name;
// }
//
// Expands to:
//
// struct X; struct Y;
//
// This is used to repeat the tests using both the `N` and `Z`
// types.
macro_rules! repeat {
(($($dollar:tt $placeholder:ident)*); $($($values:ident),+);*: $($test:tt)*) => {
macro_rules! single {
($($dollar $placeholder:ident),*) => { $($test)* }
}
$(single!($($values),+);)*
}
}
fn main() {
repeat! {
($arr $Ty); n, N; z, Z:
compare_evaluation!({ let [_, x @ .., _] = $arr!(1, 2, 3, 4); x }, [$Ty; 2]);
compare_evaluation!({ let [_, ref x @ .., _] = $arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
compare_evaluation!({ let [_, x @ .., _] = &$arr!(1, 2, 3, 4); x }, &'static [$Ty; 2]);
compare_evaluation!({ let [_, _, x @ .., _, _] = $arr!(1, 2, 3, 4); x }, [$Ty; 0]);
compare_evaluation!(
{ let [_, _, ref x @ .., _, _] = $arr!(1, 2, 3, 4); x },
&'static [$Ty; 0],
);
compare_evaluation!(
{ let [_, _, x @ .., _, _] = &$arr!(1, 2, 3, 4); x },
&'static [$Ty; 0],
);
compare_evaluation!({ let [_, .., x] = $arr!(1, 2, 3, 4); x }, $Ty);
compare_evaluation!({ let [_, .., ref x] = $arr!(1, 2, 3, 4); x }, &'static $Ty);
compare_evaluation!({ let [_, _y @ .., x] = &$arr!(1, 2, 3, 4); x }, &'static $Ty);
}
compare_evaluation!({ let [_, .., N(x)] = n!(1, 2, 3, 4); x }, u8);
compare_evaluation!({ let [_, .., N(ref x)] = n!(1, 2, 3, 4); x }, &'static u8);
compare_evaluation!({ let [_, .., N(x)] = &n!(1, 2, 3, 4); x }, &'static u8);
compare_evaluation!({ let [N(x), .., _] = n!(1, 2, 3, 4); x }, u8);
compare_evaluation!({ let [N(ref x), .., _] = n!(1, 2, 3, 4); x }, &'static u8);
compare_evaluation!({ let [N(x), .., _] = &n!(1, 2, 3, 4); x }, &'static u8);
}

View File

@ -0,0 +1,40 @@
// Based on https://github.com/anp/rust/blob/175631311716d7dfeceec40d2587cde7142ffa8c/src/test/ui/rfc-2091-track-caller/track-caller-attribute.rs
// run-pass
use std::panic::Location;
#[track_caller]
fn tracked() -> &'static Location<'static> {
Location::caller()
}
fn nested_intrinsic() -> &'static Location<'static> {
Location::caller()
}
fn nested_tracked() -> &'static Location<'static> {
tracked()
}
fn main() {
let location = Location::caller();
assert_eq!(location.file(), file!());
assert_eq!(location.line(), 21);
assert_eq!(location.column(), 20);
let tracked = tracked();
assert_eq!(tracked.file(), file!());
assert_eq!(tracked.line(), 26);
assert_eq!(tracked.column(), 19);
let nested = nested_intrinsic();
assert_eq!(nested.file(), file!());
assert_eq!(nested.line(), 13);
assert_eq!(nested.column(), 5);
let contained = nested_tracked();
assert_eq!(contained.file(), file!());
assert_eq!(contained.line(), 17);
assert_eq!(contained.column(), 5);
}

View File

@ -0,0 +1,123 @@
From f6befc4bb51d84f5f1cf35938a168c953d421350 Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Sun, 24 Nov 2019 15:10:23 +0100
Subject: [PATCH] [core] Disable not compiling tests
---
library/core/tests/Cargo.toml | 8 ++++++++
library/core/tests/num/flt2dec/mod.rs | 1 -
library/core/tests/num/int_macros.rs | 2 ++
library/core/tests/num/uint_macros.rs | 2 ++
library/core/tests/ptr.rs | 2 ++
library/core/tests/slice.rs | 2 ++
6 files changed, 16 insertions(+), 1 deletion(-)
create mode 100644 library/core/tests/Cargo.toml
diff --git a/library/core/tests/Cargo.toml b/library/core/tests/Cargo.toml
new file mode 100644
index 0000000..46fd999
--- /dev/null
+++ b/library/core/tests/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "core"
+version = "0.0.0"
+edition = "2018"
+
+[lib]
+name = "coretests"
+path = "lib.rs"
diff --git a/library/core/tests/num/flt2dec/mod.rs b/library/core/tests/num/flt2dec/mod.rs
index a35897e..f0bf645 100644
--- a/library/core/tests/num/flt2dec/mod.rs
+++ b/library/core/tests/num/flt2dec/mod.rs
@@ -13,7 +13,6 @@ mod strategy {
mod dragon;
mod grisu;
}
-mod random;
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
match decode(v).1 {
diff --git a/library/core/tests/num/int_macros.rs b/library/core/tests/num/int_macros.rs
index 0475aeb..9558198 100644
--- a/library/core/tests/num/int_macros.rs
+++ b/library/core/tests/num/int_macros.rs
@@ -88,6 +88,7 @@ mod tests {
assert_eq!(x.trailing_ones(), 0);
}
+ /*
#[test]
fn test_rotate() {
assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
@@ -112,6 +113,7 @@ mod tests {
assert_eq!(B.rotate_left(64), B);
assert_eq!(C.rotate_left(64), C);
}
+ */
#[test]
fn test_swap_bytes() {
diff --git a/library/core/tests/num/uint_macros.rs b/library/core/tests/num/uint_macros.rs
index 04ed14f..a6e372e 100644
--- a/library/core/tests/num/uint_macros.rs
+++ b/library/core/tests/num/uint_macros.rs
@@ -52,6 +52,7 @@ mod tests {
assert_eq!(x.trailing_ones(), 0);
}
+ /*
#[test]
fn test_rotate() {
assert_eq!(A.rotate_left(6).rotate_right(2).rotate_right(4), A);
@@ -76,6 +77,7 @@ mod tests {
assert_eq!(B.rotate_left(64), B);
assert_eq!(C.rotate_left(64), C);
}
+ */
#[test]
fn test_swap_bytes() {
diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs
index 1a6be3a..42dbd59 100644
--- a/library/core/tests/ptr.rs
+++ b/library/core/tests/ptr.rs
@@ -250,6 +250,7 @@ fn test_unsized_nonnull() {
assert!(ys == zs);
}
+/*
#[test]
#[allow(warnings)]
// Have a symbol for the test below. It doesnt need to be an actual variadic function, match the
@@ -289,6 +290,7 @@ fn write_unaligned_drop() {
}
DROPS.with(|d| assert_eq!(*d.borrow(), [0]));
}
+*/
#[test]
fn align_offset_zst() {
diff --git a/library/core/tests/slice.rs b/library/core/tests/slice.rs
index 6609bc3..241b497 100644
--- a/library/core/tests/slice.rs
+++ b/library/core/tests/slice.rs
@@ -1209,6 +1209,7 @@ fn brute_force_rotate_test_1() {
}
}
+/*
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn sort_unstable() {
@@ -1394,6 +1395,7 @@ fn partition_at_index() {
v.select_nth_unstable(0);
assert!(v == [0xDEADBEEF]);
}
+*/
#[test]
#[should_panic(expected = "index 0 greater than length of slice")]
--
2.21.0 (Apple Git-122)

View File

@ -0,0 +1,90 @@
From dd82e95c9de212524e14fc60155de1ae40156dfc Mon Sep 17 00:00:00 2001
From: bjorn3 <bjorn3@users.noreply.github.com>
Date: Sun, 24 Nov 2019 15:34:06 +0100
Subject: [PATCH] [core] Ignore failing tests
---
library/core/tests/iter.rs | 4 ++++
library/core/tests/num/bignum.rs | 10 ++++++++++
library/core/tests/num/mod.rs | 5 +++--
library/core/tests/time.rs | 1 +
4 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/library/core/tests/array.rs b/library/core/tests/array.rs
index 4bc44e9..8e3c7a4 100644
--- a/library/core/tests/array.rs
+++ b/library/core/tests/array.rs
@@ -242,6 +242,7 @@ fn iterator_drops() {
assert_eq!(i.get(), 5);
}
+/*
// This test does not work on targets without panic=unwind support.
// To work around this problem, test is marked is should_panic, so it will
// be automagically skipped on unsuitable targets, such as
@@ -283,6 +284,7 @@ fn array_default_impl_avoids_leaks_on_panic() {
assert_eq!(COUNTER.load(Relaxed), 0);
panic!("test succeeded")
}
+*/
#[test]
fn empty_array_is_always_default() {
@@ -304,6 +304,7 @@ fn array_map() {
assert_eq!(b, [1, 2, 3]);
}
+/*
// See note on above test for why `should_panic` is used.
#[test]
#[should_panic(expected = "test succeeded")]
@@ -332,6 +333,7 @@ fn array_map_drop_safety() {
assert_eq!(DROPPED.load(Ordering::SeqCst), num_to_create);
panic!("test succeeded")
}
+*/
#[test]
fn cell_allows_array_cycle() {
diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs
index a17c094..5bb11d2 100644
--- a/library/core/tests/num/mod.rs
+++ b/library/core/tests/num/mod.rs
@@ -651,11 +651,12 @@ macro_rules! test_float {
assert_eq!((9.0 as $fty).min($neginf), $neginf);
assert_eq!(($neginf as $fty).min(-9.0), $neginf);
assert_eq!((-9.0 as $fty).min($neginf), $neginf);
- assert_eq!(($nan as $fty).min(9.0), 9.0);
- assert_eq!(($nan as $fty).min(-9.0), -9.0);
- assert_eq!((9.0 as $fty).min($nan), 9.0);
- assert_eq!((-9.0 as $fty).min($nan), -9.0);
- assert!(($nan as $fty).min($nan).is_nan());
+ // Cranelift fmin has NaN propagation
+ //assert_eq!(($nan as $fty).min(9.0), 9.0);
+ //assert_eq!(($nan as $fty).min(-9.0), -9.0);
+ //assert_eq!((9.0 as $fty).min($nan), 9.0);
+ //assert_eq!((-9.0 as $fty).min($nan), -9.0);
+ //assert!(($nan as $fty).min($nan).is_nan());
}
#[test]
fn max() {
@@ -673,11 +674,12 @@ macro_rules! test_float {
assert_eq!((9.0 as $fty).max($neginf), 9.0);
assert_eq!(($neginf as $fty).max(-9.0), -9.0);
assert_eq!((-9.0 as $fty).max($neginf), -9.0);
- assert_eq!(($nan as $fty).max(9.0), 9.0);
- assert_eq!(($nan as $fty).max(-9.0), -9.0);
- assert_eq!((9.0 as $fty).max($nan), 9.0);
- assert_eq!((-9.0 as $fty).max($nan), -9.0);
- assert!(($nan as $fty).max($nan).is_nan());
+ // Cranelift fmax has NaN propagation
+ //assert_eq!(($nan as $fty).max(9.0), 9.0);
+ //assert_eq!(($nan as $fty).max(-9.0), -9.0);
+ //assert_eq!((9.0 as $fty).max($nan), 9.0);
+ //assert_eq!((-9.0 as $fty).max($nan), -9.0);
+ //assert!(($nan as $fty).max($nan).is_nan());
}
#[test]
fn rem_euclid() {
--
2.21.0 (Apple Git-122)

View File

@ -0,0 +1,29 @@
#!/bin/bash --verbose
set -e
rustup component add rust-src rustc-dev llvm-tools-preview
./build_sysroot/prepare_sysroot_src.sh
cargo install hyperfine || echo "Skipping hyperfine install"
git clone https://github.com/rust-random/rand.git || echo "rust-random/rand has already been cloned"
pushd rand
git checkout -- .
git checkout 0f933f9c7176e53b2a3c7952ded484e1783f0bf1
git am ../crate_patches/*-rand-*.patch
popd
git clone https://github.com/rust-lang/regex.git || echo "rust-lang/regex has already been cloned"
pushd regex
git checkout -- .
git checkout 341f207c1071f7290e3f228c710817c280c8dca1
popd
git clone https://github.com/ebobby/simple-raytracer || echo "ebobby/simple-raytracer has already been cloned"
pushd simple-raytracer
git checkout -- .
git checkout 804a7a21b9e673a482797aa289a18ed480e4d813
# build with cg_llvm for perf comparison
cargo build
mv target/debug/main raytracer_cg_llvm
popd

View File

@ -0,0 +1 @@
nightly-2020-10-31

View File

@ -0,0 +1,2 @@
This directory is for scripts that are either never directly invoked or are not used very often.
Scripts that are frequently used should be kept at the project root.

View File

@ -0,0 +1,16 @@
#!/bin/bash
dir=$(dirname "$0")
source $dir/config.sh
# read nightly compiler from rust-toolchain file
TOOLCHAIN=$(cat $dir/rust-toolchain)
cmd=$1
shift || true
if [[ "$cmd" = "jit" ]]; then
cargo +${TOOLCHAIN} rustc "$@" -- --jit
else
cargo +${TOOLCHAIN} $cmd "$@"
fi

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bash
set -e
unamestr=`uname`
if [[ "$unamestr" == 'Linux' ]]; then
dylib_ext='so'
elif [[ "$unamestr" == 'Darwin' ]]; then
dylib_ext='dylib'
else
echo "Unsupported os"
exit 1
fi
HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
TARGET_TRIPLE=$HOST_TRIPLE
#TARGET_TRIPLE="x86_64-pc-windows-gnu"
#TARGET_TRIPLE="aarch64-unknown-linux-gnu"
linker=''
RUN_WRAPPER=''
export JIT_SUPPORTED=1
if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
export JIT_SUPPORTED=0
if [[ "$TARGET_TRIPLE" == "aarch64-unknown-linux-gnu" ]]; then
# We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
linker='-Clinker=aarch64-linux-gnu-gcc'
RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu'
elif [[ "$TARGET_TRIPLE" == "x86_64-pc-windows-gnu" ]]; then
# We are cross-compiling for Windows. Run tests in wine.
RUN_WRAPPER='wine'
else
echo "Unknown non-native platform"
fi
fi
if echo "$RUSTC_WRAPPER" | grep sccache; then
echo
echo -e "\x1b[1;93m=== Warning: Unset RUSTC_WRAPPER to prevent interference with sccache ===\x1b[0m"
echo
export RUSTC_WRAPPER=
fi
dir=$(cd $(dirname "$BASH_SOURCE"); pwd)
export RUSTC=$dir"/cg_clif"
export RUSTFLAGS=$linker
export RUSTDOCFLAGS=$linker' -Ztrim-diagnostic-paths=no -Cpanic=abort -Zpanic-abort-tests '\
'-Zcodegen-backend='$dir'/librustc_codegen_cranelift.'$dylib_ext' --sysroot '$dir'/sysroot'
# FIXME remove once the atomic shim is gone
if [[ `uname` == 'Darwin' ]]; then
export RUSTFLAGS="$RUSTFLAGS -Clink-arg=-undefined -Clink-arg=dynamic_lookup"
fi
export LD_LIBRARY_PATH="$dir:$(rustc --print sysroot)/lib:$dir/target/out:$dir/sysroot/lib/rustlib/"$TARGET_TRIPLE"/lib"
export DYLD_LIBRARY_PATH=$LD_LIBRARY_PATH
export CG_CLIF_DISPLAY_CG_TIME=1

View File

@ -0,0 +1,125 @@
#!/bin/bash
#![forbid(unsafe_code)]/* This line is ignored by bash
# This block is ignored by rustc
pushd $(dirname "$0")/../
source build/config.sh
popd
PROFILE=$1 OUTPUT=$2 exec $RUSTC $RUSTFLAGS --jit $0
#*/
//! This program filters away uninteresting samples and trims uninteresting frames for stackcollapse
//! profiles.
//!
//! Usage: ./filter_profile.rs <profile in stackcollapse format> <output file>
//!
//! This file is specially crafted to be both a valid bash script and valid rust source file. If
//! executed as bash script this will run the rust source using cg_clif in JIT mode.
use std::io::Write;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let profile_name = std::env::var("PROFILE").unwrap();
let output_name = std::env::var("OUTPUT").unwrap();
if profile_name.is_empty() || output_name.is_empty() {
println!("Usage: ./filter_profile.rs <profile in stackcollapse format> <output file>");
std::process::exit(1);
}
let profile = std::fs::read_to_string(profile_name)
.map_err(|err| format!("Failed to read profile {}", err))?;
let mut output = std::fs::OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(output_name)?;
for line in profile.lines() {
let mut stack = &line[..line.rfind(" ").unwrap()];
let count = &line[line.rfind(" ").unwrap() + 1..];
// Filter away uninteresting samples
if !stack.contains("rustc_codegen_cranelift") {
continue;
}
if stack.contains("rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items")
|| stack.contains("rustc_incremental::assert_dep_graph::assert_dep_graph")
|| stack.contains("rustc_symbol_mangling::test::report_symbol_names")
{
continue;
}
// Trim start
if let Some(index) = stack.find("rustc_interface::passes::configure_and_expand") {
stack = &stack[index..];
} else if let Some(index) = stack.find("rustc_interface::passes::analysis") {
stack = &stack[index..];
} else if let Some(index) = stack.find("rustc_interface::passes::start_codegen") {
stack = &stack[index..];
} else if let Some(index) = stack.find("rustc_interface::queries::Linker::link") {
stack = &stack[index..];
}
if let Some(index) = stack.find("rustc_codegen_cranelift::driver::aot::module_codegen") {
stack = &stack[index..];
}
// Trim end
const MALLOC: &str = "malloc";
if let Some(index) = stack.find(MALLOC) {
stack = &stack[..index + MALLOC.len()];
}
const FREE: &str = "free";
if let Some(index) = stack.find(FREE) {
stack = &stack[..index + FREE.len()];
}
const TYPECK_ITEM_BODIES: &str = "rustc_typeck::check::typeck_item_bodies";
if let Some(index) = stack.find(TYPECK_ITEM_BODIES) {
stack = &stack[..index + TYPECK_ITEM_BODIES.len()];
}
const COLLECT_AND_PARTITION_MONO_ITEMS: &str =
"rustc_mir::monomorphize::partitioning::collect_and_partition_mono_items";
if let Some(index) = stack.find(COLLECT_AND_PARTITION_MONO_ITEMS) {
stack = &stack[..index + COLLECT_AND_PARTITION_MONO_ITEMS.len()];
}
const ASSERT_DEP_GRAPH: &str = "rustc_incremental::assert_dep_graph::assert_dep_graph";
if let Some(index) = stack.find(ASSERT_DEP_GRAPH) {
stack = &stack[..index + ASSERT_DEP_GRAPH.len()];
}
const REPORT_SYMBOL_NAMES: &str = "rustc_symbol_mangling::test::report_symbol_names";
if let Some(index) = stack.find(REPORT_SYMBOL_NAMES) {
stack = &stack[..index + REPORT_SYMBOL_NAMES.len()];
}
const ENCODE_METADATA: &str = "rustc_middle::ty::context::TyCtxt::encode_metadata";
if let Some(index) = stack.find(ENCODE_METADATA) {
stack = &stack[..index + ENCODE_METADATA.len()];
}
const SUBST_AND_NORMALIZE_ERASING_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::<impl rustc_middle::ty::context::TyCtxt>::subst_and_normalize_erasing_regions";
if let Some(index) = stack.find(SUBST_AND_NORMALIZE_ERASING_REGIONS) {
stack = &stack[..index + SUBST_AND_NORMALIZE_ERASING_REGIONS.len()];
}
const NORMALIZE_ERASING_LATE_BOUND_REGIONS: &str = "rustc_middle::ty::normalize_erasing_regions::<impl rustc_middle::ty::context::TyCtxt>::normalize_erasing_late_bound_regions";
if let Some(index) = stack.find(NORMALIZE_ERASING_LATE_BOUND_REGIONS) {
stack = &stack[..index + NORMALIZE_ERASING_LATE_BOUND_REGIONS.len()];
}
const INST_BUILD: &str = "<cranelift_frontend::frontend::FuncInstBuilder as cranelift_codegen::ir::builder::InstBuilderBase>::build";
if let Some(index) = stack.find(INST_BUILD) {
stack = &stack[..index + INST_BUILD.len()];
}
output.write_all(stack.as_bytes())?;
output.write_all(&*b" ")?;
output.write_all(count.as_bytes())?;
output.write_all(&*b"\n")?;
}
Ok(())
}

View File

@ -0,0 +1,42 @@
#!/bin/bash
set -e
case $1 in
"prepare")
TOOLCHAIN=$(date +%Y-%m-%d)
echo "=> Installing new nightly"
rustup toolchain install --profile minimal nightly-${TOOLCHAIN} # Sanity check to see if the nightly exists
echo nightly-${TOOLCHAIN} > rust-toolchain
rustup component add rustfmt || true
echo "=> Uninstalling all old nighlies"
for nightly in $(rustup toolchain list | grep nightly | grep -v $TOOLCHAIN | grep -v nightly-x86_64); do
rustup toolchain uninstall $nightly
done
./clean_all.sh
./prepare.sh
(cd build_sysroot && cargo update)
;;
"commit")
git add rust-toolchain build_sysroot/Cargo.lock
git commit -m "Rustup to $(rustc -V)"
;;
"push")
cg_clif=$(pwd)
pushd ../rust
branch=update_cg_clif-$(date +%Y-%m-%d)
git checkout -b $branch
git subtree pull --prefix=compiler/rustc_codegen_cranelift/ https://github.com/bjorn3/rustc_codegen_cranelift.git master
git push -u my $branch
popd
;;
*)
echo "Unknown command '$1'"
echo "Usage: ./rustup.sh prepare|commit"
;;
esac

View File

@ -0,0 +1,65 @@
#!/bin/bash
set -e
cd $(dirname "$0")/../
./build.sh
source build/config.sh
echo "[TEST] Bootstrap of rustc"
git clone https://github.com/rust-lang/rust.git || true
pushd rust
git fetch
git checkout -- .
git checkout $(rustc -V | cut -d' ' -f3 | tr -d '(')
git apply - <<EOF
diff --git a/.gitmodules b/.gitmodules
index 984113151de..c1e9d960d56 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -34,10 +34,6 @@
[submodule "src/doc/edition-guide"]
path = src/doc/edition-guide
url = https://github.com/rust-lang/edition-guide.git
-[submodule "src/llvm-project"]
- path = src/llvm-project
- url = https://github.com/rust-lang/llvm-project.git
- branch = rustc/11.0-2020-10-12
[submodule "src/doc/embedded-book"]
path = src/doc/embedded-book
url = https://github.com/rust-embedded/book.git
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 23e689fcae7..5f077b765b6 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -32,7 +32,6 @@ tempfile = "3.0.5"
[dependencies.parking_lot]
version = "0.11"
-features = ["nightly"]
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["fileapi", "psapi"] }
EOF
cat > config.toml <<EOF
[llvm]
ninja = false
[build]
rustc = "$(pwd)/../build/cg_clif"
cargo = "$(rustup which cargo)"
full-bootstrap = true
local-rebuild = true
[rust]
codegen-backends = ["cranelift"]
EOF
rm -r compiler/rustc_codegen_cranelift/{Cargo.*,src}
cp ../Cargo.* compiler/rustc_codegen_cranelift/
cp -r ../src compiler/rustc_codegen_cranelift/src
./x.py build --stage 1 library/std
popd

View File

@ -0,0 +1,123 @@
#!/bin/bash
set -e
source build/config.sh
export CG_CLIF_INCR_CACHE_DISABLED=1
MY_RUSTC=$RUSTC" "$RUSTFLAGS" -L crate=target/out --out-dir target/out -Cdebuginfo=2"
function no_sysroot_tests() {
echo "[BUILD] mini_core"
$MY_RUSTC example/mini_core.rs --crate-name mini_core --crate-type lib,dylib --target $TARGET_TRIPLE
echo "[BUILD] example"
$MY_RUSTC example/example.rs --crate-type lib --target $TARGET_TRIPLE
if [[ "$JIT_SUPPORTED" = "1" ]]; then
echo "[JIT] mini_core_hello_world"
CG_CLIF_JIT_ARGS="abc bcd" $MY_RUSTC --jit example/mini_core_hello_world.rs --cfg jit --target $HOST_TRIPLE
else
echo "[JIT] mini_core_hello_world (skipped)"
fi
echo "[AOT] mini_core_hello_world"
$MY_RUSTC example/mini_core_hello_world.rs --crate-name mini_core_hello_world --crate-type bin -g --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/mini_core_hello_world abc bcd
# (echo "break set -n main"; echo "run"; sleep 1; echo "si -c 10"; sleep 1; echo "frame variable") | lldb -- ./target/out/mini_core_hello_world abc bcd
echo "[AOT] arbitrary_self_types_pointers_and_wrappers"
$MY_RUSTC example/arbitrary_self_types_pointers_and_wrappers.rs --crate-name arbitrary_self_types_pointers_and_wrappers --crate-type bin --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/arbitrary_self_types_pointers_and_wrappers
}
function base_sysroot_tests() {
echo "[AOT] alloc_example"
$MY_RUSTC example/alloc_example.rs --crate-type bin --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/alloc_example
if [[ "$JIT_SUPPORTED" = "1" ]]; then
echo "[JIT] std_example"
$MY_RUSTC --jit example/std_example.rs --target $HOST_TRIPLE
else
echo "[JIT] std_example (skipped)"
fi
echo "[AOT] dst_field_align"
# FIXME Re-add -Zmir-opt-level=2 once rust-lang/rust#67529 is fixed.
$MY_RUSTC example/dst-field-align.rs --crate-name dst_field_align --crate-type bin --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/dst_field_align || (echo $?; false)
echo "[AOT] std_example"
$MY_RUSTC example/std_example.rs --crate-type bin --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/std_example arg
echo "[AOT] subslice-patterns-const-eval"
$MY_RUSTC example/subslice-patterns-const-eval.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/subslice-patterns-const-eval
echo "[AOT] track-caller-attribute"
$MY_RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/track-caller-attribute
echo "[AOT] mod_bench"
$MY_RUSTC example/mod_bench.rs --crate-type bin --target $TARGET_TRIPLE
$RUN_WRAPPER ./target/out/mod_bench
pushd rand
rm -r ./target || true
../build/cargo.sh test --workspace
popd
}
function extended_sysroot_tests() {
pushd simple-raytracer
if [[ "$HOST_TRIPLE" = "$TARGET_TRIPLE" ]]; then
echo "[BENCH COMPILE] ebobby/simple-raytracer"
hyperfine --runs ${RUN_RUNS:-10} --warmup 1 --prepare "cargo clean" \
"RUSTC=rustc RUSTFLAGS='' cargo build" \
"../build/cargo.sh build"
echo "[BENCH RUN] ebobby/simple-raytracer"
cp ./target/debug/main ./raytracer_cg_clif
hyperfine --runs ${RUN_RUNS:-10} ./raytracer_cg_llvm ./raytracer_cg_clif
else
echo "[BENCH COMPILE] ebobby/simple-raytracer (skipped)"
echo "[COMPILE] ebobby/simple-raytracer"
../cargo.sh build
echo "[BENCH RUN] ebobby/simple-raytracer (skipped)"
fi
popd
pushd build_sysroot/sysroot_src/library/core/tests
echo "[TEST] libcore"
rm -r ./target || true
../../../../../build/cargo.sh test
popd
pushd regex
echo "[TEST] rust-lang/regex example shootout-regex-dna"
../build/cargo.sh clean
# Make sure `[codegen mono items] start` doesn't poison the diff
../build/cargo.sh build --example shootout-regex-dna
cat examples/regexdna-input.txt | ../build/cargo.sh run --example shootout-regex-dna | grep -v "Spawned thread" > res.txt
diff -u res.txt examples/regexdna-output.txt
echo "[TEST] rust-lang/regex tests"
../build/cargo.sh test --tests -- --exclude-should-panic --test-threads 1 -Zunstable-options -q
popd
}
case "$1" in
"no_sysroot")
no_sysroot_tests
;;
"base_sysroot")
base_sysroot_tests
;;
"extended_sysroot")
extended_sysroot_tests
;;
*)
echo "unknown test suite"
;;
esac

View File

@ -0,0 +1,130 @@
//! Annotate the clif ir with comments describing how arguments are passed into the current function
//! and where all locals are stored.
use std::borrow::Cow;
use rustc_middle::mir;
use cranelift_codegen::entity::EntityRef;
use crate::abi::pass_mode::*;
use crate::prelude::*;
pub(super) fn add_args_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) {
fx.add_global_comment(
"kind loc.idx param pass mode ty".to_string(),
);
}
pub(super) fn add_arg_comment<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
kind: &str,
local: Option<mir::Local>,
local_field: Option<usize>,
params: EmptySinglePair<Value>,
pass_mode: PassMode,
ty: Ty<'tcx>,
) {
let local = if let Some(local) = local {
Cow::Owned(format!("{:?}", local))
} else {
Cow::Borrowed("???")
};
let local_field = if let Some(local_field) = local_field {
Cow::Owned(format!(".{}", local_field))
} else {
Cow::Borrowed("")
};
let params = match params {
Empty => Cow::Borrowed("-"),
Single(param) => Cow::Owned(format!("= {:?}", param)),
Pair(param_a, param_b) => Cow::Owned(format!("= {:?}, {:?}", param_a, param_b)),
};
let pass_mode = format!("{:?}", pass_mode);
fx.add_global_comment(format!(
"{kind:5}{local:>3}{local_field:<5} {params:10} {pass_mode:36} {ty:?}",
kind = kind,
local = local,
local_field = local_field,
params = params,
pass_mode = pass_mode,
ty = ty,
));
}
pub(super) fn add_locals_header_comment(fx: &mut FunctionCx<'_, '_, impl Module>) {
fx.add_global_comment(String::new());
fx.add_global_comment(
"kind local ty size align (abi,pref)".to_string(),
);
}
pub(super) fn add_local_place_comments<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
place: CPlace<'tcx>,
local: Local,
) {
let TyAndLayout { ty, layout } = place.layout();
let rustc_target::abi::Layout {
size,
align,
abi: _,
variants: _,
fields: _,
largest_niche: _,
} = layout;
let (kind, extra) = match *place.inner() {
CPlaceInner::Var(place_local, var) => {
assert_eq!(local, place_local);
("ssa", Cow::Owned(format!(",var={}", var.index())))
}
CPlaceInner::VarPair(place_local, var1, var2) => {
assert_eq!(local, place_local);
(
"ssa",
Cow::Owned(format!(",var=({}, {})", var1.index(), var2.index())),
)
}
CPlaceInner::VarLane(_local, _var, _lane) => unreachable!(),
CPlaceInner::Addr(ptr, meta) => {
let meta = if let Some(meta) = meta {
Cow::Owned(format!(",meta={}", meta))
} else {
Cow::Borrowed("")
};
match ptr.base_and_offset() {
(crate::pointer::PointerBase::Addr(addr), offset) => (
"reuse",
format!("storage={}{}{}", addr, offset, meta).into(),
),
(crate::pointer::PointerBase::Stack(stack_slot), offset) => (
"stack",
format!("storage={}{}{}", stack_slot, offset, meta).into(),
),
(crate::pointer::PointerBase::Dangling(align), offset) => (
"zst",
format!("align={},offset={}", align.bytes(), offset).into(),
),
}
}
};
fx.add_global_comment(format!(
"{:<5} {:5} {:30} {:4}b {}, {}{}{}",
kind,
format!("{:?}", local),
format!("{:?}", ty),
size.bytes(),
align.abi.bytes(),
align.pref.bytes(),
if extra.is_empty() {
""
} else {
" "
},
extra,
));
}

View File

@ -0,0 +1,763 @@
//! Handling of everything related to the calling convention. Also fills `fx.local_map`.
#[cfg(debug_assertions)]
mod comments;
mod pass_mode;
mod returning;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_target::spec::abi::Abi;
use cranelift_codegen::ir::{AbiParam, ArgumentPurpose};
use self::pass_mode::*;
use crate::prelude::*;
pub(crate) use self::returning::{can_return_to_ssa_var, codegen_return};
// Copied from https://github.com/rust-lang/rust/blob/f52c72948aa1dd718cc1f168d21c91c584c0a662/src/librustc_middle/ty/layout.rs#L2301
#[rustfmt::skip]
pub(crate) fn fn_sig_for_fn_abi<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> ty::PolyFnSig<'tcx> {
use rustc_middle::ty::subst::Subst;
// FIXME(davidtwco,eddyb): A `ParamEnv` should be passed through to this function.
let ty = instance.ty(tcx, ty::ParamEnv::reveal_all());
match *ty.kind() {
ty::FnDef(..) => {
// HACK(davidtwco,eddyb): This is a workaround for polymorphization considering
// parameters unused if they show up in the signature, but not in the `mir::Body`
// (i.e. due to being inside a projection that got normalized, see
// `src/test/ui/polymorphization/normalized_sig_types.rs`), and codegen not keeping
// track of a polymorphization `ParamEnv` to allow normalizing later.
let mut sig = match *ty.kind() {
ty::FnDef(def_id, substs) => tcx
.normalize_erasing_regions(tcx.param_env(def_id), tcx.fn_sig(def_id))
.subst(tcx, substs),
_ => unreachable!(),
};
if let ty::InstanceDef::VtableShim(..) = instance.def {
// Modify `fn(self, ...)` to `fn(self: *mut Self, ...)`.
sig = sig.map_bound(|mut sig| {
let mut inputs_and_output = sig.inputs_and_output.to_vec();
inputs_and_output[0] = tcx.mk_mut_ptr(inputs_and_output[0]);
sig.inputs_and_output = tcx.intern_type_list(&inputs_and_output);
sig
});
}
sig
}
ty::Closure(def_id, substs) => {
let sig = substs.as_closure().sig();
let env_ty = tcx.closure_env_ty(def_id, substs).unwrap();
sig.map_bound(|sig| {
tcx.mk_fn_sig(
std::iter::once(env_ty.skip_binder()).chain(sig.inputs().iter().cloned()),
sig.output(),
sig.c_variadic,
sig.unsafety,
sig.abi,
)
})
}
ty::Generator(_, substs, _) => {
let sig = substs.as_generator().poly_sig();
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty);
let pin_did = tcx.require_lang_item(rustc_hir::LangItem::Pin, None);
let pin_adt_ref = tcx.adt_def(pin_did);
let pin_substs = tcx.intern_substs(&[env_ty.into()]);
let env_ty = tcx.mk_adt(pin_adt_ref, pin_substs);
sig.map_bound(|sig| {
let state_did = tcx.require_lang_item(rustc_hir::LangItem::GeneratorState, None);
let state_adt_ref = tcx.adt_def(state_did);
let state_substs =
tcx.intern_substs(&[sig.yield_ty.into(), sig.return_ty.into()]);
let ret_ty = tcx.mk_adt(state_adt_ref, state_substs);
tcx.mk_fn_sig(
[env_ty, sig.resume_ty].iter(),
&ret_ty,
false,
rustc_hir::Unsafety::Normal,
rustc_target::spec::abi::Abi::Rust,
)
})
}
_ => bug!("unexpected type {:?} in Instance::fn_sig", ty),
}
}
fn clif_sig_from_fn_sig<'tcx>(
tcx: TyCtxt<'tcx>,
triple: &target_lexicon::Triple,
sig: FnSig<'tcx>,
span: Span,
is_vtable_fn: bool,
requires_caller_location: bool,
) -> Signature {
let abi = match sig.abi {
Abi::System => Abi::C,
abi => abi,
};
let (call_conv, inputs, output): (CallConv, Vec<Ty<'tcx>>, Ty<'tcx>) = match abi {
Abi::Rust => (
CallConv::triple_default(triple),
sig.inputs().to_vec(),
sig.output(),
),
Abi::C | Abi::Unadjusted => (
CallConv::triple_default(triple),
sig.inputs().to_vec(),
sig.output(),
),
Abi::SysV64 => (CallConv::SystemV, sig.inputs().to_vec(), sig.output()),
Abi::RustCall => {
assert_eq!(sig.inputs().len(), 2);
let extra_args = match sig.inputs().last().unwrap().kind() {
ty::Tuple(ref tupled_arguments) => tupled_arguments,
_ => bug!("argument to function with \"rust-call\" ABI is not a tuple"),
};
let mut inputs: Vec<Ty<'tcx>> = vec![sig.inputs()[0]];
inputs.extend(extra_args.types());
(CallConv::triple_default(triple), inputs, sig.output())
}
Abi::System => unreachable!(),
Abi::RustIntrinsic => (
CallConv::triple_default(triple),
sig.inputs().to_vec(),
sig.output(),
),
_ => unimplemented!("unsupported abi {:?}", sig.abi),
};
let inputs = inputs
.into_iter()
.enumerate()
.map(|(i, ty)| {
let mut layout = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap();
if i == 0 && is_vtable_fn {
// Virtual calls turn their self param into a thin pointer.
// See https://github.com/rust-lang/rust/blob/37b6a5e5e82497caf5353d9d856e4eb5d14cbe06/src/librustc/ty/layout.rs#L2519-L2572 for more info
layout = tcx
.layout_of(ParamEnv::reveal_all().and(tcx.mk_mut_ptr(tcx.mk_unit())))
.unwrap();
}
let pass_mode = get_pass_mode(tcx, layout);
if abi != Abi::Rust && abi != Abi::RustCall && abi != Abi::RustIntrinsic {
match pass_mode {
PassMode::NoPass | PassMode::ByVal(_) => {}
PassMode::ByRef { size: Some(size) } => {
let purpose = ArgumentPurpose::StructArgument(u32::try_from(size.bytes()).expect("struct too big to pass on stack"));
return EmptySinglePair::Single(AbiParam::special(pointer_ty(tcx), purpose)).into_iter();
}
PassMode::ByValPair(_, _) | PassMode::ByRef { size: None } => {
tcx.sess.span_warn(
span,
&format!(
"Argument of type `{:?}` with pass mode `{:?}` is not yet supported \
for non-rust abi `{}`. Calling this function may result in a crash.",
layout.ty,
pass_mode,
abi,
),
);
}
}
}
pass_mode.get_param_ty(tcx).map(AbiParam::new).into_iter()
})
.flatten();
let (mut params, returns): (Vec<_>, Vec<_>) = match get_pass_mode(
tcx,
tcx.layout_of(ParamEnv::reveal_all().and(output)).unwrap(),
) {
PassMode::NoPass => (inputs.collect(), vec![]),
PassMode::ByVal(ret_ty) => (inputs.collect(), vec![AbiParam::new(ret_ty)]),
PassMode::ByValPair(ret_ty_a, ret_ty_b) => (
inputs.collect(),
vec![AbiParam::new(ret_ty_a), AbiParam::new(ret_ty_b)],
),
PassMode::ByRef { size: Some(_) } => {
(
Some(pointer_ty(tcx)) // First param is place to put return val
.into_iter()
.map(|ty| AbiParam::special(ty, ArgumentPurpose::StructReturn))
.chain(inputs)
.collect(),
vec![],
)
}
PassMode::ByRef { size: None } => todo!(),
};
if requires_caller_location {
params.push(AbiParam::new(pointer_ty(tcx)));
}
Signature {
params,
returns,
call_conv,
}
}
pub(crate) fn get_function_name_and_sig<'tcx>(
tcx: TyCtxt<'tcx>,
triple: &target_lexicon::Triple,
inst: Instance<'tcx>,
support_vararg: bool,
) -> (String, Signature) {
assert!(!inst.substs.needs_infer());
let fn_sig = tcx.normalize_erasing_late_bound_regions(
ParamEnv::reveal_all(),
&fn_sig_for_fn_abi(tcx, inst),
);
if fn_sig.c_variadic && !support_vararg {
tcx.sess.span_fatal(
tcx.def_span(inst.def_id()),
"Variadic function definitions are not yet supported",
);
}
let sig = clif_sig_from_fn_sig(
tcx,
triple,
fn_sig,
tcx.def_span(inst.def_id()),
false,
inst.def.requires_caller_location(tcx),
);
(tcx.symbol_name(inst).name.to_string(), sig)
}
/// Instance must be monomorphized
pub(crate) fn import_function<'tcx>(
tcx: TyCtxt<'tcx>,
module: &mut impl Module,
inst: Instance<'tcx>,
) -> FuncId {
let (name, sig) = get_function_name_and_sig(tcx, module.isa().triple(), inst, true);
module
.declare_function(&name, Linkage::Import, &sig)
.unwrap()
}
impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> {
/// Instance must be monomorphized
pub(crate) fn get_function_ref(&mut self, inst: Instance<'tcx>) -> FuncRef {
let func_id = import_function(self.tcx, &mut self.cx.module, inst);
let func_ref = self
.cx
.module
.declare_func_in_func(func_id, &mut self.bcx.func);
#[cfg(debug_assertions)]
self.add_comment(func_ref, format!("{:?}", inst));
func_ref
}
pub(crate) fn lib_call(
&mut self,
name: &str,
input_tys: Vec<types::Type>,
output_tys: Vec<types::Type>,
args: &[Value],
) -> &[Value] {
let sig = Signature {
params: input_tys.iter().cloned().map(AbiParam::new).collect(),
returns: output_tys.iter().cloned().map(AbiParam::new).collect(),
call_conv: CallConv::triple_default(self.triple()),
};
let func_id = self
.cx
.module
.declare_function(&name, Linkage::Import, &sig)
.unwrap();
let func_ref = self
.cx
.module
.declare_func_in_func(func_id, &mut self.bcx.func);
let call_inst = self.bcx.ins().call(func_ref, args);
#[cfg(debug_assertions)]
{
self.add_comment(call_inst, format!("easy_call {}", name));
}
let results = self.bcx.inst_results(call_inst);
assert!(results.len() <= 2, "{}", results.len());
results
}
pub(crate) fn easy_call(
&mut self,
name: &str,
args: &[CValue<'tcx>],
return_ty: Ty<'tcx>,
) -> CValue<'tcx> {
let (input_tys, args): (Vec<_>, Vec<_>) = args
.iter()
.map(|arg| {
(
self.clif_type(arg.layout().ty).unwrap(),
arg.load_scalar(self),
)
})
.unzip();
let return_layout = self.layout_of(return_ty);
let return_tys = if let ty::Tuple(tup) = return_ty.kind() {
tup.types().map(|ty| self.clif_type(ty).unwrap()).collect()
} else {
vec![self.clif_type(return_ty).unwrap()]
};
let ret_vals = self.lib_call(name, input_tys, return_tys, &args);
match *ret_vals {
[] => CValue::by_ref(
Pointer::const_addr(self, i64::from(self.pointer_type.bytes())),
return_layout,
),
[val] => CValue::by_val(val, return_layout),
[val, extra] => CValue::by_val_pair(val, extra, return_layout),
_ => unreachable!(),
}
}
}
/// Make a [`CPlace`] capable of holding value of the specified type.
fn make_local_place<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
local: Local,
layout: TyAndLayout<'tcx>,
is_ssa: bool,
) -> CPlace<'tcx> {
let place = if is_ssa {
if let rustc_target::abi::Abi::ScalarPair(_, _) = layout.abi {
CPlace::new_var_pair(fx, local, layout)
} else {
CPlace::new_var(fx, local, layout)
}
} else {
CPlace::new_stack_slot(fx, layout)
};
#[cfg(debug_assertions)]
self::comments::add_local_place_comments(fx, place, local);
place
}
pub(crate) fn codegen_fn_prelude<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
start_block: Block,
) {
let ssa_analyzed = crate::analyze::analyze(fx);
#[cfg(debug_assertions)]
self::comments::add_args_header_comment(fx);
let ret_place = self::returning::codegen_return_param(fx, &ssa_analyzed, start_block);
assert_eq!(fx.local_map.push(ret_place), RETURN_PLACE);
// None means pass_mode == NoPass
enum ArgKind<'tcx> {
Normal(Option<CValue<'tcx>>),
Spread(Vec<Option<CValue<'tcx>>>),
}
let func_params = fx
.mir
.args_iter()
.map(|local| {
let arg_ty = fx.monomorphize(&fx.mir.local_decls[local].ty);
// Adapted from https://github.com/rust-lang/rust/blob/145155dc96757002c7b2e9de8489416e2fdbbd57/src/librustc_codegen_llvm/mir/mod.rs#L442-L482
if Some(local) == fx.mir.spread_arg {
// This argument (e.g. the last argument in the "rust-call" ABI)
// is a tuple that was spread at the ABI level and now we have
// to reconstruct it into a tuple local variable, from multiple
// individual function arguments.
let tupled_arg_tys = match arg_ty.kind() {
ty::Tuple(ref tys) => tys,
_ => bug!("spread argument isn't a tuple?! but {:?}", arg_ty),
};
let mut params = Vec::new();
for (i, arg_ty) in tupled_arg_tys.types().enumerate() {
let param = cvalue_for_param(fx, start_block, Some(local), Some(i), arg_ty);
params.push(param);
}
(local, ArgKind::Spread(params), arg_ty)
} else {
let param = cvalue_for_param(fx, start_block, Some(local), None, arg_ty);
(local, ArgKind::Normal(param), arg_ty)
}
})
.collect::<Vec<(Local, ArgKind<'tcx>, Ty<'tcx>)>>();
assert!(fx.caller_location.is_none());
if fx.instance.def.requires_caller_location(fx.tcx) {
// Store caller location for `#[track_caller]`.
fx.caller_location = Some(
cvalue_for_param(fx, start_block, None, None, fx.tcx.caller_location_ty()).unwrap(),
);
}
fx.bcx.switch_to_block(start_block);
fx.bcx.ins().nop();
#[cfg(debug_assertions)]
self::comments::add_locals_header_comment(fx);
for (local, arg_kind, ty) in func_params {
let layout = fx.layout_of(ty);
let is_ssa = ssa_analyzed[local] == crate::analyze::SsaKind::Ssa;
// While this is normally an optimization to prevent an unnecessary copy when an argument is
// not mutated by the current function, this is necessary to support unsized arguments.
if let ArgKind::Normal(Some(val)) = arg_kind {
if let Some((addr, meta)) = val.try_to_ptr() {
let local_decl = &fx.mir.local_decls[local];
// v this ! is important
let internally_mutable = !val.layout().ty.is_freeze(
fx.tcx.at(local_decl.source_info.span),
ParamEnv::reveal_all(),
);
if local_decl.mutability == mir::Mutability::Not && !internally_mutable {
// We wont mutate this argument, so it is fine to borrow the backing storage
// of this argument, to prevent a copy.
let place = if let Some(meta) = meta {
CPlace::for_ptr_with_extra(addr, meta, val.layout())
} else {
CPlace::for_ptr(addr, val.layout())
};
#[cfg(debug_assertions)]
self::comments::add_local_place_comments(fx, place, local);
assert_eq!(fx.local_map.push(place), local);
continue;
}
}
}
let place = make_local_place(fx, local, layout, is_ssa);
assert_eq!(fx.local_map.push(place), local);
match arg_kind {
ArgKind::Normal(param) => {
if let Some(param) = param {
place.write_cvalue(fx, param);
}
}
ArgKind::Spread(params) => {
for (i, param) in params.into_iter().enumerate() {
if let Some(param) = param {
place
.place_field(fx, mir::Field::new(i))
.write_cvalue(fx, param);
}
}
}
}
}
for local in fx.mir.vars_and_temps_iter() {
let ty = fx.monomorphize(&fx.mir.local_decls[local].ty);
let layout = fx.layout_of(ty);
let is_ssa = ssa_analyzed[local] == crate::analyze::SsaKind::Ssa;
let place = make_local_place(fx, local, layout, is_ssa);
assert_eq!(fx.local_map.push(place), local);
}
fx.bcx
.ins()
.jump(*fx.block_map.get(START_BLOCK).unwrap(), &[]);
}
pub(crate) fn codegen_terminator_call<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
span: Span,
current_block: Block,
func: &Operand<'tcx>,
args: &[Operand<'tcx>],
destination: Option<(Place<'tcx>, BasicBlock)>,
) {
let fn_ty = fx.monomorphize(&func.ty(fx.mir, fx.tcx));
let fn_sig = fx
.tcx
.normalize_erasing_late_bound_regions(ParamEnv::reveal_all(), &fn_ty.fn_sig(fx.tcx));
let destination = destination.map(|(place, bb)| (codegen_place(fx, place), bb));
// Handle special calls like instrinsics and empty drop glue.
let instance = if let ty::FnDef(def_id, substs) = *fn_ty.kind() {
let instance = ty::Instance::resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, substs)
.unwrap()
.unwrap()
.polymorphize(fx.tcx);
if fx.tcx.symbol_name(instance).name.starts_with("llvm.") {
crate::intrinsics::codegen_llvm_intrinsic_call(
fx,
&fx.tcx.symbol_name(instance).name,
substs,
args,
destination,
);
return;
}
match instance.def {
InstanceDef::Intrinsic(_) => {
crate::intrinsics::codegen_intrinsic_call(fx, instance, args, destination, span);
return;
}
InstanceDef::DropGlue(_, None) => {
// empty drop glue - a nop.
let (_, dest) = destination.expect("Non terminating drop_in_place_real???");
let ret_block = fx.get_block(dest);
fx.bcx.ins().jump(ret_block, &[]);
return;
}
_ => Some(instance),
}
} else {
None
};
let is_cold = instance
.map(|inst| {
fx.tcx
.codegen_fn_attrs(inst.def_id())
.flags
.contains(CodegenFnAttrFlags::COLD)
})
.unwrap_or(false);
if is_cold {
fx.cold_blocks.insert(current_block);
}
// Unpack arguments tuple for closures
let args = if fn_sig.abi == Abi::RustCall {
assert_eq!(args.len(), 2, "rust-call abi requires two arguments");
let self_arg = codegen_operand(fx, &args[0]);
let pack_arg = codegen_operand(fx, &args[1]);
let tupled_arguments = match pack_arg.layout().ty.kind() {
ty::Tuple(ref tupled_arguments) => tupled_arguments,
_ => bug!("argument to function with \"rust-call\" ABI is not a tuple"),
};
let mut args = Vec::with_capacity(1 + tupled_arguments.len());
args.push(self_arg);
for i in 0..tupled_arguments.len() {
args.push(pack_arg.value_field(fx, mir::Field::new(i)));
}
args
} else {
args.iter()
.map(|arg| codegen_operand(fx, arg))
.collect::<Vec<_>>()
};
// | indirect call target
// | | the first argument to be passed
// v v v virtual calls are special cased below
let (func_ref, first_arg, is_virtual_call) = match instance {
// Trait object call
Some(Instance {
def: InstanceDef::Virtual(_, idx),
..
}) => {
#[cfg(debug_assertions)]
{
let nop_inst = fx.bcx.ins().nop();
fx.add_comment(
nop_inst,
format!(
"virtual call; self arg pass mode: {:?}",
get_pass_mode(fx.tcx, args[0].layout())
),
);
}
let (ptr, method) = crate::vtable::get_ptr_and_method_ref(fx, args[0], idx);
(Some(method), Single(ptr), true)
}
// Normal call
Some(_) => (
None,
args.get(0)
.map(|arg| adjust_arg_for_abi(fx, *arg))
.unwrap_or(Empty),
false,
),
// Indirect call
None => {
#[cfg(debug_assertions)]
{
let nop_inst = fx.bcx.ins().nop();
fx.add_comment(nop_inst, "indirect call");
}
let func = codegen_operand(fx, func).load_scalar(fx);
(
Some(func),
args.get(0)
.map(|arg| adjust_arg_for_abi(fx, *arg))
.unwrap_or(Empty),
false,
)
}
};
let ret_place = destination.map(|(place, _)| place);
let (call_inst, call_args) =
self::returning::codegen_with_call_return_arg(fx, fn_sig, ret_place, |fx, return_ptr| {
let mut call_args: Vec<Value> = return_ptr
.into_iter()
.chain(first_arg.into_iter())
.chain(
args.into_iter()
.skip(1)
.map(|arg| adjust_arg_for_abi(fx, arg).into_iter())
.flatten(),
)
.collect::<Vec<_>>();
if instance
.map(|inst| inst.def.requires_caller_location(fx.tcx))
.unwrap_or(false)
{
// Pass the caller location for `#[track_caller]`.
let caller_location = fx.get_caller_location(span);
call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter());
}
let call_inst = if let Some(func_ref) = func_ref {
let sig = clif_sig_from_fn_sig(
fx.tcx,
fx.triple(),
fn_sig,
span,
is_virtual_call,
false, // calls through function pointers never pass the caller location
);
let sig = fx.bcx.import_signature(sig);
fx.bcx.ins().call_indirect(sig, func_ref, &call_args)
} else {
let func_ref =
fx.get_function_ref(instance.expect("non-indirect call on non-FnDef type"));
fx.bcx.ins().call(func_ref, &call_args)
};
(call_inst, call_args)
});
// FIXME find a cleaner way to support varargs
if fn_sig.c_variadic {
if fn_sig.abi != Abi::C {
fx.tcx.sess.span_fatal(
span,
&format!("Variadic call for non-C abi {:?}", fn_sig.abi),
);
}
let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap();
let abi_params = call_args
.into_iter()
.map(|arg| {
let ty = fx.bcx.func.dfg.value_type(arg);
if !ty.is_int() {
// FIXME set %al to upperbound on float args once floats are supported
fx.tcx
.sess
.span_fatal(span, &format!("Non int ty {:?} for variadic call", ty));
}
AbiParam::new(ty)
})
.collect::<Vec<AbiParam>>();
fx.bcx.func.dfg.signatures[sig_ref].params = abi_params;
}
if let Some((_, dest)) = destination {
let ret_block = fx.get_block(dest);
fx.bcx.ins().jump(ret_block, &[]);
} else {
trap_unreachable(fx, "[corruption] Diverging function returned");
}
}
pub(crate) fn codegen_drop<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
span: Span,
drop_place: CPlace<'tcx>,
) {
let ty = drop_place.layout().ty;
let drop_fn = Instance::resolve_drop_in_place(fx.tcx, ty).polymorphize(fx.tcx);
if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def {
// we don't actually need to drop anything
} else {
let drop_fn_ty = drop_fn.ty(fx.tcx, ParamEnv::reveal_all());
let fn_sig = fx.tcx.normalize_erasing_late_bound_regions(
ParamEnv::reveal_all(),
&drop_fn_ty.fn_sig(fx.tcx),
);
assert_eq!(fn_sig.output(), fx.tcx.mk_unit());
match ty.kind() {
ty::Dynamic(..) => {
let (ptr, vtable) = drop_place.to_ptr_maybe_unsized();
let ptr = ptr.get_addr(fx);
let drop_fn = crate::vtable::drop_fn_of_obj(fx, vtable.unwrap());
let sig = clif_sig_from_fn_sig(
fx.tcx,
fx.triple(),
fn_sig,
span,
true,
false, // `drop_in_place` is never `#[track_caller]`
);
let sig = fx.bcx.import_signature(sig);
fx.bcx.ins().call_indirect(sig, drop_fn, &[ptr]);
}
_ => {
assert!(!matches!(drop_fn.def, InstanceDef::Virtual(_, _)));
let arg_value = drop_place.place_ref(
fx,
fx.layout_of(fx.tcx.mk_ref(
&ty::RegionKind::ReErased,
TypeAndMut {
ty,
mutbl: crate::rustc_hir::Mutability::Mut,
},
)),
);
let arg_value = adjust_arg_for_abi(fx, arg_value);
let mut call_args: Vec<Value> = arg_value.into_iter().collect::<Vec<_>>();
if drop_fn.def.requires_caller_location(fx.tcx) {
// Pass the caller location for `#[track_caller]`.
let caller_location = fx.get_caller_location(span);
call_args.extend(adjust_arg_for_abi(fx, caller_location).into_iter());
}
let func_ref = fx.get_function_ref(drop_fn);
fx.bcx.ins().call(func_ref, &call_args);
}
}
}
}

View File

@ -0,0 +1,188 @@
//! Argument passing
use crate::prelude::*;
pub(super) use EmptySinglePair::*;
#[derive(Copy, Clone, Debug)]
pub(super) enum PassMode {
NoPass,
ByVal(Type),
ByValPair(Type, Type),
ByRef { size: Option<Size> },
}
#[derive(Copy, Clone, Debug)]
pub(super) enum EmptySinglePair<T> {
Empty,
Single(T),
Pair(T, T),
}
impl<T> EmptySinglePair<T> {
pub(super) fn into_iter(self) -> EmptySinglePairIter<T> {
EmptySinglePairIter(self)
}
pub(super) fn map<U>(self, mut f: impl FnMut(T) -> U) -> EmptySinglePair<U> {
match self {
Empty => Empty,
Single(v) => Single(f(v)),
Pair(a, b) => Pair(f(a), f(b)),
}
}
}
pub(super) struct EmptySinglePairIter<T>(EmptySinglePair<T>);
impl<T> Iterator for EmptySinglePairIter<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
match std::mem::replace(&mut self.0, Empty) {
Empty => None,
Single(v) => Some(v),
Pair(a, b) => {
self.0 = Single(b);
Some(a)
}
}
}
}
impl<T: std::fmt::Debug> EmptySinglePair<T> {
pub(super) fn assert_single(self) -> T {
match self {
Single(v) => v,
_ => panic!("Called assert_single on {:?}", self),
}
}
pub(super) fn assert_pair(self) -> (T, T) {
match self {
Pair(a, b) => (a, b),
_ => panic!("Called assert_pair on {:?}", self),
}
}
}
impl PassMode {
pub(super) fn get_param_ty(self, tcx: TyCtxt<'_>) -> EmptySinglePair<Type> {
match self {
PassMode::NoPass => Empty,
PassMode::ByVal(clif_type) => Single(clif_type),
PassMode::ByValPair(a, b) => Pair(a, b),
PassMode::ByRef { size: Some(_) } => Single(pointer_ty(tcx)),
PassMode::ByRef { size: None } => Pair(pointer_ty(tcx), pointer_ty(tcx)),
}
}
}
pub(super) fn get_pass_mode<'tcx>(tcx: TyCtxt<'tcx>, layout: TyAndLayout<'tcx>) -> PassMode {
if layout.is_zst() {
// WARNING zst arguments must never be passed, as that will break CastKind::ClosureFnPointer
PassMode::NoPass
} else {
match &layout.abi {
Abi::Uninhabited => PassMode::NoPass,
Abi::Scalar(scalar) => PassMode::ByVal(scalar_to_clif_type(tcx, scalar.clone())),
Abi::ScalarPair(a, b) => {
let a = scalar_to_clif_type(tcx, a.clone());
let b = scalar_to_clif_type(tcx, b.clone());
if a == types::I128 && b == types::I128 {
// Returning (i128, i128) by-val-pair would take 4 regs, while only 3 are
// available on x86_64. Cranelift gets confused when too many return params
// are used.
PassMode::ByRef {
size: Some(layout.size),
}
} else {
PassMode::ByValPair(a, b)
}
}
// FIXME implement Vector Abi in a cg_llvm compatible way
Abi::Vector { .. } => {
if let Some(vector_ty) = crate::intrinsics::clif_vector_type(tcx, layout) {
PassMode::ByVal(vector_ty)
} else {
PassMode::ByRef {
size: Some(layout.size),
}
}
}
Abi::Aggregate { sized: true } => PassMode::ByRef {
size: Some(layout.size),
},
Abi::Aggregate { sized: false } => PassMode::ByRef { size: None },
}
}
}
/// Get a set of values to be passed as function arguments.
pub(super) fn adjust_arg_for_abi<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
arg: CValue<'tcx>,
) -> EmptySinglePair<Value> {
match get_pass_mode(fx.tcx, arg.layout()) {
PassMode::NoPass => Empty,
PassMode::ByVal(_) => Single(arg.load_scalar(fx)),
PassMode::ByValPair(_, _) => {
let (a, b) = arg.load_scalar_pair(fx);
Pair(a, b)
}
PassMode::ByRef { size: _ } => match arg.force_stack(fx) {
(ptr, None) => Single(ptr.get_addr(fx)),
(ptr, Some(meta)) => Pair(ptr.get_addr(fx), meta),
},
}
}
/// Create a [`CValue`] containing the value of a function parameter adding clif function parameters
/// as necessary.
pub(super) fn cvalue_for_param<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
start_block: Block,
#[cfg_attr(not(debug_assertions), allow(unused_variables))] local: Option<mir::Local>,
#[cfg_attr(not(debug_assertions), allow(unused_variables))] local_field: Option<usize>,
arg_ty: Ty<'tcx>,
) -> Option<CValue<'tcx>> {
let layout = fx.layout_of(arg_ty);
let pass_mode = get_pass_mode(fx.tcx, layout);
if let PassMode::NoPass = pass_mode {
return None;
}
let clif_types = pass_mode.get_param_ty(fx.tcx);
let block_params = clif_types.map(|t| fx.bcx.append_block_param(start_block, t));
#[cfg(debug_assertions)]
crate::abi::comments::add_arg_comment(
fx,
"arg",
local,
local_field,
block_params,
pass_mode,
arg_ty,
);
match pass_mode {
PassMode::NoPass => unreachable!(),
PassMode::ByVal(_) => Some(CValue::by_val(block_params.assert_single(), layout)),
PassMode::ByValPair(_, _) => {
let (a, b) = block_params.assert_pair();
Some(CValue::by_val_pair(a, b, layout))
}
PassMode::ByRef { size: Some(_) } => Some(CValue::by_ref(
Pointer::new(block_params.assert_single()),
layout,
)),
PassMode::ByRef { size: None } => {
let (ptr, meta) = block_params.assert_pair();
Some(CValue::by_ref_unsized(Pointer::new(ptr), meta, layout))
}
}
}

View File

@ -0,0 +1,130 @@
//! Return value handling
use crate::abi::pass_mode::*;
use crate::prelude::*;
fn return_layout<'a, 'tcx>(fx: &mut FunctionCx<'a, 'tcx, impl Module>) -> TyAndLayout<'tcx> {
fx.layout_of(fx.monomorphize(&fx.mir.local_decls[RETURN_PLACE].ty))
}
/// Can the given type be returned into an ssa var or does it need to be returned on the stack.
pub(crate) fn can_return_to_ssa_var<'tcx>(
tcx: TyCtxt<'tcx>,
dest_layout: TyAndLayout<'tcx>,
) -> bool {
match get_pass_mode(tcx, dest_layout) {
PassMode::NoPass | PassMode::ByVal(_) | PassMode::ByValPair(_, _) => true,
// FIXME Make it possible to return ByRef to an ssa var.
PassMode::ByRef { size: _ } => false,
}
}
/// Return a place where the return value of the current function can be written to. If necessary
/// this adds an extra parameter pointing to where the return value needs to be stored.
pub(super) fn codegen_return_param<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
ssa_analyzed: &rustc_index::vec::IndexVec<Local, crate::analyze::SsaKind>,
start_block: Block,
) -> CPlace<'tcx> {
let ret_layout = return_layout(fx);
let ret_pass_mode = get_pass_mode(fx.tcx, ret_layout);
let (ret_place, ret_param) = match ret_pass_mode {
PassMode::NoPass => (CPlace::no_place(ret_layout), Empty),
PassMode::ByVal(_) | PassMode::ByValPair(_, _) => {
let is_ssa = ssa_analyzed[RETURN_PLACE] == crate::analyze::SsaKind::Ssa;
(
super::make_local_place(fx, RETURN_PLACE, ret_layout, is_ssa),
Empty,
)
}
PassMode::ByRef { size: Some(_) } => {
let ret_param = fx.bcx.append_block_param(start_block, fx.pointer_type);
(
CPlace::for_ptr(Pointer::new(ret_param), ret_layout),
Single(ret_param),
)
}
PassMode::ByRef { size: None } => todo!(),
};
#[cfg(not(debug_assertions))]
let _ = ret_param;
#[cfg(debug_assertions)]
crate::abi::comments::add_arg_comment(
fx,
"ret",
Some(RETURN_PLACE),
None,
ret_param,
ret_pass_mode,
ret_layout.ty,
);
ret_place
}
/// Invokes the closure with if necessary a value representing the return pointer. When the closure
/// returns the call return value(s) if any are written to the correct place.
pub(super) fn codegen_with_call_return_arg<'tcx, M: Module, T>(
fx: &mut FunctionCx<'_, 'tcx, M>,
fn_sig: FnSig<'tcx>,
ret_place: Option<CPlace<'tcx>>,
f: impl FnOnce(&mut FunctionCx<'_, 'tcx, M>, Option<Value>) -> (Inst, T),
) -> (Inst, T) {
let ret_layout = fx.layout_of(fn_sig.output());
let output_pass_mode = get_pass_mode(fx.tcx, ret_layout);
let return_ptr = match output_pass_mode {
PassMode::NoPass => None,
PassMode::ByRef { size: Some(_) } => match ret_place {
Some(ret_place) => Some(ret_place.to_ptr().get_addr(fx)),
None => Some(fx.bcx.ins().iconst(fx.pointer_type, 43)), // FIXME allocate temp stack slot
},
PassMode::ByRef { size: None } => todo!(),
PassMode::ByVal(_) | PassMode::ByValPair(_, _) => None,
};
let (call_inst, meta) = f(fx, return_ptr);
match output_pass_mode {
PassMode::NoPass => {}
PassMode::ByVal(_) => {
if let Some(ret_place) = ret_place {
let ret_val = fx.bcx.inst_results(call_inst)[0];
ret_place.write_cvalue(fx, CValue::by_val(ret_val, ret_layout));
}
}
PassMode::ByValPair(_, _) => {
if let Some(ret_place) = ret_place {
let ret_val_a = fx.bcx.inst_results(call_inst)[0];
let ret_val_b = fx.bcx.inst_results(call_inst)[1];
ret_place.write_cvalue(fx, CValue::by_val_pair(ret_val_a, ret_val_b, ret_layout));
}
}
PassMode::ByRef { size: Some(_) } => {}
PassMode::ByRef { size: None } => todo!(),
}
(call_inst, meta)
}
/// Codegen a return instruction with the right return value(s) if any.
pub(crate) fn codegen_return(fx: &mut FunctionCx<'_, '_, impl Module>) {
match get_pass_mode(fx.tcx, return_layout(fx)) {
PassMode::NoPass | PassMode::ByRef { size: Some(_) } => {
fx.bcx.ins().return_(&[]);
}
PassMode::ByRef { size: None } => todo!(),
PassMode::ByVal(_) => {
let place = fx.get_local_place(RETURN_PLACE);
let ret_val = place.to_cvalue(fx).load_scalar(fx);
fx.bcx.ins().return_(&[ret_val]);
}
PassMode::ByValPair(_, _) => {
let place = fx.get_local_place(RETURN_PLACE);
let (ret_val_a, ret_val_b) = place.to_cvalue(fx).load_scalar_pair(fx);
fx.bcx.ins().return_(&[ret_val_a, ret_val_b]);
}
}
}

View File

@ -0,0 +1,153 @@
//! Allocator shim
// Adapted from rustc
use crate::prelude::*;
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
use rustc_span::symbol::sym;
/// Returns whether an allocator shim was created
pub(crate) fn codegen(
tcx: TyCtxt<'_>,
module: &mut impl Module,
unwind_context: &mut UnwindContext<'_>,
) -> bool {
let any_dynamic_crate = tcx.dependency_formats(LOCAL_CRATE).iter().any(|(_, list)| {
use rustc_middle::middle::dependency_format::Linkage;
list.iter().any(|&linkage| linkage == Linkage::Dynamic)
});
if any_dynamic_crate {
false
} else if let Some(kind) = tcx.allocator_kind() {
codegen_inner(module, unwind_context, kind);
true
} else {
false
}
}
fn codegen_inner(
module: &mut impl Module,
unwind_context: &mut UnwindContext<'_>,
kind: AllocatorKind,
) {
let usize_ty = module.target_config().pointer_type();
for method in ALLOCATOR_METHODS {
let mut arg_tys = Vec::with_capacity(method.inputs.len());
for ty in method.inputs.iter() {
match *ty {
AllocatorTy::Layout => {
arg_tys.push(usize_ty); // size
arg_tys.push(usize_ty); // align
}
AllocatorTy::Ptr => arg_tys.push(usize_ty),
AllocatorTy::Usize => arg_tys.push(usize_ty),
AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"),
}
}
let output = match method.output {
AllocatorTy::ResultPtr => Some(usize_ty),
AllocatorTy::Unit => None,
AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => {
panic!("invalid allocator output")
}
};
let sig = Signature {
call_conv: CallConv::triple_default(module.isa().triple()),
params: arg_tys.iter().cloned().map(AbiParam::new).collect(),
returns: output.into_iter().map(AbiParam::new).collect(),
};
let caller_name = format!("__rust_{}", method.name);
let callee_name = kind.fn_name(method.name);
//eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns);
let func_id = module
.declare_function(&caller_name, Linkage::Export, &sig)
.unwrap();
let callee_func_id = module
.declare_function(&callee_name, Linkage::Import, &sig)
.unwrap();
let mut ctx = Context::new();
ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig.clone());
{
let mut func_ctx = FunctionBuilderContext::new();
let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = bcx.create_block();
bcx.switch_to_block(block);
let args = arg_tys
.into_iter()
.map(|ty| bcx.append_block_param(block, ty))
.collect::<Vec<Value>>();
let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func);
let call_inst = bcx.ins().call(callee_func_ref, &args);
let results = bcx.inst_results(call_inst).to_vec(); // Clone to prevent borrow error
bcx.ins().return_(&results);
bcx.seal_all_blocks();
bcx.finalize();
}
module
.define_function(
func_id,
&mut ctx,
&mut cranelift_codegen::binemit::NullTrapSink {},
)
.unwrap();
unwind_context.add_function(func_id, &ctx, module.isa());
}
let sig = Signature {
call_conv: CallConv::triple_default(module.isa().triple()),
params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)],
returns: vec![],
};
let callee_name = kind.fn_name(sym::oom);
//eprintln!("Codegen allocator shim {} -> {} ({:?} -> {:?})", caller_name, callee_name, sig.params, sig.returns);
let func_id = module
.declare_function("__rust_alloc_error_handler", Linkage::Export, &sig)
.unwrap();
let callee_func_id = module
.declare_function(&callee_name, Linkage::Import, &sig)
.unwrap();
let mut ctx = Context::new();
ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig);
{
let mut func_ctx = FunctionBuilderContext::new();
let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = bcx.create_block();
bcx.switch_to_block(block);
let args = (&[usize_ty, usize_ty])
.iter()
.map(|&ty| bcx.append_block_param(block, ty))
.collect::<Vec<Value>>();
let callee_func_ref = module.declare_func_in_func(callee_func_id, &mut bcx.func);
bcx.ins().call(callee_func_ref, &args);
bcx.ins().trap(TrapCode::UnreachableCodeReached);
bcx.seal_all_blocks();
bcx.finalize();
}
module
.define_function(
func_id,
&mut ctx,
&mut cranelift_codegen::binemit::NullTrapSink {},
)
.unwrap();
unwind_context.add_function(func_id, &ctx, module.isa());
}

View File

@ -0,0 +1,61 @@
//! SSA analysis
use crate::prelude::*;
use rustc_index::vec::IndexVec;
use rustc_middle::mir::StatementKind::*;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) enum SsaKind {
NotSsa,
Ssa,
}
pub(crate) fn analyze(fx: &FunctionCx<'_, '_, impl Module>) -> IndexVec<Local, SsaKind> {
let mut flag_map = fx
.mir
.local_decls
.iter()
.map(|local_decl| {
let ty = fx.monomorphize(&local_decl.ty);
if fx.clif_type(ty).is_some() || fx.clif_pair_type(ty).is_some() {
SsaKind::Ssa
} else {
SsaKind::NotSsa
}
})
.collect::<IndexVec<Local, SsaKind>>();
for bb in fx.mir.basic_blocks().iter() {
for stmt in bb.statements.iter() {
match &stmt.kind {
Assign(place_and_rval) => match &place_and_rval.1 {
Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
not_ssa(&mut flag_map, place.local)
}
_ => {}
},
_ => {}
}
}
match &bb.terminator().kind {
TerminatorKind::Call { destination, .. } => {
if let Some((dest_place, _dest_bb)) = destination {
let dest_layout = fx
.layout_of(fx.monomorphize(&dest_place.ty(&fx.mir.local_decls, fx.tcx).ty));
if !crate::abi::can_return_to_ssa_var(fx.tcx, dest_layout) {
not_ssa(&mut flag_map, dest_place.local)
}
}
}
_ => {}
}
}
flag_map
}
fn not_ssa(flag_map: &mut IndexVec<Local, SsaKind>, local: Local) {
flag_map[local] = SsaKind::NotSsa;
}

View File

@ -0,0 +1,309 @@
//! Creation of ar archives like for the lib and staticlib crate type
use std::collections::BTreeMap;
use std::fs::File;
use std::path::{Path, PathBuf};
use rustc_codegen_ssa::back::archive::{find_library, ArchiveBuilder};
use rustc_codegen_ssa::METADATA_FILENAME;
use rustc_session::Session;
use object::{Object, SymbolKind};
#[derive(Debug)]
enum ArchiveEntry {
FromArchive {
archive_index: usize,
entry_index: usize,
},
File(PathBuf),
}
pub(crate) struct ArArchiveBuilder<'a> {
sess: &'a Session,
dst: PathBuf,
lib_search_paths: Vec<PathBuf>,
use_gnu_style_archive: bool,
no_builtin_ranlib: bool,
src_archives: Vec<(PathBuf, ar::Archive<File>)>,
// Don't use `HashMap` here, as the order is important. `rust.metadata.bin` must always be at
// the end of an archive for linkers to not get confused.
entries: Vec<(String, ArchiveEntry)>,
update_symbols: bool,
}
impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
fn new(sess: &'a Session, output: &Path, input: Option<&Path>) -> Self {
use rustc_codegen_ssa::back::link::archive_search_paths;
let (src_archives, entries) = if let Some(input) = input {
let mut archive = ar::Archive::new(File::open(input).unwrap());
let mut entries = Vec::new();
let mut i = 0;
while let Some(entry) = archive.next_entry() {
let entry = entry.unwrap();
entries.push((
String::from_utf8(entry.header().identifier().to_vec()).unwrap(),
ArchiveEntry::FromArchive {
archive_index: 0,
entry_index: i,
},
));
i += 1;
}
(vec![(input.to_owned(), archive)], entries)
} else {
(vec![], Vec::new())
};
ArArchiveBuilder {
sess,
dst: output.to_path_buf(),
lib_search_paths: archive_search_paths(sess),
use_gnu_style_archive: sess.target.archive_format == "gnu",
// FIXME fix builtin ranlib on macOS
no_builtin_ranlib: sess.target.is_like_osx,
src_archives,
entries,
update_symbols: false,
}
}
fn src_files(&mut self) -> Vec<String> {
self.entries.iter().map(|(name, _)| name.clone()).collect()
}
fn remove_file(&mut self, name: &str) {
let index = self
.entries
.iter()
.position(|(entry_name, _)| entry_name == name)
.expect("Tried to remove file not existing in src archive");
self.entries.remove(index);
}
fn add_file(&mut self, file: &Path) {
self.entries.push((
file.file_name().unwrap().to_str().unwrap().to_string(),
ArchiveEntry::File(file.to_owned()),
));
}
fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) {
let location = find_library(name, &self.lib_search_paths, self.sess);
self.add_archive(location.clone(), |_| false)
.unwrap_or_else(|e| {
panic!(
"failed to add native library {}: {}",
location.to_string_lossy(),
e
);
});
}
fn add_rlib(
&mut self,
rlib: &Path,
name: &str,
lto: bool,
skip_objects: bool,
) -> std::io::Result<()> {
let obj_start = name.to_owned();
self.add_archive(rlib.to_owned(), move |fname: &str| {
// Ignore metadata files, no matter the name.
if fname == METADATA_FILENAME {
return true;
}
// Don't include Rust objects if LTO is enabled
if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") {
return true;
}
// Otherwise if this is *not* a rust object and we're skipping
// objects then skip this file
if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) {
return true;
}
// ok, don't skip this
false
})
}
fn update_symbols(&mut self) {
self.update_symbols = true;
}
fn build(mut self) {
enum BuilderKind {
Bsd(ar::Builder<File>),
Gnu(ar::GnuBuilder<File>),
}
let sess = self.sess;
let mut symbol_table = BTreeMap::new();
let mut entries = Vec::new();
for (entry_name, entry) in self.entries {
// FIXME only read the symbol table of the object files to avoid having to keep all
// object files in memory at once, or read them twice.
let data = match entry {
ArchiveEntry::FromArchive {
archive_index,
entry_index,
} => {
// FIXME read symbols from symtab
use std::io::Read;
let (ref _src_archive_path, ref mut src_archive) =
self.src_archives[archive_index];
let mut entry = src_archive.jump_to_entry(entry_index).unwrap();
let mut data = Vec::new();
entry.read_to_end(&mut data).unwrap();
data
}
ArchiveEntry::File(file) => std::fs::read(file).unwrap_or_else(|err| {
sess.fatal(&format!(
"error while reading object file during archive building: {}",
err
));
}),
};
if !self.no_builtin_ranlib {
match object::File::parse(&data) {
Ok(object) => {
symbol_table.insert(
entry_name.as_bytes().to_vec(),
object
.symbols()
.filter_map(|(_index, symbol)| {
if symbol.is_undefined()
|| symbol.is_local()
|| symbol.kind() != SymbolKind::Data
&& symbol.kind() != SymbolKind::Text
&& symbol.kind() != SymbolKind::Tls
{
None
} else {
symbol.name().map(|name| name.as_bytes().to_vec())
}
})
.collect::<Vec<_>>(),
);
}
Err(err) => {
let err = err.to_string();
if err == "Unknown file magic" {
// Not an object file; skip it.
} else {
sess.fatal(&format!(
"error parsing `{}` during archive creation: {}",
entry_name, err
));
}
}
}
}
entries.push((entry_name, data));
}
let mut builder = if self.use_gnu_style_archive {
BuilderKind::Gnu(
ar::GnuBuilder::new(
File::create(&self.dst).unwrap_or_else(|err| {
sess.fatal(&format!(
"error opening destination during archive building: {}",
err
));
}),
entries
.iter()
.map(|(name, _)| name.as_bytes().to_vec())
.collect(),
ar::GnuSymbolTableFormat::Size32,
symbol_table,
)
.unwrap(),
)
} else {
BuilderKind::Bsd(
ar::Builder::new(
File::create(&self.dst).unwrap_or_else(|err| {
sess.fatal(&format!(
"error opening destination during archive building: {}",
err
));
}),
symbol_table,
)
.unwrap(),
)
};
// Add all files
for (entry_name, data) in entries.into_iter() {
let header = ar::Header::new(entry_name.into_bytes(), data.len() as u64);
match builder {
BuilderKind::Bsd(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
BuilderKind::Gnu(ref mut builder) => builder.append(&header, &mut &*data).unwrap(),
}
}
// Finalize archive
std::mem::drop(builder);
if self.no_builtin_ranlib {
let ranlib = crate::toolchain::get_toolchain_binary(self.sess, "ranlib");
// Run ranlib to be able to link the archive
let status = std::process::Command::new(ranlib)
.arg(self.dst)
.status()
.expect("Couldn't run ranlib");
if !status.success() {
self.sess
.fatal(&format!("Ranlib exited with code {:?}", status.code()));
}
}
}
}
impl<'a> ArArchiveBuilder<'a> {
fn add_archive<F>(&mut self, archive_path: PathBuf, mut skip: F) -> std::io::Result<()>
where
F: FnMut(&str) -> bool + 'static,
{
let mut archive = ar::Archive::new(std::fs::File::open(&archive_path)?);
let archive_index = self.src_archives.len();
let mut i = 0;
while let Some(entry) = archive.next_entry() {
let entry = entry?;
let file_name = String::from_utf8(entry.header().identifier().to_vec())
.map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidData, err))?;
if !skip(&file_name) {
self.entries.push((
file_name,
ArchiveEntry::FromArchive {
archive_index,
entry_index: i,
},
));
}
i += 1;
}
self.src_archives.push((archive_path, archive));
Ok(())
}
}

View File

@ -0,0 +1,186 @@
//! Atomic intrinsics are implemented using a global lock for now, as Cranelift doesn't support
//! atomic operations yet.
// FIXME implement atomic instructions in Cranelift.
use crate::prelude::*;
#[cfg(all(feature = "jit", unix))]
#[no_mangle]
static mut __cg_clif_global_atomic_mutex: libc::pthread_mutex_t =
libc::PTHREAD_MUTEX_INITIALIZER;
pub(crate) fn init_global_lock(
module: &mut impl Module,
bcx: &mut FunctionBuilder<'_>,
use_jit: bool,
) {
if use_jit {
// When using JIT, dylibs won't find the __cg_clif_global_atomic_mutex data object defined here,
// so instead we define it in the cg_clif dylib.
return;
}
let mut data_ctx = DataContext::new();
data_ctx.define_zeroinit(1024); // 1024 bytes should be big enough on all platforms.
data_ctx.set_align(16);
let atomic_mutex = module
.declare_data(
"__cg_clif_global_atomic_mutex",
Linkage::Export,
true,
false,
)
.unwrap();
module.define_data(atomic_mutex, &data_ctx).unwrap();
let pthread_mutex_init = module
.declare_function(
"pthread_mutex_init",
Linkage::Import,
&cranelift_codegen::ir::Signature {
call_conv: module.target_config().default_call_conv,
params: vec![
AbiParam::new(
module.target_config().pointer_type(), /* *mut pthread_mutex_t */
),
AbiParam::new(
module.target_config().pointer_type(), /* *const pthread_mutex_attr_t */
),
],
returns: vec![AbiParam::new(types::I32 /* c_int */)],
},
)
.unwrap();
let pthread_mutex_init = module.declare_func_in_func(pthread_mutex_init, bcx.func);
let atomic_mutex = module.declare_data_in_func(atomic_mutex, bcx.func);
let atomic_mutex = bcx
.ins()
.global_value(module.target_config().pointer_type(), atomic_mutex);
let nullptr = bcx.ins().iconst(module.target_config().pointer_type(), 0);
bcx.ins().call(pthread_mutex_init, &[atomic_mutex, nullptr]);
}
pub(crate) fn init_global_lock_constructor(
module: &mut impl Module,
constructor_name: &str,
) -> FuncId {
let sig = Signature::new(CallConv::SystemV);
let init_func_id = module
.declare_function(constructor_name, Linkage::Export, &sig)
.unwrap();
let mut ctx = Context::new();
ctx.func = Function::with_name_signature(ExternalName::user(0, 0), sig);
{
let mut func_ctx = FunctionBuilderContext::new();
let mut bcx = FunctionBuilder::new(&mut ctx.func, &mut func_ctx);
let block = bcx.create_block();
bcx.switch_to_block(block);
crate::atomic_shim::init_global_lock(module, &mut bcx, false);
bcx.ins().return_(&[]);
bcx.seal_all_blocks();
bcx.finalize();
}
module
.define_function(
init_func_id,
&mut ctx,
&mut cranelift_codegen::binemit::NullTrapSink {},
)
.unwrap();
init_func_id
}
pub(crate) fn lock_global_lock(fx: &mut FunctionCx<'_, '_, impl Module>) {
let atomic_mutex = fx
.cx
.module
.declare_data(
"__cg_clif_global_atomic_mutex",
Linkage::Import,
true,
false,
)
.unwrap();
let pthread_mutex_lock = fx
.cx
.module
.declare_function(
"pthread_mutex_lock",
Linkage::Import,
&cranelift_codegen::ir::Signature {
call_conv: fx.cx.module.target_config().default_call_conv,
params: vec![AbiParam::new(
fx.cx.module.target_config().pointer_type(), /* *mut pthread_mutex_t */
)],
returns: vec![AbiParam::new(types::I32 /* c_int */)],
},
)
.unwrap();
let pthread_mutex_lock = fx
.cx
.module
.declare_func_in_func(pthread_mutex_lock, fx.bcx.func);
let atomic_mutex = fx.cx.module.declare_data_in_func(atomic_mutex, fx.bcx.func);
let atomic_mutex = fx
.bcx
.ins()
.global_value(fx.cx.module.target_config().pointer_type(), atomic_mutex);
fx.bcx.ins().call(pthread_mutex_lock, &[atomic_mutex]);
}
pub(crate) fn unlock_global_lock(fx: &mut FunctionCx<'_, '_, impl Module>) {
let atomic_mutex = fx
.cx
.module
.declare_data(
"__cg_clif_global_atomic_mutex",
Linkage::Import,
true,
false,
)
.unwrap();
let pthread_mutex_unlock = fx
.cx
.module
.declare_function(
"pthread_mutex_unlock",
Linkage::Import,
&cranelift_codegen::ir::Signature {
call_conv: fx.cx.module.target_config().default_call_conv,
params: vec![AbiParam::new(
fx.cx.module.target_config().pointer_type(), /* *mut pthread_mutex_t */
)],
returns: vec![AbiParam::new(types::I32 /* c_int */)],
},
)
.unwrap();
let pthread_mutex_unlock = fx
.cx
.module
.declare_func_in_func(pthread_mutex_unlock, fx.bcx.func);
let atomic_mutex = fx.cx.module.declare_data_in_func(atomic_mutex, fx.bcx.func);
let atomic_mutex = fx
.bcx
.ins()
.global_value(fx.cx.module.target_config().pointer_type(), atomic_mutex);
fx.bcx.ins().call(pthread_mutex_unlock, &[atomic_mutex]);
}

View File

@ -0,0 +1,206 @@
//! Abstraction around the object writing crate
use std::convert::{TryFrom, TryInto};
use rustc_data_structures::fx::FxHashMap;
use rustc_session::Session;
use cranelift_module::FuncId;
use object::write::*;
use object::{RelocationEncoding, RelocationKind, SectionKind, SymbolFlags};
use cranelift_object::{ObjectBuilder, ObjectModule, ObjectProduct};
use gimli::SectionId;
use crate::debuginfo::{DebugReloc, DebugRelocName};
pub(crate) trait WriteMetadata {
fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, is_like_osx: bool);
}
impl WriteMetadata for object::write::Object {
fn add_rustc_section(&mut self, symbol_name: String, data: Vec<u8>, _is_like_osx: bool) {
let segment = self
.segment_name(object::write::StandardSegment::Data)
.to_vec();
let section_id = self.add_section(segment, b".rustc".to_vec(), object::SectionKind::Data);
let offset = self.append_section_data(section_id, &data, 1);
// For MachO and probably PE this is necessary to prevent the linker from throwing away the
// .rustc section. For ELF this isn't necessary, but it also doesn't harm.
self.add_symbol(object::write::Symbol {
name: symbol_name.into_bytes(),
value: offset,
size: data.len() as u64,
kind: object::SymbolKind::Data,
scope: object::SymbolScope::Dynamic,
weak: false,
section: SymbolSection::Section(section_id),
flags: SymbolFlags::None,
});
}
}
pub(crate) trait WriteDebugInfo {
type SectionId: Copy;
fn add_debug_section(&mut self, name: SectionId, data: Vec<u8>) -> Self::SectionId;
fn add_debug_reloc(
&mut self,
section_map: &FxHashMap<SectionId, Self::SectionId>,
from: &Self::SectionId,
reloc: &DebugReloc,
);
}
impl WriteDebugInfo for ObjectProduct {
type SectionId = (object::write::SectionId, object::write::SymbolId);
fn add_debug_section(
&mut self,
id: SectionId,
data: Vec<u8>,
) -> (object::write::SectionId, object::write::SymbolId) {
let name = if self.object.format() == object::BinaryFormat::MachO {
id.name().replace('.', "__") // machO expects __debug_info instead of .debug_info
} else {
id.name().to_string()
}
.into_bytes();
let segment = self.object.segment_name(StandardSegment::Debug).to_vec();
// FIXME use SHT_X86_64_UNWIND for .eh_frame
let section_id = self.object.add_section(
segment,
name,
if id == SectionId::EhFrame {
SectionKind::ReadOnlyData
} else {
SectionKind::Debug
},
);
self.object
.section_mut(section_id)
.set_data(data, if id == SectionId::EhFrame { 8 } else { 1 });
let symbol_id = self.object.section_symbol(section_id);
(section_id, symbol_id)
}
fn add_debug_reloc(
&mut self,
section_map: &FxHashMap<SectionId, Self::SectionId>,
from: &Self::SectionId,
reloc: &DebugReloc,
) {
let (symbol, symbol_offset) = match reloc.name {
DebugRelocName::Section(id) => (section_map.get(&id).unwrap().1, 0),
DebugRelocName::Symbol(id) => {
let symbol_id = self.function_symbol(FuncId::from_u32(id.try_into().unwrap()));
self.object
.symbol_section_and_offset(symbol_id)
.expect("Debug reloc for undef sym???")
}
};
self.object
.add_relocation(
from.0,
Relocation {
offset: u64::from(reloc.offset),
symbol,
kind: reloc.kind,
encoding: RelocationEncoding::Generic,
size: reloc.size * 8,
addend: i64::try_from(symbol_offset).unwrap() + reloc.addend,
},
)
.unwrap();
}
}
// FIXME remove once atomic instructions are implemented in Cranelift.
pub(crate) trait AddConstructor {
fn add_constructor(&mut self, func_id: FuncId);
}
impl AddConstructor for ObjectProduct {
fn add_constructor(&mut self, func_id: FuncId) {
let symbol = self.function_symbol(func_id);
let segment = self
.object
.segment_name(object::write::StandardSegment::Data);
let init_array_section =
self.object
.add_section(segment.to_vec(), b".init_array".to_vec(), SectionKind::Data);
let address_size = self
.object
.architecture()
.address_size()
.expect("address_size must be known")
.bytes();
self.object.append_section_data(
init_array_section,
&std::iter::repeat(0)
.take(address_size.into())
.collect::<Vec<u8>>(),
8,
);
self.object
.add_relocation(
init_array_section,
object::write::Relocation {
offset: 0,
size: address_size * 8,
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
symbol,
addend: 0,
},
)
.unwrap();
}
}
pub(crate) fn with_object(sess: &Session, name: &str, f: impl FnOnce(&mut Object)) -> Vec<u8> {
let triple = crate::build_isa(sess, true).triple().clone();
let binary_format = match triple.binary_format {
target_lexicon::BinaryFormat::Elf => object::BinaryFormat::Elf,
target_lexicon::BinaryFormat::Coff => object::BinaryFormat::Coff,
target_lexicon::BinaryFormat::Macho => object::BinaryFormat::MachO,
binary_format => sess.fatal(&format!("binary format {} is unsupported", binary_format)),
};
let architecture = match triple.architecture {
target_lexicon::Architecture::X86_32(_) => object::Architecture::I386,
target_lexicon::Architecture::X86_64 => object::Architecture::X86_64,
target_lexicon::Architecture::Arm(_) => object::Architecture::Arm,
target_lexicon::Architecture::Aarch64(_) => object::Architecture::Aarch64,
architecture => sess.fatal(&format!(
"target architecture {:?} is unsupported",
architecture,
)),
};
let endian = match triple.endianness().unwrap() {
target_lexicon::Endianness::Little => object::Endianness::Little,
target_lexicon::Endianness::Big => object::Endianness::Big,
};
let mut metadata_object = object::write::Object::new(binary_format, architecture, endian);
metadata_object.add_file_symbol(name.as_bytes().to_vec());
f(&mut metadata_object);
metadata_object.write().unwrap()
}
pub(crate) fn make_module(sess: &Session, name: String) -> ObjectModule {
let mut builder = ObjectBuilder::new(
crate::build_isa(sess, true),
name + ".o",
cranelift_module::default_libcall_names(),
)
.unwrap();
// Unlike cg_llvm, cg_clif defaults to disabling -Zfunction-sections. For cg_llvm binary size
// is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
// can easily double the amount of time necessary to perform linking.
builder.per_function_section(sess.opts.debugging_opts.function_sections.unwrap_or(false));
ObjectModule::new(builder)
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,82 @@
#![feature(rustc_private)]
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_target;
use rustc_data_structures::profiling::print_time_passes_entry;
use rustc_interface::interface;
use rustc_session::config::ErrorOutputType;
use rustc_session::early_error;
use rustc_target::spec::PanicStrategy;
#[derive(Default)]
pub struct CraneliftPassesCallbacks {
time_passes: bool,
}
impl rustc_driver::Callbacks for CraneliftPassesCallbacks {
fn config(&mut self, config: &mut interface::Config) {
// If a --prints=... option has been given, we don't print the "total"
// time because it will mess up the --prints output. See #64339.
self.time_passes = config.opts.prints.is_empty()
&& (config.opts.debugging_opts.time_passes || config.opts.debugging_opts.time);
config.opts.cg.panic = Some(PanicStrategy::Abort);
config.opts.debugging_opts.panic_abort_tests = true;
config.opts.maybe_sysroot = Some(
config.opts.maybe_sysroot.clone().unwrap_or_else(
|| std::env::current_exe()
.unwrap()
.parent()
.unwrap()
.join("sysroot"),
),
);
}
}
fn main() {
let start = std::time::Instant::now();
rustc_driver::init_rustc_env_logger();
let mut callbacks = CraneliftPassesCallbacks::default();
rustc_driver::install_ice_hook();
let exit_code = rustc_driver::catch_with_exit_code(|| {
let mut use_jit = false;
let mut args = std::env::args_os()
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
early_error(
ErrorOutputType::default(),
&format!("Argument {} is not valid Unicode: {:?}", i, arg),
)
})
})
.filter(|arg| {
if arg == "--jit" {
use_jit = true;
false
} else {
true
}
})
.collect::<Vec<_>>();
if use_jit {
args.push("-Cprefer-dynamic".to_string());
}
let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks);
run_compiler.set_make_codegen_backend(Some(Box::new(move |_| {
Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend {
config: rustc_codegen_cranelift::BackendConfig { use_jit },
})
})));
run_compiler.run()
});
// The extra `\t` is necessary to align this label with the others.
print_time_passes_entry(callbacks.time_passes, "\ttotal", start.elapsed());
std::process::exit(exit_code)
}

View File

@ -0,0 +1,103 @@
//! The only difference between this and cg_clif.rs is that this binary defaults to using cg_llvm
//! instead of cg_clif and requires `--clif` to use cg_clif and that this binary doesn't have JIT
//! support.
//! This is necessary as with Cargo `RUSTC` applies to both target crates and host crates. The host
//! crates must be built with cg_llvm as we are currently building a sysroot for cg_clif.
//! `RUSTFLAGS` however is only applied to target crates, so `--clif` would only be passed to the
//! target crates.
#![feature(rustc_private)]
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_interface;
extern crate rustc_session;
extern crate rustc_target;
use std::path::PathBuf;
use rustc_interface::interface;
use rustc_session::config::ErrorOutputType;
use rustc_session::early_error;
use rustc_target::spec::PanicStrategy;
fn find_sysroot() -> String {
// Taken from https://github.com/Manishearth/rust-clippy/pull/911.
let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
match (home, toolchain) {
(Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
_ => option_env!("RUST_SYSROOT")
.expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
.to_owned(),
}
}
pub struct CraneliftPassesCallbacks {
use_clif: bool,
}
impl rustc_driver::Callbacks for CraneliftPassesCallbacks {
fn config(&mut self, config: &mut interface::Config) {
if !self.use_clif {
config.opts.maybe_sysroot = Some(PathBuf::from(find_sysroot()));
return;
}
config.opts.cg.panic = Some(PanicStrategy::Abort);
config.opts.debugging_opts.panic_abort_tests = true;
config.opts.maybe_sysroot = Some(
std::env::current_exe()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("build_sysroot")
.join("sysroot"),
);
}
}
fn main() {
rustc_driver::init_rustc_env_logger();
rustc_driver::install_ice_hook();
let exit_code = rustc_driver::catch_with_exit_code(|| {
let mut use_clif = false;
let args = std::env::args_os()
.enumerate()
.map(|(i, arg)| {
arg.into_string().unwrap_or_else(|arg| {
early_error(
ErrorOutputType::default(),
&format!("Argument {} is not valid Unicode: {:?}", i, arg),
)
})
})
.filter(|arg| {
if arg == "--clif" {
use_clif = true;
false
} else {
true
}
})
.collect::<Vec<_>>();
let mut callbacks = CraneliftPassesCallbacks { use_clif };
let mut run_compiler = rustc_driver::RunCompiler::new(&args, &mut callbacks);
if use_clif {
run_compiler.set_make_codegen_backend(Some(Box::new(move |_| {
Box::new(rustc_codegen_cranelift::CraneliftCodegenBackend {
config: rustc_codegen_cranelift::BackendConfig { use_jit: false },
})
})));
}
run_compiler.run()
});
std::process::exit(exit_code)
}

View File

@ -0,0 +1,199 @@
//! Various number casting functions
use crate::prelude::*;
pub(crate) fn clif_intcast(
fx: &mut FunctionCx<'_, '_, impl Module>,
val: Value,
to: Type,
signed: bool,
) -> Value {
let from = fx.bcx.func.dfg.value_type(val);
match (from, to) {
// equal
(_, _) if from == to => val,
// extend
(_, types::I128) => {
let lo = if from == types::I64 {
val
} else if signed {
fx.bcx.ins().sextend(types::I64, val)
} else {
fx.bcx.ins().uextend(types::I64, val)
};
let hi = if signed {
fx.bcx.ins().sshr_imm(lo, 63)
} else {
fx.bcx.ins().iconst(types::I64, 0)
};
fx.bcx.ins().iconcat(lo, hi)
}
(_, _) if to.wider_or_equal(from) => {
if signed {
fx.bcx.ins().sextend(to, val)
} else {
fx.bcx.ins().uextend(to, val)
}
}
// reduce
(types::I128, _) => {
let (lsb, _msb) = fx.bcx.ins().isplit(val);
if to == types::I64 {
lsb
} else {
fx.bcx.ins().ireduce(to, lsb)
}
}
(_, _) => fx.bcx.ins().ireduce(to, val),
}
}
pub(crate) fn clif_int_or_float_cast(
fx: &mut FunctionCx<'_, '_, impl Module>,
from: Value,
from_signed: bool,
to_ty: Type,
to_signed: bool,
) -> Value {
let from_ty = fx.bcx.func.dfg.value_type(from);
if from_ty.is_int() && to_ty.is_int() {
// int-like -> int-like
clif_intcast(
fx,
from,
to_ty,
// This is correct as either from_signed == to_signed (=> this is trivially correct)
// Or from_clif_ty == to_clif_ty, which means this is a no-op.
from_signed,
)
} else if from_ty.is_int() && to_ty.is_float() {
if from_ty == types::I128 {
// _______ss__f_
// __float tisf: i128 -> f32
// __float tidf: i128 -> f64
// __floatuntisf: u128 -> f32
// __floatuntidf: u128 -> f64
let name = format!(
"__float{sign}ti{flt}f",
sign = if from_signed { "" } else { "un" },
flt = match to_ty {
types::F32 => "s",
types::F64 => "d",
_ => unreachable!("{:?}", to_ty),
},
);
let from_rust_ty = if from_signed {
fx.tcx.types.i128
} else {
fx.tcx.types.u128
};
let to_rust_ty = match to_ty {
types::F32 => fx.tcx.types.f32,
types::F64 => fx.tcx.types.f64,
_ => unreachable!(),
};
return fx
.easy_call(
&name,
&[CValue::by_val(from, fx.layout_of(from_rust_ty))],
to_rust_ty,
)
.load_scalar(fx);
}
// int-like -> float
if from_signed {
fx.bcx.ins().fcvt_from_sint(to_ty, from)
} else {
fx.bcx.ins().fcvt_from_uint(to_ty, from)
}
} else if from_ty.is_float() && to_ty.is_int() {
if to_ty == types::I128 {
// _____sssf___
// __fix sfti: f32 -> i128
// __fix dfti: f64 -> i128
// __fixunssfti: f32 -> u128
// __fixunsdfti: f64 -> u128
let name = format!(
"__fix{sign}{flt}fti",
sign = if to_signed { "" } else { "uns" },
flt = match from_ty {
types::F32 => "s",
types::F64 => "d",
_ => unreachable!("{:?}", to_ty),
},
);
let from_rust_ty = match from_ty {
types::F32 => fx.tcx.types.f32,
types::F64 => fx.tcx.types.f64,
_ => unreachable!(),
};
let to_rust_ty = if to_signed {
fx.tcx.types.i128
} else {
fx.tcx.types.u128
};
return fx
.easy_call(
&name,
&[CValue::by_val(from, fx.layout_of(from_rust_ty))],
to_rust_ty,
)
.load_scalar(fx);
}
// float -> int-like
if to_ty == types::I8 || to_ty == types::I16 {
// FIXME implement fcvt_to_*int_sat.i8/i16
let val = if to_signed {
fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
} else {
fx.bcx.ins().fcvt_to_uint_sat(types::I32, from)
};
let (min, max) = match (to_ty, to_signed) {
(types::I8, false) => (0, i64::from(u8::MAX)),
(types::I16, false) => (0, i64::from(u16::MAX)),
(types::I8, true) => (i64::from(i8::MIN), i64::from(i8::MAX)),
(types::I16, true) => (i64::from(i16::MIN), i64::from(i16::MAX)),
_ => unreachable!(),
};
let min_val = fx.bcx.ins().iconst(types::I32, min);
let max_val = fx.bcx.ins().iconst(types::I32, max);
let val = if to_signed {
let has_underflow = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, val, min);
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, val, max);
let bottom_capped = fx.bcx.ins().select(has_underflow, min_val, val);
fx.bcx.ins().select(has_overflow, max_val, bottom_capped)
} else {
let has_overflow = fx.bcx.ins().icmp_imm(IntCC::UnsignedGreaterThan, val, max);
fx.bcx.ins().select(has_overflow, max_val, val)
};
fx.bcx.ins().ireduce(to_ty, val)
} else if to_signed {
fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
} else {
fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
}
} else if from_ty.is_float() && to_ty.is_float() {
// float -> float
match (from_ty, to_ty) {
(types::F32, types::F64) => fx.bcx.ins().fpromote(types::F64, from),
(types::F64, types::F32) => fx.bcx.ins().fdemote(types::F32, from),
_ => from,
}
} else {
unreachable!("cast value from {:?} to {:?}", from_ty, to_ty);
}
}

View File

@ -0,0 +1,165 @@
//! Replaces 128-bit operators with lang item calls where necessary
use crate::prelude::*;
pub(crate) fn maybe_codegen<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
bin_op: BinOp,
checked: bool,
lhs: CValue<'tcx>,
rhs: CValue<'tcx>,
) -> Option<CValue<'tcx>> {
if lhs.layout().ty != fx.tcx.types.u128 && lhs.layout().ty != fx.tcx.types.i128 {
return None;
}
let lhs_val = lhs.load_scalar(fx);
let rhs_val = rhs.load_scalar(fx);
let is_signed = type_sign(lhs.layout().ty);
match bin_op {
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => {
assert!(!checked);
None
}
BinOp::Add | BinOp::Sub if !checked => None,
BinOp::Add => {
let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter());
return Some(if is_signed {
fx.easy_call("__rust_i128_addo", &[lhs, rhs], out_ty)
} else {
fx.easy_call("__rust_u128_addo", &[lhs, rhs], out_ty)
});
}
BinOp::Sub => {
let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter());
return Some(if is_signed {
fx.easy_call("__rust_i128_subo", &[lhs, rhs], out_ty)
} else {
fx.easy_call("__rust_u128_subo", &[lhs, rhs], out_ty)
});
}
BinOp::Offset => unreachable!("offset should only be used on pointers, not 128bit ints"),
BinOp::Mul => {
let res = if checked {
let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter());
if is_signed {
fx.easy_call("__rust_i128_mulo", &[lhs, rhs], out_ty)
} else {
fx.easy_call("__rust_u128_mulo", &[lhs, rhs], out_ty)
}
} else {
let val_ty = if is_signed {
fx.tcx.types.i128
} else {
fx.tcx.types.u128
};
fx.easy_call("__multi3", &[lhs, rhs], val_ty)
};
Some(res)
}
BinOp::Div => {
assert!(!checked);
if is_signed {
Some(fx.easy_call("__divti3", &[lhs, rhs], fx.tcx.types.i128))
} else {
Some(fx.easy_call("__udivti3", &[lhs, rhs], fx.tcx.types.u128))
}
}
BinOp::Rem => {
assert!(!checked);
if is_signed {
Some(fx.easy_call("__modti3", &[lhs, rhs], fx.tcx.types.i128))
} else {
Some(fx.easy_call("__umodti3", &[lhs, rhs], fx.tcx.types.u128))
}
}
BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne => {
assert!(!checked);
None
}
BinOp::Shl | BinOp::Shr => {
let is_overflow = if checked {
// rhs >= 128
// FIXME support non 128bit rhs
/*let (rhs_lsb, rhs_msb) = fx.bcx.ins().isplit(rhs_val);
let rhs_msb_gt_0 = fx.bcx.ins().icmp_imm(IntCC::NotEqual, rhs_msb, 0);
let rhs_lsb_ge_128 = fx.bcx.ins().icmp_imm(IntCC::SignedGreaterThan, rhs_lsb, 127);
let is_overflow = fx.bcx.ins().bor(rhs_msb_gt_0, rhs_lsb_ge_128);*/
let is_overflow = fx.bcx.ins().bconst(types::B1, false);
Some(fx.bcx.ins().bint(types::I8, is_overflow))
} else {
None
};
// Optimize `val >> 64`, because compiler_builtins uses it to deconstruct an 128bit
// integer into its lsb and msb.
// https://github.com/rust-lang-nursery/compiler-builtins/blob/79a6a1603d5672cbb9187ff41ff4d9b5048ac1cb/src/int/mod.rs#L217
if resolve_value_imm(fx.bcx.func, rhs_val) == Some(64) {
let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs_val);
let all_zeros = fx.bcx.ins().iconst(types::I64, 0);
let val = match (bin_op, is_signed) {
(BinOp::Shr, false) => {
let val = fx.bcx.ins().iconcat(lhs_msb, all_zeros);
Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.u128)))
}
(BinOp::Shr, true) => {
let sign = fx.bcx.ins().icmp_imm(IntCC::SignedLessThan, lhs_msb, 0);
let all_ones = fx.bcx.ins().iconst(types::I64, u64::MAX as i64);
let all_sign_bits = fx.bcx.ins().select(sign, all_zeros, all_ones);
let val = fx.bcx.ins().iconcat(lhs_msb, all_sign_bits);
Some(CValue::by_val(val, fx.layout_of(fx.tcx.types.i128)))
}
(BinOp::Shl, _) => {
let val_ty = if is_signed {
fx.tcx.types.i128
} else {
fx.tcx.types.u128
};
let val = fx.bcx.ins().iconcat(all_zeros, lhs_lsb);
Some(CValue::by_val(val, fx.layout_of(val_ty)))
}
_ => None,
};
if let Some(val) = val {
if let Some(is_overflow) = is_overflow {
let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter());
let val = val.load_scalar(fx);
return Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty)));
} else {
return Some(val);
}
}
}
let truncated_rhs = clif_intcast(fx, rhs_val, types::I32, false);
let truncated_rhs = CValue::by_val(truncated_rhs, fx.layout_of(fx.tcx.types.u32));
let val = match (bin_op, is_signed) {
(BinOp::Shl, false) => {
fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.u128)
}
(BinOp::Shl, true) => {
fx.easy_call("__ashlti3", &[lhs, truncated_rhs], fx.tcx.types.i128)
}
(BinOp::Shr, false) => {
fx.easy_call("__lshrti3", &[lhs, truncated_rhs], fx.tcx.types.u128)
}
(BinOp::Shr, true) => {
fx.easy_call("__ashrti3", &[lhs, truncated_rhs], fx.tcx.types.i128)
}
(_, _) => unreachable!(),
};
if let Some(is_overflow) = is_overflow {
let out_ty = fx.tcx.mk_tup([lhs.layout().ty, fx.tcx.types.bool].iter());
let val = val.load_scalar(fx);
Some(CValue::by_val_pair(val, is_overflow, fx.layout_of(out_ty)))
} else {
Some(val)
}
}
}
}

View File

@ -0,0 +1,444 @@
use rustc_index::vec::IndexVec;
use rustc_target::abi::{Integer, Primitive};
use rustc_target::spec::{HasTargetSpec, Target};
use cranelift_codegen::ir::{InstructionData, Opcode, ValueDef};
use crate::prelude::*;
pub(crate) fn pointer_ty(tcx: TyCtxt<'_>) -> types::Type {
match tcx.data_layout.pointer_size.bits() {
16 => types::I16,
32 => types::I32,
64 => types::I64,
bits => bug!("ptr_sized_integer: unknown pointer bit size {}", bits),
}
}
pub(crate) fn scalar_to_clif_type(tcx: TyCtxt<'_>, scalar: Scalar) -> Type {
match scalar.value {
Primitive::Int(int, _sign) => match int {
Integer::I8 => types::I8,
Integer::I16 => types::I16,
Integer::I32 => types::I32,
Integer::I64 => types::I64,
Integer::I128 => types::I128,
},
Primitive::F32 => types::F32,
Primitive::F64 => types::F64,
Primitive::Pointer => pointer_ty(tcx),
}
}
fn clif_type_from_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<types::Type> {
Some(match ty.kind() {
ty::Bool => types::I8,
ty::Uint(size) => match size {
UintTy::U8 => types::I8,
UintTy::U16 => types::I16,
UintTy::U32 => types::I32,
UintTy::U64 => types::I64,
UintTy::U128 => types::I128,
UintTy::Usize => pointer_ty(tcx),
},
ty::Int(size) => match size {
IntTy::I8 => types::I8,
IntTy::I16 => types::I16,
IntTy::I32 => types::I32,
IntTy::I64 => types::I64,
IntTy::I128 => types::I128,
IntTy::Isize => pointer_ty(tcx),
},
ty::Char => types::I32,
ty::Float(size) => match size {
FloatTy::F32 => types::F32,
FloatTy::F64 => types::F64,
},
ty::FnPtr(_) => pointer_ty(tcx),
ty::RawPtr(TypeAndMut {
ty: pointee_ty,
mutbl: _,
})
| ty::Ref(_, pointee_ty, _) => {
if has_ptr_meta(tcx, pointee_ty) {
return None;
} else {
pointer_ty(tcx)
}
}
ty::Adt(adt_def, _) if adt_def.repr.simd() => {
let (element, count) = match &tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap().abi
{
Abi::Vector { element, count } => (element.clone(), *count),
_ => unreachable!(),
};
match scalar_to_clif_type(tcx, element).by(u16::try_from(count).unwrap()) {
// Cranelift currently only implements icmp for 128bit vectors.
Some(vector_ty) if vector_ty.bits() == 128 => vector_ty,
_ => return None,
}
}
ty::Param(_) => bug!("ty param {:?}", ty),
_ => return None,
})
}
fn clif_pair_type_from_ty<'tcx>(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
) -> Option<(types::Type, types::Type)> {
Some(match ty.kind() {
ty::Tuple(substs) if substs.len() == 2 => {
let mut types = substs.types();
let a = clif_type_from_ty(tcx, types.next().unwrap())?;
let b = clif_type_from_ty(tcx, types.next().unwrap())?;
if a.is_vector() || b.is_vector() {
return None;
}
(a, b)
}
ty::RawPtr(TypeAndMut {
ty: pointee_ty,
mutbl: _,
})
| ty::Ref(_, pointee_ty, _) => {
if has_ptr_meta(tcx, pointee_ty) {
(pointer_ty(tcx), pointer_ty(tcx))
} else {
return None;
}
}
_ => return None,
})
}
/// Is a pointer to this type a fat ptr?
pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
let ptr_ty = tcx.mk_ptr(TypeAndMut {
ty,
mutbl: rustc_hir::Mutability::Not,
});
match &tcx
.layout_of(ParamEnv::reveal_all().and(ptr_ty))
.unwrap()
.abi
{
Abi::Scalar(_) => false,
Abi::ScalarPair(_, _) => true,
abi => unreachable!("Abi of ptr to {:?} is {:?}???", ty, abi),
}
}
pub(crate) fn codegen_icmp_imm(
fx: &mut FunctionCx<'_, '_, impl Module>,
intcc: IntCC,
lhs: Value,
rhs: i128,
) -> Value {
let lhs_ty = fx.bcx.func.dfg.value_type(lhs);
if lhs_ty == types::I128 {
// FIXME legalize `icmp_imm.i128` in Cranelift
let (lhs_lsb, lhs_msb) = fx.bcx.ins().isplit(lhs);
let (rhs_lsb, rhs_msb) = (rhs as u128 as u64 as i64, (rhs as u128 >> 64) as u64 as i64);
match intcc {
IntCC::Equal => {
let lsb_eq = fx.bcx.ins().icmp_imm(IntCC::Equal, lhs_lsb, rhs_lsb);
let msb_eq = fx.bcx.ins().icmp_imm(IntCC::Equal, lhs_msb, rhs_msb);
fx.bcx.ins().band(lsb_eq, msb_eq)
}
IntCC::NotEqual => {
let lsb_ne = fx.bcx.ins().icmp_imm(IntCC::NotEqual, lhs_lsb, rhs_lsb);
let msb_ne = fx.bcx.ins().icmp_imm(IntCC::NotEqual, lhs_msb, rhs_msb);
fx.bcx.ins().bor(lsb_ne, msb_ne)
}
_ => {
// if msb_eq {
// lsb_cc
// } else {
// msb_cc
// }
let msb_eq = fx.bcx.ins().icmp_imm(IntCC::Equal, lhs_msb, rhs_msb);
let lsb_cc = fx.bcx.ins().icmp_imm(intcc, lhs_lsb, rhs_lsb);
let msb_cc = fx.bcx.ins().icmp_imm(intcc, lhs_msb, rhs_msb);
fx.bcx.ins().select(msb_eq, lsb_cc, msb_cc)
}
}
} else {
let rhs = i64::try_from(rhs).expect("codegen_icmp_imm rhs out of range for <128bit int");
fx.bcx.ins().icmp_imm(intcc, lhs, rhs)
}
}
fn resolve_normal_value_imm(func: &Function, val: Value) -> Option<i64> {
if let ValueDef::Result(inst, 0 /*param*/) = func.dfg.value_def(val) {
if let InstructionData::UnaryImm {
opcode: Opcode::Iconst,
imm,
} = func.dfg[inst]
{
Some(imm.into())
} else {
None
}
} else {
None
}
}
fn resolve_128bit_value_imm(func: &Function, val: Value) -> Option<u128> {
let (lsb, msb) = if let ValueDef::Result(inst, 0 /*param*/) = func.dfg.value_def(val) {
if let InstructionData::Binary {
opcode: Opcode::Iconcat,
args: [lsb, msb],
} = func.dfg[inst]
{
(lsb, msb)
} else {
return None;
}
} else {
return None;
};
let lsb = u128::from(resolve_normal_value_imm(func, lsb)? as u64);
let msb = u128::from(resolve_normal_value_imm(func, msb)? as u64);
Some(msb << 64 | lsb)
}
pub(crate) fn resolve_value_imm(func: &Function, val: Value) -> Option<u128> {
if func.dfg.value_type(val) == types::I128 {
resolve_128bit_value_imm(func, val)
} else {
resolve_normal_value_imm(func, val).map(|imm| u128::from(imm as u64))
}
}
pub(crate) fn type_min_max_value(
bcx: &mut FunctionBuilder<'_>,
ty: Type,
signed: bool,
) -> (Value, Value) {
assert!(ty.is_int());
if ty == types::I128 {
if signed {
let min = i128::MIN as u128;
let min_lsb = bcx.ins().iconst(types::I64, min as u64 as i64);
let min_msb = bcx.ins().iconst(types::I64, (min >> 64) as u64 as i64);
let min = bcx.ins().iconcat(min_lsb, min_msb);
let max = i128::MIN as u128;
let max_lsb = bcx.ins().iconst(types::I64, max as u64 as i64);
let max_msb = bcx.ins().iconst(types::I64, (max >> 64) as u64 as i64);
let max = bcx.ins().iconcat(max_lsb, max_msb);
return (min, max);
} else {
let min_half = bcx.ins().iconst(types::I64, 0);
let min = bcx.ins().iconcat(min_half, min_half);
let max_half = bcx.ins().iconst(types::I64, u64::MAX as i64);
let max = bcx.ins().iconcat(max_half, max_half);
return (min, max);
}
}
let min = match (ty, signed) {
(types::I8, false) | (types::I16, false) | (types::I32, false) | (types::I64, false) => {
0i64
}
(types::I8, true) => i64::from(i8::MIN),
(types::I16, true) => i64::from(i16::MIN),
(types::I32, true) => i64::from(i32::MIN),
(types::I64, true) => i64::MIN,
_ => unreachable!(),
};
let max = match (ty, signed) {
(types::I8, false) => i64::from(u8::MAX),
(types::I16, false) => i64::from(u16::MAX),
(types::I32, false) => i64::from(u32::MAX),
(types::I64, false) => u64::MAX as i64,
(types::I8, true) => i64::from(i8::MAX),
(types::I16, true) => i64::from(i16::MAX),
(types::I32, true) => i64::from(i32::MAX),
(types::I64, true) => i64::MAX,
_ => unreachable!(),
};
let (min, max) = (bcx.ins().iconst(ty, min), bcx.ins().iconst(ty, max));
(min, max)
}
pub(crate) fn type_sign(ty: Ty<'_>) -> bool {
match ty.kind() {
ty::Ref(..) | ty::RawPtr(..) | ty::FnPtr(..) | ty::Char | ty::Uint(..) | ty::Bool => false,
ty::Int(..) => true,
ty::Float(..) => false, // `signed` is unused for floats
_ => panic!("{}", ty),
}
}
pub(crate) struct FunctionCx<'clif, 'tcx, M: Module> {
pub(crate) cx: &'clif mut crate::CodegenCx<'tcx, M>,
pub(crate) tcx: TyCtxt<'tcx>,
pub(crate) pointer_type: Type, // Cached from module
pub(crate) instance: Instance<'tcx>,
pub(crate) mir: &'tcx Body<'tcx>,
pub(crate) bcx: FunctionBuilder<'clif>,
pub(crate) block_map: IndexVec<BasicBlock, Block>,
pub(crate) local_map: IndexVec<Local, CPlace<'tcx>>,
/// When `#[track_caller]` is used, the implicit caller location is stored in this variable.
pub(crate) caller_location: Option<CValue<'tcx>>,
/// See [`crate::optimize::code_layout`] for more information.
pub(crate) cold_blocks: EntitySet<Block>,
pub(crate) clif_comments: crate::pretty_clif::CommentWriter,
pub(crate) source_info_set: indexmap::IndexSet<SourceInfo>,
/// This should only be accessed by `CPlace::new_var`.
pub(crate) next_ssa_var: u32,
pub(crate) inline_asm_index: u32,
}
impl<'tcx, M: Module> LayoutOf for FunctionCx<'_, 'tcx, M> {
type Ty = Ty<'tcx>;
type TyAndLayout = TyAndLayout<'tcx>;
fn layout_of(&self, ty: Ty<'tcx>) -> TyAndLayout<'tcx> {
assert!(!ty.still_further_specializable());
self.tcx
.layout_of(ParamEnv::reveal_all().and(&ty))
.unwrap_or_else(|e| {
if let layout::LayoutError::SizeOverflow(_) = e {
self.tcx.sess.fatal(&e.to_string())
} else {
bug!("failed to get layout for `{}`: {}", ty, e)
}
})
}
}
impl<'tcx, M: Module> layout::HasTyCtxt<'tcx> for FunctionCx<'_, 'tcx, M> {
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
self.tcx
}
}
impl<'tcx, M: Module> rustc_target::abi::HasDataLayout for FunctionCx<'_, 'tcx, M> {
fn data_layout(&self) -> &rustc_target::abi::TargetDataLayout {
&self.tcx.data_layout
}
}
impl<'tcx, M: Module> layout::HasParamEnv<'tcx> for FunctionCx<'_, 'tcx, M> {
fn param_env(&self) -> ParamEnv<'tcx> {
ParamEnv::reveal_all()
}
}
impl<'tcx, M: Module> HasTargetSpec for FunctionCx<'_, 'tcx, M> {
fn target_spec(&self) -> &Target {
&self.tcx.sess.target
}
}
impl<'tcx, M: Module> FunctionCx<'_, 'tcx, M> {
pub(crate) fn monomorphize<T>(&self, value: &T) -> T
where
T: TypeFoldable<'tcx> + Copy,
{
self.instance.subst_mir_and_normalize_erasing_regions(
self.tcx,
ty::ParamEnv::reveal_all(),
value
)
}
pub(crate) fn clif_type(&self, ty: Ty<'tcx>) -> Option<Type> {
clif_type_from_ty(self.tcx, ty)
}
pub(crate) fn clif_pair_type(&self, ty: Ty<'tcx>) -> Option<(Type, Type)> {
clif_pair_type_from_ty(self.tcx, ty)
}
pub(crate) fn get_block(&self, bb: BasicBlock) -> Block {
*self.block_map.get(bb).unwrap()
}
pub(crate) fn get_local_place(&mut self, local: Local) -> CPlace<'tcx> {
*self.local_map.get(local).unwrap_or_else(|| {
panic!("Local {:?} doesn't exist", local);
})
}
pub(crate) fn set_debug_loc(&mut self, source_info: mir::SourceInfo) {
let (index, _) = self.source_info_set.insert_full(source_info);
self.bcx.set_srcloc(SourceLoc::new(index as u32));
}
pub(crate) fn get_caller_location(&mut self, span: Span) -> CValue<'tcx> {
if let Some(loc) = self.caller_location {
// `#[track_caller]` is used; return caller location instead of current location.
return loc;
}
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = self.tcx.sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = self.tcx.const_caller_location((
rustc_span::symbol::Symbol::intern(&caller.file.name.to_string()),
caller.line as u32,
caller.col_display as u32 + 1,
));
crate::constant::codegen_const_value(self, const_loc, self.tcx.caller_location_ty())
}
pub(crate) fn triple(&self) -> &target_lexicon::Triple {
self.cx.module.isa().triple()
}
pub(crate) fn anonymous_str(&mut self, prefix: &str, msg: &str) -> Value {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
msg.hash(&mut hasher);
let msg_hash = hasher.finish();
let mut data_ctx = DataContext::new();
data_ctx.define(msg.as_bytes().to_vec().into_boxed_slice());
let msg_id = self
.cx
.module
.declare_data(
&format!("__{}_{:08x}", prefix, msg_hash),
Linkage::Local,
false,
false,
)
.unwrap();
// Ignore DuplicateDefinition error, as the data will be the same
let _ = self.cx.module.define_data(msg_id, &data_ctx);
let local_msg_id = self.cx.module.declare_data_in_func(msg_id, self.bcx.func);
#[cfg(debug_assertions)]
{
self.add_comment(local_msg_id, msg);
}
self.bcx.ins().global_value(self.pointer_type, local_msg_id)
}
}

View File

@ -0,0 +1,473 @@
//! Handling of `static`s, `const`s and promoted allocations
use rustc_span::DUMMY_SP;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::ErrorReported;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::mir::interpret::{
read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar,
};
use rustc_middle::ty::{Const, ConstKind};
use cranelift_codegen::ir::GlobalValueData;
use cranelift_module::*;
use crate::prelude::*;
#[derive(Default)]
pub(crate) struct ConstantCx {
todo: Vec<TodoItem>,
done: FxHashSet<DataId>,
}
#[derive(Copy, Clone, Debug)]
enum TodoItem {
Alloc(AllocId),
Static(DefId),
}
impl ConstantCx {
pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut impl Module) {
//println!("todo {:?}", self.todo);
define_all_allocs(tcx, module, &mut self);
//println!("done {:?}", self.done);
self.done.clear();
}
}
pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, impl Module>) {
for constant in &fx.mir.required_consts {
let const_ = fx.monomorphize(&constant.literal);
match const_.val {
ConstKind::Value(_) => {}
ConstKind::Unevaluated(def, ref substs, promoted) => {
if let Err(err) =
fx.tcx
.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None)
{
match err {
ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
fx.tcx
.sess
.span_err(constant.span, "erroneous constant encountered");
}
ErrorHandled::TooGeneric => {
span_bug!(
constant.span,
"codgen encountered polymorphic constant: {:?}",
err
);
}
}
}
}
ConstKind::Param(_)
| ConstKind::Infer(_)
| ConstKind::Bound(_, _)
| ConstKind::Placeholder(_)
| ConstKind::Error(_) => unreachable!("{:?}", const_),
}
}
}
pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
constants_cx.todo.push(TodoItem::Static(def_id));
}
pub(crate) fn codegen_tls_ref<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
def_id: DefId,
layout: TyAndLayout<'tcx>,
) -> CValue<'tcx> {
let data_id = data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false);
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("tls {:?}", def_id));
let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id);
CValue::by_val(tls_ptr, layout)
}
fn codegen_static_ref<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
def_id: DefId,
layout: TyAndLayout<'tcx>,
) -> CPlace<'tcx> {
let data_id = data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false);
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("{:?}", def_id));
let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
assert!(!layout.is_unsized(), "unsized statics aren't supported");
assert!(
matches!(fx.bcx.func.global_values[local_data_id], GlobalValueData::Symbol { tls: false, ..}),
"tls static referenced without Rvalue::ThreadLocalRef"
);
CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
}
pub(crate) fn codegen_constant<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
constant: &Constant<'tcx>,
) -> CValue<'tcx> {
let const_ = fx.monomorphize(&constant.literal);
let const_val = match const_.val {
ConstKind::Value(const_val) => const_val,
ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => {
assert!(substs.is_empty());
assert!(promoted.is_none());
return codegen_static_ref(
fx,
def.did,
fx.layout_of(fx.monomorphize(&constant.literal.ty)),
)
.to_cvalue(fx);
}
ConstKind::Unevaluated(def, ref substs, promoted) => {
match fx
.tcx
.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None)
{
Ok(const_val) => const_val,
Err(_) => {
if promoted.is_none() {
fx.tcx
.sess
.span_err(constant.span, "erroneous constant encountered");
}
return crate::trap::trap_unreachable_ret_value(
fx,
fx.layout_of(const_.ty),
"erroneous constant encountered",
);
}
}
}
ConstKind::Param(_)
| ConstKind::Infer(_)
| ConstKind::Bound(_, _)
| ConstKind::Placeholder(_)
| ConstKind::Error(_) => unreachable!("{:?}", const_),
};
codegen_const_value(fx, const_val, const_.ty)
}
pub(crate) fn codegen_const_value<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
const_val: ConstValue<'tcx>,
ty: Ty<'tcx>,
) -> CValue<'tcx> {
let layout = fx.layout_of(ty);
assert!(!layout.is_unsized(), "sized const value");
if layout.is_zst() {
return CValue::by_ref(
crate::Pointer::dangling(layout.align.pref),
layout,
);
}
match const_val {
ConstValue::Scalar(x) => {
if fx.clif_type(layout.ty).is_none() {
let (size, align) = (layout.size, layout.align.pref);
let mut alloc = Allocation::from_bytes(
std::iter::repeat(0)
.take(size.bytes_usize())
.collect::<Vec<u8>>(),
align,
);
let ptr = Pointer::new(AllocId(!0), Size::ZERO); // The alloc id is never used
alloc.write_scalar(fx, ptr, x.into(), size).unwrap();
let alloc = fx.tcx.intern_const_alloc(alloc);
return CValue::by_ref(pointer_for_allocation(fx, alloc), layout);
}
match x {
Scalar::Int(int) => {
CValue::const_val(fx, layout, int)
}
Scalar::Ptr(ptr) => {
let alloc_kind = fx.tcx.get_global_alloc(ptr.alloc_id);
let base_addr = match alloc_kind {
Some(GlobalAlloc::Memory(alloc)) => {
fx.cx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
let data_id = data_id_for_alloc_id(
&mut fx.cx.module,
ptr.alloc_id,
alloc.mutability,
);
let local_data_id =
fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id));
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
}
Some(GlobalAlloc::Function(instance)) => {
let func_id =
crate::abi::import_function(fx.tcx, &mut fx.cx.module, instance);
let local_func_id =
fx.cx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
}
Some(GlobalAlloc::Static(def_id)) => {
assert!(fx.tcx.is_static(def_id));
let data_id =
data_id_for_static(fx.tcx, &mut fx.cx.module, def_id, false);
let local_data_id =
fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("{:?}", def_id));
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
}
None => bug!("missing allocation {:?}", ptr.alloc_id),
};
let val = if ptr.offset.bytes() != 0 {
fx.bcx
.ins()
.iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap())
} else {
base_addr
};
CValue::by_val(val, layout)
}
}
}
ConstValue::ByRef { alloc, offset } => CValue::by_ref(
pointer_for_allocation(fx, alloc)
.offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
layout,
),
ConstValue::Slice { data, start, end } => {
let ptr = pointer_for_allocation(fx, data)
.offset_i64(fx, i64::try_from(start).unwrap())
.get_addr(fx);
let len = fx.bcx.ins().iconst(
fx.pointer_type,
i64::try_from(end.checked_sub(start).unwrap()).unwrap(),
);
CValue::by_val_pair(ptr, len, layout)
}
}
}
fn pointer_for_allocation<'tcx>(
fx: &mut FunctionCx<'_, 'tcx, impl Module>,
alloc: &'tcx Allocation,
) -> crate::pointer::Pointer {
let alloc_id = fx.tcx.create_memory_alloc(alloc);
fx.cx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
let data_id = data_id_for_alloc_id(&mut fx.cx.module, alloc_id, alloc.mutability);
let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
#[cfg(debug_assertions)]
fx.add_comment(local_data_id, format!("{:?}", alloc_id));
let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
crate::pointer::Pointer::new(global_ptr)
}
fn data_id_for_alloc_id(
module: &mut impl Module,
alloc_id: AllocId,
mutability: rustc_hir::Mutability,
) -> DataId {
module
.declare_data(
&format!(".L__alloc_{:x}", alloc_id.0),
Linkage::Local,
mutability == rustc_hir::Mutability::Mut,
false,
)
.unwrap()
}
fn data_id_for_static(
tcx: TyCtxt<'_>,
module: &mut impl Module,
def_id: DefId,
definition: bool,
) -> DataId {
let rlinkage = tcx.codegen_fn_attrs(def_id).linkage;
let linkage = if definition {
crate::linkage::get_static_linkage(tcx, def_id)
} else if rlinkage == Some(rustc_middle::mir::mono::Linkage::ExternalWeak)
|| rlinkage == Some(rustc_middle::mir::mono::Linkage::WeakAny)
{
Linkage::Preemptible
} else {
Linkage::Import
};
let instance = Instance::mono(tcx, def_id).polymorphize(tcx);
let symbol_name = tcx.symbol_name(instance).name;
let ty = instance.ty(tcx, ParamEnv::reveal_all());
let is_mutable = if tcx.is_mutable_static(def_id) {
true
} else {
!ty.is_freeze(tcx.at(DUMMY_SP), ParamEnv::reveal_all())
};
let align = tcx
.layout_of(ParamEnv::reveal_all().and(ty))
.unwrap()
.align
.pref
.bytes();
let attrs = tcx.codegen_fn_attrs(def_id);
let data_id = module
.declare_data(
&*symbol_name,
linkage,
is_mutable,
attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
)
.unwrap();
if rlinkage.is_some() {
// Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141
// Declare an internal global `extern_with_linkage_foo` which
// is initialized with the address of `foo`. If `foo` is
// discarded during linking (for example, if `foo` has weak
// linkage and there are no definitions), then
// `extern_with_linkage_foo` will instead be initialized to
// zero.
let ref_name = format!("_rust_extern_with_linkage_{}", symbol_name);
let ref_data_id = module
.declare_data(&ref_name, Linkage::Local, false, false)
.unwrap();
let mut data_ctx = DataContext::new();
data_ctx.set_align(align);
let data = module.declare_data_in_data(data_id, &mut data_ctx);
data_ctx.define(
std::iter::repeat(0)
.take(pointer_ty(tcx).bytes() as usize)
.collect(),
);
data_ctx.write_data_addr(0, data, 0);
match module.define_data(ref_data_id, &data_ctx) {
// Every time the static is referenced there will be another definition of this global,
// so duplicate definitions are expected and allowed.
Err(ModuleError::DuplicateDefinition(_)) => {}
res => res.unwrap(),
}
ref_data_id
} else {
data_id
}
}
fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut impl Module, cx: &mut ConstantCx) {
while let Some(todo_item) = cx.todo.pop() {
let (data_id, alloc, section_name) = match todo_item {
TodoItem::Alloc(alloc_id) => {
//println!("alloc_id {}", alloc_id);
let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
GlobalAlloc::Memory(alloc) => alloc,
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
};
let data_id = data_id_for_alloc_id(module, alloc_id, alloc.mutability);
(data_id, alloc, None)
}
TodoItem::Static(def_id) => {
//println!("static {:?}", def_id);
let section_name = tcx
.codegen_fn_attrs(def_id)
.link_section
.map(|s| s.as_str());
let alloc = tcx.eval_static_initializer(def_id).unwrap();
let data_id = data_id_for_static(tcx, module, def_id, true);
(data_id, alloc, section_name)
}
};
//("data_id {}", data_id);
if cx.done.contains(&data_id) {
continue;
}
let mut data_ctx = DataContext::new();
data_ctx.set_align(alloc.align.bytes());
if let Some(section_name) = section_name {
// FIXME set correct segment for Mach-O files
data_ctx.set_segment_section("", &*section_name);
}
let bytes = alloc
.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len())
.to_vec();
data_ctx.define(bytes.into_boxed_slice());
for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
let addend = {
let endianness = tcx.data_layout.endian;
let offset = offset.bytes() as usize;
let ptr_size = tcx.data_layout.pointer_size;
let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter(
offset..offset + ptr_size.bytes() as usize,
);
read_target_uint(endianness, bytes).unwrap()
};
let reloc_target_alloc = tcx.get_global_alloc(reloc).unwrap();
let data_id = match reloc_target_alloc {
GlobalAlloc::Function(instance) => {
assert_eq!(addend, 0);
let func_id = crate::abi::import_function(tcx, module, instance);
let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
continue;
}
GlobalAlloc::Memory(target_alloc) => {
cx.todo.push(TodoItem::Alloc(reloc));
data_id_for_alloc_id(module, reloc, target_alloc.mutability)
}
GlobalAlloc::Static(def_id) => {
if tcx
.codegen_fn_attrs(def_id)
.flags
.contains(CodegenFnAttrFlags::THREAD_LOCAL)
{
tcx.sess.fatal(&format!(
"Allocation {:?} contains reference to TLS value {:?}",
alloc, def_id
));
}
// Don't push a `TodoItem::Static` here, as it will cause statics used by
// multiple crates to be duplicated between them. It isn't necessary anyway,
// as it will get pushed by `codegen_static` when necessary.
data_id_for_static(tcx, module, def_id, false)
}
};
let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
}
module.define_data(data_id, &data_ctx).unwrap();
cx.done.insert(data_id);
}
assert!(cx.todo.is_empty(), "{:?}", cx.todo);
}
pub(crate) fn mir_operand_get_const_val<'tcx>(
fx: &FunctionCx<'_, 'tcx, impl Module>,
operand: &Operand<'tcx>,
) -> Option<&'tcx Const<'tcx>> {
match operand {
Operand::Copy(_) | Operand::Move(_) => None,
Operand::Constant(const_) => Some(
fx.monomorphize(&const_.literal)
.eval(fx.tcx, ParamEnv::reveal_all()),
),
}
}

View File

@ -0,0 +1,202 @@
//! Write the debuginfo into an object file.
use rustc_data_structures::fx::FxHashMap;
use gimli::write::{Address, AttributeValue, EndianVec, Result, Sections, Writer};
use gimli::{RunTimeEndian, SectionId};
use crate::backend::WriteDebugInfo;
use super::DebugContext;
impl DebugContext<'_> {
pub(crate) fn emit<P: WriteDebugInfo>(&mut self, product: &mut P) {
let unit_range_list_id = self.dwarf.unit.ranges.add(self.unit_range_list.clone());
let root = self.dwarf.unit.root();
let root = self.dwarf.unit.get_mut(root);
root.set(
gimli::DW_AT_ranges,
AttributeValue::RangeListRef(unit_range_list_id),
);
let mut sections = Sections::new(WriterRelocate::new(self.endian));
self.dwarf.write(&mut sections).unwrap();
let mut section_map = FxHashMap::default();
let _: Result<()> = sections.for_each_mut(|id, section| {
if !section.writer.slice().is_empty() {
let section_id = product.add_debug_section(id, section.writer.take());
section_map.insert(id, section_id);
}
Ok(())
});
let _: Result<()> = sections.for_each(|id, section| {
if let Some(section_id) = section_map.get(&id) {
for reloc in &section.relocs {
product.add_debug_reloc(&section_map, section_id, reloc);
}
}
Ok(())
});
}
}
#[derive(Clone)]
pub(crate) struct DebugReloc {
pub(crate) offset: u32,
pub(crate) size: u8,
pub(crate) name: DebugRelocName,
pub(crate) addend: i64,
pub(crate) kind: object::RelocationKind,
}
#[derive(Clone)]
pub(crate) enum DebugRelocName {
Section(SectionId),
Symbol(usize),
}
/// A [`Writer`] that collects all necessary relocations.
#[derive(Clone)]
pub(super) struct WriterRelocate {
pub(super) relocs: Vec<DebugReloc>,
pub(super) writer: EndianVec<RunTimeEndian>,
}
impl WriterRelocate {
pub(super) fn new(endian: RunTimeEndian) -> Self {
WriterRelocate {
relocs: Vec::new(),
writer: EndianVec::new(endian),
}
}
/// Perform the collected relocations to be usable for JIT usage.
#[cfg(feature = "jit")]
pub(super) fn relocate_for_jit(
mut self,
jit_product: &cranelift_simplejit::SimpleJITProduct,
) -> Vec<u8> {
use std::convert::TryInto;
for reloc in self.relocs.drain(..) {
match reloc.name {
super::DebugRelocName::Section(_) => unreachable!(),
super::DebugRelocName::Symbol(sym) => {
let addr = jit_product
.lookup_func(cranelift_module::FuncId::from_u32(sym.try_into().unwrap()));
let val = (addr as u64 as i64 + reloc.addend) as u64;
self.writer
.write_udata_at(reloc.offset as usize, val, reloc.size)
.unwrap();
}
}
}
self.writer.into_vec()
}
}
impl Writer for WriterRelocate {
type Endian = RunTimeEndian;
fn endian(&self) -> Self::Endian {
self.writer.endian()
}
fn len(&self) -> usize {
self.writer.len()
}
fn write(&mut self, bytes: &[u8]) -> Result<()> {
self.writer.write(bytes)
}
fn write_at(&mut self, offset: usize, bytes: &[u8]) -> Result<()> {
self.writer.write_at(offset, bytes)
}
fn write_address(&mut self, address: Address, size: u8) -> Result<()> {
match address {
Address::Constant(val) => self.write_udata(val, size),
Address::Symbol { symbol, addend } => {
let offset = self.len() as u64;
self.relocs.push(DebugReloc {
offset: offset as u32,
size,
name: DebugRelocName::Symbol(symbol),
addend: addend as i64,
kind: object::RelocationKind::Absolute,
});
self.write_udata(0, size)
}
}
}
fn write_offset(&mut self, val: usize, section: SectionId, size: u8) -> Result<()> {
let offset = self.len() as u32;
self.relocs.push(DebugReloc {
offset,
size,
name: DebugRelocName::Section(section),
addend: val as i64,
kind: object::RelocationKind::Absolute,
});
self.write_udata(0, size)
}
fn write_offset_at(
&mut self,
offset: usize,
val: usize,
section: SectionId,
size: u8,
) -> Result<()> {
self.relocs.push(DebugReloc {
offset: offset as u32,
size,
name: DebugRelocName::Section(section),
addend: val as i64,
kind: object::RelocationKind::Absolute,
});
self.write_udata_at(offset, 0, size)
}
fn write_eh_pointer(&mut self, address: Address, eh_pe: gimli::DwEhPe, size: u8) -> Result<()> {
match address {
// Address::Constant arm copied from gimli
Address::Constant(val) => {
// Indirect doesn't matter here.
let val = match eh_pe.application() {
gimli::DW_EH_PE_absptr => val,
gimli::DW_EH_PE_pcrel => {
// TODO: better handling of sign
let offset = self.len() as u64;
offset.wrapping_sub(val)
}
_ => {
return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe));
}
};
self.write_eh_pointer_data(val, eh_pe.format(), size)
}
Address::Symbol { symbol, addend } => match eh_pe.application() {
gimli::DW_EH_PE_pcrel => {
let size = match eh_pe.format() {
gimli::DW_EH_PE_sdata4 => 4,
_ => return Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)),
};
self.relocs.push(DebugReloc {
offset: self.len() as u32,
size,
name: DebugRelocName::Symbol(symbol),
addend,
kind: object::RelocationKind::Relative,
});
self.write_udata(0, size)
}
_ => Err(gimli::write::Error::UnsupportedPointerEncoding(eh_pe)),
},
}
}
}

View File

@ -0,0 +1,258 @@
//! Line info generation (`.debug_line`)
use std::ffi::OsStr;
use std::path::{Component, Path};
use crate::prelude::*;
use rustc_span::{
FileName, Pos, SourceFile, SourceFileAndLine, SourceFileHash, SourceFileHashAlgorithm,
};
use cranelift_codegen::binemit::CodeOffset;
use cranelift_codegen::machinst::MachSrcLoc;
use gimli::write::{
Address, AttributeValue, FileId, FileInfo, LineProgram, LineString, LineStringTable,
UnitEntryId,
};
// OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
let mut iter = path.components();
let file_name = match iter.next_back() {
Some(Component::Normal(p)) => p,
component => {
panic!(
"Path component {:?} of path {} is an invalid filename",
component,
path.display()
);
}
};
let parent = iter.as_path();
(parent, file_name)
}
// OPTIMIZATION: Avoid UTF-8 validation on UNIX.
fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
return path.as_bytes();
}
#[cfg(not(unix))]
{
return path.to_str().unwrap().as_bytes();
}
}
pub(crate) const MD5_LEN: usize = 16;
pub(crate) fn make_file_info(hash: SourceFileHash) -> Option<FileInfo> {
if hash.kind == SourceFileHashAlgorithm::Md5 {
let mut buf = [0u8; MD5_LEN];
buf.copy_from_slice(hash.hash_bytes());
Some(FileInfo {
timestamp: 0,
size: 0,
md5: buf,
})
} else {
None
}
}
fn line_program_add_file(
line_program: &mut LineProgram,
line_strings: &mut LineStringTable,
file: &SourceFile,
) -> FileId {
match &file.name {
FileName::Real(path) => {
let (dir_path, file_name) = split_path_dir_and_file(path.stable_name());
let dir_name = osstr_as_utf8_bytes(dir_path.as_os_str());
let file_name = osstr_as_utf8_bytes(file_name);
let dir_id = if !dir_name.is_empty() {
let dir_name = LineString::new(dir_name, line_program.encoding(), line_strings);
line_program.add_directory(dir_name)
} else {
line_program.default_directory()
};
let file_name = LineString::new(file_name, line_program.encoding(), line_strings);
let info = make_file_info(file.src_hash);
line_program.file_has_md5 &= info.is_some();
line_program.add_file(file_name, dir_id, info)
}
// FIXME give more appropriate file names
filename => {
let dir_id = line_program.default_directory();
let dummy_file_name = LineString::new(
filename.to_string().into_bytes(),
line_program.encoding(),
line_strings,
);
line_program.add_file(dummy_file_name, dir_id, None)
}
}
}
impl<'tcx> DebugContext<'tcx> {
pub(super) fn emit_location(&mut self, entry_id: UnitEntryId, span: Span) {
let loc = self.tcx.sess.source_map().lookup_char_pos(span.lo());
let file_id = line_program_add_file(
&mut self.dwarf.unit.line_program,
&mut self.dwarf.line_strings,
&loc.file,
);
let entry = self.dwarf.unit.get_mut(entry_id);
entry.set(
gimli::DW_AT_decl_file,
AttributeValue::FileIndex(Some(file_id)),
);
entry.set(
gimli::DW_AT_decl_line,
AttributeValue::Udata(loc.line as u64),
);
// FIXME: probably omit this
entry.set(
gimli::DW_AT_decl_column,
AttributeValue::Udata(loc.col.to_usize() as u64),
);
}
pub(super) fn create_debug_lines(
&mut self,
isa: &dyn cranelift_codegen::isa::TargetIsa,
symbol: usize,
entry_id: UnitEntryId,
context: &Context,
function_span: Span,
source_info_set: &indexmap::IndexSet<SourceInfo>,
) -> CodeOffset {
let tcx = self.tcx;
let line_program = &mut self.dwarf.unit.line_program;
let func = &context.func;
let line_strings = &mut self.dwarf.line_strings;
let mut last_span = None;
let mut last_file = None;
let mut create_row_for_span = |line_program: &mut LineProgram, span: Span| {
if let Some(last_span) = last_span {
if span == last_span {
line_program.generate_row();
return;
}
}
last_span = Some(span);
// Based on https://github.com/rust-lang/rust/blob/e369d87b015a84653343032833d65d0545fd3f26/src/librustc_codegen_ssa/mir/mod.rs#L116-L131
// In order to have a good line stepping behavior in debugger, we overwrite debug
// locations of macro expansions with that of the outermost expansion site
// (unless the crate is being compiled with `-Z debug-macros`).
let span = if !span.from_expansion() || tcx.sess.opts.debugging_opts.debug_macros {
span
} else {
// Walk up the macro expansion chain until we reach a non-expanded span.
// We also stop at the function body level because no line stepping can occur
// at the level above that.
rustc_span::hygiene::walk_chain(span, function_span.ctxt())
};
let (file, line, col) = match tcx.sess.source_map().lookup_line(span.lo()) {
Ok(SourceFileAndLine { sf: file, line }) => {
let line_pos = file.line_begin_pos(span.lo());
(
file,
u64::try_from(line).unwrap() + 1,
u64::from((span.lo() - line_pos).to_u32()) + 1,
)
}
Err(file) => (file, 0, 0),
};
// line_program_add_file is very slow.
// Optimize for the common case of the current file not being changed.
let current_file_changed = if let Some(last_file) = &last_file {
// If the allocations are not equal, then the files may still be equal, but that
// is not a problem, as this is just an optimization.
!rustc_data_structures::sync::Lrc::ptr_eq(last_file, &file)
} else {
true
};
if current_file_changed {
let file_id = line_program_add_file(line_program, line_strings, &file);
line_program.row().file = file_id;
last_file = Some(file);
}
line_program.row().line = line;
line_program.row().column = col;
line_program.generate_row();
};
line_program.begin_sequence(Some(Address::Symbol { symbol, addend: 0 }));
let mut func_end = 0;
if let Some(ref mcr) = &context.mach_compile_result {
for &MachSrcLoc { start, end, loc } in mcr.buffer.get_srclocs_sorted() {
line_program.row().address_offset = u64::from(start);
if !loc.is_default() {
let source_info = *source_info_set.get_index(loc.bits() as usize).unwrap();
create_row_for_span(line_program, source_info.span);
} else {
create_row_for_span(line_program, function_span);
}
func_end = end;
}
line_program.end_sequence(u64::from(func_end));
func_end = mcr.buffer.total_size();
} else {
let encinfo = isa.encoding_info();
let mut blocks = func.layout.blocks().collect::<Vec<_>>();
blocks.sort_by_key(|block| func.offsets[*block]); // Ensure inst offsets always increase
for block in blocks {
for (offset, inst, size) in func.inst_offsets(block, &encinfo) {
let srcloc = func.srclocs[inst];
line_program.row().address_offset = u64::from(offset);
if !srcloc.is_default() {
let source_info =
*source_info_set.get_index(srcloc.bits() as usize).unwrap();
create_row_for_span(line_program, source_info.span);
} else {
create_row_for_span(line_program, function_span);
}
func_end = offset + size;
}
}
line_program.end_sequence(u64::from(func_end));
}
assert_ne!(func_end, 0);
let entry = self.dwarf.unit.get_mut(entry_id);
entry.set(
gimli::DW_AT_low_pc,
AttributeValue::Address(Address::Symbol { symbol, addend: 0 }),
);
entry.set(
gimli::DW_AT_high_pc,
AttributeValue::Udata(u64::from(func_end)),
);
self.emit_location(entry_id, function_span);
func_end
}
}

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